Skip to content

docs(lint): correct the ./runtime subpath's "light entry" claim with measurements - #9802

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-9772-runtime-subpath-doc
Aug 19, 2026
Merged

docs(lint): correct the ./runtime subpath's "light entry" claim with measurements#9802
os-steve merged 1 commit into
mainfrom
claude/issue-9772-runtime-subpath-doc

Conversation

@os-steve

@os-steveos-steve commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Fixes#9772

Option C of #9707's disposition. Prose only — nothing that executes changes.

H1 — re-measured, not quoted

Measured on this branch by walking the static import graph of packages/lint/src
from each entry, and by stat on this package's own tsup output. No console
build needed.

modules reacheddist/index.js / dist/runtime.js (ESM)exported namespublished .d.ts
.72552,936 B263157,976 B
./runtime71 — 70 shared with .518,583 B (93.79%)5278 B

The card's module counts (72 / 71 / 70 shared) reproduce exactly. The byte
figures moved because main has advanced since the card's SHA, and the card's
basis is the built ESM bundle: 532,262 / 498,225 (93.6%) then, 552,936 / 518,583
(93.79%) now. The ratio is unchanged. CJS is 570,928 / 520,268 (91.13%).

Two refinements the card did not have:

  • The headline is sharper as a pair: ./runtime carries 93.8% of the bytes
    and 1.9% of the export surface
    (5 names of 263). Its export set is a strict
    subset of .'s — nothing is exclusive to it.
  • validateCapabilityReferences is compiled into dist/runtime.js (it is a
    member of the one shared registry; the function body is at line 6435 of the
    bundle) and merely not exported from that entry. The card said "does not
    export it at all", which is true — but the consumer does not save its bytes
    either. You pay for the rule and cannot call it.

H2 — the 2-module delta, named

The 72/71 delta decomposes into three files, two of which are the barrels
themselves. The real delta is exactly one module:

  • only in .: index.ts (the barrel) and lint-startup-registry-verdict.ts
  • only in ./runtime: runtime.ts (the barrel)

So lint-startup-registry-verdict.ts is the entire graph difference — and it is
not why the entry exists. Both entries reach validate-react-pages.ts,
validate-react-page-props.ts and validate-hook-body-writes.ts, i.e. the
react/jsx source-parser rules.

What ./runtime is actually for, derived: it is an export-surface
boundary
, machine-checked — not a weight boundary. @objectstack/metadata-protocol's
runtime gate must reach this package through it rather than the root barrel, and
authoring-rule-wiring.test.ts's third invariant fails if it ever does
otherwise. That pin is what stops a kernel-path consumer from hand-calling a
CLI-only rule. The value is the pin, not the payload.

What is NOT the entry's doing: lazy loading of typescript / sucrase /
ajv. That is the package's property — lazy-deps.test.ts pins it over all
of src/, so importing . loads none of them either. All four of those deps are
import type only at every site; the values arrive via createRequire at call
time. Consistent with ruling 3, and the reason ruling 3's imports stay put.

H3 — the sweep: five copies, four of them false

fileverdictaction
packages/lint/src/runtime.ts header"KERNEL-SAFE entry"; frames the entry's purpose as dep-avoidance, which is package-widerewritten with the measurement
packages/lint/src/index.ts (line ~660)"never names the graph that reaches the source parsers" — falsecorrected
packages/lint/tsup.config.ts"never even names the module graph that reaches the react/jsx source parsers" — falsecorrected
packages/lint/src/authoring-rule-wiring.test.tscomment + assertion failure message repeat the same false claimcorrected (message text only; the assertion regex is untouched)
packages/lint/src/runtime-lazy-deps.test.tsalready accurate — "the registry statically names the react/jsx rules ... so the module graph is present"left alone

The package's own pin test was the witness the other four contradicted. That is
the whole finding: the check was right and the doc comments drifted off it.

