Skip to content

Commit 6053f38

Browse files
dmealingclaude
andcommitted
feat(#195): register 4 projection read-model origin capabilities (coordinated cross-port vocab)
The coordinated all-ports registration for the four #195 origin capabilities. Registration is atomic across TS/C#/Java/Python because registry-conformance byte-matches a single shared manifest — a port registering alone reddens the gate until the shared file updates, which reddens the others until they follow. New vocabulary (canonical spec spec/metamodel/{origin,attr}.json → per-port): - origin.aggregate @agg extended: count|sum|avg|min|max + any|all (predicate quantifiers) + collect (array rollup). @Of relaxed to schema-optional (any/all forbid it, the rest require it — presence enforced per-@agg in validation). - new @distinct (collect set-semantics) + @orderby (element/row ordering, nulls-last) origin attrs. - new attr.expression subtype: an object-valued closed expression tree stored verbatim (no desugar), backing origin.computed. Mirrors attr.filter per port. - new origin.computed (@expr, required) — row-level value from the base entity's own fields via the expression tree. - new origin.first (@Of + @orderby required, @via + @filter optional) — the one related row picked by @orderby along @via (argmax-then-project). Per port: - TS: constants, ExpressionAttr (registerAttrClass), MetaComputedOrigin/ MetaFirstOrigin + ORIGIN_CLASS_MAP, regenerated embedded copies, completeness/ schema tests. Standalone attr.expression grammar (validate/infer/embed) already shipped (4586832). - C#: AttrConstants + AttrDataType (expression→Object), OriginConstants/ OriginSchema (agg vocab, @Of optional, +distinct/+orderBy, computed/first attr maps), SpecMetamodel/{origin,attr}.json synced. - Java: ExpressionAttribute + ComputedOrigin + FirstOrigin + provider registration; AggregateOrigin agg enum + @Of optional + @distinct/@orderby; FR024_PENDING @Of override retired (auto-refreshes spec/). - Python: attr ExpressionAttr + constants, origin constants + core_types schema entries (agg vocab, @Of optional, computed/first), spec_metamodel synced. Validation logic (per-@agg presence, computed type-inference, first type-preservation) + view synthesis are follow-up phases. This commit only registers + types the vocabulary. Conformance: 3 new fixtures (origin-agg-collect, origin-computed-isnotnull, origin-first-latest) exercise the new subtypes/attrs; expected-registry.json + coverage-report.json + metamodel-docs + ERROR-CODES.json (ERR_COMPUTED_TYPE_ MISMATCH, ERR_UNKNOWN_EXPR_NODE) updated. Gates green: TS metadata 2152 / codegen 918 / migrate 586, C# conformance 736, Java metadata 1141, Python conformance 336 — all byte-match the shared manifest. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGQ7oSuNcjhsMHWwZzhBwr
1 parent 62f2afd commit 6053f38

50 files changed

Lines changed: 1652 additions & 120 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

fixtures/conformance/ERROR-CODES.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@
6767
"ERR_ENTITY_PRIMARY_SOURCE_READONLY": "FR-024 (ADR-0028) hard cutover: an object.entity's PRIMARY source has a read-only @kind (view/materializedView/storedProc/tableFunction). Read-only kinds are legal only in non-primary roles; a derived read model is an object.projection.",
6868
"ERR_RELATIVE_REF_IN_CANONICAL": "FR-032 (ADR-0032): a ref-bearing attribute (extends/@objectRef/@references/origin @from/@of/@via/@parameterRef/@payloadRef/@responseRef) in CANONICAL JSON used a relative authoring form (leading :: or ..::). Canonical JSON is the self-contained interchange form — every reference MUST be fully-qualified. Relative navigation (root-absolute ::, parent-relative ..::, bare-current-package) is a YAML-authoring affordance the desugar expands to FQN; it must never survive into canonical JSON.",
6969
"ERR_INVALID_METAMODEL_CONSTRAINT": "FR-033: a provider set's merged metamodel constraint graph contains a contradiction surfaced by validateConstraints — one of: (1) a parents/children rule references an unregistered type.subType (dangling); (2) a required child (min>=1) whose type.subType is not admitted under that parent (unsatisfiable); (3) bad cardinality (min>max, max:0 with min>=1, or min<0); (4) closed-set clash — a child declares a parent whose OWN children is a closed set (no * wildcard) that does not admit it; (5) a required-child cycle that cannot bottom out; (6) the same attr name contributed twice (across providers or the extends chain) with a conflicting valueType/required/default. The detail names which check and the offending type(s).",
70-
"ERR_INVALID_INDEX": "An index.lookup's @fields is empty (at least one field is required) or names a field that does not exist on the owning entity's effective (resolved, via extends) field set."
70+
"ERR_INVALID_INDEX": "An index.lookup's @fields is empty (at least one field is required) or names a field that does not exist on the owning entity's effective (resolved, via extends) field set.",
71+
"ERR_COMPUTED_TYPE_MISMATCH": "#195: an origin.computed @expr tree's inferred root type does not equal the carrying field's declared field.<subType>. A computed column's type is DERIVED from its expression, never asserted (no @convert escape), so a mismatch is a hard load error (sibling of ERR_PASSTHROUGH_TYPE_MISMATCH).",
72+
"ERR_UNKNOWN_EXPR_NODE": "#195: an origin.computed @expr tree contains a node whose kind/op/fn is not in the closed expression grammar (field/value refs, comparisons sharing the filter op vocabulary, isNull/isNotNull, and/or/not, coalesce). Fail-closed per ADR-0023; the detail names the offending token."
7173
}
7274
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
{
2+
"metadata.root": {
3+
"package": "acme::commerce",
4+
"children": [
5+
{
6+
"object.entity": {
7+
"name": "Order",
8+
"children": [
9+
{
10+
"source.rdb": {
11+
"@table": "orders"
12+
}
13+
},
14+
{
15+
"field.long": {
16+
"name": "id"
17+
}
18+
},
19+
{
20+
"relationship.association": {
21+
"name": "items",
22+
"@cardinality": "many",
23+
"@objectRef": "acme::commerce::Item"
24+
}
25+
},
26+
{
27+
"identity.primary": {
28+
"name": "id",
29+
"@fields": [
30+
"id"
31+
]
32+
}
33+
}
34+
]
35+
}
36+
},
37+
{
38+
"object.entity": {
39+
"name": "Item",
40+
"children": [
41+
{
42+
"source.rdb": {
43+
"@table": "items"
44+
}
45+
},
46+
{
47+
"field.long": {
48+
"name": "id"
49+
}
50+
},
51+
{
52+
"field.string": {
53+
"name": "category"
54+
}
55+
},
56+
{
57+
"field.string": {
58+
"name": "sku"
59+
}
60+
},
61+
{
62+
"field.timestamp": {
63+
"name": "createdAt"
64+
}
65+
},
66+
{
67+
"identity.primary": {
68+
"name": "id",
69+
"@fields": [
70+
"id"
71+
]
72+
}
73+
}
74+
]
75+
}
76+
},
77+
{
78+
"object.projection": {
79+
"name": "OrderSummary",
80+
"children": [
81+
{
82+
"source.rdb": {
83+
"@kind": "view",
84+
"@view": "v_order_summary"
85+
}
86+
},
87+
{
88+
"field.long": {
89+
"name": "id",
90+
"extends": "acme::commerce::Order.id"
91+
}
92+
},
93+
{
94+
"field.string": {
95+
"name": "categories",
96+
"isArray": true,
97+
"children": [
98+
{
99+
"origin.aggregate": {
100+
"@agg": "collect",
101+
"@distinct": true,
102+
"@of": "acme::commerce::Item.category",
103+
"@via": "acme::commerce::Order.items"
104+
}
105+
}
106+
]
107+
}
108+
},
109+
{
110+
"field.string": {
111+
"name": "skuTimeline",
112+
"isArray": true,
113+
"children": [
114+
{
115+
"origin.aggregate": {
116+
"@agg": "collect",
117+
"@of": "acme::commerce::Item.sku",
118+
"@orderBy": [
119+
"createdAt:asc"
120+
],
121+
"@via": "acme::commerce::Order.items"
122+
}
123+
}
124+
]
125+
}
126+
},
127+
{
128+
"identity.primary": {
129+
"name": "id",
130+
"extends": "acme::commerce::Order.id"
131+
}
132+
}
133+
]
134+
}
135+
}
136+
]
137+
}
138+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{
2+
"metadata.root": {
3+
"package": "acme::commerce",
4+
"children": [
5+
{
6+
"object.entity": {
7+
"name": "Order",
8+
"children": [
9+
{ "source.rdb": { "@table": "orders" } },
10+
{ "field.long": { "name": "id" } },
11+
{ "relationship.association": { "name": "items", "@objectRef": "acme::commerce::Item", "@cardinality": "many" } },
12+
{ "identity.primary": { "name": "id", "@fields": "id" } }
13+
]
14+
}
15+
},
16+
{
17+
"object.entity": {
18+
"name": "Item",
19+
"children": [
20+
{ "source.rdb": { "@table": "items" } },
21+
{ "field.long": { "name": "id" } },
22+
{ "field.string": { "name": "category" } },
23+
{ "field.string": { "name": "sku" } },
24+
{ "field.timestamp": { "name": "createdAt" } },
25+
{ "identity.primary": { "name": "id", "@fields": "id" } }
26+
]
27+
}
28+
},
29+
{
30+
"object.projection": {
31+
"name": "OrderSummary",
32+
"children": [
33+
{ "source.rdb": { "@kind": "view", "@view": "v_order_summary" } },
34+
{ "field.long": { "name": "id", "extends": "acme::commerce::Order.id" } },
35+
{
36+
"field.string": {
37+
"name": "categories",
38+
"isArray": true,
39+
"children": [
40+
{
41+
"origin.aggregate": {
42+
"@agg": "collect",
43+
"@of": "acme::commerce::Item.category",
44+
"@via": "acme::commerce::Order.items",
45+
"@distinct": true
46+
}
47+
}
48+
]
49+
}
50+
},
51+
{
52+
"field.string": {
53+
"name": "skuTimeline",
54+
"isArray": true,
55+
"children": [
56+
{
57+
"origin.aggregate": {
58+
"@agg": "collect",
59+
"@of": "acme::commerce::Item.sku",
60+
"@via": "acme::commerce::Order.items",
61+
"@orderBy": ["createdAt:asc"]
62+
}
63+
}
64+
]
65+
}
66+
},
67+
{ "identity.primary": { "name": "id", "extends": "acme::commerce::Order.id" } }
68+
]
69+
}
70+
}
71+
]
72+
}
73+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
["metaobjects-core-types","metaobjects-db"]
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
{
2+
"metadata.root": {
3+
"package": "acme::observability",
4+
"children": [
5+
{
6+
"object.entity": {
7+
"name": "LlmCall",
8+
"children": [
9+
{
10+
"source.rdb": {
11+
"@table": "llm_calls"
12+
}
13+
},
14+
{
15+
"field.long": {
16+
"name": "id"
17+
}
18+
},
19+
{
20+
"field.string": {
21+
"name": "payloadJson"
22+
}
23+
},
24+
{
25+
"identity.primary": {
26+
"name": "id",
27+
"@fields": [
28+
"id"
29+
]
30+
}
31+
}
32+
]
33+
}
34+
},
35+
{
36+
"object.projection": {
37+
"name": "LlmCallSummary",
38+
"children": [
39+
{
40+
"source.rdb": {
41+
"@kind": "view",
42+
"@view": "v_llm_call_summary"
43+
}
44+
},
45+
{
46+
"field.long": {
47+
"name": "id",
48+
"extends": "acme::observability::LlmCall.id"
49+
}
50+
},
51+
{
52+
"field.boolean": {
53+
"name": "hasPayload",
54+
"children": [
55+
{
56+
"origin.computed": {
57+
"@expr": {
58+
"op": "isNotNull",
59+
"arg": {
60+
"field": "payloadJson"
61+
}
62+
}
63+
}
64+
}
65+
]
66+
}
67+
},
68+
{
69+
"identity.primary": {
70+
"name": "id",
71+
"extends": "acme::observability::LlmCall.id"
72+
}
73+
}
74+
]
75+
}
76+
}
77+
]
78+
}
79+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"metadata.root": {
3+
"package": "acme::observability",
4+
"children": [
5+
{
6+
"object.entity": {
7+
"name": "LlmCall",
8+
"children": [
9+
{ "source.rdb": { "@table": "llm_calls" } },
10+
{ "field.long": { "name": "id" } },
11+
{ "field.string": { "name": "payloadJson" } },
12+
{ "identity.primary": { "name": "id", "@fields": "id" } }
13+
]
14+
}
15+
},
16+
{
17+
"object.projection": {
18+
"name": "LlmCallSummary",
19+
"children": [
20+
{ "source.rdb": { "@kind": "view", "@view": "v_llm_call_summary" } },
21+
{ "field.long": { "name": "id", "extends": "acme::observability::LlmCall.id" } },
22+
{
23+
"field.boolean": {
24+
"name": "hasPayload",
25+
"children": [
26+
{
27+
"origin.computed": {
28+
"@expr": { "op": "isNotNull", "arg": { "field": "payloadJson" } }
29+
}
30+
}
31+
]
32+
}
33+
},
34+
{ "identity.primary": { "name": "id", "extends": "acme::observability::LlmCall.id" } }
35+
]
36+
}
37+
}
38+
]
39+
}
40+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
["metaobjects-core-types","metaobjects-db"]

0 commit comments

Comments
 (0)