Skip to content

Commit efa7486

Browse files
committed
docs(llms),feat(site): requirements becomes the fifth pillar, and the expander gets a class
Task 12 needed a home on index.html for the requirement snippets, and the plan called for a fifth pillar card. The site said "Four pillars" and so did docs/llms/llms.txt, docs/llms/llms-full.txt and the README — five cards under a "Four pillars" heading is visibly wrong, and renaming only the heading would have left the site's own llms.txt contradicting the page it sits beside. So the count moves in the llms corpus too, which is what the site mirrors. The port split is stated everywhere it appears and must not be softened: the `requirement.*` vocabulary and every `meta verify` check are cross-port; `requirementTests()` is TypeScript-only. There is no five-language test generation, and the copy says so in each of the three places it could be read. Deliberately NOT touched: the fit assessment. `metaobjects-fit-assessment/SKILL.md` carries a written ruling that capability requirements are deferred from it on purpose — "NOT a fifth pillar, NOT a P4 rubric row, NOT an R0 verdict line" — with a pre-committed shape, trigger and anti-trigger. That deferral now rests on whether anyone fills a ledger in, which is still unmeasured; advertising it pre-adoption is the brochure failure the skill exists to avoid. A website copy edit is not where that gets overturned. Also: the injector emits `<details class="example-details">` rather than a bare `<details>`. The site styles the expander through that class, and a class-less one would have shipped the browser's default disclosure widget onto a page that has no other one — with nothing to catch it, since the site has no tests. The BLOCK regex now matches `<details[^>]*>` so idempotence survives both spellings; the two existing assertions move to the open-tag prefix, which is what they were really asserting.
1 parent c1f4979 commit efa7486

4 files changed

Lines changed: 41 additions & 15 deletions

File tree

docs/llms/llms-full.txt

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# MetaObjects -- Full Reference Corpus
22

3-
> A cross-language metadata standard for declaring typed entity models that drive code generation, runtime metadata access, drift detection, and prompt construction across TypeScript, Java, Kotlin, C#, and Python. Apache 2.0. Shipping at `0.24.4` on npm and `7.24.4` on Maven Central.
3+
> A cross-language metadata standard for declaring typed entity models that drive code generation, runtime metadata access, drift detection, prompt construction, and capability requirements across TypeScript, Java, Kotlin, C#, and Python. Apache 2.0. Shipping at `0.24.4` on npm and `7.24.4` on Maven Central.
44

