{
  "schemaVersion": "1.0",
  "capturedAt": "2026-10-04",
  "localSourceRevision": "f9ce30befba0ed093cf11e52ba33afe9a934c7cf",
  "limit": "These are local reviewed source files, captured before the run. Their hashes bind this source snapshot; they do not independently identify the code deployed behind the public endpoint. The observed HTTP contract and complete responses are recorded separately.",
  "sources": [
    {
      "path": "lib/contribution.ts",
      "sha256": "e10339f2a1f8ced58fad8ed736e520d3eb4190698250fa082dfeef75d1ac753a",
      "content": "export const inputNames = [\n  \"netRevenue\",\n  \"costOfGoods\",\n  \"variableCosts\",\n  \"adSpend\",\n] as const;\nexport type ContributionInput = Record<(typeof inputNames)[number], number>;\nexport const inputLabels: Record<keyof ContributionInput, string> = {\n  netRevenue: \"Net revenue\",\n  costOfGoods: \"Cost of goods\",\n  variableCosts: \"Other variable costs\",\n  adSpend: \"Advertising spend\",\n};\nexport const exampleInput: ContributionInput = {\n  netRevenue: 1000,\n  costOfGoods: 550,\n  variableCosts: 150,\n  adSpend: 200,\n};\nexport function calculateContribution(input: ContributionInput) {\n  for (const key of inputNames)\n    if (!Number.isFinite(input[key]) || input[key] < 0 || input[key] > 1e12)\n      throw new Error(\n        `${inputLabels[key]} must be a finite number between 0 and 1,000,000,000,000.`,\n      );\n  const { netRevenue, costOfGoods, variableCosts, adSpend } = input;\n  const grossProfit = netRevenue - costOfGoods;\n  const contributionBeforeAds = grossProfit - variableCosts;\n  const result = {\n    formulaVersion: \"1.0\",\n    input,\n    grossProfit,\n    contributionBeforeAds,\n    contributionAfterAds: contributionBeforeAds - adSpend,\n    revenueRoas: adSpend > 0 ? netRevenue / adSpend : null,\n    grossProfitReturn: adSpend > 0 ? grossProfit / adSpend : null,\n    contributionReturn: adSpend > 0 ? contributionBeforeAds / adSpend : null,\n    breakEvenRoas:\n      netRevenue > 0 && contributionBeforeAds > 0\n        ? netRevenue / contributionBeforeAds\n        : null,\n    contributionMarginBeforeAds:\n      netRevenue > 0 ? contributionBeforeAds / netRevenue : null,\n  };\n  if (Object.values(result).some(value => typeof value === \"number\" && !Number.isFinite(value)))\n    throw new Error(\"These amounts exceed the calculator\u2019s numerical precision. Use a less extreme scale.\");\n  return result;\n}\nexport function parseContributionParams(\n  params: URLSearchParams,\n): ContributionInput {\n  if (\n    [...params.keys()].some(\n      (k) => !(inputNames as readonly string[]).includes(k),\n    )\n  )\n    throw new Error(\n      \"Unknown parameter. Use netRevenue, costOfGoods, variableCosts, and adSpend only.\",\n    );\n  const entries = inputNames.map((key) => {\n    const values = params.getAll(key);\n    if (values.length !== 1 || !/^(?:\\d+(?:\\.\\d*)?|\\.\\d+)$/.test(values[0]))\n      throw new Error(\n        `${key} is required exactly once as a non-negative decimal number.`,\n      );\n    const number = Number(values[0]);\n    if (number === 0 && /[1-9]/.test(values[0]))\n      throw new Error(`${key} is too small to represent reliably.`);\n    return [key, number];\n  });\n  const input = Object.fromEntries(entries) as ContributionInput;\n  calculateContribution(input);\n  return input;\n}\n"
    },
    {
      "path": "app/api/tools/contribution/route.ts",
      "sha256": "4f900fdf76cb7cefd933284691fb26e59f0598bd9f8c5c6998ce8109239e0651",
      "content": "import {\n  calculateContribution,\n  parseContributionParams,\n} from \"@/lib/contribution\";\nconst headers = {\n  \"Cache-Control\": \"no-store\",\n  \"Access-Control-Allow-Origin\": \"*\",\n  \"X-Robots-Tag\": \"noindex\",\n  Link: '<https://www.iamgeorgekelly.com/openapi.json>; rel=\"service-desc\"',\n};\nexport function GET(request: Request) {\n  try {\n    return Response.json(\n      {\n        ...calculateContribution(\n          parseContributionParams(new URL(request.url).searchParams),\n        ),\n        units:\n          \"Use the same currency for all monetary inputs and outputs. Returns are multiples; contributionMarginBeforeAds is a decimal fraction.\",\n        scope:\n          \"Arithmetic scenario only; excludes fixed overhead and does not establish incrementality.\",\n      },\n      { headers },\n    );\n  } catch (error) {\n    return Response.json(\n      { error: (error as Error).message },\n      { status: 400, headers },\n    );\n  }\n}\n"
    },
    {
      "path": "app/openapi.json/route.ts",
      "sha256": "69ba41359915b93b5cc8c394af371dcd6bc2dc53ec573696f821e5f7da4854ad",
      "content": "import { inputNames, inputLabels } from \"@/lib/contribution\";\nexport const dynamic = \"force-static\";\nexport function GET() {\n  const monetary = { type: \"number\", minimum: 0, maximum: 1e12 };\n  const descriptions = {\n    netRevenue: \"Revenue after discounts and refunds, excluding sales tax.\",\n    costOfGoods: \"Cost assigned to the items sold.\",\n    variableCosts:\n      \"Other variable order costs not already included in costOfGoods.\",\n    adSpend: \"Advertising spend for the same cohort.\",\n  };\n  const ratio = {\n    type: [\"number\", \"null\"],\n    description:\n      \"A multiple, or null when the denominator is zero. Negative returns are possible.\",\n  };\n  return Response.json({\n    openapi: \"3.1.1\",\n    info: {\n      title: \"George Kelly Contribution Calculator\",\n      version: \"1.0.0\",\n      description:\n        \"Read-only arithmetic for commerce scenarios. No authentication, transactions, external data retrieval, or side effects. Use the same currency for every monetary input. GET query strings can appear in hosting or client logs; use the local browser calculator for sensitive figures.\",\n    },\n    servers: [{ url: \"https://www.iamgeorgekelly.com\" }],\n    paths: {\n      \"/api/tools/contribution\": {\n        get: {\n          operationId: \"calculateContribution\",\n          summary:\n            \"Calculate contribution after advertising and explicit return ratios\",\n          description:\n            \"All four parameters are required exactly once as non-negative decimal numbers, at most 1e12. Unknown, duplicate, empty, signed, exponent, and non-finite values are rejected. Excludes fixed overhead. Zero ad spend returns null spend ratios; non-positive pre-ad margin returns null breakEvenRoas. Calculations use JavaScript double precision. Inputs that underflow to zero or cause a non-finite output are rejected. No display rounding is applied to the JSON result.\",\n          parameters: inputNames.map((name) => ({\n            name,\n            in: \"query\",\n            required: true,\n            description: inputLabels[name] + \". \" + descriptions[name],\n            schema: monetary,\n            example: {\n              netRevenue: 1000,\n              costOfGoods: 550,\n              variableCosts: 150,\n              adSpend: 200,\n            }[name],\n          })),\n          responses: {\n            \"200\": {\n              description:\n                \"Calculated scenario, not a forecast or attribution claim\",\n              content: {\n                \"application/json\": {\n                  schema: {\n                    type: \"object\",\n                    required: [\n                      \"formulaVersion\",\n                      \"input\",\n                      \"grossProfit\",\n                      \"contributionBeforeAds\",\n                      \"contributionAfterAds\",\n                      \"revenueRoas\",\n                      \"grossProfitReturn\",\n                      \"contributionReturn\",\n                      \"breakEvenRoas\",\n                      \"contributionMarginBeforeAds\",\n                      \"units\",\n                      \"scope\",\n                    ],\n                    properties: {\n                      formulaVersion: { const: \"1.0\" },\n                      input: {\n                        type: \"object\",\n                        additionalProperties: false,\n                        required: [...inputNames],\n                        properties: Object.fromEntries(\n                          inputNames.map((k) => [k, monetary]),\n                        ),\n                      },\n                      grossProfit: { type: \"number\" },\n                      contributionBeforeAds: { type: \"number\" },\n                      contributionAfterAds: { type: \"number\" },\n                      revenueRoas: ratio,\n                      grossProfitReturn: ratio,\n                      contributionReturn: ratio,\n                      breakEvenRoas: {\n                        type: [\"number\", \"null\"],\n                        description:\n                          \"Net revenue divided by contribution before ads when both are positive; otherwise null.\",\n                      },\n                      contributionMarginBeforeAds: {\n                        type: [\"number\", \"null\"],\n                        description:\n                          \"Contribution before ads divided by net revenue, as a decimal fraction; null when revenue is zero.\",\n                      },\n                      units: { type: \"string\" },\n                      scope: { type: \"string\" },\n                    },\n                  },\n                },\n              },\n            },\n            \"400\": {\n              description: \"Missing or invalid input\",\n              content: {\n                \"application/json\": {\n                  schema: {\n                    type: \"object\",\n                    required: [\"error\"],\n                    properties: { error: { type: \"string\" } },\n                  },\n                },\n              },\n            },\n          },\n        },\n      },\n    },\n  });\n}\n"
    }
  ]
}
