Skip to content

feat(promote): pin assistants around the apply so a rollout org ramps instead of cutting over - #87

Open
scott-lowe-vapi wants to merge 1 commit into
gradual-deploy-rollout-configfrom
gradual-deploy-rollout-pin-around-apply
Open

scott-lowe-vapi wants to merge 1 commit into
gradual-deploy-rollout-configfrom
gradual-deploy-rollout-pin-around-apply

Conversation

@scott-lowe-vapi

@scott-lowe-vapi scott-lowe-vapi commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Support partial GitOps promotions using traffic splitting

Part of DEPLOY-205. 2 of 5: promote into a rollout org pins, publishes at 0%, then takes step 1.

What changes

transitionRun (src/promote-cmd.ts), via src/rollout-promote.ts, for changed assistants that already exist in the destination:

  1. Preflight, after if (!apply) return false and before the check: gate, so a refusal costs no simulation run and writes nothing. It refuses a split set outside gitops (a manual split wins) and a Vapi for CX lock, and skips assistants that 404 or have no version. If a gate ran, it runs again after the gate and before promotionPlanApply, because the gate can take 300 minutes while the ticker or an abort writes.
  2. Pin every assistant to the version serving now, with compare-and-set. Any pin that a fresh read and one retry can't land undoes the others and stops before the apply child runs, so nothing is ever published unpinned.
  3. Apply child, unchanged. A publish under an explicit allocation appends nothing, so the new version lands at 0%.
  4. In a finally: step 1 where a new version was published, or undo the pin (writing back exactly the row it replaced) where nothing was. An abort that landed meanwhile is left alone. The applied paths are recorded before any rollout error is thrown, so committed state always has its files.

Also:

  • Deletes are never pinned, and an assistant with no state UUID goes live on create.
  • Unsplit changes (tools, squads, structured outputs) are listed.
  • An assistant the apply published without a pin is reported.
  • --no-rollout publishes at once and releases gitops holds on the assistants it published, which is the revert path.
  • --rollout-override (single transition only) pins a manual split on its largest share, never the canary.
  • Each write carries a gitops-rollout … marker (src/rollout-marker.ts), the ramp's only state.

Evidence

tests/promote-rollout.test.ts drives promotionCommandRun against an in-memory allocation API with the platform's publish semantics (tests/rollout-fake.ts). It covers:

  • the write order (pin, then publish with no system follow-latest row, then step 1);
  • an apply that fails after one publish;
  • dedup undo with no targets;
  • preflight refusals before the gate;
  • override on the largest share;
  • a pin failing on assistant 2 of 3;
  • a race retry;
  • abort during the apply;
  • a split, and separately an abort, written during the gate;
  • deletes, creates and 404s;
  • the --no-rollout variants;
  • the unpinned-publish report.

631 tests pass.

🤖 Generated with Claude Code

… instead of cutting over

Into an org with rollout:, promote now pins each changed assistant to the
version it serves, so the apply's publish lands at 0%, then writes the first
ramp step (or undoes the pin when nothing was published). Pins are written
before the apply child and resolved in a finally, so a failed apply never
leaves an assistant stranded at 0% and never publishes one unpinned. A split
set outside gitops or a Vapi for CX lock is refused up front; preflight runs
again after a gate. --no-rollout publishes at once and releases gitops holds;
--rollout-override pins over a manual split on its largest share.

Part of DEPLOY-205.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

scott-lowe-vapi commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor Author

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.

1 participant