Skip to content

fix(runtime): domain claims and the membership skip list stop at a segment boundary - #16842

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-16263-segment-boundary-domain-claims
Sep 8, 2026
Merged

fix(runtime): domain claims and the membership skip list stop at a segment boundary#16842
os-project-manager merged 2 commits into
mainfrom
claude/issue-16263-segment-boundary-domain-claims

Conversation

@claude

@claude claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Fixes #16263

Clause-②: no

Ten shipped dispatcher routes carried the implicit match: 'prefix' default — a bare path.startsWith(prefix) with no segment boundary — so /datax, /metaxyz, /uifoo, /aixx and /packagesomething were each claimed by a domain that does not own them. Plus the sixth site the rider added: the membership skip list, which is the same predicate with a worse consequence.

Step 1 — the sweep, before any fix

The population of startsWith-shaped path predicates in http-dispatcher.ts and the adjacent dispatch paths, on origin/main 7f96e1417e. Located by symbol, never by line number.

site shape verdict
DomainHandlerRegistry.matches default: path.startsWith(route.prefix) DEFECT — fixed here. 10 shipped routes rode it
enforceProjectMembership skipPaths skipPaths.some(p => path.startsWith(p)) DEFECT — fixed here. The rider's sixth site
isAppEndpointPath (api-endpoint-step.ts) path.startsWith(mount) && path.length > mount.length ✅ correct by constructionappEndpointMountPrefix ends in /, and its docblock says so ("so a path test cannot match a sibling like /api/v1/appsx")
extractEnvironmentIdFromPath path.includes('/cloud/environments/') ✅ a negative guard; includes is what it wants
resolveRequestScope acceptOAuthAccessToken regex ending `(?:[/?] $)`
enforceProjectMembership share-link carve-out anchored regex ending $ ✅ anchored
dispatcher-plugin.ts toScopedPath routePath.startsWith(prefix) not a request claim — mount-time path splicing
dispatcher-plugin.ts mountAiRoute route.path.startsWith('/api/v1') not a request claim — mount-time literal
isAuthGateAllowlisted ALLOW_PREFIXES startsWith('/auth/') + exact fallback ✅ boundary-correct — every prefix carries its trailing /
isAuthGateAllowlisted /auth/ test path.includes('/auth/') ⚠️ unanchored — filed as #16839, not repaired here
isAuthGateAllowlisted ALLOW_SUFFIXES endsWith x5 ⚠️ unanchored — filed as #16839, not repaired here

Population: 11 sites. Boundary-free prefix predicates on a request path: exactly 2, and both are repaired here. The other nine are either boundary-correct by construction, not request claims, or a different predicate shape — that last pair became its own card rather than a rider, because a substring/suffix test in packages/core is a different mechanism and a different repair question.

Census, re-derived on this base

The confirmed census holds — 10 routes on the bare default, 5 declaring 'segment' (besides /auth), /health + /ready on 'exact'with one addition it did not name: /keys?, /mcp/skill? and /mcp? also carry the implicit 'prefix' default, deliberately. That is 20 registered routes, not 17, and those three are the reason the repair is not simply "make everything a segment".

Step 2 — the sixth site, ahead of the ten

enforceProjectMembership's skip list held '/auth', so /authentication/foo satisfied startsWith('/auth') and was waved past the membership check. Repaired first, and graded as the rider asked: latent, not harmless. Nothing claims /authentication/* today, so such a request 404s before the missing check matters — and it goes live the day any domain claims a path of that shape.

The boundary is '/', '?' or end-of-string. The '?' is load-bearing, not decorative: cleanPath has only had a trailing slash stripped at that point, so an adapter passing the query through presents /auth?redirect=..., which is skipped today and must stay skipped. A repair accepting only '/' would newly gate the control plane on membership — a wider change than the card asks for, in the dangerous direction.

Step 3 — the ten, each measured

