Skip to content

Commit c37dfdb

Browse files
dmealingclaude
andcommitted
docs(#246): address Fable whole-branch review — enum guidance + fixture counts
- field-types.md: drop the 'extend a concrete enum for an independent set' recommendation (steered users into a shape Kotlin's collapse mishandles); guide to a separate field.enum instead. - CHANGELOG: reword the closing note (the C# materialized-enum + enum-PK items are recorded in the design spec, not open trackers) and note the Kotlin isAbstract-leg follow-up. - CONFORMANCE.md + CLAUDE.md: metamodel conformance fixture count 255 -> 258. - design spec: record the pre-existing Kotlin enumTypeName isAbstract-leg divergence as an out-of-scope follow-up (surfaced by the Fable review). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HLoJkFSyoticveo5ehMUAr
1 parent 2d93d84 commit c37dfdb

5 files changed

Lines changed: 12 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@ output for any model that doesn't hit the specific cross-package/two-hop enum sh
4242
instead of silently emitting nothing (the exact silent no-emit #259 reported). Gated by a
4343
two-hop `KotlinCompilation` compile-gate + a depth-2 cross-port conformance fixture.
4444

45-
The C# materialized-enum cross-namespace sibling and the enum-primary-key-as-`String` issue remain
46-
tracked separately.
45+
The C# materialized-enum cross-namespace sibling and the enum-primary-key-as-`String` issue are
46+
documented as out-of-scope in the design spec
47+
(`docs/superpowers/specs/2026-07-31-shared-enum-cross-package-design.md`); a follow-up is the
48+
Kotlin `enumTypeName` collapse gaining the `isAbstract` leg the other ports already carry (so a
49+
root-level *concrete* enum extended with own `@values` gets a per-field enum on every port).
4750

4851
## [0.20.10] — 2026-08-02
4952

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ _Last refreshed 2026-08-02._
3030
- **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.
3131

3232
**Cross-port conformance corpora** (every port runs the shared corpus):
33-
- Metamodel: `fixtures/conformance/` (255 fixtures; 19 shared corpora in total — per-corpus counts + the corpus x port matrix live in `docs/CONFORMANCE.md`). TS / C# / Java / Python all green.
33+
- Metamodel: `fixtures/conformance/` (258 fixtures; 19 shared corpora in total — per-corpus counts + the corpus x port matrix live in `docs/CONFORMANCE.md`). TS / C# / Java / Python all green.
3434
- Render: `fixtures/render-conformance/`. TS / C# / Java / Kotlin / Python byte-identical.
3535
- 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".)
3636
- 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.

docs/CONFORMANCE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ regenerate with `ls -d fixtures/<corpus>/*/ | wc -l`.
2525

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

7070
## Fixture-to-doc mapping
7171

72-
### `fixtures/conformance/` — metamodel loader + canonical serializer (255)
72+
### `fixtures/conformance/` — metamodel loader + canonical serializer (258)
7373

7474
| Fixture prefix | Feature doc |
7575
|---|---|

docs/features/field-types.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,9 @@ Two rules the loader/codegen enforce:
9393
A shared enum is one type with one member set, so an own `@values` on the
9494
extending field would be silently dropped by the shared-enum collapse — it is a
9595
load error, `ERR_ENUM_EXTENDS_VALUES_CONFLICT`. Remove the own `@values` to
96-
inherit the shared set, or `extends` a **concrete** (non-shared) enum if you
97-
genuinely need an independent member set on top of a base shape.
96+
inherit the shared set. If you need a **different** member set, declare a
97+
separate `field.enum` with its own `@values` rather than extending the shared
98+
one — extending is for *reuse* of one member set, not for overriding it.
9899
- **Inherited members resolve through any number of `extends` hops.** A projection
99100
field that extends an entity field which itself extends a shared abstract enum
100101
gets the shared members transitively — the projection still materializes its own

docs/superpowers/specs/2026-07-31-shared-enum-cross-package-design.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ The loader intentionally treats an own `@values` as authoritative when present (
4646
- **Python latent same-premise** (`fr019_shared_enum.py` relative `from .enums import Name`) — correct today because Python emits entity files flat; only a risk if a nested per-package layout is ever added. No change now.
4747
- TypeScript is already correct (`enum-import.ts` computes a real relative module specifier); it is the reference design. No change.
4848
- Java (`codegen-spring`) is immune — no generated typed persistence class (OMDB is runtime); its DTO tier already uses an inline-FQN fallback.
49+
- **Kotlin `enumTypeName` collapse lacks the `isAbstract` leg** (`KotlinTypeMapper.kt`). After #259 the collapse keys on the *immediate* super having no declaring object (root-level), but — unlike the TS/C#/Python `resolveSharedEnumDecl` resolvers, which also require the super to be **abstract** — it does not check `isAbstract`. So a root-level *concrete* enum extended with own `@values` collapses onto the super's name on Kotlin (with a first-wins FQN dedupe that silently drops one member set under multiple extenders), where the other three ports emit an independent per-field enum. Pre-existing (the old `resolveSuperRoot` keying had the same hole); output-changing to fix (renames the collapsed enum for existing consumers), so it is a separate follow-up, not part of the #246/#259 PATCH. Surfaced by the Fable whole-branch review 2026-08-02.
4950

5051
## Design
5152

0 commit comments

Comments
 (0)