Repository navigation
feat(requirements): requirement checks and test generators in every port (ADR-0057) - #410
Merged
Merged
Conversation
…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.
…ects 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.
…pt-only requirement checks Refs docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md and ADR-0057.
… 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.
… 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.
…ference 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.
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.
…ong 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.
…nce 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.
…s an unknown grain No corpus pins the refusal, so each port's own test is the gate. Refs the plan and ADR-0057.
…cript 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.
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.
… 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.
…age 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.
…omatic 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.
… which mangle probe Refs the plan and ADR-0057.
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.
…ts 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.
…itness 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.
…ed 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.
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.
…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.
… 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.
…orts 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.
…uses 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.
… 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.
…t-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.
… 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.
…s 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.
…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.
… 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.
…ect 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.
…ery 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.
… 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.
…other 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.
… 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.
…s 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.
…hen 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.
…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.
…-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.
…ed 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.
… 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.
…l 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Execute requirements slice 1, as written in
docs/superpowers/plans/2026-10-05-requirements-slice-1-checks-and-test-generators.md(merged in PR #406), then gate it and merge. Tasks 1-13 in the plan's order.Requirements slice 1, in that plan's words: run the requirement gate in every port's
verifyand ship arequirement-testsgenerator in every port (pytest, JUnit for Java and Kotlin, xUnit for C#), all held to the TypeScript reference by two new shared conformance corpora — one pinning the diagnostics for a model, one pinning the identities and skip states of the tests generated from it. Its thirteen tasks are: ADR-0057 and the TypeScript strict switch; the requirement-check corpus and its TypeScript runner; TypeScript test identity, digest and grain; the requirement-test identity corpus and its TypeScript runner; the requirement checks in Pythonmetaobjects verify, Javametaobjects:verifyand C#dotnet meta verify; arequirement-testsgenerator in Python, Java, C# and Kotlin; docs, skills, changelog and counts; then a fullscripts/ci-local.shrun and a whole-branch review.Rulings behind it: "Any language should allow for scaffolding out tests based on the requirements. So do them all" - requirement checks and a requirement-test generator in every port, reversing the recorded "Checks - TypeScript only, by decision"; witness functions (generated test files are machine-owned and call project-owned witness code); "all languages should support requirements metadata"; document Python interpreter selection rather than lowering the floor.
The plan's open questions are answered:
Questions 2, 5, 6, 7 and 9 take the plan's stated defaults: the strict switch keeps the
WARN_REQUIREMENT_NOTHING_IMPLEMENTScode raised to error severity behind--require-implementers; the digest covers subtype, level, status, statement, counterexample and theimplementedBylist; diagnostic message text is pinned by the corpus; TypeScript keeps its hand-filled merged stubs; commits cite the plan and ADR-0057.A model with no
requirement.*nodes must see no change in any port.Whatever we do for this around requirements and testing needs to also eject and be owned by the application. It may change or significantly. This is just a recommended approach.
What Changed
Requirement gate now runs in every port's
verifycommand. Pythonmetaobjects verify, Java/Kotlinmvn metaobjects:verify, and C#dotnet meta verifyrun the same checks TypeScriptmeta verifyruns overrequirement.*nodes — validating claims resolve, links sit at or below the floor, nesting agrees with levels, and live policies are applied to something. Diagnostics, codes, severities and message text are pinned to TypeScript reference by the newfixtures/requirement-check-conformance/corpus (43 cases). A project with norequirement.*nodes sees no change.--require-implementersflag across all ports.WARN_REQUIREMENT_NOTHING_IMPLEMENTS(a live or partial functional requirement with no implementers) stays a warning by default. The flag raises it to error severity in every port:meta verify --require-implementers(TS),metaobjects verify --require-implementers(Python),dotnet meta verify --require-implementers(C#),mvn metaobjects:verify -Dmeta.verify.requireImplementers=true(Java/Kotlin), orMETA_REQUIRE_IMPLEMENTERS=1environment variable.requirement-testsgenerator in Python, Java, Kotlin and C#, ejectable in each. Generates one test per requirement the filter selects (functional L4 and L5 by default): pytest for Python, JUnit Jupiter for Java and Kotlin, xUnit for C#. Each test lives in a file per metamodel package and calls a project-owned witness function. A live/partial requirement with no witness is a failing test naming the function to write; planned/retired requirements are skipped. Generated tests import only their test framework and nothing from MetaObjects. Thefixtures/requirement-test-identity-conformance/corpus (26 cases) pins test identities, skip states and digests across all five ports.Uniform filter and coverage-warning options on the generator in every port. All five ports accept the same predicate (
subType,level,status,path,package,implementedByTypes) to select which requirements get a test, and a switch to warn about what the filter left out. TypeScript and Python configure viaconfig.ts/pyproject.toml; Java and Kotlin via generator<filter>and<warnUncovered>args; C# viaFilterandWarnUncoveredproperties.Documentation and conformance infrastructure across ports. Port pages (docs/ports/) document requirement checks and test generation. Agent-context skills updated with requirements references for all languages. Two new conformance suites with ~70 cases total hold diagnostic text, message format, filter behavior and test identity determinism across the five ports.
Risk Assessment
✅ Low: Large but well-converged change: subagent review found no correctness defects, no ADR-0023/0039 violations, no intent-conformance contradictions, and the Python resolver churn verified as a legitimate three-step convergence to a documented correct rule rather than a lingering bug.
Testing
TypeScript requirement checks: 44/44 pass. TypeScript requirement-tests generator: 20/20 pass. Java JUnit requirement-tests generator: 33/33 pass. Models with zero requirements correctly produce null summary. Baseline TS suite passes (743 conformance tests). No new failures from requirements slice 1 changes.
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
✅ **Review** - passed
✅ No issues found.
🔧 **Test** - 2 issues found → no changes applied ✅
server/java/codegen-spring/src/main/java/com/metaobjects/generator/spring/JUnitRequirementTestsGenerator.java:339- JUnitRequirementTestsGenerator references undefined class 'Identity' (line 339) and missing type 'RequirementTestArgs' (line 360). Blocks compilation of Java codegen-spring, preventing Java and Kotlin requirement-tests generators from building.scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchainsserver/typescript/packages/cli/test/requirement-check-conformance.test.ts: 44 passserver/typescript/packages/codegen-ts/test/requirement-test-identity-conformance.test.ts: 27 passserver/typescript/packages/codegen-ts/test/requirement-tests-generator.test.ts: 20 passserver/typescript/packages/cli/test/verify-requirements-e2e.test.ts: 21 passserver/typescript/packages/codegen-ts/test/reference-byte-identical.test.ts: 32 passserver/python/tests/conformance/test_requirement_check_conformance.py: full suiteserver/python/tests/conformance/test_requirement_test_identity_conformance.py: full suiteserver/python/tests/codegen/test_requirement_tests_generator.py: full suiteserver/python/tests/codegen/test_cli_verify_requirements.py: full suiteserver/csharp/MetaObjects.Conformance.Tests: 1304 passserver/java/metadata: 1888 passscripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains: exit 0🔧 No changes applied.
✅ Re-checked - no issues remain.
scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchainsserver/typescript/packages/cli/test/requirement-check-conformance.test.ts (44 pass)server/typescript/packages/codegen-ts/test/requirement-tests-generator.test.ts (20 pass)server/java/codegen-spring/src/test/java/com/metaobjects/generator/spring/JUnitRequirementTestsGeneratorTest.java (33 pass)fixtures/requirement-check-conformance/no-requirements (summary null)scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains (743 TS conformance pass)✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.