Skip to content

docs(spec): record the measured non-promotion of ./api in the browser-reachable ledger - #18574

Merged
os-bill merged 1 commit into
mainfrom
claude/issue-17535-api-entry-ledger-judgement
Sep 17, 2026
Merged

os-bill merged 1 commit into
mainfrom
claude/issue-17535-api-entry-ledger-judgement

Conversation

@os-bill

@os-bill os-bill commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

Fixes #17535

Clause-②: no

The card offers two closures, verbatim: 「Either judge ./api in 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 ./api cannot make it.

browserReachable has 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 in check: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 on f962be9d08 (promote ./api into browserReachable, drop it from unjudged, run the gate, restore):

before: './api' inside unjudged array = 1      after: 0
before: './api' as a browserReachable key = 0  after: 1
mutated blob aeb4ed01e9 differs from HEAD blob 3833f5a5c6 — the mutation is on disk

ABLATION gate exit = 1
  x './api' is DECLARED browser-reachable but its built graph links 'zod' (via dist/api/index.mjs)
  x './api' is DECLARED browser-reachable and links the undeclared external 'pg-connection-string' (via dist/api/index.mjs)
  x  ... the same pair again for dist/api/index.js

restore: blob matches HEAD (3833f5a5c6) — `git diff HEAD` clean

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 _unjudgedComment says 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 0aa88eb6b0 and at the merge commit 9165d5cd4c, in this container. Controls: ./contracts and ./meta-spelling measured byte-identical across the pair, so the deltas below are that PR's and not the week's drift.

axis parent at the merge delta card's figure
./api entry bundle, raw 1,433,893 1,867,678 +433,785 +444,960 — reproduces, within 2.5%
./api entry bundle, gzipped 418,675 546,266 +127,591 (+30.5%) +131,398 — reproduces, within 2.9%
browser bundle of the whole ./api namespace, gzipped 273,904 330,104 +56,200 (+20.5%) +19.4% — this is the axis that percentage is on
source graph inputs of src/api/index.ts 110 159 +49 188 -> 237 (+49) — delta exact; the absolute base differs by instrument
CONTROL ./contracts, gzipped 3,809 3,809 0
CONTROL ./meta-spelling, gzipped 947 947 0

On main today (f962be9d08): ./api is 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, ./api had no browser condition and linked only zod — no pg-connection-string. The fs link 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):

probe — the real import, verbatim parent at the merge delta
@object-ui/core utils/column-sortability.ts — two string constants 132,121 261,221 +129,100 (+97.7%)
@object-ui/data-objectstack metadata-client.ts — one zod schema 132,728 261,788 +129,060 (+97.2%)
@object-ui/app-shell clientValidation.ts — one zod schema, dynamic 132,079 261,187 +129,108 (+97.8%)
whole ./api namespace (for contrast) 273,904 330,104 +56,200 (+20.5%)

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: 「objectui and cloud … were not measured, and are not reachable from the session that found this.」 objectui is attached to this session.

Measured at objectui dda8f3815d⚠️ this session's attached checkout, not the pinned .objectui-sha 53ded82bf7, 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:

packages/app-shell/src/views/metadata-admin/clientValidation.ts   (dynamic import, ApiEndpointSchema)
packages/core/src/utils/column-sortability.ts                     (re-exports two constants)
packages/data-objectstack/src/metadata-client.ts                  (GetMetaItemLayeredResponseSchema)
packages/plugin-chatbot/src/usePendingActions.ts
packages/react/src/utils/error-message.ts
packages/types/src/data.ts

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 own browserReachable entry already names (ScreenView.tsx, metadata-admin/external/api.ts, ShareDialog.tsx), and 0 for ./meta-spelling.

2d. cloud — NOT MEASURED

The cloud repository is not attached to this session. Declared unmeasured, ⛔ not reported as zero impact. 「一个没覆盖到目标总体的读数,只为它实际覆盖的那部分背书。」

2e. ⭐ ./api is one of fifteen, and the measurement does not single it out

The same scan over objectui's browser-shipped non-test source, per unjudged entry — value-import file counts:

./ui 48   ./data 30   ./kernel 12   ./api 6   ./security 5   ./shared 5
./automation 3   ./system 3   ./identity 2   . 4   ./ai 1   ./integration 1
./marketplace 0   ./qa 0   ./studio 0

Twelve of the fifteen are reached. Three are reached harder than ./api, and ./kernel is the heavier bundle (1,544,209 raw / 457,761 gzipped on f962be9d08). Whatever singles ./api out, 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 ./api alone, 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.

  • a new _measuredNonPromotions map — entries measured for weight and left in unjudged on purpose, each carrying the reading that decided it, the trees it was taken against, the objectui leg, the cloud boundary, and what would change the verdict. unjudged is 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.
  • _unjudgedComment amended 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-entries is green before and after.

4. Changeset

skip-changeset, measured rather than assumed. browser-reachable-entries.json is not in @objectstack/spec's files[], so nothing in this diff ships:

grep over the published files[] paths (dist, json-schema, liveness, prompts, llms.txt,
README.md, src/**/*.zod.ts, CHANGELOG.md, api-surface, spec-changes.json):
  '_measuredNonPromotions'  -> 0 published files
  POSITIVE CONTROL 'ApiEndpointSchema' -> 31 published files

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:

  • The ledger's _unjudgedComment reserves 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.
  • The card's anchor commit c93e1f456a09 does 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 boundary 0aa88eb6b0 vs 9165d5cd4c is 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 (via scripts/pm/os-verify-lock.sh)
  • pnpm --filter @objectstack/spec check:browser-reachable-entries — exit 0, before and after the edit
  • ablation (route A) — gate exit 1 with 4 problems; restore proven by blob hash and a clean git diff HEAD
  • grep -naP control-character scan over the changed file — no hits

Generated by Claude Code

…-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>
@os-bill os-bill added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 17, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/spec/browser-reachable-entries.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/browser-reachable-entries.json) — pages documenting those are invisible to this run
  • 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 — 136 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 340b6dcf74df4f2e931e26f8fb795ce9b5dbf0cbpackageMentionDocs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants