Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
8c32371
docs(plan): requirements slice 1 — a uniform requirement-test filter …
dmealing Oct 6, 2026
cf1dc14
docs(plan): requirements slice 1 — the requirement-tests generator ej…
dmealing Oct 6, 2026
10c49a7
feat(verify): --require-implementers, and ADR-0057 reversing TypeScri…
dmealing Oct 6, 2026
c63b57b
feat(codegen-ts): requirement test identities, the requirement digest…
dmealing Oct 6, 2026
fbd3e48
feat(codegen-ts): a reference template for requirement-tests, so meta…
dmealing Oct 6, 2026
27f283d
test(conformance): requirement-check corpus, run by the TypeScript re…
dmealing Oct 6, 2026
e8e66ce
docs(plan): requirements slice 1 — what building the corpora found
dmealing Oct 6, 2026
f332be0
test(conformance): requirement-check corpus cases that a plausible wr…
dmealing Oct 6, 2026
ca6f2a4
fix(codegen-ts): compare every branch of the requirement-tests refere…
dmealing Oct 6, 2026
c7dbb34
docs(plan): requirements slice 1 — every port's generator test refuse…
dmealing Oct 6, 2026
be764b8
test(conformance): requirement-test identity corpus, run by the TypeS…
dmealing Oct 6, 2026
de2ab96
feat(java): the requirement gate in metaobjects:verify
dmealing Oct 6, 2026
d88ae37
docs(plan): requirements slice 1 — what reviewing the identity corpus…
dmealing Oct 6, 2026
e4f8398
fix(python): import PACKAGE_SEP in yaml_desugar, so a node-level pack…
dmealing Oct 6, 2026
56bfc1e
test(conformance): requirement-test identity corpus cases that an idi…
dmealing Oct 6, 2026
cc8fc86
docs(plan): requirements slice 1 — say exactly which word class fails…
dmealing Oct 6, 2026
2e752e8
feat(python): the requirement gate in metaobjects verify
dmealing Oct 6, 2026
50af5f8
test(conformance): a member reference whose owner does not resolve ge…
dmealing Oct 6, 2026
4a7a58d
feat(java): requirement-tests generator emitting JUnit with a typed w…
dmealing Oct 6, 2026
57809ba
fix(python): the requirement gate no longer fails open on an unexpect…
dmealing Oct 6, 2026
a96968d
feat(csharp): the requirement gate in dotnet meta verify
dmealing Oct 6, 2026
4d2e673
docs(plan): requirements slice 1 — one uncovered-warning text, stale …
dmealing Oct 6, 2026
522fa9e
fix(java): report a requirement-test filter or renderer that fails to…
dmealing Oct 6, 2026
b2e7c10
docs(plan): requirements slice 1 — which package-context shapes the p…
dmealing Oct 6, 2026
314bd20
fix(csharp): the requirement gate formats numbers invariantly and ref…
dmealing Oct 6, 2026
74bedae
feat(kotlin): requirement-tests generator emitting JUnit with a typed…
dmealing Oct 6, 2026
1c6904a
feat(python): requirement-tests generator emitting pytest with projec…
dmealing Oct 6, 2026
f0d32d9
feat(csharp): requirement-tests generator emitting xUnit with a typed…
dmealing Oct 6, 2026
9c30d37
docs(plan): requirements slice 1 — the stale-witness compile signal i…
dmealing Oct 6, 2026
4f5b607
fix(csharp): generated requirement tests steer witnesses to explicit …
dmealing Oct 6, 2026
551240b
fix(kotlin): pin the @Test annotation on generated requirement tests,…
dmealing Oct 6, 2026
41b82c2
fix(python): an ejected requirement-tests generator works with a proj…
dmealing Oct 6, 2026
8d8ed26
docs(requirements): the gate and the requirement-test generator in ev…
dmealing Oct 6, 2026
a8516d8
docs(requirements): three remaining statements of the TypeScript-only…
dmealing Oct 6, 2026
d6a71dc
fix(python): the project symbol resolver loads the right copy when an…
dmealing Oct 6, 2026
ec18b21
docs(loader): the requirement shape rule's comment no longer says the…
dmealing Oct 6, 2026
b4e429b
docs(plan): requirements slice 1 — record where the built code differ…
dmealing Oct 6, 2026
55dcec9
fix(python): the project symbol resolver drops a cached module only w…
dmealing Oct 6, 2026
aa15256
fix(python): the requirement gate reports a model that does not load …
dmealing Oct 6, 2026
4188477
fix(csharp): the requirement gate reports a failed load on the handed…
dmealing Oct 6, 2026
6df1fac
fix(java): a requirement-test hook whose constructor throws is report…
dmealing Oct 6, 2026
5d1dcb3
fix(codegen-ts): named package separator in the requirement walk, and…
dmealing Oct 6, 2026
ca63007
docs(requirements): the decision record and port pages state the fina…
dmealing Oct 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ MetaObjects is a **cross-language metadata standard** for declaring typed entity

The metamodel is the **durable spine**; generated code is the **disposable artifact**. Substrate is local-first: typed metadata lives in your repo, generated code is idiomatic per-language output with **no proprietary runtime**: the generated entity and model code imports nothing from MetaObjects at runtime, and the REST, prompt, client and runtime tiers import ordinary Apache-2.0 packages you can vendor, fork or replace. If `@metaobjectsdev/*` disappears tomorrow, you keep working code.

