Skip to content

docs(client,runtime): the AI slot answers 501, not 404 — with its 401-first and /ai/agents arms - #17844

Merged
claude[bot] merged 2 commits into
mainfrom
claude/issue-16211-ai-slot-501-not-404
Sep 12, 2026
Merged

docs(client,runtime): the AI slot answers 501, not 404 — with its 401-first and /ai/agents arms#17844
claude[bot] merged 2 commits into
mainfrom
claude/issue-16211-ai-slot-501-not-404

Conversation

@claude

@claude claude Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Fixes #16211

Clause-②: no

Re-derived on the final diff, not inherited. Every changed line is comment or docblock text with one exception, which is a data literal and not a comment: the * /ai/** row's note: string in ROUTE_LEDGER (packages/runtime/src/route-ledger.ts). It moves no response byte all the same — route-ledger.ts is not re-exported from packages/runtime/src/index.ts, ROUTE_LEDGER is absent from packages/runtime/dist entirely, and no non-test source reads .note as a served value; the ledger is a CI-audit artifact whose conformance test asserts note presence (!e.note), never its text. Apart from that one string the diff adds and removes only */// prose, and a mechanical scan for any other non-comment added or removed line returns empty. No export, authorable key, accept set, status code or response byte moves.

File face — declared in full

file in face what changed
packages/client/src/index.ts the ai namespace docblock
packages/runtime/src/route-ledger.ts the * /ai/** row's note: prose
packages/runtime/src/domains/ai.ts the unserveable-slot branch comment
.changeset/16211-ai-slot-501-not-404.md new, @objectstack/client: patch
packages/spec/src/api/protocol.zod.ts OUT carries the same stale sentence, untouched — see below

⛔ The out-of-face site, reported rather than fixed

packages/spec/src/api/protocol.zod.ts:2787 (at 6b2ec3b97; locate by the sentence) carries the same stale claim:

// `buildAIRoutes()` mounted, or 404s "AI service is not configured". So these

「凡触 packages/spec 一律转 domain:spec 座位,不论谁需要它」 — so it is left exactly as found, and ⛔ no card was filed in that lane by this seat. The domain:cli PM seat files the companion at ACCEPT.

1. The sweep — triage's «four» is correct; the dispatch order's «three» is an artifact of its matcher

The order asked for this reconciliation as the first deliverable, with a lit and a negative control per pass. Tree: objectstack-ai/objectstack at 6b2ec3b97.

Pass A — exact phrase, whole tree. git grep -n 'AI service is not configured'3 live source sites (client/src/index.ts, runtime/src/route-ledger.ts, spec/src/api/protocol.zod.ts).
Lit control not configured45 files. Negative control zzz_absent_16211 → exit 1, 0 lines.

Pass B — the card's own executable criterion, widened repo-wide. git grep -n '404s' excluding archive → 188 lines, among them the site Pass A structurally cannot see:

packages/runtime/src/domains/ai.ts:89:  // without looking like a fault. Every other /ai/* route still 404s.

Pass C/D — census cross-check. Every source file mentioning both 404 and /ai/ (49 candidates) was read. Exactly two other files state an AI-related 404 — skills/objectstack-ai/SKILL.md:344 and packages/lint/src/validate-ai-agent-authoring.ts:13 — and both are a different mechanism: ADR-0063 §2 stack-authored agents refused by loadAgent(). They are correct as written and are not a fifth site.

The live-source site census is four. Triage was right. The dispatch order's «three» is right for its matcher and misses domains/ai.ts because that site never spells the phrase — which is precisely the site the addendum called the most surprising one, sitting two lines above its own falsifier.

⚠️ One relayed reading does not reproduce

The order attributes «3 source files plus 4 CHANGELOG.md occurrences» to git grep -n 'AI service is not configured' origin/main. Re-run verbatim, that command returns 3 lines and zero CHANGELOG hits — the archive text is line-wrapped, so the exact phrase is split and cannot match:

packages/client/CHANGELOG.md:2159:  repo; this repo only proxies `/api/v1/ai/**` and 404s `AI service is not

The 4 occurrences are real (2 in packages/client/CHANGELOG.md, 2 in packages/spec/CHANGELOG.md) but are found by the wrapped prefix, not by the matcher they are credited to. It changes no conclusion; recorded because the order asked for its own claims to be falsified rather than satisfied. ⛔ CHANGELOG.md is archive and was not edited.

2. The replacement is narrower than "/ai/* answers 501" — all three arms verified in source

Verified directly against the unserveable-slot branch in packages/runtime/src/domains/ai.ts, in evaluation order, ⛔ not taken from the order or the card:

order condition answer authority
1 anonymous caller 401 ANONYMOUS_DENY_STATUS = 401 (packages/core/src/security/anonymous-deny.ts:66)
2 GET /ai/agents 200 { agents: [] } deps.success({ agents: [] })
3 every other /ai/* 501 capabilityUnavailable(deps, 'ai')deps.error(serviceUnavailableMessage(slot), 501)

All three are already pinned in domains/ai-anonymous-deny-ordering.test.ts (Group A anonymous-deny; Group B 501 with the remedy sentence and the 200 agents envelope). Replacing the sites with the obvious sentence would have manufactured a second inaccurate statement, so each site now carries the exceptions.

Two further details the stale text lost, now stated: /ai/* is registered unconditionally, which is why the answer is 501 and not 404; and the 501 body is not a local string — it comes from the shared serviceUnavailableMessage, the same sentence discovery reports for the slot, so the two cannot drift.

⭐ The GET /ai/agents empty-list courtesy was mentioned nowhere in the client docblock, the one an SDK reader actually opens. It is added there, as the deliverable triage named.

3. Changeset, not skip-changeset — measured

@objectstack/client ships files: ["dist","README.md","CHANGELOG.md"], and this TSDoc is emitted into all four built artifacts (dist/index.d.ts, dist/index.d.mts, dist/index.js, dist/index.mjs) — measured on the built tree, with the stale sentence absent from every built file afterwards and a neighbouring docblock sentence present as the lit control. So the published surface moves: patch.

The two runtime-side siblings publish nothing and are deliberately not named in the changeset: ROUTE_LEDGER is absent from packages/runtime/dist entirely, and three pre-existing domains/ai.ts comments probed as controls appear in zero built files (while that dist retains 896 comment lines, so this is not blanket comment-stripping).

4. Verification — at c3b4c470f

Gate families derived mechanically, ⛔ not hand-listed: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack. Each exit code captured before any pipe; each verdict read from the gate's own printed line.

  • Derived gates: 58/58 green. Reconciled with --ran: 58 derived, 58 run, 0 NOT-MEASURED, 0 UNRUN.
  • Two gates first returned PREREQUISITE NOT MET (check:skill-examples, check:dual-build-cjs-loads — both read built output that did not exist yet). ⛔ Not recorded as failures: a full pnpm build was run (73/73 tasks) and both were re-run to a real verdict — 258 prose examples type-check across 3 surface(s) and 104 published require entry point(s) across 67 package(s) load.
  • pnpm typecheck + pnpm test for both affected packages, under the shared heavy-verify lock (VERDICT command-exit 0): @objectstack/runtime 260 files / 3623 tests passed; @objectstack/client **43 files / 519 tests passed`.
  • Repo-wide pnpm lint (this lane always adds it): exit 0, no findings.
  • check:nul-bytes green, plus an independent control-character scan of all four changed files: 0 hits.

Named, not measured

  • The 5 CI jobs scheduled by these paths (Test Core, Temporal Conformance, Dogfood Regression, Dogfood Verify CLI, Build Core) — CI's own shell in CI's environment; no local invocation exists.
  • The 10 wide-population families, the 49 artifact-roster families and the 5 workflow-valued families the deriver reports as unplaceable locally. Their silence is ⛔ not a clearance.
  • No browser/dogfood verification: the diff is prose and moves no rendered surface.

Verified by Claude Code in session session_01TSf4DV7ziu4V5j73e46b7c.


Generated by Claude Code

…-first and /ai/agents arms

Three source sites still described the pre-`capabilityUnavailable` behaviour,
saying `/ai/*` 404s "AI service is not configured" when no AI service is
mounted. The dispatcher has answered 501 since the shared exit landed: the
`/ai/*` routes are registered unconditionally, so a request reaches a handler
with nothing behind it, which is 501 Not Implemented and not 404.

The replacement is deliberately narrower than "`/ai/*` answers 501", because
that sentence is not true either. Verified in `domains/ai.ts` against the
unserveable-slot branch and its pins:

- an anonymous caller is refused 401 first (ANONYMOUS_DENY_STATUS);
- `GET /ai/agents` answers 200 with an empty list as a console courtesy;
- every other route answers 501 via `capabilityUnavailable(deps, 'ai')`.

The client docblock additionally gains the `/ai/agents` empty-list courtesy,
which it never mentioned, and all three sites now say the 501 body comes from
the shared `serviceUnavailableMessage` and so cannot drift from what discovery
reports for the slot.

Comment text only — no runtime behaviour changes.

Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c
Co-authored-by: Claude <noreply@anthropic.com>
…ock correction

The docblock is emitted into all four of the client's built artifacts, so the
published surface moves and `skip-changeset` would be wrong. Measured on the
built tree with a lit control; the runtime-side siblings publish nothing and
are deliberately not named.

Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/client, @objectstack/runtime, touching 2 documentable anchor(s).

9 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/client-sdk.mdx (via ObjectStackClient (symbol, a top-level class))
  • content/docs/api/environment-routing.mdx (via ObjectStackClient (symbol, a top-level class))
  • content/docs/api/wire-format.mdx (via ObjectStackClient (symbol, a top-level class))
  • content/docs/kernel/runtime-services/data-service.mdx (via ObjectStackClient (symbol, a top-level class))
  • content/docs/kernel/runtime-services/storage-service.mdx (via ObjectStackClient (symbol, a top-level class))
  • content/docs/permissions/authentication.mdx (via ObjectStackClient (symbol, a top-level class))
  • content/docs/permissions/system-context.mdx (via handleAIRequest (symbol, a top-level function))
  • content/docs/plugins/packages.mdx (via ObjectStackClient (symbol, a top-level class))
  • content/docs/protocol/kernel/realtime-protocol.mdx (via ObjectStackClient (symbol, a top-level class))

2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-0.mdx (via ObjectStackClient (symbol, a top-level class))
  • content/docs/releases/v17/17-2.mdx (via ObjectStackClient (symbol, a top-level class))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 32 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 6059b29c03e470dbaa513cc3f0992a8e610855bepackageMentionDocs.

Which tree this was computed on

This run read content/docs from 0d4c77d2dd74403ef47c062b8c868ce8917c5bd3 — the merge of head c3b4c470f835e71ca501b385a422bf631876254c into base 6059b29c03e470dbaa513cc3f0992a8e610855be, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 0d4c77d2dd74403ef47c062b8c868ce8917c5bd3 && git checkout 0d4c77d2dd74403ef47c062b8c868ce8917c5bd3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6059b29c03e470dbaa513cc3f0992a8e610855be c3b4c470f835e71ca501b385a422bf631876254c && git checkout -B drift-repro 6059b29c03e470dbaa513cc3f0992a8e610855be && git merge --no-ff c3b4c470f835e71ca501b385a422bf631876254c

node scripts/docs-audit/affected-docs.mjs --json 6059b29c03e470dbaa513cc3f0992a8e610855be

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 6059b29c03e470dbaa513cc3f0992a8e610855be → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling labels Sep 12, 2026
@claude
claude Bot marked this pull request as ready for review September 12, 2026 12:14
@claude
claude Bot added this pull request to the merge queue Sep 12, 2026
Merged via the queue into main with commit 3c86008 Sep 12, 2026
49 of 50 checks passed
@claude
claude Bot deleted the claude/issue-16211-ai-slot-501-not-404 branch September 12, 2026 12:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling

Projects

None yet

1 participant