feat(promotion): gate promotion out of an org on a passing check - #66
Open
scott-lowe-vapi wants to merge 3 commits into
Open
scott-lowe-vapi wants to merge 3 commits into
scott-lowe-vapi wants to merge 3 commits into
Conversation
…ater one fails When a promotion failed partway, the workflow's commit step staged only the UUID state, but the failing transition had already rewritten tracked files in its target org, so `git pull --rebase` refused and nothing was pushed — not the state, and not the files of transitions that had already reached the platform. - promote-cmd truncates tmp/promotion-applied.txt at the start of each --apply run and, after each successful apply.ts, appends the paths git reports changed under resources/<target>/ (from git, not the plan: apply's own pull and push can rewrite other files). - On a non-success outcome, the commit step adds the state files plus exactly those paths, commits, then resets and cleans resources/ so the failed transition's rewrites can't block the rebase. - promotionCommandRun(args, deps) takes an injectable child runner, used by the new tests/promote-cmd.test.ts. Refs TEST-141 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eck-gate The promotion check gate needs #65's partial-failure commit step and its promote-cmd test seam. Resolved against the config-free org-connection refactor: the deps seam wraps orgScriptRun. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`orgs.<slug>.check: <name>` in promotion.yml names a vapi-checks.yml
check that must pass in that org before any transition promotes out of
it. The gate runs the same inline check as the PR workflow, built from
the source org's files at the promoted commit, using that org's key from
VAPI_PROMOTION_TOKENS.
- Checks are validated before any transition: the named check must exist
and read and run in the gated org.
- Transitions with no changes skip the gate; plan-only runs print
`check would run <name> in <org> (<n> simulations × <t> targets)`
and run nothing.
- On --apply the check runs after bindings refresh and before
promotionPlanApply writes the target. Any non-pass (failed,
incomplete, build error) throws `Promotion out of <org> blocked: check
<name> <outcome> (<run url>)`, so the target is untouched and earlier
transitions are still committed (previous change).
- A pass is reused for later transitions out of the same org in the same
run, and dropped once a transition applies into that org.
- The "Reconcile configured promotions" step gets timeout-minutes: 90 on
the step, not the job, so the always() commit step still runs.
- Docs: promotion.example.yml and README ("Check before promoting", and a
pointer from "PR Checks").
Refs TEST-141
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 1, 2026
Contributor
Author
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
This was referenced Oct 1, 2026
scott-lowe-vapi
marked this pull request as ready for review
October 1, 2026 23:52
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.

Value
V.A.L.U.E. tier: project — PR 10 of 10 for inline simulation PR checks (TEST-141), the "check before deploy" step in promotion.
promotion.yml. Single-org users are unaffected.promotion.ymlacceptsorgs.<slug>.check: <name>(a slug), naming avapi-checks.ymlcheck.src/promotion-gate.ts:organdrunOrgmust be the gated org, otherwise the run errors.vapi-checks.ymlis required once any org is gated.src/promote-cmd.ts: in each transition, after the plan is built:check would run <name> in <org> (<n> simulations × <t> targets);--applyruns the check (after the bindings refresh, beforepromotionPlanApplywrites anything). Any non-pass throwsPromotion out of <org> blocked: check <name> <outcome> (<run url>).promotionCommandRun(args, overrides)now takesPartial<PromotionDeps>(childRun,checkRun)..github/workflows/promotion.yml:timeout-minutes: 90on the "Reconcile configured promotions" step, not the job, so theif: always()commit step (fixed in fix(promotion): commit the files of transitions that applied when a later one fails #65) still runs after a blocked or slow gate.promotion.example.yml(a commentedcheck:), a README "Check before promoting" section, and a pointer from "PR Checks".Evidence of value
The real gate, run live in the owner's test org on the TEST-141 parity squad.
promotion.ymlgatesparityon checkcore, with pipelineparity → parity-prod.promote --pipeline release --from parity --to parity-prod --apply.apply.tswas recorded but not run. No second org was needed or touched.resources/parity-prod/Promotion out of parity blocked: check core failed (https://dashboard.vapi.ai/simulations/run/7ed19587-…)["parity-prod"]The test org's resource counts were identical before and after both gate runs.
Tests:
npm testgoes from 477 (the merge with #65) to 484 passing.Testing plan
tests/promotion-gate.test.ts(6 tests, real git fixture, injectedchildRun/checkRun):vapi-checks.yml, unknown check, check in another org) stop before anything applies;tests/promotion.test.ts:orgs.<slug>.checkis parsed, and a non-slug is rejected.Stacked on #64, with #65 merged in.
Refs TEST-141
🤖 Generated with Claude Code