Uh oh!
There was an error while loading. Please reload this page.
fix(runtime): endpoint-route 401 arm writes the shared anonymous-deny body — code key included - #9848
Conversation
…_BODY — code key included (#9823) The mountRouteOnServer 401 arm wrote an inline { error, message } copy of the flat deny body, so #9487's additive code key never reached it. The arm now writes @objectstack/core's ANONYMOUS_DENY_BODY / ANONYMOUS_DENY_STATUS verbatim (additive only per the #9487 maintainer ruling), and a pin asserts this arm's exact body — the existing integration test covers the OTHER (http-dispatcher, nested-envelope) path, not this one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WeN7F6jQFpcqW2BN56RdPa
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ✅ What this run could not seeCoarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 61d897354e8789dcbfc097218d3f372ceca20784 && git checkout 61d897354e8789dcbfc097218d3f372ceca20784
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e77a9b45e4ea3a0a9960446f52f0e6689ce3a9e4 7dffa23303f13d1866ab601ecf004c991b6f28a6 && git checkout -B drift-repro e77a9b45e4ea3a0a9960446f52f0e6689ce3a9e4 && git merge --no-ff 7dffa23303f13d1866ab601ecf004c991b6f28a6
node scripts/docs-audit/affected-docs.mjs --json e77a9b45e4ea3a0a9960446f52f0e6689ce3a9e4 |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#9823
Part of the #9487 acceptance property ("one documented key identifies the machine code on every error family, 401 included") — no closing keyword for #9487 by design; whether that card can now close is the PM's call. #9559 / ADR-0112 D5 (envelope convergence) is deliberately not touched: this arm stays in the flat family, additive key only, per the #9487 maintainer ruling.
What
mountRouteOnServerinpackages/runtime/src/dispatcher-plugin.ts(theif (route.auth !== false && !user)arm) wrote an inline{ error, message }copy of the flat anonymous-deny body, so #9487's additivecodekey (landed in@objectstack/core'sANONYMOUS_DENY_BODYvia PR #9824) never reached it. The arm now writesANONYMOUS_DENY_BODY/ANONYMOUS_DENY_STATUSverbatim. Wire effect: hook-emitted endpoint routes answer anonymous callers401 { error, code, message }— additive only, no key removed or moved, message byte-identical.Files: the 401 arm + one import line in
dispatcher-plugin.ts; a new pindispatcher-plugin.route-auth-deny-body.test.ts; a changeset (@objectstack/runtimeminor). Sibling card #9813's two discovery bodies in the same file are untouched — that card is held serial behind this one. #9813 is not addressed here; #9559 remains open.Measurements (the dispatch's Zone-2 assumptions)
ANONYMOUS_DENY_STATUSis 401 (matches the arm's hardcoded status),ANONYMOUS_DENY_MESSAGEis byte-identical to the inline message, the constant carries no header behaviour (the arm's securityHeaders loop is untouched), and@objectstack/corealready exports all of it on this file's existing import path. The only wire delta is thecodekey.152bff8fcd, merge-base of this branch): exact-message grep → 2 non-test producers (the core constant + this inline copy); tolerant prefix grep ("Authentication is required") → the same 2;error: 'UNAUTHENTICATED'literal producers → 1 (this arm; the other hit is a core docstring line, not a producer). Control (typo'd message) → 0, proving the grep shape distinguishes. No third producer.dispatcher-plugin.endpoint-fallback.integration.test.tsasserts a nestedbody.error.code— that is the http-dispatcher wildcard path, a different 401 producer in the wrapped family. No existing test pinned this arm's flat body. The new pin exercises the concrete hook-route mount (__aiRoutesrecovery path) and spells the exact body literally.registerAIRoutesmounts/ai/*wildcards for get/post/delete/put only, so a PATCHRouteDefinitionunder/ai/*(a legal method per the interface) reaches these concrete mounts unshadowed, as does any emitted path outside/ai/*. The arm is live on the wire; the issue's premise holds.Tests — union run at
7dffa23303(the final commit; no push since)auth: false→ open).git restore --source=origin/mainon the source file → exactly the 2 body pins fail (AssertionError: expected { error: 'UNAUTHENTICATED', …(1) } to deeply equal { …(2) }— the missingcode), the 2 behaviour tests stay green (2 failed | 2 passed). Restored from HEAD, clean porcelain, 4/4 green again. No ablation-rebuild caveat: the pin imports./dispatcher-plugin.jsas a same-package sibling source, compiled by vitest directly — the mutation never had to reach adist/.@objectstack/runtimesuite: 176 files / 2621 tests green.pnpm --filter @objectstack/runtime typecheckgreen.node scripts/pm/dispatch-gates.mjs, no hand-fed paths): all 13 green — changeset family (adr-0087, no-major, empty, gate-self-tests, objectui-changeset), cross-package-test-inputs (both spellings), affected-docs, and the convention-triggered test-file set (query-options-erasure, type-check-coverage, type-check-debt--re-measureafter a full packages-closure build — "none above its recorded number", engine-double-contract, where-matcher). Pluspnpm lint(mandated regardless of derivation) andcheck:nul-bytes— both green.Out of scope, reported
anonymous-deny.tsdocstring says "Exactly one consumer writes it" — true today, false once this lands (this arm becomes the second writer). Outside the declared file surface; filed as a finding issue rather than a rider.Generated by Claude Code