Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ loading. A port that reports `1.2` does not know that vocabulary; upgrade every

### Added

- **`maven` and `nuget` dependency transports (FR-023).** `meta deps sync` and `meta deps check` can
now read a shared model out of a published package that is already in the local cache, so a JVM or
.NET consumer no longer needs the publisher's directory on disk. `{ "name": "x", "maven":
"com.acme:model", "version": "1.0.0" }` reads the jar in the local Maven repository (honouring
`$MAVEN_REPO_LOCAL`, `-Dmaven.repo.local`, `settings.xml`'s `<localRepository>`, then
`~/.m2/repository`); `{ "name": "x", "nuget": "Acme.Model", "version": "1.0.0" }` reads the package
in the NuGet global packages folder (`$NUGET_PACKAGES`, else `~/.nuget/packages`). Both read the
manifest and artifact from the package's `metaobjects/` directory (`dir` changes it), write the same
snapshot and lock entry a `path` sync writes, and refuse a package that is not cached with the
coordinates and the command that fetches it (`mvn dependency:get`, `dotnet restore`); nothing is
downloaded. The config and lock readers in Java, Kotlin, Python and C# accept the new keys, and
their `check` reports such a dependency `unresolved` and names the Node `meta deps check`.
`dependency-conformance` gains 2 resolution cases and 18 transport cases.
- **Java, Kotlin and C#: publish a shared model (FR-023).** A `shared-model` generator in every one
of the three ports writes the artifact `<name>.metaobjects.json` and the manifest
`metaobjects.pkg.json`, byte-identical to the TypeScript `sharedModelFile()` and gated by
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ first-week wedge plan — and `meta init` picks up from there.
| Capability requirements (`requirement.*`) | `meta verify` gate + authoring lint; `requirementTests()` stubs | `mvn metaobjects:verify` gate; JUnit Jupiter `requirement-tests` | Gate via the Java Maven goal; JUnit Jupiter `requirement-tests` | `dotnet meta verify` gate; xUnit `requirement-tests` | `metaobjects verify` gate; pytest `requirement-tests` |
| Reporting vocabulary (`dimension` / `measure` / `segment` / `object.report`, FR-044) | Registered; a view-backed report becomes a SQL view in `meta migrate`, is read by `ObjectManager`, and gets generated read-only list routes and a TanStack list hook (no grid or form); the `cube-model` generator writes the vocabulary as Cube data-model files | Registered; OMDB reads a view-backed report; generates a read-only Spring controller, DTO, repository and filter allowlist | Registered (via Java); generates an Exposed table object, a row data class, a filter allowlist and a read-only Spring controller per view-backed report | Registered; generates a keyless EF Core row type, read-only routes and a filter allowlist per view-backed report | Registered; `ObjectManager` reads a view-backed report; generates a Pydantic row model, a read-only FastAPI router and a filter allowlist |
| Libraries (`libraries: [...]`) | Yes | Yes | Yes (via Java) | Yes | Yes |
| Metadata dependencies (`dependencies`) | Yes (`meta deps sync`: `path`, `npm` and `python` transports) | Yes (loads the synced snapshot; `-Dmeta.verify.mode=deps` checks drift; `meta deps sync` is Node) | Yes (via Java) | Yes (loads the synced snapshot) | Yes (loads the synced snapshot) |
| Metadata dependencies (`dependencies`) | Yes (`meta deps sync`: `path`, `npm`, `python`, `maven` and `nuget` transports) | Yes (loads the synced snapshot; `-Dmeta.verify.mode=deps` checks drift; `meta deps sync` is Node) | Yes (via Java) | Yes (loads the synced snapshot) | Yes (loads the synced snapshot) |
| Runtime metadata (ObjectManager-style) | Yes (`runtime-ts`) | Yes (OMDB) | Yes (via Java OMDB + Exposed) | Roadmap | Yes (ObjectManager) |
| React / Angular UI client (browser) | React: **published** (`@metaobjectsdev/react` + `@metaobjectsdev/tanstack`), codegen + runtime. Angular 18: **source-only by decision** ([ADR-0048](spec/decisions/ADR-0048-angular-tier-source-only.md)) — `@metaobjectsdev/angular` + `@metaobjectsdev/codegen-ts-angular` build in-repo on their own `0.6.x` line but are deliberately not on npm (`npm i @metaobjectsdev/angular` will 404) until they meet the ADR's promotion bar. Consume them from source. | Consumes TS client via REST | Consumes TS client via REST | Consumes TS client via REST | Consumes TS client via REST |
| Own a generator (`eject`) | Yes (`meta eject`) | Yes (`mvn metaobjects:eject`) | Yes (`mvn metaobjects:eject`) | Yes (`dotnet meta eject`) | Yes (`metaobjects eject`) |
Expand Down
23 changes: 12 additions & 11 deletions docs/CONFORMANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ regenerate with `ls -d fixtures/<corpus>/*/ | wc -l` for directory-shaped corpor
| [`fixtures/provider-composition-conformance/`](../fixtures/provider-composition-conformance/) | 18 (11 error-shape + 4 compose-load + 3 attr-ownership) | ✓ | ✓ | — (JVM registry via Java) | ✓ | ✓ |
| [`fixtures/source-resolution-conformance/`](../fixtures/source-resolution-conformance/) | 25 cases | ✓ (reference implementation) | ✓ | inherits via Java | ✓ | ✓ |
| [`fixtures/scope-conformance/`](../fixtures/scope-conformance/) | 10 cases | ✓ (reference implementation) | ✓ | inherits via Java | ✓ | ✓ |
| [`fixtures/dependency-conformance/`](../fixtures/dependency-conformance/) | 37 cases (26 resolution + 11 check) + 14 transport cases | ✓ (reference implementation; all three arms) | ✓ (resolution arm, all 26; check arm, all 11) | inherits via Java | ✓ (resolution arm, all 26; check arm, all 11, as `verify --deps`) | ✓ (resolution arm, all 26 identically; check arm, all 11, as `deps check` and `verify --deps`; the transport arm is Node-only) |
| [`fixtures/dependency-conformance/`](../fixtures/dependency-conformance/) | 39 cases (28 resolution + 11 check) + 32 transport cases | ✓ (reference implementation; all three arms) | ✓ (resolution arm, all 28; check arm, all 11) | inherits via Java | ✓ (resolution arm, all 28; check arm, all 11, as `verify --deps`) | ✓ (resolution arm, all 28 identically; check arm, all 11, as `deps check` and `verify --deps`; the transport arm is Node-only) |
| [`fixtures/shared-model-conformance/`](../fixtures/shared-model-conformance/) | 11 cases (7 publish + 4 refusal) | ✓ (reference implementation) | ✓ (all 11; `SharedModelConformanceTest`) | inherits via Java (`KotlinSharedModelGeneratorTest` pins the generator) | ✓ (all 11; `SharedModelConformanceTests`) | ✓ (serializer arm: the 7 publish cases' document bytes; no publisher) |
| [`fixtures/agent-context-conformance/`](../fixtures/agent-context-conformance/) | 4 | ✓ (the emitter is TS-owned) | — | — | — | — |
| [`fixtures/metamodel-docs/`](../fixtures/metamodel-docs/) | 1 | ✓ (docs emit is TS-owned) | — | — | — | — |
Expand Down Expand Up @@ -463,17 +463,17 @@ also matches before a trailing newline. Kotlin has no runner of its own: it inhe
the Java loader. Each of those two guards is pinned by a test beside the corpus, since
the corpus asserts matches only and carries no invalid-pattern case.

### `fixtures/dependency-conformance/` (37 cases + 14 transport cases)
### `fixtures/dependency-conformance/` (39 cases + 32 transport cases)

All 37 cases → [features/metadata-dependencies.md](features/metadata-dependencies.md)
All 39 cases → [features/metadata-dependencies.md](features/metadata-dependencies.md)
(declaring a dependency, `meta deps sync`, the committed snapshot + lock, default
exclusion of imported metadata, overlay/extends across the boundary, every load-
and resolution-time failure, and the upstream check). File-shaped like `scope-conformance/`
above: one committed `cases.json`, no per-port fixture, no ledger. The corpus has three arms:

- **Resolution (26 cases).** Every case's `expectFiles`/`expectImported`/`expectSelected`/
- **Resolution (28 cases).** Every case's `expectFiles`/`expectImported`/`expectSelected`/
`expectMigrateGoverned`/`expectLoadError` assertions. **TypeScript (the reference
implementation), Python, Java and C# all run all 26, identically**, with no exemption. The two
implementation), Python, Java and C# all run all 28, identically**, with no exemption. The two
overlay cases that need a view child as incidental content use `view.currency` (the one
concrete `view.*` subtype registered cross-port), not `view.text` — a prior revision used
`view.text`, which Python did not register then, and carried a since-discharged allowlist
Expand All @@ -486,12 +486,13 @@ above: one committed `cases.json`, no per-port fixture, no ledger. The corpus ha
(`metaobjects deps check` and `verify --deps`), and by **C#**, in `DependencyConformanceTests`
(`dotnet meta verify --deps`). The arm exists so that each port's check is written against the
same edge cases (Phase 2, #482).
- **Transport (14 cases, `transport-cases.json`).** The `npm` and `python` transports `meta deps sync`
uses to find an installed package: npm packages under a temp `node_modules`, Python packages on a
temp import path, the directory each resolves to, the exact lock written, and each refusal. Run by
- **Transport (32 cases, `transport-cases.json`).** The `npm`, `python`, `maven` and `nuget` transports
`meta deps sync` uses to find an installed package: npm packages under a temp `node_modules`, Python
packages on a temp import path, jars in a temp local Maven repository, packages in a temp NuGet cache,
the directory each resolves to, the exact lock written, and each refusal. Run by
Node only, in `cli/test/dependency-transport-conformance.test.ts`, because Node `meta deps sync` is
the only writer of snapshots and the lock. `cases.json` also carries an `npm` and a `python`
dependency loading from their committed snapshot with nothing installed, which both ports run.
the only writer of snapshots and the lock. `cases.json` also carries an `npm`, a `python`, a `maven` and a `nuget`
dependency loading from their committed snapshot with nothing installed, which every port runs.

**C#** (S6 of FR-023 Phase 2) runs the resolution arm on two rungs of the source ladder: the
neutral one (`Resolve(null, dir)`), and the explicit metadata directory a
Expand Down Expand Up @@ -640,7 +641,7 @@ JSON and verify payload feeds it unchanged.

The fixtures in the nine corpora mapped above (metamodel 449 + yaml 16 + verify 45
+ render 18 + persistence 59 + api-contract 121 + source-resolution 25 + scope 10 +
dependency 37) each map to a feature doc, and so do the two requirement corpora, `shared-model-conformance`, the
dependency 39) each map to a feature doc, and so do the two requirement corpora, `shared-model-conformance`, the
`cube-model` corpus and `explorer-conformance`, whose case lists live in their own READMEs. None are orphaned today. The remaining
corpora in the totals table gate tooling contracts (registry manifests, provider
composition, agent context, docs emit) rather than user-facing metamodel behaviour,
Expand Down
4 changes: 2 additions & 2 deletions docs/features/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ command surface splits in two:
| **Vocabulary upgrade** (`upgrade`) | **Node `meta`** | `meta upgrade [--to <version>] [--apply]` | **any backend** — rewrites RETIRED metadata vocabulary (`@violation` → `@counterexample`, `@readOnly` → `@mutability`, dropping `@verifiedBy`) and resolves ATTRIBUTE CONTRADICTIONS (`@fields` beside `@expr` on an index key). Node-only because it edits the metadata documents themselves, which every port shares; a non-TS project runs `npx meta upgrade` against its own `metaobjects/`. **Canonical JSON and YAML alike.** Previews by default. Retirements needing a human decision are refused and the run exits non-zero, so CI cannot record a partial migration as finished |
| **Model export** (`export`) | **Node `meta`** | `meta export [--effective] [--out <file>]` | the loaded model, overlays merged, as one canonical JSON document. `--effective` also writes every inherited field, child and attribute onto the node that inherits it, which is the input the [explorer builder](explorer-data.md) reads. The output is for reading: loading an effective document back would declare each inherited member twice |
| **Vocabulary search** (`types`) | **Node `meta`** | `meta types [query]` | **any backend** — apropos/`kubectl explain` over the live metamodel registry (names + descriptions + when-to-use); the vocabulary is cross-port identical (registry-conformance) |
| **Dependency sync** (`deps`) | **Node `meta`** | `meta deps sync \| check \| list` | **any backend** — resolves a declared metadata dependency (`path`, or an installed `npm` / `python` package) into a committed snapshot + lock (FR-023), so `sync`/`check`/`list` themselves stay Node-only regardless of your server language. TypeScript, Python and Java then LOAD that snapshot at every rung of their own source ladder (Kotlin loads through Java's loader and Maven plugin); C# loads the snapshot through `dotnet meta` (Phase 2 S6). On a project whose own metadata Node `meta` cannot read (a Maven `src/main/resources/metadata` directory, a C# metadata directory, or another port's vocabulary), `sync` and `check` prove the locked dependencies load together and say that the port's own `verify` is the full check. See [`metadata-dependencies.md`](metadata-dependencies.md) |
| **Dependency sync** (`deps`) | **Node `meta`** | `meta deps sync \| check \| list` | **any backend** — resolves a declared metadata dependency (`path`, an installed `npm` / `python` package, or a `maven` jar / `nuget` package already in the local Maven repository / NuGet cache; it never downloads) into a committed snapshot + lock (FR-023), so `sync`/`check`/`list` themselves stay Node-only regardless of your server language. TypeScript, Python and Java then LOAD that snapshot at every rung of their own source ladder (Kotlin loads through Java's loader and Maven plugin); C# loads the snapshot through `dotnet meta` (Phase 2 S6). On a project whose own metadata Node `meta` cannot read (a Maven `src/main/resources/metadata` directory, a C# metadata directory, or another port's vocabulary), `sync` and `check` prove the locked dependencies load together and say that the port's own `verify` is the full check. See [`metadata-dependencies.md`](metadata-dependencies.md) |
| **Canonical format** (`fmt`) | every port | `meta fmt` / `dotnet meta fmt` / `mvn metaobjects:fmt` / `metaobjects fmt` | (#304) ships in **every** port — the canonical serializer already exists in all five, so formatting is local, not Node-only. `--check` lists non-canonical files and exits non-zero without changing anything. Formats each file STANDALONE — own-mode, declared-here layer only (ADR-0039); an `extends` onto another file's base is preserved as-is, never resolved or erred on; a file declaring `overlay: true` anywhere — whether or not a same-file base exists — is reported as skipped rather than merged (merging would drop the overlay marker, a structural change fmt never makes). A YAML file is always skipped — no canonical YAML emitter exists (ADR-0006). Before writing, every port reloads the whole project with the candidate swapped in and refuses the write unless the reloaded model's canonical form is byte-identical to the untouched baseline — fmt never changes meaning. See [Fixture corpus](#meta-fmt--canonical-formatting-304) below and `fixtures/fmt-conformance/README.md` |
| TS codegen | Node `meta` | `meta gen` | TS projects. **No `--template-spec` flag, deliberately** — `metaobjects.config.ts` already takes generator VALUES, so a declarative template generator is declared there (`templateGenerator()`, or `templateSpecToGenerators(parseTemplateSpec(spec))` to reuse a C#/Python spec file). Keeping it in the config is what lets `meta verify --codegen` regenerate WITH it, since that gate re-runs the config's generator list; see [declarative template scopes](codegen-concepts.md#declarative-template-scopes) |
| C# codegen | `dotnet meta` | `dotnet meta init` / `gen` / `verify --templates` / `verify --codegen` | a .NET tool (`ToolCommandName=dotnet-meta`); invoked `dotnet meta` so it never shadows the Node `meta`; ships the ADR-0021 D2 subverbs (`--db` rejected, exit 2; bare `verify` = `--templates` plus `--codegen` when an output directory (`csharp.outDir` in `.metaobjects/config.json`, or `--out`) and `codegen/Generators.cs` are present). **The generator list is `codegen/Generators.cs`** (ADR-0034 Amendment 9): `dotnet meta init` writes it with `codegen/Program.cs` (two lines that run it) and `codegen/Codegen.csproj`, and `gen` / `verify --codegen` always run it through `dotnet run --project codegen`. With no `codegen/Generators.cs` they exit 2 and write nothing; `--generators` only narrows the list to declared entries. Every entry may set its own output directory (`ApiDocsGenerator { OutDir = ... }`), and `verify --codegen` compares each one. A declarative Mustache template-spec (the cross-port JSON contract shared with Python) is one more entry, `TemplateSpecGenerator { Spec = "<json>", TemplateRoot = "<dir>" }`; `gen` no longer reads `--template-spec` or a project-root `template-spec.json`, and a run that finds either with no such entry fails before writing anything; see [declarative template scopes](codegen-concepts.md#declarative-template-scopes). `dotnet meta docs` was removed in 1.1.0: it prints the line that points at [the migration section](migrations/docs-are-generators.md#c) and exits 2 |
Expand All @@ -56,7 +56,7 @@ vocabulary everywhere, each port implementing the modes it supports.
| `verify --codegen` | **Codegen drift** — regenerate from metadata into a temp dir and fail if it differs from the committed generated output. Catches "metadata changed but `meta gen` wasn't re-run". A hand-edited generated file is NOT drift — `meta gen` three-way-merges hand edits by design, so the gate compares the *generated contribution* against the committed `.gen-state/.hashes.json` rather than the file byte-for-byte. It still LISTS every generated file carrying a hand edit (count + paths, and how to see the edit) as a notice that does not change the exit code; `--forbid-hand-edits` makes any hand edit fail the build, for teams that want generated code untouchable (Node `meta` only). | no |
| `verify --templates` | **Template/prompt drift** — `Renderer.verify` checks each `template.*` node's `{{field}}` references against its payload VO (FR-004). A Jinja body (Python and TypeScript, ADR-0061) is checked the same way. | no |
| `verify --docs` | **Removed in 1.1.0** (ADR-0034 Amendment 9). It prints where the check went and exits 2: documentation is written by entries in `generators`, so `verify --codegen` checks it with the rest of the declared output. | — |
| `verify --deps` | **Dependency drift** (FR-023) — re-resolves each declared dependency (its `path`, or its installed `npm` / `python` package) and compares its installed artifact's hash against `.metaobjects/deps.lock.json`, same comparison as `meta deps check`. Needs the publisher reachable, so it is never part of the bare-`verify` default. A `drifted` or `unresolved` dependency fails with `ERR_DEPENDENCY_UPSTREAM_DRIFT`. **Node `meta`; Python (`metaobjects verify --deps`, plus `metaobjects deps check`), C# (`dotnet meta verify --deps`), and Java and Kotlin (`-Dmeta.verify.mode=deps`) have the `path`-only counterpart.** See [`metadata-dependencies.md`](metadata-dependencies.md). | no |
| `verify --deps` | **Dependency drift** (FR-023) — re-resolves each declared dependency (its `path`, its installed `npm` / `python` package, or its `maven` / `nuget` package in the local cache) and compares its installed artifact's hash against `.metaobjects/deps.lock.json`, same comparison as `meta deps check`. Needs the publisher reachable, so it is never part of the bare-`verify` default. A `drifted` or `unresolved` dependency fails with `ERR_DEPENDENCY_UPSTREAM_DRIFT`. **Node `meta`; Python (`metaobjects verify --deps`, plus `metaobjects deps check`), C# (`dotnet meta verify --deps`), and Java and Kotlin (`-Dmeta.verify.mode=deps`) have the `path`-only counterpart.** See [`metadata-dependencies.md`](metadata-dependencies.md). | no |

Rules of the contract:

Expand Down
Loading
Loading