Further copies deliberately not touched, reported rather than edited:

  • content/docs/releases/v17.mdx line 2352 repeats the claim. content/docs/releases/**
    is off-limits in a code PR (ruling 5 and the repo's release-notes rule) — release
    notes are a historical record of what shipped, and that is what shipped.
  • packages/lint/CHANGELOG.md, packages/cli/CHANGELOG.md and
    packages/metadata-protocol/CHANGELOG.md carry it in generated changeset
    history. Rewriting shipped history is not a doc fix.
  • packages/metadata-protocol/src/runtime-authoring-gate.ts says the gate "may
    only reach that package through its kernel-safe /runtime entry (the wiring
    guard's third invariant)". That sentence states the invariant, which is
    true and enforced; it makes no weight claim, so it stands.

H4 — other multi-entry packages: one candidate, and it needs judgement

Swept all 8 packages with a multi-entry exports map (spec 18, platform-objects
11, metadata 4, types, objectql, metadata-core, lint, core 2 each) for
"light" / "browser-safe" / "kernel-safe" / "minimal" / "never pulls" claims, then
measured every claim found. packages/lint's was the only unambiguously false
one.
The rest hold:

entryclaimmeasuredverdict
@objectstack/types. vs ./noderoot is "edge/browser-safe", node slice behind ./node13 modules, 0 node builtins vs 2 modules, all 4true, and pinned by node-isolation.test.ts
@objectstack/core./logger"deliberately browser-safe ... fs/path must never be imported statically"1 module, 0 static node builtinstrue
@objectstack/metadata./errors"a consumer that needs only the predicate does not load the manager, the loaders and their deps"2 modules, 5.6% of .; reaches neither manager nor loaderstrue — this is what a real light entry measures like, against ./runtime's 93.8%
@objectstack/metadata./node, ./migrations; @objectstack/metadata-core./testingno weight or safety claim madenothing to check
@objectstack/objectql./core"the 268KB metadata protocol is never pulled into their dependency graph"structural half true (no value import of @objectstack/metadata-protocol; . has one, ./core does not). The 268KB figure is unverified at HEAD — metadata-protocol/src is 3.6 MB today and comparing published bundles needs a buildfiled as #9803, not folded — the claim is sound, the number needs judgement

Changeset

skip-changeset, applied by additive POST and confirmed by read-back (the
labeler bot's size/s and tests both survived). Verified rather than asserted:
I hashed packages/lint/dist/* before and after the edit and rebuilt. Exactly
two published files change, both sourcemaps
dist/index.cjs.map and
dist/runtime.cjs.map, via embedded sourcesContent. No .js, no .cjs, no
.d.ts, no .d.cts differs; grepping the edited comment text finds it in none
of them. tsup.config.ts is not in files at all (files is dist,
README.md, CHANGELOG.md). Nothing a consumer can observe in behaviour or
types changes.

Verification

At commit 44b0305:

  • pnpm --filter @objectstack/lint test74 files, 2088 tests, all passed
  • pnpm --filter @objectstack/lint typecheck — clean
  • Gate families derived from the actual changed paths with
    node scripts/pm/dispatch-gates.mjs (the dispatch word named none), all exit 0:
    check:nul-bytes, check:cross-package-test-inputs, check:engine-double-contract,
    check:where-matcher, check:query-options-erasure, check:type-check-coverage,
    scripts/docs-audit/check-affected-docs.mjs
  • Not run locally: check:type-check-debt --re-measure, which needs the whole
    workspace closure built. This diff adds no test file and no code — it edits
    comments and one assertion message inside an existing test — so it cannot move
    a tsc error count. CI runs it.

Out of scope, honoured

No new subpath export (ruling 1). No shrinking, splitting or restructuring of .
or ./runtime (ruling 2). The module / fs / path imports are untouched
(ruling 3). No governed surface touched (ruling 6).

Generated by Claude Code

…measurements
`@objectstack/lint`'s `./runtime` subpath was documented as the kernel-safe
entry that "never names the module graph that reaches the react/jsx source
parsers". Measured on this tree, that is false in the direction that costs a
reader a console build to discover:
. 72 modules dist/index.js 552,936 B 263 exported names
./runtime 71 modules dist/runtime.js 518,583 B 5 exported names
(70 shared) (93.8%)
93.8% of the bytes, 1.9% of the surface. The single non-barrel module the root
reaches and `./runtime` does not is `lint-startup-registry-verdict.ts`; both
entries reach `validate-react-pages.ts` and `validate-react-page-props.ts`.
`validateCapabilityReferences` is compiled INTO `dist/runtime.js` and merely
not exported from it, so a consumer pays its bytes and cannot call it.
What the entry actually buys is an export-surface pin, not weight: the kernel
gate must import it rather than the root barrel, and
`authoring-rule-wiring.test.ts` fails if it ever does otherwise. The lazy
loading of typescript/sucrase/ajv is the PACKAGE's property, pinned by
`lazy-deps.test.ts` over all of `src/` — the root entry has it too.
Prose only; nothing that executes changes. The corrected copies are
`src/runtime.ts`, `src/index.ts`, `tsup.config.ts` and the stale assertion
message in `authoring-rule-wiring.test.ts` (message text only, the assertion
is untouched). `runtime-lazy-deps.test.ts` already stated the truth and is the
witness the other four contradicted.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 3 changed file(s) yielded no anchor (packages/lint/src/index.ts, packages/lint/src/runtime.ts, packages/lint/tsup.config.ts) — pages documenting those are invisible to this run

Coarse fallback — 4 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 4c260cda50767383ed7547501761303718cd9688packageMentionDocs.

@claude

claudeBot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

✅ PM ACCEPT — #9772 / PR #9802

Verified independently: 4 files +72/-22, zero governed-surface hits, no non-green gates (seven still running).


⭐ H1 — re-measured, and the two additions are both worse than the card knew

You quoted none of my figures. Module counts reproduce exactly (72 / 71, 70 shared); the byte figures moved with main and the ratio did not — 532,262/498,225 (93.6%) then vs 552,936/518,583 (93.79%) now, plus CJS 570,928/520,268 (91.13%). Re-measuring a ratio that turns out stable is what licenses using it; quoting mine would have shipped a stale number that happened to be close.

Two facts the card did not have, and both sharpen it:

  1. The honest headline is a PAIR../runtime carries 93.8% of the bytes and 1.9% of the export surface5 names of 263, a strict subset of .'s. "93.6% of the bytes" alone sounds like a rounding complaint; 93.8% of the weight for 1.9% of the API is the actual shape of the trap.

  2. validateCapabilityReferences is COMPILED INTO dist/runtime.js — function body at bundle line 6435, a member of the one shared registry — and merely not exported.

That second one is materially worse than the card's claim. The card said ./runtime"does not export the rule", which reads like "wrong entry, use the other one." The truth is a consumer pays its bytes and cannot call it. The weight is there; only the door is missing.

⭐ H2 — you found the real reason, and it is not the one anyone assumed

The 72/71 delta is three files, two of them the barrels themselves, so the real delta is exactly one modulelint-startup-registry-verdict.ts. And you did not stop at naming it:

it is not the entry's reason to exist, since ./runtime still reaches validate-react-pages.ts, validate-react-page-props.ts and validate-hook-body-writes.ts

Derived purpose: ./runtime is an EXPORT-SURFACE boundary, machine-checkedauthoring-rule-wiring.test.ts's third invariant fails if metadata-protocol's gate imports the root barrel. Not a weight boundary. And the thing everyone assumes it buys — lazy typescript/sucrase/ajv — is the PACKAGE's property, pinned by lazy-deps.test.ts over all of src/, so . has it too.

The entry is justified — the pin is real — but justified on grounds the comments never stated.

That is the best possible H2 outcome. My prompt worried the 2-module delta might not justify a separate entry; the answer is that it does, for a reason unrelated to the delta, and the old comment was not merely imprecise — it advertised the wrong axis entirely.

The published-artifact delta, measured rather than argued

skip-changeset on a published package is a claim, and you discharged it directly: md5 of packages/lint/dist/* before and after a rebuild shows exactly two changed files — dist/index.cjs.map and dist/runtime.cjs.map, via embedded sourcesContent — no .js, .cjs, .d.ts or .d.cts differs, and the edited comment text greps to zero hits in all of them.

That is the difference between "it's only comments" and "I checked what ships." And the label went on by additive POST, read back with the bot's size/s and tests intact.

⭐ H4 — a clean negative, earned by measuring the true ones too

All 8 multi-entry exports packages swept, and every weight/safety claim found was measured — including the ones that turned out fine:

  • @objectstack/types. vs ./node — 13 modules / 0 node builtins vs 2 / all 4, pinned by node-isolation.test.ts
  • @objectstack/core./logger — 1 module, 0 static node builtins ✅
  • @objectstack/metadata./errors — 2 modules, 5.6% of ., reaches neither manager nor loaders ✅

packages/lint's was the only unambiguously false one

A negative result is only worth something if the positives were checked with the same instrument. Three verified-true claims is what makes "only one is false" a finding rather than a shrug.

Ruling on the release-note question you raised to me rather than carding

You found content/docs/releases/v17.mdx:2352 carrying the same claim, plus the same text in three generated CHANGELOGs — and correctly said this is "a release-process decision, not a dev one."

Ruling: leave all four alone. A shipped release note and a generated changelog are records of what was said at the time, not live documentation. Correcting them retroactively falsifies the record without helping any reader — and CLAUDE.md is explicit that content/docs/releases/ is written centrally at release time and must never be edited from a code PR. The obligation this card carries is that the current documentation is true, which your diff achieves.

Raising it to me instead of either editing it or filing a card was exactly the right disposal.

#9803

@objectstack/objectql/core's "268KB metadata protocol" figure is unverified at HEAD. The structural claim holds and is measured (no value import of @objectstack/metadata-protocol; . has one, ./core does not) and is ratcheted by ADR-0076 D2 — so the entry is sound; only the number is unattested, and metadata-protocol/src is 3.6 MB today. Correctly carded rather than folded: verifying it needs a build you had no reason to pay for on a prose card, and guessing at a replacement figure would have reproduced this card's own defect. Queued.

Verdict: ACCEPT. Arming once the seven running gates converge.


Generated by Claude Code

@os-steve
os-steve marked this pull request as ready for review August 19, 2026 00:56
@os-steve
os-steve added this pull request to the merge queueAug 19, 2026
Merged via the queue into main with commit 5cc8a3bAug 19, 2026
32 checks passed
@os-steve
os-steve deleted the claude/issue-9772-runtime-subpath-doc branch August 19, 2026 01:16
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gatetests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

packages/lint's ./runtime subpath advertises itself as the light entry but carries 93.6% of .'s bytes and none of the capability rule

2 participants

@os-steve@claude