Skip to content

Commit b3c3e34

Browse files
committed
fix(loader): register the generic view.* controls for loading in Python, C# and Java
A document carrying view.text, view.dropdown or any other web-presentation control loaded in TypeScript and failed with ERR_UNKNOWN_SUBTYPE in Python, C# and Java (and Kotlin, which shares the Java loader). Those ports had left the controls unregistered on purpose, as vocabulary with no backend consumer, which made metadata shared between a TypeScript web client and a backend port unloadable in the backend. Every port now registers the same view subtype list as TypeScript. The controls stay presentation-only: no backend generator reads them, and each manifest emitter still classifies them PRESENTATION_ONLY, so expected-registry.json and metamodelVersion do not move. Gated by the new shared view-text-basic conformance fixture. The registry conformance README, CONFORMANCE.md, the audit skill and the code comments that described the old ruling are corrected.
1 parent 8fb10f5 commit b3c3e34

27 files changed

Lines changed: 299 additions & 57 deletions

File tree

‎AGENTS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ PyPI has had no product change since `0.25.0` — nothing is broken.
7575
- **Kotlin** — `codegen-kotlin` (KotlinPoet on JVM): entity + Exposed table + Spring controller + payload + relations + filter allowlist + validator + stored-proc + output-parser generators. `integration-tests-kotlin` runs the persistence-conformance corpus through Exposed against Testcontainers Postgres.
7676