55
**Note for AI assistants:** This file is the concatenated reference material for MetaObjects, intended to be loaded as LLM context. For the short index, see [llms.txt](https://metaobjects.dev/llms.txt). For the canonical spec and source, see the [GitHub repo](https://github.com/metaobjectsdev/metaobjects). When this file is out of date relative to the GitHub source, the GitHub source wins.
66

@@ -10,12 +10,13 @@
1010

1111
## What MetaObjects is
1212

13-
MetaObjects is a cross-language metadata standard for declaring typed entity models. From a single metadata definition, MetaObjects drives four capabilities — **all four ship today across all five ports** (TypeScript, Java, Kotlin, C#, Python), though not uniformly deep (field ranking: drift > codegen > prompts > runtime metadata):
13+
MetaObjects is a cross-language metadata standard for declaring typed entity models. From a single metadata definition, MetaObjects drives five capabilities. The first four **ship today across all five ports** (TypeScript, Java, Kotlin, C#, Python), though not uniformly deep (field ranking: drift > codegen > prompts > runtime metadata); the fifth ships its vocabulary and checks in every port and its test scaffolding in TypeScript only:
1414

1515
1. **Code generation** -- idiomatic per-language code generated from the same metadata model.
1616
2. **Runtime metadata access** -- load the metadata at runtime to drive dynamic behavior: CRUD operations, validation, relationships, dynamic admin UIs. (Typed tool payloads are declared today; MCP exposure of tools is on the roadmap.)
1717
3. **Drift detection** -- catch divergence between code and metadata before it ships, surfacing drift as build-time breakage.
1818
4. **Prompt construction** -- treat LLM prompts as governed metadata: a typed payload (a projection), external provider-resolved prompt text, byte-identical cross-language render, and build-time prompt-to-payload drift detection.
19+
5. **Requirements and testing** -- declare what the software is supposed to *do* as a node in the same model, so `implementedBy` is resolved rather than trusted, `meta verify` reports the ledger on every run, and `requirementTests()` scaffolds a test stub per claim (TypeScript only).
1920

2021
The metamodel is the **durable spine**; generated code is the **disposable artifact**. Substrate is local-first: typed metadata lives in your repo, and the generated code is idiomatic per-language output with **no proprietary runtime** — the entity/model tier is dependency-free, and the optional client, prompt-render, and runtime tiers are ordinary Apache-2.0 packages you could vendor or fork. If the `@metaobjectsdev/*` (npm) or `com.metaobjects:*` (Maven) packages disappeared tomorrow, your generated code keeps working in every language.
2122

@@ -41,9 +42,9 @@ This file is the short corpus; the deep, version-matched how-to is the scaffolde
4142

4243
---
4344

44-
## The four pillars
45+
## The five pillars
4546

46-
The pillars are views on the same metadata, not separate products. All four ship per language today, conformance-gated so behavior is byte-identical across ports — though they are not uniformly deep. In field materialization the ranking is drift > codegen > prompts > runtime metadata (the youngest pillar); see the capability matrix in the README for per-port coverage.
47+
The pillars are views on the same metadata, not separate products. All five ship today. The first four ship per language, conformance-gated so behavior is byte-identical across ports — though they are not uniformly deep. In field materialization the ranking is drift > codegen > prompts > runtime metadata (the youngest of the four); see the capability matrix in the README for per-port coverage. The fifth, requirements and testing, ships its vocabulary and its `verify` checks in every port and its test scaffolding in TypeScript only — the split is stated below because it is easy to overclaim.
4748

4849
### Codegen
4950

@@ -81,19 +82,31 @@ Combinations aggregate (exit non-zero if any selected mode reports drift). Bare
8182

8283
### Prompt construction
8384

84-
The prompt is code too — but most teams still ship it as a string assembled imperatively and scattered across services, with no declared shape, no tests, and no visibility into token cost. The fourth pillar makes a prompt a declared, deterministic, testable artifact.
85+
The prompt is code too — but most teams still ship it as a string assembled imperatively and scattered across services, with no declared shape, no tests, and no visibility into token cost. This pillar makes a prompt a declared, deterministic, testable artifact.
8586

8687
A prompt's input payload is declared as a typed projection — the same machinery used for DB views — so the data a prompt consumes has one declared shape and unused fields (payload bloat, token cost) show up as a diff. The prompt text is never inline: it is external and resolved by a runtime-configured provider (filesystem, RDB, or NoSQL), addressed by a backend- and locale-agnostic logical reference, so a rule block triplicated across call sites lives once. A logic-less Mustache engine renders `(payload + provider-resolved text) → final prompt string` deterministically: the same inputs always produce the same string, which makes prompts snapshot-testable in CI and keeps them byte-stable so a stray whitespace or key-order change can't silently break exact-prefix prompt caching (where the savings run up to ~90% of input cost). A build-time `verify --templates` step proves every prompt variable resolves to a real field on its payload, so a renamed field three services away breaks the build instead of quietly degrading output.
8788

8889
Because the render is conformance-gated, the determinism guarantee holds in every language port — which is also what lets an eval harness (often Python) render exactly what a production service (often not) ships. The library-side building blocks all ship in all five ports today: render, payload-VO codegen, `verify`, `template.output` parser-on-receipt (FR-006), the output-format prompt fragment, and the tolerant `extract` parser (FR-010/FR-011). The one remaining library-side piece is MCP exposure of declared prompts/tools (see the roadmap).
8990

91+
### Requirements and testing
92+
93+
The other four pillars keep the code honest about the *model*. This one asks a question none of them can: is the thing you said the software does actually built? A test exercises code that exists; there is no test that fails because a column nobody writes was never wired to anything. The absence has no address — so give it one.
94+
95+
A capability is declared as a node in the same model as the entities, not in a side document and not in a tool where the link to the code is a string. `requirement.functional` (fails when *nothing* implements it — existence) and `requirement.architectural` (fails when something *violates* it — universality) are registered vocabulary in all five ports, with the loader enforcing the closed `@status` enum so a typo fails the load in every language rather than passing silently in four of them. Hierarchy is nesting, so regrouping moves a subtree.
96+
97+
`@implementedBy` is **resolved, not trusted**: it names 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 — a gate that says nothing when it passes cannot be told apart from a gate that checked nothing — covering unresolved links, entities no claim covers, and gaps recorded versus gaps nobody has ruled on, plus an authoring lint whose findings are warnings that can never fail a build. `meta docs` renders the ledger for humans and for agents.
98+
99+
**The port split, stated exactly:** the vocabulary and the `verify` checks are cross-port. **`requirementTests()` — which scaffolds a test stub per claim, carrying the statement and counterexample in, kept from rotting by `verify --codegen` — is TypeScript-only.** There is no five-language test generation.
100+
101+
What MetaObjects does not do is write the assertion. A generated stub is a place to put a proof and a guarantee that it stays in step with the claim; it is not a proof. Entirely opt-in — a project that declares no `requirement.*` nodes sees no change at all.
102+
90103
---
91104

92105
## Implementations
93106

94107
| Language | Status | Notes |
95108
|---|---|---|
96-
| TypeScript | Reference implementation, npm `0.24.4` | All four pillars. 2500+ tests passing. Owns the canonical schema-migration toolchain used by every port (ADR-0015). Bun-first dev. |
109+
| TypeScript | Reference implementation, npm `0.24.4` | All five pillars. 2500+ tests passing. Owns the canonical schema-migration toolchain used by every port (ADR-0015). Bun-first dev. |
97110
| Java | Maven Central `7.24.4` | Spring REST + DTO + repository-interface codegen, OMDB runtime persistence (pure data-access) with Spring-tx. Fully green across all conformance corpora. |
98111
| Kotlin | Maven Central `7.24.4` | KotlinPoet codegen + Exposed runtime + `metadata-ktx` facade. Ships via the Java reactor. |
99112
| C# | NuGet `0.24.4` (.NET tool) | Loader + canonical serializer + EF Core + ASP.NET codegen + render/verify. `dotnet meta` tool. |
@@ -300,7 +313,7 @@ Apache License 2.0. See [`LICENSE`](https://github.com/metaobjectsdev/metaobject
300313

301314
For the "why metadata is the architectural spine AI codegen needs" argument, see Doug Mealing's flagship essay: [I thought AI killed MetaObjects. Then AI drift made it essential.](https://dougmealing.com/writing/metaobjects-ai-drift/)
302315

303-
On the fourth pillar — why prompts are code too: [The prompt is code — and yours is drifting too.](https://dougmealing.com/writing/the-prompt-is-code/)
316+
On the prompt pillar — why prompts are code too: [The prompt is code — and yours is drifting too.](https://dougmealing.com/writing/the-prompt-is-code/)
304317

305318
For the comparison between MetaObjects and the broader AI stack (context engineering, schema-driven agent design, knowledge-graph grounding, the Model Context Protocol), see: [The 2025 AI stack and the architecture it's missing](https://dougmealing.com/writing/ai-stack-missing-architecture/).
306319

docs/llms/llms.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# MetaObjects
22

3-
> A cross-language metadata standard for declaring typed entity models that drive code generation, runtime metadata access, drift detection, and prompt construction across TypeScript, Java, Kotlin, C#, and Python. Apache 2.0. Shipping at `0.24.4` on npm and `7.24.4` on Maven Central.
3+
> A cross-language metadata standard for declaring typed entity models that drive code generation, runtime metadata access, drift detection, prompt construction, and capability requirements across TypeScript, Java, Kotlin, C#, and Python. Apache 2.0. Shipping at `0.24.4` on npm and `7.24.4` on Maven Central.
44

55
The metamodel is the durable spine; generated code is the disposable artifact. Substrate is local-first: typed metadata lives in your repo, and the generated code is idiomatic per-language output with **no proprietary runtime** — the entity/model tier is dependency-free, and the optional client, prompt-render, and runtime tiers are ordinary Apache-2.0 packages you could vendor or fork. If `@metaobjectsdev/*` (npm) or `com.metaobjects:*` (Maven) disappears tomorrow, you keep working code in every language.
66

@@ -34,14 +34,15 @@ This `llms.txt` is the short index; the deep, version-matched how-to is the scaf
3434
- [Conformance fixtures](https://github.com/metaobjectsdev/metaobjects/tree/main/fixtures): cross-port corpora — `conformance/` (metamodel, 270 fixtures), `render-conformance/`, `persistence-conformance/` (against Testcontainers Postgres, including an `op: roundtrip` gate so every `field.*` subtype write+read round-trips on every port), `api-contract-conformance/` (41 scenarios — 26 core REST contract + filter operators, plus TPH / M:N / jsonb / write-through — run in two lanes: a reference server AND each port's generated API booted over HTTP), `registry-conformance/` (byte-matched metamodel-vocabulary manifest, live + green in all five ports), and `yaml-conformance/` / `verify-conformance/`. Every port runs the shared corpus byte-identically.
3535
- [Roadmap](https://github.com/metaobjectsdev/metaobjects/blob/main/spec/roadmap.md): current + planned work across all implementations.
3636

37-
## The four pillars
37+
## The five pillars
3838

39-
MetaObjects pillars are capabilities of the same metadata spine, not separate products. **All four ship today across all five ports** (TS / Java / Kotlin / C# / Python) — though not uniformly deep: see the [capability matrix](https://github.com/metaobjectsdev/metaobjects#capability-matrix) for per-port coverage, and note the field ranking is drift > codegen > prompts > runtime metadata (the youngest pillar).
39+
MetaObjects pillars are capabilities of the same metadata spine, not separate products. **All five ship today.** The first four ship across all five ports (TS / Java / Kotlin / C# / Python) — though not uniformly deep: see the [capability matrix](https://github.com/metaobjectsdev/metaobjects#capability-matrix) for per-port coverage, and note the field ranking is drift > codegen > prompts > runtime metadata (the youngest of the four). The fifth ships its vocabulary and its `verify` checks in every port, and its test scaffolding in TypeScript only.
4040

4141
- **Codegen** — emit idiomatic per-language code from a single metadata model. Drizzle + Zod + Fastify (TypeScript), Spring REST + DTO + repository interfaces (the persistence impl is consumer-supplied) (Java via `codegen-spring`), KotlinPoet + Exposed + Spring (Kotlin via `codegen-kotlin`), EF Core + ASP.NET (C#), Pydantic + FastAPI (Python). Hand-edit-preserving regeneration via three-way merge. Includes M:N relationship codegen (FR-018) in all five ports — entity navigation, idiomatic ORM wiring, and REST traversal (`GET /<source-plural>/{id}/<relation>`).
4242
- **Runtime metadata** — load metadata at runtime and drive behavior dynamically: CRUD, validation, relationships, dynamic admin UIs. (Typed tool payloads are declared today; MCP exposure of tools is on the roadmap.) Kysely (TS), a DB-API 2 driver (pg8000 / psycopg) via ObjectManager (Python), modernized JDBC + Spring-tx via OMDB (Java), Exposed (Kotlin); C# runtime metadata is on the roadmap (its EF Core output is generated code, not a metadata-driven runtime). Runtime queries return native in-process types (ADR-0019); wire canonicalization happens only at the serialization boundary.
4343
- **Drift detection** — catch divergence between generated code and metadata before it ships. `verify` is one verb with explicit subverbs (ADR-0021): `verify --codegen` (regen-and-diff against committed output), `verify --templates` (prompt `{{field}}` ↔ payload-VO drift), and `verify --db` (live-DB schema drift, Node `meta` only). Surfaces drift as build-time breakage rather than a production incident.
4444
- **Prompt construction** — treat LLM prompts as governed metadata instead of strings scattered across services. A typed payload declared as a projection (so payload bloat and token cost are a diff, not a mystery), external provider-resolved prompt text, and a logic-less Mustache engine that renders deterministically: snapshot-testable in CI, byte-stable so an exact-prefix prompt-cache hit doesn't break on a stray whitespace, and drift-checked at build time so a renamed field can't silently degrade a prompt. Conformance-gated, so the guarantee holds in every language port. Render + payload-VO codegen + `verify` + `template.output` parser-on-receipt (FR-006) + the output-format prompt fragment & tolerant `extract` parser (FR-010/FR-011) ship in all five ports today.
45+
- **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, with the loader enforcing the closed `@status` enum, and `@implementedBy` is **resolved, not trusted**: it names a real member of the real model, so a claim whose implementation was renamed or deleted fails the build rather than 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; `meta docs` renders it for humans and for agents. **`requirementTests()`, which scaffolds a test stub per claim carrying the statement and counterexample in, is TypeScript-only** — the vocabulary and the `verify` checks are cross-port, the test scaffolding is not. Newest of the five; a project that declares no `requirement.*` nodes sees no change at all.
4546

4647
## Implementations (npm `0.24.4` / Maven Central `7.24.4`)
4748

scripts/site/inject.test.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,22 @@ describe("injectSnippets", () => {
1818

1919
test("appends a <details> when a full file exists — and NO script tag", () => {
2020
const out = injectSnippets(`<pre class="example-code" data-snippet="ts-entity"></pre>`, payload);
21-
expect(out).toContain("<details>");
21+
expect(out).toContain("<details");
2222
expect(out).toContain("Show the whole generated file (2 lines)");
2323
expect(out).not.toContain("<script");
2424
});
2525

26+
// The site styles the expander through `.example-details`. A bare <details> would
27+
// ship with the browser's own disclosure widget on a page that has no other one —
28+
// and nothing on the site would fail, because the site has no tests.
29+
test("the appended <details> carries the class the site styles", () => {
30+
const out = injectSnippets(`<pre class="example-code" data-snippet="ts-entity"></pre>`, payload);
31+
expect(out).toContain(`<details class="example-details">`);
32+
});
33+
2634
test("appends no <details> when there is no full file", () => {
2735
const out = injectSnippets(`<pre class="example-code" data-snippet="showcase-model"></pre>`, payload);
28-
expect(out).not.toContain("<details>");
36+
expect(out).not.toContain("<details");
2937
});
3038

3139
test("replaces existing content, so re-injection is idempotent", () => {
@@ -42,7 +50,7 @@ describe("injectSnippets", () => {
4250
const once = injectSnippets(`<pre class="example-code" data-snippet="ts-entity"></pre>`, payload);
4351
const twice = injectSnippets(once, payload);
4452
expect(twice).toBe(once);
45-
expect([...twice.matchAll(/<details>/g)]).toHaveLength(1);
53+
expect([...twice.matchAll(/<details[^>]*>/g)]).toHaveLength(1);
4654
});
4755

4856
test("throws on a placeholder with no payload entry", () => {

0 commit comments

Comments
 (0)