Skip to content

feat: /api/compare/{a}/{b} + openapi.json coverage (Wave 1 P0/P1)#1156

Merged
Flotapponnier merged 2 commits into
devfrom
feat/geo-cache-compare-openapi
Jul 13, 2026
Merged

feat: /api/compare/{a}/{b} + openapi.json coverage (Wave 1 P0/P1)#1156
Flotapponnier merged 2 commits into
devfrom
feat/geo-cache-compare-openapi

Conversation

@Flotapponnier

Copy link
Copy Markdown
Collaborator

Ship the two P0/P1 items from the GEO audit's Wave 1 that actually add value after empirical verification.

Shipped

`/api/compare/{a}/{b}` — head-to-head payload as one tool call. LLMs asked "Mobula vs Codex head lag" would otherwise fire two /api/stat calls plus reason about the delta; this returns one flat structure with per benchmark p50 for each side, the winner on the metric's higher-is-better convention, the absolute delta, and a link to the canonical HTML compare page. Intersection logic mirrors src/lib/related-providers.ts so this endpoint and the "compare with" cards on /products/[slug] never disagree on which benches show up in the head-to-head.

openapi.json coverage — added `/api/citable/{date}` (real API endpoint, previously undocumented) and `/api/compare/{a}/{b}` to the OpenAPI 3.1 spec. Also documented the dimension query params (chain/region/kind/venue) on `/api/stat/{slug}` that shipped in #1154 but were undocumented. Added Compare schema; extended Stat schema with `filters` field.

citableAsOf helper widened — signature relaxed from full Benchmark to `Pick<Benchmark, "status" | "lastRunAt">` so the slim ProviderAppearance.benchmark used by the new compare route passes without a downcast. No behavior change on existing callers.

Not shipped (empirically verified as false positives)

  • Cache-Control on /llms.txt / /api/citable / /api/llm-context. Audit report showed `cache-control: public` and inferred no CDN caching. Source already sets `public, s-maxage=300, stale-while-revalidate=900`. Vercel strips `s-maxage` from the client-visible header while honoring it at the edge; `x-vercel-cache: HIT` with `age: 256` confirms the CDN cache is active. Not a real bug.
  • .bib / .ris file MIME endpoints. String versions already ship inside `/api/stat` JSON via `cite.bibtex` / `cite.apa`. Marginal upside for Zotero-style one-click imports; downgraded to P2.
  • `/api/stat/{slug}/sparkline.svg`. Sparkline data ships as a float array in `/api/stat` and the OG image at `/api/og/{slug}` covers the "embed a chart in an answer" case. P2.
  • JSON-LD `Dataset.variableMeasured.value`. Current schema emits bare strings; upgrading to PropertyValue objects would duplicate the measured value already carried by the StatisticalReport node's `Observation.measuredValue`. P2.

Test plan

  • pnpm typecheck clean
  • Verify staging `/api/compare/mobula/codex` returns valid JSON with a `shared` array
  • Verify staging `/api/compare/mobula/mobula` returns 400 `same_provider`
  • Verify staging `/api/compare/mobula/nonexistent-slug` returns 404 `unknown_provider`
  • Verify staging `/api/openapi.json` includes `/api/compare/{a}/{b}` and `/api/citable/{date}`
  • Cherry-pick to main + `vercel --prod`

Florent Tapponnier added 2 commits July 13, 2026 22:39
Two GEO surfaces flagged by the audit as high-value gaps.

## /api/compare/{a}/{b}

Head-to-head comparison as a single citable payload. LLMs asked "Mobula
vs Codex head lag" would otherwise need two /api/stat calls plus their
own delta reasoning; this endpoint returns one flat structure with per
benchmark p50 for each side, the winner on the metric's
higher-is-better convention, and the absolute delta.

The intersection logic mirrors src/lib/related-providers.ts and the
HTML /compare/{slug} page: shared benchmark = both providers appear in
it. Intentionally returns flat leader-p50 only (no chain/region
breakdowns, no cache-heavy fan out) so the agent tool call stays fast.

## openapi.json coverage

Added /api/citable/{date} (was missing despite being a real API
endpoint) and /api/compare/{a}/{b} to the OpenAPI 3.1 spec. Also
documented the dimension query params (chain/region/kind/venue) on
/api/stat/{slug} that shipped in the previous PR but were undocumented.
Added Compare schema; extended Stat schema with `filters` field.

/llms.txt and /llms-full.txt stay outside the OpenAPI surface — they
are text files not RPC endpoints, and OpenAPI consumers do not handle
plain-text specs well anyway.

## citableAsOf helper widened

Type signature relaxed from full Benchmark to
`Pick<Benchmark, "status" | "lastRunAt">` so the slim
ProviderAppearance.benchmark used by the new compare route can pass
through without a downcast. No behavior change on existing callers.

## Notes on items NOT shipped in this batch

- Cache-Control on /llms.txt / /api/citable / /api/llm-context: the
  audit report showed `cache-control: public` and inferred no CDN
  caching, but the source already sets
  `public, s-maxage=300, stale-while-revalidate=900`. Vercel strips the
  `s-maxage` directive from the client-visible header while honoring it
  at the edge; `x-vercel-cache: HIT` with `age: 256` confirms the CDN
  cache is active. Not a real bug.
- .bib / .ris file MIME endpoints: string versions already ship inside
  the /api/stat JSON via cite.bibtex / cite.apa. Marginal upside for
  Zotero-style one-click imports; downgraded to P2.
- /api/stat/{slug}/sparkline.svg: sparkline data already ships as a
  float array in /api/stat and the OG image at /api/og/{slug} covers
  the "embed a chart in an answer" case. P2.
- JSON-LD `Dataset.variableMeasured.value`: current schema emits bare
  strings; upgrading to PropertyValue objects would duplicate the
  measured value already carried by the StatisticalReport node's
  Observation.measuredValue. P2.
@Flotapponnier
Flotapponnier merged commit 47f0f6c into dev Jul 13, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant