Skip to content

Commit 8cee363

Browse files
dmealingclaude
andcommitted
feat(fr-033): C# — re-home ui/prompt attrs into metaobjects-ui/prompt providers (match TS/Java/Python)
Completes the cross-port ui/prompt re-home: C# ui/prompt metadata config now lives in dedicated data-driven metaobjects-ui + metaobjects-prompt providers (reading the embedded ui.json/prompt.json extends), NOT in core. Renames TemplateProvider → metaobjects-prompt (absorbs @xmlText; metaobjects-template kept as a back-compat alias), adds metaobjects-ui. Removes the re-homed attrs from core (no double-registration). registry-conformance stays GREEN (manifest provider-agnostic); the 5 metaobjects-ui/prompt ConformanceTests now pass. All 5 ported engines now share the same provider split. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 19fce8b commit 8cee363

18 files changed

Lines changed: 409 additions & 448 deletions

server/csharp/MetaObjects.Conformance.Tests/ConformanceAdapter.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,17 @@ public static class ConformanceAdapter
123123
// (ADR-0023). The @dbColumnType subtype×value pairing validation additionally fires
124124
// in the always-on loader pass (ValidationPasses.ValidateDbColumnType).
125125
["metaobjects-db"] = MetaObjects.Persistence.Db.DbMetaDataProvider.Instance,
126-
// The "metaobjects-template" provider registers the @xmlText field marker (XML
127-
// text-content extraction) on every field subtype. The template.prompt-under-entity
128-
// nesting (FR-004 TS pilot) is NOT ported to C#; a fixture exercising that nesting
129-
// stays a known-gap in conformance-expected-failures.json.
130-
["metaobjects-template"] = MetaObjects.Template.TemplateTypesProvider.Instance,
126+
// FR-033 concern providers — the UI / query-surface attrs (@filterable / @sortable
127+
// / view.currency @locale / layout.dataGrid) and the prompt / extract attrs
128+
// (@xmlText / @example / @instruction / enum overlays / object.value @normalize /
129+
// template.* attrs) are re-homed out of core into dedicated data-driven providers
130+
// (reading spec/metamodel/ui.json + prompt.json), matching the TS provider split.
131+
["metaobjects-ui"] = MetaObjects.Presentation.Ui.UiMetaDataProvider.Instance,
132+
["metaobjects-prompt"] = MetaObjects.Template.PromptMetaDataProvider.Instance,
133+
// Back-compat alias: pre-rename fixtures (and other ports' fixtures) still name the
134+
// provider "metaobjects-template"; map it to the renamed prompt provider — the same
135+
// alias TS / Java / Python keep.
136+
["metaobjects-template"] = MetaObjects.Template.PromptMetaDataProvider.Instance,
131137
// Test-only — provider-extension-* fixtures.
132138
["example-template-briefing"] = new ExampleTemplateBriefingProvider(),
133139
["cycle-a"] = new NoopTestProvider("cycle-a", "cycle-b"),

