Skip to content

Commit cbce00b

Browse files
committed
docs: write-your-own-generator is the normal path; typed-client recipe, handEdits advice, one install line, llms.txt copy, CHANGELOG
1 parent 5d12334 commit cbce00b

8 files changed

Lines changed: 479 additions & 16 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ here.**
1212

1313
### Docs
1414

15+
- **Writing your own generator is documented as the normal path.** `llms.txt`, `llms-full.txt`
16+
and the README now say a team should expect to write generators, and that documentation is a
17+
generator. New recipe, [a typed client for the reference routes](docs/recipes/typed-client-for-the-reference-routes.md),
18+
with a worked generator that fails `verify --codegen` when the routes move. The generator recipes
19+
advise `handEdits: "overwrite"` for output that carries a safety rule (the default `"keep"`
20+
merges a hand edit that removes it). One install pair that compiles the stock generated
21+
TypeScript replaces the lines that disagreed. The `llms.txt` copy no longer repeats the homepage
22+
lede or leans on "spine" and "substrate", and its longest sentences are split.
23+
1524
- **Port docs and `llms-full.txt` now say what a fresh reader got wrong.** Each port page opens with its keep-your-ORM path (TypeScript `runtime: false`, Java and Kotlin records and data classes, sourceless C# POCOs as DTOs, Pydantic in Python); three-way merge is scoped to TypeScript everywhere it was claimed unscoped, with what the other ports do instead (committed hash manifest, refusal by name, eject); framework baselines are stated (Java 21 rather than the contradictory 17, Spring Boot 3.x with Boot 2 unsupported, .NET 8, Python 3.11, Node first and Bun second); and each port page carries its known gaps with absolute links.
1625

1726
### Added
@@ -52,6 +61,40 @@ here.**
5261
`fixtures/dependency-conformance/` (the 11 check cases pin the Node `meta deps check`); Kotlin loads through the same loader and adds a codegen test.
5362
Spring runtime auto-load and `verify` drift for dependencies are later slices.
5463

64+
- **Own your generators, and give back the ones you did not change.** A team is expected to
65+
write generators of its own, because a generator it owns is what keeps output from drifting.
66+
Two things now help it carry fewer copies than it needs. `meta gen` prints one line naming
67+
every owned generator copy that is still the shipped reference, unchanged. `meta eject --undo
68+
[<name>...] [--force]` removes them: with no names, every unchanged copy (and the template
69+
modules only they used); with names, those, refusing the whole call and removing nothing if a
70+
named copy differs. It never edits `metaobjects.config.ts`; it prints the import to swap back.
71+
The other ports have the same switch: `metaobjects eject --undo` (Python), `mvn
72+
metaobjects:eject -Dundo=true` (Java and Kotlin) and `dotnet meta eject --undo` (C#). There is no
73+
`doctor` command in any port; `eject --list` is the report. See
74+
[the CLI notes](docs/features/cli.md#owned-generators-the-unchanged-copy-notice-eject---undo-and-gen---strict).
75+
76+
- **`gen --strict`: an unknown attribute is an error, as it is in `verify`.** `gen` has always
77+
loaded leniently, so a typo such as `@sensitve` was a warning and the run went on with changed
78+
output. `meta gen --strict` (TypeScript), `metaobjects gen --strict` (Python) and `dotnet meta gen
79+
--strict` (C#) exit 1 and write nothing. `mvn metaobjects:generate` already loaded strictly
80+
(`-Dmeta.lax=true` relaxes it), so Java and Kotlin needed no new switch.
81+
82+
- **`meta init --with-docs` (TypeScript, C#) and a new `metaobjects init [--with-docs]`
83+
(Python).** The scaffold wires the documentation generators and their `docs` target, so the
84+
first `gen` writes documentation. The default scaffold is unchanged: nothing is generated until
85+
you choose it. In C# it adds `ApiDocsGenerator` to the new `codegen/Generators.cs`.
86+
87+
- **`entityTypeNames(obj)` is exported from `@metaobjectsdev/codegen-ts`.** It returns the six
88+
type names the entity module exports (`<Entity>`, `Insert`, `Update`, `Create`, `Patch`,
89+
`Filter`; `<Entity>Row` for a TPH base), and the templates that emit them now call it, so an
90+
owned generator that refers to them cannot fall behind a rename. `docs/ports/typescript.md`
91+
has the table and says which to pass to `create<Entity>` and `update<Entity>` (#490).
92+
93+
- **Python generated routers take an `apiPrefix` setting.** It is a top-level key of
94+
`metaobjects.config.yaml`, the counterpart of the TypeScript key of the same name. It defaults
95+
to `/api`, so existing output is byte-identical, and `""` mounts routers at `/<plural>`. The generated API docs use
96+
the same prefix (#488).
97+
5598
- **Java, Kotlin and C# can compile and match `scope` patterns.** `com.metaobjects.config.Scope`
5699
(Java; Kotlin inherits it) and `MetaObjects.Config.Scope` (C#) port the scope-pattern grammar from
57100
TypeScript (`compilePattern` / `compileScope` / `matchesScope`), and both run the shared
@@ -172,6 +215,19 @@ here.**
172215
the command and rolls the sync back. A stale `docs/CONFORMANCE.md` cell that claimed Python passed
173216
only 2 of the dependency corpus's cases is corrected.
174217

218+
- **The "Model / metadata" link in the Python, C#, Java and Kotlin API docs no longer points at
219+
a page that does not exist.** Those ports write no model pages, so the relative link was dead.
220+
It is now written only when `modelBaseUrl` is set.
221+
222+
- **TypeScript API docs describe only what the run writes.** The `api-docs` pages listed the
223+
stock queries and Fastify routes for every entity, whatever the generator list held. A project
224+
that wires neither now gets pages without them, and an owned generator that writes them
225+
counts when it carries the `emitsQueries` or `mountsRoutes` marker.
226+
227+
- **The metamodel reference index reads as prose.** The generated index said "NOT documentation"
228+
in capitals and told a reader of an HTML page to "follow a link into `types/<family>.md`". It
229+
now says what the page covers and that each row links to its type page.
230+
175231
- **A failed `meta deps sync` no longer leaves a lock and snapshot that cannot load.** The
176232
command wrote `deps.lock.json` and the `.metaobjects/deps/` snapshot before loading the
177233
combined model, and on a load failure it exited 1 but kept the new files. Because the lock

‎README.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,20 @@ npx meta eject entity queries names barrel # copy them in; add the lin
4040
npx meta gen && npx meta verify --codegen # generate, then fail if generated code has drifted
4141
```
4242

43+
The generated TypeScript imports `drizzle-orm`, `zod`, `fastify` and a database driver, which
44+
`npm` does not add for you. This pair installs everything the stock `entity`, `queries` and `routes`
45+
output needs to compile (SQLite shown; swap in `pg` and `@types/pg` for Postgres):
46+
47+
```bash
48+
npm i -D @metaobjectsdev/cli @metaobjectsdev/codegen-ts @metaobjectsdev/metadata typescript @types/better-sqlite3
49+
npm i @metaobjectsdev/runtime-ts drizzle-orm zod fastify better-sqlite3
50+
```
51+
52+
Expect to write generators of your own. The reference ones cover the common cases, and a
53+
generator you own (`npx meta generator new <name>`) is regenerated on every model change and
54+
gated by `meta verify --codegen`, which is what keeps output from drifting. See
55+
[write your own generator](docs/recipes/write-your-own-generator.md).
56+
4357
Java, Kotlin, C# and Python start the same way from their own package: see
4458
[`docs/ports/`](docs/ports/).
4559

‎docs/features/cli.md‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,26 @@ at `-Dmeta.verify.templateRoot`). The one goal covers BOTH Java (`codegen-spring
230230
migrate engine, ADR-0015"); an unknown mode fails listing the valid ones. (Schema `--db` remains
231231
Node-only by the ADR-0015 design — see below.)
232232

233+
## Owned generators: the unchanged-copy notice, `eject --undo`, and `gen --strict`
234+
235+
You are expected to own generators, but not to carry a copy that never changed. An ejected copy
236+
that is still the shipped reference stays at the release it was copied from, so `gen` names
237+
those copies in one line and a flag takes them back. A copy you changed is never touched
238+
unless you name it and pass the force flag.
239+
240+
| Port | Notice | Take unchanged copies back | Unknown attribute in `gen` |
241+
| --- | --- | --- | --- |
242+
| TypeScript | `meta gen` | `meta eject --undo [<name>...] [--force]` | warns; `meta gen --strict` is an error |
243+
| Python | `metaobjects gen` | `metaobjects eject --undo [<name>...] [--force]` | warns; `metaobjects gen --strict` is an error |
244+
| Java / Kotlin | `metaobjects:generate` | `mvn metaobjects:eject -Dundo=true [-Dnames=a,b] [-Dforce=true]` | already an error (as `verify`); `-Dmeta.lax=true` relaxes both goals |
245+
| C# | `dotnet meta gen` | `dotnet meta eject --undo [<name>...] [--force]` | warns; `dotnet meta gen --strict` is an error |
246+
247+
Undo with no names removes every copy that is the shipped reference; with names, any named copy
248+
that differs refuses the whole call and removes nothing. It never edits your generator list
249+
(`metaobjects.config.ts`, `codegen/Generators.cs`, `pom.xml`, `metaobjects.config.yaml`): it prints the
250+
edit that points each entry back at the shipped generator. `--strict` exits 1 and writes
251+
nothing. No port has a `doctor` command: `eject --list` (or the matching goal) is the report.
252+
233253
## `gen` refuses rather than guesses — and `--baseline` is how you answer it
234254

235255
Every port's `gen` decides per file whether it may overwrite, from a committed hash

‎docs/features/own-your-codegen.md‎

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ describes. A generator is a name plus a function from the model to files; `verif
2020
scaffolds a working one and wires it. Every port's 20-line shape, run end to end, plus
2121
JSON Schema and OpenAPI 3.1 examples to copy:
2222
[Write your own generator](../recipes/write-your-own-generator.md). Eject a reference
23-
(below) only when one already emits something close to what you want.
23+
(below) only when one already emits something close to what you want. A worked TypeScript
24+
example that sits on top of the stock routes: [a typed fetch client for the reference
25+
routes](../recipes/typed-client-for-the-reference-routes.md).
2426

2527
**A stock generator already emits what you want? Import it.** Every stock generator runs
2628
as shipped from its package, with nothing copied into your repo, and an upgrade is how its
@@ -686,6 +688,35 @@ Where you set it:
686688
one-off for one run: it overwrites every refused file of that run, of every generator,
687689
including files the manifest has no record of.
688690

691+
### Safety-relevant output: set `handEdits: "overwrite"`
692+
693+
A generator whose output enforces something (a redaction rule, an audit log, an auth check)
694+
needs one more setting. The default is `handEdits: "keep"`: someone can hand-edit the emitted
695+
file, `meta gen` merges that edit into fresh output, and `meta verify --codegen` still passes
696+
after the merge. Run on a scratch project, a line appended to a generated client survived
697+
`meta gen` (status `merged`) and `verify --codegen` exited 0. For a redaction rule that is a
698+
silent hole.
699+
700+
Set `handEdits: "overwrite"` on that generator. A hand edit is then discarded and named in the
701+
run's warnings, and the committed file always equals fresh output. One line, on the entry in
702+
`metaobjects.config.ts`:
703+
704+
```ts
705+
generators: [entityFile(), { ...redactionGenerator(), handEdits: "overwrite" }],
706+
```
707+
708+
Seen: with it set, `meta verify --codegen` exits 1 on an edited file (`edited by hand; its
709+
generator sets handEdits: "overwrite", so 'meta gen' replaces it with fresh output`), and
710+
`meta gen` prints `overwrote hand-edited <path> with fresh output` and restores it.
711+
The setting is per generator, and an edited file is discarded only when it is one `gen` has a record of writing.
712+
713+
The same trap exists one layer up, in the model. `meta gen` loads metadata **leniently**: an
714+
unknown attribute is a warning, and generation goes on. A typo'd `@sensitve` on a field
715+
silently changes what your generator sees, so a redaction keyed on `@sensitive` quietly stops
716+
applying. `meta gen --strict` makes an unknown attribute an error (exit 1, nothing written).
717+
`meta verify` always rejects it. Use `--strict` in CI for any project whose generators
718+
enforce something.
719+
689720
## The sibling module (`<Entity>.extra.ts`) is a convention, not a mechanism
690721

691722
Generated TypeScript files point at a sibling module for code metadata can't express, and

‎docs/llms/llms-full.txt‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ Underneath those two verbs, the model is a cross-language metadata standard for
1717
1. **Code generation** -- idiomatic per-language code generated from the same metadata model.
1818
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.)
1919
3. **Drift detection** -- catch divergence between code and metadata before it ships, surfacing drift as build-time breakage.
20-
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.
21-
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, `verify` reports the ledger on every run in every port, and each port's `requirement-tests` generator scaffolds a test per claim.
20+
4. **Prompt construction** -- keep LLM prompts in typed metadata: a typed payload (a projection), external provider-resolved prompt text, byte-identical cross-language render, and build-time prompt-to-payload drift detection.
21+
5. **Requirements and testing** -- declare what the software is supposed to *do* as a node in the same model, so `implementedBy` is checked against the model rather than taken on faith, `verify` reports the ledger on every run in every port, and each port's `requirement-tests` generator scaffolds a test per claim.
2222
6. **Libraries** -- reusable declared design opted into by name (`"libraries": ["iam"]`): entities, requirements and implied generators as one drift-gated artifact. Since 1.0.4, in all five ports; `ai` is `stable`, `iam` is `preview`.
2323

24-
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.
24+
The model is the part you keep, and the generated code can be thrown away and regenerated. Your typed metadata lives in your repo, and the generated code is idiomatic per-language output with **no proprietary runtime**: the entity and model tier has no dependencies, 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.
25+
26+
**Write your own generators; expect to.** Documentation is a generator too (`docsFile()`, `apiDocsFile()`; `meta init --with-docs` wires them). The reference generators cover the common cases, but a team should plan to write the ones its application needs: an OpenAPI file, a typed client, a service layer in your house style. A generator you own is regenerated on every model change and gated by `verify --codegen`, which is what keeps the output from drifting. In TypeScript, `meta generator new <name>` writes a working one and wires it. See [Write your own generator](https://github.com/metaobjectsdev/metaobjects/blob/main/docs/recipes/write-your-own-generator.md) for every port, and [a typed client for the reference routes](https://github.com/metaobjectsdev/metaobjects/blob/main/docs/recipes/typed-client-for-the-reference-routes.md) for a worked example.
2527

2628
---
2729

@@ -36,7 +38,7 @@ If you are helping someone decide whether to adopt MetaObjects in an existing pr
3638
If you are an AI assistant helping someone adopt MetaObjects, do this **first** — the scaffolded local context is richer and version-matched, and it loads automatically into the project's agent context:
3739

3840
1. **Install the port's package:**
39-
- TypeScript / Node: `npm install -D @metaobjectsdev/cli @metaobjectsdev/codegen-ts`
41+
- TypeScript / Node: `npm i -D @metaobjectsdev/cli @metaobjectsdev/codegen-ts @metaobjectsdev/metadata typescript @types/better-sqlite3`, then `npm i @metaobjectsdev/runtime-ts drizzle-orm zod fastify better-sqlite3` (SQLite; use `pg` and `@types/pg` for Postgres). The generated files import `drizzle-orm`, `zod`, `fastify` and a driver, and `npm` does not add them for you.
4042
- Java / Kotlin: add `com.metaobjects:metaobjects-metadata`, `com.metaobjects:metaobjects-codegen-spring` (Java) or `com.metaobjects:metaobjects-codegen-kotlin` (Kotlin), and `com.metaobjects:metaobjects-maven-plugin`, all at `8.1.4`.
4143
- Python: `pip install metaobjects`
4244
- C#: install the MetaObjects .NET tool (invoked as `dotnet meta`).

0 commit comments

Comments
 (0)