3636 * a different card and a different decision. Copying is what THIS card is.
3737 * Recorded here so the next author inherits the count rather than the habit.
3838 *
39- * ## The one exempted component type
39+ * ## No standing exemptions (#12344)
4040 *
41- * `page:header` carries a `ComponentPropsMap` row, so door 3 reads its
42- * `properties` bag. `mcp:connect-agent` does NOT: it is a console-registered
43- * widget provided by objectui's app-shell, so door 3 has no schema to read its
44- * `properties` with — the same standing-exemption shape `cloud-connection`'s
45- * two widgets were in between #11480 and #11575, before #11575 gave them
46- * strict, empty rows measured from the renderers' read points at the
47- * `.objectui-sha` pin. Giving this type its row is that same piece of work and
48- * belongs on its own card (filed); it is outside this card's declared surface.
49- *
50- * Until then the exemption is asserted EXACTLY (a NEW unmapped type reds), and
51- * it is sound only while the exempted widget authors an EMPTY props bag —
52- * nothing authored is nothing to serve bare. The moment it grows a real
53- * authored prop, the emptiness assert reds and forces the decision: give the
54- * type a `ComponentPropsMap` row, or widen the exemption knowingly. Both halves
55- * are needed; the exemption alone would be a door-3 blind spot that widens in
56- * silence.
41+ * `mcp:connect-agent` was exempted here between #12269 and #12344: a
42+ * console-registered widget provided by objectui's app-shell with no
43+ * `ComponentPropsMap` row, so door 3 had no schema to read its `properties`
44+ * with — the same standing-exemption shape `cloud-connection`'s two widgets
45+ * were in between #11480 and #11575. #12344 gave the type its row (strict,
46+ * empty — measured from the renderer's read points at the `.objectui-sha`
47+ * pin, where the registration discards the schema node entirely and the
48+ * component function takes no parameters), so door 3 now reads its bag and
49+ * the exemption list is empty. The machinery stays: the exemption set is
50+ * still asserted EXACTLY, so any NEW unmapped type reds and forces the same
51+ * decision — declare the props schema in `ComponentPropsMap`, or record the
52+ * exemption here with the reason (and then also pin the exempted bag empty
53+ * and the list non-vacuous, as the pre-#12344 revision of this file did).
5754 */
5855
5956import { readFileSync , readdirSync } from 'node:fs' ;
@@ -64,7 +61,6 @@ import type { Page } from '@objectstack/spec/ui';
6461import {
6562 auditPageExpressionEnvelopes ,
6663 renderBareExpressionFindings ,
67- walkPageComponents ,
6864} from '@objectstack/lint' ;
6965// The one answer this tree has to "comment, literal, or code". It is a plain
7066// `.mjs`, but `scripts/js-comment-mask.d.mts` beside it is a hand-written
@@ -86,13 +82,14 @@ const HERE = dirname(fileURLToPath(import.meta.url));
8682
8783/**
8884 * Every page this package ships, audited by export name — with the unmapped
89- * component types each page is EXPECTED to report (the exemption above).
85+ * component types each page is EXPECTED to report (none since #12344; see
86+ * the module header).
9087 */
9188const AUDITED_PAGES : { exportName : string ; page : Page ; exemptUnmappedTypes : string [ ] } [ ] = [
9289 {
9390 exportName : 'CONNECT_AGENT_PAGE' ,
9491 page : CONNECT_AGENT_PAGE ,
95- exemptUnmappedTypes : [ 'mcp:connect-agent' ] ,
92+ exemptUnmappedTypes : [ ] ,
9693 } ,
9794] ;
9895
@@ -227,42 +224,14 @@ describe('mcp Page exports serve canonical expression envelopes', () => {
227224 } ) ;
228225
229226 it . each ( AUDITS ) ( '$exportName: unmapped component types are EXACTLY the recorded exemptions (door 3 precondition)' , ( { audit, exemptUnmappedTypes } ) => {
230- // See the header for why `mcp:connect-agent` is exempt . Anything ELSE
227+ // No exemptions stand since #12344 (see the module header) . Anything
231228 // unmapped is a new door-3 blind spot: declare the props schema in
232- // `ComponentPropsMap`, or record the exemption here with the reason — and
233- // then also pin the exempted bag empty, as the test below does, so the
234- // exemption cannot quietly cover a growing bag .
229+ // `ComponentPropsMap`, or record the exemption here with the reason —
230+ // and then also pin the exempted bag empty and the exemption list
231+ // non-vacuous, as the pre-#12344 revision of this file did .
235232 expect ( audit . unmappedTypes . map ( e => e . type ) . sort ( ) ) . toEqual ( [ ...exemptUnmappedTypes ] . sort ( ) ) ;
236233 } ) ;
237234
238- it . each ( AUDITS ) ( '$exportName: every exempted component authors an EMPTY props bag' , ( { page, exemptUnmappedTypes } ) => {
239- // The exemption above is only sound while there is nothing authored for
240- // door 3 to miss. A real key landing in one of these bags must force a
241- // decision (props schema row, or a conscious wider exemption) — not ride
242- // through a standing exemption silently.
243- const offenders = walkPageComponents ( page as AnyRec , '' )
244- . filter ( w => typeof w . component . type === 'string' && exemptUnmappedTypes . includes ( w . component . type ) )
245- . filter ( w => {
246- const props = w . component . properties ;
247- return ! ! props && typeof props === 'object' && Object . keys ( props ) . length > 0 ;
248- } )
249- . map ( w => `${ w . path } [${ String ( w . component . type ) } ]` ) ;
250- expect ( offenders . join ( '\n' ) ) . toBe ( '' ) ;
251- } ) ;
252-
253- it . each ( AUDITS ) ( '$exportName: the exemption list is not vacuous — every exempted type is really on this page' , ( { page, exemptUnmappedTypes } ) => {
254- // The reverse rot: a type left in the list after it stopped appearing on
255- // the page (or after it gained a `ComponentPropsMap` row) is an exemption
256- // covering nothing, and it would keep the EXACT assert above green while
257- // hiding the fact that the door is now open. Delete it when it goes stale.
258- const present = new Set (
259- walkPageComponents ( page as AnyRec , '' )
260- . map ( w => w . component . type )
261- . filter ( ( t ) : t is string => typeof t === 'string' ) ,
262- ) ;
263- expect ( exemptUnmappedTypes . filter ( t => ! present . has ( t ) ) ) . toEqual ( [ ] ) ;
264- } ) ;
265-
266235 it . each ( AUDITS ) ( '$exportName: every authored `properties` bag parses against its props schema (door 3 precondition)' , ( { audit } ) => {
267236 expect (
268237 audit . unreadableProps . map ( e => `${ e . path } [${ e . type } ]: ${ e . issues } ` ) . join ( '\n' ) ,
0 commit comments