Skip to content

feat(api-sync): generate SDK methods for new spec operations (operation-insert) - #71

Merged
ericviana merged 1 commit into
mainfrom
eric/operation-scaffolding
Aug 4, 2026
Merged

ericviana merged 1 commit into
mainfrom
eric/operation-scaffolding

Conversation

@ericviana

Copy link
Copy Markdown
Member

What

Extends the deterministic api-sync patcher (scripts/api-sync/) with an operation-insert applicable change kind. New spec operations that are STANDARD no longer need a human touch; the generator writes the endpoint method itself.

Classifier boundary

STANDARD (auto-generated):

  • JSON in/out only (requestBody and 200/201 response content, if any, must be exactly application/json).
  • Instance-scoped (/v1/instances/{instance_id}/...).
  • Belongs to exactly one existing resource, identified by the deepest literal path segment that starts a new collection level (e.g. bank-accounts in .../customers/{customer_id}/bank-accounts/{id}, not the generic customers container).
  • That resource's factory function has the standard (instanceId: string, client: InternalApiClient) signature.
  • Only optional header/cookie params (e.g. every mutation's Idempotency-Key, never modeled by this SDK); a required one is out of scope.
  • Query params only on GET.

For a STANDARD operation, the generator:

  • Picks the method name from the HTTP verb + path shape (list/get/create/update/delete, plus any trailing literal segment PascalCased, e.g. createEvm).
  • Synthesizes Input/Output types recursively from the operation's schemas (typegen.ts), reusing:
    • the canonical shared enum symbol (from types/index.d.ts) when a spec-inlined enum's value set matches one exactly, instead of a fresh inline literal union;
    • the SDK symbol of a schema already modeled whole in spec-map.json, as a bare alias, instead of re-inlining it.
  • Adds a new spec-map.json schema/path-locator entry for any never-before-modeled named schema, so future field/enum drift on it is patchable the same way as everything else.
  • Wires the method into the resource's returned object and adds any needed shared-type imports.

NON-STANDARD (stays needs-human, precise reason, never guessed):

  • multipart/binary/other non-JSON content types (request or response)
  • not instance-scoped (available/*, upload, the instances resource's own {id}-keyed CRUD, /e/... tracking routes)
  • no existing resource owns the path segment, or more than one does (ambiguous)
  • resource factory isn't the standard (instanceId, client) shape
  • required header/cookie param, or query params on a non-GET

Discovery is state-based (mirrors enum-insert/field-insert): a spec operation with no matching SDK endpoint is a gap whether the spec just introduced it or a human deleted the SDK method by hand, not a diff against the previous spec snapshot. A precise per-operation path-shape matcher (resource-registry.ts) does the discovery for the common instance-scoped case; the existing coarse coverage.ts heuristic (last-segment-appears-anywhere) is reused as a fallback for the handful of operations outside that template's scope, so an already-hand-implemented available/*-style endpoint doesn't turn into a spurious finding every run.

A handful of pre-existing, already-known non-standard gaps (the Rfi family, POST /v1/upload/analyze) are grandfathered into a new ignore.operations list in spec-map.json, so this doesn't turn previously-tolerated coverage gaps into a sudden CI failure — a genuinely new non-standard gap still fails needs-human until a human reviews and either builds it by hand or adds it to the ignore list.

Any operation-insert bumps minor (existing classifyBump rule, now reachable). --check fails on a pending, un-applied operation-insert the same way it fails on pending enum/field drift today.

Golden self-test evidence

scripts/api-sync/operation-gen.golden.test.ts: in a scratch copy of the whole repo, deletes payins.get (+ GetPayinInput/GetPayinResponse) and quotes.create (+ CreateQuoteInput/CreateQuoteResponse), then runs --apply against the committed snapshot spec. Asserts:

  • the regenerated methods and types are semantically identical to what was deleted (same route, verb, field names/types)
  • bun run check-types and bun run test pass against the regenerated repo
  • a second --apply run is byte-identical (idempotent)
  • --check passes clean afterward

scripts/api-sync/operation-gen.test.ts: isolated classifier unit tests against a synthetic fixture resource/spec — a GET-by-id classifies STANDARD with the expected method/type text, a multipart/form-data request body classifies NON-STANDARD with a precise reason, and a request body referencing a never-before-modeled named schema gets a new spec-map.json schema entry.

Also ran the full local gauntlet clean: bun run check-types, bun run test (182 tests), bun run contract-check, bun run sync:check (against the live repo, no regressions on the 130 existing known divergences), bun run lint:check.

What still needs a human

Multipart/binary uploads, anything outside the instances/{instance_id} template shape (a brand-new top-level resource, available/*-style bare-client resources), ambiguous or unowned path segments, and required header/cookie params. All of these fail loud with a specific reason instead of silently guessing.

Claude-Session: https://claude.ai/code/session_01F1stiNzuNtJXoXtiW9ZCbs

…on-insert)

Extends the deterministic patcher with an operation-insert change kind so
STANDARD spec operations (JSON in/out, path/query/body params, belonging to
an existing resource by literal path segment) no longer need a human: the
generator adds the method to the right resource, synthesizes Input/Output
types from the operation's schemas (reusing whole-schema/canonical-enum
symbols already mapped in spec-map.json where possible), and records new
schema map entries for any never-before-modeled named schema so future
field/enum drift on it stays patchable.

Discovery is state-based (like enum-insert/field-insert), not a diff against
the previous spec: a spec operation with no matching SDK endpoint is a gap
whether the spec just introduced it or a human deleted the SDK method by
hand. NON-STANDARD operations (multipart/binary, non-JSON content, no
matching resource, ambiguous resource ownership, required header/cookie
params) stay needs-human with a precise reason; a handful of pre-existing,
already-known non-standard gaps (the Rfi family, POST /v1/upload/analyze)
are grandfathered into a new ignore.operations list so this doesn't
regress previously-tolerated coverage gaps into a sudden CI failure.

Any operation-insert bumps minor, matching the existing classifyBump rule.
--check now also fails on a pending, un-applied operation-insert, same as
existing drift.

Claude-Session: https://claude.ai/code/session_01F1stiNzuNtJXoXtiW9ZCbs
@ericviana
ericviana merged commit 1b06607 into main Aug 4, 2026
0 of 3 checks passed
@ericviana
ericviana deleted the eric/operation-scaffolding branch August 4, 2026 15:05
@BernardoSM

BernardoSM commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues
✅ Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

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.

2 participants