Found while working #10642 (CI-vs-production docs build divergence). Out of that card's file surface: the fix is in the root turbo.json, which #10642 is fenced out of.
Measured
turbo.json has no @objectstack/docs#build entry, so the docs build inherits the generic build task, whose inputs are $TURBO_DEFAULT$ — the package's own files. Every one of the docs site's 400+ pages lives in content/, which is outside apps/docs/ and is declared nowhere.
On 98ea344, in a clean worktree:
$ pnpm turbo run build --filter=@objectstack/docs --dry=json
docs#build declared input file count: 35
any input path under content/ : 0
hash: 4e0c51cb3db203a8
cache status: {"local":true,"remote":false,"status":"HIT","source":"LOCAL","timeSaved":110068}
and the direct probe — append one line to content/docs/index.mdx, re-derive, revert:
hash BEFORE content edit: 4e0c51cb3db203a8
hash AFTER content edit: 4e0c51cb3db203a8
The hash does not move. Sibling tasks show this is a declaration the repo already knows how to write: @objectstack/spec#test, @objectstack/cli#test and create-objectstack#test all reach outside their package with $TURBO_ROOT$/content/** inputs. build never got one.
Why it matters
apps/docs/vercel.json builds production with cd ../.. && pnpm turbo run build --filter=@objectstack/docs. If that turbo invocation ever has a warm cache — Vercel enables Turborepo remote caching automatically for linked projects — a content-only commit hashes identically to the previous one and turbo replays the previous .next instead of building the new content. The deploy then succeeds while serving the old pages: green everywhere, stale site, no error anywhere.
Honest split of what is measured: the input set and the hash identity are measured here, locally, and they are the whole mechanism. Whether Vercel's builder actually presents a warm turbo cache for this project is not measured — it is not observable from a dev container. That is the one thing to check before deciding severity: if remote caching is on for this project, this is live; if it is off, this is a loaded gun whose trigger is one dashboard toggle.
Not the same hole as #10642's gate
#10642 lands TURBO_FORCE: 'true' on the CI step so the gate cannot be satisfied by a replayed artifact. That protects CI only, and deliberately: forcing a rebuild is right for a gate and wrong as a way to make production correct. Production should be correct because the task declares its real inputs.
Suggested fix
Give the docs build the declaration its inputs already have elsewhere:
"@objectstack/docs#build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**"],
"inputs": ["$TURBO_DEFAULT$", "$TURBO_ROOT$/content/**"]
}Acceptance: the same probe above must move the hash. Worth checking in the same pass whether any other task consuming content/ has the same gap.
Found while working #10642 (CI-vs-production docs build divergence). Out of that card's file surface: the fix is in the root
turbo.json, which #10642 is fenced out of.Measured
turbo.jsonhas no@objectstack/docs#buildentry, so the docs build inherits the genericbuildtask, whose inputs are$TURBO_DEFAULT$— the package's own files. Every one of the docs site's 400+ pages lives incontent/, which is outsideapps/docs/and is declared nowhere.On
98ea344, in a clean worktree:and the direct probe — append one line to
content/docs/index.mdx, re-derive, revert:The hash does not move. Sibling tasks show this is a declaration the repo already knows how to write:
@objectstack/spec#test,@objectstack/cli#testandcreate-objectstack#testall reach outside their package with$TURBO_ROOT$/content/**inputs.buildnever got one.Why it matters
apps/docs/vercel.jsonbuilds production withcd ../.. && pnpm turbo run build --filter=@objectstack/docs. If that turbo invocation ever has a warm cache — Vercel enables Turborepo remote caching automatically for linked projects — a content-only commit hashes identically to the previous one and turbo replays the previous.nextinstead of building the new content. The deploy then succeeds while serving the old pages: green everywhere, stale site, no error anywhere.Honest split of what is measured: the input set and the hash identity are measured here, locally, and they are the whole mechanism. Whether Vercel's builder actually presents a warm turbo cache for this project is not measured — it is not observable from a dev container. That is the one thing to check before deciding severity: if remote caching is on for this project, this is live; if it is off, this is a loaded gun whose trigger is one dashboard toggle.
Not the same hole as #10642's gate
#10642 lands
TURBO_FORCE: 'true'on the CI step so the gate cannot be satisfied by a replayed artifact. That protects CI only, and deliberately: forcing a rebuild is right for a gate and wrong as a way to make production correct. Production should be correct because the task declares its real inputs.Suggested fix
Give the docs build the declaration its inputs already have elsewhere:
Acceptance: the same probe above must move the hash. Worth checking in the same pass whether any other task consuming
content/has the same gap.