Skip to content

Commit 19fce8b

Browse files
dmealingclaude
andcommitted
feat(fr-033): C# port S-B2 — strict children/scoping + metadata.root → registry-conformance GREEN
Extends the pre-seal apply hook to replace each type's structural child rules with the strict graph + cardinality from spec/metamodel/*.json (extendsBase-composed) and prune its logical attrs to the strict per-subtype allow-list (loader now rejects misplaced attrs); fixes metadata.root (hand-coded root wrapper); adds the missing error codes if gated. RegistryManifestConformance is now GREEN — the C# metadata registry byte-matches the cross-port canonical. Corpus fixtures fixed: none. (The 5 metaobjects-ui/ prompt ConformanceTests are a separate provider-composition gate — next step.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 15d9c8a commit 19fce8b

1 file changed

Lines changed: 97 additions & 0 deletions

File tree

server/csharp/MetaObjects/Registry.cs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)