You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit e075dab
Browse filesBrowse the repository at this point in the historyBrowse files
docs(authoring): the jsonb ladder still taught the #362 gap 1.0.1 closed
The authoring skill's `field.map` caveat told readers that Java skips nested
value-object validation on TPH write paths and that "C# validates them on NO
write path", and pointed at #362 as a live gap — advising against the rung for
a Java TPH entity "or for C# at all". 1.0.1 fixed all of that.
The audit skill and docs/features/field-types.md were both updated at the fix;
this one was missed. It is the worst of the three to leave stale, because it is
the instrument an author uses to CHOOSE a rung — a closed gap presented as open
steers a correct `field.map` declaration down to an untyped bag, which is the
one column shape the ladder exists to replace.
Reframed the way the audit skill already puts it: this is a VERSION question,
not a port question. Every port validates on every write path as of 1.0.1; on
1.0.0 the hole is open, so an adopter's pinned version is what decides whether
they still need their own boundary check — and one written while it was open is
now redundant rather than load-bearing. The mechanism is kept, in the past
tense, because the failure mode was silent acceptance and someone diagnosing an
old row still needs to recognise it.
Regenerated fixtures/agent-context-conformance/ in the same commit: the
authoring skill lands in all five stacks, so an edit without the regen turns
ts-unit red on the next push.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTcEKXTQMYt84fAjuw5A2M
Copy file name to clipboardExpand all lines: fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-authoring/SKILL.md
+15-13Lines changed: 15 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -584,19 +584,21 @@ Python's `ObjectManager` encodes a map today; `runtime-ts`, Java's OMDB and the
584
584
lane carry no map handling at all. So a map you intend to read back through a PORT RUNTIME is
585
585
still better declared as a value object, and a genuinely dynamic key set stays a bag.
586
586
587
-
**One sharp edge where generated code IS the consumer: nested map values can be written
588
-
UNVALIDATED, per port.** TypeScript (`z.record` over the VO's insert schema) and Python
589
-
(`dict[str, VO]` Pydantic) validate map values, and Kotlin writes no map column. **Java**
590
-
validates them on its vanilla create/PATCH handlers but NOT on TPH (discriminator-rooted) write
591
-
paths — those validate field-by-field with `validateValue`, which does not cascade `@Valid`.
592
-
**C# validates them on NO write path** — vanilla create, vanilla PATCH, and TPH alike: the map
593
-
never reaches the recursively-validating value-object arms (they admit `field.object` only),
594
-
and the generic arms check the dictionary property itself, never its values. A posted value
595
-
violating the referenced `object.value`'s constraints is accepted and written, silently, and
596
-
reading the adopter's own source will not reveal it. Scalar-valued maps (`@valueType`) are
597
-
unaffected. Do not recommend this rung for a Java TPH entity — or for C# at all — without
598
-
saying so and pointing at boundary validation of map values before write;
599
-
[issue #362](https://github.com/metaobjectsdev/metaobjects/issues/362) tracks the gap. Every rung but the first keeps the
587
+
**One sharp edge where generated code IS the consumer, and it is now a VERSION question rather
588
+
than a port question: nested map values.** As of **1.0.1** every port validates them on every
589
+
write path ([#362](https://github.com/metaobjectsdev/metaobjects/issues/362)). On **1.0.0 the
590
+
hole is open**, so check the adopter's pinned version before recommending this rung to a Java
591
+
or C# consumer: Java validated nested map values on its vanilla create/PATCH handlers but not
592
+
on TPH (discriminator-rooted) write paths, which validate field-by-field with `validateValue`
593
+
and do not cascade `@Valid`; C# validated them on no write path at all — the map never reached
594
+
the recursively-validating value-object arms (they admit `field.object` only), and
595
+
`ValueObjectValidator` treated a `Dictionary` as a plain `IEnumerable`, validating
596
+
`KeyValuePair` structs instead of the values. TypeScript and Python were never affected, and
597
+
Kotlin writes no map column. Scalar-valued maps (`@valueType`) carry no nested bean and were
598
+
never affected on any version. **The failure mode was silent acceptance, not an error** — a
599
+
POST or PATCH carrying an invalid nested value returned 201/200 and wrote the row — so an
600
+
adopter still on 1.0.0 needs their own boundary check, and one written while this was open is
601
+
now redundant rather than load-bearing. Every rung but the first keeps the
600
602
column jsonb, so moving a column up the ladder is a codegen/contract change rather than a
601
603
migration — read the emitted DDL before promising that.
Copy file name to clipboardExpand all lines: fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-authoring/SKILL.md
+15-13Lines changed: 15 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -584,19 +584,21 @@ Python's `ObjectManager` encodes a map today; `runtime-ts`, Java's OMDB and the
584
584
lane carry no map handling at all. So a map you intend to read back through a PORT RUNTIME is
585
585
still better declared as a value object, and a genuinely dynamic key set stays a bag.
586
586
587
-
**One sharp edge where generated code IS the consumer: nested map values can be written
588
-
UNVALIDATED, per port.** TypeScript (`z.record` over the VO's insert schema) and Python
589
-
(`dict[str, VO]` Pydantic) validate map values, and Kotlin writes no map column. **Java**
590
-
validates them on its vanilla create/PATCH handlers but NOT on TPH (discriminator-rooted) write
591
-
paths — those validate field-by-field with `validateValue`, which does not cascade `@Valid`.
592
-
**C# validates them on NO write path** — vanilla create, vanilla PATCH, and TPH alike: the map
593
-
never reaches the recursively-validating value-object arms (they admit `field.object` only),
594
-
and the generic arms check the dictionary property itself, never its values. A posted value
595
-
violating the referenced `object.value`'s constraints is accepted and written, silently, and
596
-
reading the adopter's own source will not reveal it. Scalar-valued maps (`@valueType`) are
597
-
unaffected. Do not recommend this rung for a Java TPH entity — or for C# at all — without
598
-
saying so and pointing at boundary validation of map values before write;
599
-
[issue #362](https://github.com/metaobjectsdev/metaobjects/issues/362) tracks the gap. Every rung but the first keeps the
587
+
**One sharp edge where generated code IS the consumer, and it is now a VERSION question rather
588
+
than a port question: nested map values.** As of **1.0.1** every port validates them on every
589
+
write path ([#362](https://github.com/metaobjectsdev/metaobjects/issues/362)). On **1.0.0 the
590
+
hole is open**, so check the adopter's pinned version before recommending this rung to a Java
591
+
or C# consumer: Java validated nested map values on its vanilla create/PATCH handlers but not
592
+
on TPH (discriminator-rooted) write paths, which validate field-by-field with `validateValue`
593
+
and do not cascade `@Valid`; C# validated them on no write path at all — the map never reached
594
+
the recursively-validating value-object arms (they admit `field.object` only), and
595
+
`ValueObjectValidator` treated a `Dictionary` as a plain `IEnumerable`, validating
596
+
`KeyValuePair` structs instead of the values. TypeScript and Python were never affected, and
597
+
Kotlin writes no map column. Scalar-valued maps (`@valueType`) carry no nested bean and were
598
+
never affected on any version. **The failure mode was silent acceptance, not an error** — a
599
+
POST or PATCH carrying an invalid nested value returned 201/200 and wrote the row — so an
600
+
adopter still on 1.0.0 needs their own boundary check, and one written while this was open is
601
+
now redundant rather than load-bearing. Every rung but the first keeps the
600
602
column jsonb, so moving a column up the ladder is a codegen/contract change rather than a
601
603
migration — read the emitted DDL before promising that.
0 commit comments