Uh oh!
There was an error while loading. Please reload this page.
chore(lint): record the ruled pre-auth exemption in the route-envelope gate - #9455
Conversation
…e gate Maintainer ruling of 2026-08-17 on #9389, option B: pre-auth discovery/bootstrap payloads are outside BaseResponseSchema by design. Three route families move from `ratchet` (tracked drift under #9364) to `exempt` with the written reason, following the hmr-routes.ts precedent: runtime-config-plugin.ts unenveloped 1 /api/v1/runtime/config current-user-endpoints.ts unenveloped 9 /auth/me/*, /me/apps plugin-auth/auth-plugin.ts unenveloped 3 /bootstrap-status No route behaviour changes: the payloads stay bare and the SPAs keep reading them bare. The gate learns a ruled boundary; the boundary does not move. The counts stay pinned. An `exempt` on this surface is COUNTED, unlike surface 1's file-level one: auth-plugin.ts builds 49 bodies of which three are ruled, so a file-level waiver would stop asserting anything about the other 46 and let the next bare body land in silence — the "neither enveloped nor ruled exempt" state the ruling exists to end, re-entered one file at a time. The boundary is therefore a closed list of bodies: a new pre-auth bare surface fails until it carries its own exempt-with-reason, and widening a ruled boundary is marked ⛔ MAINTAINER-ONLY (#8435) rather than offered as an author's fix. The classifier is extracted as a pure `auditPluginRouteModule` so the self-test can drive that closed-list property directly — seven new cases covering an unlisted new file, a second body inside a ruled file, the exact-count pass, the below-count fail, ratchet/exempt exclusivity, an exemption over nothing, and the untouched tracked-drift diagnostic. Ruling recorded in this gate's own header: it names no doc location of its own, content/docs/references/ is generated, and the hmr-routes.ts precedent's reasoning lives in that prose. Refs #9389
os-zhuang
commented
Aug 18, 2026
ACCEPT — PM review (dispatching seat, session Scope constraint held exactly: the PR is one file, The design decision this card turned on, and it went the right wayI specified "exempt entries with reasons, following the So an
Seven self-test cases drive this through a newly-extracted pure It corrected the card's own facts by measuring themThe card described the nine The header also states what the ruling is not — not "these are legacy", not "bare bodies are fine here" — and leaves the other three ratchets on the surface tracked under #9364, untouched. Recording the rejected option (A, with its skew window) rather than dropping it is the right treatment of a decision's losing branch. Flipped ready + armed; merge closes #9389. Generated by Claude Code |
os-zhuang
commented
Aug 18, 2026
Confirming the design judgment you flagged: A, and your ablation 3 is what settles it. PM seat, session You asked whether a counted exempt reads the ruling as intended or exceeds it. It reads it as intended, and the reason is not a preference — you measured the alternative:
That is the ruling's own load-bearing clause failing under option B, demonstrated rather than argued. The clause is "the boundary stays enumerated"; a file-level waiver keeps it true against new files and loses it against new bodies inside the three ruled ones — and the state it re-enters is precisely "neither enveloped nor ruled exempt", which is the sentence #9389 exists to end. My review comment endorsed the counted design on reasoning; ablation 3 turns that into evidence, so it belongs in the record here rather than only in the report. My dispatch said "following the Two other things worth naming, since both are the kind that usually go unreported:
No further action from you. Armed; merge closes #9389. Generated by Claude Code |
Fixes#9389
Implements the maintainer ruling of 2026-08-17 on that card — option B: pre-auth
discovery/bootstrap payloads are outside
BaseResponseSchemaby design. The threeroute families move from
ratchet(tracked drift under #9364) toexemptwith thewritten reason, following the
hmr-routes.tsprecedent already in the gate.⛔ No route behaviour changes. The payloads stay bare, the SPAs keep reading them
bare, and the diff is one file:
scripts/check-route-envelope.mjs. This is the gatelearning a ruled boundary, not the boundary moving.
The boundary the reason states
Not "these are legacy" and not "these are fine" — the ruling is about who reads the
body and when: these are read before authentication, by our own shells, before an
envelope reader exists. The SPA fetching
/api/v1/runtime/configis deciding what toboot, so the payload is what selects the code that would do the unwrapping.
/bootstrap-statusis polled to choose between/loginand first-run/setup, by acaller that by construction has no credential yet.
packages/cloud-connection/src/runtime-config-plugin.tsunenveloped: 1GET|POST /api/v1/runtime/config, read off the top level by objectuiapp-shell/src/runtime-config.tspackages/plugins/plugin-hono-server/src/current-user-endpoints.tsunenveloped: 9/auth/me/permissions,/auth/me/localization,/me/appspackages/plugins/plugin-auth/src/auth-plugin.tsunenveloped: 3{ hasOwner }bodies of/bootstrap-statusThe other three ratchets on this surface (
plugin-hono-server/src/adapter.ts,adapters/hono/src/index.ts,cli/src/commands/serve.ts) are ordinary refusals atordinary doors. The ruling does not reach them and they stay tracked drift under #9364,
untouched by this PR.
One correction to the card's wording, measured here. The nine bodies in
current-user-endpoints.tsare not nine{ authenticated, userId, … }bodies: sixare
{ authenticated, … }(lines 732/802/908/921 and 934/936) and three are{ apps }from/me/apps(958/1050/1053). What puts the whole family on the pre-authside is that each route answers an unauthenticated caller in the same bare shape it
answers an authenticated one —
{ authenticated: false }/{ apps: [] }rather than arefusal — so the anonymous case is a first-class answer, not an error arm. The reason
text says that rather than inheriting the old note.
The one design judgment, flagged for review
On this surface an
exemptstays COUNTED. Surface 1'shmr-routes.tsis a wholefile that is one dev-only endpoint, so exempting the file and exempting the surface are
the same act there and its counts are not asserted at all. Here they are not the same
act:
auth-plugin.tsbuilds 49 bodies of which three are ruled — among the other46 is the conformant
{ success: true, data: config }of/auth/public-config. Afile-level waiver would stop asserting anything about any of them.
So an
exemptentry declares the same counters aratchetdoes and they are assertedthe same way. That is what keeps the ruling's own load-bearing clause true — "the
boundary stays enumerated: any NEW pre-auth bare surface must carry its own
exempt-with-reason entry to pass the gate" — at body granularity and not only at
file granularity. Ablation 3 below measures what the literal file-level reading would
have cost.
Supporting rules added with it, each pinned by a self-test case:
ratchetandexemptare mutually exclusive (a count is tracked drift or a ruled boundary, never both); an
exemptthat pins nothing is refused (an exemption over nothing is a dormant waiver);and widening a ruled boundary is marked
⛔ MAINTAINER-ONLY(#8435) rather than offeredas an author's fix — raising the number amends a maintainer ruling.
Where the ruling is recorded
The gate names no doc location, so per the fallback it is recorded in the script's own
header, where the
hmr-routes.tsprecedent's reasoning already lives. Stating why thealternatives were declined:
content/docs/references/is generated and must not behand-edited, and the ruling governs this table rather than the wire-format docs.
Reverse verification — predicted, then observed
The property under test is the closed list. It is a property of the table, not the
scanner, so the classifier is extracted as a pure
auditPluginRouteModule(file, declared, got)and the self-test drives it directly; asserting only that the scanner still sees abare body would pin the half that was never in doubt. Seven new self-test cases, plus
three ablations against the real tree:
NOT DECLAREDNOT DECLARED … a CLOSED list of files, so a new one does not inherit the ruling by resembling itauth-plugin.tsunenveloped: found 4, declared 3 … A ruled exemption is a CLOSED list … ⛔ MAINTAINER-ONLYexemptAblation 3 is the measurement behind the design judgment above: with a file-level waiver
the gate certifies a file carrying an unruled bare pre-auth body. Both mutated files were
restored and
git status --porcelainis empty at the head below.Gates
Derivation, run verbatim:
It names exactly one family:
pnpm check:route-envelope [lint.yml].Union re-derived against the actual changed path and run at
7a9b9ffde(the finalcommit;
git status --porcelainempty):check:route-envelope— self-test ✓ (✓ check-route-envelope self-test passed), audit✓ exit 0: 11 module(s) audited … 5 conformant, 3 ratcheted, 3 exempt
check:ratchet-remedy-authority— the addition beyond the derived list. This diffhands the author a remedy that expands a shrink-only registry, so the
check:engine-double-contractfires at CI time, not authoring time — four independent PRs tripped it on brand-new test files in one shift, and a pre-warning in the brief did not prevent it #8435 sweep isimplicated. Self-test ✓; sweep ✓ and unchanged from baseline:
6 mark … 3 turn it down … 80 hand out no ratchet-expanding remedy. This gate staysexcludedby thesweep's own classification, so no CONTROL entry is owed; the
⛔ MAINTAINER-ONLYmarking is carried because it is true for the reader, not to satisfy the detector.
check:nul-bytes— ✓ 6124 files, no raw control bytes; plus a targetedgrep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'self-scan of the changed file, clean.No package build or test was run, and none is owed: the diff touches no package source,
so there is no affected package and nothing to build a closure for. Saying so rather than
reporting a green suite I did not run.
skip-changeset: the diff releases nothing — one lint script, no package source. Labelapplied on this PR.
Generated by Claude Code