…rt set so a false "no renderer" cannot pass
`exclusion-reason-truthfulness.test.ts` asks the runtime `ComponentRegistry`
whether a `PALETTE_EXCLUSIONS` reason claiming "no renderer" is true, so its
coverage is bounded by its own side-effect import set. That set never grew to
include this package, which has registered page blocks since #6757 and #7091,
nor `@object-ui/plugin-detail`, which registers `record:chatter`.
Measured on 44ea62d: setting `PALETTE_EXCLUSIONS['app:launcher']` to
'no renderer ZZMUTZZ' — a string that matches the guard's CLAIMS_NO_RENDERER
regex — passed 4/4 while `views/app-launcher-renderer.tsx` registers a
renderer for it.
Widen the set to the six `views/*-renderer.tsx` leaves and plugin-detail, and
add a derived guard: the leaf list is read from the directory and every
registered key must resolve, so a seventh renderer leaf that is not imported
here reds this file instead of silently shrinking its coverage. A
hand-maintained list is what drifted; deriving it is the point.
The package barrel would track registrations automatically too, but costs
6105ms to load against 553ms for the leaves (same harness, same baseline) —
this is a pure-logic gate in the cheap `unit` project.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
Fixes#7117
exclusion-reason-truthfulness.test.tsasks the runtimeComponentRegistrywhether aPALETTE_EXCLUSIONSreason claiming "no renderer" is true, so its coverage is bounded by its own side-effect import set. That set never grew to include this package — which has registered page blocks since #6757 and #7091 — nor@object-ui/plugin-detail.All measurements below were taken on this branch; the union of gates was run at
778fb78b4(the final commit).Leg 0 — the blindness, reproduced before any fix
Predicted GREEN (the guard is blind), observed GREEN. On
44ea62d29,PALETTE_EXCLUSIONS['app:launcher']set to'no renderer ZZMUTZZ'— a string that DOES match the guard'sCLAIMS_NO_RENDERERregex — whileviews/app-launcher-renderer.tsxregisters a renderer for it:Restore proven by state, not by an exit code:
blob_restored=186b4a2d…(back to the HEAD blob),git diff HEAD0 bytes,git diff --cached0 bytes, injected-marker count back to 0.The at-risk population, re-derived
Measured with a throwaway probe that snapshots
ComponentRegistry.getConfig(key)after each import step, rather than argued from source. All eightPALETTE_EXCLUSIONSkeys:app:launcherns=app)views/app-launcher-renderer.tsx, eager at module loadglobal:notificationsns=global)views/global-notifications-renderer.tsx, eagerrecord:chatterns=record)@object-ui/plugin-detail, eagerelement:record_pickerns=element)@object-ui/componentselement:text_inputns=element)@object-ui/componentsuser:profileregisterPlaceholders()(ns=protocol-placeholder)ai:chat_windowelement:formThree corrections to what the card assumed, all measured:
PALETTE_EXCLUSIONScarries exactly three shell-singleton entries (app:launcher,global:notifications,user:profile);nav:menuandglobal:searchare not excluded at all, so they never enter this guard's loop. The blind set isapp:launcher,global:notificationsand — outside app-shell —record:chatter.app:launcherdoes not need an opt-in probe. It is opt-in-only in@object-ui/components, but since feat(app-shell): console renderers forapp:launcherandnav:menu(Phase 1 of objectstack#12183) #7091 app-shell registers the real renderer eagerly at module load. The probe finds it with noregisterPlaceholders()call:app:launcher REG ns=app label=App Launcher.CLAIMS_NO_RENDERERtoday areai:chat_windowandelement:form, and a repo-wide grep for a registration of either (control:namespace: 'element', which returns many hits) returns zero. The defect was latent, as filed.registerPlaceholders()is deliberately NOT called: it would make the file answer "has a renderer" for types that have only the dashed "Component Placeholder" scaffold (user:profileis one, measured). This repo's own language says the scaffold is not a renderer —views/app-launcher-renderer.tsxdescribes the state before it existed as "nothing rendered it, so a page that authored it drew a dashed box", with placeholders registered the whole time.Mechanism chosen, and the cost that chose it
Marginal import cost, same harness and same baseline (after
@object-ui/components+plugin-chatbot+plugin-formare loaded):../../../../index.jsThe barrel would track the package automatically, but it drags the console, marketplace, cloud and diagnostics graphs into a pure-logic gate in the cheap
unitproject — whose entire design point (vitest.config.mts) is not paying for graphs it does not touch. So: leaves, plus a derived guard that buys the barrel's one real advantage for 0ms.the import set covers every page block app-shell registersreads theviews/*-renderer.tsxleaf list from the directory, derives each leaf's registered key from its ownComponentRegistry.register(...)call, and requires every one to resolve. A seventh renderer leaf that is not imported here reds this file instead of silently shrinking its coverage. A hand-maintained list is exactly what drifted — deriving it is the point. It carries three controls of its own, because a derivation that finds nothing would pass vacuously and that is this file's whole subject: the leaf list must be non-empty, every leaf's key must be extractable, and the derived list must still containapp:launcherandglobal:notifications.The barrel remains viable and cycle-free — it loaded and registered correctly in the probe — so this is a cost decision, not a feasibility one.
Positive probes — one per import, each shown hitting
Required by the finding's own text: "it needs one positive probe per new import or it guards vacuously."
@object-ui/components(pre-existing)element:text@object-ui/plugin-chatbot(pre-existing)chatbot@object-ui/plugin-form(pre-existing)object-form@object-ui/plugin-detail(new)record:chatterviews/app-launcher-renderer.js(new)app:launcher, derivedviews/global-notifications-renderer.js(new)global:notifications, derivedviews/global-search-renderer.js(new)global:search, derivedviews/nav-menu-renderer.js(new)nav:menu, derivedviews/record-approvals-renderer.js(new)record:approvals, derivedviews/record-attachments-renderer.js(new)record:attachments, derivedAll ten hit — the suite is green at 5/5, and every derived probe is an assertion inside the coverage test, so a miss is a red rather than a silent skip. Ablation A3 below proves that is true and not merely stated.
Both pre-existing anti-vacuity guards are preserved verbatim and neither is weakened:
the registry under test is actually populated (guards a vacuous green)keeps its three probes and gains a fourth, andthe ledger still contains a no-renderer claim to check (guards a vacuous loop)is untouched.Post-fix ablations — all three predicted RED, all three observed RED
Run against the committed fix, so each restore is
git checkout HEAD -- (absolute path)against a HEAD that already contains the implementation. Every leg proved its mutation on disk (anchor count moved, marker count moved, blob hash moved) and its restore by state (blob back to the HEAD blob,git diff HEADandgit diff --cachedboth 0 bytes).A1 — the card's own false claim, the one that passed 4/4 in leg 0:
A2 — the same false claim on
record:chatter, proving the plugin-detail import is not decorative:A3 — one renderer-leaf import deleted, proving the derived coverage guard bites:
Each leg reports
Tests 1 failed | 4 passed (5), so the suite really executed rather than collapsing to "no tests" behind a plausible exit 1.Scope note — one bounded in-place addition, named
@object-ui/plugin-detailis outside the card's title ("excludes app-shell"), and it is included deliberately:record:chatteris a current ledger key, blind in the same loop, in the same file, for the same reason, and measured the same way (A2). The correct shape was already pinned by the two sibling suites in this same directory —palette-discussion-alias.test.tsx:50andcanvas-display-meta.test.tsx:44both carryimport '@object-ui/plugin-detail';for exactly this question. Closing the app-shell half while leaving a known, measured blind key in the loop this PR certifies would reproduce the defect the card is about.Gates
vitest runon the guard fileTest Files 1 passed (1)/Tests 5 passed (5)vitest run --project unit(whole project)Test Files 801 passed (801)/Tests 12489 passed | 9 skipped (12498)vitest run packages/app-shell/src/views/metadata-admin/previews/Test Files 45 passed (45)/Tests 521 passed (521)turbo run type-check --filter=@object-ui/app-shellTasks: 30 successful, 30 total; the package script istsc --noEmit && tsc -p tsconfig.test.jsoneslinton the changed filecheck-changeset-presencecheck-changeset-no-majormajorbump."check-control-bytesThe whole-
unit-project run is not incidental: theunitproject runs withisolate: false, so the added side-effect registrations are visible to every other unit file sharing a worker. 801/801 green is the measurement that they leak into nothing.Two notes on reading the evidence honestly:
Cannot find module '@object-ui/*'across files this PR never touches — an unbuilt workspace closure, which is NOT MEASURED rather than red. Routing through turbo builds the closure first, and--listFilesconfirmed the edited file is actually in thetsconfig.test.jsonprogram (6 hits) rather than excluded like it is fromtsconfig.json.Not changed
No production code. The ledger text, the palette decisions and
CLAIMS_NO_RENDERERare all untouched — this PR changes only what the guard can see. The changeset declares an empty frontmatter: nothing is released.Generated by Claude Code