fix(promotion): commit the files of transitions that applied when a later one fails - #65
Open
scott-lowe-vapi wants to merge 1 commit into
Conversation
Contributor
Author
scott-lowe-vapi
force-pushed
the
fix/promotion-commit-applied-transitions
branch
from
October 3, 2026 06:18
308b8d5 to
ef619e1
Compare
scott-lowe-vapi
marked this pull request as ready for review
October 3, 2026 06:19
…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, records every path git reports changed under resources/<target>/ (from git, not the plan: apply's own pull and push can rewrite other files) with its content at that moment, written to git's object store (`-` for a deletion). - On a non-success outcome, the commit step stages the state files plus exactly those recorded blobs, commits, then resets and cleans resources/ so the failed transition's rewrites can't block the rebase. Staging the recorded content rather than the working tree means a later failed transition that rewrote the same file (two pipelines promoting into one org in one --all run) can't replace what was applied. - promotionCommandRun(args, deps) takes an injectable child runner. - tests/promotion-workflow-commit.test.ts runs the workflow's real commit step (read from promotion.yml, run with bash -e) against a bare origin: partial failure, the same-org case, success, and no changes. Refs TEST-141 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scott-lowe-vapi
force-pushed
the
fix/promotion-commit-applied-transitions
branch
from
October 3, 2026 06:28
ef619e1 to
c07fed1
Compare
scott-lowe-vapi
changed the base branch from
graphite-base/65
to
test/promotion-golden-baseline
October 3, 2026 06:28
This was referenced Oct 3, 2026
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 9 of 10 for inline simulation PR checks (TEST-141). This is a fix to promotion, independent of the check stack (cut from
main), and the promotion gate (PR 10) depends on it..vapi-state.*.json, but the failed transition'spromotionPlanApplyhas already rewritten tracked files in its target org, sogit pull --rebaserefuses with "You have unstaged changes".promotion.ymlwith two or more transitions (dev → staging → prod), and anyone who adds the promotion gate in PR 10, where a failing gate is exactly a mid-run failure.src/promote-cmd.ts:--applyrun truncatestmp/promotion-applied.txt(gitignored);apply.ts, it records each pathgit status --porcelain -z --untracked-files=all -- resources/<target>reports (new, modified, deleted and renamed files, by current path), with its content at that moment written to git's object store (git hash-object -w;-for a deletion). These are read from git, not the plan, because apply's own pull and push can rewrite files the plan didn't name;promotionCommandRun(args, deps = { childRun })gains a test seam..github/workflows/promotion.yml: on a non-success outcome, the commit step:git update-index --cacheinfo; deletions withgit rm --cached), and commits. Staging the recorded content, not the working tree, means a later failed transition that rewrote the same file can't replace what was applied;git reset --hard HEAD && git clean -fd -- resources, so the failed transition's rewrites are discarded;On success it's unchanged: it commits all of
resources.improvements.md: refactor(engine): extract shared slug + folder helpers into slug-utils #35, RESOLVED.Evidence of value
Red/green in a scratch repo.
a → b → c, andcstarts with a trackedassistants/intake.yml.promote --all --applyruns this branch'spromote-cmdwith a fake child runner. Apply intobsucceeds (writing state and one extra file, as apply's pull can). Apply intocwrites state, then fails.promotion.yml, runs withPROMOTION_OUTCOME=failure.main(69c7e83)error: cannot pull with rebase: You have unstaged changes.exit 128origin/mainbase)chore: record promoted Vapi state [skip promotion].vapi-state.b.json/.c.jsonon origin{}/{}(state lost)uuid-b/uuid-c(both recorded)resources/b/assistants/intake.yml,pulled-by-apply.ymlresources/c/assistants/intake.ymlresources/c/assistants/pulled-by-apply.ymlgit cleantmp/promotion-applied.txtafter the run held exactly the tworesources/b/...paths.Same-org case: red before the fix, green after. Two pipelines promote into the same org in one
--allrun. The first appliesresources/c/assistants/intake.ymlwith a's content, and the second rewrites that file with b's content and then fails. Before the fix, the committed file was b's (the failed rewrite); after it, the commit holds a's, the content that was actually applied.Tests: on top of #68,
npm testgoes from 476 to 484 passing.Testing plan
tests/promote-cmd.test.ts(3 tests, a real temp git repo, injected child runner):--applyrun starts a fresh record, and a plan-only run leaves it alone;tests/promote-cmd.test.tsalso checks that each applied file is recorded with its content at apply time (git cat-fileof the recorded blob, after a later rewrite), and a deletion as-.New
tests/promotion-workflow-commit.test.ts(4 tests) runs the workflow's real commit step, read frompromotion.ymland run withbash -e, against a clone of a bare origin, after a promotion driven throughpromote-cmd:A later edit to the YAML that brings either bug back fails here.
The existing
tests/promotion.test.ts, and test(promotion): pin the exact files promoting an assistant writes to the target #68's golden promotion test, pass unchanged.Not tested:
Stacked on #68.
Refs TEST-141
🤖 Generated with Claude Code