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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ loading. A port that reports `1.2` does not know that vocabulary; upgrade every

### Added

- **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
`fixtures/shared-model-conformance/` (the engine is `SharedModel` in the Java `metadata` module
and `MetaObjects.Config.SharedModel` in C#). Java and Kotlin run it through `metaobjects:generate`
(version from the project's `<version>`) and eject it with `mvn metaobjects:eject`; C# declares
`SharedModelGenerator` in `codegen/Generators.cs` (version from `<Version>`) and ejects it with
`dotnet meta eject shared-model`. `generator-registry-conformance` now lists `java`, `kotlin` and
`csharp` under `shared-model`. The Node `meta deps sync` remains the only writer of the lock and
the snapshot.
- **Java and Kotlin: a Spring app loads a shared model's committed snapshot at runtime, and
`metaobjects:verify` checks dependency drift.** `metaobjects:generate` now ships
`.metaobjects/deps.lock.json` and `.metaobjects/deps/**` as resources (target path `.metaobjects`)
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,11 +276,11 @@ sixth ships two libraries at their own stability labels:
into its `/db` layer (`["iam", "iam/db"]`). Copy one into your repo with
`meta eject <library>`. See [`docs/features/libraries.md`](docs/features/libraries.md).

**Sharing a model across your own projects** *(the TypeScript toolchain publishes;
TypeScript, Python, Java, Kotlin and C# projects consume, from an installed npm or Python
package or over a `path` dependency such as a sibling checkout)* — one project publishes
part of its model (a common `Customer`, an audited base, a set of enums) with
`sharedModelFile()`, and another declares it in `dependencies`, syncs it into a
**Sharing a model across your own projects** *(TypeScript, Java, Kotlin and C# projects
publish; TypeScript, Python, Java, Kotlin and C# projects consume, from an installed npm or
Python package or over a `path` dependency such as a sibling checkout)* — one project publishes
part of its model (a common `Customer`, an audited base, a set of enums) with its `shared-model`
generator (`sharedModelFile()` in TypeScript), and another declares it in `dependencies`, syncs it into a
committed, hash-locked snapshot and builds on it with `extends` and references. When the
publisher's model moves, `meta verify --deps` says so, instead of two copies drifting
apart. See [`docs/features/metadata-dependencies.md`](docs/features/metadata-dependencies.md).
Expand Down
6 changes: 4 additions & 2 deletions docs/CONFORMANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ regenerate with `ls -d fixtures/<corpus>/*/ | wc -l` for directory-shaped corpor
| [`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/shared-model-conformance/`](../fixtures/shared-model-conformance/) | 11 cases (7 publish + 4 refusal) | ✓ (reference implementation) | — (Phase 2) | — (Phase 2) | — (Phase 2) | ✓ (serializer arm: the 7 publish cases' document bytes; no publisher) |
| [`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) | — | — | — | — |
| [`fixtures/fmt-conformance/`](../fixtures/fmt-conformance/) (#304 — `meta fmt`) | 12 | ✓ (reference) | ✓ | inherits via Java | ✓ | ✓ |
Expand Down Expand Up @@ -514,7 +514,9 @@ the manifest records the toolchain's `metamodelVersion`, so they do not move wit
the serializer arm**: for each publish case it loads `files`, takes the nodes the pinned manifest
lists, and `serialize_shared_document` must reproduce the artifact byte for byte (and the sha256
the manifest records). Python has no publisher, so selection and the refusals are TypeScript's.
Java, Kotlin and C# have no runner (Phase 2).
**Java runs all 11**, in `metadata`'s `SharedModelConformanceTest` against the `SharedModel` engine
the `shared-model` generator calls; **Kotlin** runs through that same engine and Maven goals and adds
only `KotlinSharedModelGeneratorTest`. **C# runs all 11**, in `SharedModelConformanceTests`.

> **ADR-0055 (deferred overlay application) — SHIPPED in every port.** The nine cases added
> for it (`overlay-mixed-file-base-in-later-file`, `overlay-mixed-file-base-in-earlier-file`,
Expand Down
21 changes: 11 additions & 10 deletions docs/features/metadata-dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,8 @@ publisher reachable.

## Publishing a dependency: `sharedModelFile()`

A publisher wires the `shared-model` generator (TypeScript only, in Phase 1a) to
select a subset of its own metadata by the same include/exclude scope-pattern
A publisher wires the `shared-model` generator (TypeScript, Java, Kotlin and C#; Python
consumes only) to select a subset of its own metadata by the same include/exclude scope-pattern
grammar `scope` already uses, and emit it as one canonical-JSON artifact plus a
manifest:

Expand Down Expand Up @@ -580,16 +580,18 @@ Then `meta deps sync` as for any other installed package. Nothing about the regi
reaches `.metaobjects/`: the lock records the transport (`npm`/`python` + `dir`), never a
URL.

**Only TypeScript publishes in Phase 1a — TypeScript, Python, Java, Kotlin and C# consume.**
**TypeScript, Java, Kotlin and C# publish; TypeScript, Python, Java, Kotlin and C# consume.**
TypeScript, Python, Java, Kotlin and C# can *consume* a dependency (load its snapshot, extend
it, overlay it). Java reads it at every rung of the Maven plugin's source ladder
(`<sourceDir>`/`<sources>`, the neutral config, the default directory), and Kotlin inherits that
through the same plugin and loader, so a Spring or Kotlin build consumes a shared model with no
extra configuration. Only the TypeScript toolchain can *publish*: it alone can generate the
artifact a publisher ships, and the Node `meta deps sync` is the only writer of the lock and the
snapshot. A Python, Java, Kotlin or C# project that wants to publish a shared model runs a
TypeScript `sharedModelFile()` generator against its own metadata the same way any TypeScript
consumer would.
extra configuration. Java, Kotlin and C# can also *publish*: each ships a `shared-model`
generator (Java and Kotlin through the Maven plugin's `metaobjects:generate`, C# through
`dotnet meta gen`; all three ejectable) that writes the artifact and manifest, byte-identical to
the TypeScript one and gated by `fixtures/shared-model-conformance/`. The Node `meta deps sync`
stays the only writer of the lock and the snapshot, in every port. A Python project that wants
to publish a shared model runs a TypeScript `sharedModelFile()` generator against its own
metadata.

**C#.** `dotnet meta` reads `dependencies` from `.metaobjects/config.json` at every rung of its
source ladder — C# has no native config surface, so the explicit `<metadataDir>` form reads the
Expand Down Expand Up @@ -710,8 +712,7 @@ but **not** built yet — do not assume any of the following exists:
- **A runtime `ObjectManager` scope predicate.** Nothing at runtime refuses to serve
an imported entity — "what's imported" is a load-time/codegen concept today, not a
runtime one.
- **Java, Kotlin, Python and C# as publishers** — Java, Kotlin and C# consume (S3 and S6);
publishing is TypeScript only.
- **Python as a publisher** — Java, Kotlin and C# publish (S8 and S9); Python consumes only.
- **`npm` and `python` transports in the Python and C# check.** Both ports' check resolves
`path` only; an `npm` or `python` dependency reports `unresolved` and names the Node `meta deps
check`, because resolving it needs the consumer's installed packages (a check that cannot check
Expand Down
4 changes: 4 additions & 0 deletions docs/features/own-your-codegen.md
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,10 @@ import { sharedModelFile } from "./codegen/generators/shared-model.js";
generators: [sharedModelFile({ name: "acme-common", include: ["acme::common::**"] })],
```

Java, Kotlin and C# publish with the same generator and eject it with their own commands; see
[Java and Kotlin](../ports/java.md#publishing-a-shared-model), [Kotlin](../ports/kotlin.md#publishing-a-shared-model-shared-model)
and [C#](../ports/csharp.md#publishing-a-shared-model-shared-model).

An unchanged copy publishes exactly what the packaged generator publishes, and a consumer's
sync accepts it. The two functions are in your project now, so they are yours to change, and
a consumer is the one that checks them: the selection, the closure, the bytes, the hash and
Expand Down
2 changes: 1 addition & 1 deletion docs/llms/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ A library is **layered, and its core layer is inert**: the core declares no `sou

### Sharing a model across your own projects

Today only the TypeScript toolchain publishes; TypeScript, Python, Java, Kotlin and C# projects consume, from an installed npm or Python package or over a `path` dependency (a sibling checkout or submodule). Maven and NuGet transports are design only. One project publishes part of its model with the `sharedModelFile()` generator; another declares it under `dependencies` in `.metaobjects/config.json`, runs `meta deps sync` to commit a hash-locked snapshot, and builds on it with `extends`, overlays and references. When the publisher's model moves, `meta verify --deps` reports it instead of two copies drifting apart. See docs/features/metadata-dependencies.md.
TypeScript, Java, Kotlin and C# projects publish; TypeScript, Python, Java, Kotlin and C# projects consume, from an installed npm or Python package or over a `path` dependency (a sibling checkout or submodule). Maven and NuGet transports are design only. One project publishes part of its model with its `shared-model` generator (`sharedModelFile()` in TypeScript); another declares it under `dependencies` in `.metaobjects/config.json`, runs `meta deps sync` to commit a hash-locked snapshot, and builds on it with `extends`, overlays and references. When the publisher's model moves, `meta verify --deps` reports it instead of two copies drifting apart. See docs/features/metadata-dependencies.md.

---

Expand Down
2 changes: 1 addition & 1 deletion docs/llms/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ MetaObjects pillars are capabilities of one metadata model, not separate product
- **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. `requirement.functional` / `requirement.architectural` are registered vocabulary in all five ports, and the loader enforces the closed `@status` enum. `@implementedBy` is checked against the model, not taken on faith: it must name 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 — and lints the authoring; the documentation generators render it for humans and for agents (`requirementsFile()`, `agentDocsFile()` and their counterparts in the other ports). A live claim naming nothing is a warning; a green run proves the references resolve, never that an implementation is correct. **Every port ships a `requirement-tests` generator.** It scaffolds a test per claim, carrying the statement and counterexample in: a stub you fill in for TypeScript, a test that calls a witness you own in the other four. The vocabulary and the `verify` checks are in every port too. Only the authoring lint is TypeScript-only. A project that declares no `requirement.*` nodes sees no change at all.
- **Libraries** — reusable declared design you opt into by name (`"libraries": ["iam"]` in `.metaobjects/config.json`): entities, the requirements they promise and the generators they imply, as one drift-gated artifact. A library is layered and its core layer is inert: it declares no source, so it adds no tables until you also opt into its `/db` layer (`["iam", "iam/db"]`). `meta eject <library>` copies one into your repo to own. `ai` (the LLM-call trace envelope) is `stable`; `iam` (users, nestable groups, roles, grants) is `preview`.

**Sharing a model across your own projects** — today only the TypeScript toolchain publishes; TypeScript, Python, Java, Kotlin and C# projects consume, from an installed npm or Python package or over a `path` dependency (a sibling checkout or submodule). Maven and NuGet transports are design only. One project publishes part of its model with the `sharedModelFile()` generator; another declares it under `dependencies` in `.metaobjects/config.json`, runs `meta deps sync` to commit a hash-locked snapshot, and builds on it with `extends` and references. When the publisher's model moves, `meta verify --deps` reports it instead of two copies drifting apart.
**Sharing a model across your own projects** — TypeScript, Java, Kotlin and C# projects publish; TypeScript, Python, Java, Kotlin and C# projects consume, from an installed npm or Python package or over a `path` dependency (a sibling checkout or submodule). Maven and NuGet transports are design only. One project publishes part of its model with its `shared-model` generator (`sharedModelFile()` in TypeScript); another declares it under `dependencies` in `.metaobjects/config.json`, runs `meta deps sync` to commit a hash-locked snapshot, and builds on it with `extends` and references. When the publisher's model moves, `meta verify --deps` reports it instead of two copies drifting apart.

## Implementations (npm `1.2.0` / Maven Central `8.2.0`)

Expand Down
27 changes: 27 additions & 0 deletions docs/ports/csharp.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,33 @@ the attribute, node and file, and gen generates anyway. `dotnet meta gen --stric
`verify` does (ADR-0023): the same `ERR_UNKNOWN_ATTR` lines with a gen-specific hint (drop `--strict`), exit 1, before any file is
written. `verify` itself stays strict unless you pass `--lax`.

## Publishing a shared model (`shared-model`)

A C# project can publish part of its model for another repository to consume (FR-023). The
`shared-model` generator writes `<ModelName>.metaobjects.json` and the manifest
`metaobjects.pkg.json` that pins it by `sha256`, byte-identical to what the TypeScript generator
writes (`fixtures/shared-model-conformance/`). Declare it in `codegen/Generators.cs`:

```csharp
new SharedModelGenerator
{
ModelName = "acme-common",
Include = ["acme::common::**"],
Files = ["metaobjects/meta.common.json"],
OutDir = "shared",
}
```

`ModelName`, `Include` and `Files` are required; `Exclude` removes patterns from `Include`, and
`Version` overrides the version recorded in the manifest, which otherwise is the project's
`<Version>` (from the project file, else the nearest `Directory.Build.props`). It writes nothing and
names the option to change when the selection is empty, is not closed over `extends` and reference
attributes, or needs vocabulary outside the library's. Give `OutDir` a directory that holds nothing
else. `requirement.*` and `template.*` nodes are never published. `dotnet meta eject shared-model`
copies the generator into your project; the artifact bytes stay in the package, because a copy that
changed them would publish a model a consumer's sync refuses. The consumer runs the Node `meta deps
sync`, which stays the only writer of the lock and the snapshot.

## Write your own generator

A generator is a class that implements `IGenerator` (`MetaObjects.Codegen/Generator.cs`): a
Expand Down
Loading
Loading