diff --git a/CLAUDE.md b/CLAUDE.md index a4f0862..9b81491 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,12 +35,12 @@ what gets fixed. Before changing anything under `docs/adr/` or `spec/v1/`: - Read [`docs/adr/README.md`](docs/adr/README.md) for the register, the citation - rule and the project table, and `docs/adr/model/0003`–`0006` for the model's + rule and the domain table, and `docs/adr/model/0003`–`0006` for the model's premises. -- `docs/adr/` carries **one directory per project**: `model/` (v1 model, +- `docs/adr/` carries **one directory per decision domain**: `model/` (v1 model, pointers into `spec/v1`), `architecture/` (the compiler's own structure, pointers into `docs/architecture.md`), `deferred/` (not linted). Numbers run - in one estate-wide sequence, so never reuse a number from another project. + in one estate-wide sequence, so never reuse a number from another domain. - Run `npm run lint:adrs`. It enforces frontmatter schema, register integrity, qualified citations (a bare `ADR-` token outside a link fails), normative anchors resolving against real headings in `spec/v1`, and content shape. @@ -55,17 +55,18 @@ Delivery mechanics and co-testing are **defined separately**: see applier, a prune pass, a field manager or a co-test gate in `spec/v1`; state the model-level rule and point at the deferred set. -### The coursework implementation under `emf/` +### The model-driven implementation under `emf/` The MDE course requires the compiler to be built with Ecore, Xtext, OCL, -QVT-Operational and Acceleo, so `emf/` holds a second, hand-written Java +QVT-Operational and Acceleo, so `emf/` holds the model-driven implementation: +a second, hand-written Java implementation beside the TypeScript production implementation (Maven and Tycho, JDK 21, no Eclipse IDE to build, but loadable in Eclipse for the course's examiners). It is deprecated from the day it lands and deleted at its sunset: - **The root stays TypeScript.** Every pom, module, check, ledger and decision of the Java side lives under `emf/`. The root references it only from CI - (the `emf` job and the `emf` ADR lint step), the `emf` project in + (the `emf` job and the `emf` ADR lint step), the `emf` domain in `scripts/lint-adrs.ts` and its test, and [`docs/architecture.md#the-parity-contract`](docs/architecture.md#the-parity-contract). - **Never generate one implementation from the other.** Both are tested, diff --git a/README.md b/README.md index 76e370e..da4e07b 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ lands, the **compiler** that turns that model into deployable artifacts. | --- | --- | | [`CONTEXT.md`](CONTEXT.md) | The vocabulary. One term, one meaning; also the naming authority for code. | | [`docs/architecture.md`](docs/architecture.md) | Normative for code structure, the way `spec/v1` is normative for the model. | -| [`docs/adr/`](docs/adr/README.md) | The decision surface, one directory per project. Machine-checked. | +| [`docs/adr/`](docs/adr/README.md) | The decision surface, one directory per decision domain. Machine-checked. | | [`docs/adr/model/`](docs/adr/model/) | The v1 model: 8 premises carrying falsifiable claims, 43 decisions resting on them. | | [`docs/adr/architecture/`](docs/adr/architecture/README.md) | The compiler's own structure. Pointers resolve against `docs/architecture.md`, not `spec/v1`. | | [`docs/adr/deferred/`](docs/adr/deferred/README.md) | Delivery and co-testing decisions, defined separately from the model. Direction work, not v1. | diff --git a/docs/adr/architecture/0065-one-hexagon-domain-mirrors-the-layers.md b/docs/adr/architecture/0065-one-hexagon-domain-mirrors-the-layers.md index de69a29..c163af3 100644 --- a/docs/adr/architecture/0065-one-hexagon-domain-mirrors-the-layers.md +++ b/docs/adr/architecture/0065-one-hexagon-domain-mirrors-the-layers.md @@ -45,7 +45,7 @@ folders after the layers is not decoration. It means a reviewer reading `src/domain/resolved/` are looking at the same thing, and a rule that lands in the wrong ring is visible as a wrong import rather than as a wrong idea. -The two runtimes in [chapter 30](../../../spec/v1/30-deliverables.md#adapters) (five fragment producers in the Project repository, eleven central adapters over +The two runtimes in [chapter 30](../../../spec/v1/30-deliverables.md#adapters) (five fragment producers in the project repository, eleven central adapters over the union) are the reason to be careful here. They differ in *what documents they receive*, not in what an Application means. One core with two use-cases keeps the invariants in one place; two applications would put them in a third package diff --git a/docs/adr/architecture/0105-two-implementations-meet-at-committed-oracles.md b/docs/adr/architecture/0105-two-implementations-meet-at-committed-oracles.md index ab9167b..e8f2227 100644 --- a/docs/adr/architecture/0105-two-implementations-meet-at-committed-oracles.md +++ b/docs/adr/architecture/0105-two-implementations-meet-at-committed-oracles.md @@ -10,6 +10,14 @@ rests-on: ["0003", "0006"] # Two hand-written implementations meet at committed oracle files, and neither is generated from or tested against the other +> **Amended 2026-09-14.** Aligned with the revised project proposal +> ([#102](https://github.com/JorisJonkers-dev/deploy-kit/pull/102)): the TypeScript code is the production implementation and `emf/` the +> model-driven implementation. They are compared on validation, dependency +> resolution, errors and generated resources. `resolved.json` binds the +> production implementation only, because the model-driven target metamodel +> holds the Resolved Deployment and the typed resources as one model; a new +> dependency-edge oracle carries the part of resolution both must agree on. + ## Rests on The three layers give three points where a run's state is a complete, closed document, and pinned inputs make each of those documents a function of the diff --git a/docs/architecture.md b/docs/architecture.md index 6df72fe..89ab4b8 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -188,26 +188,41 @@ clean tree is untested: nothing proves it would fail. ## The parity contract -This compiler has a second implementation. The model-driven engineering course -this repository is coursework for requires Ecore, Xtext, OCL, QVT-Operational -and Acceleo, so a Java implementation lives under [`emf/`](../emf/README.md) +This compiler has two implementations. The TypeScript code under `src/` is the +**production implementation**. The model-driven engineering course this +repository is coursework for requires Ecore, Xtext, OCL, QVT-Operational and +Acceleo, so a **model-driven implementation** in Java lives under +[`emf/`](../emf/README.md) until its sunset condition holds ([0105](adr/architecture/0105-two-implementations-meet-at-committed-oracles.md)). Everything about that implementation, its build, its checks and its decisions, lives inside `emf/`. What lives here is the contract both implementations -answer to, because the contract outlives the second implementation. +answer to, because the contract outlives the model-driven implementation. Neither implementation is generated from the other, and neither is the oracle for the other. Each is tested on its own against committed oracle files, and agreement between the two follows from both agreeing with the oracle. -| oracle | where | compared as | -|---|---|---| -| the parsed Project Intent | `spec/v1/examples//expected/intent.json` | canonical JSON, byte for byte | -| the Resolved Deployment | `spec/v1/examples//expected/resolved.json` | canonical JSON, byte for byte | -| the Deliverable Set | `spec/v1/examples//rendered/` | the existing golden tree, byte for byte | -| the diagnostics of a refused case | `.diagnostics.json` beside the refused input in `refusals/` or `negative/` | a set of `(code, path)` pairs | -| the metamodel's structure | `spec/v1/examples/expected/descriptor.json` | canonical JSON, byte for byte | +The two are compared on what the project proposal names: validation, dependency +resolution, errors and generated resources. They are not required to agree on +anything else. In particular their intermediate models differ: the production +implementation keeps the Resolved Deployment and the typed object model its +adapters build as two things, while the model-driven implementation has one +target metamodel holding both, because a model-to-text template reads one model. + +| oracle | where | compared as | binds | +|---|---|---|---| +| the parsed Project Intent (validation) | `spec/v1/examples//expected/intent.json` | canonical JSON, byte for byte | both | +| the diagnostics of a refused case (errors) | `.diagnostics.json` beside the refused input in `refusals/` or `negative/` | a set of `(code, path)` pairs | both | +| the resolved dependency edges (dependency resolution) | `spec/v1/examples//expected/dependencies.json` | canonical JSON, byte for byte | both | +| the Deliverable Set (generated resources) | `spec/v1/examples//rendered/` | the existing golden tree, byte for byte | both | +| the source metamodel's structure | `spec/v1/examples/expected/descriptor.json` | canonical JSON, byte for byte | both | +| the Resolved Deployment | `spec/v1/examples//expected/resolved.json` | canonical JSON, byte for byte | production only | + +**The dependency edges** are, per Application, every dependency edge after +resolution: the consumer, the provider Application and Surface, the address the +consumer is given, and the policy peers that allow the connection. It is the +part of resolution both implementations must agree on before either renders. **Canonical JSON** is RFC 8785 (JSON Canonicalization Scheme): keys sorted, numbers in their shortest form, no insignificant whitespace. An absent @@ -218,8 +233,10 @@ optional field is absent, never `null`. points at the authored value it derives from. Messages and hints are free per implementation; the code and the path are the contract. -**The descriptor** lists every class of every layer with its features, each +**The descriptor** lists every class of the source metamodel (Project Intent +and the platform data it is resolved against) with its features, each feature's type and multiplicity, and every closed vocabulary with its literals. +Target structures are compared through what they generate, not structurally. The TypeScript side builds it from the Zod schemas with Zod's native `z.toJSONSchema()` and a normaliser; the descriptor's shape is fixed here, not by either source format. @@ -231,8 +248,8 @@ of the model's validation, whichever implementation happens to enforce it. **Behaviour rows.** A row of the [behaviour ledger](requirements.md) whose behaviour is the model's own (parse, validate, resolve, render) is proved in -both implementations. The row names the TypeScript test; the Java witness for -the same id is listed inside `emf/`, and `emf/`'s own gate fails when a model +both implementations. The row names the TypeScript test; the model-driven witness +for the same id is listed inside `emf/`, and `emf/`'s own gate fails when a model row has no witness there. An oracle file changes in the pull request that changes the behaviour it diff --git a/docs/mde/README.md b/docs/mde/README.md index 6221f4d..867ed69 100644 --- a/docs/mde/README.md +++ b/docs/mde/README.md @@ -14,7 +14,7 @@ One directory per deadline, named after the course's own task numbering. | Directory | Deliverable | Overleaf project | State | |---|---|---|---| -| [`task-0-proposal/`](task-0-proposal/) | Task 0 — project description, the proposal that must be approved before work starts | `6aa2ba0c3920baa59ec20901` | re-mirrored 2026-09-14, after the joint-implementation revision | +| [`task-0-proposal/`](task-0-proposal/) | Task 0 — project description, the proposal that must be approved before work starts | `6aa2ba0c3920baa59ec20901` | re-mirrored 2026-09-14, after the simplification in #102 | | `task-1-metamodelling/` | Task 1 — domain analysis, modelling decisions, the two metamodels and their diagrams, example models | — | not started | | `task-2-transformations/` | Task 2 — the model-to-model transformation, its rules and limitations, evidence it runs | — | not started | | `task-3-code-generation/` | Task 3 — the model-to-text transformation, the generated code, evidence it loads | — | not started | diff --git a/emf/README.md b/emf/README.md index 451b194..929ed9d 100644 --- a/emf/README.md +++ b/emf/README.md @@ -1,7 +1,8 @@ -# emf: the coursework implementation +# emf: the model-driven implementation -Everything under this directory is the second implementation of the -deploy-kit compiler, built with the Eclipse Modeling Framework toolchain the +Everything under this directory is the model-driven implementation of the +deploy-kit compiler, the second implementation beside the TypeScript production +implementation, built with the Eclipse Modeling Framework toolchain the model-driven engineering course at the University of Twente requires: Ecore, Xtext, OCL, QVT-Operational and Acceleo. It is **deprecated from the day it lands**: it exists for the length of the course and is deleted when its sunset @@ -14,7 +15,7 @@ ledgers and its decisions all live here, so the sunset is deleting this directory and the few root lines that name it, listed in [the architecture](docs/architecture.md#scope-and-sunset). -The one thing this implementation shares with the TypeScript compiler is the +The one thing this implementation shares with the production implementation is the [parity contract](../docs/architecture.md#the-parity-contract): the committed oracle files under `spec/v1/examples/` that both are tested against, separately. diff --git a/emf/docs/adr/README.md b/emf/docs/adr/README.md index 635f210..3492961 100644 --- a/emf/docs/adr/README.md +++ b/emf/docs/adr/README.md @@ -1,11 +1,11 @@ -# Decision register: the coursework implementation +# Decision register: the model-driven implementation The decisions that shape the EMF implementation under `emf/`. Every one is deleted with this directory ([0107](emf/0107-emf-is-coursework-scoped-and-self-contained.md)), and none of them can change what the model means: the model's decisions are in [`docs/adr/`](../../../docs/adr/README.md), and the contract this -implementation shares with the TypeScript compiler is +implementation shares with the production implementation is [0105](../../../docs/adr/architecture/0105-two-implementations-meet-at-committed-oracles.md). The contract every ADR here satisfies is the root register's, unchanged: @@ -35,7 +35,7 @@ taking a number, check both registers. |---|---|---| | [0107](emf/0107-emf-is-coursework-scoped-and-self-contained.md) | The EMF implementation is coursework-scoped, lives entirely under `emf/`, and is deleted when two conditions hold | open | | [0108](emf/0108-maven-and-tycho-against-a-pinned-target-platform.md) | Maven builds `emf/`, with Tycho resolving p2-only bundles against a pinned target platform, on JDK 21, with no Eclipse IDE | open | -| [0109](emf/0109-ecore-metamodels-are-hand-written-per-document.md) | Each model document has a hand-written Ecore metamodel, and its Java is generated at build time | open | +| [0109](emf/0109-source-and-target-metamodels-are-hand-written.md) | The source and target metamodels are hand-written in Ecore, and their Java is generated at build time | open | | [0110](emf/0110-constraints-are-complete-ocl-named-by-code.md) | Constraints are Complete OCL invariants named by the diagnostic code they emit | open | | [0111](emf/0111-xtext-parses-the-authored-yaml-into-the-metamodel.md) | The Xtext grammar parses the authored YAML files themselves, into the imported metamodel | open | | [0112](emf/0112-qvto-derives-the-resolved-deployment.md) | QVT-Operational derives the Resolved Deployment | open | diff --git a/emf/docs/adr/emf/0108-maven-and-tycho-against-a-pinned-target-platform.md b/emf/docs/adr/emf/0108-maven-and-tycho-against-a-pinned-target-platform.md index 1bc8a8c..32d9f04 100644 --- a/emf/docs/adr/emf/0108-maven-and-tycho-against-a-pinned-target-platform.md +++ b/emf/docs/adr/emf/0108-maven-and-tycho-against-a-pinned-target-platform.md @@ -13,8 +13,9 @@ rests-on: ["0106"] > **Amended 2026-09-14.** No Eclipse IDE is needed to build, and CI never > uses one. The projects must still be loadable in Eclipse Modeling Tools for > the course's examiners, as the Task 0 proposal now promises: they import as -> existing Maven projects, the metamodels open and the example models validate -> against the OCL constraints, and committed launch configurations run the +> existing Maven projects, the metamodels open, the Xtext-generated editor +> reports OCL constraint violations while editing, and committed launch +> configurations run the > QVT-Operational transformation and the Acceleo generator. Keeping that > working is part of this decision; the Maven build stays the only path CI > checks. diff --git a/emf/docs/adr/emf/0109-ecore-metamodels-are-hand-written-per-document.md b/emf/docs/adr/emf/0109-source-and-target-metamodels-are-hand-written.md similarity index 63% rename from emf/docs/adr/emf/0109-ecore-metamodels-are-hand-written-per-document.md rename to emf/docs/adr/emf/0109-source-and-target-metamodels-are-hand-written.md index 548fa45..45a90c2 100644 --- a/emf/docs/adr/emf/0109-ecore-metamodels-are-hand-written-per-document.md +++ b/emf/docs/adr/emf/0109-source-and-target-metamodels-are-hand-written.md @@ -8,7 +8,15 @@ normative: docs/architecture.md#metamodels rests-on: ["0106"] --- -# Each model document has a hand-written Ecore metamodel, and its Java is generated at build time +# The source and target metamodels are hand-written in Ecore, and their Java is generated at build time + +> **Amended 2026-09-14.** Aligned with the revised project proposal +> ([#102](https://github.com/JorisJonkers-dev/deploy-kit/pull/102)). This decision originally gave each model document its own package: Project +> Intent, Platform Intent, Resolved Deployment and Deliverable Set. The +> proposal defines two metamodels, so there are two packages: a source +> metamodel holding Project Intent and the Platform document, and a target +> metamodel holding the Resolved Deployment with its typed Kubernetes and +> extension resources. The descriptor covers the source metamodel only. > **Amended 2026-09-14.** Vocabulary renamed by > [0116](../../../../docs/adr/model/0116-project-application-process.md): Domain is now Project, @@ -17,9 +25,11 @@ rests-on: ["0106"] ## Rests on Resting on [0106](0106-the-model-is-expressible-in-the-emf-toolchain.md), the -claim is that Project Intent, Platform Intent, the Resolved Deployment and the -Deliverable Set each fit one Ecore package whose structure, exported as the -parity descriptor, equals the structure the Zod schemas in `src/` declare. +claim is that the source metamodel (Project Intent, with the Platform document +it is resolved against) fits one Ecore package whose structure, exported as the +parity descriptor, equals the structure the Zod schemas in `src/` declare, and +that the target metamodel (the Resolved Deployment with its typed resources) +fits a second package the templates can walk. False if: the descriptor exported from Ecore cannot equal the committed descriptor without a type, multiplicity or vocabulary that the TypeScript side does not have. Settled by: the descriptor parity suite green in both @@ -32,9 +42,14 @@ the transformation maps between its packages, and the templates walk it. It is written by hand because the course grades authored metamodels and because generating it from Zod would make its structure unfalsifiable against Zod. -One package per document follows the model's own layering -([0003](../../../../docs/adr/model/0003-three-layer-meta-model.md)), with -Platform Intent separate because it is a separately authored document. +The proposal defines two metamodels, a source and a target, and the course +grades them as such. The model's three layers +([0003](../../../../docs/adr/model/0003-three-layer-meta-model.md)) still hold: +the Deliverable Set is the generated files, so it has no metamodel, and the +target metamodel carries layer 2 together with the typed resources the files +are written from, because an Acceleo template reads one model. The Platform +document is part of the source package because the transformation resolves a +Project against it. Cross-document references are Ecore references, so the transformation navigates them rather than joining strings. diff --git a/emf/docs/adr/emf/0110-constraints-are-complete-ocl-named-by-code.md b/emf/docs/adr/emf/0110-constraints-are-complete-ocl-named-by-code.md index 563ec4d..3d9b924 100644 --- a/emf/docs/adr/emf/0110-constraints-are-complete-ocl-named-by-code.md +++ b/emf/docs/adr/emf/0110-constraints-are-complete-ocl-named-by-code.md @@ -10,6 +10,12 @@ rests-on: ["0106"] # Constraints are Complete OCL invariants named by the diagnostic code they emit +> **Amended 2026-09-14.** Aligned with the revised project proposal +> ([#102](https://github.com/JorisJonkers-dev/deploy-kit/pull/102)). The same Complete OCL constraints also run in the Xtext-generated editor, +> which marks each violation while a source file is edited. Diagnostics in the +> build keep the code and JSON Pointer contract; the editor shows the code as +> the message. + ## Rests on Resting on [0106](0106-the-model-is-expressible-in-the-emf-toolchain.md), the claim is that every constraint in the constraint ledger is expressible as an diff --git a/emf/docs/adr/emf/0111-xtext-parses-the-authored-yaml-into-the-metamodel.md b/emf/docs/adr/emf/0111-xtext-parses-the-authored-yaml-into-the-metamodel.md index 309c6ba..a67c95a 100644 --- a/emf/docs/adr/emf/0111-xtext-parses-the-authored-yaml-into-the-metamodel.md +++ b/emf/docs/adr/emf/0111-xtext-parses-the-authored-yaml-into-the-metamodel.md @@ -10,6 +10,12 @@ rests-on: ["0106"] # The Xtext grammar parses the authored YAML files themselves, into the imported metamodel +> **Amended 2026-09-14.** Aligned with the revised project proposal +> ([#102](https://github.com/JorisJonkers-dev/deploy-kit/pull/102)). Xtext also generates the editor that examiners use in Eclipse, configured to +> run the OCL validators and report constraint violations during editing. The +> grammar imports the one source metamodel, which now holds the Platform +> document as well. + > **Amended 2026-09-14.** Vocabulary renamed by > [0116](../../../../docs/adr/model/0116-project-application-process.md): Domain is now Project, > Service is Application, Workload is Process, and Service Intent is Project @@ -19,7 +25,7 @@ rests-on: ["0106"] Resting on [0106](0106-the-model-is-expressible-in-the-emf-toolchain.md), the claim is that the YAML subset every authored example uses can be parsed by an Xtext grammar with synthetic indentation tokens, producing instances of the -hand-written Project Intent and Platform Intent metamodels. False if: an +hand-written source metamodel. False if: an authored example needs YAML the grammar cannot parse without ambiguity, or the parsed model serialises to an `intent.json` different from the committed one. Settled by: the parsed-intent parity suite green for every authored file under diff --git a/emf/docs/adr/emf/0112-qvto-derives-the-resolved-deployment.md b/emf/docs/adr/emf/0112-qvto-derives-the-resolved-deployment.md index f0ca7fb..7578ffe 100644 --- a/emf/docs/adr/emf/0112-qvto-derives-the-resolved-deployment.md +++ b/emf/docs/adr/emf/0112-qvto-derives-the-resolved-deployment.md @@ -10,14 +10,22 @@ rests-on: ["0106"] # QVT-Operational derives the Resolved Deployment +> **Amended 2026-09-14.** Aligned with the revised project proposal +> ([#102](https://github.com/JorisJonkers-dev/deploy-kit/pull/102)). The transformation targets the model-driven Resolved Deployment metamodel, +> which holds typed resources, so its output is not compared with +> `resolved.json`. The two implementations are compared on the resolved +> dependency edges and on the generated files instead, as the proposal names +> them. + ## Rests on Resting on [0106](0106-the-model-is-expressible-in-the-emf-toolchain.md), the claim is that every derivation and assignment chapter 20 defines, including the ones over the whole composed union, is expressible as QVTo mappings and helpers run by the standalone executor. False if: a derivation needs Java black-box -code for anything but hashing, or the transformation's output serialises to a -`resolved.json` different from the committed one. Settled by: the resolved -parity suite green for every case under `spec/v1/examples/`. +code for anything but hashing, or the transformation's output yields dependency +edges or generated files different from the committed ones. Settled by: the +dependency-edge and rendered parity suites green for every case under +`spec/v1/examples/`. ## Why The course teaches ATL and QVT-Operational for model-to-model transformation. @@ -41,5 +49,5 @@ Undo cost today: nothing exists. Becomes irreversible once: never; deleted with ## Consequences - Hashing for `renderHash` and content-hashed Assets is a black-box Java helper, the one allowed. Paid once. -- A derivation the transformation lacks shows up as a resolved parity failure, - not a silent default. Paid by the author of the derivation. +- A derivation the transformation lacks shows up as a dependency-edge or + rendered parity failure, not a silent default. Paid by the author of the derivation. diff --git a/emf/docs/adr/emf/0113-acceleo-4-renders-the-deliverable-set.md b/emf/docs/adr/emf/0113-acceleo-4-renders-the-deliverable-set.md index 9fed94a..9720f74 100644 --- a/emf/docs/adr/emf/0113-acceleo-4-renders-the-deliverable-set.md +++ b/emf/docs/adr/emf/0113-acceleo-4-renders-the-deliverable-set.md @@ -10,6 +10,11 @@ rests-on: ["0106"] # Acceleo 4 renders the Deliverable Set, byte for byte against the committed tree +> **Amended 2026-09-14.** Aligned with the revised project proposal +> ([#102](https://github.com/JorisJonkers-dev/deploy-kit/pull/102)). The templates read a Resolved Deployment model of the target metamodel, +> which already holds the typed resources; the Deliverable Set itself has no +> metamodel. + ## Rests on Resting on [0106](0106-the-model-is-expressible-in-the-emf-toolchain.md), the claim is that Acceleo 4 templates run standalone can reproduce every committed diff --git a/emf/docs/architecture.md b/emf/docs/architecture.md index 8ed62b9..28f8d64 100644 --- a/emf/docs/architecture.md +++ b/emf/docs/architecture.md @@ -1,4 +1,4 @@ -# The coursework implementation's structure +# The model-driven implementation's structure Normative for the code under `emf/`, the way [`docs/architecture.md`](../../docs/architecture.md) is normative for `src/`. @@ -18,8 +18,8 @@ grades it. | course task | due | lands here | |---|---|---| -| Task 1: metamodelling | 2026-09-25 | Ecore metamodels for all three layers and Platform Intent, the Xtext grammar, the OCL constraints; parity on the parsed intent, the descriptor and refused cases | -| Task 2: transformations | 2026-10-16 | the QVT-Operational transformation into the Resolved Deployment; parity on `resolved.json` | +| Task 1: metamodelling | 2026-09-25 | the two Ecore metamodels, the Xtext grammar and its editor, the OCL constraints; parity on the parsed intent, the descriptor and refused cases | +| Task 2: transformations | 2026-10-16 | the QVT-Operational transformation into the Resolved Deployment; parity on the resolved dependency edges | | Task 3: code generation | 2026-10-30 | the Acceleo templates rendering the Deliverable Set; parity on `rendered/` | The tree is deleted in one pull request when both of these hold: @@ -43,9 +43,9 @@ Maven Central coordinates are used wherever a tool publishes there. JDK 21. No Eclipse IDE, workspace or launch configuration is part of the build: every step CI runs is `mvn verify` from `emf/`. The projects are nonetheless kept loadable in Eclipse Modeling Tools for the course's examiners: they import as -existing Maven projects, the metamodels open and the example models validate -against the OCL constraints, and committed launch configurations run the -transformation and the generator. +existing Maven projects, the metamodels open, the Xtext-generated editor reports +OCL constraint violations while a source file is edited, and committed launch +configurations run the transformation and the generator. The first change to this tree is a walking skeleton that proves each tool runs headless in CI before any model work depends on it: an `.ecore` loads, an OCL @@ -59,10 +59,10 @@ Tycho configuration and the target platform. | module | holds | graded in | |---|---|---| -| `metamodel/` | `.ecore` and `.genmodel` per layer, Complete OCL `.ocl` per layer, the descriptor exporter | Task 1 | -| `syntax/` | the Xtext grammar for the authored YAML subset | Task 1 | +| `metamodel/` | the source and target `.ecore` and `.genmodel`, Complete OCL `.ocl` for the source metamodel, the descriptor exporter | Task 1 | +| `syntax/` | the Xtext grammar for the authored YAML subset, and the generated editor bundles that run the OCL validators | Task 1 | | `resolve/` | the QVTo transformation from Project Intent and Platform Intent to the Resolved Deployment | Task 2 | -| `render/` | the Acceleo 4 templates from the Resolved Deployment to the Deliverable Set | Task 3 | +| `render/` | the Acceleo 4 templates from a Resolved Deployment model to the Deliverable Set's files | Task 3 | | `cli/` | the pipeline entry point: files in, canonical JSON, diagnostics and rendered files out | Task 1 onward | | `parity/` | JUnit suites asserting each stage against the committed oracles, and the witness ledger check | Task 1 onward | @@ -71,23 +71,38 @@ below. ## Metamodels -One Ecore metamodel per document the model defines: Project Intent, Platform -Intent, Resolved Deployment and Deliverable Set. Each is hand-written `.ecore` -XMI, committed, with names taken unchanged from -[`CONTEXT.md`](../../CONTEXT.md). Cross-document references are Ecore -references across packages, not strings. +Two hand-written Ecore metamodels, as the project proposal defines them. Both +are committed `.ecore` XMI, with names taken unchanged from +[`CONTEXT.md`](../../CONTEXT.md). + +| metamodel | role | holds | +|---|---|---| +| Project Intent | source | the authored Project, Application and Process with everything layer 1 declares, and the Platform document the source is resolved against | +| Resolved Deployment | target | every derived value of layer 2 together with the typed Kubernetes and extension resources, identities and output paths the templates write | + +The Deliverable Set is not a metamodel: it is the files Acceleo generates from +a Resolved Deployment model. The target metamodel is deliberately not the shape +of the production implementation's Resolved Deployment, which keeps layer 2 in +model words and builds typed objects separately; a model-to-text template reads +one model, so here the two are one package. This is why the parity contract +compares the two implementations through dependency edges and generated files +rather than through `resolved.json`. + +Cross-document references, including those from a Project into the Platform +document, are Ecore references, not strings. Typed Java for each metamodel is generated from its `.genmodel` during the Maven build into `target/`, and never committed. -The descriptor exporter walks each `EPackage` reflectively and writes the +The descriptor exporter walks the source `EPackage` reflectively and writes the descriptor the parity contract fixes. It is the only place the Ecore structure is compared with anything. ## Constraints -Every constraint lives in a Complete OCL file beside the metamodel it -constrains, one file per layer, evaluated by Eclipse OCL standalone. An +Every constraint lives in a Complete OCL file beside the source metamodel, +evaluated by Eclipse OCL standalone in the build and by the Xtext-generated +editor while a file is edited. An invariant's name is the diagnostic code it emits, so a failed invariant maps to a diagnostic without a lookup table. @@ -108,23 +123,31 @@ The Xtext grammar parses the same authored `.project.yml` and subset those files use, with indentation handled by synthetic block tokens, and refuses anything outside the subset with a diagnostic rather than a guess. -The grammar imports the hand-written Project Intent and Platform Intent -metamodels, so the parser produces instances of the graded metamodel directly. -There is no inferred syntax metamodel and no mapping step between parsing and -validation. +The grammar imports the hand-written source metamodel, so the parser produces +instances of the graded metamodel directly. There is no inferred syntax +metamodel and no mapping step between parsing and validation. + +The generated editor is configured to run the OCL validators and to mark each +constraint violation on the source line it concerns, with the diagnostic code as +its message. It is built by the same Maven and Tycho build as an Eclipse plugin; +CI builds it but never runs it. ## Transformation -A QVT-Operational transformation derives the Resolved Deployment from the -parsed Project Intent and Platform Intent, run through the standalone -transformation executor. Every derivation `spec/v1/20-resolved-deployment.md` -names is a mapping or a helper in it; a derived value with no mapping is a gap -the parity case for it exposes. +A QVT-Operational transformation derives a Resolved Deployment model from the +parsed Project Intent model, run through the standalone transformation +executor. Every derivation `spec/v1/20-resolved-deployment.md` names is a mapping +or a helper in it; a derived value with no mapping is a gap a parity case +exposes, through the dependency edges or the generated files. The resolved +dependency edges are exported from the target model as canonical JSON and +compared with `expected/dependencies.json`. ## Text generation -Acceleo 4 templates render the Deliverable Set from the Resolved Deployment, -run through Acceleo's standalone API. Output is compared byte for byte with the +Acceleo 4 templates generate the Deliverable Set's YAML and JSON files from a +Resolved Deployment model, run through Acceleo's standalone API. Until the +transformation covers a case, a template test reads a hand-written Resolved +Deployment model kept inside `emf/`; it is test input, never an oracle. Output is compared byte for byte with the committed `rendered/` tree, so whitespace, key order and the `GENERATED` header are template decisions made to match the oracle, not presentation.