server/csharp/MetaObjects.Conformance.Tests/FixtureDiscovery.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,23 @@ public sealed record Fixture(
7474
/// </summary>
7575
public static class FixtureDiscovery
7676
{
77+
// FR-033 — the default provider set for a fixture with no providers.json is the
78+
// FULL core bundle (core types + the four concern providers), NOT core-types alone.
79+
// The field's filter/sort/teaching/extract attrs + the view/layout/template attrs
80+
// were re-homed out of core-types into the db/ui/prompt concern providers (matching
81+
// the TS provider split), so a fixture exercising @filterable / @example / @normalize
82+
// / a template.* attr / a layout.dataGrid attr needs those providers composed. Mirrors
83+
// the TS conformance DEFAULT_PROVIDERS (server/typescript/.../conformance/src/fixture.ts)
84+
// and the Python adapter, keeping the cross-port no-providers.json fixtures green.
7785
private static readonly IReadOnlyList<string> DefaultProviders =
78-
new[] { "metaobjects-core-types" };
86+
new[]
87+
{
88+
"metaobjects-core-types",
89+
"metaobjects-db",
90+
"metaobjects-documentation",
91+
"metaobjects-prompt",
92+
"metaobjects-ui",
93+
};
7994

8095
/// <summary>
8196
/// Discover every scenario directory under <paramref name="corpusRoot"/>,

server/csharp/MetaObjects.Conformance.Tests/Fr010LoaderAttrsTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class Fr010LoaderAttrsTests
1919
{
2020
private static LoadResult LoadJson(string json, string id)
2121
{
22-
var registry = Provider.ComposeRegistry(new[] { CoreTypes.CoreTypesProvider });
22+
var registry = FullCoreRegistry.Compose();
2323
var loader = new MetaDataLoader(registry);
2424
return loader.Load(new IMetaDataSource[]
2525
{
@@ -28,7 +28,7 @@ private static LoadResult LoadJson(string json, string id)
2828
}
2929

3030
private static TypeRegistry Registry() =>
31-
Provider.ComposeRegistry(new[] { CoreTypes.CoreTypesProvider });
31+
FullCoreRegistry.Compose();
3232

3333
// ------------------------------------------------------------------------
3434
// @promptStyle — registered on template.output, closed enum, default guide.

server/csharp/MetaObjects.Conformance.Tests/Fr011LoaderAttrsTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class Fr011LoaderAttrsTests
1717
{
1818
private static LoadResult LoadJson(string json, string id)
1919
{
20-
var registry = Provider.ComposeRegistry(new[] { CoreTypes.CoreTypesProvider });
20+
var registry = FullCoreRegistry.Compose();
2121
var loader = new MetaDataLoader(registry);
2222
return loader.Load(new IMetaDataSource[]
2323
{
@@ -26,7 +26,7 @@ private static LoadResult LoadJson(string json, string id)
2626
}
2727

2828
private static TypeRegistry Registry() =>
29-
Provider.ComposeRegistry(new[] { CoreTypes.CoreTypesProvider });
29+
FullCoreRegistry.Compose();
3030

3131
// ------------------------------------------------------------------------
3232
// Registration shape.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// FR-033 — the full core provider bundle for unit tests.
2+
//
3+
// After the ui/prompt re-home, the field-teaching / extract / template / view /
4+
// layout attrs no longer live in CoreTypesProvider — they are registered by the
5+
// dedicated metaobjects-ui / metaobjects-prompt concern providers. Unit tests that
6+
// assert those attrs are registered (or load metadata that uses them) must compose
7+
// the full bundle, not core-types alone. This mirrors the Python unit-test update
8+
// (compose the full core_providers list) and the conformance DEFAULT_PROVIDERS set.
9+
10+
using MetaObjects.Core.Documentation;
11+
12+
namespace MetaObjects.Conformance.Tests;
13+
14+
/// <summary>Shared full-core-bundle composition for unit tests (FR-033).</summary>
15+
internal static class FullCoreRegistry
16+
{
17+
/// <summary>
18+
/// The full core provider bundle: core types + the DB / documentation / UI /
19+
/// prompt concern providers — the same composition the loader's default registry
20+
/// and the conformance default provider set use.
21+
/// </summary>
22+
internal static readonly IReadOnlyList<IMetaDataTypeProvider> Providers = new[]
23+
{
24+
CoreTypes.CoreTypesProvider,
25+
MetaObjects.Persistence.Db.DbMetaDataProvider.Instance,
26+
DocumentationTypes.DocTypesProvider,
27+
MetaObjects.Presentation.Ui.UiMetaDataProvider.Instance,
28+
MetaObjects.Template.PromptMetaDataProvider.Instance,
29+
};
30+
31+
/// <summary>Compose a fresh registry from the full core bundle.</summary>
32+
internal static TypeRegistry Compose() => Provider.ComposeRegistry(Providers);
33+
}

server/csharp/MetaObjects.Conformance.Tests/RegistryManifestConformanceTests.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@ public void Emit_matches_the_committed_cross_port_canonical()
2626
CoreTypes.CoreTypesProvider,
2727
// DB-domain field attrs (@column / @db.indexed / @dbColumnType) — Extend over core.
2828
MetaObjects.Persistence.Db.DbMetaDataProvider.Instance,
29-
// Template/output-domain field attr (@xmlText) — Extend over core.
30-
MetaObjects.Template.TemplateTypesProvider.Instance,
29+
// FR-033 concern providers — UI / prompt attrs re-homed out of core
30+
// (read spec/metamodel/ui.json + prompt.json). The prompt provider
31+
// absorbs the @xmlText marker the former TemplateTypesProvider registered.
32+
MetaObjects.Presentation.Ui.UiMetaDataProvider.Instance,
33+
MetaObjects.Template.PromptMetaDataProvider.Instance,
3134
DocumentationTypes.DocTypesProvider,
3235
});
3336

@@ -58,8 +61,11 @@ public void Every_registered_per_type_attr_is_explicitly_classified()
5861
CoreTypes.CoreTypesProvider,
5962
// DB-domain field attrs (@column / @db.indexed / @dbColumnType) — Extend over core.
6063
MetaObjects.Persistence.Db.DbMetaDataProvider.Instance,
61-
// Template/output-domain field attr (@xmlText) — Extend over core.
62-
MetaObjects.Template.TemplateTypesProvider.Instance,
64+
// FR-033 concern providers — UI / prompt attrs re-homed out of core
65+
// (read spec/metamodel/ui.json + prompt.json). The prompt provider
66+
// absorbs the @xmlText marker the former TemplateTypesProvider registered.
67+
MetaObjects.Presentation.Ui.UiMetaDataProvider.Instance,
68+
MetaObjects.Template.PromptMetaDataProvider.Instance,
6369
DocumentationTypes.DocTypesProvider,
6470
});
6571

server/csharp/MetaObjects.Conformance.Tests/TemplateToolcallTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class TemplateToolcallTests
1818
{
1919
private static LoadResult LoadJson(string json, string id)
2020
{
21-
var registry = Provider.ComposeRegistry(new[] { CoreTypes.CoreTypesProvider });
21+
var registry = FullCoreRegistry.Compose();
2222
var loader = new MetaDataLoader(registry);
2323
return loader.Load(new IMetaDataSource[]
2424
{
@@ -33,7 +33,7 @@ private static LoadResult LoadJson(string json, string id)
3333
[Fact]
3434
public void Core_provider_registers_template_toolcall_subtype()
3535
{
36-
var registry = Provider.ComposeRegistry(new[] { CoreTypes.CoreTypesProvider });
36+
var registry = FullCoreRegistry.Compose();
3737
Assert.True(registry.Has(TYPE_TEMPLATE, TEMPLATE_SUBTYPE_TOOLCALL));
3838
}
3939

@@ -50,7 +50,7 @@ public void Template_toolcall_attr_schema_does_not_inherit_generic_attrs()
5050
// Per ADR-0011 the toolcall subtype does NOT inherit the prompt/output
5151
// generic attrs. It declares its own minimal set: toolName + payloadRef
5252
// + owner + since. @textRef and @format are intentionally absent.
53-
var registry = Provider.ComposeRegistry(new[] { CoreTypes.CoreTypesProvider });
53+
var registry = FullCoreRegistry.Compose();
5454
var def = registry.Find(TYPE_TEMPLATE, TEMPLATE_SUBTYPE_TOOLCALL);
5555
Assert.NotNull(def);
5656

server/csharp/MetaObjects/Core/Field/FieldSchema.cs

Lines changed: 15 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
// Colocated per ADR-0003. Mirrors typescript/packages/metadata/src/core/field/field-schema.ts.
55

66
using MetaObjects.Core.Attr;
7-
using MetaObjects.Core.Query;
87

98
namespace MetaObjects.Core.Field;
109

@@ -95,24 +94,11 @@ public static class FieldSchema
9594
Required: false,
9695
Description: "Number of digits to the right of the decimal point for decimal-typed fields."),
9796

98-
new AttrSchema(
99-
Name: FieldConstants.FIELD_ATTR_FILTERABLE,
100-
ValueType: AttrConstants.ATTR_SUBTYPE_BOOLEAN,
101-
Required: false,
102-
Description: "When true, the field is exposed in generated CRUD filter allowlists (Project D filter layer)."),
103-
104-
new AttrSchema(
105-
Name: FieldConstants.FIELD_ATTR_SORTABLE,
106-
ValueType: AttrConstants.ATTR_SUBTYPE_BOOLEAN,
107-
Required: false,
108-
Description: "When true, the field is exposed in generated CRUD sort allowlists. Inherits from @filterable by default; set false to opt out."),
109-
110-
new AttrSchema(
111-
Name: FieldConstants.FIELD_ATTR_SORTABLE_DEFAULT_ORDER,
112-
ValueType: AttrConstants.ATTR_SUBTYPE_STRING,
113-
Required: false,
114-
AllowedValues: [.. QueryConstants.SORT_ORDER_VALUES],
115-
Description: "Default sort direction applied when this field is the default sort field."),
97+
// FR-033 — the UI / query-surface markers (@filterable / @sortable /
98+
// @sortableDefaultOrder) are NO LONGER declared here. They are re-homed to the
99+
// metaobjects-ui concern provider (UiMetaDataProvider, reads ui.json's field.*
100+
// extends), matching the TS uiProvider split. Core keeps only the attrs it
101+
// legitimately owns.
116102

117103
new AttrSchema(
118104
Name: FieldConstants.FIELD_ATTR_AUTO_SET,
@@ -128,19 +114,10 @@ public static class FieldSchema
128114
// string-typed @column still fires because the DB provider is composed into the default
129115
// registry.)
130116

131-
// FR-010 field-teaching attrs (any field): free-text shown in the generated
132-
// output-format prompt fragment. Never carried in comments.
133-
new AttrSchema(
134-
Name: FieldConstants.FIELD_ATTR_EXAMPLE,
135-
ValueType: AttrConstants.ATTR_SUBTYPE_STRING,
136-
Required: false,
137-
Description: "FR-010: an example value for this field, shown in the generated output-format prompt fragment."),
138-
139-
new AttrSchema(
140-
Name: FieldConstants.FIELD_ATTR_INSTRUCTION,
141-
ValueType: AttrConstants.ATTR_SUBTYPE_STRING,
142-
Required: false,
143-
Description: "FR-010: a short instruction for this field, shown in the generated output-format prompt fragment."),
117+
// FR-033 — the FR-010 field-teaching prompt markers (@example / @instruction)
118+
// are NO LONGER declared here. They are re-homed to the metaobjects-prompt
119+
// concern provider (PromptMetaDataProvider, reads prompt.json's field.* extends),
120+
// alongside @xmlText and the field.enum tolerant-extract overlays.
144121
];
145122

146123
/// <summary>The @currency attr — only on field.currency.</summary>
@@ -174,48 +151,10 @@ public static class FieldSchema
174151
"codegen references the type (resolved via per-port codegen config) instead of " +
175152
"materializing it. Default false. Not a field attr — it lives on the type declaration.");
176153

177-
/// <summary>
178-
/// The @enumAlias attr — only on field.enum. Map of off-vocabulary token → canonical
179-
/// member, feeding the FR-010 tolerant extract alias-fold (runtime aliases win on conflict).
180-
/// </summary>
181-
public static readonly AttrSchema EnumAliasAttr = new AttrSchema(
182-
Name: FieldConstants.FIELD_ATTR_ENUM_ALIAS,
183-
ValueType: AttrConstants.ATTR_SUBTYPE_PROPERTIES,
184-
Required: false,
185-
Description: "Map of alternate/off-vocabulary tokens to canonical enum members; feeds the FR-010 tolerant extract alias-fold.");
186-
187-
/// <summary>
188-
/// The @enumDoc attr — only on field.enum. Map of member → human-readable description,
189-
/// shown per-member in the FR-010 'guide'-style output-format prompt fragment.
190-
/// </summary>
191-
public static readonly AttrSchema EnumDocAttr = new AttrSchema(
192-
Name: FieldConstants.FIELD_ATTR_ENUM_DOC,
193-
ValueType: AttrConstants.ATTR_SUBTYPE_PROPERTIES,
194-
Required: false,
195-
Description: "Map of enum member to a human-readable description; shown per-member in the FR-010 'guide'-style prompt fragment.");
196-
197-
/// <summary>
198-
/// FR-011: the @coerceDefault attr — only on field.enum. String member symbol used as the
199-
/// extract fallback when an LLM sends a present-but-uncoercible value. Loader-validated to be
200-
/// one of the field's @values (ERR_BAD_ATTR_VALUE otherwise).
201-
/// </summary>
202-
public static readonly AttrSchema CoerceDefaultAttr = new AttrSchema(
203-
Name: FieldConstants.FIELD_ATTR_COERCE_DEFAULT,
204-
ValueType: AttrConstants.ATTR_SUBTYPE_STRING,
205-
Required: false,
206-
Description: "Fallback enum member used by tolerant extract when a present value cannot be coerced; must be one of the field's @values.");
207-
208-
/// <summary>
209-
/// FR-011: the @normalize attr — on field.enum (per-field) and object.value (object default).
210-
/// Closed enum (none|collapse|strip); controls the ASCII normalization applied during tolerant
211-
/// enum extract. Resolved field → owning object.value → global default (strip).
212-
/// </summary>
213-
public static readonly AttrSchema NormalizeAttr = new AttrSchema(
214-
Name: FieldConstants.FIELD_ATTR_NORMALIZE,
215-
ValueType: AttrConstants.ATTR_SUBTYPE_STRING,
216-
Required: false,
217-
Default: FieldConstants.NORMALIZE_DEFAULT,
218-
AllowedValues: new object[] { "none", "collapse", "strip" },
219-
Description: "ASCII normalization mode for tolerant enum extract (none|collapse|strip, default strip). " +
220-
"On field.enum it is per-field; on object.value it is the default for the object's enum fields.");
154+
// FR-033 — the field.enum tolerant-extract overlays (@enumAlias / @enumDoc /
155+
// @coerceDefault / @normalize) and object.value's @normalize default are NO LONGER
156+
// declared here. They are re-homed to the metaobjects-prompt concern provider
157+
// (PromptMetaDataProvider, reads prompt.json's field.enum + object.value extends),
158+
// matching the TS promptProvider split. Core keeps @values / @provided (the
159+
// structural enum vocabulary it legitimately owns).
221160
}

0 commit comments

Comments
 (0)