Skip to content

Pause migrate for a release approval and let rollback run without one (S-008-13, S-008-14) - #182

Merged
shipmill-romamo[bot] merged 1 commit into
mainfrom
feat/165-migrate-approval-pause
Oct 31, 2026
Merged

shipmill-romamo[bot] merged 1 commit into
mainfrom
feat/165-migrate-approval-pause

Conversation

@shipmill-romamo

Copy link
Copy Markdown
Contributor

Summary

migrate's deploy:<component> step called lifecycle.deploy directly, so after #178 a manual application was gated through cloudfall deploy but not through cloudfall migrate. Now the step runs the same approval check deploy --yes runs (deployment_approval in release_approval.py, shared by both):

  • No approval that counts: the run pauses at deploy:<component> (deployment_approval_missing joins _PAUSE_CODES). migrate --yes exits 3 PARTIAL_FAILURE with context.code: deployment_approval_missing, the built release and the exact cloudfall release approve command, and the message names both
  • After release approve, migrate --yes resumes at that step with the release the completed build:<component> step recorded. It does not build again, the deploy is pinned to the approved archive digest (approved_sha256), and the step's detail gains approval (the record it went on). The resumed run no longer needs --build for a component whose build step the saved plan already completed
  • A broken approvals file stops the step and migrate exits RECORD_INVALID, as deploy does. migrate gains --approvals (default decisions/releases), which cloudfall mcp serve already binds by name, so the migrate tool can't name its own folder
  • rollback needs no approval: its plan gains approval, {"policy": "manual", "required": false, "reason": ...} for a manual application and {"policy": "automatic"} otherwise

Docs: README.md, sdk/README.md and docs/render-migration-guide.md show the release approve step for the manual application in their migrate --yes and deploy --yes examples. This is the last build issue of S-008, so the spec is set to status: built, with one Verification line per criterion.

Plan

  • Serves: G-2
  • Files: sdk/src/cloudfall/migrate.py, sdk/src/cloudfall/release_approval.py, sdk/src/cloudfall/agent_tools.py, sdk/src/cloudfall/app.py, sdk/src/cloudfall/commands.py, sdk/tests/test_migrate_approval.py, sdk/tests/test_agent_server.py, README.md, sdk/README.md, docs/render-migration-guide.md, docs/specs/008-enforce-manual-approval.md, changelog.d/165-migrate-approval-pause.md
  • Order: the spec's Behaviour, "migrate, rollback and restart": gate migrate's deploy:<component> runner with the same approval check deploy uses and pause on deployment_approval_missing; resume deploys the release the completed build: step recorded, pinned to the approved digest; rollback's plan gains approval (required: false and a reason for manual); then the docs, and the spec's Verification
  • Proof: S-008-13 and S-008-14 tests in sdk/tests/test_migrate_approval.py; specs.py check and specs.py coverage --spec 008

Test notes

  • New sdk/tests/test_migrate_approval.py, run through the CLI against a copy of the example project without its domains. The engine is a stand-in whose playbooks run ansible-playbook on localhost and only leave markers and a receipt. The artifact is built for real by cloudfall-engine artifact build from a local git repository
    • test_s008_14_migrate_pauses_for_an_approval_and_resumes_without_a_rebuild: exit 3 PARTIAL_FAILURE at deploy:crm-backend with the built release and command, and no deploy playbook. After release approve the source repository is deleted, so a second build couldn't succeed; migrate --yes completes deploy:crm-backend with the recorded release and the record, and the artifacts folder is unchanged
    • test_s008_14_a_broken_approval_record_stops_the_deploy_step (exit 86) and test_s008_14_an_automatic_application_migrates_without_a_record
    • test_s008_13_rollback_needs_no_approval (manual and automatic): the plan's approval, then rollback --yes runs with no approvals folder at all
  • test_s008_5_the_deploy_tool_refuses_an_unapproved_manual_release also checks that the migrate MCP tool takes no approvals argument
  • Run locally and passing: uv run ruff check ., uv run mypy, uv run pytest sdk/tests/test_migrate_approval.py sdk/tests/test_migrate.py sdk/tests/test_deploy_approval.py sdk/tests/test_release_approval.py sdk/tests/test_agent_server.py sdk/tests/test_commands.py sdk/tests/test_app.py sdk/tests/test_lifecycle.py sdk/tests/test_data_migration.py sdk/tests/test_data_copy_preflight.py sdk/tests/test_owned_postgresql_converge.py, and uv run pytest engine/tests. specs.py check and specs.py coverage --spec 008 pass. The full suite is left to CI
  • Seen but not changed: with the stand-in engine, a resumed run goes past the deploy step to audit, which crashes with HANDLER_CRASHED (ConfigValidationError) when tmp/observed holds no observations. The tests read the saved plan for that reason. This predates this change

Stacked on #178

Fixes #165
Fixes #106

🤖 Generated with Claude Code

… (S-008-13, S-008-14)

migrate's deploy:<component> step called lifecycle.deploy directly, so a
manual application was gated through cloudfall deploy but not through
cloudfall migrate. The step now runs the same approval check deploy --yes
runs: without an approval that counts the run pauses there with
deployment_approval_missing, the built release and the release approve
command, and a resumed migrate --yes deploys the release the completed
build step recorded, pinned to the approved archive digest, without
building again or needing --build again. A broken approvals file stops the
step with RECORD_INVALID, and migrate gains --approvals, bound by mcp serve.

rollback needs no approval; its plan says so with approval.required false
and a reason. The README, the SDK README and the Render migration guide
show the release approve step, and spec 008 is built with its
verification.

Fixes #165
Fixes #106

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

Copy link
Copy Markdown
Contributor Author

@romamo Decided without waiting: a resumed migrate --yes no longer needs --build for a component whose build step the saved plan already completed, and migrate gains --approvals (bound by mcp serve) and exits RECORD_INVALID on a broken approvals file, as deploy does.

Why: S-008-14 says plain migrate --yes resumes after release approve without rebuilding; a stale plan is still refused with PLAN_STALE, and the deploy stays pinned to the approved archive digest. The review found no path around the approval.
Not taken: keeping the migrate_component_unbuilt refusal on resume, which would make S-008-14's resume fail unless --build is repeated.

Reply with a comment starting with veto before 2026-10-31 15:55 UTC to reverse it, adding what you want instead if you like. A later veto still counts and gets a follow-up.

@shipmill-romamo shipmill-romamo Bot added the decided An agent decided at notify; the maintainer may veto label Oct 30, 2026
@shipmill-romamo

Copy link
Copy Markdown
Contributor Author

On hold: stacked on #178, which waits on the maintainer's decision about its Breaking changelog entry, decided in #178. Reviewed: MERGE, CI green; it lands right after #178.

Base automatically changed from feat/163-enforce-manual-approval to main October 31, 2026 07:43
@shipmill-romamo
shipmill-romamo Bot merged commit 409b2a8 into main Oct 31, 2026
1 check passed
@shipmill-romamo
shipmill-romamo Bot deleted the feat/165-migrate-approval-pause branch October 31, 2026 07:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

decided An agent decided at notify; the maintainer may veto

Projects

None yet

0 participants