From 3105887f77ccb8b40ff04851388ccbc18f8a1d22 Mon Sep 17 00:00:00 2001 From: Doug Mealing Date: Sun, 25 Oct 2026 22:43:56 -0400 Subject: [PATCH] docs: packaging admission rule (four package kinds, five tests) --- AGENTS.md | 2 ++ docs/features/libraries.md | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 2f608de1d..27ac3574f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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. diff --git a/docs/features/libraries.md b/docs/features/libraries.md index f1f3d172b..a35f53be3 100644 --- a/docs/features/libraries.md +++ b/docs/features/libraries.md @@ -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//` with a `library.json`: metadata and requirements (`iam`, `ai`) | `meta eject ` (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 |