You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
docs deploy is red: llms-full.txt reached 20 MB, Vercel's cap for an ISR page, so every production docs deploy now fails #23625
Error: ISR page llms-full.txt is 20.00 MB, over the 20 MB limit, and will fail at runtime (FALLBACK_BODY_TOO_LARGE). Reduce its size and redeploy, or set VERCEL_BYPASS_FALLBACK_OVERSIZED_ERROR=1 to deploy anyway.
apps/docs/app/llms-full.txt/route.ts concatenates getLLMText over every page in source.getPages() and declares revalidate = false. Vercel stores it as a prerendered ISR fallback, and a fallback body is capped at 20 MB. The docs content has grown past that cap.
Impact
Production docs stay on the last successful deploy (2026-10-14 18:51 UTC). Every run, 4 a day, will fail at the same step until this is fixed, so no docs change reaches objectstack.ai in the meantime.
The failure is loud by design: the workflow goes red instead of skipping. Nothing silently stops publishing.
Options (pick one)
Serve it as a static asset rather than an ISR page: write llms-full.txt into the build output at build time (for example public/ or a build-step script), so it is not subject to the 20 MB ISR fallback cap.
Shrink it: drop sections an LLM reader does not need, or split it into per-section files linked from llms.txt.
Bypass with VERCEL_BYPASS_FALLBACK_OVERSIZED_ERROR=1. Not recommended: Vercel says the page "will fail at runtime".
Option 1 or 2 is the real fix. Content keeps growing, so a fix that only gets the file just under 20 MB will break again.
Read fresh. docs-deploy.yml has failed at Vercel's upload step since its 01:07Z scheduled run. apps/docs/app/llms-full.txt/route.ts is stored as an ISR fallback, and the page has reached Vercel's 20 MB cap (FALLBACK_BODY_TOO_LARGE). Production docs are frozen at the 2026-10-14 18:51Z deploy, and every run (4 a day) fails the same way.
priority:p1: no docs change reaches objectstack.ai, during the 18 prerelease line, when the upgrade notes and the retirement prescriptions are what readers need.
Lane domain:devx (apps/docs, by the domain table).
Direction: option 1 (a static build artifact, out of the ISR fallback cap) or option 2 (split per section, linked from llms.txt), the seat's call.
Measure first how far the file sits over the cap, and how fast it grew, so the fix does not break again as content grows. The card itself says a fix that only lands just under 20 MB will break again.
⛔ Not option 3 (the bypass flag). Vercel says that page fails at runtime.
Done: a green scheduled docs-deploy.yml run, with llms-full.txt (or its parts) served in production.
Claim: PM loop round 2
Session: session_01ESFcvW3staenyBCGq7kcDH
Account: huangyiirene (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-23625-llms-full-static
Worktree: objectstack-issue-23625
Domain: domain:devx
Seat: domain:devx#1
File surface: apps/docs/** — the llms-full.txt route (apps/docs/app/llms-full.txt/route.ts), plus whatever build-time producer replaces or splits it inside apps/docs (its package.json scripts, a scripts/ file under apps/docs, public/ output rules, next.config.*), and llms.txt only if a split links the parts from it (stop on breach; explain in the report). ⛔ Not .github/workflows/** (governed; a needed workflow edit is a reported fork, not a hard-done change), and ⛔ not the bypass flag.
Container & model: M, mode:subagent, model: TIER_DEFAULT (dispatch-gates --tier on the surface: "no path-derived mandate", the PM's per-card call)
Clause-②: no
Responsibility: apps/docs/app/llms-full.txt/route.ts (this lane's docs app) produces the oversized ISR fallback | no platform path covers it: the deploy fails loudly at Vercel's upload step and production stays on the 2026-10-14 18:51Z deploy | every objectstack.ai docs reader and every agent fetching llms-full.txt reaches it today (scheduled runs 38663939159 and 38695971788 red)
Thread-read: 6186351941
Serial constraints cleared: none — no open PR's file list touches apps/docs/{app,lib,next*,package*,source*,vercel*} or docs-deploy (8 open PRs read at this claim); no claude/issue-23625* branch exists.
os-dev-report
{
"issue": 23625,
"status": "done",
"branch": "claude/issue-23625-llms-full-static",
"pr": "#23640",
"session": "session_01ESFcvW3staenyBCGq7kcDH",
"premise_still_valid": true,
"summary": "Option 1. apps/docs/app/llms-full.txt/route.ts (an ISR prerender) is retired, and apps/docs/scripts/build-llms-full.mjs (with llms-full/resolve-hooks.mjs) now writes public/llms-full.txt during the docs build, after the spec generators and before next build. It imports lib/source.ts through fumadocs-mdx's Node loader, so the text still comes from getLLMText over source.getPages(): one producer, same URL, same text/plain type, same X-Robots-Tag noindex. Measured: 20,032,800 bytes over 418 pages at e611e2b, 20,040,773 with main 23e689d merged in. Vercel's cap is decimal 20,000,000 bytes: the last green deploy commit 989aa48 measured 19,998,748 and the first red, 252775d, 20,004,539. Growth: 8,046,416 bytes on 2026-08-25, 14,632,088 on 09-14, 16,787,480 on 09-30, 17,357,732 on 10-07, about 12.0 MB in 50.6 days (1.66 MB/week); the last 30 days averaged 1.25 MB/week, the last 7 days 2.57 MB/week. About 66% of the bytes are auto-generated references/** pages and 9.4% build-time protocol-upgrade pages. Headroom: the 20 MB ISR cap no longer applies to this file, and the largest remaining prerender is 3,916,137 bytes (protocol-upgrade/18.html, 19.6% of the cap). The only static-file figure found is an unverified search excerpt of Vercel's limits page, 100 MB (Hobby) / 1 GB (Pro). At 100 MB that is about 31-64 weeks at the measured rates. Before merge this PR proves: the production buildCommand writes the file and fails without it; the route is gone from the prerender table; next start serves /llms-full.txt 200 with byte-equal content and the same headers. Vercel's acceptance of the upload (the card's Done) is NOT MEASURED until a scheduled or dispatched docs-deploy.yml run after merge.",
"tests": "(1) e611e2b: NODE_OPTIONS=--max-old-space-size=4096 next build (lock VERDICT command-exit 0, 208s). Route body .next/server/app/llms-full.txt.body = 20,032,800 bytes, sha256 cbbf6f782618778397483dcbea544cc1bc1c144d002e409807e7d2bae1986d0b. The generator prototype (same getLLMText logic) on the .source that build left produced the same sha256 (cmp exit 0). (2) 40af083: apps/docs/vercel.json buildCommand (pnpm turbo run build --filter=@objectstack/docs) from a fresh-checkout state (no .next; .source regenerated without protocol-upgrade; protocol-upgrade dir deleted). Lock VERDICT command-exit 0, turbo Tasks 2 successful, 0 cached. Printed build-llms-full: 418 pages, 20,032,800 bytes; the route table no longer lists /llms-full.txt. (3) Same-content proof (scratch script, not committed): public/llms-full.txt is PERMUTATION-EQUAL 418/418 against the per-page /llms.mdx bodies Next prerendered in that same build, in llms.txt order. The e611e2b route body is also PERMUTATION-EQUAL. A one-byte mutant FAILs at offset 9886232 (exit 1). Order differs only because fumadocs-mdx writes .source in a different order on every run: 4 runs on one tree gave 4 index hashes. (4) next start -p 38617 on that build: GET /llms-full.txt 200, Content-Type text/plain; charset=UTF-8, X-Robots-Tag noindex, Content-Length 20032800, cmp-equal to public/llms-full.txt. /llms.txt 200, /docs/ai/agents.mdx 200. Server PID tree killed by recorded PID. (5) d777181: generator rerun printed 418 pages, 20,032,800 bytes; PERMUTATION-EQUAL 418/418. (6) f7ce0dd (final head): pnpm install + gen:schema + gen:docs + gen:upgrade-guide + build-llms-full (418 pages, 20,040,773 bytes) + pnpm typecheck in apps/docs (fumadocs-mdx && next typegen && tsc --noEmit; Types generated successfully). Lock VERDICT command-exit 0. (7) Growth (A3): the same generator ran in a throwaway detached worktree at 0d91eaa, 8c657f7, 05be352, 51290bc, 989aa48 and 252775d, each after pnpm install and the three spec generators. Every run exited 0 under the lock; the worktree was removed afterwards. No ablation: no tested behaviour changed (the app has no test suite), and the proofs above are one-shot.",
"gates": "Local, at final head f7ce0dd: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 38 commands (the same 38 at d777181). Each was run with its exit captured before any pipe; all 38 exited 0. dispatch-gates --ran on the exit-annotated record: 38 derived famil(ies) accounted for, 38 run, 0 NOT-MEASURED (a DERIVED zero; all 38 recorded an exit code). The set includes pnpm --filter @objectstack/spec run check:llms-txt (98 claims re-derived), check-doc-frontmatter + --self-test, check-docs-nav-label + --self-test, check:docs-locale-catch-all, check:published-files, check:turbo-task-graph, check:test-source-alias, check:nul-bytes (11564 files, no raw control bytes), check:watch-hint-literal and check-undeclared-dep-imports. A5: check:llms-txt reads packages/spec/llms.txt (TARGET, line 177), not the docs site's /llms.txt or /llms-full.txt, so it is unaffected. The 14 pending-changeset families do not apply: @objectstack/docs is private, so the PR carries skip-changeset and no changeset. CI check-runs on PR head f7ce0dd at report time. Success: Check Changeset (the labelled rerun; the opened run skipped), Governed Surface Queue Guard, Test Core, Type Check debt ledger, Type Check workspace, Validate Package Dependencies, Check Documentation Links, Dogfood Regression Gate, Check PR Size, filter, the three claim/closing-keyword guards. In progress: Build Docs (the CI half of the build proof), all eight Lint jobs, Type Check consumer gates, Type Check source gates. Skipped by filter: Build Core. Not waited on, per the dispatch contract.",
"mcp_calls": "0",
"api_writes": "3 relay strokes, each one POST /repos/objectstack-ai/objectstack/dispatches by this session, executed by fleet-write.yml as objectstack-fleet[bot]. (1) pr_create, run 38705901758: POST /repos/objectstack-ai/objectstack/pulls (draft, 10895 bytes sent and stored identical) + POST /repos//issues/23640/assignees (huangyiirene). (2) labels_add via scripts/pm/label-write.mjs, run 38706014256: POST /repos//issues/23640/labels (skip-changeset; read-back MATCHES). (3) This report: POST /repos//issues/23625/comments. Plus 4 git pushes (not REST). Reads were single-card or single-run GET via gh api.",
"line_budget": "+184 / -12 = 196 changed lines in 6 files (dispatch-gates count vs merge base 23e689d), under the 3000 human-merge threshold. No skills/, .claude/ or governed path touched.",
"files_changed": [
"apps/docs/.gitignore (new)",
"apps/docs/app/llms-full.txt/route.ts (deleted)",
"apps/docs/next.config.mjs (comment)",
"apps/docs/package.json (build script)",
"apps/docs/scripts/build-llms-full.mjs (new)",
"apps/docs/scripts/llms-full/resolve-hooks.mjs (new)"
],
"deviations": [
"A1: the failing step's log text is NOT MEASURED. The job-log download redirects to productionresultssa1.blob.core.windows.net, which the container proxy refuses (403 on CONNECT). What was measured: step 13 'Deploy the prebuilt output to production' failed, and steps 11 (build) and 12 (output assertion) succeeded, in run 38695971788's job steps. Annotations carry only 'Process completed with exit code 1.'",
"A4: verified from @vercel/next 15.0.1 source (the builder vercel@60.1.3 pins). getStaticFiles() globs public/** after the build command and emits static outputs; FALLBACK_BODY_TOO_LARGE is absent from the CLI dist, so the check is server-side. Not verified with a real vercel build or deploy (no VERCEL_TOKEN here), and not against Vercel's docs: vercel.com answers proxy 403 and docs.vercel.com does not resolve. The static-file limit figure is an unverified search excerpt.",
"Byte identity: the exact-hash proof (sha256 match) is for the generator prototype (identical getLLMText logic) reading the .source left by the same next build, at e611e2b. The final script regenerates .source itself, and fumadocs-mdx's codegen order differs on every run, so its proof is PERMUTATION-EQUAL (same 418 page bodies, same bytes, order per build), not one fixed hash. The old route's order varied per build in the same way. Within one build, llms-full's order no longer matches llms.txt's.",
"The full production buildCommand ran at 40af083. d777181 changed only comments, an error message and the print of out-of-app paths. At d777181 and at the final f7ce0dd (main merged in), I reran the generator, permutation proof, typecheck and all 38 gates, not the whole next build. CI Build Docs runs the full command on the final head.",
"Commit trailers use the model-free pair AGENTS.md mandates (Claude-Session + Co-authored-by: Claude), and the PR body ends with AGENTS.md's session-URL footer. The harness attribution reminder asked for a model-named Co-Authored-By and a different PR footer; AGENTS.md and the role file outrank it.",
"origin/main (23e689d, 3 commits, no overlap with this diff) was merged in before the PR opened, per AGENTS.md Multi-agent section 10."
],
"open_questions": [
{
"question": "Should turbo.json's @objectstack/docs#build outputs list public/llms-full.txt? Today a turbo cache-hit replay restores .next/** without the file. turbo.json is outside this card's claimed file surface, so I left it.",
"options": [
"A. Leave it. Measured reach: docs-deploy.yml and the ci.yml Build Docs gate both set TURBO_FORCE, and the deploy runner has no turbo cache or remote-cache credentials, so neither production path can replay; only a local cached pnpm turbo run build --filter=@objectstack/docs can. Cost: zero now, plus a latent hole if remote caching is ever enabled.",
"B. Add apps/docs/public/llms-full.txt (package-relative: public/llms-full.txt) to that task's outputs in turbo.json, either as a one-line widening of this PR's surface or as a seat follow-up. Cost: one line, no new gate. It makes the declared outputs equal the real ones."
],
"recommendation": "B. Actual business need: no production path is hit today (measured above), so this is not urgent; it does stop an agent who enables remote caching from shipping a deploy without llms-full.txt and getting no error. Long-term soundness: a task's declared outputs should be its real outputs; A is a latent workaround. Preventing AI mistakes: B removes a trap where a cache hit silently drops a file the build claims to produce. Startup focus: one config line, no new gate, no new capability. Taken together B wins, and its cost is negligible."
}
],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted, not filed — fumadocs-mdx 15.2.3 writes the .source index in a different order on every run (4 runs on one tree gave 4 index hashes), so /llms.txt and /llms-full.txt page order changes per build, as it did before this PR. No consumer reads the order; it is in the PR's Acceptance notes.",
"carrier: none (承接者:无) · noted, not filed — the next-largest prerender against the same 20 MB fallback cap is /docs/protocol-upgrade/18, at 3,916,137 bytes of HTML; its source page is 1.46 MB of markdown and grows with each 18.x upgrade entry. Headroom observation only.",
"carrier: none (承接者:无) · noted, not filed — next dev no longer serves /llms-full.txt until a build or node scripts/build-llms-full.mjs writes it; the old route rendered it on demand in dev. Production is unaffected. In the PR's Acceptance notes."
]
}
The reason is the dev's open_questions[0] option B. The PR introduces that build artifact, so its declared outputs must equal its real ones. Without the line, a cache-hit replay would deploy without /llms-full.txt and print no error. Both production paths set TURBO_FORCE today, so the gap is latent, not live.
Serial check: no open PR touches turbo.json (all open PRs' file lists read at this addendum).
os-dev-report
{
"issue": 23625,
"status": "done",
"round": "patch round 1 (open_questions[0] option B taken into PR #23640)",
"branch": "claude/issue-23625-llms-full-static",
"pr": "#23640",
"head": "b7c786c22bf4d82c6889ab13d91e71ec6e909494",
"session": "session_01ESFcvW3staenyBCGq7kcDH",
"premise_still_valid": true,
"summary": "turbo.json now declares public/llms-full.txt as an output of the existing @objectstack/docs#build override; outputs are now dist/, json-schema/, .next/, !.next/cache/ and public/llms-full.txt. That was the only turbo.json change (1 line), in commit b7c786c. Choice measured: the dispatch said the docs app uses the generic build task, but origin/main 23e689d turbo.json already carries a @objectstack/docs#build override at line 37. Package-scoped overrides are the repo's convention: check:turbo-task-graph judges 46 package-scoped and 9 generic tasks. So the output went into that override, and the generic build task is untouched. Measured with turbo --dry=json, before vs after: the global hash and the task hashes of core, metadata-core, refd-timer-testkit, skills, spec and types are identical; only @objectstack/docs#build changed (71b4359b35d85a0a to 7dfad10147e2df6a). Replay proof (step 3): with the file deleted, a cache-hit run (no TURBO_FORCE) restored public/llms-full.txt byte-identical (sha256 01178364f1437c6e38a6af567cd184e9c4e9a309a91d6e42fae7894f8327caed) in 3 s. CI on b7c786c is all green, including Build Docs and the five required contexts. PR body not edited, per instruction; two of its lines are now stale (see open_questions).",
"tests": "Replay proof under the lock, at b7c786c. Run 1, pnpm turbo run build --filter=@objectstack/docs --concurrency=2: spec#build cache hit; docs#build cache miss, executing 7dfad10147e2df6a; build-llms-full printed 418 pages, 20,040,773 bytes; VERDICT command-exit 0. Run 2, after rm public/llms-full.txt: docs#build cache MISS at a new hash 37dfc25c6c8e82a0, VERDICT 0. Cause: run 1's own gen:upgrade-guide wrote the gitignored content/docs/protocol-upgrade/* pages, and the task's $TURBO_ROOT$/content/** input glob hashes them (4 protocol-upgrade paths in the dry-run inputs), so a fresh checkout's first build changes its own input hash. Run 3, after rm public/llms-full.txt again: docs#build 'cache hit, replaying logs 37dfc25c6c8e82a0', Cached 2 of 2, lock held 3 s, VERDICT 0; file restored at 20,040,773 bytes, sha256 equal to run 2's (RESTORED-IDENTICAL). Control leg (the pre-patch turbo.json replay leaves the file absent): NOT MEASURED, because it costs another full docs build; it holds by construction, since turbo restores only declared outputs and the old list lacked this file. Hash isolation: turbo --dry=json for docs, core and skills plus their closure, at HEAD and with f7ce0dd's turbo.json written to the tree only (trap restore via git checkout HEAD -- turbo.json; git hash-object equals HEAD's blob; git status clean). Result: 6 of 7 task hashes and the global hash unchanged; docs#build changed. git status clean after every run.",
"gates": "Re-derived at b7c786c (dispatch-gates --commands --repo objectstack-ai/objectstack, change set 7 paths vs merge base 23e689d): 43 commands, the previous 38 plus 5 that turbo.json adds: node scripts/check-dts-emitted.mjs --self-test, pnpm check:dts-closure, pnpm check:dual-build-cjs-loads, pnpm check:lean-entry-closure, pnpm check:sourcemap-no-sources-content. Each was run with its exit captured before any pipe. First pass: 39 exited 0; the four dist readers exited 3 (PREREQUISITE NOT MET, no dist/ in the fresh worktree), which is not a measurement. Then pnpm turbo run build --filter=!@objectstack/docs --concurrency=2 under the lock (75 tasks successful, 0 cached, 7m44s, VERDICT 0), and the four reran at exit 0. check:dts-closure: 75 built packages swept, 176/176 declaration files present. check:lean-entry-closure: admitted set held exactly (15 packages). check:sourcemap-no-sources-content: 71 packages, 570 maps, none embed source text. check:dual-build-cjs-loads: exit 0. pnpm check:turbo-task-graph: exit 0 ('OK: 46 package-scoped and 9 generic turbo task(s) judged against 83 workspace package(s)'); no gate pins the outputs shape. pnpm check:nul-bytes: exit 0 (11567 tracked text files, no raw ASCII control bytes). pnpm check:merge-driver: exit 0. dispatch-gates --ran on the exit-annotated record: '43 derived famil(ies) accounted for, 43 run, 0 NOT-MEASURED (a DERIVED zero, all 43 recorded an exit code and none of them is 3)'. CI check-runs on b7c786c, all success: Lint & Repo Gates, TypeScript Type Check, Test Core (6/6 shards), Build Core, Governed Surface Queue Guard, Build Docs, the eight Lint jobs, the four Type Check jobs, Validate Package Dependencies, Check Documentation Links, Dogfood Regression Gate, the three claim/closing-keyword guards. Check Changeset skipped via the skip-changeset label fast path. CI's own NOT MEASURED lines (Test Core completeness, Type Check workspace lane) are covered by those check-runs.",
"mcp_calls": "0",
"api_writes": "1 relay stroke (POST /repos/objectstack-ai/objectstack/dispatches by this session, executed as objectstack-fleet[bot]): POST /repos//issues/23625/comments, this report. Plus 1 git push (not REST). Reads: gh api GET on PR check-runs and refs.",
"line_budget": "This round: turbo.json +1 / -1. Whole PR: +185 / -13 = 198 changed lines in 7 files, under the 3000 threshold. No governed path.",
"files_changed": [
"turbo.json (this round: one output added to @objectstack/docs#build)",
"apps/docs/.gitignore",
"apps/docs/app/llms-full.txt/route.ts (deleted)",
"apps/docs/next.config.mjs",
"apps/docs/package.json",
"apps/docs/scripts/build-llms-full.mjs",
"apps/docs/scripts/llms-full/resolve-hooks.mjs"
],
"deviations": [
"The dispatch premise 'the docs app uses the generic build task' was false on origin/main 23e689d: turbo.json already has a @objectstack/docs#build override (line 37). The output went into that override; no new override was created and the generic task was not touched.",
"Four dist-reading gates exited 3 (prerequisite) on the first pass. I built the whole non-docs workspace under the lock and reran them; only the reruns' exit 0 is counted.",
"Replay proof needed three runs, not two: the second was a cache miss caused by the build writing gitignored pages into its own input glob (see tests). The cache-hit run is the third. The control leg is NOT MEASURED."
],
"open_questions": [
{
"question": "Two lines in the PR #23640 body are stale after this round, and the dev does not rewrite the body. Which edit should the seat make?",
"options": [
"A. In '## The change', replace 'docs-deploy.yml, vercel.json and turbo.json are untouched. No bypass flag.' with 'docs-deploy.yml and vercel.json are untouched; turbo.json adds public/llms-full.txt to the @objectstack/docs#build outputs (patch round, b7c786c). No bypass flag.' In '## Acceptance notes', replace the 'Turbo outputs' bullet with: 'Turbo outputs: @objectstack/docs#build now declares public/llms-full.txt; a cache-hit replay restored it byte-identical (measured on b7c786c).'",
"B. Leave the body and rely on the seat's one added line."
],
"recommendation": "A. Actual business need: a reviewer reads the body as the account of the diff, and 'turbo.json untouched' is now false. Long-term soundness: the body should describe the merged commit. Preventing AI mistakes: a later agent reading 'turbo.json untouched' next to a diff that touches it learns to distrust bodies. Startup focus: two sentence edits, no new surface."
}
],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted, not filed — @objectstack/docs#build's input glob $TURBO_ROOT$/content/** hashes the gitignored content/docs/protocol-upgrade/* pages, which the task's own gen:upgrade-guide writes. A fresh checkout's first build therefore changes its own input hash, and the second build is a miss (measured: 7dfad10147e2df6a, then 37dfc25c6c8e82a0, then a hit). Cost: one extra rebuild per fresh tree. Production and CI force anyway (TURBO_FORCE), so nothing is wrong; noted only."
]
}
yes; apps/docs/app/llms-full.txt/route.ts is gone, apps/docs/scripts/build-llms-full.mjs is present, and turbo.json declares public/llms-full.txt (1 hit)
The card
GET /issues/23625
closed / completed, on the merge
State change: this act removes pm:dispatched.
Still owed (this seat, a reading, not dev work): the card's Done is a green scheduled docs-deploy.yml run with /llms-full.txt served in production. The first run that carries 42718df38 is the next schedule, about 12:53Z.
The seat reads that run and records it here.
If it is red, the card is reopened with that run's reading, under the seat's own claim.
Activity
objectstack-fleet commentedon Oct 15, 2026
Triage: first grade ·
bug·priority:p1·domain:devx·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-15T06:54ZRead fresh.
docs-deploy.ymlhas failed at Vercel's upload step since its 01:07Z scheduled run.apps/docs/app/llms-full.txt/route.tsis stored as an ISR fallback, and the page has reached Vercel's 20 MB cap (FALLBACK_BODY_TOO_LARGE). Production docs are frozen at the 2026-10-14 18:51Z deploy, and every run (4 a day) fails the same way.priority:p1: no docs change reaches objectstack.ai, during the 18 prerelease line, when the upgrade notes and the retirement prescriptions are what readers need.domain:devx(apps/docs, by the domain table).llms.txt), the seat's call.docs-deploy.ymlrun, withllms-full.txt(or its parts) served in production.objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 2
Session:
session_01ESFcvW3staenyBCGq7kcDHAccount:
huangyiirene(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-23625-llms-full-staticWorktree:
objectstack-issue-23625Domain:
domain:devxSeat:
domain:devx#1File surface:
apps/docs/**— thellms-full.txtroute (apps/docs/app/llms-full.txt/route.ts), plus whatever build-time producer replaces or splits it insideapps/docs(itspackage.jsonscripts, ascripts/file underapps/docs,public/output rules,next.config.*), andllms.txtonly if a split links the parts from it (stop on breach; explain in the report). ⛔ Not.github/workflows/**(governed; a needed workflow edit is a reported fork, not a hard-done change), and ⛔ not the bypass flag.Container & model:
M,mode:subagent,model: TIER_DEFAULT(dispatch-gates --tieron the surface: "no path-derived mandate", the PM's per-card call)Clause-②: no
Responsibility:
apps/docs/app/llms-full.txt/route.ts(this lane's docs app) produces the oversized ISR fallback | no platform path covers it: the deploy fails loudly at Vercel's upload step and production stays on the 2026-10-14 18:51Z deploy | every objectstack.ai docs reader and every agent fetchingllms-full.txtreaches it today (scheduled runs38663939159and38695971788red)Thread-read: 6186351941
Serial constraints cleared: none — no open PR's file list touches
apps/docs/{app,lib,next*,package*,source*,vercel*}ordocs-deploy(8 open PRs read at this claim); noclaude/issue-23625*branch exists.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
os-dev-report
{
"issue": 23625,
"status": "done",
"branch": "claude/issue-23625-llms-full-static",
"pr": "#23640",
"session": "session_01ESFcvW3staenyBCGq7kcDH",
"premise_still_valid": true,
"summary": "Option 1. apps/docs/app/llms-full.txt/route.ts (an ISR prerender) is retired, and apps/docs/scripts/build-llms-full.mjs (with llms-full/resolve-hooks.mjs) now writes public/llms-full.txt during the docs build, after the spec generators and before next build. It imports lib/source.ts through fumadocs-mdx's Node loader, so the text still comes from getLLMText over source.getPages(): one producer, same URL, same text/plain type, same X-Robots-Tag noindex. Measured: 20,032,800 bytes over 418 pages at e611e2b, 20,040,773 with main 23e689d merged in. Vercel's cap is decimal 20,000,000 bytes: the last green deploy commit 989aa48 measured 19,998,748 and the first red, 252775d, 20,004,539. Growth: 8,046,416 bytes on 2026-08-25, 14,632,088 on 09-14, 16,787,480 on 09-30, 17,357,732 on 10-07, about 12.0 MB in 50.6 days (1.66 MB/week); the last 30 days averaged 1.25 MB/week, the last 7 days 2.57 MB/week. About 66% of the bytes are auto-generated references/** pages and 9.4% build-time protocol-upgrade pages. Headroom: the 20 MB ISR cap no longer applies to this file, and the largest remaining prerender is 3,916,137 bytes (protocol-upgrade/18.html, 19.6% of the cap). The only static-file figure found is an unverified search excerpt of Vercel's limits page, 100 MB (Hobby) / 1 GB (Pro). At 100 MB that is about 31-64 weeks at the measured rates. Before merge this PR proves: the production buildCommand writes the file and fails without it; the route is gone from the prerender table; next start serves /llms-full.txt 200 with byte-equal content and the same headers. Vercel's acceptance of the upload (the card's Done) is NOT MEASURED until a scheduled or dispatched docs-deploy.yml run after merge.",
"tests": "(1) e611e2b: NODE_OPTIONS=--max-old-space-size=4096 next build (lock VERDICT command-exit 0, 208s). Route body .next/server/app/llms-full.txt.body = 20,032,800 bytes, sha256 cbbf6f782618778397483dcbea544cc1bc1c144d002e409807e7d2bae1986d0b. The generator prototype (same getLLMText logic) on the .source that build left produced the same sha256 (cmp exit 0). (2) 40af083: apps/docs/vercel.json buildCommand (pnpm turbo run build --filter=@objectstack/docs) from a fresh-checkout state (no .next; .source regenerated without protocol-upgrade; protocol-upgrade dir deleted). Lock VERDICT command-exit 0, turbo Tasks 2 successful, 0 cached. Printed build-llms-full: 418 pages, 20,032,800 bytes; the route table no longer lists /llms-full.txt. (3) Same-content proof (scratch script, not committed): public/llms-full.txt is PERMUTATION-EQUAL 418/418 against the per-page /llms.mdx bodies Next prerendered in that same build, in llms.txt order. The e611e2b route body is also PERMUTATION-EQUAL. A one-byte mutant FAILs at offset 9886232 (exit 1). Order differs only because fumadocs-mdx writes .source in a different order on every run: 4 runs on one tree gave 4 index hashes. (4) next start -p 38617 on that build: GET /llms-full.txt 200, Content-Type text/plain; charset=UTF-8, X-Robots-Tag noindex, Content-Length 20032800, cmp-equal to public/llms-full.txt. /llms.txt 200, /docs/ai/agents.mdx 200. Server PID tree killed by recorded PID. (5) d777181: generator rerun printed 418 pages, 20,032,800 bytes; PERMUTATION-EQUAL 418/418. (6) f7ce0dd (final head): pnpm install + gen:schema + gen:docs + gen:upgrade-guide + build-llms-full (418 pages, 20,040,773 bytes) + pnpm typecheck in apps/docs (fumadocs-mdx && next typegen && tsc --noEmit; Types generated successfully). Lock VERDICT command-exit 0. (7) Growth (A3): the same generator ran in a throwaway detached worktree at 0d91eaa, 8c657f7, 05be352, 51290bc, 989aa48 and 252775d, each after pnpm install and the three spec generators. Every run exited 0 under the lock; the worktree was removed afterwards. No ablation: no tested behaviour changed (the app has no test suite), and the proofs above are one-shot.",
"gates": "Local, at final head f7ce0dd: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 38 commands (the same 38 at d777181). Each was run with its exit captured before any pipe; all 38 exited 0. dispatch-gates --ran on the exit-annotated record: 38 derived famil(ies) accounted for, 38 run, 0 NOT-MEASURED (a DERIVED zero; all 38 recorded an exit code). The set includes pnpm --filter @objectstack/spec run check:llms-txt (98 claims re-derived), check-doc-frontmatter + --self-test, check-docs-nav-label + --self-test, check:docs-locale-catch-all, check:published-files, check:turbo-task-graph, check:test-source-alias, check:nul-bytes (11564 files, no raw control bytes), check:watch-hint-literal and check-undeclared-dep-imports. A5: check:llms-txt reads packages/spec/llms.txt (TARGET, line 177), not the docs site's /llms.txt or /llms-full.txt, so it is unaffected. The 14 pending-changeset families do not apply: @objectstack/docs is private, so the PR carries skip-changeset and no changeset. CI check-runs on PR head f7ce0dd at report time. Success: Check Changeset (the labelled rerun; the opened run skipped), Governed Surface Queue Guard, Test Core, Type Check debt ledger, Type Check workspace, Validate Package Dependencies, Check Documentation Links, Dogfood Regression Gate, Check PR Size, filter, the three claim/closing-keyword guards. In progress: Build Docs (the CI half of the build proof), all eight Lint jobs, Type Check consumer gates, Type Check source gates. Skipped by filter: Build Core. Not waited on, per the dispatch contract.",
"mcp_calls": "0",
"api_writes": "3 relay strokes, each one POST /repos/objectstack-ai/objectstack/dispatches by this session, executed by fleet-write.yml as objectstack-fleet[bot]. (1) pr_create, run 38705901758: POST /repos/objectstack-ai/objectstack/pulls (draft, 10895 bytes sent and stored identical) + POST /repos//issues/23640/assignees (huangyiirene). (2) labels_add via scripts/pm/label-write.mjs, run 38706014256: POST /repos//issues/23640/labels (skip-changeset; read-back MATCHES). (3) This report: POST /repos//issues/23625/comments. Plus 4 git pushes (not REST). Reads were single-card or single-run GET via gh api.",
"line_budget": "+184 / -12 = 196 changed lines in 6 files (dispatch-gates count vs merge base 23e689d), under the 3000 human-merge threshold. No skills/, .claude/ or governed path touched.",
"files_changed": [
"apps/docs/.gitignore (new)",
"apps/docs/app/llms-full.txt/route.ts (deleted)",
"apps/docs/next.config.mjs (comment)",
"apps/docs/package.json (build script)",
"apps/docs/scripts/build-llms-full.mjs (new)",
"apps/docs/scripts/llms-full/resolve-hooks.mjs (new)"
],
"deviations": [
"A1: the failing step's log text is NOT MEASURED. The job-log download redirects to productionresultssa1.blob.core.windows.net, which the container proxy refuses (403 on CONNECT). What was measured: step 13 'Deploy the prebuilt output to production' failed, and steps 11 (build) and 12 (output assertion) succeeded, in run 38695971788's job steps. Annotations carry only 'Process completed with exit code 1.'",
"A4: verified from @vercel/next 15.0.1 source (the builder vercel@60.1.3 pins). getStaticFiles() globs public/** after the build command and emits static outputs; FALLBACK_BODY_TOO_LARGE is absent from the CLI dist, so the check is server-side. Not verified with a real vercel build or deploy (no VERCEL_TOKEN here), and not against Vercel's docs: vercel.com answers proxy 403 and docs.vercel.com does not resolve. The static-file limit figure is an unverified search excerpt.",
"Byte identity: the exact-hash proof (sha256 match) is for the generator prototype (identical getLLMText logic) reading the .source left by the same next build, at e611e2b. The final script regenerates .source itself, and fumadocs-mdx's codegen order differs on every run, so its proof is PERMUTATION-EQUAL (same 418 page bodies, same bytes, order per build), not one fixed hash. The old route's order varied per build in the same way. Within one build, llms-full's order no longer matches llms.txt's.",
"The full production buildCommand ran at 40af083. d777181 changed only comments, an error message and the print of out-of-app paths. At d777181 and at the final f7ce0dd (main merged in), I reran the generator, permutation proof, typecheck and all 38 gates, not the whole next build. CI Build Docs runs the full command on the final head.",
"Commit trailers use the model-free pair AGENTS.md mandates (Claude-Session + Co-authored-by: Claude), and the PR body ends with AGENTS.md's session-URL footer. The harness attribution reminder asked for a model-named Co-Authored-By and a different PR footer; AGENTS.md and the role file outrank it.",
"origin/main (23e689d, 3 commits, no overlap with this diff) was merged in before the PR opened, per AGENTS.md Multi-agent section 10."
],
"open_questions": [
{
"question": "Should turbo.json's @objectstack/docs#build outputs list public/llms-full.txt? Today a turbo cache-hit replay restores .next/** without the file. turbo.json is outside this card's claimed file surface, so I left it.",
"options": [
"A. Leave it. Measured reach: docs-deploy.yml and the ci.yml Build Docs gate both set TURBO_FORCE, and the deploy runner has no turbo cache or remote-cache credentials, so neither production path can replay; only a local cached pnpm turbo run build --filter=@objectstack/docs can. Cost: zero now, plus a latent hole if remote caching is ever enabled.",
"B. Add apps/docs/public/llms-full.txt (package-relative: public/llms-full.txt) to that task's outputs in turbo.json, either as a one-line widening of this PR's surface or as a seat follow-up. Cost: one line, no new gate. It makes the declared outputs equal the real ones."
],
"recommendation": "B. Actual business need: no production path is hit today (measured above), so this is not urgent; it does stop an agent who enables remote caching from shipping a deploy without llms-full.txt and getting no error. Long-term soundness: a task's declared outputs should be its real outputs; A is a latent workaround. Preventing AI mistakes: B removes a trap where a cache hit silently drops a file the build claims to produce. Startup focus: one config line, no new gate, no new capability. Taken together B wins, and its cost is negligible."
}
],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted, not filed — fumadocs-mdx 15.2.3 writes the .source index in a different order on every run (4 runs on one tree gave 4 index hashes), so /llms.txt and /llms-full.txt page order changes per build, as it did before this PR. No consumer reads the order; it is in the PR's Acceptance notes.",
"carrier: none (承接者:无) · noted, not filed — the next-largest prerender against the same 20 MB fallback cap is /docs/protocol-upgrade/18, at 3,916,137 bytes of HTML; its source page is 1.46 MB of markdown and grows with each 18.x upgrade entry. Headroom observation only.",
"carrier: none (承接者:无) · noted, not filed — next dev no longer serves /llms-full.txt until a build or node scripts/build-llms-full.mjs writes it; the old route rendered it on demand in dev. Production is unaffected. In the PR's Acceptance notes."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
File-surface addendum to claim
6186873847·domain:devxseat 1 ·session_01ESFcvW3staenyBCGq7kcDH· 2026-10-15T08:35Zturbo.json: the@objectstack/docsbuild task declarespublic/llms-full.txtamong its outputs. It is a patch round of the same dev, under this claim.open_questions[0]option B. The PR introduces that build artifact, so its declared outputs must equal its real ones. Without the line, a cache-hit replay would deploy without/llms-full.txtand print no error. Both production paths setTURBO_FORCEtoday, so the gap is latent, not live.turbo.json(all open PRs' file lists read at this addendum).Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Review pointer ·
domain:devxseat 1 ·session_01ESFcvW3staenyBCGq7kcDH· 2026-10-15T08:53Zb7c786c2, including theturbo.jsonpatch round). The PR carries this card's closing keyword.docs-deploy.ymlwith/llms-full.txtserved) is read after the merge, on the next scheduled run.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
os-dev-report$TURBO_ROOT$ /content/** input glob hashes them (4 protocol-upgrade paths in the dry-run inputs), so a fresh checkout's first build changes its own input hash. Run 3, after rm public/llms-full.txt again: docs#build 'cache hit, replaying logs 37dfc25c6c8e82a0', Cached 2 of 2, lock held 3 s, VERDICT 0; file restored at 20,040,773 bytes, sha256 equal to run 2's (RESTORED-IDENTICAL). Control leg (the pre-patch turbo.json replay leaves the file absent): NOT MEASURED, because it costs another full docs build; it holds by construction, since turbo restores only declared outputs and the old list lacked this file. Hash isolation: turbo --dry=json for docs, core and skills plus their closure, at HEAD and with f7ce0dd's turbo.json written to the tree only (trap restore via git checkout HEAD -- turbo.json; git hash-object equals HEAD's blob; git status clean). Result: 6 of 7 task hashes and the global hash unchanged; docs#build changed. git status clean after every run.",$TURBO_ROOT$ /content/** hashes the gitignored content/docs/protocol-upgrade/* pages, which the task's own gen:upgrade-guide writes. A fresh checkout's first build therefore changes its own input hash, and the second build is a miss (measured: 7dfad10147e2df6a, then 37dfc25c6c8e82a0, then a hit). Cost: one extra rebuild per fresh tree. Production and CI force anyway (TURBO_FORCE), so nothing is wrong; noted only."
{
"issue": 23625,
"status": "done",
"round": "patch round 1 (open_questions[0] option B taken into PR #23640)",
"branch": "claude/issue-23625-llms-full-static",
"pr": "#23640",
"head": "b7c786c22bf4d82c6889ab13d91e71ec6e909494",
"session": "session_01ESFcvW3staenyBCGq7kcDH",
"premise_still_valid": true,
"summary": "turbo.json now declares public/llms-full.txt as an output of the existing @objectstack/docs#build override; outputs are now dist/, json-schema/, .next/, !.next/cache/ and public/llms-full.txt. That was the only turbo.json change (1 line), in commit b7c786c. Choice measured: the dispatch said the docs app uses the generic build task, but origin/main 23e689d turbo.json already carries a @objectstack/docs#build override at line 37. Package-scoped overrides are the repo's convention: check:turbo-task-graph judges 46 package-scoped and 9 generic tasks. So the output went into that override, and the generic build task is untouched. Measured with turbo --dry=json, before vs after: the global hash and the task hashes of core, metadata-core, refd-timer-testkit, skills, spec and types are identical; only @objectstack/docs#build changed (71b4359b35d85a0a to 7dfad10147e2df6a). Replay proof (step 3): with the file deleted, a cache-hit run (no TURBO_FORCE) restored public/llms-full.txt byte-identical (sha256 01178364f1437c6e38a6af567cd184e9c4e9a309a91d6e42fae7894f8327caed) in 3 s. CI on b7c786c is all green, including Build Docs and the five required contexts. PR body not edited, per instruction; two of its lines are now stale (see open_questions).",
"tests": "Replay proof under the lock, at b7c786c. Run 1, pnpm turbo run build --filter=@objectstack/docs --concurrency=2: spec#build cache hit; docs#build cache miss, executing 7dfad10147e2df6a; build-llms-full printed 418 pages, 20,040,773 bytes; VERDICT command-exit 0. Run 2, after rm public/llms-full.txt: docs#build cache MISS at a new hash 37dfc25c6c8e82a0, VERDICT 0. Cause: run 1's own gen:upgrade-guide wrote the gitignored content/docs/protocol-upgrade/* pages, and the task's
"gates": "Re-derived at b7c786c (dispatch-gates --commands --repo objectstack-ai/objectstack, change set 7 paths vs merge base 23e689d): 43 commands, the previous 38 plus 5 that turbo.json adds: node scripts/check-dts-emitted.mjs --self-test, pnpm check:dts-closure, pnpm check:dual-build-cjs-loads, pnpm check:lean-entry-closure, pnpm check:sourcemap-no-sources-content. Each was run with its exit captured before any pipe. First pass: 39 exited 0; the four dist readers exited 3 (PREREQUISITE NOT MET, no dist/ in the fresh worktree), which is not a measurement. Then pnpm turbo run build --filter=!@objectstack/docs --concurrency=2 under the lock (75 tasks successful, 0 cached, 7m44s, VERDICT 0), and the four reran at exit 0. check:dts-closure: 75 built packages swept, 176/176 declaration files present. check:lean-entry-closure: admitted set held exactly (15 packages). check:sourcemap-no-sources-content: 71 packages, 570 maps, none embed source text. check:dual-build-cjs-loads: exit 0. pnpm check:turbo-task-graph: exit 0 ('OK: 46 package-scoped and 9 generic turbo task(s) judged against 83 workspace package(s)'); no gate pins the outputs shape. pnpm check:nul-bytes: exit 0 (11567 tracked text files, no raw ASCII control bytes). pnpm check:merge-driver: exit 0. dispatch-gates --ran on the exit-annotated record: '43 derived famil(ies) accounted for, 43 run, 0 NOT-MEASURED (a DERIVED zero, all 43 recorded an exit code and none of them is 3)'. CI check-runs on b7c786c, all success: Lint & Repo Gates, TypeScript Type Check, Test Core (6/6 shards), Build Core, Governed Surface Queue Guard, Build Docs, the eight Lint jobs, the four Type Check jobs, Validate Package Dependencies, Check Documentation Links, Dogfood Regression Gate, the three claim/closing-keyword guards. Check Changeset skipped via the skip-changeset label fast path. CI's own NOT MEASURED lines (Test Core completeness, Type Check workspace lane) are covered by those check-runs.",
"mcp_calls": "0",
"api_writes": "1 relay stroke (POST /repos/objectstack-ai/objectstack/dispatches by this session, executed as objectstack-fleet[bot]): POST /repos//issues/23625/comments, this report. Plus 1 git push (not REST). Reads: gh api GET on PR check-runs and refs.",
"line_budget": "This round: turbo.json +1 / -1. Whole PR: +185 / -13 = 198 changed lines in 7 files, under the 3000 threshold. No governed path.",
"files_changed": [
"turbo.json (this round: one output added to @objectstack/docs#build)",
"apps/docs/.gitignore",
"apps/docs/app/llms-full.txt/route.ts (deleted)",
"apps/docs/next.config.mjs",
"apps/docs/package.json",
"apps/docs/scripts/build-llms-full.mjs",
"apps/docs/scripts/llms-full/resolve-hooks.mjs"
],
"deviations": [
"The dispatch premise 'the docs app uses the generic build task' was false on origin/main 23e689d: turbo.json already has a @objectstack/docs#build override (line 37). The output went into that override; no new override was created and the generic task was not touched.",
"Four dist-reading gates exited 3 (prerequisite) on the first pass. I built the whole non-docs workspace under the lock and reran them; only the reruns' exit 0 is counted.",
"Replay proof needed three runs, not two: the second was a cache miss caused by the build writing gitignored pages into its own input glob (see tests). The cache-hit run is the third. The control leg is NOT MEASURED."
],
"open_questions": [
{
"question": "Two lines in the PR #23640 body are stale after this round, and the dev does not rewrite the body. Which edit should the seat make?",
"options": [
"A. In '## The change', replace 'docs-deploy.yml, vercel.json and turbo.json are untouched. No bypass flag.' with 'docs-deploy.yml and vercel.json are untouched; turbo.json adds public/llms-full.txt to the @objectstack/docs#build outputs (patch round, b7c786c). No bypass flag.' In '## Acceptance notes', replace the 'Turbo outputs' bullet with: 'Turbo outputs: @objectstack/docs#build now declares public/llms-full.txt; a cache-hit replay restored it byte-identical (measured on b7c786c).'",
"B. Leave the body and rely on the seat's one added line."
],
"recommendation": "A. Actual business need: a reviewer reads the body as the account of the diff, and 'turbo.json untouched' is now false. Long-term soundness: the body should describe the merged commit. Preventing AI mistakes: a later agent reading 'turbo.json untouched' next to a diff that touches it learns to distrust bodies. Startup focus: two sentence edits, no new surface."
}
],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted, not filed — @objectstack/docs#build's input glob
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Landed: PR #23640 →
42718df38(Fixes #23625) ·domain:devxseat 1 ·session_01ESFcvW3staenyBCGq7kcDH· 2026-10-15T09:24ZGET /pulls/23640merged: trueat 09:23:10Z,merge_commit_sha42718df38git ls-remote origin refs/heads/gh-readonly-queue/main/pr-23640*origin/maingit merge-base --is-ancestor 42718df38 origin/mainapps/docs/app/llms-full.txt/route.tsis gone,apps/docs/scripts/build-llms-full.mjsis present, andturbo.jsondeclarespublic/llms-full.txt(1 hit)GET /issues/23625closed/completed, on the mergeState change: this act removes
pm:dispatched.Still owed (this seat, a reading, not dev work): the card's Done is a green scheduled
docs-deploy.ymlrun with/llms-full.txtserved in production. The first run that carries42718df38is the next schedule, about 12:53Z.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Owed reading handed over at clock-out ·
domain:devxseat 1 ·session_01ESFcvW3staenyBCGq7kcDH· 2026-10-15T10:39ZThis seat clocks out on the maintainer's order 「当前任务处理完,合并后就下班」 before the reading its landing record
6188846135owes.docs-deploy.ymlrun on amainthat carries42718df38. The 6-hourly schedule's next run is about 12:53Z./llms-full.txtserved in production, settles this card's Done.domain:devxseat (seat post [PM seat] domain:devx @ objectstack — ⏳ vacant #6023). Until one sits, triage on its hourly sweep.6188275286.Generated by Claude Code