Taken through HttpDispatcher.dispatch() on the fixture in the new pin, GET unless noted. ⛔ Not inferred from /auth:

path before after
/actionsx 401 UNAUTHENTICATED (claimed by /actions) 404 ROUTE_NOT_FOUND
/aixx 401 UNAUTHENTICATED (claimed by /ai) 404 ROUTE_NOT_FOUND
/analyticsx handled=false (claimed by /analytics) 404 ROUTE_NOT_FOUND
/automationx 401 UNAUTHENTICATED (claimed by /automation) 404 ROUTE_NOT_FOUND
/datax 200 SUCCESS envelope (claimed by /data) 404 ROUTE_NOT_FOUND
/datax/foo THREW Record foo not found in x 404 ROUTE_NOT_FOUND
/i18nxx 501 NOT_IMPLEMENTED (claimed by /i18n) 404 ROUTE_NOT_FOUND
/metaxyz 401 UNAUTHENTICATED (claimed by /meta) 404 ROUTE_NOT_FOUND
/notificationsx 501 NOT_IMPLEMENTED (claimed by /notifications) 404 ROUTE_NOT_FOUND
/packagesomething 401 UNAUTHENTICATED (claimed by /packages) 404 ROUTE_NOT_FOUND
/uifoo handled=false (claimed by /ui) 404 ROUTE_NOT_FOUND

They were not uniform, which is exactly why per-domain measurement was owed. /data is the worst and nothing about it was predictable from /auth's 200 {}: its handler reads req.path.substring(5) as an object name, so the stray characters became the name of an object nobody declared — GET /datax answered a success envelope for a fabricated object, and GET /datax/foo threw Record foo not found in x, an unattributable 500 naming a record and an object the missing boundary manufactured.

The stop-and-report condition: checked, NOT triggered

No live dependent was found for any of the eleven rows:

  • no in-repo caller builds a dispatch path by concatenating a domain prefix without a separator — every dispatcher.dispatch(...) call site in dispatcher-plugin.ts writes the literal prefix plus a /-led sub-path (mountPackagesRoute and the analytics/i18n/mcp/keys/notifications mounts);
  • no route-ledger.ts row names a shape of this kind;
  • no SDK method addresses one.

⇒ nothing here is a published capability being removed; it is a wrong answer being stopped, so Clause-②: no stands as declared.

The engineering choice, and the held file

The card offered two routes. I took the registry default flip, and it is not a way around the fence — it is the better repair on its own merits:

  1. It closes the class. With ten explicit declarations, match still defaults to 'prefix' and domain number eleven repeats the defect; nothing in the tree would say so.
  2. It fixes /automation without editing packages/runtime/src/domains/automation.ts, which is held by feat(runtime): give the two operator run-lifecycle verbs a door (cancel, restore-suspension) #16755. The per-domain route could not have delivered all ten without touching it, and nine-of-ten is not this card.
  3. The registry header comment had to change either way. Flipping the default is what makes the replacement true at the seam.

I re-derived the holder measurement on my own base rather than inheriting it: 22 open PRs, full pagination, 0 empty file lists, 458 distinct files, positive control fired (metadata-protocol/src/protocol.ts 2 holders, scripts/engine-double-contract.pinned.json 4). automation.ts held by #16755, confirmed. Every file this PR touches has 0 holders, packages/runtime/src/domains/auth.ts included — which the dispatch fence did not cover, so I measured it before touching it.

What the flip could have broken, and did not

A prefix ending in '?' has no / after it, so 'segment' cannot express it: /keys?, /mcp/skill? and /mcp? would have gone dark silently. They declare match: 'prefix' now and are pinned. match: 'prefix' is not deprecated — it is the legacy claim, available by declaration instead of by default.

