1.0.1 — completing 1.0
Why this is a PATCH and not a MINOR. docs/RELEASING.md's table says an additive CLI
flag and a new opt-in codegen capability are each a MINOR post-1.0, and this release carries
both. It is cut as a PATCH by explicit decision: everything here completes 1.0 rather than
extending it. --prompts closes D2, a 1.0-readiness gate that was still 🔶 at the cut,
and field.map codegen on Java and C# makes 1.0's own claim — that field.map is registered
cross-port vocabulary — true on the two ports where a map-bearing model previously failed
codegen outright. No adopter on 1.0.0 sees a behaviour change they did not already need.
Fixed
field.map @objectRefwrote its nested value objects UNVALIDATED — Java on TPH
(discriminator-rooted) write paths, C# on every write path (#362). The failure mode was
silent acceptance: a POST or PATCH carrying an invalid nested value returned 201/200 and the
row was written, with nothing in a log and nothing an adopter could find by reading their own
source, because the defect lived in generated code. Java's TPH paths validate field-by-field
withvalidateValue, which does not cascade@Validinto a nested bean; C# had two
independent causes — the map never reached the validating arms, and
ValueObjectValidatortreated aDictionaryas a plainIEnumerable, validating
KeyValuePairstructs instead of the values (which also left a map nested inside a value
object unchecked). TypeScript, Python and Kotlin were never affected; scalar-valued maps
(@valueType) carry no bean and are unchanged.
Changed
- One spelling for the prompt directory:
--prompts <dir>, on every port (F101, closing
D2). It had been spelled three ways —--prompts(Node, the reference),
--templates-root(Python) and--templates <root>inline on the subverb (C#). Both old
spellings keep working; they are deprecated in 1.1 and removed no earlier than a major, so
no script breaks at a patch or a minor.--promptssets the directory only — it does not
imply the gate, exactly as--outdoes not imply--codegen.
Documentation
codegen-kotlin's twoKNOWN_GAPS.mdfiles are merged (#361). They were not copies: each
documented gaps the other did not, so all 11 sections now live in the canonical file beside
the generators and the module root carries a pointer.- C#
KNOWN_GAPSG7 ruled (#359): narrowed to the TPH residual, and that residual is a
deliberate cross-port stage-out, not a C# divergence — Java stages out the same columns on
the same path. - Corpus and module counts corrected across README, AGENTS.md,
docs/CONFORMANCE.mdand the
llmsmirrors: the metamodel corpus is 313 fixtures (variously recorded as 270/286/253),
there are 21 shared corpora (recorded as 19), and Maven publishes 14 modules plus the
parent POM.server/java/README.mdhad pinned7.11.3in eight copy-pasteable blocks. fixtures/conformance/CAPABILITIES.jsonregenerated — it is derived from every fixture's
script.jsonand nothing reads it at test time, which is why it had gone stale unnoticed.
Fixed — field.map codegen completes: Java generates it, C# persists it
The subtype is registered in all five ports and TypeScript, Kotlin and Python already
generated it. Java and C# were the two halves left, failing in opposite directions — and
this entry is CODEGEN only, a bound the last paragraph states because the headline
invites the wider reading.
Java (codegen-spring) failed the build outright. A field.map on an entity flowed
into the DTO record and reached SpringTypeMapper's unsupported-type throw, so any entity
carrying one failed Java codegen. It now emits java.util.Map<String, V> — V the scalar
named by @valueType or the value object named by @objectRef — and the value-object
emission walk now spans a map's @objectRef, so a record reached only through a map is
actually generated rather than merely named by a DTO. isArray does not apply to a map, so
the type is never wrapped in List<>; every other port emits the map bare.
C# (MetaObjects.Codegen) emitted the property but not the storage.
EntityGenerator's Dictionary<string, V> property was already there;
DbContextGenerator had no map branch, so EF got no column type and no converter — the
property did not persist onto the jsonb column the TS-owned migration creates (ADR-0015).
It now emits an explicit jsonb column type plus a shared converter/comparer pair, on
entities, read-only projections and flattened value-object members alike. The comparer is
load-bearing, not decoration: EF snapshots a value-converted property by reference, so a
converter alone would leave an in-place entity.Labels["k"] = v undetected and the UPDATE
would never fire. Two details of the emitted shape: the property's NULLABILITY follows the
column — a @required map is a non-null dictionary with an empty-dictionary initializer,
any other map a nullable dictionary with no initializer, because the migration's column is
nullable by default and a non-nullable property over it makes EF Core 8 skip the shaper's
NULL check (one NULL cell — a row written by another port, or before the field existed —
would 500 every read arm), and NULL stays distinct from a present {}. And the shared
serializer options carry a JsonStringEnumConverter, so a field.enum member of the map's
value object persists as its member SYMBOL — the rule the owned-field.object jsonb column
already follows; System.Text.Json's default int ordinal is a value no sibling port writes
for the same declared field.
Scope. No runtime persistence layer reads or writes a map except Python's
ObjectManager, and no persistence- or api-contract-conformance corpus exercises
field.map on any port — the subtype remains loader- and codegen-gated only, and
field-types.md carries the full runtime picture. An
adopter who read "field.map now works" out of this entry would be over-reading it.
metamodelVersion does not move: no registered vocabulary changed, and
expected-registry.json is untouched.
metamodelVersion is unchanged at 1.0 — no registered vocabulary moved.