worker: deploy account-directory without R2 binding until the subscription is enabled - #1126
Merged
Conversation
…ption is enabled R2 is not enabled on the Cloudflare account. Creating either diagnostics bucket fails with "Please enable R2 through the Cloudflare Dashboard [code: 10042]" — from the REST API and from the account's own S3 endpoint, which rejects the TLS handshake outright. That is an account-level product enablement, not a token scope: the same token lists Workers, reads D1, and deploys Pages. A Worker bound to a bucket that cannot exist fails to start, so shipping wrangler.jsonc as-is would take down machine registration, pairing, and heartbeat for every user in order to deliver a route nobody can reach yet. Leaving DIAGNOSTICS unbound is the degradation the code was written for: `DiagnosticsEnv` types it `DIAGNOSTICS?: R2Bucket`, /diagnostics/upload answers 503, and the in-app "Send to ADE" button reports that sending is unavailable. Every other route deploys unchanged — including the two-phase pairing-grant reservation, supersede-by-hardware-anchor dedup, and refusal logging that production is currently missing. Both `r2_buckets` blocks are preserved verbatim in comments next to the re-enable steps, so restoring them is a revert plus bucket creation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
arul28
added a commit
that referenced
this pull request
Aug 19, 2026
…rkers (#1131) * skill(release): own every Cloudflare surface, not just "deploy the changed Workers" The old Phase 5.5 knew one failure mode: a changed Worker sitting undeployed. It was blind to the class that actually bit us twice this week, because that class lives in account state and no test in this repo can fail on it. A Cloudflare surface has two halves and only one is in git. The code half is wrangler.jsonc plus src/. The account half — whether the R2 bucket exists, whether it carries a lifecycle rule, whether a D1 migration was applied, whether a secret is bound for that environment — lives in the Cloudflare account and in no repository file. Phase 5.5 is now a 7-step phase: scope (verify the deploy-web.yml run for the release SHA — CI already deploys these, so the normal case is verification, not deployment), a factual per-surface binding inventory, a bindings-vs-account drift preflight, an un-codeable account-state checklist, guarded deploy, per-surface post-deploy verification, and rollback/blocker discipline. Three incidents cited: R2 not enabled on the account (#1126/#1127), buckets created without lifecycle rules, and the 2026-08-06 pending-migration/unbound- secret 500s. All three shipped green /health. Sharpened throughout: /health is the floor, not the check — this repo has four health endpoints proving four different things and not one of them checks migration state. Every command verified against the pinned wrangler 4.105.0, and the R2 public-access check uses `r2 bucket dev-url get` / `domain list`, which actually report posture, rather than `bucket info`, which does not. State schema gains a cloudflare section; the final report now requires a per-surface decision and verification line. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * review: address CodeRabbit — deployment-derived Cloudflare baseline, honest coverage gaps - Scope no longer keys off the desktop tag. Each surface's baseline is the newest deploy-web.yml run whose job for that surface concluded success; UNKNOWN means reconcile, never "unchanged". Desktop-tag diff demoted to a secondary signal. Verified live against the repo (webhook/tunnel/push-relay currently resolve UNKNOWN — exactly the drift the old check hid). - RUN_ID is now resolved from the release SHA and fails loudly with no match, instead of being referenced unassigned. - Wrangler invocations wrapped in their owning app directory after npm ci; the repo-root Pages deploy pinned to 4.105.0, with CI's floating wrangler@4 called out rather than papered over. - R2 public-access posture checked for both diagnostics buckets, not just production. Verified live: both report dev URL disabled, no custom domains. - push-relay gains a read-only sqlite_master trigger check — d1 migrations list is blind to the attention_triggers.sql sidecar. Verified live against remote D1; both triggers present. - account-directory preflight's real coverage stated: 1 of 4 secrets, 1 of 4 vars, /health checks none of them. Unconfirmed values get named, not implied. - Rollback stated as an attempt (refused on missing bindings or DO lifecycle changes) with fix-forward defined. Pages has no rollback subcommand — verified on 4.105.0; record the deployment id and restore via dashboard. - accountState replaced aggregate booleans with evidence keyed by resource and environment; final report updated to match. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * skill(release): tag only a commit with a green ci-pass release-core's verify job is fail-closed on the tagged SHA having a green ci-pass check run. Merging the release-docs PR and tagging immediately hits it every time — the squash-merge creates a new commit on main whose CI has not started. This just cost the v1.2.62 run. Documents the wait, and the recovery: the tag is still correct and nothing was published, so wait for ci-pass on the same SHA and rerun only the failed job rather than deleting or moving the tag. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.
Why
Phase 5.5 of the v1.2.61 release could not deploy the account-directory Worker. Creating the diagnostics buckets fails:
This is account-level product enablement, not a token scope. The same API token lists Workers scripts, reads Worker settings, queries D1, and deployed Pages successfully. Independently, the account's own S3 endpoint (
<acct>.r2.cloudflarestorage.com) rejects the TLS handshake withSSLV3_ALERT_HANDSHAKE_FAILURE— the R2 endpoint is not provisioned at all. Enabling it requires adding the R2 subscription in the dashboard, which is a billing decision for the account owner.A Worker bound to a bucket that cannot exist fails to start. Deploying
wrangler.jsoncas-is would take down machine registration, pairing, and heartbeat for every user in order to ship a route nobody can reach yet.What
Comment out both
r2_bucketsblocks (top-level andenv.production), leaving the exact bindings and the re-enable steps inline.This is the degradation the code was already written for:
DiagnosticsEnv = Env & { DIAGNOSTICS?: R2Bucket }— the binding is optionalPOST /diagnostics/uploadanswers503 {"error":"diagnostics upload unavailable"}when unboundEvery other route deploys unchanged. That matters here: production is currently running #1115-era code against the 0008 schema, so two-phase pairing-grant reservation, supersede-by-hardware-anchor dedup, and refusal logging are not live. This unblocks them.
Verification
npm test— 145 passed / 7 files. Nothing assertsr2_buckets;verifyDeploymentConfig.test.tsreads the committedwrangler.jsoncand still accepts it.npm run verify:deploy-config -- productionand-- default— both complete (DIRECTORY_AUTH_SECRET+PUSH_RELAY_URLconfigured).npm run build:productiondry-run — 121.17 KiB / 28.30 KiB gzip; resolved bindings areDB,ONLINE_WINDOW_MS,WEB_CLIENT_ORIGIN,PUSH_RELAY_URL, with no R2 entry.Re-enable path
Once the R2 subscription is added: revert this commit, create
ade-diagnostics+ade-diagnostics-production, add the 90-day expiry lifecycle rule to both (nothing in the Worker ever deletes a report), redeploy, then run the fail-closed probes (garbage Bearer → 401, >512 KiB → 413).🤖 Generated with Claude Code