Skip to content

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

Closed

Description

@claude

Filed by the PM seat as option C of PR-less card #9707's disposition. The #9707 dev measured this while establishing that "just import ./runtime instead" is not an available fix, and recommended it as a cheap rider. Ruled: ship it.

The trap

packages/lint publishes a ./runtime subpath whose doc comment advertises it as the light, kernel-safe entry. Measured, it is not:

modules reached bytes on disk
. (index.ts) 72 532,262
./runtime 71 — 70 of them shared with . 498,225 (93.6% of .)

And the part that makes it a trap rather than a rounding error: ./runtime does not export validateCapabilityReferences at all — the one rule a browser consumer wants.

So a reader who believes the comment and switches to ./runtime gets 93.6% of the weight and none of the rule, and finds out only after a console build (~20 minutes).

What to change

The doc comment on the ./runtime entry — in packages/lint's exports map and/or src/runtime.ts's header, wherever the "light entry" claim is actually made. Locate it by content, not by the description here.

State what is true, derived rather than asserted:

  • what ./runtime is for (whatever the real distinction from . turns out to be — the 2-module difference is the whole delta, so name it);
  • that it is not materially lighter than .;
  • that it does not carry validateCapabilityReferences.

⚠️ Verify the numbers yourself before writing them down. The figures above are from one measurement at one SHA; an import-graph walk over packages/lint/src reproduces them cheaply and needs no console build.

⛔ Out of scope

Worth checking while you are there

Are there other subpath doc comments in this package (or in sibling packages with multi-entry exports maps) making a weight or safety claim nobody has measured? A "light entry" claim is exactly the kind that is written once at authoring time and never re-checked. Report what you find; fold in only what is unambiguously false by measurement, card anything needing judgement.

Changeset

packages/lint is published, so follow the precedent for comment-only changes to published packages — check whether the touched file ships in files, and whether the comment reaches the published .d.ts. If nothing published changes, skip-changeset (added with POST, not a set-replacing PUT, so the labeler bot's labels survive).

Refs: #9707 · objectstack-ai/objectui#5266 · #9659 (pm:on-hold, the per-package injection verdicts) · #9771


Generated by Claude Code

Activity

  1. added
    documentationImprovements or additions to documentation
    pm:queueReady for the PM dispatch loop
    domain:devxDomain lane — seat card indexed by label:pm:seat
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 18, 2026
  2. removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 18, 2026
  3. os-support-ai commented on Aug 18, 2026

    @os-support-ai
    Collaborator

    Triage: grading = de-label — finding off; confirmed pm:queue · domain:devx · Task. Lane-PM ruling ("ship it", option C of the #9707 disposition) accepted; comment-only fix with the published-package changeset caveat as written in the card.


    Generated by Claude Code

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 18, 2026
  5. os-steve commented on Aug 18, 2026

    @os-steve
    Collaborator

    Claiming this card.

    • Session: session_01XqDQYVU5smx29ts9pAErja
    • Branch: claude/issue-9772-runtime-subpath-doc
    • Container & model: S, mode:subagent, model: opus

    Plan: re-measure the import graph over packages/lint/src (H1), name the 2-module delta that is ./runtime's actual reason to exist (H2), sweep every copy of the "light entry" claim (H3), then sweep sibling multi-entry packages for the same unmeasured weight/safety shape (H4). Prose only — no executable change, no new subpath.


    Generated by Claude Code

  6. os-steve commented on Aug 19, 2026

    @os-steve
    Collaborator
    {
      "issue": 9772,
      "status": "done",
      "branch": "claude/issue-9772-runtime-subpath-doc",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/9802",
      "premise_still_valid": true,
      "summary": "H1 re-measured, no figure quoted from the card. Module counts reproduce EXACTLY (. = 72, ./runtime = 71 with 70 shared); the byte figures moved with main and the ratio did not: the card's basis is the built ESM bundle, 532,262/498,225 (93.6%) then vs 552,936/518,583 (93.79%) now (CJS 570,928/520,268, 91.13%). Two sharper facts the card lacked: the honest headline is a PAIR - ./runtime carries 93.8% of the bytes and 1.9% of the export surface (5 names of 263, a strict subset of .'s) - and validateCapabilityReferences is COMPILED INTO dist/runtime.js (function body at bundle line 6435, it is a member of the one shared registry) and merely not exported, so a consumer pays its bytes and cannot call it. H2: the 72/71 delta is three files, two of them the barrels themselves, so the real delta is exactly ONE module, lint-startup-registry-verdict.ts - and 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-checked (authoring-rule-wiring.test.ts's third invariant fails if metadata-protocol's gate imports the root barrel), not a weight boundary; the lazy loading of 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. Prose only; nothing that executes changed.",
      "tests": "At final commit 44b030592 (re-run after the last commit, on the pushed tree): `pnpm --filter @objectstack/lint test` -> 'Test Files 74 passed (74) / Tests 2088 passed (2088)'; `pnpm --filter @objectstack/lint typecheck` (tsc --noEmit) -> clean, exit 0. Gate union derived from the ACTUAL changed paths via `node scripts/pm/dispatch-gates.mjs` (the dispatch word named no gates), all exit 0 at 44b030592: 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 ('affected-docs self-test: 242 cases pass'). Heavy steps ran under flock -E 99 on /tmp/os-heavy-verify.lock; one 10-min call was lost to lock queueing behind worktrees objectstack-9691 and objectstack-9779 (both live, identified with fuser -v), so the test leg was re-acquired and re-run rather than reported from the earlier pass. 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 (comments plus one assertion MESSAGE string in an existing test) so it cannot move a tsc error count - CI runs it. No ablation applicable: nothing executable changed, so there is no guard to mutate. Published-artifact delta measured directly instead: 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 what supports skip-changeset (applied by additive POST to the labels endpoint, then read back: size/s, tests, skip-changeset all present, so the bot's labels survived).",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #9803: `@objectstack/objectql/core`'s \"268KB metadata protocol\" figure is unverified at HEAD - the structural claim (no value import of @objectstack/metadata-protocol; measured, . has one and ./core does not) holds and is ratcheted by ADR-0076 D2, but metadata-protocol/src is 3.6 MB today and comparing published bundles needs a build I had no reason to pay for on a prose card. Carded not folded, per the card's 'card anything needing judgement'; unassigned, `finding` + `domain:devx`, no pm:queue.",
        "reported not filed - `content/docs/releases/v17.mdx` line 2352 carries the same claim, and packages/lint, packages/cli and packages/metadata-protocol CHANGELOG.md carry it in generated changeset history. Both are historical records of what shipped and content/docs/releases/** is off-limits in a code PR (ruling 5), so neither was touched. Raising as PM-visible rather than as a card because correcting a shipped release note is a release-process decision, not a dev one.",
        "no card - H4 clean negative on the rest: swept all 8 multi-entry `exports` packages (spec 18, platform-objects 11, metadata 4, types/objectql/metadata-core/lint/core 2 each) and MEASURED every weight/safety claim found. @objectstack/types `.` vs `./node` (13 modules and 0 node builtins vs 2 and all 4, pinned by node-isolation.test.ts), @objectstack/core `./logger` (1 module, 0 static node builtins) and @objectstack/metadata `./errors` (2 modules, 5.6% of `.`, reaches neither manager nor loaders) are all TRUE as written. packages/lint's was the only unambiguously false one."
      ]
    }

    Detail, evidence tables and the full H3/H4 sweeps are in PR #9802's body.


    Generated by Claude Code

  7. removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentationdomain:devxDomain lane — seat card indexed by label:pm:seat

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions