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 7eba5b8
Browse filesBrowse the repository at this point in the historyBrowse files
docs(fr-029): design the metadata API + browser read-model; rule out a runtime data surface
FR-029's UI-1 and UI-2. `GET /_meta` serves the whole model as EFFECTIVE canonical
JSON, and `runtime-web` reads it into a slim structural model that the already-shipped
`buildGrid()` can walk. `buildGrid` landed in ea2b9e9 and has had no way to obtain a
MetaObject in a browser ever since; this is that missing half.
Two decisions carry the design:
- **Effective, not raw.** A raw model carries `extends` refs, so a client would have to
resolve them — and per ADR-0039 an own-vs-resolving error there silently corrupts
exactly the attrs a runtime grid reads (@columns, @pageSize, @sortableDefaultOrder).
Serving effective moves that to the server, where it is already byte-gated in five
ports, and makes the browser model a reader that cannot get it wrong.
- **A slim client read-model, not the loader in the browser.** The metadata root barrel
transitively imports `node:url` and cannot be bundled (#287, gated by
runtime-web/test/browser-bundleable.test.ts). `buildGrid` already imports MetaObject
as a TYPE only and values from the browser-safe /constants subpath, so a structurally
compatible model suffices. The metadata package's module graph does not move.
Only TypeScript has a web-bound runtime home: C#'s runtime package references only
YamlDotNet, Java's core-spring has spring-context but no spring-web, and neither of
Python's runtime packages imports FastAPI. So UI-1 ships as a contract plus a
framework-free metaJson(root) helper per port, with a mount only in TS. The durable
deliverable is the contract, which is what lets one browser read-model work against any
backend (UI-8).
Also records, in the design's section 8, that a four-port runtime metadata-driven REST
DATA surface was considered and rejected: FR-029 never scoped one (roadmap:330 — UI-8
says "the existing data REST API"), no issue asks for one, EntityFetcher is a bare path
fetch so the client tier cannot tell how an endpoint was produced, and the 2026-07-12
oversell review records that the flagship adopter did not use runtime metadata at all.
Written down because the same design was independently re-derived in 2026-09 having
already been specified in 2026-06.
Amends the 2026-06-16 slice-1 design: its server half (sortableFields, handleList,
mountMetaCrudRoutes) is retired, its client half (buildColumns, useMetaGrid) kept as
UI-3. Neither retired piece was ever built; this records the decision that was never
written down.
Gap-table corrections found while grounding:
- UI-5 was marked MISSING but shipped. use-entity-form.tsx derives from the GENERATED
schema, which is what makes it UI-5 and not UI-4. UI-4 remains genuinely missing.
- UI-3 is PARTIAL, not missing — buildGrid shipped.
Two unrelated doc-drift fixes, same commit rather than filed:
- AGENTS.md listed a `server/java/dynamic` module that does not exist.
- AGENTS.md billed EF Core under the runtime pillar. The 2026-07-12 oversell review
ruled that dishonest ("EF Core executing generated code is codegen, not a
metadata-driven runtime") and docs/llms/llms.txt already says so; AGENTS.md was the
outlier. Verified: the runtime MetaObjects package depends only on YamlDotNet.
- The api-contract corpus README still said Actions is disabled. It is on since
2026-09-20, integration-tests.yml runs the corpus on tag push and dispatch, and
local-ci.yml's ts-slow/java-slow lanes carry it on push to main. None run on a PR,
so the advice to run ci-local.sh before opening one is unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NrWUxA6euwvnFcwKxcK6A
Copy file name to clipboardExpand all lines: AGENTS.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,7 +11,7 @@ The metamodel is the **durable spine**; generated code is the **disposable artif
11
11
The first four ship per-language today across the five ports (TS / C# / Java / Python / Kotlin), with cross-port conformance corpora verifying byte-identical behavior. The fifth ships its vocabulary in every port, its `meta verify` checks in the Node `meta` CLI, and its test scaffolding in TypeScript only. The sixth is content the other five compose, shipped as named opt-in artifacts:
12
12
13
13
1.**Codegen** — emit idiomatic per-language code (Drizzle/Zod + Fastify for TS, EF Core + ASP.NET for C#, Spring REST + DTO + Repository for Java via `codegen-spring`, Pydantic + FastAPI for Python, KotlinPoet + Exposed + Spring for Kotlin via `codegen-kotlin`). Hand-edit-preserving regen via three-way merge.
14
-
2.**Runtime metadata** — load metadata at runtime, drive behavior dynamically (CRUD, validation, relationships, dynamic admin UIs, LLM tool registration). On Kysely (TS), a DB-API 2 driver via ObjectManager (Python), modernized JDBC + Spring-tx via OMDB (Java), Exposed (Kotlin), EF Core (C#).
14
+
2.**Runtime metadata** — load metadata at runtime, drive behavior dynamically (CRUD, validation, relationships, dynamic admin UIs, LLM tool registration). On Kysely (TS), a DB-API 2 driver via ObjectManager (Python), modernized JDBC + Spring-tx via OMDB (Java), Exposed (Kotlin). **C# runtime metadata is on the roadmap** — its EF Core output is generated code, not a metadata-driven runtime; the runtime `MetaObjects` package depends only on YamlDotNet and ships no data access.
15
15
3.**Drift detection** — `meta verify` catches divergence between code and metadata (covers entity codegen, prompt templates, output parsers, schema). Quality-of-life on top of codegen + runtime.
16
16
4. **Prompt construction** — a prompt is code, not a string scattered across services. Declare a prompt's payload as a typed projection (payload bloat becomes a diff), keep its text external and provider-resolved, and render it deterministically: snapshot-testable, cache-stable (no whitespace change silently breaking exact-prefix prompt-cache hits), and drift-checked at build time so a renamed field can't degrade a prompt. Conformance-gated, so the guarantee holds in every language port. **Render + payload-VO codegen + `verify` + parser-on-receipt for a *responding* `template.prompt` — one carrying `@responseRef` (FR-006) — + the output-format prompt fragment & tolerant `extract` parser (FR-010) ship in all five ports today** (since 0.24.0 the whole inbound tier keys off `@responseRef`; a `template.output` is outbound-only and emits no parser — ADR-0052) — the library-side building blocks of the pillar are complete. The one remaining library-side piece is MCP exposure of declared prompts/tools (see `spec/roadmap.md`); the application-level consolidation (eval harness, end-to-end declared-prompt orchestration) and consumer adoption are exercised in adopter projects, not in this library repo. Designed in `docs/superpowers/specs/2026-05-22-fr-004-cross-language-prompt-construction-design.md`.
17
17
5. **Requirements and testing** — declare what the software is supposed to *do* in the same model as the entities, so a capability claim is checkable instead of prose. The other four pillars keep the code honest about the *model*; this one asks whether the thing you said the software does is actually built — an absence no test can fail on, because a test exercises code that exists. `requirement.functional` (existence: `meta verify` WARNS when nothing implements it) and `requirement.architectural` (universality: `meta verify` FAILS a live policy applied to nothing, and does not check that each claimed node complies) are registered vocabulary in all five ports, with the loader enforcing the closed `@status` enum. `@implementedBy` is **resolved, not trusted** — it names a real member of the real model, so a claim whose implementation was renamed or deleted fails the build instead of going quietly stale. `meta verify` reports the ledger on every run (unresolved links, entities no claim covers, gaps recorded versus gaps nobody has ruled on) plus an authoring lint whose findings can never fail a build; `meta docs` renders it for humans and for agents. **The port split, stated exactly: the vocabulary loads and validates in all five ports; the `meta verify` checks run in the Node `meta` CLI only (no other port's CLI runs them — `docs/CONFORMANCE.md` "Split coverage"); `requirementTests()` — which scaffolds a test stub per claim — is TypeScript-only.** A green run proves referential integrity, never that a status is true or an implementation correct (`docs/features/requirements.md`, "What a green run does not prove"). A project that declares no `requirement.*` nodes sees no change at all. Note the standing carve-out: `agent-context/skills/metaobjects-fit-assessment/SKILL.md` deliberately does NOT treat `requirement.*` as an assessment axis — see the ruling in that file before "finishing the job" there.
@@ -65,7 +65,7 @@ PyPI has had no product change since `0.25.0` — nothing is broken.
-**C#** — `MetaObjects` (loader + canonical serializer + conformance) + `MetaObjects.Render` (Mustache + payload-VO + `verify`) + `MetaObjects.Codegen` (EF Core entities + `AppDbContext` + CRUD minimal-API routes). Schema migrations are TS-owned (ADR-0015): the C# migrate engine and the `migrate`/`--from-db` CLI surface were removed; the C# CLI is `gen`/`verify` only, packaged as a .NET tool invoked **`dotnet meta`** (not a bare `meta` — that name belongs to the Node schema CLI). EF Core runtime data-access stays per-port.
68
-
- **Java** — `metadata` + `omdb` + `om` + `dynamic` + `core-spring` + `metadata-ktx` (Kotlin facade) + `codegen-spring` (Spring controllers + DTOs + repositories + filter allowlists + payload records + output parsers) + `codegen-mustache` + `codegen-plantuml` + `render` + `maven-plugin` (`metaobjects:generate`/`metaobjects:editor` + a `metaobjects:verify` **codegen-drift** goal — distinct from the removed live-DB `metaobjects:verify`; Kotlin generators run through `metaobjects:generate` via the shared SPI). FR-003 (OMDB runtime persistence + binding registry + typed jsonb + Spring-tx + source/origin metamodel) fully shipped, including Plan 4 (engine-debt remediation: atomic mapping cache, JDBC codec registry, `inTransaction` template). **Schema migrations are owned by the TypeScript toolchain** (`@metaobjectsdev/cli migrate`); the Java port's diff-and-converge migration engine and its `metaobjects:migrate` / live-DB-drift `metaobjects:verify` Maven goals were removed, and per ADR-0015 Decision 2 the dev/test runtime auto-create path (`MetaClassDBValidatorService` + the drivers' `createTable`/`createIndex`/`createForeignKey`/`createSequence` DDL) is **also** removed — **OMDB is pure data-access** (CRUD/query/codec/transactions only).
68
+
- **Java** — `metadata` + `omdb` + `om` + `core-spring` + `metadata-ktx` (Kotlin facade) + `codegen-spring` (Spring controllers + DTOs + repositories + filter allowlists + payload records + output parsers) + `codegen-mustache` + `codegen-plantuml` + `render` + `maven-plugin` (`metaobjects:generate`/`metaobjects:editor` + a `metaobjects:verify` **codegen-drift** goal — distinct from the removed live-DB `metaobjects:verify`; Kotlin generators run through `metaobjects:generate` via the shared SPI). FR-003 (OMDB runtime persistence + binding registry + typed jsonb + Spring-tx + source/origin metamodel) fully shipped, including Plan 4 (engine-debt remediation: atomic mapping cache, JDBC codec registry, `inTransaction` template). **Schema migrations are owned by the TypeScript toolchain** (`@metaobjectsdev/cli migrate`); the Java port's diff-and-converge migration engine and its `metaobjects:migrate` / live-DB-drift `metaobjects:verify` Maven goals were removed, and per ADR-0015 Decision 2 the dev/test runtime auto-create path (`MetaClassDBValidatorService` + the drivers' `createTable`/`createIndex`/`createForeignKey`/`createSequence` DDL) is **also** removed — **OMDB is pure data-access** (CRUD/query/codec/transactions only).
69
69
-**Python** — `metaobjects` (loader + canonical serializer + conformance + render + verify + codegen) + an `ObjectManager` runtime layer. The `migrate` module was removed (schema is TS-owned, ADR-0015); Python is pure data-access (codegen + ObjectManager runtime), with a **`metaobjects` console-script** (`gen`/`verify` codegen — no `migrate`). All five conformance corpora green.
70
70
-**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.
Copy file name to clipboardExpand all lines: docs/superpowers/specs/2026-06-13-metadata-runtime-ui-and-serializers-gaps.md
+18-5Lines changed: 18 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,16 +16,29 @@ The principle: the **web UI is TS (browser-native), but it must be drivable by m
16
16
17
17
| ID | Gap | Status | Scope |
18
18
|---|---|---|---|
19
-
| UI-1 |**Metadata API endpoint** — each backend serves its loaded metadata as canonical JSON over HTTP (`GET /_meta` / per-entity), so a browser can fetch the model. |❌ MISSING| all 5 backends |
20
-
| UI-2 |**Browser runtime metadata loader** — `runtime-web` loads canonical metadata JSON into an in-browser MetaData read-model (entities/fields/views/validators/layouts queryable client-side). |❌ MISSING| TS web |
21
-
| UI-3 |**Runtime-driven dataGrid** — build columns + cell renderers + sort/filter/page config from fetched metadata at runtime (no codegen). |❌ MISSING| TS web |
19
+
| UI-1 |**Metadata API endpoint** — each backend serves its loaded metadata as canonical JSON over HTTP (`GET /_meta`), so a browser can fetch the model. |📋 **DESIGNED 2026-09-20** — whole-model, **effective** canonical JSON; a framework-free `metaJson(root)` helper per port + a TS Fastify/Hono mount (only TS has a web-bound runtime home). Per-entity/ETag/versioning deferred.| all 5 backends |
20
+
| UI-2 |**Browser runtime metadata loader** — `runtime-web` loads canonical metadata JSON into an in-browser MetaData read-model (entities/fields/views/validators/layouts queryable client-side). |📋 **DESIGNED 2026-09-20** — a slim structural read-model (`loadMetaModel`), NOT `MetaDataLoader` in the browser (#287: the root barrel pulls `node:url`). Settles this theme's open question.| TS web |
21
+
| UI-3 |**Runtime-driven dataGrid** — build columns + cell renderers + sort/filter/page config from fetched metadata at runtime (no codegen). |🟡 **PARTIAL** — `buildGrid()` shipped `ea2b9e932` (#40). `buildColumns` + `useMetaGrid` remain (2026-06-16 slice-1 design §5.4/§5.5); they consume the EXISTING generated data API.| TS web |
22
22
| UI-4 |**Runtime-driven create + edit forms** — build the form fields + client validation from fetched metadata + validators at runtime. | ❌ MISSING | TS web |
23
-
| UI-5 |**Codegen edit forms** — `UpdateSchema` is generated but unused; emit `<Entity>EditForm` (load defaults, PATCH). |❌ MISSING (FR-026)| TS web |
23
+
| UI-5 |**Codegen edit forms** — `UpdateSchema` is generated but unused; emit `<Entity>EditForm` (load defaults, PATCH). |✅ **SHIPPED** (status corrected 2026-09-20 — was stale). `spec/roadmap.md:41` records FR-026 edit forms shipped; `client/web/packages/react/src/use-entity-form.tsx` derives from the GENERATED schema, which is what makes it UI-5 and not UI-4.| TS web |
| UI-7 |**"Both ways" demo + docs** — one reference app showing the codegen UI and the runtime-metadata-driven UI side by side, against each backend language. | ❌ MISSING | docs/example |
26
26
| UI-8 |**Backend-agnostic guarantee** — the TS UI verified working against all 5 backends (each serving the metadata API + the existing data REST API). | ❌ MISSING | cross-port |
27
27
28
-
Open questions: metadata-API shape (whole-model vs per-entity), caching/ETag/versioning, auth, and whether the runtime read-model reuses the `@metaobjectsdev/metadata` loader compiled to the browser or a slim client model.
28
+
Open questions: caching/ETag/versioning, and per-entity vs whole-model.
0 commit comments