feat(ci): make api-sync deterministic, drop claude-code-action - #22
Merged
Conversation
Replace the LLM-driven api-sync workflow with a pure script pipeline (scripts/api-sync/) that parses blindpay-v2's changelog, applies only the additive request-body fields it can safely express on known resource paths, and routes everything else (new endpoints, removed fields, enum changes, response-only changes) to a needs-human PR or issue instead of guessing. Auto-merge only fires when nothing needed a human. Also enables allow_auto_merge on the repo and lints/typechecks scripts/ alongside src/. Claude-Session: https://claude.ai/code/session_01F1stiNzuNtJXoXtiW9ZCbs
applySchemaField anchored the new FieldDef's indentation on the closing bracket line of the fields array, which this codebase indents 2 spaces shallower than the array elements, so every generated field landed visibly under-indented. The anchor now takes its indent from the last sibling element, and the test asserts the inserted line matches a sibling's indentation instead of only checking the substring exists. Found by the adversarial gate reviewing this PR. Claude-Session: https://claude.ai/code/session_01F1stiNzuNtJXoXtiW9ZCbs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the LLM-driven
api-sync.yml(Claude viaclaude-code-action, gated onCLAUDE_CODE_OAUTH_TOKEN) with a pure, reviewable script pipeline inscripts/api-sync/. No LLM, no OAuth token, in the loop at all.parse-changelog.ts— parses blindpay-v2'sspec-diff.tsmarkdown into structured events; fails loudly on anything it doesn't recognize instead of dropping it.known-resources.ts— the generator's entire "what can I touch" map (path -> resource -> function name). A path not in this map is unmappable by construction.classify.ts— splits events intoapplicable(an additive optional field on the REQUEST body of a known create/update path — the one pattern every historical additive api-sync PR here actually used, e.g. feat: sync CLI with API changes #6) andneedsHuman(everything else: removed fields, enum changes, response-only field changes, new/removed endpoints/methods/schemas).apply.ts— anchor-based, idempotent source patches toresources.ts,index.ts, andschema.tsfor each applicable field.version-bump.ts— minor if any endpoint/method/enum value was added or removed, patch otherwise. Scripted, not guessed.generate.ts— orchestrates the above and emits aSUMMARY_JSONline the workflow reads.api-sync.ymlnow: fetches the changelog, runs the generator, runs this repo's existingci.ymlcommands (typecheck/lint/test/build) against the result, commits, opens/updates a PR — and only callsgh pr merge --auto --squashwhen the generator reports nothing needed a human. If a changelog contains only unmappable changes, no PR is opened (there's no diff); a plain issue is opened instead so it isn't silently lost.Also flipped the repo's
allow_auto_mergesetting on via the GitHub API (was off, required for--autoto work), and addedscripts/tobun run typecheck/lintso the generator itself is covered by CI.Determinism proof
Ran the generator twice from the same changelog fixture against two independent copies of this repo:
Byte-identical. Re-applying the generator a second time against already-patched output is also a no-op (see the idempotency tests in
scripts/api-sync/__tests__/apply.test.ts).Equivalence proof
Fetched the real parent commit of historical PR #6 (
ba11caa) and its first commit (125c09b, the additiveswift_ifsc_branch_codefield oncreateBankAccount). Built a changelog fixture describing that exact spec change and ran the generator against theba11caabaseline. Diff against the real merged commit:Same flag name, same anchor point, functionally equivalent body handling (the generator omits the key entirely instead of sending
nullwhen the flag isn't passed — arguably better, and matches the newer partial-update convention already used elsewhere in this file). The only differences are the auto-generated help text (cosmetic; a human can refine it in review) and that the real PR still bumped the oldconstants.ts-based version scheme, which the very next commit in that same PR replaced with thepackage.jsonscheme this generator uses. Both diffs are explained line-by-line, not hidden.Scope, honestly
The CAN-express surface is intentionally narrow: additive optional fields on the request body of a resource already in
known-resources.ts. Field removals, enum-only changes, and response-field changes are routed to needs-human even though earlier LLM-driven syncs sometimes handled them — see the comment block at the top ofclassify.tsfor why each isn't safe to script deterministically today (no stable anchor for enum help text, "is this interesting enough for a column" is a judgment call, removing a flag can break someone's script). New resources/endpoints always need hand-written command wiring and are never touched.v2-side proposals (not applied — I don't touch blindpay-v2)
sdk-sync.ymlonly pusheschangelog.md(markdown) and the OLD baseline spec to downstream SDK repos, not the NEW spec JSON (unlikemcp-sync.yml, which does push full JSON to blindpay-mcp). Pushing the new spec JSON alongside the changelog would let this generator diff structured JSON directly instead of parsing markdown, which is more robust long-term and would let future generators (here or in other SDKs) go beyond the field/enum surface safely.scripts/spec-diff.tscould optionally emit a parallelchangelog.json(structured events) alongside the markdown, generated from the same diff pass — removes the need for every downstream repo to hand-roll a markdown parser likeparse-changelog.tshere.Test plan
bun run typecheck,bun run lint,bun test,bun run buildall pass on this branchscripts/api-sync/__tests__/covers the parser (recognized formats + fail-loud on unrecognized ones), the classifier (applicable vs needs-human for every event kind), andapply.ts(real repo source, plus idempotency)repository_dispatchfrom blindpay-v2 after merge, to confirm the end-to-end flow against a real changelog