Skip to content

Commit fff01da

Browse files
authored
feat(requirements): requirement checks and test generators in every port (ADR-0057) (#410)
* docs(plan): requirements slice 1 — a uniform requirement-test filter in all five ports The owner's answer to the plan's open question 8: the filter that selects which requirements get a generated test offers the same options in every port. Table I gains the filter and uncovered-warning rows, Table J gains seven filter cases run through each port's predicate seam, and a separate worked-example case replaces the double duty the plan gave concern-fanout. Refs the plan and ADR-0057. * docs(plan): requirements slice 1 — the requirement-tests generator ejects in every port The owner's added requirement: whatever ships for requirements and testing must eject and be owned by the application; the stock generator and the witness model are a recommended approach. Table L records how each port's existing eject mechanism takes the new generator and the byte-identity test that proves an unedited copy, and the port tasks gain the wiring. The open questions section becomes the record of the answers. Refs the plan and ADR-0057. * feat(verify): --require-implementers, and ADR-0057 reversing TypeScript-only requirement checks Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * feat(codegen-ts): requirement test identities, the requirement digest and a per-member grain The requirement walk gains the record every port's test generator agrees on (package, path, unit, id, witnessKey, status, skip, digest), the requirement-digest/v1 fingerprint of a claim, and a grain option that fans a requirement out per resolving reference instead of per concern. The default grain keeps its paths, test names and bytes; the renderer hook now receives the identity. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * feat(codegen-ts): a reference template for requirement-tests, so meta eject can hand it to the application requirement-tests was package-only because no reference template existed. It now ships one holding the generator and the default stub renderer in a single file, since an eject copies one file and the stub text is what an application is most likely to change. The built-in stays the oracle: the copy is byte-identity-gated over a ledger with requirements, in both grains. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * test(conformance): requirement-check corpus, run by the TypeScript reference 42 cases under fixtures/requirement-check-conformance pin the requirement gate's codes, severities, paths, message text and summary counts, so the four other ports can be held to the TypeScript reference. Expectations are written by scripts/write-requirement-corpus-expected.ts and reviewed by hand; the corpus gets its row in docs/CONFORMANCE.md and the site payload its count, which the site-counts gate requires of any new corpus. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * docs(plan): requirements slice 1 — what building the corpora found A 43rd check case so the strict switch cannot pass as warnings-as-errors, file-name load order and the runner's coverage defaults as runner rules, an unknown grain refused in every port, and the single-document rule for the unpackaged identity case. Refs the plan and ADR-0057. * test(conformance): requirement-check corpus cases that a plausible wrong port would have passed Adds require-implementers-raises-only-that-code (the strict switch leaves the deferral and coverage warnings at warn), a levelled architectural L4 with no links, a string-prefix probe for the undecided roll-up and a cross-package subtype extending by qualified name. The runner now validates option types and asserts the require-implementers input equals its twin; the README says what the supersededBy and did-you-mean cases really pin and names two runner preconditions. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * fix(codegen-ts): compare every branch of the requirement-tests reference template, and refuse an unknown grain The byte-identity gate now reaches the custom-path warning, the capped uncovered list, the undecided gap line and the generator's name, target and orphan policy, and renders hand-built arguments through both renderers; the reference renderer reads the status as the built-in does. An unknown grain is refused by the identity functions and both generators instead of running half of each grain, and a non-ASCII digest is pinned so a character count cannot pass for a byte length. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * docs(plan): requirements slice 1 — every port's generator test refuses an unknown grain No corpus pins the refusal, so each port's own test is the gate. Refs the plan and ADR-0057. * test(conformance): requirement-test identity corpus, run by the TypeScript reference 24 cases under fixtures/requirement-test-identity-conformance pin what may not differ between the ports' requirement-test generators: which tests a ledger yields, each test's id, witness key, skip state and claim digest, and the requirement view a project's filter is shown, through seven named predicates each port's runner implements. Expectations are written by scripts/write-requirement-corpus-expected.ts identity and were recomputed independently from the contract; docs/CONFORMANCE.md gains the corpus's row and the check corpus's current count (43), and AGENTS.md and the site payload the true corpora count (28). Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * feat(java): the requirement gate in metaobjects:verify Ports the TypeScript requirement gate (codes, order, message text, summary) to Java, held to it by the shared check corpus; Kotlin gets it through the same Maven goal. One did-you-mean helper is shared with the loader. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * docs(plan): requirements slice 1 — what reviewing the identity corpus found The effective package of a requirement nested under a package-declaring parent is the parent's, as the reference has it; the mangle class is ASCII and excludes the underscore; an eighth named predicate pins an absent level; an abstract requirement is pinned as collected; and the check corpus gains a member reference whose owner does not resolve. Refs the plan and ADR-0057. * fix(python): import PACKAGE_SEP in yaml_desugar, so a node-level package under a packaged root loads A YAML node declaring its own package under a packaged root hit an undefined name at load; the desugar's package branch was otherwise correct (absolute taken literally, leading :: relative to the parent). Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * test(conformance): requirement-test identity corpus cases that an idiomatic wrong port would have passed Review of the identity corpus found rules a wrong port could pass. filter-by-package now nests a child under each package-declaring requirement, witness-key-collision gains a doubled underscore, digest-multibyte-and-crlf a CR LF in the statement, and digest-claim-fields a pair differing only in supersededBy; two cases are new, filter-by-absent-level (an eighth named predicate, unlevelled) and abstract-collected. The runner's and the script's filter tables become Maps, so a name found on Object.prototype is refused, and the key function gets a direct test with letters outside ASCII. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * docs(plan): requirements slice 1 — say exactly which word class fails which mangle probe Refs the plan and ADR-0057. * feat(python): the requirement gate in metaobjects verify Ports the requirement checks to Python, held to the TypeScript reference by the shared requirement-check corpus, and runs them on every verify. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * test(conformance): a member reference whose owner does not resolve gets the object hint dangling-object-did-you-mean gains an L5 claiming Invoice.total from a package where Invoice is not local: the message quotes the whole reference and the did-you-mean hint names the object. No case reached that branch of the dangling check before. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * feat(java): requirement-tests generator emitting JUnit with a typed witness interface Adds the Java identity function and the ejectable JUnit Jupiter generator over the shared identity corpus, and hands the project class loader to a generator that loads a renderer or filter class by name. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * fix(python): the requirement gate no longer fails open on an unexpected load error The shared verify load helper caught every Exception, so a loader bug turned the gate off with exit 0 and no line. It now catches only the errors the loader and config readers raise, and the gate no longer retries a load that already failed. Adds config-mode CLI coverage of in_scope, the file count, the dependencies suffix and the undecided line, and a direct three-segment resolve_claim test. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * feat(csharp): the requirement gate in dotnet meta verify Ports the TypeScript requirement gate to C# (claim resolver, eleven codes plus object coverage, summary) and runs it on every verify; the shared 43-case corpus passes. A model with no requirement sees no change. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * docs(plan): requirements slice 1 — one uncovered-warning text, stale files and load failures of a project's hook Rulings from the Java generator's review that the C#, Kotlin and Python generators must share. Refs the plan and ADR-0057. * fix(java): report a requirement-test filter or renderer that fails to link, and pin three unpinned output rules A class found but unlinkable is no longer reported as missing; the uncovered warning names paths; sort, carriage-return escaping and the witness header are pinned; the identity runner has a case floor and its direct tests run once. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * docs(plan): requirements slice 1 — which package-context shapes the ports are held to agree on Two multi-file shapes differ between ports at the loader level and are recorded, not pinned. Refs the plan and ADR-0057. * fix(csharp): the requirement gate formats numbers invariantly and refuses a level that overflows Level saturates instead of wrapping and the gate reads the authored RawLevel; messages and printed lines format numbers with the invariant culture; the CLI test child no longer inherits META_REQUIRE_IMPLEMENTERS; the gate is exercised under --codegen. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * feat(kotlin): requirement-tests generator emitting JUnit with a typed witness interface Adds the ejectable Kotlin generator that writes one JUnit Jupiter test per declared requirement over the shared JVM identity function, registers it with its eject resource, and proves a package-renamed copy emits the same bytes. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * feat(python): requirement-tests generator emitting pytest with project-owned witnesses One machine-owned pytest file per package; each live test calls a project-owned witness function and fails naming the counterexample when there is none. Adds the identity function and digest (26/26 corpus cases), the requirementTests config block and an ejectable registry entry. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * feat(csharp): requirement-tests generator emitting xUnit with a typed witness interface Adds the C# identity function (digest, witness key, filter seam; all 26 identity-corpus cases pass) and an ejectable generator writing one xUnit test per requirement over a witness interface with default members. Options (TestNamespace, WitnessClass, Grain, Filter, Renderer, WarnUncovered) are public init properties on the generator because this port has no per-generator option channel; with none set, the namespace and witness class derive from the run namespace. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * docs(plan): requirements slice 1 — the stale-witness compile signal is conditional in Java and C# Table H stated it unconditionally. It holds by language rule only in Kotlin; Java needs @OverRide and C# needs explicit interface implementation, and each generated header must say so. Refs the plan and ADR-0057. * fix(csharp): generated requirement tests steer witnesses to explicit interface implementation, so a stale one stops compiling The witness header now tells the project to implement each member explicitly (a stale one fails with CS0539), the limit of the implicit form is pinned, generated names use global:: and keyword or non-ASCII segments are handled, the generator selects through the function the identity corpus pins, and the owned copy is tested with a project filter and renderer. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * fix(kotlin): pin the @test annotation on generated requirement tests, and escape keyword package segments Kotlin: backtick-escape hard-keyword package segments; tests pin @test, the uncovered warning's default and nested paths, triple quotes, name refusals, a silenced log, a stale override failing to compile, and the shipped eject resource (maven-plugin). Java generator: the header now asks for @OverRide (the orphan-witness signal depends on it) and its test changes (@test, warning default and nested path, silenced log, stale override compile checks) are in this commit too. codegen-base: renderer javadoc is language-neutral. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * fix(python): an ejected requirement-tests generator works with a project renderer, and one resolver for config symbols The renderer hook types now live in the package, so an ejected copy accepts the RenderedTest a project renderer imports. Config symbols and owned generators share one resolver that never unloads an installed package. Also: witnessModule validated as a config error, a wrong renderer return is a clean error, member grain keeps the first duplicate reference, control characters are escaped in comments. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * docs(requirements): the gate and the requirement-test generator in every port (ADR-0057) Docs, agent skills, the changelog and the pillar summaries now state what ships per port: the requirement gate in every verify, an ejectable requirement-tests generator in every port presented as a recommended approach, the strict switch, the conditional stale-witness signal and the known cross-port differences. The project's own ledger is unchanged: no planned entry concerned the gate or the generated tests, and it omits implementedBy by design. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * docs(requirements): three remaining statements of the TypeScript-only split, and small corrections Review round 1: two llms sentences and the Java README still said test scaffolding was TypeScript-only; the showcase config comment, the harness slot counts (115 and 575), the C# Program.cs example's using, and which ports the load-failure refusal applies to. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * fix(python): the project symbol resolver loads the right copy when another project's is cached The resolver now drops a cached module that is not under the current project root (except the running metaobjects package and the standard library), so a config provider or another project cannot leave its copy answering for an owned generator. Path-aware root test, fullmatch for module names, and a hook-types docstring that says what each grain passes. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * docs(loader): the requirement shape rule's comment no longer says the gate is TypeScript-only The gate runs in every port's verify since ADR-0057; the rule stays in the loader because a refused shape is unreachable whether or not verify runs. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * docs(plan): requirements slice 1 — record where the built code differs from the task text An as-built table beside the amendments, and the Python eject destination and hook-module corrections in Table L and Task 6. Refs the plan and ADR-0057. * fix(python): the project symbol resolver drops a cached module only when the project offers that name An installed package named by several config hooks is now one module again; a project package that shadows a standard-library name is refused deterministically instead of by load order. Module:symbol patterns reject whitespace, and the docstrings state the final rule and the pre-existing holes. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * fix(python): the requirement gate reports a model that does not load instead of passing silently verify with no generators selected loaded nothing else, so metadata the loader refuses exited 0. The gate's own load is now strict unless --lax and reports its failure once, unless a gate that ran already printed it. Also: the stdlib-name refusal names the real path, the witness-key collision message matches the other ports, and tests pin the summary sentence and the library package set. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * fix(csharp): the requirement gate reports a failed load on the handed-off codegen path When --codegen ran in an owned codegen/ project, stopped for want of --out, or only --db was asked for, no gate in the process loaded the model and a failed load was skipped with nothing said. The gate now prints the load errors and returns 1, unless a gate that ran here already reported them. Also: the generated witness header names the interface with its namespace, a dead regex is removed, and tests pin the summary sentence, the recorded-gaps line and the library package set. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * fix(java): a requirement-test hook whose constructor throws is reported with its real cause A renderer or filter whose constructor threw was told it needs a public no-argument constructor, in the Java and the Kotlin generator; both now name what was thrown. Also: the Maven goal logs every error before any warning, getLevel() saturates instead of wrapping, the requirement javadoc no longer says the checks are the Node CLI's or that an architectural requirement carries no level, and tests pin the summary sentence, the recorded-gaps line, the claims comment under member grain and the library package set. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * fix(codegen-ts): named package separator in the requirement walk, and hermetic strict-switch tests The qualified address was built with a literal separator where PACKAGE_SEPARATOR exists. The verify end-to-end tests read the ambient META_REQUIRE_IMPLEMENTERS and failed with it exported; each test now starts with it unset and restores it after. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057. * docs(requirements): the decision record and port pages state the final behaviour ADR-0057 says what is realized and states the stale-witness signal per language. The port pages, the changelog, the ledger spec and the corpus README are corrected where a review found them wrong or silent: the owned Program.cs advice for C#, the Python refusal of a project package under a standard-library name, how hooks and providers are imported, the Kotlin generator count, which loaders refuse a non-integer level, bare-name binding, and what verify does with metadata that does not load. Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057.
1 parent e0fc3f4 commit fff01da

325 files changed

Lines changed: 21927 additions & 319 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎AGENTS.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ MetaObjects is a **cross-language metadata standard** for declaring typed entity
66

77
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.
88

9-
**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.
9+
**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.
1010

1111
## Six pillars
1212

13-
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:
13+
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:
1414

1515
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.
1616
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.
1717
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.
1818
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`.
19-
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.
19+
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.
2020

2121
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`.
2222

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

7777
**Cross-port conformance corpora** (every port runs the shared corpus):
78-
- 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.
78+
- 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.
7979
- Render: `fixtures/render-conformance/`. TS / C# / Java / Kotlin / Python byte-identical.
8080
- 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".)
8181
- 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.

0 commit comments

Comments
 (0)