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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
21 changes: 11 additions & 10 deletions AGENTS.md

Large diffs are not rendered by default.

269 changes: 171 additions & 98 deletions CHANGELOG.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions agent-context/servers/csharp.meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"displayName": "C#",
"install": "install the MetaObjects .NET tool (dotnet tool install) + add the MetaObjects.Codegen package",
"codegenCommand": "dotnet meta gen",
"docsCommand": "dotnet meta docs",
"docsCondition": "",
"docsMissing": "",
"docsCommand": "dotnet meta gen",
"docsCondition": " (when the project's generator list declares the ApiDocsGenerator)",
"docsMissing": "Declare `ApiDocsGenerator` in the generator list in codegen/Generators.cs if it is missing",
"apiSubDir": "api/csharp"
}
6 changes: 3 additions & 3 deletions agent-context/servers/python.meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"displayName": "Python",
"install": "pip install metaobjects",
"codegenCommand": "metaobjects gen",
"docsCommand": "metaobjects docs",
"docsCondition": "",
"docsMissing": "",
"docsCommand": "metaobjects gen",
"docsCondition": " (when a target's generator list declares `api-docs`)",
"docsMissing": "Declare `api-docs` in that target's `generators` in metaobjects.config.yaml if it is missing",
"apiSubDir": "api/python"
}
2 changes: 1 addition & 1 deletion agent-context/servers/typescript.meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"displayName": "TypeScript",
"install": "npm install -D @metaobjectsdev/cli @metaobjectsdev/codegen-ts",
"codegenCommand": "npx meta gen",
"docsCommand": "npx meta docs",
"docsCommand": "npx meta gen",
"docsCondition": "",
"docsMissing": "",
"apiSubDir": "api"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,11 @@ classify it (using the classification scheme in `SKILL.md`) and route the cutove
names artifact from the declaration, so a literal is a second source of truth even when it
agrees with the naming strategy today. A typed ORM handle in its place is correct. **Check
the artifact is emitted at all before scoring the literals: on every port the selection IS
the complete list — the config's on TypeScript and the JVM, `--generators` on C# and Python —
the complete list, documentation included — `generators: [...]` in `metaobjects.config.ts`
(TypeScript), `codegen/Generators.cs` (C#), a target's `generators` in
`metaobjects.config.yaml` (Python), `<generators>` in the pom (Java, Kotlin) —
so an existing project emits none and the un-wired generator is the finding FIRST** (no port
ships a default suite; ADR-0034 Amendment 2). **This entry is the physical-name INSTANCE of signature 11.** The rule that
ships a default suite; ADR-0034 Amendments 2 and 9). **This entry is the physical-name INSTANCE of signature 11.** The rule that
generates the rest — and the reason an enum member compared as a bare string is NOT one — is
the Cross-cutting entry below.
- **`@kind` = `view` / `materializedView`** — hunt hand-written SQL views where an authored
Expand Down Expand Up @@ -272,8 +274,8 @@ to nothing, and does not check that each claimed node complies).
accepted.
- **The doc slots behave differently on this node type** — do not audit them by the generic
rule below. `title` is CHARTERED here (a requirement's `name` is an identifier, so the label
lives in `title`) and is **rendered** by `meta docs`, heading each entry after its dotted
path; `summary` is INERT (nothing reads it, and `@statement` is already the required
lives in `title`) and is **rendered** by `meta gen`'s `requirementsFile()`, heading each
entry after its dotted path; `summary` is INERT (nothing reads it, and `@statement` is already the required
one-liner) and `meta verify` warns on one. So an absent `summary` is correct, a populated
one is the finding, and a populated `title` is correct.
- **CALIBRATION — entirely opt-in and warning-only where it counts.** A project declaring no
Expand Down
7 changes: 4 additions & 3 deletions agent-context/skills/metaobjects-audit/references/csharp.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ Generated files are named `<Entity>.g.cs` and carry an `@generated` guard commen
The metadata source directory is the first positional arg (`<metadataDir>`); `--out`
is required (errors if absent, no default). The runner overwrites only files bearing
this header; it refuses to touch files without it. Locate the output directory from
how the team invokes `dotnet meta gen` (or a CI script) — the arg is explicit, there
is no config file equivalent on the C# side.
`codegen/Generators.cs` (the project's generator list, each entry's `OutDir` init
property — scaffolded by `dotnet meta init`, new in 1.1.0) or, for a project that predates
it, from how the team invokes `dotnet meta gen` (or a CI script).

