✅ Unblocked 2026-09-04T12:45Z (repo:objectos seat, objectstack#9831). #261's fix landed (1933e623) and Deploy Docs run #142 is green, so the ordering constraint is discharged and this card is dispatchable.
❗ Set the budget from the measured figure, not from the projection this card quotes below. Run #142 logged Total Upload: 58541.00 KiB / gzip: 8796.63 KiB — 57.17 MiB, 89.3% of the 64 MiB limit, with ~5.3 MiB of headroom. PR #263's ~83% estimate was a projection scaled from one ratio and it missed by ~4.3 MiB in the unfavourable direction. The "#261 takes the bundle to a projected ~83%" line further down is superseded.
That makes this card more urgent than when it was filed, not less: at 89.3% the next change to cross the line will do it as silently as c243ad79 did.
Filed by the os-dev subagent working #261 (branch claude/issue-261-docs-deploy-worker-size), as an out-of-scope finding. Not assigned, for PM triage.
The gap
.github/workflows/deploy-docs.yml runs pnpm run deploy (opennextjs-cloudflare build && opennextjs-cloudflare deploy) and nothing else. The Worker's uncompressed size is never measured by this repository. The only thing that ever checks it is the Cloudflare API, at upload time, on main, after merge.
That failure is silent in the way that matters. The rejection lands on /versions, i.e. version creation, so the upload simply never becomes a version and the previously accepted Worker keeps being served. Nothing 500s. No page changes. The site just stops moving.
Measured while working #261: this ran 35 consecutive red deploys (runs #106 through #140, 2026-08-25 to 2026-09-02) before anyone filed a card, and the CI build job was green for every one of them — build compiles the Next app, it does not bundle or weigh the Worker. (Run #141 made it 36, from a merge landed before the fix.)
Why it is worth a guard rather than vigilance
The margin was never healthy, and nothing said so. Run #105 — the last successful deploy before the outage — logged:
Total Upload: 62747.87 KiB / gzip: 9207.47 KiB
62747.87 KiB is 95.7% of the 64 MiB limit. The deploy that broke the site was not a regression into an unhealthy state; it was one ordinary commit landing on a bundle that had been one commit from the edge for some time, with no signal anywhere that this was true. A threshold check would have turned red weeks earlier, on a PR, with a diff attached.
Post-fix the bundle sits at 89.3% (run #142, measured). Better, still finite, still unguarded — every page added spends some of it.
Shape of a fix (not prescriptive)
A step in ci.yml after turbo run build that runs opennextjs-cloudflare build, stats .open-next/server-functions/default/apps/docs/handler.mjs plus the sibling files wrangler uploads, and fails above a budget set below 64 MiB — so the red arrives on the PR that causes it rather than on main after merge. Two details worth carrying over from this repo's existing gates: the budget should be a declared constant with the limit written next to it, and the step needs shell: bash if its output is piped, per the pipefail note already in ci.yml.
⚠️ handler.mjs alone is not what wrangler weighs. Post-fix handler.mjs measured 48.47 MiB locally while Total Upload was 58541 KiB — so a budget stated against one file will not track the number Cloudflare enforces. Measure the set that is actually uploaded, and calibrate against a known-good run's Total Upload line.
Worth deciding alongside: whether the deploy job should also assert that a new version ID was created, since "upload rejected" and "upload succeeded" currently look identical from the outside once the step's exit code is the only thing anyone reads. Run #142's log shows the shape to assert on: Current Version ID: 88f1fa3c-….
Verification notes
Related: #261 (the outage this was found under), #263 (the fix), #265 (the sibling gap — behaviour unverifiable in local preview).
Filed by the os-dev subagent working #261 (branch
claude/issue-261-docs-deploy-worker-size), as an out-of-scope finding. Not assigned, for PM triage.The gap
.github/workflows/deploy-docs.ymlrunspnpm run deploy(opennextjs-cloudflare build && opennextjs-cloudflare deploy) and nothing else. The Worker's uncompressed size is never measured by this repository. The only thing that ever checks it is the Cloudflare API, at upload time, onmain, after merge.That failure is silent in the way that matters. The rejection lands on
/versions, i.e. version creation, so the upload simply never becomes a version and the previously accepted Worker keeps being served. Nothing 500s. No page changes. The site just stops moving.Measured while working #261: this ran 35 consecutive red deploys (runs #106 through #140, 2026-08-25 to 2026-09-02) before anyone filed a card, and the CI
buildjob was green for every one of them —buildcompiles the Next app, it does not bundle or weigh the Worker. (Run #141 made it 36, from a merge landed before the fix.)Why it is worth a guard rather than vigilance
The margin was never healthy, and nothing said so. Run #105 — the last successful deploy before the outage — logged:
62747.87 KiB is 95.7% of the 64 MiB limit. The deploy that broke the site was not a regression into an unhealthy state; it was one ordinary commit landing on a bundle that had been one commit from the edge for some time, with no signal anywhere that this was true. A threshold check would have turned red weeks earlier, on a PR, with a diff attached.
Post-fix the bundle sits at 89.3% (run #142, measured). Better, still finite, still unguarded — every page added spends some of it.
Shape of a fix (not prescriptive)
A step in
ci.ymlafterturbo run buildthat runsopennextjs-cloudflare build, stats.open-next/server-functions/default/apps/docs/handler.mjsplus the sibling files wrangler uploads, and fails above a budget set below 64 MiB — so the red arrives on the PR that causes it rather than onmainafter merge. Two details worth carrying over from this repo's existing gates: the budget should be a declared constant with the limit written next to it, and the step needsshell: bashif its output is piped, per the pipefail note already inci.yml.handler.mjsalone is not what wrangler weighs. Post-fixhandler.mjsmeasured 48.47 MiB locally whileTotal Uploadwas 58541 KiB — so a budget stated against one file will not track the number Cloudflare enforces. Measure the set that is actually uploaded, and calibrate against a known-good run'sTotal Uploadline.Worth deciding alongside: whether the deploy job should also assert that a new version ID was created, since "upload rejected" and "upload succeeded" currently look identical from the outside once the step's exit code is the only thing anyone reads. Run #142's log shows the shape to assert on:
Current Version ID: 88f1fa3c-….Verification notes
GET /repos/objectstack-ai/objectos/actions/workflows/deploy-docs.yml/runs?per_page=100(2 pages, 131 runs at filing).Related: #261 (the outage this was found under), #263 (the fix), #265 (the sibling gap — behaviour unverifiable in local preview).