-
Notifications
You must be signed in to change notification settings - Fork 163
feat: spec-sync pipeline (Problem 3) #99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
068287e
chore(spec-sync): add fetch/normalize script and V1 spec snapshot bas…
yzld2002 92ee58b
chore(spec-sync): add drift detector
yzld2002 ee48f45
chore(spec-sync): add reference-model codegen (datamodel-code-generator)
yzld2002 0d98f60
chore(spec-sync): add griffe surface-lock gate (baseline=last release…
yzld2002 9ce063a
test(spec-sync): add staging contract-test scaffold (contract marker)
yzld2002 af14062
ci(spec-sync): add two-phase spec-sync workflow (mechanical + AI)
yzld2002 abddece
ci(spec-sync): add PR gates (surface-lock + staging contract tests)
yzld2002 40d8efe
ci(spec-sync): add production-spec release gate (staging-in, producti…
yzld2002 c30c495
docs(spec-sync): document the spec-sync pipeline and TS-port follow-up
yzld2002 2afbbb6
fix(spec-sync): exclude generated specs from linters; lock deps; stag…
yzld2002 98e7bb8
fix(spec-sync): pin codegen/griffe to validated versions; regen model…
yzld2002 93666d5
ci(spec-sync): use fine-grained PAT (SPEC_SYNC_TOKEN) instead of a Gi…
yzld2002 e94036c
fix(spec-sync): method-granular release gate; mkdir -p snapshot parent
yzld2002 0ad5bea
chore(spec-sync): re-baseline V1 snapshot to the SDK's implemented su…
yzld2002 dfce66d
ci(spec-sync): deterministic AI push, no duplicate PRs, injection har…
yzld2002 513fb06
ci(spec-sync): scope live-staging gate, exact codegen pins, opt-in co…
yzld2002 5942ca6
fix(spec-sync): close staging client in fixture; align docs to hourly…
yzld2002 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| name: PR Gates | ||
| on: | ||
| pull_request: | ||
| branches: [main] | ||
|
|
||
| jobs: | ||
| surface-lock: | ||
| # Escape hatch for intentional breaking changes (major releases): label the PR | ||
| # `breaking-change-approved` to skip the released-surface lock. | ||
| if: ${{ !contains(github.event.pull_request.labels.*.name, 'breaking-change-approved') }} | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| steps: | ||
| - uses: actions/checkout@v6 | ||
| with: | ||
| fetch-depth: 0 # tags for the release-tag baseline | ||
| - name: Install Rye | ||
| run: | | ||
| curl -sSf https://rye.astral.sh/get | bash | ||
| echo "$HOME/.rye/shims" >> "$GITHUB_PATH" | ||
| env: | ||
| RYE_VERSION: '0.44.0' | ||
| RYE_INSTALL_OPTION: '--yes' | ||
| - name: Install dependencies | ||
| run: rye sync --all-features | ||
| - name: Surface lock (no breaking change to released API) | ||
| run: ./scripts/spec-sync/surface-lock.sh | ||
|
|
||
| contract-tests: | ||
| # Only spec-sync PRs are gated on the live staging API. Gating *every* PR would let a | ||
| # staging outage or transient 5xx block all merges repo-wide. Keep this job OUT of the | ||
| # required-checks list for ordinary PRs (it is skipped there); require it only where it runs. | ||
| if: startsWith(github.head_ref, 'spec-sync/') | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 15 | ||
| steps: | ||
| - uses: actions/checkout@v6 | ||
| - name: Install Rye | ||
| run: | | ||
| curl -sSf https://rye.astral.sh/get | bash | ||
| echo "$HOME/.rye/shims" >> "$GITHUB_PATH" | ||
| env: | ||
| RYE_VERSION: '0.44.0' | ||
| RYE_INSTALL_OPTION: '--yes' | ||
| - name: Install dependencies | ||
| run: rye sync --all-features | ||
| - name: Contract tests vs staging | ||
| env: | ||
| LANDINGAI_ADE_STAGING_APIKEY: ${{ secrets.LANDINGAI_ADE_STAGING_APIKEY }} | ||
| # -n 0 forces serial execution: these hit the live API, and the repo's default | ||
| # `-n auto` (xdist) would run them in parallel, inviting flakiness/rate-limits. | ||
| run: rye run pytest tests/contract -m contract -n 0 -v |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| name: Spec Sync | ||
| on: | ||
| schedule: | ||
| - cron: '0 * * * *' # hourly; cron covers correctness, dispatch cuts latency | ||
| workflow_dispatch: {} | ||
|
|
||
| # Serialize runs so a cron tick and a manual dispatch can't race on the sync branch. | ||
| concurrency: | ||
| group: spec-sync | ||
| cancel-in-progress: false | ||
|
|
||
| permissions: | ||
| contents: read # all writes go through SPEC_SYNC_TOKEN, never GITHUB_TOKEN | ||
|
|
||
| jobs: | ||
| spec-sync: | ||
| # Guard so the scheduled/dispatch job only runs on the canonical repo — forks lack the | ||
| # secrets and would just produce noisy failing runs. | ||
| if: github.repository == 'landing-ai/ade-python' | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 30 | ||
| env: | ||
| V1_SPEC_URL: https://api.va.staging.landing.ai/v1/ade/openapi.json # staging drives the loop | ||
| SYNC_BRANCH: spec-sync/v1 # fixed branch: reruns update one PR in place, never a pile of them | ||
| steps: | ||
| - uses: actions/checkout@v6 | ||
| with: | ||
| # SPEC_SYNC_TOKEN is a fine-grained PAT (Contents: RW, Pull requests: RW) scoped to this | ||
| # repo. It must NOT be the default GITHUB_TOKEN: pushes/PRs authored by GITHUB_TOKEN do | ||
| # not trigger the gate workflows (anti-recursion). | ||
| token: ${{ secrets.SPEC_SYNC_TOKEN }} | ||
| fetch-depth: 0 # tags needed for surface-lock baseline | ||
|
|
||
| - name: Install Rye | ||
| run: | | ||
| curl -sSf https://rye.astral.sh/get | bash | ||
| echo "$HOME/.rye/shims" >> "$GITHUB_PATH" | ||
| env: | ||
| RYE_VERSION: '0.44.0' | ||
| RYE_INSTALL_OPTION: '--yes' | ||
|
|
||
| - name: Install dependencies | ||
| run: rye sync --all-features | ||
|
|
||
| - name: Detect drift | ||
| id: drift | ||
| run: | | ||
| set +e | ||
| ./scripts/spec-sync/check-drift.sh "$V1_SPEC_URL" specs/v1-ade.json | ||
| echo "code=$?" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: No drift | ||
| if: steps.drift.outputs.code == '0' | ||
| run: echo "specs in sync; nothing to do." | ||
|
|
||
| - name: Fail on fetch error | ||
| if: steps.drift.outputs.code != '0' && steps.drift.outputs.code != '10' | ||
| run: | | ||
| echo "spec fetch/normalize failed (exit ${{ steps.drift.outputs.code }})" | ||
| exit 1 | ||
|
|
||
| # If a sync PR is already open, do nothing: it's awaiting human review, and re-running | ||
| # would duplicate gate runs and Claude API spend every hour until it merges. | ||
| - name: Check for an open sync PR | ||
| id: existing | ||
| if: steps.drift.outputs.code == '10' | ||
| env: | ||
| GH_TOKEN: ${{ secrets.SPEC_SYNC_TOKEN }} | ||
| run: | | ||
| if gh pr list --state open --head "$SYNC_BRANCH" --json number --jq '.[0].number' | grep -q .; then | ||
| echo "open=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "open=false" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| - name: Sync PR already open — skip | ||
| if: steps.drift.outputs.code == '10' && steps.existing.outputs.open == 'true' | ||
| run: echo "A spec-sync PR is already open; skipping until it is merged or closed." | ||
|
|
||
| # ---- Phase 1: mechanical (deterministic) ---- | ||
| - name: Mechanical commit + push | ||
| if: steps.drift.outputs.code == '10' && steps.existing.outputs.open != 'true' | ||
| run: | | ||
| git config user.name "spec-sync[bot]" | ||
| git config user.email "[email protected]" | ||
| git checkout -B "$SYNC_BRANCH" | ||
| ./scripts/spec-sync/gen-models.sh specs/v1-ade.json specs/_generated/v1_models.py | ||
| git add specs/v1-ade.json specs/_generated/v1_models.py | ||
| git commit -m "chore(spec-sync): update V1 spec snapshot + regenerated reference models" | ||
| git push --force origin "$SYNC_BRANCH" | ||
|
|
||
| # Open the PR now — before the AI step — so a run that dies in phase 2 leaves a visible PR | ||
| # (with just the mechanical commit) instead of an orphan branch. | ||
| - name: Open sync PR | ||
| if: steps.drift.outputs.code == '10' && steps.existing.outputs.open != 'true' | ||
| env: | ||
| GH_TOKEN: ${{ secrets.SPEC_SYNC_TOKEN }} | ||
| run: | | ||
| gh pr create --base main --head "$SYNC_BRANCH" \ | ||
| --title "spec-sync: track V1 spec drift" \ | ||
| --body $'Automated spec-sync PR.\n\n- **Commit 1 (mechanical):** normalized spec snapshot + regenerated reference models.\n- **Commit 2 (AI):** resources/methods/tests/docs wired from the spec diff (added after this PR opened).\n\nGates (surface-lock, contract tests, lint/test/typecheck) must pass. **Human review required before merge.**' | ||
|
|
||
| # ---- Phase 2: AI wiring — edits ONLY. The agent holds no git/push capability, so a | ||
| # prompt-injected spec description cannot route around review to push to a branch. ---- | ||
| - name: AI wiring (edits only) | ||
| if: steps.drift.outputs.code == '10' && steps.existing.outputs.open != 'true' | ||
| uses: anthropics/claude-code-action@v1 | ||
| with: | ||
| anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} | ||
| github_token: ${{ secrets.SPEC_SYNC_TOKEN }} | ||
| prompt: | | ||
| The previous commit updated specs/v1-ade.json and regenerated reference models in | ||
| specs/_generated/v1_models.py. Edit the SDK to match the spec diff: add or adjust | ||
| resource classes, method signatures, param TypedDicts, response models, tests, api.md, | ||
| and README examples, mirroring the conventions in | ||
| src/landingai_ade/resources/parse_jobs.py. Then run `./scripts/format`. | ||
| Rules: PURELY ADDITIVE — never modify or remove an existing public signature (the | ||
| surface-lock CI job will fail the PR). Do NOT run git or push; a later workflow step | ||
| commits and pushes your edits. | ||
| claude_args: | | ||
| --max-turns 40 | ||
| --allowedTools "Edit,Write,Read,Bash(rye *),Bash(./scripts/*)" | ||
|
|
||
| # Deterministic: commit whatever the AI edited and push to the sync branch. This guarantees | ||
| # the AI changes actually reach the PR (the action's automation mode does not push), and it | ||
| # is the only step with push capability. | ||
| - name: Commit + push AI wiring | ||
| if: steps.drift.outputs.code == '10' && steps.existing.outputs.open != 'true' | ||
| run: | | ||
| git add -A | ||
| if git diff --cached --quiet; then | ||
| echo "AI step produced no changes." | ||
| else | ||
| git commit -m "feat(spec-sync): wire SDK to spec diff (AI)" | ||
| git push origin HEAD:"$SYNC_BRANCH" | ||
| fi | ||
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
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
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.