Skip to content

Return the named content-root 503 on single-document docs reads too #191

Description

@alexeygrigorev

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Nice to havebackendBackend/APIbugSomething is brokenneeds groomingRaw intake that needs PM groomingtestingTests and QA

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions