Skip to content

feat(cloudflare-pages): add verified production rollback - #51

Merged
hseshadr merged 2 commits into
mainfrom
feat/pages-rollback
Sep 25, 2026
Merged

hseshadr merged 2 commits into
mainfrom
feat/pages-rollback

Conversation

@hseshadr

Copy link
Copy Markdown
Owner

Claim: a production deploy that fails its live smoke check is rolled back automatically.

Consumers (aml-filter, almamesh) must change production through this module, so until now a failed post-deploy smoke check could only fail loudly. This adds a module-owned recovery path.

What ships

Function Writes Returns
previous_production_deployment(cloudflare_api_token: Secret, cloudflare_account_id: Secret, project: str) none ProductionDeployment{project, deployment_id, deployment_url}: what production serves now, recorded before deploy
rollback(cloudflare_api_token: Secret, cloudflare_account_id: Secret, project: str, deployment_id: str = "") one non-retried POST /accounts/{a}/pages/projects/{p}/deployments/{id}/rollback ProductionRollbackEvidence{project, from_deployment_id, to_deployment_id, live_deployment_id, live_deployment_url}

One transport: the existing pinned curl + jq container (the module already uses the REST API for project and deployment reads). The jq projection now keeps canonical_deployment.id and projects single-deployment responses.

Fail closed. Refused before any write: no live production deployment; target not in the 10 most recent production deployments; preview target; non-deploy/success target; foreign project id/name/URL; target already live (explicit no-op error); no previous successful deployment when no id is given. After the POST: the response must name the target, then the project is re-read until canonical_deployment equals the target (1/2/4/8 s, 60 s deadline), otherwise does not serve the rollback target. HTTP 4xx/5xx raise a sanitized CloudflareApiError.

Docs: new "Roll back after a failed live smoke check" section in docs/dagger-modules.md (record target, deploy, smoke, rollback on failure, fail the job).

Evidence

Check Result
Red first 35/35 new tests failed against stubs before implementation
Module gate (poe gate: lint, mypy strict, xenon A, pytest+cov, branch rate, schema, pip-audit) 216 passed, 95% total coverage, exit 0
Mutation: skip post-rollback verification 4 tests red (happy path, non-convergence, delayed convergence, deadline)
Mutation: drop canonical_deployment from jq projection real Dagger mock-provider contract red (no live production deployment)
Real curl/jq path mock TLS provider contract runs record → rollback → no-op refusal, exactly one POST
git merge-tree vs #47, #48, #50 clean, no conflicts

Not verified

  • No real Cloudflare rollback was executed (write calls to the real API are out of scope).
  • The read-only helper was not run against the real aml-filter/almamesh projects: no API token is available locally (the wrangler OAuth token expired 2026-07-19).
  • It relies on Cloudflare's canonical_deployment pointing at the rolled-back deployment. That matches the documented project object, but no live run has confirmed it.
  • Verification goes through the API, the same way deploy does. It does not fetch the custom domain; the consumer's smoke check covers that.

🤖 Generated with Claude Code

https://claude.ai/code/session_015oBArfm762nN1r4F4Fst5a

hseshadr and others added 2 commits September 25, 2026 09:59
Add a module-owned rollback so consumers can recover when a production
deploy fails its live smoke check.

- previous_production_deployment: read-only; returns the deployment
  production serves now, to record as the rollback target before deploy.
- rollback: POSTs the documented Pages rollback endpoint over the same
  pinned curl transport, to an explicit id or the previous successful
  production deployment. Refuses preview, unsuccessful, foreign, absent,
  or already-live targets before any write, then re-reads the project
  until canonical_deployment equals the target (1/2/4/8s, 60s deadline)
  and fails closed otherwise.
- The real Dagger mock-provider contract now runs the rollback through
  real curl and the pinned jq projection.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015oBArfm762nN1r4F4Fst5a
…ntract

- Every rollback test now reads Given / When / Then and is named
  test_should_<expected>_when_<condition>.
