Found while doing the one-line reword in #7092 (PR #7119). Filed, not fixed — widening the guard is a test-design decision well outside that card, and this is latent today, not a live false green.
The gap
packages/app-shell/src/views/metadata-admin/previews/__tests__/exclusion-reason-truthfulness.test.ts pins the class "an exclusion reason claiming no renderer must not have one". It asks the runtime ComponentRegistry, and its coverage is therefore bounded by its own side-effect import set:
import '@object-ui/components';
import '@object-ui/plugin-chatbot';
import '@object-ui/plugin-form';
Its own header names this exact hazard and states the maintenance rule:
Scope is bounded by the import set. A renderer registered in a package NOT imported here reads as unregistered, which would let a false "no renderer" pass. ... Widen the set — and its positive probes — when a new package starts registering page blocks.
app-shell has since become such a package and the set was not widened:
So for the five shell singletons, the guard cannot see a renderer that exists.
Measured, not argued
On ab85b515e I mutated PALETTE_EXCLUSIONS['app:launcher'] to 'no renderer ZZMUTZZ' — a string that DOES match the guard's CLAIMS_NO_RENDERER regex /\bno\s+(?:\w+\s+){0,2}renderer\b/i — confirmed the mutation on disk (injected-text count 1, anchor count 0, blob hash moved off the HEAD blob), and ran the suite:
Test Files 1 passed (1)
Tests 4 passed (4)
Green. A reason asserting app:launcher has no renderer passes, while app-shell/src/views/app-launcher-renderer.tsx registers one. The tree was restored and the restore verified by blob-hash match plus an empty git diff HEAD.
The mechanism is two-layered, which is why @object-ui/components being imported does not save it: per #7091's own docblock, app:launcher sits only in PROTOCOL_COMPONENTS, registered when a host opts in via registerPlaceholders() — which only apps/console does — rather than in the eager PALETTE_PLACEHOLDER_BLOCKS. So it is absent from that harness's registry twice over.
Why latent rather than live
No shell singleton's reason currently claims "no renderer". The two entries that do — ai:chat_window and element:form — are inside the imported packages' scope, so today's ledger is correctly judged. #7092's reword deliberately left app:launcher's new wording renderer-agnostic, so it does not enter this population either.
The cost is future-tense and exactly the cost #6071 and #5837 already paid once on this ledger: the next author who writes "no renderer" over one of the five shell singletons gets a green from the file whose stated purpose is to refuse it.
Repair sketch (not prescribed)
Add the app-shell registering leaves to the import set with the positive probes the header's own discipline requires — one probe per import, so an unpopulated registry fails loudly rather than passing vacuously. Worth checking first whether importing app-shell from a test inside app-shell creates a cycle with its register-builtins leaf; if it does, importing the four renderer modules directly is the narrower move.
Refs: #7092 · PR #7119 · PR #7091 · #6757 · #6071 · #5837.
Found while doing the one-line reword in #7092 (PR #7119). Filed, not fixed — widening the guard is a test-design decision well outside that card, and this is latent today, not a live false green.
The gap
packages/app-shell/src/views/metadata-admin/previews/__tests__/exclusion-reason-truthfulness.test.tspins the class "an exclusion reason claiming no renderer must not have one". It asks the runtimeComponentRegistry, and its coverage is therefore bounded by its own side-effect import set:Its own header names this exact hazard and states the maintenance rule:
app-shellhas since become such a package and the set was not widened:global:searchandglobal:notifications— both platform data sources are shipped; the two members still draw the Component Placeholder scaffold #6757 registeredglobal:searchandglobal:notificationsthereapp:launcherandnav:menu(Phase 1 of objectstack#12183) #7091 registeredapp:launcherandnav:menuthereSo for the five shell singletons, the guard cannot see a renderer that exists.
Measured, not argued
On
ab85b515eI mutatedPALETTE_EXCLUSIONS['app:launcher']to'no renderer ZZMUTZZ'— a string that DOES match the guard'sCLAIMS_NO_RENDERERregex/\bno\s+(?:\w+\s+){0,2}renderer\b/i— confirmed the mutation on disk (injected-text count 1, anchor count 0, blob hash moved off the HEAD blob), and ran the suite:Green. A reason asserting
app:launcherhas no renderer passes, whileapp-shell/src/views/app-launcher-renderer.tsxregisters one. The tree was restored and the restore verified by blob-hash match plus an emptygit diff HEAD.The mechanism is two-layered, which is why
@object-ui/componentsbeing imported does not save it: per #7091's own docblock,app:launchersits only inPROTOCOL_COMPONENTS, registered when a host opts in viaregisterPlaceholders()— which onlyapps/consoledoes — rather than in the eagerPALETTE_PLACEHOLDER_BLOCKS. So it is absent from that harness's registry twice over.Why latent rather than live
No shell singleton's reason currently claims "no renderer". The two entries that do —
ai:chat_windowandelement:form— are inside the imported packages' scope, so today's ledger is correctly judged. #7092's reword deliberately leftapp:launcher's new wording renderer-agnostic, so it does not enter this population either.The cost is future-tense and exactly the cost #6071 and #5837 already paid once on this ledger: the next author who writes "no renderer" over one of the five shell singletons gets a green from the file whose stated purpose is to refuse it.
Repair sketch (not prescribed)
Add the app-shell registering leaves to the import set with the positive probes the header's own discipline requires — one probe per import, so an unpopulated registry fails loudly rather than passing vacuously. Worth checking first whether importing app-shell from a test inside app-shell creates a cycle with its
register-builtinsleaf; if it does, importing the four renderer modules directly is the narrower move.Refs: #7092 · PR #7119 · PR #7091 · #6757 · #6071 · #5837.