---

Expand Down Expand Up @@ -73,7 +74,7 @@ copies the helper runtime the generated routes call (`FilterParser`, `EfCoreFilt
`codegen/runtime/` under the namespace `Codegen.Runtime`. That folder is owned code, not a
hand-rolled filter parser: do not flag it. `dotnet meta gen --list` marks each owned copy
`identical` or `DIFFERS`. A generator the project WRITES is an `IGenerator` listed in
`codegen/Program.cs` the same way.
`codegen/Generators.cs` the same way.

**A finding of the form "the built-ins do not emit the shape this project needs", or a
hand-written layer the model fully describes, resolves to a generator the project writes**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Per finding: `file:line` → what → generated-equivalent exists? → recommend

**Score only where a constant actually exists.** A fact declared in the project's CODEGEN CONFIG rather than in metadata — a bespoke route mount path, a hand-chosen JSON envelope key — is genuinely spelled twice when a client re-types it, but no generated constant holds it. That is a GENERATOR GAP, reported as one, never a literal-site finding: never invent a constant that the emitters do not emit.

**No verify subverb sees this** — `--codegen` diffs generated files, `--db` compares schema to metadata — so this audit is the only gate. Remedy: reference the constant — but **check the artifact is emitted at all before scoring the literals, because on ALL FIVE ports an existing project emits none until it asks**. ADR-0034 Amendment 2 made codegen opt-in everywhere: C# and Python require `--generators`, TypeScript's `generators: [...]` and the JVM's `<generators>` are each the COMPLETE list, and `meta init` scaffolds `generators: []` — so no port begins emitting this artifact merely because the package was upgraded. Where no artifact exists, the un-wired generator is the FIRST finding and the first remedy (`namesFile()` on TS after `meta eject names`; `SpringNamesGenerator` / `KotlinNamesGenerator` in the pom) — score the literals under it rather than as N independent findings, since one config line fixes the cause and generated code stops embedding the names too.
**No verify subverb sees this** — `--codegen` diffs generated files, `--db` compares schema to metadata — so this audit is the only gate. Remedy: reference the constant — but **check the artifact is emitted at all before scoring the literals, because on ALL FIVE ports an existing project emits none until it asks**. ADR-0034 Amendment 2 made codegen opt-in everywhere, and on every port the project's own generator list is the COMPLETE list, documentation included (Amendment 9) — `generators: [...]` in `metaobjects.config.ts` (TypeScript), `codegen/Generators.cs` (C#), a target's `generators` in `metaobjects.config.yaml` (Python), `<generators>` in the pom (Java, Kotlin) — and `meta init` scaffolds `generators: []` — so no port begins emitting this artifact merely because the package was upgraded. Where no artifact exists, the un-wired generator is the FIRST finding and the first remedy (`namesFile()` on TS after `meta eject names`; `SpringNamesGenerator` / `KotlinNamesGenerator` in the pom) — score the literals under it rather than as N independent findings, since one config line fixes the cause and generated code stops embedding the names too.

**Report TWO numbers for this signature, always, and never just the first: (a) is the artifact EMITTED, and (b) how many of the literal sites actually IMPORT it — the adoption ratio, as `<adopted>/<total> sites`.** Emission is the cheap half and it is the half that gets done: measured on an adopter whose audit proved the artifact emitted correctly, ran the generator to show it, and then adopted it at **0 of 53 sites** (37 table + 16 column). Wiring the generator changes the scorecard; it changes nothing a reader of the code experiences, because the second spelling is still the one every query uses. An audit that reports only (a) makes the estate look upgraded while every literal it found is still there — so a project that emits the artifact and imports it nowhere scores WORSE than one that has not started, not better, because it now carries a third spelling that claims to be the source of truth.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ functional one.
model. If the tree is flat, or every entry is L3, the levels are carrying no information and
should be simplified rather than defended.

**6. Titles that are not labels.** `title` is now **rendered** — `meta docs` heads each entry
`## checkout.payment — Payment capture`, the path first and the label after it — so a slot
**6. Titles that are not labels.** `title` is now **rendered** — `meta gen`'s
`requirementsFile()` heads each entry `## checkout.payment — Payment capture`, the path
first and the label after it — so a slot
that used to be inert is read by every reader of the generated page. The lint catches only
the id-shaped case (`FR-448 …`); it cannot tell a *useless* label from a good one. Read the
titles: one that restates `@statement`, or repeats the path in prose, is now visible noise in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ A concrete report whose read source is `@kind: view` is served by every port's g
- **Every derived field with filter operators is filterable and sortable**, dimension and measure alike, with its type's own operators. You cannot narrow that set: a report has no field to put `@filterable` on. A field of the `@from` entity that the report does not expose is refused with a `400`.
- A time dimension at `day` or coarser is `YYYY-MM-DD`; a null measure is `null` with the key present (a measure with `@default` is never null); `currency` is integer minor units.
- **A decimal (`avg`, a ratio, a `sum` of a decimal) has no agreed JSON spelling**: each port sends its own (TypeScript a string). Do not compare one across backends as text.
- `meta docs` writes a model page for every report (a sourceless one is marked "Not served") and an API page for a served one.
- `meta gen` writes a model page for every report (`docsFile()`; a sourceless one is marked "Not served") and an API page for a served one (`apiDocsFile()`) — each an ordinary entry in `generators`.

The files each port generates are in the `metaobjects-codegen` skill's reference for that language ("Reports").

Expand Down
21 changes: 13 additions & 8 deletions agent-context/skills/metaobjects-codegen/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ exact config, generator names and commands for this project; read it. The other
| `references/typescript-mysql.md` | a TypeScript project on a MySQL database |
| `references/typescript-retargeting.md` | a TypeScript project on a framework other than Fastify, Hono or React |
| `references/typescript-templates.md` | declarative Mustache template-codegen in a TypeScript project |
| `references/typescript-docs.md` | `meta docs` in a TypeScript project |
| `references/typescript-docs.md` | the docs generators (`docsFile`, `apiDocsFile`, `agentDocsFile`, `requirementsFile`, `metamodelDocsFile`, `docsSiteFile`) in a TypeScript project |

## Need an output? Write the generator — this is the primary path

Expand Down Expand Up @@ -90,7 +90,7 @@ what it emits.
|---|---|---|
| TypeScript | import it in `metaobjects.config.ts`, add it to `generators: [...]` (the scaffold does both) | `@metaobjectsdev/codegen-ts`: `objectRefTarget`, `enumValues`, `effectivePackage`, `packageToPath`, `servedPath`, `toCamelCase`/`toPascalCase`/`toSnakeCase`/`pluralize`, `isAbstract`, `hasAnyRdbSource`, `servesReadApi`/`servesWriteApi` |
| Python | a `module:symbol` entry in `--generators` or a target's `generators` in `metaobjects.config.yaml`; the symbol is an instance or a function returning one, never the class | `metaobjects.codegen.model_walk` |
| C# | `new YourGenerator()` in the owned `codegen/Program.cs`; `dotnet meta gen` / `verify --codegen` hand off to `codegen/` whenever `codegen/Codegen.csproj` exists | node accessors, `ValueObjectNames.ResolveFieldRef`, `CSharpNaming.RoutePath` |
| C# | `new YourGenerator()` in the owned `codegen/Generators.cs` list, which `codegen/Program.cs` passes to `CodegenCli.Run`; `dotnet meta gen` / `verify --codegen` hand off to `codegen/` whenever `codegen/Codegen.csproj` exists | node accessors, `ValueObjectNames.ResolveFieldRef`, `CSharpNaming.RoutePath` |
| Java / Kotlin | `<generator><classname>` plus `<args><outputDir>`, from a module on the plugin's classpath; extend `FileEmittingGenerator` | `com.metaobjects.generator.ModelWalk` |

**Verify it** — nothing to register. `verify --codegen` (`mvn metaobjects:verify` on the
Expand Down Expand Up @@ -243,11 +243,16 @@ reads as an invitation to adopt the surface you decided against.

## Selecting generators — NOTHING is generated until you choose it

**`meta init` wires no generators, and no port ships a default suite.** A fresh
scaffold has `generators: []` and an empty `codegen/generators/`; `--generators` is
required on the C# and Python CLIs; Java has never had a default set. Choosing what an
application needs is a judgment over its purpose and stack, and the tool's job is to
make that choice cheap and truthful, not to make it for you.
**`meta init` wires no generators, and no port ships a default suite.** A fresh TypeScript
scaffold has `generators: []` and an empty `codegen/generators/`; a fresh C# scaffold
(`dotnet meta init`) has an empty `codegen/Generators.cs`; Java and Python have never had a
default set. In every port the project's own generator list — `generators` in
`metaobjects.config.ts`, `codegen/Generators.cs`, a target's `generators` in
`metaobjects.config.yaml`, or `<generators>` in the pom — is the COMPLETE list,
documentation included (ADR-0034 Amendment 9); `--generators` (Maven: a second
`<execution>`) only narrows a run to entries already in it. Choosing what an application
needs is a judgment over its purpose and stack, and the tool's job is to make that choice
cheap and truthful, not to make it for you.

Each generator has a **stable name** (kebab-case) that is the same in every port and
surfaces in diagnostics — reference generators by that name, never by inlining what
Expand Down Expand Up @@ -301,7 +306,7 @@ implies that you have not wired.
| `persistence` | how rows are read and written | app shape |
| `api` | the HTTP surface — pick one framework | app shape |
| `client` | the browser tier | app shape |
| `docs` | on by default; the canonical door is `meta docs` | — |
| `docs` | model pages, api reference, agent pages, requirement ledger — each its own generator entry, like any other | app shape |
| `capability` | prompts, parsers, payloads, traces, test stubs | **`--probe`** |

At intent level: a **headless data service** is `model` + `persistence`; an **HTTP API**
Expand Down
56 changes: 31 additions & 25 deletions agent-context/skills/metaobjects-codegen/references/csharp.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ is **schema-migrations only** on the C# side (ADR-0015): `meta migrate` /
- Install
- Run
- `MetaObjects.Codegen` generators
- Docs — `dotnet meta docs`
- Docs — one more entry in `codegen/Generators.cs`
- Persistence + routes are the deployed artifact
- Write your own generator
- Extending the generators (open-for-extension, ADR-0002)
Expand Down Expand Up @@ -49,10 +49,17 @@ Node `meta` tool (see the migration reference).

## `MetaObjects.Codegen` generators

Wire generators by their stable name — **`--generators <names>` is REQUIRED**. There is
no default set: a run that names none is a usage error and writes nothing (ADR-0034
Amendment 2). `dotnet meta gen --list` is the catalog. Output lands under `--namespace`
in `--output-dir`.
Wire generators as a list in **`codegen/Generators.cs`** — a static list of constructed
generators, with their options as init properties (ADR-0034 Amendment 9); `codegen/Program.cs`
is a two-line entry point that passes the list to `CodegenCli.Run`. `dotnet meta init`
(new in 1.1.0) scaffolds both files; `dotnet meta eject` writes them when either is missing.
`dotnet meta gen` runs the declared list, documentation included — there is no default
set and no separate docs command. `--generators <names>` narrows a run to entries the
list already declares, by stable name; it never adds one, and naming an entry the list
does not declare is refused. `dotnet meta gen --list` is the catalog of what the port
registers, not what this project runs. Output lands under `--namespace` in
`--output-dir` unless a generator's own init properties say otherwise — documentation
does (below).

| Stable name | Output |
|---|---|
Expand Down Expand Up @@ -118,31 +125,29 @@ implicit `public void req_…()` goes stale silently. Give the generator its own
`dotnet meta gen … --out <test project dir> --generators requirement-tests` run (a run has
one `--out`, and the test project needs xunit), and pass `--namespace` to
`verify --codegen` for that directory. Options (`TestNamespace`, `WitnessClass`, `Grain`,
`Filter`, `Renderer`, `WarnUncovered`) are public properties set in an owned
`codegen/Program.cs`; `Grain` needs `using MetaObjects.Core.Requirement;`. A filter
`Filter`, `Renderer`, `WarnUncovered`) are init properties set on the entry in an owned
`codegen/Generators.cs`; `Grain` needs `using MetaObjects.Core.Requirement;`. A filter
REPLACES the default of functional L4/L5. `dotnet meta eject requirement-tests` copies the
generator with its default rendering. The requirement checks in `dotnet meta verify` are
core and are not ejectable.

## Docs — `dotnet meta docs`
## Docs — one more entry in `codegen/Generators.cs`

```bash
dotnet meta docs metaobjects --out Docs # → Docs/api/csharp (AGENT-API.md + per-entity pages)
There is no `dotnet meta docs` command (removed in 1.1.0, ADR-0034 Amendment 9): the api
reference is `ApiDocsGenerator`, an ordinary entry in the same list as every other
generator:

```csharp
new ApiDocsGenerator { OutDir = "Docs" } // → Docs/api/csharp (AGENT-API.md + per-entity pages)
```

`dotnet meta docs` emits this project's C# SDK api surface (`api/csharp`), including
`AGENT-API.md` — the exact imports, signatures, and payload field shapes for the
`dotnet meta gen` writes it, alongside everything else `codegen/Generators.cs` declares.
`AGENT-API.md` has the exact imports, signatures, and payload field shapes for the
generated code. **Before calling any generated code, read `api/csharp/AGENT-API.md`.**

**The two `docs` positionals are NOT the same argument.** This one is the METADATA
directory. The Node `meta docs` positional — used by every stack, since `migrate`,
`verify --db` and the neutral model docs are Node-only — is the PROJECT ROOT that
CONTAINS the metadata. Run `meta docs` with no positional, from the project root:

```bash
dotnet meta docs metaobjects --out Docs # C#: the METADATA dir
meta docs --out Docs # Node: run from the PROJECT ROOT (no positional)
```
Options (init properties): `OutDir` (required), `ApiSubDir` (default `api/csharp`),
`ProjectName`, `ModelBaseUrl`. To write only the documentation,
`dotnet meta gen --generators api-docs`. Migrating a project that ran `dotnet meta docs`:
`docs/features/migrations/docs-are-generators.md`, section "C#".

## Persistence + routes are the deployed artifact

Expand Down Expand Up @@ -177,9 +182,10 @@ public sealed class FieldListGenerator : IGenerator
}
```

List it in `codegen/Program.cs` — `IReadOnlyList<IGenerator> generators = [new
FieldListGenerator()]; return CodegenCli.Run(args, generators);`. An owned generator the
`--generators` selection does not name still runs. Model reads: `Fields()`, `Attr(n)`,
List it in `codegen/Generators.cs` — `IReadOnlyList<IGenerator> Generators = [new
FieldListGenerator()];` — which `codegen/Program.cs` passes to `CodegenCli.Run`. An owned
generator a `--generators` selection does not name still exists in the list but does not
run that invocation. Model reads: `Fields()`, `Attr(n)`,
`ResolvedIsArray()`, `EffectiveEnumValues`, `MaxLength` resolve; `IsArray`, `OwnAttr`,
`EnumValues` do not. `ValueObjectNames.ResolveFieldRef(field, ctx.Root)` resolves an
`@objectRef`; `CSharpNaming.RoutePath(obj)` is the REST segment; constants come from
Expand Down
Loading
Loading