docs(spec): record the measured non-promotion of ./api in the browser-reachable ledger - #18574
Merged
Merged
Conversation
…-reachable ledger `./api` sits in `browser-reachable-entries.json`'s `unjudged` list, where rule 1 (the schema-free promise) asserts nothing. The ledger offered two closures for that: judge the entry, or record deliberately that it stays unjudged and why. Measurement picks the second, because the first is not available as a ledger edit at all: `browserReachable` is a binary schema-free promise, not a byte budget, and this entry's vocabulary IS the zod graph. Adds `_measuredNonPromotions`, a per-entry record for entries whose weight was measured and which stay unjudged on purpose, and amends `_unjudgedComment` so "this gate asserts nothing" can no longer be read as "nobody looked". Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude <noreply@anthropic.com>
Contributor
📓 Docs Drift Check
What this run could not see
Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
This was referenced Sep 17, 2026
os-bill
marked this pull request as ready for review
September 17, 2026 02:39
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #17535
Clause-②: no
The card offers two closures, verbatim: 「Either judge
./apiin the ledger (giving rule 1 something to assert), or record deliberately that it stays unjudged and why. Both are answers; silence is not.」 This PR takes the second, and the measurement below is why — the first turns out not to be an available ledger edit at all.1. Route A is not a threshold. It is a binary schema-free promise, and
./apicannot make it.browserReachablehas no byte axis. Its two assertions, from the gate's own header, are (1) no zod anywhere in the entry's built module graph and (2) every bare external declared. There is no threshold incheck:browser-reachable-entries— for any entry, judged or not../api's vocabulary is the zod graph: the symbols a consumer reaches there are request/response schemas. Ablated onf962be9d08(promote./apiintobrowserReachable, drop it fromunjudged, run the gate, restore):So "judge
./api" is not a knob anyone can turn: it would first require the entry to stop linking zod, which is a redesign of a published export surface, not a ledger edit. The ledger's own_unjudgedCommentsays the same thing from the governance side — promotion is 「a maintainer decision plus a passing gate, never a ledger edit alone」.2. The measurement — the card's first deliverable
2a. The card's reading reproduces, isolated to PR #17517
Built from source at the merge's parent
0aa88eb6b0and at the merge commit9165d5cd4c, in this container. Controls:./contractsand./meta-spellingmeasured byte-identical across the pair, so the deltas below are that PR's and not the week's drift../apientry bundle, raw+444,960— reproduces, within 2.5%./apientry bundle, gzipped+131,398— reproduces, within 2.9%./apinamespace, gzipped+19.4%— this is the axis that percentage is onsrc/api/index.ts188 -> 237(+49) — delta exact; the absolute base differs by instrument./contracts, gzipped./meta-spelling, gzippedOn
maintoday (f962be9d08):./apiis 1,914,896 raw / 560,968 gzipped on the node condition and 1,912,968 / 560,390 on the browser condition, 160 source graph inputs.One correction to the card's mechanism, not its numbers: before PR #17517,
./apihad nobrowsercondition and linked onlyzod— nopg-connection-string. Thefslink is that PR's own consequence, and the same PR resolved it. Rule 2 was green before (by absence) and is green after (by the swap).2b. ⭐ The reading the card does not carry: the real consumers pay roughly twice, not +19.4%
The
+19.4%is measured on a bundle that imports the whole namespace. No consumer does that. Bundling exactly what the named objectui modules import (esbuild 0.28.2,platform: browser,conditions: ['browser','import'], minified, gzip -9):@object-ui/coreutils/column-sortability.ts— two string constants@object-ui/data-objectstackmetadata-client.ts— one zod schema@object-ui/app-shellclientValidation.ts— one zod schema, dynamic./apinamespace (for contrast)The narrower the import, the worse the ratio: tree-shaking recovers proportionally less of the new graph than of the old one. A browser bundle whose only use of this entry is two re-exported string constants went from 132 KB to 261 KB gzipped. The headline percentage understates what the actual call sites pay by about 5x.
2c. The objectui leg — measured, and it was the half the card could not reach
The card: 「
objectuiandcloud… were not measured, and are not reachable from the session that found this.」objectuiis attached to this session.Measured at objectui⚠️ this session's attached checkout, not the pinned
dda8f3815d—.objectui-sha53ded82bf7, which is absent from that shallow clone and was deliberately not fetched or bumped to take a reading. Six browser-shipped non-test source files value-import@objectstack/spec/api:So the bytes are paid by a real downstream browser consumer, not hypothetically. Control for the scanner: it reports 9 such files for
./contracts, matching the sites this ledger's ownbrowserReachableentry already names (ScreenView.tsx,metadata-admin/external/api.ts,ShareDialog.tsx), and 0 for./meta-spelling.2d.
cloud— NOT MEASUREDThe
cloudrepository is not attached to this session. Declared unmeasured, ⛔ not reported as zero impact. 「一个没覆盖到目标总体的读数,只为它实际覆盖的那部分背书。」2e. ⭐
./apiis one of fifteen, and the measurement does not single it outThe same scan over objectui's browser-shipped non-test source, per unjudged entry — value-import file counts:
Twelve of the fifteen are reached. Three are reached harder than
./api, and./kernelis the heavier bundle (1,544,209 raw / 457,761 gzipped onf962be9d08). Whatever singles./apiout, it is not its downstream reach and it is not its weight — it is that a contract review happened to be standing next to it. The ledger row this PR adds is therefore written to be a record about./apialone, and says so twice, so it cannot be read as a clean bill for the other fourteen.3. What landed
One file:
packages/spec/browser-reachable-entries.json._measuredNonPromotionsmap — entries measured for weight and left inunjudgedon purpose, each carrying the reading that decided it, the trees it was taken against, the objectui leg, thecloudboundary, and what would change the verdict.unjudgedis a flat array of subpaths, so a member has nowhere to carry a reason; without this map a deliberate non-promotion and a never-examined entry are the same silence, which is the state the card was filed against._unjudgedCommentamended so 「this gate asserts NOTHING about」 can no longer be read as "nobody looked", and so an entry absent from the new map reads as a third state — unmeasured — rather than as either of the first two.⛔ No entry moves between sections, so the gate's reconciliation is untouched and no fixture or pin test is implicated.
check:browser-reachable-entriesis green before and after.4. Changeset
skip-changeset, measured rather than assumed.browser-reachable-entries.jsonis not in@objectstack/spec'sfiles[], so nothing in this diff ships:The two published files that do contain the string
browser-reachable-entries(README.md,CHANGELOG.md) mention the gate's name in prose; neither is touched by this diff.Acceptance notes
Noted, not filed — observations, no card:
_unjudgedCommentreserves promotion to 「a maintainer decision plus a passing gate」, while this card's triage ruling classed moving an entry into the judged set as 「既有门禁内部参数与盲区修复(加强,非削弱,非新增)」. The two do not collide in practice here, because route A reds the gate regardless, so no one had to choose. Recorded rather than resolved.c93e1f456a09does not resolve in this repository (fatal: Not a valid object name), so the exact figures could not be re-derived at the tree they were taken against. The merge boundary0aa88eb6b0vs9165d5cd4cis a strictly better anchor for the same question and is what the reproduction above uses.One finding is handed to the dispatching seat with its readings rather than filed here, because it is a decision and not a repair: a schema-bearing browser-reachable entry has no weight axis in this gate at all, so "watch
./api's bytes" cannot be satisfied by any ledger edit — it needs a new rule, and section 2b's+97.7%is the reading that makes the size of that rule a product question rather than a mechanical one.Verification
pnpm --filter @objectstack/spec build— exit 0 (viascripts/pm/os-verify-lock.sh)pnpm --filter @objectstack/spec check:browser-reachable-entries— exit 0, before and after the editgit diff HEADgrep -naPcontrol-character scan over the changed file — no hitsGenerated by Claude Code