Follow-up from PM acceptance of #190.
#190 fixed the collection-route half of the docs content-root contract: with a
missing configured content root, GET /docs, GET /docs/registry and
GET /docs/process-quality now fail with a 503 that names the path
(Docs content root is unavailable: <path> (set DTC_CACHE_ROOT to a hydrated content cache, or let GitHub hydration run)), instead of the old misleading
404 {"error":"Document not found"}.
The single-document read path was deliberately left unchanged and is scoped out
of #190 (its AC3 required "existing single-document behavior unchanged"). The
Tester flagged the remaining inconsistency and I agree it deserves its own
issue.
Observed behavior after #190
GET /docs?path=<anything> against a missing content root returns
404 {"error":"Document not found"}, not the 503. getDoc() in
backend/src/docs/contentApi.ts reads through rt.store.readFile() without
calling rt.ensureSynced(), so the content-root assertion never runs on that
route. Every other corpus-reading route calls ensureSynced().
Verified live by the Tester with a spawned backend/scripts/test-server.ts
(DTC_OFFLINE=1, DATAOPS_DOCS_DOMAIN=1) against three content-root states.
Why this matters to an operator
This is the same class of defect #190 removed, one route narrower. During a real
content-root outage an operator who opens a specific process document is told
"Document not found", which reads as "this SOP was deleted" rather than "the
docs corpus is unavailable". #190 removed that lie for listings; the single-doc
read still tells it.
Blast radius is smaller because frontend/src/shell/bootstrap.js calls
loadDocuments() (GET /docs) on every app boot, so the honest 503 normally
fires first. It is still the wrong answer on the direct route.
Things to settle during grooming
- Should
getDoc() call ensureSynced() (so a missing root is a 503 on every
docs route), or should the assertion move somewhere shared by all routes?
- Online production behavior must not change:
store.readFile() hydrates from
GitHub on demand, and sync() creates the root online, so the guard must stay
unreachable in a healthy deployment.
- A genuinely missing document under a present content root must still
return 404 {"error":"Document not found"}. That contract is covered by
backend/tests/docs-content-api.test.ts and must stay green.
- Decide whether
GET /images/... and any other per-file read share the same
gap.
Suggested scope hints
Out of scope
Follow-up from PM acceptance of #190.
#190 fixed the collection-route half of the docs content-root contract: with a
missing configured content root,
GET /docs,GET /docs/registryandGET /docs/process-qualitynow fail with a 503 that names the path(
Docs content root is unavailable: <path> (set DTC_CACHE_ROOT to a hydrated content cache, or let GitHub hydration run)), instead of the old misleading404 {"error":"Document not found"}.The single-document read path was deliberately left unchanged and is scoped out
of #190 (its AC3 required "existing single-document behavior unchanged"). The
Tester flagged the remaining inconsistency and I agree it deserves its own
issue.
Observed behavior after #190
GET /docs?path=<anything>against a missing content root returns404 {"error":"Document not found"}, not the 503.getDoc()inbackend/src/docs/contentApi.tsreads throughrt.store.readFile()withoutcalling
rt.ensureSynced(), so the content-root assertion never runs on thatroute. Every other corpus-reading route calls
ensureSynced().Verified live by the Tester with a spawned
backend/scripts/test-server.ts(
DTC_OFFLINE=1,DATAOPS_DOCS_DOMAIN=1) against three content-root states.Why this matters to an operator
This is the same class of defect #190 removed, one route narrower. During a real
content-root outage an operator who opens a specific process document is told
"Document not found", which reads as "this SOP was deleted" rather than "the
docs corpus is unavailable". #190 removed that lie for listings; the single-doc
read still tells it.
Blast radius is smaller because
frontend/src/shell/bootstrap.jscallsloadDocuments()(GET /docs) on every app boot, so the honest 503 normallyfires first. It is still the wrong answer on the direct route.
Things to settle during grooming
getDoc()callensureSynced()(so a missing root is a 503 on everydocs route), or should the assertion move somewhere shared by all routes?
store.readFile()hydrates fromGitHub on demand, and
sync()creates the root online, so the guard must stayunreachable in a healthy deployment.
return
404 {"error":"Document not found"}. That contract is covered bybackend/tests/docs-content-api.test.tsand must stay green.GET /images/...and any other per-file read share the samegap.
Suggested scope hints
backend/src/docs/contentApi.tsbackend/tests/docs-content-api.test.ts(extend thedocs listing content-root contract (offline)suite added by Fix deterministic /docs 404 failures in frontend module characterization #190)Out of scope
../dtc-operations,../datatasks,../podcast-assistant,../aws-infra.