7777
**Cross-port conformance corpora** (every port runs the shared corpus):
78-
- Metamodel: `fixtures/conformance/` (362 fixtures; 25 shared corpora in total — per-corpus counts + the corpus x port matrix live in `docs/CONFORMANCE.md`). TS / C# / Java / Python all green.
78+
- Metamodel: `fixtures/conformance/` (363 fixtures; 25 shared corpora in total — per-corpus counts + the corpus x port matrix live in `docs/CONFORMANCE.md`). TS / C# / Java / Python all green.
7979
- Render: `fixtures/render-conformance/`. TS / C# / Java / Kotlin / Python byte-identical.
8080
- Persistence: `fixtures/persistence-conformance/`. **Query** scenarios run on every port (TS / C# / Java / Kotlin / Python), each provisioning its test DB by executing the committed, TS-produced `canonical/schema.postgres.sql` (Postgres only — Derby dropped for the cross-port query corpus, ADR-0015). The **migration** scenarios are exercised by **TS only** (TS owns schema migrations). **The corpus now gates WRITES, not just reads (SP-H):** an `op: roundtrip` scenario type INSERTs through each port's runtime/ORM write codec (NOT raw SQL), reads the row back, and asserts the wire-normalized value. The `AllTypes` entity (`roundtrip-all-types.yaml`) carries one field of **every** persistable `field.*` subtype — string/int/long/double/float/decimal/boolean/date/time/timestamp(+tz)/currency/enum/uuid/object — plus an **array-of-VO** `field.object @isArray @storage:jsonb` column (`labels`, written as 2-element / empty-`[]` / single-element arrays across the three rows) — so every subtype write+read (incl. the array-of-value-object jsonb codec) round-trips through every port against Testcontainers PG. (`field.byte`/`field.short`/`field.class` were cut as non-functional registration-only stubs — the matrix tracks only genuinely-supported subtypes; see `fixtures/registry-conformance/README.md` → "Per-subtype write-round-trip matrix".)
8181
- API-contract: `fixtures/api-contract-conformance/`. TS / C# / Java / Kotlin / Python all green — each port runs **two lanes**: a hand-rolled reference server AND its **generated** API artifact booted over HTTP (the deployed controller/routes; TS+C# full-stack vs Testcontainers PG, Java/Kotlin/Python generated controller + in-memory repo behind the consumer seam). The generated fan-out found 10 real deployment bugs golden snapshots missed. Two sub-corpora run the **generated lane only, on all five ports** — `write-through/` and `projection/` (F22: a view-only `object.projection` serves GET list + GET by id and answers every write verb with `405 {"error": "method_not_allowed"}`). That is deliberate, not a gap: what is under test is whether a port's GENERATOR emits those routes, and a hand-rolled reference server would answer every scenario by construction. The `m2m/` sub-corpus also gates **TPH x M:N together** (base-declared, subtype-declared, abstract-mid-declared, a non-subtype source onto a subtype TARGET, and the cross-subtype source id answering `200 []`) — the two corpora were originally built disjoint (`tph/` had no relationships, `m2m/` no discriminators), which is precisely how that defect class survived.

‎CHANGELOG.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,17 @@ it until 1.1 ships._
6464

6565
### Fixed
6666

67+
- **Python, C#, Java: the generic `view.*` controls now load.** A document carrying
68+
`view.text`, `view.dropdown` or any of the other web-presentation controls (`textarea`, `date`,
69+
`month`, `hotlink`, `radio`, `checkbox`, `number`, `password`, `hidden`, `web`, `image`)
70+
loaded in TypeScript and failed with `ERR_UNKNOWN_SUBTYPE` in every other port, so metadata
71+
shared between a TypeScript web client and a Python, C#, Java or Kotlin backend could not be
72+
loaded by the backend. Those ports had left the controls unregistered on purpose, as
73+
vocabulary with no backend consumer. They are now registered for LOADING in every port and
74+
remain presentation-only: no backend generator reads them, and they stay out of
75+
`expected-registry.json`, so `metamodelVersion` does not move for this. Gated by the
76+
`view-text-basic` conformance fixture. Attributes on these controls are still registered only
77+
by the TypeScript `ui-web` provider.
6778
- **Python, C#, Java: an inline object-valued attribute now loads as an `attr.properties` bag.**
6879
`"@store": { "collection": "orders" }` on a node that declares no `@store` is the registered
6980
property bag, the same thing the explicit `{ "attr.properties": { "name": ..., "value": {...} } }`

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ metaobjects/
261261
├── CLAUDE.md # project instructions for Claude
262262
├── spec/ # canonical metamodel docs, ADRs, roadmap
263263
├── fixtures/ # 25 cross-language conformance corpora — the oracle
264-
│ ├── conformance/ # metamodel (loader + serializer + navigation), 362 fixtures
264+
│ ├── conformance/ # metamodel (loader + serializer + navigation), 363 fixtures
265265
│ ├── yaml-conformance/ # YAML authoring desugar
266266
│ ├── render-conformance/ # FR-004 byte-identical render oracle
267267
│ ├── verify-conformance/ # FR-004 template-drift gate

‎agent-context/skills/metaobjects-audit/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ The audit never edits code. Pattern: **dry-run → review the diff → apply**.
560560
`APIRouter`; relationship / non-`table` source-kind / `field.object flattened` codegen is partial.
561561
- **C#** has no ObjectManager runtime tier (EF Core is the runtime) — hand services over the generated `DbContext` are expected.
562562
- **Cut subtypes** — `field.byte` / `field.short` / `field.class` are removed; never recommend them.
563-
- **TS/web-only** — `view.*` widget subtypes exist only for TS/web consumers; only `view.base` / `view.currency` are cross-port-gated.
563+
- **TS/web-only** — `view.*` widget subtypes load in every port but are consumed only by TS/web; only `view.base` / `view.currency` are cross-port-gated.
564564
- **Planned, not shipped** — `api.*` / `operation.*` / `binding.*` (FR-024) and MCP exposure of declared prompts/tools are not yet in the registry; their absence is not an adopter defect.
565565
- **Cross-port version-NUMBER skew is by design** — TS/C#/Python `0.x` vs Java/Kotlin `7.x` Maven is correct; never flag the *number lines* differing. But that is exactly why you can't eyeball cross-language drift: compare **`metamodelVersion`** (Phase 0 cross-language consistency item), not the package numbers. A `metamodelVersion` MISMATCH across ports *is* a finding; so is a port lagging its ecosystem's latest release. Also flag *intra-port* drift (mixed versions within one port, or a runtime package in `devDependencies`).
566566
- **Stale upstream prose** — "hand-write the Spring controller" (Java/Kotlin) is out of date; trust `meta gen --list`, not stale prose.

‎agent-context/skills/metaobjects-audit/references/capability-checklist.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,12 @@ soft-delete / status / type view it models.
176176
options the currency view models. **Cross-port-gated** (with `view.base`).
177177
- **`layout.dataGrid`** (`@columns`, `@defaultSortField`, `@defaultSortOrder`, `@pageSize`) —
178178
hunt hand-written grid column definitions + data hooks a data-grid layout generates.
179-
- **CALIBRATION — TS/web-only:** the `view.*` widget subtypes exist only for TS/web consumers
179+
- **CALIBRATION — TS/web-only:** the `view.*` widget subtypes are consumed only by TS/web
180180
and are NOT in the cross-port registry — `view.text`, `view.textarea`, `view.date`,
181181
`view.month`, `view.hotlink`, `view.dropdown`, `view.radio`, `view.checkbox`, `view.number`,
182-
`view.password`, `view.hidden`, `view.web`. **Audit these only for TS adopters.** Only
182+
`view.password`, `view.hidden`, `view.web`. Every port LOADS them, so shared metadata that
183+
carries them is valid in a backend port; nothing outside TS/web reads them. **Audit these
184+
only for TS adopters.** Only
183185
`view.base` / `view.currency` are cross-port-gated.
184186

185187
## Template — `template.*` (prompt pillar)

‎docs/CONFORMANCE.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ regenerate with `ls -d fixtures/<corpus>/*/ | wc -l` for directory-shaped corpor
2626

2727
| Corpus | Fixtures | TS | Java | Kotlin | C# | Python |
2828
|---|---|---|---|---|---|---|
29-
| [`fixtures/conformance/`](../fixtures/conformance/) (metamodel) | 362 | ✓ | ✓ | inherits via `metadata-ktx` | ✓ | ✓ |
29+
| [`fixtures/conformance/`](../fixtures/conformance/) (metamodel) | 363 | ✓ | ✓ | inherits via `metadata-ktx` | ✓ | ✓ |
3030
| [`fixtures/yaml-conformance/`](../fixtures/yaml-conformance/) | 16 | 16 / 16 | 15 / 16 (1 ledgered: `yaml-quoted-leading-zero` — Java pipeline strips quotes off `"007"`) | inherits via Java | 15 / 16 (1 ledgered: `error-yaml-coerced-hex-in-string` — YamlDotNet doesn't coerce `0xFF`) | 16 / 16 |
3131
| [`fixtures/verify-conformance/`](../fixtures/verify-conformance/) | 31 | ✓ | ✓ | inherits via Java | ✓ | ✓ |
3232
| [`fixtures/verify-strict-conformance/`](../fixtures/verify-strict-conformance/) | 1 | ✓ | — | — | — | ✓ |
@@ -202,7 +202,7 @@ unit-test runners (`bun test`, `dotnet test`, `pytest`, `mvn test`) pull Docker.
202202

203203
## Fixture-to-doc mapping
204204

205-
### `fixtures/conformance/` — metamodel loader + canonical serializer (362)
205+
### `fixtures/conformance/` — metamodel loader + canonical serializer (363)
206206

207207
| Fixture prefix | Feature doc |
208208
|---|---|
@@ -340,7 +340,7 @@ identically** — every case's `expectFiles`/`expectImported`/`expectSelected`/
340340
`expectMigrateGoverned`/`expectLoadError` assertions apply to both runners with no
341341
exemption. The two overlay cases that need a view child as incidental content use
342342
`view.currency` (the one concrete `view.*` subtype registered cross-port), not
343-
`view.text` — a prior revision used `view.text`, which Python doesn't register, and
343+
`view.text` — a prior revision used `view.text`, which Python did not register then, and
344344
carried a since-discharged allowlist for it. Java, Kotlin and C# have no runner —
345345
Phase 1a is TypeScript + Python only; those three ports arrive in Phase 2.
346346

@@ -397,7 +397,7 @@ own those two functions), and
397397

398398
## Orphaned fixtures (tested but not yet documented)
399399

400-
The fixtures in the nine corpora mapped above (metamodel 362 + yaml 16 + verify 31
400+
The fixtures in the nine corpora mapped above (metamodel 363 + yaml 16 + verify 31
401401
+ render 15 + persistence 39 + api-contract 61 + source-resolution 25 + scope 10 +
402402
dependency 23) each map to a feature doc. None are orphaned today. The remaining
403403
corpora in the totals table gate tooling contracts (registry manifests, provider

‎fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ The audit never edits code. Pattern: **dry-run → review the diff → apply**.
560560
`APIRouter`; relationship / non-`table` source-kind / `field.object flattened` codegen is partial.
561561
- **C#** has no ObjectManager runtime tier (EF Core is the runtime) — hand services over the generated `DbContext` are expected.
562562
- **Cut subtypes** — `field.byte` / `field.short` / `field.class` are removed; never recommend them.
563-
- **TS/web-only** — `view.*` widget subtypes exist only for TS/web consumers; only `view.base` / `view.currency` are cross-port-gated.
563+
- **TS/web-only** — `view.*` widget subtypes load in every port but are consumed only by TS/web; only `view.base` / `view.currency` are cross-port-gated.
564564
- **Planned, not shipped** — `api.*` / `operation.*` / `binding.*` (FR-024) and MCP exposure of declared prompts/tools are not yet in the registry; their absence is not an adopter defect.
565565
- **Cross-port version-NUMBER skew is by design** — TS/C#/Python `0.x` vs Java/Kotlin `7.x` Maven is correct; never flag the *number lines* differing. But that is exactly why you can't eyeball cross-language drift: compare **`metamodelVersion`** (Phase 0 cross-language consistency item), not the package numbers. A `metamodelVersion` MISMATCH across ports *is* a finding; so is a port lagging its ecosystem's latest release. Also flag *intra-port* drift (mixed versions within one port, or a runtime package in `devDependencies`).
566566
- **Stale upstream prose** — "hand-write the Spring controller" (Java/Kotlin) is out of date; trust `meta gen --list`, not stale prose.

‎fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-audit/references/capability-checklist.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,12 @@ soft-delete / status / type view it models.
176176
options the currency view models. **Cross-port-gated** (with `view.base`).
177177
- **`layout.dataGrid`** (`@columns`, `@defaultSortField`, `@defaultSortOrder`, `@pageSize`) —
178178
hunt hand-written grid column definitions + data hooks a data-grid layout generates.
179-
- **CALIBRATION — TS/web-only:** the `view.*` widget subtypes exist only for TS/web consumers
179+
- **CALIBRATION — TS/web-only:** the `view.*` widget subtypes are consumed only by TS/web
180180
and are NOT in the cross-port registry — `view.text`, `view.textarea`, `view.date`,
181181
`view.month`, `view.hotlink`, `view.dropdown`, `view.radio`, `view.checkbox`, `view.number`,
182-
`view.password`, `view.hidden`, `view.web`. **Audit these only for TS adopters.** Only
182+
`view.password`, `view.hidden`, `view.web`. Every port LOADS them, so shared metadata that
183+
carries them is valid in a backend port; nothing outside TS/web reads them. **Audit these
184+
only for TS adopters.** Only
183185
`view.base` / `view.currency` are cross-port-gated.
184186

185187
## Template — `template.*` (prompt pillar)

‎fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-audit/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ The audit never edits code. Pattern: **dry-run → review the diff → apply**.
560560
`APIRouter`; relationship / non-`table` source-kind / `field.object flattened` codegen is partial.
561561
- **C#** has no ObjectManager runtime tier (EF Core is the runtime) — hand services over the generated `DbContext` are expected.
562562
- **Cut subtypes** — `field.byte` / `field.short` / `field.class` are removed; never recommend them.
563-
- **TS/web-only** — `view.*` widget subtypes exist only for TS/web consumers; only `view.base` / `view.currency` are cross-port-gated.
563+
- **TS/web-only** — `view.*` widget subtypes load in every port but are consumed only by TS/web; only `view.base` / `view.currency` are cross-port-gated.
564564
- **Planned, not shipped** — `api.*` / `operation.*` / `binding.*` (FR-024) and MCP exposure of declared prompts/tools are not yet in the registry; their absence is not an adopter defect.
565565
- **Cross-port version-NUMBER skew is by design** — TS/C#/Python `0.x` vs Java/Kotlin `7.x` Maven is correct; never flag the *number lines* differing. But that is exactly why you can't eyeball cross-language drift: compare **`metamodelVersion`** (Phase 0 cross-language consistency item), not the package numbers. A `metamodelVersion` MISMATCH across ports *is* a finding; so is a port lagging its ecosystem's latest release. Also flag *intra-port* drift (mixed versions within one port, or a runtime package in `devDependencies`).
566566
- **Stale upstream prose** — "hand-write the Spring controller" (Java/Kotlin) is out of date; trust `meta gen --list`, not stale prose.

‎fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-audit/references/capability-checklist.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,12 @@ soft-delete / status / type view it models.
176176
options the currency view models. **Cross-port-gated** (with `view.base`).
177177
- **`layout.dataGrid`** (`@columns`, `@defaultSortField`, `@defaultSortOrder`, `@pageSize`) —
178178
hunt hand-written grid column definitions + data hooks a data-grid layout generates.
179-
- **CALIBRATION — TS/web-only:** the `view.*` widget subtypes exist only for TS/web consumers
179+
- **CALIBRATION — TS/web-only:** the `view.*` widget subtypes are consumed only by TS/web
180180
and are NOT in the cross-port registry — `view.text`, `view.textarea`, `view.date`,
181181
`view.month`, `view.hotlink`, `view.dropdown`, `view.radio`, `view.checkbox`, `view.number`,
182-
`view.password`, `view.hidden`, `view.web`. **Audit these only for TS adopters.** Only
182+
`view.password`, `view.hidden`, `view.web`. Every port LOADS them, so shared metadata that
183+
carries them is valid in a backend port; nothing outside TS/web reads them. **Audit these
184+
only for TS adopters.** Only
183185
`view.base` / `view.currency` are cross-port-gated.
184186

185187
## Template — `template.*` (prompt pillar)

0 commit comments

Comments
 (0)