@@ -476,6 +476,103 @@ internal void ApplySpecDescriptions(MetaObjects.Registry.Spec.SpecMetamodelReade
476476 } ;
477477 }
478478 }
479+
480+ // FR-033 (sub-step B2a) — replace each declared type's STRUCTURAL child
481+ // rules with the strict cross-port graph + cardinality from the JSON.
482+ ApplyStrictStructuralChildren ( reader ) ;
483+
484+ // FR-033 (sub-step B2b) — prune each declared type's LOGICAL attrs to the
485+ // strict per-subtype allow-list from the JSON (the loader now rejects a
486+ // misplaced attr → ERR_UNKNOWN_ATTR).
487+ ApplyStrictAttrScoping ( reader ) ;
488+ }
489+
490+ /// <summary>
491+ /// FR-033 (sub-step B2a) — for every registered <c>(type, subType)</c> the spec
492+ /// declares, REBUILD its <see cref="TypeDefinition"/> so its STRUCTURAL (non-attr)
493+ /// child rules are EXACTLY the strict <c>spec/metamodel/*.json</c> graph
494+ /// (extendsBase-composed, carrying <c>min</c>/<c>max</c>/<c>maxIsNull</c>/<c>named</c>).
495+ /// The attr child rules and any attr requirements are preserved verbatim; only the
496+ /// structural rules are swapped — C#'s broad/cardinality-less structural wildcards
497+ /// (e.g. <see cref="ChildRuleHelper"/> <c>Wildcard(...)</c>) are dropped. Types NOT
498+ /// in the JSON (e.g. <c>metadata.root</c>, <c>attr.*</c>) keep their hand-coded
499+ /// structural children. Mirrors the Java/Python reader's pass 4.
500+ /// </summary>
501+ private void ApplyStrictStructuralChildren ( MetaObjects . Registry . Spec . SpecMetamodelReader reader )
502+ {
503+ foreach ( string key in _defs . Keys . ToList ( ) )
504+ {
505+ TypeDefinition def = _defs [ key ] ;
506+ TypeId id = def . TypeId ;
507+ if ( ! reader . IsDeclared ( id . Type , id . SubType ) )
508+ {
509+ continue ; // not in the spec → keep C#'s existing structural children
510+ }
511+
512+ // Keep every attr child rule (the strict model expresses attrs via the
513+ // attrs block, but C# carries an attr-type wildcard the manifest already
514+ // drops in SortedChildren); drop the broad structural wildcards.
515+ var rules = new List < ChildRule > ( ) ;
516+ foreach ( ChildRule rule in def . ChildRules )
517+ {
518+ if ( rule . ChildType == TYPE_ATTR )
519+ {
520+ rules . Add ( rule ) ; // attr placement — preserved (manifest drops it anyway)
521+ }
522+ }
523+ foreach ( MetaObjects . Registry . Spec . SpecStructChild sc in reader . StructuralChildren ( id . Type , id . SubType ) )
524+ {
525+ rules . Add ( new ChildRule (
526+ sc . ChildType , sc . ChildSubType , sc . ChildName ,
527+ sc . Min , sc . Max , sc . MaxIsNull , sc . Named ) ) ;
528+ }
529+
530+ _defs [ key ] = new TypeDefinition (
531+ id , def . Description , rules , def . Factory , def . Attributes . ToList ( ) ,
532+ def . DataType , def . Rules , def . Example , def . WhenToUse , def . Parents ) ;
533+ }
534+ }
535+
536+ /// <summary>
537+ /// FR-033 (sub-step B2b) — for every registered <c>(type, subType)</c> the spec
538+ /// declares, DROP every LOGICAL (INCLUDED) attr whose name is NOT in the strict
539+ /// per-subtype allow-list (<see cref="MetaObjects.Registry.Spec.SpecMetamodelReader.StrictAttrNames"/>).
540+ /// The carved-out attrs (the <see cref="RegistryManifest.ClassifyPerTypeAttr"/>
541+ /// exclusions — structural keywords / native bindings / the per-type
542+ /// <c>description</c> dup) are LEFT REGISTERED: the emitter drops them from the
543+ /// manifest anyway and the loader needs them. Only the INCLUDED logical attrs are
544+ /// pruned, which TIGHTENS the loader — a misplaced attr → its unknown-attr error.
545+ /// Types NOT in the JSON keep their attrs untouched. Mirrors Java/Python's pass 5.
546+ /// </summary>
547+ private void ApplyStrictAttrScoping ( MetaObjects . Registry . Spec . SpecMetamodelReader reader )
548+ {
549+ foreach ( string key in _defs . Keys . ToList ( ) )
550+ {
551+ TypeDefinition def = _defs [ key ] ;
552+ TypeId id = def . TypeId ;
553+ if ( ! reader . IsDeclared ( id . Type , id . SubType ) )
554+ {
555+ continue ; // not in the spec → keep C#'s existing attr scoping
556+ }
557+
558+ ISet < string > allow = reader . StrictAttrNames ( id . Type , id . SubType ) ;
559+
560+ var attrs = new List < AttrSchema > ( ) ;
561+ foreach ( AttrSchema attr in def . Attributes )
562+ {
563+ bool prunable =
564+ RegistryManifest . ClassifyPerTypeAttr ( attr . Name ) == RegistryManifest . ExclusionReason . Included ;
565+ if ( prunable && ! allow . Contains ( attr . Name ) )
566+ {
567+ continue ; // logical attr not scoped to this subtype → prune
568+ }
569+ attrs . Add ( attr ) ;
570+ }
571+
572+ _defs [ key ] = new TypeDefinition (
573+ id , def . Description , def . ChildRules . ToList ( ) , def . Factory , attrs ,
574+ def . DataType , def . Rules , def . Example , def . WhenToUse , def . Parents ) ;
575+ }
479576 }
480577}
481578
0 commit comments