@@ -22,7 +22,9 @@ export type DaemonPlatformRuntimeEdge = Readonly<{
2222 /**
2323 * Exact named symbols across every edge of the pair; empty for side-effect imports and dynamic
2424 * imports that do not destructure named bindings (a destructured dynamic import records its
25- * bindings, so widening the destructure is a drift, not a silent expansion).
25+ * bindings, so widening the destructure is a drift, not a silent expansion). A rest or computed
26+ * destructure binding cannot be recorded here: it exposes exports beyond this list and R74
27+ * rejects the edge.
2628 */
2729 symbols : readonly string [ ] ;
2830 classification : DaemonPlatformRuntimeClassification ;
@@ -202,8 +204,10 @@ function sorted(symbols: readonly string[]): string[] {
202204
203205/**
204206 * Catches: unclassified daemon-to-root platform-runtime coupling regrowing — a new edge (or a
205- * new symbol on an existing edge) that the #2278 audit never classified, and the mirror
206- * failure, a classified edge that no longer exists and would silently admit its return.
207+ * new symbol on an existing edge) that the #2278 audit never classified, the mirror failure, a
208+ * classified edge that no longer exists and would silently admit its return, and a
209+ * dynamic-import destructure holding a rest or computed binding, which exposes every export
210+ * the inventory cannot name.
207211 * Evidence: #2278 measured 14 production edges in 9 daemon files at origin/main 6e22e266d7;
208212 * this table is that measurement, classified per ADR 0022.
209213 * Cost: attributed to the R74 rule registration in check.ts; not a standalone CI job.
@@ -214,14 +218,19 @@ function sorted(symbols: readonly string[]): string[] {
214218export function checkDaemonPlatformRuntimeInventory (
215219 edges : readonly ResolvedImportEdge [ ] ,
216220) : LayeringViolation [ ] {
217- const actual = new Map < string , { line : number ; symbols : Set < string > } > ( ) ;
221+ const actual = new Map < string , { line : number ; symbols : Set < string > ; residue : boolean } > ( ) ;
218222 for ( const edge of edges ) {
219223 if ( ! edge . file . startsWith ( 'src/daemon/' ) ) continue ;
220224 if ( ! isProductionSourceFile ( edge . file ) ) continue ;
221225 if ( ! isRootPlatformRuntimeTarget ( edge . target ) ) continue ;
222226 const key = keyOf ( edge . file , edge . target ) ;
223- const entry = actual . get ( key ) ?? { line : edge . line , symbols : new Set < string > ( ) } ;
227+ const entry = actual . get ( key ) ?? {
228+ line : edge . line ,
229+ symbols : new Set < string > ( ) ,
230+ residue : false ,
231+ } ;
224232 for ( const symbol of edge . symbols ) entry . symbols . add ( symbol ) ;
233+ entry . residue = entry . residue || edge . bindingResidue ;
225234 actual . set ( key , entry ) ;
226235 }
227236
@@ -245,6 +254,18 @@ export function checkDaemonPlatformRuntimeInventory(
245254 continue ;
246255 }
247256 seen . add ( key ) ;
257+ if ( entry . residue ) {
258+ violations . push ( {
259+ rule : DAEMON_PLATFORM_RUNTIME_RULE ,
260+ file : key . split ( ' -> ' ) [ 0 ] ! ,
261+ line : entry . line ,
262+ message :
263+ `unnameable dynamic-import binding for ${ key } : a rest or computed destructure exposes ` +
264+ `bindings the inventory cannot name. Destructure every binding explicitly and record it ` +
265+ `in DAEMON_PLATFORM_RUNTIME_EDGES (${ DAEMON_PLATFORM_RUNTIME_RULE } ), or remove the coupling.` ,
266+ } ) ;
267+ continue ;
268+ }
248269 const expected = sorted ( declaration . symbols ) ;
249270 const measured = sorted ( [ ...entry . symbols ] ) ;
250271 if ( expected . join ( '\u0000' ) !== measured . join ( '\u0000' ) ) {
0 commit comments