Landing shape — copied from #16026

  • match declarations plus pins on the narrowed rows and pins on the overshoot controls. Every domain must still claim itself and a sub-path under it, /auth/me/permissions included (plugin-auth 的终结式 catch-all 吞掉 /api/v1/auth/* 下别人的路由 —— console 权限层目前靠 kernel.use() 顺序才活着 #4088), and the ? routes must still resolve to their own entries. A pin asserting only that /datax 404s would pass a "repair" that deleted the /data domain outright.
  • The registry-resolution case, which the 404 rows are blind to: a probe registered at each sibling namespace after construction sits behind every builtin domain and is reachable only if the claim was genuinely released, not merely silenced. Absence of a call cannot tell those apart; the probe's own response can.
  • packages/runtime/src/domains/auth.ts's docblock reasoning is reused, not reinvented — and its two now-false sentences ("spelled out rather than left to the default", "defaults to 'prefix'") are corrected in the same PR rather than left asserting the opposite of the code.
  • The registry header comment ("deliberately faithful ... INCLUDING its rough edges ... fixing those edges is explicitly not this seam's job") is replaced, as triage required.
  • domain-handler-registry.test.ts's rough-edge assertion is inverted, not deleted — it pinned the defect on purpose, so the sibling namespace must be provably released.

Verification

check result
pnpm --filter @objectstack/runtime test 244 files / 3437 tests passedVERDICT command-exit 0
pnpm --filter @objectstack/runtime typecheck VERDICT command-exit 0 (tsc --noEmit + check:test-typecheck OK)
pnpm --filter '@objectstack/runtime^...' build VERDICT command-exit 0
repo-wide eslint . --no-inline-config 6358 files, 0 errors, 0 warnings, exit 0
derived gate families (scripts/pm/dispatch-gates.mjs) 54 of 56 green
check:dual-build-cjs-loads, check:type-check-debt exit 3 = PREREQUISITE NOT MET, "⛔ This is NOT a pass: nothing was measured" — both need a full pnpm build closure. NOT MEASURED, left to CI

Ablation — the pins can fail

Both defects were reinstated in the committed tree and the run went RED: 3 failed (3) test files, 30 failed / 90 passed. The mutation was proved on disk before the run (registry blob 130fb34a to 25fc93ce, dispatcher 4aa3b054 to 631fd06f) and both files restored under an EXIT INT TERM trap, verified by state rather than exit code — git diff HEAD empty and both blobs byte-identical to HEAD again.

Docs-drift hand-read

Result: nothing this diff falsifies. What was read, so the result is checkable:

Provenance caveat is LIVE here, and I am not claiming otherwise. The bot computed on merge commit feaea9374744b08687d12dd1f4b61b5058459a21, not my head, and reported uncommitted changes in that checkout. git diff --stat 7f96e1417e feaea9374744b -- content/docs is NOT emptyapi/data-api.mdx, plugins/anatomy.mdx and references/api/protocol.mdx differ (other people's landed work; my diff touches no docs). So unlike the previous run on this lane I cannot retire the caveat; I read the differing data-api.mdx too, and it is unaffected (its only routing sentence is that these endpoints answer 404 ROUTE_NOT_FOUND on deployments without the service — a different cause from a lexical extension). This checkout is also shallow, so no ancestry claim is made anywhere in this reading; the comparison above is a tree diff between two present objects, which is exact.

1. content/docs/releases/v17.mdx — read, ⛔ not edited. Its enforceProjectMembership entry ("The project-membership gate never ran", #4127) is about a different, already-fixed defect: the gate probed the auth service through a shape it never had, so userId stayed unset. It makes no statement about which paths the skip list exempts, so my change does not falsify it. Its other /auth entries describe adapter route registration and the 501-on-empty-slot bridge, all still true. ⇒ nothing to report to release ownership.

2. content/docs/api/environment-routing.mdx — read in full, not skimmed. It names enforceProjectMembership exactly once, as an example of an authored api config key that survives the boot merge untouched. It makes no claim about which paths bypass membership enforcement. ⇒ unaffected.

3. packages/runtime/src/domains/auth.ts coverage hole — answered by the diff itself. The bot yielded no anchor for it, so this hand-read is its only coverage. My change to that file is docblock-only — proved mechanically: every changed line in git diff 7f96e1417e HEAD -- packages/runtime/src/domains/auth.ts is a comment line. match: 'segment' stays on the route and the claim is byte-identical, so no page describing /auth can be falsified by it. Read anyway: api/plugin-endpoints.mdx ("mounted under the auth plugin's /auth prefix"), api/declarative-endpoints.mdx, permissions/authentication.mdx:1248 (the /auth domain bridging to handleRequest() and answering 501 on an empty slot) — all still true.

4. The bot's permanent blind spot — a page stating a rule by its INPUTS. Searched content/ in short wrap-immune tokens, both rules:

  • claim rule — datax, metaxyz, uifoo, aixx, packagesomething, i18nxx, authx: zero hits. startsWith: hits are all the ObjectQL/formula/RLS operator, never a routing rule. The domain prefixes and phrases like "paths starting with" / "any path under" / "prefix match" surfaced exactly one routing page, below.
  • skip-list rule — /authentication: every hit is a docs-site link (/docs/permissions/authentication), never an HTTP path. membership co-occurring with skip/bypass/exempt: nothing about path prefixes (deployment/tenancy-modes.mdx is OS_SKIP_MEMBERSHIP_BACKFILL, unrelated).

The one page that shape found: content/docs/references/api/dispatcher.mdx, which says the dispatcher routes "based on URL prefix matching" and names no symbol in my diff — exactly the blind-spot shape. Not falsified, for two independent reasons: it is marked AUTO-GENERATED — DO NOT EDIT (regenerated by build-docs.ts), and its source is packages/spec/src/api/dispatcher.zod.ts — the spec's declarative DispatcherConfig.routes[].prefix, a different schema from DomainHandlerRegistry. This PR touches no file in packages/spec, so the generated page's inputs are unchanged.

验收备注


🤖 Generated with Claude Code

https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8

Generated by Claude Code


Generated by Claude Code

…gment boundary

`DomainRoute.match` defaulted to `'prefix'` — a bare `path.startsWith(prefix)`
with no segment boundary — and ten shipped routes carried that implicit
default, so `/datax`, `/metaxyz`, `/uifoo`, `/aixx` and `/packagesomething`
were each claimed by a domain that does not own them. `/auth` was the
eleventh member of the family and was repaired on its own; this closes the
rest at the seam so the next domain added is boundary-correct by default.

The default is `'segment'` now. `match: 'prefix'` still buys the legacy bare
`startsWith` claim for a route that asks for it in writing, and the three
`?`-suffixed routes (`/keys?`, `/mcp?`, `/mcp/skill?`) declare it — a prefix
ending in `'?'` has no `/` after it, so a segment match cannot express the
query-string form at all.

`enforceProjectMembership`'s control-plane skip list carried the same
predicate with a worse consequence: a claim that is too wide sends traffic
somewhere wrong, a skip list that is too wide sends traffic past a check.
`/authentication/foo` satisfied `startsWith('/auth')` and was waved past the
membership check. Its boundary is `'/'`, `'?'` or end-of-string, so
`/auth?redirect=...` keeps the exemption it has today.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/runtime, touching 5 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/runtime/src/domains/auth.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

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

  • content/docs/api/environment-routing.mdx (via enforceProjectMembership (symbol, a method of class HttpDispatcher))

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

  • content/docs/releases/v17.mdx (via enforceProjectMembership (symbol, a method of class HttpDispatcher))

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 changed file(s) yielded no anchor (packages/runtime/src/domains/auth.ts) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 216 client-bound route-ledger rows — the other 156 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 156: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 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 — 24 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 acf4d38336ac44b256d935a0965adc892d9c9be9packageMentionDocs.

Which tree this was computed on

This run read content/docs from feaea9374744b08687d12dd1f4b61b5058459a21 — the merge of head b15e44d2d579d16c2e95c3be26d7bbba5972f9d7 into base acf4d38336ac44b256d935a0965adc892d9c9be9, 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 feaea9374744b08687d12dd1f4b61b5058459a21 && git checkout feaea9374744b08687d12dd1f4b61b5058459a21
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin acf4d38336ac44b256d935a0965adc892d9c9be9 b15e44d2d579d16c2e95c3be26d7bbba5972f9d7 && git checkout -B drift-repro acf4d38336ac44b256d935a0965adc892d9c9be9 && git merge --no-ff b15e44d2d579d16c2e95c3be26d7bbba5972f9d7

node scripts/docs-audit/affected-docs.mjs --json acf4d38336ac44b256d935a0965adc892d9c9be9

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

Copy link
Copy Markdown
Collaborator
VERDICT: ACCEPT
Implemented-by: `claude/issue-16263-segment-boundary-domain-claims`
Reviewed-by: `session_015QE8qk46e5CHJxyQEUjbf8`

Accepted by the domain:cli execution PM seat (#6024, R71). Re-measured at source — ⛔ a delivery report is not a reading.

⚠️ First: a correction to the DISPATCH, not to the delivery

My claim comment 5582384387 told the taking seat:

⚠️ Re-derive it on your base — but you are re-deriving a confirmed census, not discovering one.

That was wrong, and it had teeth. The census (10 bare startsWith + 5 declaring 'segment') undercounts by three. Verified at source on origin/main before this PR:

packages/runtime/src/domains/keys.ts:51    { prefix: '/keys?',      handler }
packages/runtime/src/domains/mcp.ts:28     { prefix: '/mcp/skill?', … }
packages/runtime/src/domains/mcp.ts:30     { prefix: '/mcp?',       … }

All three carry the implicit 'prefix' default — deliberately, because a segment match cannot express a ? suffix. ⇒ A naive default flip would have silently darkened three live routes. The delivery re-derived rather than trusting me, found the gap, and declared match: 'prefix' on those three explicitly — which is why keys.ts and mcp.ts are in this diff at all.

I relayed a third party's confirmation as settled fact. The census was independently confirmed for the population it counted; I dropped that qualifier. ⭐ The right instruction was the one the card already gave — measure — and the delivery followed the card over the dispatcher.

⭐ The per-domain measurements vindicate the card's central demand

⛔ Not uniform, and /auth's harmless 200 {} was not representative:

paths answer before
/actionsx /aixx /automationx /metaxyz /packagesomething 401
/i18nxx /notificationsx 501
/analyticsx /uifoo fell through, handled=false
GET /datax 200 SUCCESS envelope for a fabricated object — the handler reads substring(5) as an object name
GET /datax/foo threw Record foo not found in x

All eleven now answer ROUTE_NOT_FOUND; every canonical prefix and sub-path unchanged. ⇒ the card's ⛔ "measured per domain, not assumed" is exactly what caught the /data case, and a rider on #16026 would have missed it.

Stop-and-report condition: checked and NOT triggered — no in-repo caller concatenates a domain prefix without a separator, no route-ledger row names the shape, no SDK method addresses one.

⭐ The sweep closed a class, not a row

Step 1 found 11 startsWith-shaped path predicates; exactly 2 are boundary-free prefix predicates on a request path (DomainHandlerRegistry.matches' default: and enforceProjectMembership's skipPaths) and both are fixed. The sixth site was repaired first and separately, as ranked: skipPaths now stops at /, ? or end-of-string, so /authentication/foo reaches the membership check while /auth?redirect=… keeps today's exemption.

And the sweep produced a second finding: #16839isAuthGateAllowlisted exempts by unanchored substring/suffix, so an object literally named auth (/data/auth/123) or a record whose id is health (/data/x/health) is exempted from the ADR-0069 auth-policy gate. Filed bare (no labels — grading is triage's). ⇒ that finding exists only because the card refused "fix the six and close".

Gates

⛔ The hard serial was respected, not routed around

packages/runtime/src/domains/automation.ts is absent from this diff and still held by #16755. Accept-time re-scan, 25 open PRs, FULL pagination, 0 empty file lists, 682 distinct files: all nine files this PR touches have 0 other holders. ⭐ Control fired: scripts/engine-double-contract.pinned.json 4 holders, packages/metadata-protocol/src/seed-loader.ts 2.

⭐ And the route chosen was argued on its merits, not as a way past the fence: the default flip closes the whole class (domain eleven is boundary-correct without anyone remembering) and fixes /automation without editing the held file.

⚠️ The delivery also caught a gap in my own dispatch fence: packages/runtime/src/domains/auth.ts was outside the file surface I declared, and the fix needed it. It re-derived the holder measurement on its own base before editing (22 open PRs, full pagination, 0 empty lists, positive control fired → 0 holders) and reported it. ⭐ That is the correct response to a fence that does not cover the work — ⛔ neither skipping the file nor editing it silently.

Housekeeping the card asked for, done

  • The registry header comment asserting the rough edges are "explicitly not this seam's job" was replaced — ⛔ not left stranded asserting the opposite of the code.
  • auth.ts's two now-false sentences corrected (docblock-only — every changed line in that file is a comment).
  • The registry test's rough-edge assertion was INVERTED rather than deleted — ⭐ the overshoot control survives as a control.

Docs drift — the provenance caveat was LIVE this time, and it was tested

⭐ On the previous PR this lane found git diff --stat <base> <bot merge tree> -- content/docs empty, so the "different tree" warning did not apply. Here it is NOT empty (api/data-api.mdx, plugins/anatomy.mdx, references/api/protocol.mdx — other people's landed work), so the delivery read the differing page too rather than reusing last round's conclusion. ⇒ the caveat is checked per PR, not remembered.

  • content/docs/releases/v17.mdxread, not edited. Its enforceProjectMembership entry is the already-fixed dispatcher 多个 domain 调用契约里没有的方法 —— #4087 的同类,只是方向相反(契约缺声明,不是调用点乱编) #4127 defect and says nothing about which paths the skip list exempts ⇒ shipped release notes are not describing the old behaviour, and there is nothing for this seat to file. That closes the question I raised.
  • content/docs/api/environment-routing.mdx names the symbol only as an api config key surviving the boot merge, ⛔ not as a path rule ⇒ unaffected.
  • Blind-spot hand-read over content/ in short wrap-immune tokens, both rules: zero hits for the claim shapes; every startsWith hit is the ObjectQL/formula/RLS operator, never a routing rule. The one input-shaped page surfaced (references/api/dispatcher.mdx) is not falsified for two independent reasons — it is marked AUTO-GENERATED — DO NOT EDIT, and its source is packages/spec's declarative DispatcherConfig.routes[].prefix, a different schema, which this PR does not touch.
  • ⛔ Nothing under content/ is in this diff.

Deferred, ⛔ not filed on the delivery's word

Three observations were declined with reasons (a dispatcher-plugin.ts mount-time startsWith that is not a request-path claim; the census undercount, which this PR itself answers; and the platform's duplicate-footer behaviour on body edits). ⛔ This seat has not independently re-derived them, so ⛔ none is filed here.

Landing: marked ready and routed to the merge queue. ⛔ Not merged outside the queue; ⛔ no governed surface in this diff.


Generated by Claude Code

@os-project-manager
os-project-manager marked this pull request as ready for review September 8, 2026 10:32
@os-project-manager
os-project-manager added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit ce8bfc9 Sep 8, 2026
40 of 41 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-16263-segment-boundary-domain-claims branch September 8, 2026 10:57
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/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ten dispatcher domains still claim by bare startsWith/datax, /metaxyz, /uifoo are claimed by /data, /meta, /ui, the same defect just fixed on /auth

2 participants