Skip to content

feat: parse the minimal project intent in both implementations and match its oracle - #112

Merged
ExtraToast merged 1 commit into
mainfrom
feat/83-minimal-intent-parity
Sep 15, 2026
Merged

ExtraToast merged 1 commit into
mainfrom
feat/83-minimal-intent-parity

Conversation

@ExtraToast

Copy link
Copy Markdown
Contributor

Closes #83 (parent #78). First of three pull requests rewriting #77: this one carries the minimal case through both implementations; #84 widens the language to every accepted example; #85 adds the refusals and closes #38.

What this branch does

minimal's authored project file now parses in both implementations, and both are compared with the committed expected/intent.json byte for byte.

TypeScript, the production implementation

  • src/wire/project-intent/: the Zod schema for the fields minimal uses, a YAML reader that accepts one document with no anchors, aliases or explicit tags, and the mapper into the domain.
  • src/domain/project-intent/: the domain model and the closed vocabularies, declared once and enumerated by the schema.
  • src/application/parse-project-intent.ts: text in, a Result of the validated document and the domain model out, or schema diagnostics carrying an RFC 6901 JSON Pointer.

Java, the model-driven implementation

  • emf/metamodel/: project-intent.ecore, hand-written, with its .genmodel; an MWE2 workflow runs EMF's EcoreGenerator in generate-sources, so the typed Java lands under target/ and is never committed.
  • emf/syntax/: the Xtext grammar for the YAML subset, importing that metamodel, so the parser yields instances of the graded model directly. Indentation is handled by a token source: a deeper line opens a block, a dash opens one around the item that follows it, and a flow collection opens and closes one on a line, so { path: /, match: prefix } and the indented form parse through one rule. Keys are unordered groups.
  • emf/cli/: the pipeline entry, and the reflective reader that turns a parsed model into the intent JSON. A feature is named as the authored key it holds, a map entry is written as an object, an enumeration as its literal, and an optional feature the document left out is absent.
  • emf/parity/: the parity suite, table-driven over every case that carries an intent oracle. It is now an Eclipse bundle, because it depends on cli.

Decisions worth naming

The metamodel is the authored shape, not the domain shape. The Java side compares intent.json, which is the authored document canonicalised, so its features carry authored names. The TypeScript side keeps Zod for that shape and a mapper into a domain model that renames (project to name, exposure to exposures, provides to a map). Both agree on the oracle, which is what the contract asks.

The Xtext language binds .yml, not .project.yml. EMF resolves a factory by the last extension, so a two-segment extension registers something nothing looks up. Routing by file name lands with the Platform document in #84.

Mutation testing now runs on src/. Stryker, with the break threshold at the measured score (100%), a Mutation CI job, RULE-064 and REQ-022. The runner config lists the suites that reach src/: the oracle suite reads the git index, which Stryker's sandbox does not have, and is named with its reason in test/mutation-contract.test.ts.

Generated code is excluded by naming each generated package. JaCoCo's * reaches across package separators, so the first exclusion glob silently excluded the hand-written token source too, and the module reported 0% with a green agent. The agent takes class names and the report takes file paths, so each is configured on its own execution.

Verification

  • ./mvnw clean verify in emf/: 76 tests, line coverage 100.0%, mutation score 100.0% (169 of 169).
  • npm run verify: 422 tests, src/ at 100% statements, branches, functions and lines; Stryker 158 of 158 mutants killed. Coverage ratchet raised to statements 98.46, branches 93.44, functions 100, lines 98.35, with README.md matching.
  • Both implementations produce minimal's committed expected/intent.json byte for byte, and each suite carries the negative case: one changed field no longer matches the oracle.
  • The Ecore smoke test from the walking skeleton is deleted, and so is the Xtext one; the OCL smoke test stays until Refuse documents with OCL, addressed by JSON Pointer #85.

What this does not do yet

auth, data, knowledge and the platform files (#84), the descriptor and the committed JSON Schema (#84), the refusal fixtures and their diagnostics oracles (#85), and stateful's removal from the specification (#38, in #85).

@ExtraToast ExtraToast added type: feature New user-facing or operator-facing capability. area: deploy homelab-deploy, deploy-v2, manifests, and rollout flow. component: build Gradle, npm, packaging, or local build tooling. component: tests Unit, integration, system, or contract test concern. labels Sep 15, 2026
@ExtraToast ExtraToast self-assigned this Sep 15, 2026
@ExtraToast
ExtraToast merged commit aa8947b into main Sep 15, 2026
19 checks passed
@ExtraToast
ExtraToast deleted the feat/83-minimal-intent-parity branch September 15, 2026 08:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: deploy homelab-deploy, deploy-v2, manifests, and rollout flow. component: build Gradle, npm, packaging, or local build tooling. component: tests Unit, integration, system, or contract test concern. type: feature New user-facing or operator-facing capability.

Projects

None yet

1 participant