**Core vs helpers (ADR-0034 Amendment 3, 2026-09-22).** Only the core is a product promise: the metamodel, loader, canonical format and registry; runtime metadata access (the `ObjectManager` and its drivers; the HTTP adapters that mount it on a web framework are helpers, 2026-09-24 ruling); schema migrations (`meta migrate`); the drift gates (`meta verify`); prompt render and the reply parser. Every generator that writes application code into an adopter's repo — routes, controllers, ORM wiring, DTOs, forms, grids, hooks, filter allowlists — is a **reference helper**: it must compile and pass its reference fixtures, and the adopter copies it with `meta eject` and owns the copy. Mechanical test: what the tool guarantees is core; what it writes into your repo is a helper. Every port can eject: `meta eject` (TypeScript), `metaobjects eject` (Python), `mvn metaobjects:eject` (Java/Kotlin), `dotnet meta eject` (C#). Not every generator ships a reference copy: `meta gen --list` marks the ones that do not `package-only` (in TypeScript: `callable`, `trace-helper`, `requirement-tests`, `template`, the docs tier and `shared-model`; ADR-0034 Amendment 3 correction, 2026-09-26). When you find a generator defect, fix the reference; do not describe it as a broken core guarantee.
**Core vs helpers (ADR-0034 Amendment 3, 2026-09-22).** Only the core is a product promise: the metamodel, loader, canonical format and registry; runtime metadata access (the `ObjectManager` and its drivers; the HTTP adapters that mount it on a web framework are helpers, 2026-09-24 ruling); schema migrations (`meta migrate`); the drift gates (`meta verify`); prompt render and the reply parser. Every generator that writes application code into an adopter's repo — routes, controllers, ORM wiring, DTOs, forms, grids, hooks, filter allowlists — is a **reference helper**: it must compile and pass its reference fixtures, and the adopter copies it with `meta eject` and owns the copy. Mechanical test: what the tool guarantees is core; what it writes into your repo is a helper. Every port can eject: `meta eject` (TypeScript), `metaobjects eject` (Python), `mvn metaobjects:eject` (Java/Kotlin), `dotnet meta eject` (C#). Not every generator ships a reference copy: `meta gen --list` marks the ones that do not `package-only` (in TypeScript: `callable`, `trace-helper`, `template`, the docs tier and `shared-model`; ADR-0034 Amendment 3 correction, 2026-09-26). When you find a generator defect, fix the reference; do not describe it as a broken core guarantee.

## Six pillars

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:
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, its `verify` checks and a `requirement-tests` generator in every port, and its authoring lint in TypeScript only. The sixth is content the other five compose, shipped as named opt-in artifacts:

1. **Codegen** *(reference helpers, not core — see "Core vs helpers" above; ejectable in every port)* — emit 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.
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.
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.
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`.
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.
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 requirement gate runs in every port's `verify` (ADR-0057 — `docs/CONFORMANCE.md` "Split coverage"); every port ships an ejectable `requirement-tests` generator — which scaffolds a test per claim; the seven authoring-lint advisories are 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.

6. **Libraries** — reusable declared design, shipped as metadata and opted into by name (`"libraries": ["iam"]` in `.metaobjects/config.json`). Not a sixth verb: a library is the **reuse unit that composes the other five** — entities, requirements, the generators its design implies, its runtime packages — as one named, opt-in, drift-gated artifact. What makes it a pillar rather than a folder of YAML is the fifth: without requirements a library is a schema snippet; with them it is design an adopter's build is held to, which is the same test the requirements pillar passes (*does it change what an agent can be checked against?*). **A library is LAYERED and its core layer is INERT** — the core declares no `source.rdb`, and a sourceless object generates nothing and migrates to nothing (#248), so `["iam"]` adds zero tables and zero generated code while making the design present and resolvable; `["iam", "iam/db"]` is the separate opt-in that proposes the schema. **Copy is the expected mode** (`meta eject <library>` — ADR-0034's ruling applied to metadata), and an ejected copy still named in `libraries` is refused at load (`ERR_LIBRARY_PACKAGE_COLLISION`) rather than merging asymmetrically. Object coverage activates on ADOPTER-authored requirements only, so a library cannot volunteer a project for a gate it did not ask for. `iam` (preview) and `ai` (stable) ship today; rows appear in `meta gen --list` beside the generators. See [docs/features/libraries.md](docs/features/libraries.md) and `docs/superpowers/specs/2026-09-13-fr-043-feature-and-nfr-packages-design.md`.

Expand Down Expand Up @@ -75,7 +75,7 @@ PyPI has had no product change since `0.25.0` — nothing is broken.
- **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.

**Cross-port conformance corpora** (every port runs the shared corpus):
- Metamodel: `fixtures/conformance/` (364 fixtures; 26 shared corpora in total — per-corpus counts + the corpus x port matrix live in `docs/CONFORMANCE.md`). TS / C# / Java / Python all green.
- Metamodel: `fixtures/conformance/` (364 fixtures; 28 shared corpora in total — per-corpus counts + the corpus x port matrix live in `docs/CONFORMANCE.md`). TS / C# / Java / Python all green.
- Render: `fixtures/render-conformance/`. TS / C# / Java / Kotlin / Python byte-identical.
- 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".)
- 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. Three sub-corpora run the **generated lane only, on all five ports** — `write-through/`, `projection/` (F22: a view-only `object.projection` serves GET list + GET by id and answers every write verb with `405 {"error": "method_not_allowed"}`) and `report/` (FR-044: a view-backed `object.report` serves GET list, answers `POST` with that same `405`, and mounts no `/{id}` route). That is deliberate, not a gap: what is under test is whether a port's GENERATOR emits those routes, and a hand-rolled reference server would answer every scenario by construction. The `m2m/` sub-corpus also gates **TPH x M:N together** (base-declared, subtype-declared, abstract-mid-declared, a non-subtype source onto a subtype TARGET, and the cross-subtype source id answering `200 []`) — the two corpora were originally built disjoint (`tph/` had no relationships, `m2m/` no discriminators), which is precisely how that defect class survived.
Expand Down
Loading
Loading