# MetaObjects Roadmap _Last refreshed 2026-08-24._ > **This file is the single source of truth for the roadmap.** GitHub Milestones + Issues + > the Project board mirror it. Keeping them in sync: `docs/ROADMAP-PROCESS.md`. ## FR registry Every feature request, its status, target release, and tracking issue. Shipped FRs are detailed under **Shipped**; planned FRs under **Planned** + the **Release plan**. βœ… shipped Β· πŸ”΅ active Β· πŸ“‹ planned. | FR | Title | Status | Release | Issue | |---|---|---|---|---| | FR-002 | Client/web package split | βœ… | β€” | β€” | | FR-003 | Java RDB persistence & projections | βœ… | β€” | β€” | | FR-004 | Cross-language prompt construction | βœ… (MCP remains) | β€” | [#18](https://github.com/metaobjectsdev/metaobjects/issues/18) | | FR-005 | Actionable loader errors (FR5 a–e) | βœ… | β€” | β€” | | FR-006 | `template.output` parser-on-receipt | βœ… | β€” | β€” | | FR-007 | Codegen conformance corpus | βœ… | β€” | β€” | | FR-008 | Universal REST API contract | βœ… | β€” | β€” | | FR-009 | Filter operators per port | βœ… | β€” | β€” | | FR-010 | Output-format prompt + tolerant extract | βœ… | β€” | β€” | | FR-011 | Extract hardening (enum coercion + nesting) | βœ… | β€” | β€” | | FR-012 | Nested-object prompt expansion | βœ… | β€” | β€” | | FR-013 | Field read-only | βœ… | β€” | β€” | | FR-014 | TPH discriminator | βœ… | β€” | β€” | | FR-015 | Source parameter-ref | βœ… | β€” | β€” | | FR-016 | `source.rdb` name/kind aliases | βœ… | β€” | β€” | | FR-017 | TPH polymorphic codegen | βœ… | β€” | β€” | | FR-018 | Many-to-many relationships | βœ… | β€” | β€” | | β€” | AI LLM-call trace persistence | πŸ”΅ active | β€” | β€” | | FR-019 | Shared / externally-provided enums | πŸ“‹ | 1.1 | [#5](https://github.com/metaobjectsdev/metaobjects/issues/5) | | FR-020 | `@inheritance` joined strategy | πŸ“‹ | 1.x | [#6](https://github.com/metaobjectsdev/metaobjects/issues/6) | | FR-021 | api metadata + contract projections | πŸ“‹ | 1.3 | [#7](https://github.com/metaobjectsdev/metaobjects/issues/7) | | FR-022 | Contract emitters (JSON Schema/OpenAPI/protobuf) | πŸ“‹ | 1.3 | [#8](https://github.com/metaobjectsdev/metaobjects/issues/8) | | FR-023 | Metadata packages (cross-project sharing) | πŸ“‹ | 1.x | [#9](https://github.com/metaobjectsdev/metaobjects/issues/9) | | FR-024 | Entity surfaces (projection/value + declared API) | 🟒 taxonomy + B3/B4a/B4b/B5/B6 validation parity + projection codegen **shipped 5 ports (1.0)**; declared-API surface (`api.operational`/`operation`/`binding.rest`) **deferred to 1.1** (foundation for FR-034) | 1.0 Β· 1.1 | [#10](https://github.com/metaobjectsdev/metaobjects/issues/10) | | FR-025 | Cross-port package-binding codegen config | πŸ“‹ | 1.1 | [#11](https://github.com/metaobjectsdev/metaobjects/issues/11) | | FR-026 | Forms completeness (edit forms + view parity) | 🟒 edit forms + view-kind control dispatch **shipped**; renderer parity / validator wiring / `layout.form` **deferred to 1.1** | 1.1 | [#12](https://github.com/metaobjectsdev/metaobjects/issues/12) | | FR-027 | DataGrid downloads (CSV/XLSX/PDF/TXT) | πŸ“‹ | 1.2 | [#13](https://github.com/metaobjectsdev/metaobjects/issues/13) | | FR-028 | Strict serializer parity + `meta export` CLI parity | πŸ“‹ | 1.1 | [#14](https://github.com/metaobjectsdev/metaobjects/issues/14) | | FR-029 | Metadata API + runtime-driven UI | πŸ“‹ | 1.4 | [#15](https://github.com/metaobjectsdev/metaobjects/issues/15) | | FR-030 | Runtime serializers (SPI/XML/binary/round-trip) | πŸ“‹ | 1.1 | [#16](https://github.com/metaobjectsdev/metaobjects/issues/16) | | FR-031 | MetaData read-path caching + perf | βœ… shipped | 1.0 | [#17](https://github.com/metaobjectsdev/metaobjects/issues/17) | | FR-032 | Canonical FQN refs (YAML-only relative paths) | βœ… shipped (5 ports) | 1.0 | [#21](https://github.com/metaobjectsdev/metaobjects/issues/21) | | FR-033 | Provider definitions as declarative data + metamodel docs for LLMs | πŸ“‹ designed | 1.x | [#23](https://github.com/metaobjectsdev/metaobjects/issues/23) | | FR-034 | Ecosystem tier β€” connected systems (`system`/`container`/`surface`/`environment`) | πŸ“‹ designed (**draft, deferred post-1.0**) | 1.1 | β€” | | FR-035 | Present-key PATCH tristate (mutation surface) | 🟒 shipped 5 ports (absentβ†’untouched / present-nullβ†’clears / null-on-`@required`β†’400); coordinated **breaking** release held for FR-036 | 1.0 | β€” | | FR-036 | Constraint-validation enforcement + semantic pins | 🟒 shipped 5 ports in the coordinated `0.16.0`/`7.8.0` breaking release (required-string = non-emptyΒ·accept-whitespace Β· `@Pattern` = full-match Β· strictest-wins length; HTTP-tier enforcement all 5 ports; TPH tristate) | 1.0 | β€” | | FR-037 | Projection expressiveness (`origin.rank`) + field write-access modes (`@mutability`) | 🟒 R1 + R2 shipped `0.24.0`; #335 shipped `0.24.1`; R3/R4/R5 open. **R1/R2 retire registered vocabulary (`@readOnly`), so the breaking half must ride a coordinated pre-1.0 breaking MINOR** β€” post-1.0 a metamodel-vocabulary break moves `metamodelVersion`'s major (ADR-0035 Β§1 + Am. 2) and should still ride the pre-1.0 slot, where the caret rule is a real gate. `origin.rank` + the rest are additive β†’ 1.1. **R1 and R2 SHIPPED in the coordinated `0.24.0` breaking MINOR** (`@readOnly` β†’ `@mutability`; `origin.collection` β†’ reserved-not-registered). **R2's designated re-entry shape β€” `@agg: collect` with `@of` OPTIONAL, the whole-object rollup, [#335](https://github.com/metaobjectsdev/metaobjects/issues/335) β€” SHIPPED in `0.24.1`** and was additive as predicted: `@of` was already `required: false` in the registry, the constraint lived in validation, and relaxing it made previously-invalid metadata valid. **R3/R4/R5 remain**, all additive β†’ 1.1. Design: `docs/superpowers/specs/2026-08-10-fr-037-projection-expressiveness-and-write-once-design.md` | 1.0 Β· 1.1 | [#335](https://github.com/metaobjectsdev/metaobjects/issues/335) | | FR-038 | Requirement-derived test stubs (inverts `@verifiedBy`) | 🟒 the vocabulary retirement shipped `0.24.0`; the stub generator remains. Generate the test from the requirement so the link is structural, not a name the author picks β€” an audit of one 19-name ledger found 4 names that did not verify their claim. **The `@verifiedBy` retirement was breaking and rode the SAME pre-1.0 MINOR as FR-037's R1/R2, not a second one β€” SHIPPED in `0.24.0`** (`@verifiedBy`, `@supersededBy`, and `@status: abandoned|superseded` all deregistered). The stub generator is additive and remains β†’ 1.1. Design: `docs/superpowers/specs/2026-08-15-fr-038-requirement-derived-test-stubs-design.md` | 1.0 Β· 1.1 | β€” | _(FR-001 was the original metamodel foundation β€” pre-dates the FR-numbered tracking.)_ _(FR-032 was developed under the working number "FR-026" β€” see commit history; renumbered to avoid the FR-026=Forms collision. Design: `docs/superpowers/specs/2026-06-13-fr-032-canonical-fqn-refs-design.md`, ADR-0032.)_ ## Shipped ### Capability requirements (2026-08-12, unreleased) - **`requirement.functional` / `requirement.architectural`** β€” capabilities as registered metamodel vocabulary in all five ports. Opt-in by declaration: a model with no `requirement.*` nodes gets no diagnostics and no generated output. Loader owns the `@status` enum and required attrs; `meta verify` (TS CLI) owns `@implementedBy` / `@verifiedBy` resolution, whose severity depends on `@status` β€” a dangling reference is an error on `live` and *expected* on `abandoned`. Justified by resurrection, not duplication: model-only runs proposed reviving a deliberately-retired capability 24 times out of 24. Docs: [`docs/features/requirements.md`](../docs/features/requirements.md), full reference [`spec/capability-ledger.md`](capability-ledger.md). Related: [ADR-0050](decisions/ADR-0050-own-vs-projected-attributes.md) (own vs projected attributes) and [ADR-0051](decisions/ADR-0051-extension-is-registration.md) (extension is registration), both surfaced by building it. ### Foundation - **H1 β€” Polyglot monorepo migration** (2026-05-14) TS code consolidated under `server/typescript/`; package names normalized to `@metaobjectsdev/*`; CLI binary renamed to `meta`; config file `metaobjects.config.ts`; tool-state dir `.metaobjects/`. - **H2 β€” Shared conformance fixtures** (2026-05-15) Fixtures extracted into `fixtures/conformance/`; per-port conformance runners; canonical serializer (fused-key form); format documented in `spec/conformance-tests.md`. - **H7 β€” npm publish: first public release** (2026-05-23) All publish-candidate `@metaobjectsdev/*` packages published to npm at `0.5.0` then `0.6.0` (`latest`). The JS/TS workspace root was hoisted to the repo root so `workspace:*` resolves uniformly across the server + client package trees at publish time. - **H8 β€” First TS consumer on published packages** (2026-05-23) A real TS consumer migrated off `link:` filesystem deps onto the published packages and builds clean end-to-end β€” validating the published dist, `.d.ts` types, and runtime imports through a real pnpm install. ### Per-port ports + codegen layers - **TypeScript** β€” `@metaobjectsdev/metadata` + `codegen-ts` (Vite-style plugins) + `runtime-ts` + `migrate-ts` + the universal web client packages (`runtime-web`, `react`, `tanstack`). The reference port for everything cross-language. - **C# full-stack target** β€” `MetaObjects` (loader + canonical serializer + conformance) + `MetaObjects.Render` (Mustache + payload-VO codegen + `verify`) + `MetaObjects.Codegen` (EF Core entities + `AppDbContext`/owned-types via `OwnsOne` + CRUD minimal-API routes). Schema migrations are TS-owned (ADR-0015) β€” the C# migrate engine and its `--from-db` CLI surface were removed; the `dotnet meta` CLI is `gen`/`verify` only. - **Java port** β€” `metadata` + `omdb` + `om` + `dynamic` + `core-spring` + `metadata-ktx` (Kotlin facade) + `codegen-spring` (Spring REST controllers + DTOs + repos + filter allowlists) + `codegen-mustache` + `codegen-plantuml` + `codegen-base` + `render` + `maven-plugin` (`metaobjects:generate` / `:verify` / `:editor`; schema migrations are owned by the TS toolchain β€” the Java diff-and-converge engine and the live-DB-drift mode of `verify` were removed). - **Kotlin** β€” `codegen-kotlin` (KotlinPoet on JVM): entity + Exposed table + Spring controller + Spring config + payload + relations + filter allowlist + validator + stored-proc + output-parser generators; `integration-tests-kotlin` runs the persistence-conformance corpus through Exposed against Testcontainers Postgres. - **Python port** β€” `metaobjects` (metadata loader + canonical serializer + conformance) + `ObjectManager` runtime + render (Mustache) + codegen (Pydantic + FastAPI, output parsers). The `migrate` module was removed when schema consolidated onto the TS toolchain (ADR-0015). ### Cross-port conformance corpora (every port runs the shared corpus) - **Metamodel conformance** β€” `fixtures/conformance/` (270 fixtures + CAPABILITIES + ERROR-CODES manifests). TS / C# / Java / Python all green; Kotlin inherits via `metadata-ktx`. Per-corpus counts: `docs/CONFORMANCE.md`. - **Render conformance** β€” `fixtures/render-conformance/`. TS / C# / Java / Kotlin / Python byte-identical. - **Persistence conformance** β€” `fixtures/persistence-conformance/` (24 query + 6 migration scenarios). Every port runs the **query** scenarios against Testcontainers Postgres, each provisioning its test DB from the committed, TS-produced `canonical/schema.postgres.sql` (Derby was dropped for the cross-port query corpus, ADR-0015). The **migration** scenarios are TS-only, since TS owns schema. - **API-contract conformance** β€” `fixtures/api-contract-conformance/` (26 core + tph/m2m/jsonb/write-through sub-corpora). All five ports run it in BOTH lanes β€” a hand-rolled reference server and the port's own GENERATED API artifact booted over HTTP. - **Extract conformance** β€” `fixtures/extract-conformance/` (33 dirty-input cases, incl. `default-scalar-int` + `array-of-enum`). TS / C# / Java / Python all green; Kotlin reuses the shared JVM engine. - **Output-prompt conformance** β€” `fixtures/output-prompt-conformance/` (12 cases Γ— 3 `@promptStyle` styles). Pins the FR-010 output-format prompt fragment byte-identically across all 5 ports (zero-drift, no ledger) + a renderβ†’extract round-trip skew guard. Fragments expand **nested objects + arrays-of-objects + scalar arrays** (FR-012). TS pilot; C# / Java / Python reproduce the bytes natively; Kotlin drives the shared JVM engine. Backlog item R13. (Also added three `fixtures/conformance/error-enum-*` negative fixtures pinning FR-011 enum-coercion attr-validation β€” `@coerceDefault`/`@default` membership + `@normalize` mode β€” across the 4 loader ports.) - **Object-model conformance** β€” `fixtures/object-model-conformance/` (7 scenarios). Pins the cross-port runtime object model: `newInstance` factory (map-backed `ValueObject` default, or a self-registered code-generated type), instanceβ†’MetaObject back-reference (`MetaObjectAware`), nested-object + array-of-objects field IO, overflow, and the codegen'd-or-not invariant. All 5 ports 7/7 (Java reference + reconcile; Kotlin over the JVM model; TS/Python/C# native, reflection-free). ADR-0017. (Phase A; metadata-driven extract + generalized `@default` build on it next.) - **YAML / verify** corpora β€” green across the ports that ship those layers. ### Key cross-language features - **Source v2 paradigm** β€” `source.rdb` + `@kind: table|view|materializedView|storedProc|tableFunction`; multi-source via `@role`. ADR-0007. - **FR-003 β€” Java RDB persistence & projections** (Plans 1/2/3/4a + Plan 4): port of `dynamic`/`om`/`omdb` onto current core; build-time FQN-keyed binding registry + typed jsonb value-objects + Spring-tx connection; `source.*`+`origin.*` metamodel registered in Java; OMDB engine-debt remediation (atomic mapping cache, JDBC codec registry per ADR-0002, `inTransaction` template). The diff-and-converge schema-migration engine that originally shipped under FR-003 (`SchemaMigrationEngine` + introspector + emitter + the `meta:migrate` goal) was **removed** when schema migrations consolidated onto the TS toolchain (`@metaobjectsdev/cli migrate`); the Java port retains runtime persistence only β€” per ADR-0015 Decision 2 the dev/test runtime auto-create path (`MetaClassDBValidatorService` + the drivers' DDL) was removed too, so OMDB is pure data-access. - **FR5 family β€” actionable loader errors** (a/b/c/d/e + WARN envelope-shape). ADR-0009. - FR5a: source-on-node + envelope-shaped errors (`format` ∈ `json|yaml|merged|resolved|database|code`) - FR5b: YAML source positions on yaml-input envelopes - FR5c: multi-file merge attribution (`MergedSource` + `contributors[]` + `ERR_MERGE_CONFLICT` + `WARN_DUPLICATE_DECLARATION`) - FR5d: reference-resolution errors (`format: "resolved"` + `referrer` + `target`) - FR5e: database-source envelope schema reserved + per-port shape tests + design questions resolved. Real DB-source loader is a future FR. - WARN envelope-shape assertion finalized across all 4 ports. - **FR-006 β€” `template.output` parser-on-receipt codegen.** ADR-0010. Shipped in all 5 ports (TS / C# / Java / Python / Kotlin); `meta verify` extended to cover output drift. - **FR-010 β€” output-format prompt fragment + tolerant `extract` parser.** Shipped in all 5 ports. One `template.output` drives three artifacts: a comment-free output-format prompt fragment (3 styles Γ— json/xml via `@promptStyle`), a tolerant `extractLenient()` (8-stage, never-throws, returns an all-nullable mirror of the payload) that complements FR-006's strict parser, and the `@example`/`@instruction`/`@enumAlias`/`@enumDoc` field-teaching attrs. Pinned by the shared `fixtures/extract-conformance/` corpus; tolerance is at classification + canonical value (not byte-identity). (Originally named `recover`; cross-port-renamed to `extract` pre-GA β€” #87.) Designed in `docs/superpowers/specs/2026-05-29-fr-010-output-format-prompt-and-tolerant-parsing-design.md`. - **FR-011 β€” extract hardening (enum coercion + nested-object extraction).** Shipped in all 5 ports (TS pilot β†’ C# / Java / Kotlin / Python). Hardens FR-010's `extractLenient()` in place: an enum coercion pipeline (exact β†’ `@normalize` `none|collapse|strip` β†’ `@enumAlias` β†’ `@coerceDefault` β†’ MALFORMED), `@default` fills an absent enum (emitting the now-live `DEFAULTED` state, which satisfies `@required`), and uniform nested/embedded-object recovery via dotted child paths (`meta.score`, `items[i].label`) across JSON + XML (the renamed `extract` engine). Normalization is ASCII-only (manual case-fold, byte-identical cross-port); `@normalize`/`@coerceDefault`/`@default` are member-validated on `field.enum` at load time, with an object-level `@normalize` default. Fuzzy matching deferred (reserved pipeline slot). Corpus expanded 10 β†’ 20 cases. Designed in `docs/superpowers/specs/2026-05-30-fr-011-recover-hardening-design.md`. - **FR-012 β€” nested-object prompt expansion.** Shipped in all 5 ports (TS pilot β†’ Java / C# / Python; Kotlin inherits the shared JVM engine). The FR-010 output-format prompt renderer now recurses into nested `OBJECT` fields, arrays-of-objects, and scalar arrays (instead of a flat `{name}` placeholder) across all 3 styles (guide/inline/exampleOnly) Γ— json/xml, with an identity-based cycle guard + `MAX_NEST_DEPTH`. Closes the FR-010 request/response asymmetry (`extract` already handled nesting). Output-prompt-conformance corpus 10 β†’ 12 cases (added `json-deep-nest` + `json-array-of-objects`; `json-nested`/`xml-nested` now round-trip). Designed in `docs/superpowers/specs/2026-05-30-fr-012-nested-object-prompt-expansion-design.md`. - **Cross-port runtime object model (Phase A).** `MetaObject.newInstance()` factory β†’ a map-backed `ValueObject` default or a self-registered code-generated type, with an instanceβ†’MetaObject back-reference (`MetaObjectAware`), a self-registering FQNβ†’constructor `ObjectClassRegistry`, and a field get/set-by-name SPI β€” all reflection-free (the ValueObject path resolves no native class; typed binding is generated self-registration, never `Class.forName`/`Type.GetType`/`reflect-metadata`/`importlib`). Brought Java's runtime object model to TS/Python/C# (Kotlin reuses the JVM model). Pinned by `object-model-conformance` (7 scenarios, all 5 ports). ADR-0017. - **Metadata-driven extract (Phase B).** Runtime `extract(MetaObject, text) β†’ ExtractionResult` assembles a typed object graph (nested objects + arrays-of-objects) via the Phase A model β€” never-throws, opt-in `orThrow()`, cycle/depth guard. Adds **array-of-enum** per-element coercion and generalizes **`@default`** to all field types (engine fill β†’ DEFAULTED + per-type load validation in all 4 loader ports; Java legacy `setDefaultValues`/`getDefaultValue` unified). Each port's **codegen `extractLenient()` delegates** to the runtime (codegen-wrapping-runtime), closing the nested-object/array codegen gap ("Plan 2.1") while keeping a self-contained scalar/enum baked `extractLenient()` for back-compat. All 5 ports; extract-conformance byte-green. (Originally named `recover`; cross-port-renamed to `extract` pre-GA β€” `extract` is the industry-standard term and avoids colliding with the retry-loop sense of "recovery" β€” #87.) Designed in `docs/superpowers/specs/2026-05-30-recover-codegen-nested-design.md`. - **Prompt-construction pillar β€” per-port building blocks complete.** Render (Mustache) + payload-VO codegen + `verify` (FR-004), the output parser (FR-006), and the output-format prompt + extract (FR-010) all ship in all 5 ports. The library-side primitives of the fourth pillar are delivered; what remains is MCP exposure (see Planned). - **Java flavored object + extractor codegen** (shipped 2026-05-31, **Java only**). Direct (code-as-code, no templates) generation of objects that carry their `MetaObject` and extract a typed graph (nested + arrays-of-objects), in two selectable flavors via a `flavor` generator option: `pojoAware` (`extends PojoObject`) and `valueObject` (`extends ValueObject`, perf-tuned cached per-field value-holder accessors), plus a self-registering `ObjectClassBindingProvider` and a `Extractor` wrapping the Phase-B runtime extract. Reuses the legacy direct object-codegen framework byte-unchanged (extends via hooks; downstream-extensible `protected` seams + `createWriter` factory). Compile-and-run gated per flavor + Extractor + a downstream-customization proof. Other ports frozen until Java is correct; designed in `docs/superpowers/specs/2026-05-31-java-flavored-object-codegen-design.md`. - **Cross-port `Extractor` codegen** (shipped 2026-05-31, TS / Python / C# / Kotlin β€” capability parity with Java). A generated `Extractor` per `template.output` adds a **strict `extract` tier** over the existing lenient tier: `extract` returns the port's **idiomatic strict typed payload** (TS inferred type, Python Pydantic model, C# `record`, Kotlin `data class`) and throws on lost-required; `extractLenient` (never-throws, all-nullable mirror) is re-exposed. It reuses each port's existing nested-capable runtime-delegating lenient extract and a generated recursive mirrorβ†’strict mapper (nested objects + arrays-of-objects, one-shot construct), with **no registry / binding / factory** (codegen knows the type graph statically) and **no new flavored object-class generation** (constructs each port's existing idiomatic types). Compile-and-run gated per port (incl. non-string scalar arrays). Surfaced + fixed a latent cross-port scalar-array extract bug (JVM `DataConverter` array conversions were dead/throwing; the JVM/C# lenient extract emitted scalar arrays as non-arrays). **Idiom divergence (intentional):** TS/Python honor `@required` optionality in the strict payload; C#/Kotlin payloads are all-`required` (no optional-absent modeling) β€” extract works into each port's existing payload shape. The cross-port `recover β†’ extract` rename (#87) then swept all 5 ports together. Designed in `docs/superpowers/specs/2026-05-31-cross-port-extractor-codegen-design.md`. - **Typed enums in payload VOs** (shipped 2026-05-31, all 5 ports). The strict `extract` payload types a `field.enum` (and enum arrays) with a **value-constrained, idiomatic** type instead of the prior inconsistent `unknown`/`str`/`object`/`String`: TS string-literal union, Python `Literal[...]`, C# nested `enum`, Kotlin `enum class`, Java generated `enum`. The extract mapper coerces the engine-validated mirror string to the typed value β€” identity for TS (cast `as `, gated by `tsc --strict`) / Python (`Literal`, Pydantic runtime-validated); `Enum.Parse`/`valueOf` for C#/Java/Kotlin (the call IS the bridge; gated by Roslyn/javac/kotlin-compile-testing). Reuses each port's established entity enum-type emitter + the shared ``/`` naming + dedup (two fields extending one abstract `field.enum` β†’ one generated type, proven per port). The lenient mirror stays raw string (Java has no separate mirror β€” its single typed payload record is shared by parse/extract/extractLenient with null-safe `valueOf`, a documented idiomatic divergence; never-throws preserved because the engine yields a valid member or null). Engine + `fixtures/extract-conformance/` unchanged (codegen-typing only). Surfaced + fixed the enum-array "check-enum-before-isArray" lenient-mirror collapse bug in TS/Python/C#. Follow-up: super-resolution naming diverges only for 2+ level abstract-enum chains (immediate-super TS/C#/Python vs root-super JVM β€” pre-existing in the entity emitters, latent). Designed in `docs/superpowers/specs/2026-05-31-typed-enums-payload-design.md`. - **`template.output` render-helper codegen** (phase 1 shipped 2026-06-01, TS + Java; C#/Python/Kotlin = phase 2). `template.output` gains `@kind` (`document` default | `email`); a new per-port generator emits a typed render helper wrapping the existing shared `render()` engine β€” `render(payload, provider): string` for a document (renders `@textRef` in `@format`), `render(payload, provider): EmailDocument {subject, htmlBody, textBody?}` for an email (renders `@subjectRef`/`@htmlBodyRef`/`@textBodyRef`). The **build-time mustache↔VO drift gate** is the headline: the generator resolves each referenced in-repo `.mustache` and runs the existing `verify()` at codegen, FAILING the build when a `{{field}}` isn't on the payload VO (runtime check stays too). Replaces hand-written `render({ref, payload, format, provider, verify})` boilerplate (the email-rendering hack). Reuses `render()`/`verify()`/the filesystem provider/the payload field-tree walk + `EmailDocument` β€” engine + render-conformance UNCHANGED (codegen + small metamodel only). Shared `fixtures/template-output-render-conformance/` (document + email + drift) pins identical TS/Java output. (The recollected `template.document` metatype never existed β€” this is `template.output` + codegen.) Designed in `docs/superpowers/specs/2026-06-01-template-output-render-helper-design.md`. - **Cross-port `templateGenerator()`** (shipped 2026-05-28). TS reference + Python / C# / Java factories; 3/3 conformance fixtures byte-equivalent. Java ships lightweight types under `com.metaobjects.render.templategen` (its legacy `Generator` interface was incompatible). See `design-docs/2026-05-28-cross-port-template-generator.md`. - **SP-1b β€” JVM declarative Mustache template generator** (shipped 2026-06-28, Java + Kotlin). Wires the cross-port template generator into the JVM as a Maven-wirable `TemplateScopeGenerator` (`com.metaobjects.generator.template`, in `codegen-base`) over the byte-equivalent render engine β€” closing the prior "Maven-plugin integration is a follow-up" gap. Implements the three named scope walks (`perEntity`/`perPackage`/`perModel`), the neutral structural data dict, and the output-pattern grammar (`{name}`/`{Name}`/`{package}`), all gated byte-identical for both Java and Kotlin against the shared `fixtures/template-codegen-conformance/` corpus (the same corpus the TS port passes). **Kotlin gains a template generator for the first time** (reuses the shared JVM engine β€” no KotlinPoet). Part of the SP-1 codegen authoring-parity program; designed in `docs/superpowers/specs/2026-06-28-mustache-codegen-parity-design.md`. - **SP-1d β€” C# declarative Mustache template generator** (shipped 2026-06-28). The **last of the four SP-1 ports** β€” all changes under `server/csharp/`. Brings the declarative template generator (scope `perEntity`/`perPackage`/`perModel` + `outputPattern`, no walk code) to C# in `MetaObjects.Codegen.TemplateCodegen` (`OutputPattern.Expand`, the `TemplateData` neutral data dict, `ScopeWalk.ForScope`, `TemplateSpec.Parse`/`ToGenerators`), and exposes it via a `--template-spec ` (+ `--template-root `) JSON surface on `dotnet meta gen` β€” turning the previously no-op `templateGenerator()` registry primitive into a real consumer-usable generator. A `target` field is rejected (C# has no output-target concept, same decision as Python), and a bad template ref / wrong `--template-root` yields a clean error not a stack trace. Gated byte-identical against the SAME `fixtures/template-codegen-conformance/` corpus the TS + JVM + Python ports pass β€” all four ports now produce identical output. Part of the SP-1 codegen authoring-parity program; designed in `docs/superpowers/specs/2026-06-28-mustache-codegen-parity-design.md`. - **OMDB Spring Boot 3 starter** (shipped 2026-05-30). Autoconfiguration wires a `DataSource` β†’ `ObjectManagerDB` with Spring-tx; closes the OMDB-modernization open question (jOOQ migration ruled out as a non-goal). - **FR-008/FR-009 β€” Cross-port REST API contract + 10 filter operators.** Shipped in all 5 ports. - **FR-018 β€” Many-to-many relationships.** Shipped in all 5 ports. Slim vocabulary (`@through`/`@sourceRefField`/`@symmetric`, with `@joinEntity`/`@joinFields` removed; FK fields derived from the junction's `identity.reference` SSOT) + a generic runtime M:N resolver (TS / Java / Kotlin / Python / C#) + codegen (entity navigation + idiomatic ORM wiring [Drizzle m2m / EF Core `UsingEntity` / Spring repo+JPA / Exposed / Pydantic+route as the SQLAlchemy-secondary equivalent]) + REST traversal (`GET //{id}/`) + Tier-2 docs. Gated by the shared api-contract m2m corpus in both lanes (reference-server + generated-artifact-over-HTTP) + persistence-conformance, across hetero / directed-self-join / symmetric modes. Remaining: a typed TanStack M:N client hook (client-ergonomics follow-up). Designed in `docs/superpowers/specs/2026-06-02-fr-018-many-to-many-relationship-design.md`. - **SP-G β€” registry-conformance gate.** Shipped in all 5 ports (TS / C# / Java / Kotlin / Python, live + green). A structural enforcer of the "metamodel subtype vocabularies must be identical across languages" contract: each port emits its registry as a canonical, fully-sorted, byte-stable JSON manifest matched to the single committed `fixtures/registry-conformance/expected-registry.json`. Reconciled the Java/Kotlin registries at source (the JVM runners compose from the defined metamodel provider set so codegen/om SPI doesn't pollute the measured vocabulary). Plus an untested-vocabulary coverage report (`coverage-report.json`) surfacing every registered `(type, subType)`/attr no fixture exercises. See `fixtures/registry-conformance/README.md`. - **SP-H β€” field-subtype end-to-end hardening + write-path conformance.** Shipped in all 5 ports. Every concrete `field.*` subtype now write+read round-trips through each port's runtime/ORM, gated cross-port: the persistence corpus gained an `op: roundtrip` scenario type (INSERT via the port's write codec, NOT raw SQL β†’ read-back β†’ assert wire-normalized value) whose `AllTypes` entity carries one field of every persistable subtype against Testcontainers PG β€” the structural complement to SP-G (which gated vocabulary). Cut `field.byte`/`field.short`/`field.class` as non-functional registration-only stubs; reconciled uuid/currency filter-op bands cross-port + a loader guard for `@filterable`-without-op-band; Java write-codecs (timestamp/currency/enum/native-uuid/time) + Spring `field.time` arm; TS decimal string-exact dataType; Kotlin runs the shared metamodel corpus + a mapper-`else` negative test; bare `field.object` rejected at load (ADR-0013). The write-gate surfaced + drove latent breakage "all corpora green" had masked (TS enum-CHECK, Java timestamp/native-uuid/time/jsonb writes, Kotlin LocalTime-write, C# read-back-caching, the filter-op divergences). Designed in `docs/superpowers/specs/2026-06-03-sp-h-field-subtype-hardening-design.md`. - **Per-target output directories (TS codegen).** Each generator routes to a named output target (`{ outDir, importBase?, outputLayout?, dbImport? }`). - **0.6.x β†’ 0.7.0 consumer-friction batch.** Stock `promptRender()` generator; `db`-parameter generated repo helpers (ADR-0008); Cloudflare Workers deploy recipe; CHANGELOG.md backfill + camelCase ↔ snake_case docs. (Shipped; long since superseded β€” the current published line is npm / PyPI / NuGet `0.20.15` and Maven Central `7.20.15`.) ## Active - **1.0 promotion.** The published line is npm `0.21.0` Β· PyPI `0.21.0` Β· NuGet `0.21.0` Β· Maven Central `7.21.0`, all on the metamodel-1.0 vocabulary. Since the `0.20.11` re-baseline the four registries **share one `minor.patch` and stay locked in lockstep** β€” only the major differs (Maven keeps its historical `7`), and every coordinated cut advances all four to the same patch. The promotion itself therefore stays on the decoupled-major scheme (npm/PyPI/NuGet -> `1.0.0`, Java/Kotlin -> `8.0.0`, one shared "Metamodel 1.0"); the lockstep policy governs the `minor.patch` beneath it, not the major. GA mechanics (a coordinated all-four-registries re-cut + an external-install smoke) are **satisfied** as of `0.20.14`, so what remains is the renumbering β€” deliberately a later, feedback-informed move rather than a 1.0-by-inertia one. Procedure + per-registry gotchas: `docs/RELEASING.md`. - **AI LLM-call trace persistence β€” cross-port rollout.** A metaobjects-native typed-trace store: each LLM call is persisted in the adopter's own DB with request/response as **typed value objects** (the same payload projection the prompt pillar declares + drift-checks), the one thing blob-only observability tools (Langfuse/Helicone/OTel) structurally can't do. **Scope locked by [ADR-0024](decisions/ADR-0024-ai-trace-scope-and-llm-caller-boundary.md):** MetaObjects owns the typed trace + recorder seam + the vendor-NEUTRAL call glue; the provider call + pricing are **bring-your-own** (plug the stack's LLM library β€” Vercel AI SDK / LiteLLM / Spring AI β€” behind the one-method `LlmClient`). **Shipped in TypeScript** (the reference port): shipped `library/ai/llm-call.yaml` (`LlmCallBase`) + opt-in `libraries` loader; the recorder (`recordLlmCall`, never-throws, redaction seam) + `buildLlmCallRow` whose key set is gated == `LlmCallBase`'s fields; `deriveTraceFields` (typed `voRequest`/`voResponse` jsonb columns derived from a nested `template.prompt`'s `@payloadRef`/`@responseRef`); generated `record`/`call` (extract + persist base + typed in one row); `@metaobjectsdev/ai-runtime` (`LlmClient` seam, `runLlmCall`, `callLlm`, `CompositeRecorder` + optional `LangfuseRecorder`/`OtelRecorder` exporters); shared-table STI (`@discriminator`/`@discriminatorValue`, reuses FR-017 TPH). **Cross-port (P3) in progress, JVM first:** the Java metamodel slice shipped (`@responseRef` on `template.prompt` + `template.*` admitted as `object.entity` children + a `TS_PILOT_VOCAB` registry-manifest exclusion; `ai-trace-prompt-nested` + `ai-trace-sti` un-ledgered in Java). Remaining: the Java recording half (OMDB recorder + the `deriveTraceFields` loader pre-pass + `record` Spring codegen + a persistence round-trip), then Python β†’ C# β†’ Kotlin, then the **atomic carve-out close** (register `@responseRef` in every port + remove `TsPilotVocab` from all ports + add it to `expected-registry.json`). Phased plan: `docs/superpowers/plans/2026-06-06-ai-trace-descope-and-cross-port.md`. - **Website self-updating codegen β€” the site publishes generated output, gated.** Every code block on `metaobjects.dev` was hand-transcribed HTML and nothing checked any of it, while the page claimed the blocks were "real `meta gen` output, conformance-gated". Three jobs, one transport: **B** example snippets (the one that had actually been wrong), **A** the version payload (31 refs hand-edited every release), **C** the `llms` mirrors (a manual copy). Design: [`docs/superpowers/specs/2026-08-29-website-self-updating-codegen-design.md`](../docs/superpowers/specs/2026-08-29-website-self-updating-codegen-design.md); 18-task plan: [`docs/superpowers/plans/2026-08-29-website-self-updating-codegen.md`](../docs/superpowers/plans/2026-08-29-website-self-updating-codegen.md). **Tasks 1–11 SHIPPED** (2026-08-29/30): a showcase corpus generated by all five ports plus SQL, marker extraction, the committed-excerpt subsequence gate, registry-driven metadata + code highlighters, live CLI transcript capture, the payload builder with four build gates (subsequence, drift-fixture-still-fails, requirements-resolve, no-home-path), `gate_site_payload` in the `gates` lane plus a release-preflight check, and the injector + local preview. Nothing renders the payload yet β€” **task 12** gives the site's pages their `data-snippet` placeholders and **task 13** wires deploy-time injection; 14–15 are jobs A and C; 16–18 restructure `/reference`. **The cross-repo gating question is settled** (task 13, verified 2026-08-30): the site's `deploy.yml` already clones a public repo at deploy time with no secrets, so the pattern works, and the check is deliberately ASYMMETRIC β€” a placeholder with no payload entry hard-fails the deploy (it would ship a visibly empty block), while a payload entry no page references only warns there, because the bidirectional check at deploy would fail every unrelated site edit from the moment a placeholder lands until the next release. The bidirectional bijection belongs in this repo's **release preflight**, which may reach the network; `ci-local.sh`'s gates lane stays offline-safe, which is why the cross-repo test is deliberately filed outside `scripts/site/` (`gate_site_payload` globs that directory). Recorded here because eleven tasks shipped before the roadmap named the program at all. ## Planned - **#195 follow-ups β€” projection read-model origins.** The four capabilities (`origin.aggregate @agg:any|all|collect`, `origin.computed @expr`, `origin.first`) shipped (validate + type in all 5 ports; TS `meta migrate` view synthesis; see `CHANGELOG.md` [Unreleased] + `docs/superpowers/specs/2026-07-15-issue-195-projection-readmodel-origins-design.md`). Remaining as fast-follows: (a) the **"not-migrate-managed" escape valve** β€” now tracked as **#208** in the projection/view-materialization epic below (an `@sqlView`-style attribute carrying hand-written view DDL PLUS a distinct "managed elsewhere" marker, generalizing to **any** DB object, so a genuinely-irreducible view no longer aborts the whole `meta migrate` run); (b) a **`collect` native-array persistence roundtrip** gate (Postgres `text[]` vs SQLite `json_group_array` β†’ same wire) in `fixtures/persistence-conformance/`. The TS-side real-engine value gate now EXISTS (`integration-tests/test/view-lifecycle-{pg,sqlite}.test.ts` value-probe all four #195 origin kinds β€” any/all/collect/computed/first β€” incl. the empty-set pins `any=false`/`all=true`/`collect=[]`/`first=null` on real PG *and* real SQLite). The remaining work is the CROSS-PORT persistence-conformance scenario (a projection with a `collect` array column read by all 5 ports' runtimes), which is blocked on per-port native-array read support: **TS + Python already read a `text[]` β†’ JSON array** (native driver decode + a list branch in each normalizer); **C#** needs an array/`IEnumerable` case in `MetaObjects.Conformance` `Normalization.NormalizeValue` (emit a `JsonArray`) + a generated/mapped `ProgramInsights` EF entity; **Java** needs an OMDB JDBC array-read codec (`java.sql.Array` β†’ `List`) for `field.string @isArray` (its `Normalization` already has a `List` branch, but `JdbcCodecs` never produces a `List`); **Kotlin** needs a `ProgramInsightsView` Exposed table with a native-array column type registered in `QueryScenarioRunner.tableFor` (+ its `DEFERRED_SCENARIOS` skip valve in the interim). Note the ports pure-glob-discover `queries/*.yaml` (Java/Python/C# have no skip mechanism), so the scenario cannot land until C#/Java array reads exist. `normalization.md` will gain the "SQL array β†’ JSON array (empty `{}`β†’`[]`, NULLβ†’`null`)" contract when it lands. (c) **#204 β€” SHIPPED** (`codegen-ts` `projection-decl` now carries a projection field's `isArray`/`storage:jsonb` through `extends` into both the Drizzle view column and the Zod read schema, so `collect β†’ T[]` types correctly; TS-only, pending the next npm release). `origin.computed`'s expression grammar is designed so **#159**'s arithmetic / `case` / `@via`-joined refs slot in as additive node kinds. - **Projection / view-materialization epic (#206–212).** Follow-ons from #195 + adopter feedback on migrating legacy SQL-view-heavy schemas. Grouped by effort Γ— 1.0-timing; **Group A is TS-`meta migrate`-owned (schema is TS-only per ADR-0015 β†’ single-port, no 5-port fan-out).** - **Group A β€” concrete, 1.0-shippable (TS migrate/codegen):** - **#206** β€” Docs on-ramp: surface **entity read-views** (FR-024 Β§7) as the first answer for legacy `SELECT A.*, extras` views + an entity-vs-projection decision table. Docs-only, no metamodel change. Shipped. **#213 β€” SHIPPED** (schema/write half): `meta migrate` now excludes derived (`origin.*`) fields from the write table DDL (new `MetaField.isDerived()`) AND emits the replica read-view (`isWriteThrough` wired into `buildProjectionViews`, entity-host mode in `extractViewSpec` β€” "one emitter, two hosts"); the TS write codecs (Drizzle table + Insert/Update Zod schemas) exclude them too. Gated by a real-Postgres round-trip (emit β†’ apply β†’ introspect β†’ re-diff EMPTY) in `view-lifecycle-pg`. Was a real correctness bug (silent wrong-table + missing view). **#214 β€” SHIPPED (unreleased)**: the FR-024 Β§7 codegen **READ half** across all five ports β€” generated reads route to the replica view, the entity read type carries the derived fields via a by-PK re-read (read-your-writes), writes target the table and exclude derived fields. (Flattened `field.object` on a write-through entity is a tracked sub-limitation.) **A shared cross-port #214 gate SHIPPED (unreleased) β€” and found+fixed a real TS bug.** Building it surfaced that the TS reference port's generated REST **routes** for a write-through entity were never wired to the view: `routes-file.ts` mounted vanilla `mountCrudRoutes(table)` (no view concept), so the deployed HTTP surface returned GET/POST responses OMITTING the derived field β€” the read-half had shipped in `queries.ts` + the view but the routes layer was left behind. Fixed: `mountCrudRoutes` gained an optional `readView` (list/get + post-write re-read route through the view; writes stay on the table), and `routes-file.ts` passes it for write-through entities. The shared gate lives in **`fixtures/api-contract-conformance/write-through/`** (a Customer + write-through Order corpus: POST create returns the derived `customerName`, GET reads it through the replica view) and runs on **TS / C# / Kotlin** β€” the ports whose GENERATED artifact re-reads through the view (TS `readView`, C# EF `.ToView` read-model, Kotlin inline Exposed `OrderView`). **Java and Python are intentionally excluded** (weak signal): their generated controller delegates read-your-writes to the runtime (Python `ObjectManager`) or the consumer persistence seam (Java), which the api-contract in-memory-repo lane bypasses β€” so a gate there would test a hand-written seam repo, not the generated re-read. A `persistence-conformance` `op: roundtrip` gate was ruled out as the route because that corpus exercises the runtime read path, where write-through view-routing exists only in Python's `ObjectManager` (TS `runtime-ts`, Java OMDB, C# EF `FindAsync`, Kotlin/Exposed all read the write table). - **#209 β€” SHIPPED** (released in `0.17.0`): derive **INNER vs LEFT OUTER** join from reference optionality β€” a NOT-NULL belongs-to FK renders `INNER JOIN` (matching the hand-written view + keeping `verify --db` fingerprints aligned), a nullable belongs-to or any has-many hop stays `LEFT OUTER`, with nested-chain demotion (an INNER hop under a LEFT ancestor demotes to LEFT to avoid dropping base rows). `extract-view-spec.ts` + `view-ddl-emit.ts`, gated by `join-type-derivation.test.ts`. The optional explicit `@join: inner|left` override is not needed (the optionality-derived default is correct); defer until an adopter wants to force it. - **#207 β€” SHIPPED (unreleased)**: **View-level `@filter`** (row-scope WHERE) reusing the 5-port `attr.filter` AST, lowered to an outer `WHERE` in `emitViewDdl`. Closed the single largest functional gap for filtered views (soft-delete / status / type views). Semantic (field + op), never raw SQL. Cross-port validation (dangling + aggregate-derived); op-band / malformed-shape checks are TS-only. - **#208 β€” SHIPPED** (unreleased): **DDL-ownership escape valves** β€” `source.rdb` **`@sql`** (a hand-written view body migrate registers + fingerprints + drift-checks but never authors/parses, adopted via `migrate --allow adopt-view`) **+ `@unmanaged`** (skip create/drop/drift for a Flyway/hand-migration-owned view **or table**). The load-bearing piece is the **suppression rule** (classify DDL-ownership *before* `viewIsDerived`), which closes a silent-wrong-synthesis hole. Registration + 6 loader-validation rules cross-port (5 ports); migrate/verify lowering TS-only (ADR-0015). See [ADR-0043](decisions/ADR-0043-ddl-ownership-escape-valves.md) + `docs/superpowers/specs/2026-07-17-issue-208-ddl-ownership-escape-valves-design.md`. **Deferred follow-ups:** (a) **`@dependsOn` for `@sql` views** β€” an opaque body's `dependsOn` is derived from `extends`-anchors only, so a JOINed-but-unanchored table is not tracked for auto-recreate around a column `ALTER` (add the explicit attr when an adopter hits it); (b) the **matview managed path** (`@sql` on `@kind: materializedView` β€” needs `pg_matviews` introspection + COMMENT-on-matview + REFRESH); (c) **opaque-body column-name verification** (`verify` cross-checks an `@sql` view's `information_schema.columns` against the declared field set). - **Group B β€” 1.0-freeze-window vocab decision (needs an owner ruling before the freeze):** - **#210 β€” SHIPPED (unreleased, closed)** β€” landed 2026-08-06 in all four loaders + the codegen resolvers of all five ports, gated by seven new conformance fixtures (`error-value-origin-{aggregate,computed,collection,first}`, `template-payload-ref-sourceless-projection`, `error-template-payload-ref-sourced-projection`, `error-payload-nested-object-ref-entity`) + the `object.value`/`object.projection` `rules` strings in `expected-registry.json`; ADR-0028 amended; migration guide at `docs/features/migrations/value-assembly-origins-and-source-role-shrink.md`. Nested payload `field.object @objectRef` targets stay value-only (now loader-enforced, closing the #270-adjudicated gap). **[RULED 2026-08-05: DO IT, batched into the pre-1.0 consolidation, scope narrowed]** Retire the **assembly** origins (`aggregate`/`computed`/`collection`/`first`) on `object.value`, and widen `@payloadRef`/`@responseRef` to accept a **sourceless** `object.projection`. `origin.passthrough` **stays** β€” FR-015 parameter VOs are out of scope, because the loader already draws that line (FR-024 B5 in `validateOriginPaths`: a value's passthrough is *parameter lineage*, not an assembly path), and retiring it would silently drop the `ERR_PASSTHROUGH_TYPE_MISMATCH` check on proc args. The "total value purity" framing is dropped β€” ADR-0046's `@enforce: false` reference already made value rules conditional; the durable rule is **"passthrough on a value is lineage; assembly origins live on projections."** No new vocabulary (`origin.collection` is registered; a sourceless projection already loads), but `expected-registry.json` **does** change β€” `object.value`'s `rules` string said "by assembly" β€” so all five ports sync in lockstep. Timing: **not** during the Phase-1 window (the churn is in the payload tier, this repo's recent cross-port bug hotspot, and it would churn the very pillar the launch showcases); it rides the single ADR-0035 Β§3 breaking batch while 1.0 stays unscheduled. Blocked on two non-breaking preludes: **#270 β€” SHIPPED** (payload typing is declared-type-authoritative: the Kotlin, Python **and Java** payload emitters all derived a field's type, nullability, or nested-payload closure from its `origin.*` child β€” Java's `SpringPayloadGenerator` carried the identical origin dispatch, surfaced in review after the initial ruling named only Kotlin/Python β€” and all three now type exactly as if the origin child were absent, converging on the genuinely origin-blind TS/C# reference emitters; the `origin.collection` `@via` closure edge is deleted from the ADR-0044 name map in all three ports (gated positive + negative per port; the declared edge's target-subtype question β€” no loader constrains a nested `@objectRef` target's subtype today, TS/C#/Python don't filter, Kotlin/Java keep their pre-existing codegen filter as status quo β€” is adjudicated to #210's loader-validation ruling), and the stale CLAUDE.md open-questions pointer to the codegen-spring `KNOWN_GAPS` entry β€” an entry that had disappeared because Java *implemented* origin resolution, not because it was moot β€” is closed) and **#271 β€” SHIPPED** (landed in `efe12d43`: sourceless projections pinned cross-port β€” every fan-out proven to no-op on one β€” retiring #210's falsifier; #248's persistability-from-source contract now holds in all five ports). Reversal triggers: real adopters authoring assembly origins on payload VOs; #271 finding a port that needs new vocabulary for sourceless projections (ADR-0023 cost class β‡’ reopen); or 1.0 being scheduled imminently (then ship it and document the rule-of-thumb instead). - **Group C β€” post-1.0 backend generalization (design-now, build-later):** - **#212 β€” SHIPPED (unreleased, closed)** β€” the actionable content (the `@role` shrink to `primary | replica`) landed in all five ports + `expected-registry.json` + a new `error-source-role-reserved` conformance fixture; the ADR-0007/0028/FR-024 doc amendments landed with the ruling. **[RULED 2026-08-05: RATIFIED with three amendments]** Events emit projections: resolve the **ADR-0007 vs ADR-0028 contradiction** (a stream is a channel, not a source). Drop `source.event`; model emission at the surface as `api.eventing`/`operation.event`/`binding.messaging` with a payloadβ†’projection ref; keep the "a stream becomes a source exactly when it is treated as addressable state" escape clause. Lands the CQRS+events symmetry: queries return projections, commands take values, events emit projections. **Amendment 1 β€” the doctrine's test is single-prong.** The proposed two-prong AND (addressable *and* drift-inspectable) fails in both directions: it would KEEP a schema-registry-backed Kafka topic and KILL `source.memory`, and it wrongly maims `document`/`keyValue`/`objectStore`/`timeSeries`/`graph`. Ratified wording: a `source.*` binds an object to **addressable state at rest** β€” readable on demand by a declared key or address, not merely observed in flight; **drift-inspectability is a per-paradigm `verify` capability, never an admission criterion**. Only `event`'s flow kinds (topic/stream) fall; `eventStore`/`changelog` are the escape clause; `vector` and `search` survive (both have fetch-by-id). **Amendment 2 β€” removing `@role: publish` is NOT docs-only**: it is live registered vocabulary (`expected-registry.json`, `spec/metamodel/db.json`, five ports' embedded definitions), so it rides the #210 consolidation batch. **Amendment 3** β€” FR-024 Β§7's channel row references a **projection**, not a value. **Sub-decision β€” [RULED 2026-08-05: option A + doctrine]** Shrink `@role` to `primary | replica`; document `index`/`cache`/`publish`/`mirror` as **reserved-not-registered** (the ADR-0040 treatment), and write the re-entry bar into the ADR-0007 amendment: *a role member enters the registry only when a shipping consumer dispatches on it.* Cross-port verification closed the gate β€” every read of `@role` in all five ports is an equality test against `primary` (Java OMDB has **zero** role usage; Kotlin `KotlinGenUtil` and Python `_read_source_name` are explicitly documented "role-agnostic", finding the replica by `@kind`), so the consumed information content is one bit and the four unused members are indistinguishable from `replica` to every consumer. A `@primary: boolean` was **rejected**: `@role` defaults to `primary` when omitted and the one-primary invariant rejects two primaries, so a *second* source must carry an explicit non-primary role β€” `replica` is the registered opt-out that makes multi-source declarable at all, and the enum cannot shrink below two without redesigning the default. `@role` survives ADR-0037 narrowly at step 3 (primacy is a tie-breaking *designation*, not derivable: `table(primary)` + `table(replica)` is legal and becomes inexpressible under any "the writable one is primary" derivation). Decided by the freeze asymmetry β€” removing a registered member post-1.0 is a **2.0 event**, re-adding a reserved one is **additive**, so pruning now is the reversible move β€” and by the template argument: `@role` is registered on `source.rdb` specifically, so the shrunk registration is what every future paradigm in this catalog copies. Recorded finding, not acted on: `@role` today is a **designation** mechanism, not a routing one β€” ADR-0007's "consumers route by `@role`" prose describes a dispatch no port ever built. **Adopter scan re-run before landing (2026-08-06):** ADR-0036's scan + the public reference app + downstream consumer models β€” zero uses of the four retired members. Splits: the ADR-0007/0028/FR-024 doc amendments **landed** with the ruling (ADR-0035 Β§1 excludes reserved-but-unregistered from the compat surface, so no quiet-clock reset); the `@role` registry shrink **shipped** with the #210 consolidation batch (loaders reject a retired member via the generic `allowedValues` check β†’ `ERR_BAD_ATTR_VALUE`); the eventing surface rides FR-024 to 1.1 (`api.*`/`operation.*`/`binding.*` are all unregistered today, so it is additive by construction). - **#211** β€” **Backend-agnostic projection materialization** (one derivation spec, N lowerings): generalize `SelectSpec` from RDB-only to document/search/… with an origin Γ— (paradigm, `@kind`) capability matrix + load-time errors. Large FR; its foundation is Group A landing with the "semantic, not RDB-specific" lens (#207/#208/#209 become the first lowerings). Post-1.0; composes with FR-034 (ecosystem tier). - **FR-019 β€” Shared + externally-provided enums.** Stop redeclaring a `field.enum` inline in every consuming entity: a package-level abstract `field.enum` materializes ONE standalone enum type per port (the existing D6 `extends` reuse vocabulary), and **`@provided: true`** β€” a provenance flag on the named-type *declaration* (not the field), shared cross-type with value objects β€” references an existing hand-written type instead of emitting one (per-port namespace via codegen config, never a metadata FQN β€” retires the C#-only `@csEnumType`). Decision in [ADR-0026](decisions/ADR-0026-shared-and-provided-named-types.md); implementation spec `docs/superpowers/specs/2026-06-06-fr-019-shared-and-provided-enums-design.md`. (Generators are now subclass-extensible across all 5 ports, so this lands on open seams.) - **FR-020 β€” `@inheritance` persistence strategy (single-table vs joined).** TPH (one physical table, nullable subtype columns) is the single implicit strategy today (FR-014/FR-017). FR-020 adds an `@inheritance` attr to choose **joined** (a base table + per-subtype tables, joined on read) as an alternative β€” **additive, default stays TPH** (no breaking change). Status: design (proposed, sequenced after FR-017, which shipped). Design: `docs/superpowers/specs/2026-06-07-fr-020-inheritance-strategy-design.md`. *(Was not previously surfaced in this roadmap.)* - **FR-025 β€” Cross-port package-binding codegen config.** Config for how a metadata FQN/package maps to each port's native namespace/package at codegen time (the ADR-0001 build-time binding), so cross-package references (and metadata shared across projects, FR-023) emit correct imports per language. Status: design (ready for implementation). Design: `docs/superpowers/specs/2026-06-08-fr-025-cross-port-package-binding-config-design.md`. *(Was not previously surfaced in this roadmap.)* - **MCP exposure of declared prompts/tools** β€” the remaining library-side piece of the prompt-construction pillar. Surface a `template.output` / tool declaration over the Model Context Protocol (model-agnostic) so an LLM host can discover + register it, built on the shipped render / payload / verify / FR-006 / FR-010 primitives. Designed in `docs/superpowers/specs/2026-05-22-fr-004-cross-language-prompt-construction-design.md`. - **FR-024 β€” Entity surfaces: `object.projection`, universal field-`extends`, and the declared API.** A third object subtype (derived, read-only, borrowed identity via `extends`; the declared field set IS the exposure β€” fail-closed), universal `Entity.child` extends-resolution (load-time drift gate on every contract shape), `@via` single-hop-unique inference, multi-source entity view behavior, and the `api.operational` / `operation.query|command` / `binding.rest` surface β€” across all 5 ports, conformance-gated; the two pre-taxonomy spellings (entity-`extends`-entity views, proc-results-as-entities) are removed outright (hard cutover, pre-GA). Decisions: [ADR-0028](decisions/ADR-0028-object-taxonomy-projection-value-purity.md) / [ADR-0029](decisions/ADR-0029-entity-child-extends-and-via-inference.md) / [ADR-0030](decisions/ADR-0030-declared-api-surface-and-org-tier-boundary.md). Design: `docs/superpowers/specs/2026-06-12-fr-024-entity-surfaces-projections-design.md`; program plan: `docs/superpowers/plans/2026-06-12-fr-024-entity-surfaces-program.md`. Supersedes the shape-vocabulary half of the FR-021 sketch (its `api`/`wireId` direction stands, retyped onto projection/value). - **FR-021 β€” `api` metadata type + contract projections.** Declared API surfaces (operations + per-protocol bindings) over the derived-CRUD default; operation payloads are declared contract shapes (post-#210: an `object.value` carries `origin.passthrough` lineage only β€” assembly origins live on `object.projection`, the same machinery as prompts and views); wire-stable `wireId` numbering lives on the contract projection (never the entity) so domain evolution can't break wire compat. Design sketch: `docs/superpowers/specs/2026-06-11-fr-021-api-metadata-and-contract-projections-design.md`. **Revised by FR-024:** contract shapes are `object.projection` (query outputs) / `object.value` (command inputs); subtype vocabulary `api.base`/`api.operational`; see ADR-0030. - **FR-034 β€” Ecosystem tier: connecting meta-modeled systems over a network.** A semantic topology layer that models the *ecosystem* β€” many meta-modeled systems and how they call each other β€” rather than a single system's shapes. C4-style two-level nesting: `system` (logical software system) β†’ `container.service|client` (the deployable network participants), each carrying `surface.provided|consumed` edges whose `@apiRef` resolves (FQN, ADR-0041) to an `api.*` node in the same or a dependency package; plus `environment.deployment` (the named instantiation that keys a generated per-environment address-config matrix). URLs are **not** metadata (a generated, fail-closed *address contract* keyed per environment carries them); the logical topology moves **into** core (superseding ADR-0030 Β§5) while the physical tier stays permanently out. Reuses FR-023 (metadata-package transport), ADR-0041 (resolution), and ADR-0029 (the load-time drift gate, now spanning repos β†’ cross-system drift detection = pillar 3, one tier up). Depends on the FR-024 declared-API surface. Owner rulings (2026-07-10): C4-nesting, topology-into-core, names-only environments, defer third-party. **Draft, deferred post-1.0.** Design: `docs/superpowers/specs/2026-07-10-fr-034-ecosystem-tier-connected-systems-design.md`. Downstream convergence evidence (a consumer independently re-deriving the consumed-edge address contract + protocol-off-the-type-axis): `spec/design-docs/2026-07-11-downstream-convergence-outbound-adapters.md`. - **FR-022 β€” Contract emitters (Tier-2): JSON Schema 2020-12 (canonical + strict structured-output profile) β†’ OpenAPI 3.1 β†’ protobuf.** One shared neutral engine per ADR-0020; strict profile targets the cross-provider structured-output intersection (also MCP input schemas); proto emission consumes FR-021 `wireId`s, maps types per the locked table (Decimal/minor-units/uuid-string/optional/UNSPECIFIED-enum-zero), and inherits wire-compat regression detection via `buf breaking` on the emitted artifacts. Design sketch: `docs/superpowers/specs/2026-06-11-fr-022-contract-emitters-design.md`. - **FR-023 β€” Metadata packages: cross-repo distribution + reuse.** A code-free, versioned artifact (the `metaobjects/` tree + manifest) published through each ecosystem's registry (npm/Maven/PyPI/NuGet) and declared via `metadataDependencies` in config; loader source composition + overlay/extends across package boundaries (mechanics already shipped) with dependency-then-local merge order, collision rules, and per-package provenance attribution. Design sketch: `docs/superpowers/specs/2026-06-11-fr-023-metadata-packages-design.md`. - **Near-term doc-first quick wins (available now, no new code):** the *mechanics* of cross-project sharing already work β€” overlay merge, deferred cross-package `extends`, and dir/URI loaders β€” so before the full package-resolution build, ship **recipes**: (a) a monorepo pattern (one shared `metaobjects/` dir loaded by TS + Java + Python projects), (b) a git-submodule pattern (pull upstream metadata into a local `metaobjects/`), and (c) wire the already-defined-but-unused `.metaobjects/config.json` `sources` field (TS) to the loader so `{ kind: "path" }` overlays compose. These cover most "share a model across our backends" needs with documentation; FR-023 proper adds true dependency resolution + the `metaobjects.pkg.json` manifest. TS-only `libraries` mechanism (`metaobjects::ai`) is the single existing precedent β€” generalize it under FR-023. - **FR-026 β€” Forms completeness: edit forms, view-render parity, validator wiring (TS web client).** Builds on what ships today β€” create forms (`@emitForm` β†’ `formFile` β†’ `useEntityForm` + Zod `InsertSchema`), field display via `view.*` cell renderers (~10 of 13 subtypes), and client form rules (`required`/`length`/`regex`). **Edit forms shipped in `0.19.2` (#227)** β€” the generated `Form` switches its resolver from `InsertSchema` to `UpdateSchema` when `defaultValues` are present, so the one form is both create and edit (previously it validated every submit against `InsertSchema`, whose optionals reject `null`, which blocked editing any row holding a NULL optional column). **View-kind form-control dispatch shipped in `0.18.0`** (enumβ†’`