- Deployment fixture rows are a frozen Row dataclass instead of dicts
  passed through parametrize; magic numbers are named Final constants.
- The public-signature AST check is split into two single-behavior tests.
- _jq_projection is split into _jq_project and _jq_deployment helpers so
  every function is 15 lines or fewer; the emitted jq filter is
  byte-identical.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015oBArfm762nN1r4F4Fst5a
hseshadr added a commit to hseshadr/aml-filter that referenced this pull request Sep 25, 2026
…8c14e

A failed live identity verification (production serving the wrong commit or
bundle after upload) now takes the same path as a red smoke: rollback to the
recorded target, a recovery smoke against the live domain, and one loud
failure. The smoke is skipped once identity fails, so a job rolls back at
most once however many checks fail.

Re-pin both shared modules and CENTRAL_MODULE_SHA to hseshadr/ci#51 head
468c14e (test cleanup only; same module behaviour). Still a draft: re-pin to
the ci merge SHA after ci#51 merges.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015oBArfm762nN1r4F4Fst5a
@hseshadr
hseshadr merged commit 363be0b into main Sep 25, 2026
3 checks passed
@hseshadr
hseshadr deleted the feat/pages-rollback branch September 25, 2026 18:40
hseshadr added a commit to hseshadr/aml-filter that referenced this pull request Sep 25, 2026
Before upload, record the deployment production serves now through the shared
cloudflare-pages module's read-only previous_production_deployment (no upload
if it cannot be read). On a red live smoke: rollback(target) through the same
module (it confirms Cloudflare's canonical deployment is the target), re-run
the fresh smoke against https://aml-filter.com, and fail loudly either way:
recovered, STILL BROKEN after rollback, or automatic rollback refused (roll
back by hand).

Both shared modules and CENTRAL_MODULE_SHA are pinned to hseshadr/ci#51 head
e11bcef (dd19871 + the rollback commits only). Re-pin to the ci merge SHA
after ci#51 merges.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015oBArfm762nN1r4F4Fst5a
hseshadr added a commit to hseshadr/aml-filter that referenced this pull request Sep 25, 2026
…8c14e

A failed live identity verification (production serving the wrong commit or
bundle after upload) now takes the same path as a red smoke: rollback to the
recorded target, a recovery smoke against the live domain, and one loud
failure. The smoke is skipped once identity fails, so a job rolls back at
most once however many checks fail.

Re-pin both shared modules and CENTRAL_MODULE_SHA to hseshadr/ci#51 head
468c14e (test cleanup only; same module behaviour). Still a draft: re-pin to
the ci merge SHA after ci#51 merges.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015oBArfm762nN1r4F4Fst5a
hseshadr added a commit to hseshadr/almamesh that referenced this pull request Sep 25, 2026
…ages module

DRAFT: re-pin to the ci merge SHA after hseshadr/ci#51 merges.

Pins cloudflare-pages and foundation to hseshadr/ci#51 head e11bcef (one
central SHA, as the contract requires). Before the upload, deliverProduction
records the live production deployment via previousProductionDeployment.
After verifyLive it runs @fresh and @returning (returning uses that
deployment's URL). On failure it calls the module's rollback with that id,
checks the evidence (from = this release, to = live = the baseline), re-runs
@fresh against almamesh.com to confirm recovery, and fails loudly with the
failed pass, both ids, and the recovery result.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015oBArfm762nN1r4F4Fst5a
hseshadr added a commit to hseshadr/almamesh that referenced this pull request Sep 25, 2026
ci#51 (cloudflare-pages rollback + previous_production_deployment) is
merged as 363be0b98c753c027353f35db0f6cc5b24402f78 on ci main, which
contains dd19871 (greenMain rerun skew). Between e11bcef and 363be0b the
pinned modules changed only by 468c14e, a behaviour-preserving refactor
of the cloudflare-pages jq projection. Contract pins updated together.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015oBArfm762nN1r4F4Fst5a
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