Skip to content

feat(runtime): envelope the dispatcher's two discovery bodies (#9813) - #9949

Merged
huangyiirene merged 1 commit into
mainfrom
claude/issue-9813-dispatcher-discovery-envelope
Aug 19, 2026
Merged

feat(runtime): envelope the dispatcher's two discovery bodies (#9813)#9949
huangyiirene merged 1 commit into
mainfrom
claude/issue-9813-dispatcher-discovery-envelope

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes#9813

Implements the inherited #9436 maintainer ruling (2026-08-18, option A — envelope machine-read discovery bodies, because they are the envelope's core constituency and the migration is one additive key), applied to the runtime dispatcher's two discovery bodies in packages/runtime/src/dispatcher-plugin.ts. All readings below at head f78a83e0cc.

The flip (additive only)

Both bodies located by text (the card's ~738/~765 and the claim's :775/:801 were both stale spellings of the same two sites):

  • GET /.well-known/objectstack (unconditionally dispatcher-owned) — { data }{ success: true, data }.
  • GET {prefix}/discovery — same flip. Its registration condition re-verified live, not assumed: kernel.hasPlugin('com.objectstack.rest.api') still gates it, so the route is the fallback owner in REST-less compositions, not dead.

No key removed or moved. Reader tolerance re-measured, not inherited (population stated per sweep):

  • SDK connect() (packages/client/src/index.ts :532/:557): body.data || body — unaffected.
  • QA http-adapter (packages/core/src/qa/http-adapter.ts :140): discriminates 'routes' in body, else unwraps body.data — unaffected. (Its comment describing the dispatcher wrap as { data } stays literally true of the unwrap logic it explains — the data key is still what carries the document — so it was deliberately left untouched rather than widening this PR into packages/core.)
  • objectui at 97fba31 (control: 156 fetch( sites): readers discriminate on typeof body.success === 'boolean' && 'data' in body — today's bare shape fails that predicate; the flip makes these routes unwrap correctly, the same measurement adapters/hono's two bare { data } discovery bodies lose their ratchet owner when #9364 lands #9436 reported.
  • Repo-wide code sweep for well-known/objectstack: 38 hits across 4457 non-dist .ts files, all tests/route tables/comments/redirects — no intolerant reader. The single reopening condition (a reader that chokes) did not fire.

Pinned in dispatcher-plugin.routes.test.ts: both handlers answer exactly the top-level key set ['data', 'success'] with success === true.

The gate half — rode the PR, judged small

scripts/check-route-envelope.mjs grew a fourth scan population for express-style res.json(...) writes on an IHttpServerenumerated, not discovered: the scanner's receiver set is now a parameter (Hono pair stays the default), and IHTTP_ROUTE_MODULES names dispatcher-plugin.ts, audited through the same auditPluginRouteModule grammar. No exempt entry anywhere (the rejected option B), and no existing counter weakened — the gate before/after readings are in the issue report. A discovery walk for the dialect is real gate-architecture work (fetch's zero-arg res.json() READ shares the receiver spelling; 20 candidate files measured) and is filed as follow-up #9937 per the dispatch instruction — #9937 is not addressed here.

The new population's scan found one genuinely off-envelope literal body beyond the card's two: the SSE-fallback res.json({ events }). That is a different consumer population, so the inherited ruling does not reach it — filed as #9936 (which remains open) and pinned as unenveloped: 1, ratchet: #9936, the first-registration path the gate's own NOT-DECLARED text prescribes.

Docs

Complete documented set re-swept with a control (12 well-known/objectstack hits across 393 mdx files): the two pages the card named are the set, but with three wrap-documenting spots, not two — content/docs/api/index.mdx (the wrap sentence) and content/docs/protocol/kernel/http-protocol.mdx (the wrap sentence + JSON example, and the REST-less callout's envelope parenthetical at ~:224, which the card missed). All three updated. realtime-protocol.mdx :110 shows inner fields with no envelope at all (pre-existing elision, equally true after an additive key) and client-sdk.mdx :82 documents probing, not shape — both left.

Changeset

minor on @objectstack/runtime, ADR-0087 marker not-required (no-migration-prescription) with the measured-reader reasoning inline — mirrors the accepted #9814 pattern.

Verification

  • check:route-envelope before: 3 populations, exit 0; after: 4 populations, exit 0, self-test green (new pins: same source reads 0 bodies under Hono receivers and unenveloped 1 under express receivers; relayed bodies counted, never judged).
  • Ablation from the committed state, no dist in either chain (gate reads source text; the pin test imports ./dispatcher-plugin.js relatively through vitest): de-enveloping one body → gate exit 1 with unenveloped: found 2, declared 1 and exactly the mutated route's pin red (1 failed / 15 passed); restored via checkout from the branch → gate exit 0, 16/16 green.
  • @objectstack/runtime: 176 test files / 2629 tests pass, tsc --noEmit exit 0. Downstream integration: @objectstack/http-conformance (boots a real composition and fetches these routes) 86/86 pass against the rebuilt runtime.
  • Full derived union from node scripts/pm/dispatch-gates.mjs (no hand-fed paths) plus mandated pnpm lint: all exit 0, details in the issue report.

Generated by Claude Code

Inherits the #9436 maintainer ruling (2026-08-18, option A) with its reason
intact: machine-read discovery bodies are the envelope's core constituency
and the migration is one additive key. Adds success: true to both
GET /.well-known/objectstack and the REST-less GET {prefix}/discovery
fallback, pins the shape in dispatcher-plugin.routes.test.ts, updates the
two documenting pages, and grows check-route-envelope.mjs an enumerated
fourth scan population for express-style res.json writes on an IHttpServer
(dispatcher-plugin.ts: unenveloped 1, ratcheted to #9936; the discovery
walk for the dialect is #9937).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WeN7F6jQFpcqW2BN56RdPa
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/deployment/production-readiness.mdx(via createDispatcherPlugin (symbol))
  • content/docs/plugins/packages.mdx(via createDispatcherPlugin (symbol))
What this run could not see
  • the SDK route bridge reached 45 of 221 client-bound route-ledger rows — the other 176 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 23 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 ba5709cbd630a4d4aa5ced28402a6c5250bb489epackageMentionDocs.

Which tree this was computed on

This run read content/docs from ce1fdfac330af599fade5d5d2974d044a2b9f567 — the merge of head f78a83e0cc05d1425822117f33aca27c81920c56 into base ba5709cbd630a4d4aa5ced28402a6c5250bb489e, 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 ce1fdfac330af599fade5d5d2974d044a2b9f567 && git checkout ce1fdfac330af599fade5d5d2974d044a2b9f567
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ba5709cbd630a4d4aa5ced28402a6c5250bb489e f78a83e0cc05d1425822117f33aca27c81920c56 && git checkout -B drift-repro ba5709cbd630a4d4aa5ced28402a6c5250bb489e && git merge --no-ff f78a83e0cc05d1425822117f33aca27c81920c56
node scripts/docs-audit/affected-docs.mjs --json ba5709cbd630a4d4aa5ced28402a6c5250bb489e

⚠️ 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 ba5709cbd630a4d4aa5ced28402a6c5250bb489e → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Aug 19, 2026
@huangyiirene
huangyiirene marked this pull request as ready for review August 19, 2026 09:50
@huangyiirene
huangyiirene added this pull request to the merge queueAug 19, 2026
Merged via the queue into main with commit 5989b0dAug 19, 2026
27 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-9813-dispatcher-discovery-envelope branch August 19, 2026 10:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

2 participants

@huangyiirene@claude