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
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,8 @@ These are the load-bearing principles that have emerged through implementation.

- **Each port defaults to its own language's standard behaviour; identical cross-port output is opt-in.** Generated code writes what that language's standard serializer writes: C# decimals as JSON numbers, a value object's null member as `null`. Do not standardize the ports for the convenience of our own conformance tests. A cross-port form, such as `"wire": { "decimals": "string", "nullMembers": "omit" }`, is a project setting that adopters choose. The corpora pin it explicitly in their own config and never decide a default. 1.1.0 broke this rule by making the decimal string and null omission the only output, and 1.1.2 put it back. See [ADR-0019 Amendment 2](spec/decisions/ADR-0019-runtime-return-type-contract.md).

- **New packaged content must pass five tests to live in this project.** Most apps would re-derive it by hand; it is model-shaped; all five ports can carry it in their own idiom; its default needs no copyleft or commercial dependency; it ships `preview` until one external adopter runs it with the drift gate. Otherwise it stays with the adopter or in a separate project. The four package kinds and the worked example: [docs/features/libraries.md](docs/features/libraries.md), "What belongs in MetaObjects".

- **Pattern-derivable from metadata = codegen, never hand-code.** This is the metaobjects raison d'être. If you find yourself proposing that users hand-write something the metadata fully describes (FK references, basic CRUD, validator chains, type-safe finders, relations() blocks), stop. Codegen it. The only exception is what metadata genuinely cannot express (custom SQL views, regex patterns from outside metadata, business logic). When in doubt, generate.

- **Study reference implementations for subtle pipeline behavior; don't re-derive from spec.** For complex orchestration (loader, parser, super resolution, overlay/override merging, registry lifecycle), the spec describes WHAT but the implementation captures HOW — including edge cases and error handling. When porting, read existing implementations first. First-principles reasoning produces subtly wrong behavior that breaks cross-language interop.
Expand Down
29 changes: 29 additions & 0 deletions docs/features/libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,35 @@ in, implied generator not wired* and *generator wired, its library not opted in*
second matters more than it looks: a generator whose anchor is absent matches nothing
and emits zero files, which reads exactly like "my model has no trace entities yet".

## What belongs in MetaObjects: four kinds of package, five tests

MetaObjects ships four kinds of package. They differ in who owns the result:

| kind | what it is | ownership |
|---|---|---|
| **Vocabulary** | metamodel types and attributes | core, not ejectable: the metadata you write with it is already yours |
| **Library** | `library/<name>/` with a `library.json`: metadata and requirements (`iam`, `ai`) | `meta eject <library>` (above) |
| **Catalog generator** | a row in `meta gen --list` | import it as shipped, or eject it, except `template` and the package-only rows ([ADR-0034](../../spec/decisions/ADR-0034-codegen-scaffold-and-own.md), Amendment 5). A library may imply one only when it anchors on a library node (above). |
| **Runtime helper** | an Apache-2.0 per-port package that generated code imports | copied by the route generator's eject, helper runtime included ([ADR-0034](../../spec/decisions/ADR-0034-codegen-scaffold-and-own.md), Amendment 3) |

Something new belongs in this project only if it passes **all five** tests:

1. Most apps would otherwise re-derive it by hand.
2. It is model-shaped: derivable from declared metadata.
3. It can be carried in all five ports, each in its own language's standard way.
4. Its default needs no copyleft or commercial dependency.
5. It ships as `preview` until one external adopter runs it with the drift gate enforced
([compatibility-policy.md](../compatibility-policy.md)).

Anything else stays with the adopter (policy, presentation, branding, scheduling) or in a
separate project.

**Worked example, reporting** ([reporting.md](reporting.md)). The vocabulary (dimensions,
measures, segments, `object.report`) is core. The planned `reporting` library holds
metadata only, so opting in adds no code. Exporting to another tool and dashboards are
catalog generators, one per heavy format, so an adopter never takes a dependency they did
not wire: `cube-model` ships today, a dashboard generator does not exist yet.

## What ships today

| library | what it is | stability |
Expand Down
Loading