diff --git a/AGENTS.md b/AGENTS.md index 6b51e229a..396cd39d7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -66,7 +66,7 @@ PyPI has had no product change since `0.25.0` — nothing is broken. **All five ports ship loader + canonical serializer + conformance + codegen + render + payload-VO + `verify`:** - **TypeScript** — `codegen-ts` (Vite-style plugins; Drizzle, Zod, Fastify) + `runtime-ts` + `migrate-ts` + the universal web client packages (`runtime-web`, `react`, `tanstack`). -- **C#** — `MetaObjects` (loader + canonical serializer + conformance) + `MetaObjects.Render` (Mustache + payload-VO + `verify`) + `MetaObjects.Codegen` (EF Core entities + `AppDbContext` + CRUD minimal-API routes). Schema migrations are TS-owned (ADR-0015): the C# migrate engine and the `migrate`/`--from-db` CLI surface were removed; the C# CLI is `gen`/`verify` only, packaged as a .NET tool invoked **`dotnet meta`** (not a bare `meta` — that name belongs to the Node schema CLI). EF Core runtime data-access stays per-port. +- **C#** — `MetaObjects` (loader + canonical serializer + conformance) + `MetaObjects.Render` (Mustache + payload-VO + `verify`) + `MetaObjects.Codegen` (EF Core entities + `AppDbContext` + CRUD minimal-API routes). Schema migrations are TS-owned (ADR-0015): the C# migrate engine and the `migrate`/`--from-db` CLI surface were removed; the C# CLI is codegen (`init`/`gen`/`eject`), `verify` and `fmt` only, packaged as a .NET tool invoked **`dotnet meta`** (not a bare `meta` — that name belongs to the Node schema CLI). EF Core runtime data-access stays per-port. - **Java** — `metadata` + `omdb` + `om` + `core-spring` + `metadata-ktx` (Kotlin facade) + `codegen-spring` (Spring controllers + DTOs + repositories + filter allowlists + payload records + output parsers) + `codegen-mustache` + `codegen-plantuml` + `render` + `maven-plugin` (`metaobjects:generate`/`metaobjects:editor` + a `metaobjects:verify` **codegen-drift** goal — distinct from the removed live-DB `metaobjects:verify`; Kotlin generators run through `metaobjects:generate` via the shared SPI). FR-003 (OMDB runtime persistence + binding registry + typed jsonb + Spring-tx + source/origin metamodel) fully shipped, including Plan 4 (engine-debt remediation: atomic mapping cache, JDBC codec registry, `inTransaction` template). **Schema migrations are owned by the TypeScript toolchain** (`@metaobjectsdev/cli migrate`); the Java port's diff-and-converge migration engine and its `metaobjects:migrate` / live-DB-drift `metaobjects:verify` Maven goals were removed, and per ADR-0015 Decision 2 the dev/test runtime auto-create path (`MetaClassDBValidatorService` + the drivers' `createTable`/`createIndex`/`createForeignKey`/`createSequence` DDL) is **also** removed — **OMDB is pure data-access** (CRUD/query/codec/transactions only). - **Python** — `metaobjects` (loader + canonical serializer + conformance + render + verify + codegen) + an `ObjectManager` runtime layer. The `migrate` module was removed (schema is TS-owned, ADR-0015); Python is pure data-access (codegen + ObjectManager runtime), with a **`metaobjects` console-script** (`gen`/`verify` codegen — no `migrate`). All five conformance corpora green. - **Kotlin** — `codegen-kotlin` (KotlinPoet on JVM): entity + Exposed table + Spring controller + payload + relations + filter allowlist + validator + stored-proc + output-parser generators. `integration-tests-kotlin` runs the persistence-conformance corpus through Exposed against Testcontainers Postgres. diff --git a/CHANGELOG.md b/CHANGELOG.md index ef7c00ce1..45cb6b224 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -282,18 +282,17 @@ generator catalog and the Atlas evaluation are 1.2 work. the model (`FindInbound`, `TphPlan`, `InstanceArtifacts`, `ReportRows`). `template` is not ejectable: it has no emit logic of its own. Packaged output is unchanged and gated to be. - **`api-docs` is a generator you can own.** `dotnet meta eject api-docs` copies the page - generator with its page model, builder, renderer and path math. `dotnet meta docs` hands - off to `dotnet run --project codegen -- docs` once `codegen/generators/ApiDocsGenerator.cs` - exists, so your copy writes the reference. What the reference documents is still what the - packaged generators emit for (`GeneratorApplicability.Packaged`): an owned `routes` whose + generator with its page model, builder, renderer and path math. The api reference is an + entry in `codegen/Generators.cs` like any other (see **Removed**: `dotnet meta docs` is + gone), with `OutDir`, `ApiSubDir`, `ProjectName` and `ModelBaseUrl` as init properties; + `dotnet meta gen` writes the pages and `verify --codegen` checks them. List your copy in + place of the packaged entry. What the reference documents is still what the packaged + generators emit for (`GeneratorApplicability.Packaged`): an owned `routes` whose `AppliesTo` you changed is documented as the packaged one behaves, unless you edit the owned builder. - `dotnet meta gen` and `verify --codegen` leave a docs generator out. `gen` says so only - when a `--generators` selection names `api-docs`; an owned `Program.cs` that lists it runs - without a warning. - New public API in `MetaObjects.Codegen`: `IDocsGenerator`, `DocsRunFacts`, - `GeneratorApplicability`, `GenContext.Applicability` and `GenContext.Docs`, - `CodegenCli.RunDocs`, and `ValueObjectNames.ResolveValueObject` (moved from + New public API in `MetaObjects.Codegen`: `GeneratorApplicability`, + `GenContext.Applicability`, the `ApiDocsGenerator` init properties and + `ApiDocsGenerator.DefaultApiSubDir`, and `ValueObjectNames.ResolveValueObject` (moved from `RenderHelperGenerator`, where it was `internal`). `CSharpStringLiteral` moved from `Fr010FieldMapping` to `CSharpNaming`, so `names` needs no emitter; the old member stays as an `[Obsolete]` forwarder (see **Deprecated**). @@ -340,13 +339,14 @@ generator catalog and the Atlas evaluation are 1.2 work. `instance_artifacts`). `template` is not ejectable: it has no emit logic of its own. Packaged output is unchanged and gated to be. - **`api-docs` is a generator you can own.** `metaobjects eject api-docs` copies the page - generator with its page model, builder, renderer and path math. The api reference is now a - generator (`docs_surface = "api"`) that `metaobjects docs` runs: an owned copy is listed - under the new `docs.generators` block of `metaobjects.config.yaml` and runs in place of the - packaged one. `gen` and `verify --codegen` skip a docs generator they find under a target - and say so: the packaged `api-docs` name is to be removed (`metaobjects docs` already runs - it), an owned copy is to be listed under `docs.generators`. The reference asks `shared_predicates` which objects get a router (the new - `emits_writable_router` and the moved `is_read_only_routed`, beside `emits_router`), the + generator with its page model, builder, renderer and path math. The api reference is an + ordinary generator listed in a target's `generators` (see **Removed**: `metaobjects docs` + is gone), with its options in the new top-level `apiDocs` block of + `metaobjects.config.yaml` (`apiSubDir`, `projectName`, `modelBaseUrl`); `metaobjects gen` + writes the pages into that target's `outDir` and `verify --codegen` checks them. An owned + copy is listed in place of `api-docs`. The reference reads `shared_predicates` to decide + which objects get a router (the new `emits_writable_router` and the moved + `is_read_only_routed`, beside `emits_router`), the module your owned `routes` and `filter-allowlist` read, so one edit to the owned predicate moves the router, the allowlist and the pages together. The cross-port manifest lists `api-docs` for `python`. @@ -356,9 +356,10 @@ generator catalog and the Atlas evaluation are 1.2 work. `fr019_shared_enum`, `m2m_codegen`, and the api reference as `api_docs_model`, `api_docs_builder`, `api_docs_renderer` and `api_docs_paths`. `metaobjects.apidocs` keeps `naming` and exports `PythonApiModelBuilder`, `PythonApiDocsRenderer` and the path - functions as before. New public API: `DocsRunFacts`, `GenContext.docs`, `DEFAULT_API_SUBDIR` - and `DOCS_SURFACE_API` in `metaobjects.codegen.generator`, `ProjectConfig.docs_generators`, - `GeneratorEntry.docs_surface`, and `metaobjects.codegen.docs_run`. + functions as before. New public API: `DEFAULT_API_SUBDIR` in + `metaobjects.codegen.generator`, `ApiDocsGenerator(api_subdir, project_name, model_base_url)`, + `GeneratorBuildContext.api_docs`, and `ApiDocsConfig`, `ProjectConfig.api_docs` and + `TargetConfig.template_spec` in `metaobjects.codegen.project_config`. - **A copy you ejected before this release** keeps running: it imports the emitters from their old paths, which are deprecated aliases (see **Deprecated**), so it calls the packaged emitters as it always did. `metaobjects gen --list` marks it `DIFFERS: N behind`. @@ -383,7 +384,7 @@ generator catalog and the Atlas evaluation are 1.2 work. one documented difference). Another fails on a helper module no registry entry hands over, or a generator module no registry entry names. The nine Python entries of the shared eject contract's `eject.pending` list are closed, and the - contract test runs `api-docs` through `metaobjects docs`, the way it is run. + contract test runs `api-docs` through `metaobjects gen`, the way it is run. - **The stock `entity`, `queries`, `routes` and `barrel` generators are importable again (TypeScript).** `entityFile`, `queriesFile`, `routesFile` and `barrel` are exported from @@ -1070,15 +1071,92 @@ until you regenerate. `metaobjects.codegen.extract_delegate_emitter`, `.fr010_field_mapping`, `.output_format_spec_emitter`, `.extract_schema_emitter`, `metaobjects.codegen.generators.fr019_shared_enum`, `.m2m_codegen`, and `metaobjects.apidocs.api_model`, `.builder`, `.renderer`, `.paths` are - each one-line modules that become the new module and warn (`DeprecationWarning`) on import. + each one-line modules that become the new module and warn (`FutureWarning`, which Python + shows by default, unlike a `DeprecationWarning` raised inside an imported module) on import. Same objects, so nothing else changes; import from `metaobjects.codegen.runtime` (the api reference modules are `api_docs_model`, `api_docs_builder`, `api_docs_renderer` and - `api_docs_paths` there). The old paths will be removed in a later release. + `api_docs_paths` there). The old paths will be removed in 2.0. `metaobjects.codegen.generators.router_generator.is_read_only_routed` moved to `metaobjects.codegen.runtime.shared_predicates`; the router module still re-exports it. +- **C#: `CodegenCli.ComposeGenerators` is `[Obsolete]`.** It composed an owned list with a + `--generators` selection by ADDING packaged generators the list did not name. Since 1.1.0 a + selection only narrows the declared list (`CodegenCli.SelectDeclared`, ADR-0034 Amendment 9), + and `CodegenCli.Run` no longer calls it. It still behaves as in 1.0 for a caller of its own. + ### Removed +- **C#: `dotnet meta docs`, and `dotnet meta gen` without a generator list.** Every generator, + the api reference included, is an entry in `codegen/Generators.cs`, and `dotnet meta gen` + runs that list and nothing else (ADR-0034 Amendment 9). `dotnet meta docs` prints a line + naming `dotnet meta gen` and the migration section, and exits 2. `dotnet meta gen` and + `dotnet meta verify --codegen` in a project with no `codegen/Generators.cs` exit 2 with + `no codegen/Generators.cs` and write nothing: the in-process `gen --out + --generators ` form, which chose the generators on the command line, is gone. `gen` + given `--project` or `--model-base-url` (1.0 docs flags) fails with `found 1.0 docs + configuration` before writing anything. + - **New:** `dotnet meta init` writes `codegen/Generators.cs` (the list, a static `All`), + `codegen/Program.cs` (two lines: `using MetaObjects.Codegen;` and + `return CodegenCli.Run(args, Generators.All);`) and `codegen/Codegen.csproj`, each only + when missing; `dotnet meta eject` writes the same three files when they are missing, with + the copies it ejected already listed. `--generators` now narrows a run to declared entries + and refuses a name the list does not declare. Each entry may name its own output directory + (`IGenerator.OutDir`, a default interface member returning `null`, which writes under + `--out`), and `verify --codegen` compares each entry's directory; `RequirementTestsGenerator` + takes one (`OutDir`), so the xUnit tests go to the test project in the same run. A + `verify --codegen --generators` narrowing compares only what the named entries write. A + blank `--generators` exits 2 rather than running the whole list. The + owned runner now also refuses an unknown flag and an unknown `--baseline` value, as + `dotnet meta gen` did. + - **A 1.0 `codegen/Program.cs` is refused.** One that does not run `Generators.All` would + ignore the list, so `dotnet meta gen` and `verify --codegen` exit 2 with + `codegen/Program.cs does not run Generators.All` and write nothing; `init` and `eject` say + to replace it, and warn when `codegen/Codegen.csproj` pins an older `MetaObjects.Codegen`. + - **A template spec is an entry too.** `MetaObjects.Codegen.TemplateCodegen.TemplateSpecGenerator` + (`Spec`, `TemplateRoot`, `OutDir`) declares one in `codegen/Generators.cs`. `gen` and + `verify --codegen` no longer read `--template-spec` or discover a project-root + `template-spec.json`; a run that finds either with no such entry fails with + `found 1.0 template-spec configuration` before writing anything. + - Removed public API, all added after 1.0.13 and never released: `IDocsGenerator`, + `DocsRunFacts`, `GenContext.Docs`, `CodegenCli.RunDocs`, `CodegenCli.DocsOutcome`, + `CodegenCli.DocsGeneratorSkipped`, `CodegenHandoff.HasOwnedDocs`. `MetaObjects.Cli.DocsCommand` + (the removed command's logic) is gone with the command. + - **To upgrade**, run `dotnet meta init`, move each generator you ran into + `codegen/Generators.cs` with the api reference's flags as `ApiDocsGenerator` init + properties, and replace `dotnet meta docs` with `dotnet meta gen` in scripts: before and + after, and each flag's new home, are in + [docs-are-generators.md, "C#"](docs/features/migrations/docs-are-generators.md#c). +- **Python: `metaobjects docs`, and `metaobjects gen` without a config.** Every generator, the + api reference included, is an entry in a target's `generators` in `metaobjects.config.yaml`, + and `metaobjects gen` runs that list and nothing else (ADR-0034 Amendment 9). `metaobjects + docs` prints a line naming `metaobjects gen` and the migration section, and exits 2. + `metaobjects gen` and `metaobjects verify --codegen` with no `metaobjects.config.yaml` exit 2 + with `no metaobjects.config.yaml` and write nothing: the positional + `gen --out --generators ` form and the no-config fallback are gone, + and with a config the positional `` and `--out` are refused with a line naming + `metadata` and the targets' `outDir`. A config with a top-level `docs` block fails `gen` and + `verify --codegen` with `found 1.0 docs configuration` before anything is written. + - **New:** the top-level `apiDocs` block (`apiSubDir`, `projectName`, `modelBaseUrl`) carries + the api reference's options, and a target's `templateSpec: ` declares a template-spec + whose generators write into that target, replacing the `--template-spec` flag, which is now + refused. A project-root `template-spec.json` that no target declares, which the 1.0 flag form + found on its own, fails `gen` and `verify --codegen` with + `found 1.0 template-spec configuration`. `--generators` narrows a run to declared entries and refuses a name no target + declares; `--column-naming`, `--entities`, `--provider`, `--templates` and `--baseline` now + apply to the config's list. + - **New refusals.** A target with no `generators` key exits 2 with the migration line + (`generators: []` still writes nothing and says so). An `--entities` name that matches no + object exits 2, where it used to warn and exit 0. A blank `--generators` (an unset shell + variable) exits 2 rather than running the whole list. A `generators` entry holding a comma + (`"entity, routes"`) is refused: list each generator as its own entry. + - Removed public API, all added after 1.0.13 and never released: `DocsRunFacts`, + `GenContext.docs`, `DOCS_SURFACE_API`, `ProjectConfig.docs_generators`, + `GeneratorEntry.docs_surface` and the `metaobjects.codegen.docs_run` module. + - **To upgrade**, list each generator in a target of `metaobjects.config.yaml`, `api-docs` in a + target whose `outDir` is the docs directory, move the docs flags into `apiDocs`, and replace + `metaobjects docs` with `metaobjects gen` in scripts: before and after, and each flag's new + home, are in + [docs-are-generators.md, "Python"](docs/features/migrations/docs-are-generators.md#python). - **Java and Kotlin: the `metaobjects:docs` goal.** Documentation is generated the way code is: the api reference is a `JavaApiDocsGenerator` or `KotlinApiDocsGenerator` `` entry in the list `metaobjects:generate` runs, with its options in `` (see diff --git a/agent-context/skills/metaobjects-audit/references/csharp.md b/agent-context/skills/metaobjects-audit/references/csharp.md index 41944575c..0021f5894 100644 --- a/agent-context/skills/metaobjects-audit/references/csharp.md +++ b/agent-context/skills/metaobjects-audit/references/csharp.md @@ -26,9 +26,9 @@ Generated files are named `.g.cs` and carry an `@generated` guard commen The metadata source directory is the first positional arg (``); `--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 -`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). +how the team invokes `dotnet meta gen` (or a CI script) — the arg is explicit. What is +generated is the list in `codegen/Generators.cs`; an entry with its own `OutDir` +(`ApiDocsGenerator`, `RequirementTestsGenerator`) writes there instead of under `--out`. --- @@ -38,7 +38,7 @@ it, from how the team invokes `dotnet meta gen` (or a CI script). dotnet meta gen metaobjects --out Generated --namespace Acme.Generated dotnet meta gen --list # list all registered generators -dotnet meta gen --generators entity,db-context,routes # select a subset +dotnet meta gen metaobjects --out Generated --generators entity # narrow to declared entries dotnet meta verify --codegen # regenerate to temp, diff vs committed output dotnet meta verify --templates # check {{field}} refs vs payload VO (bare default) @@ -60,26 +60,28 @@ rejected (exit 2). | `FromSqlInterpolated(` outside `.g.cs` | stored-proc call — candidate for the `callable` generator | | `// keep in sync with` / `// mirrors the` | second-source-of-truth comment — always a finding | | `HasPrecision(` hand-coded | `field.decimal` with `@precision`/`@scale` drives this from the `entity` generator | -| a table/column string in raw-SQL EF calls, or `nameof(Entity.Prop)` standing in for a column | second spelling of a declared physical name — reference `Names.g.cs` (`AuthorNames.SourcePrimaryTable` / `Column`, emitted when `names` is named in `--generators` — `Names.Name` is the OBJECT's name, not the table); an EF property inside LINQ is the typed handle — correct | +| a table/column string in raw-SQL EF calls, or `nameof(Entity.Prop)` standing in for a column | second spelling of a declared physical name — reference `Names.g.cs` (`AuthorNames.SourcePrimaryTable` / `Column`, emitted when `codegen/Generators.cs` lists `names` — `Names.Name` is the OBJECT's name, not the table); an EF property inside LINQ is the typed handle — correct | --- ## Owned generators -`dotnet meta eject ...` copies a reference generator into `codegen/generators/` -and scaffolds an owned `codegen/` console project; once `codegen/Codegen.csproj` exists, -`dotnet meta gen` and `dotnet meta verify --codegen` hand off to it. Ejecting `routes` also +`dotnet meta eject ...` copies a reference generator into `codegen/generators/`; +the copy is listed in `codegen/Generators.cs`, the generator list `dotnet meta gen` and +`dotnet meta verify --codegen` run through the `codegen/` console project. Ejecting `routes` also copies the helper runtime the generated routes call (`FilterParser`, `EfCoreFilterDispatch`, `ValueObjectValidator`, `ConstraintErrors`, `Iso8601TimestampConverter`) into `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/Generators.cs` the same way. +`codegen/Generators.cs` the same way. With no `codegen/Generators.cs`, `dotnet meta gen` +exits 2 and generates nothing. **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** -(ADR-0034 Amendment 4), or to a declarative template (`dotnet meta gen --template-spec - --template-root `) where the shape is what matters. Recommend it; it is the +(ADR-0034 Amendment 4), or to a declarative template (a `TemplateSpecGenerator { Spec = +"", TemplateRoot = "" }` entry in `codegen/Generators.cs`) where the shape is what +matters. Recommend it; it is the primary path. Worked template example: `docs/ports/csharp.md`. To re-scaffold the agent-context into a C# project, use the Node `meta` CLI (the @@ -124,6 +126,6 @@ version across these packages is an intra-port skew finding. (`?search=`, `filter[or][N]` / `filter[and][N]` combinators, leading-wildcard gating) is TS-only — do NOT flag its absence in C#. - **Output-parser codegen ships in C#.** `output-parser` / `extractor` / `render-helper` - generators are available (`dotnet meta gen --generators output-parser`). Absence of + generators are available (list `OutputParserGenerator` in `codegen/Generators.cs`). Absence of wired output parsers where a **responding `template.prompt`** (`@responseRef`) exists IS a finding. ADR-0052: a `template.output` is outbound only and generates no parser. diff --git a/agent-context/skills/metaobjects-audit/references/python.md b/agent-context/skills/metaobjects-audit/references/python.md index 57368c30a..47beec1af 100644 --- a/agent-context/skills/metaobjects-audit/references/python.md +++ b/agent-context/skills/metaobjects-audit/references/python.md @@ -19,8 +19,8 @@ SQLAlchemy Core or asyncpg), or a hand-written repository implementation. ## Finding generated files -Generated files land under the `--out` directory passed to `metaobjects gen` and -carry an `@generated` guard comment: +Generated files land under each target's `outDir` in `metaobjects.config.yaml` (the +generator list `metaobjects gen` runs) and carry an `@generated` guard comment: ```python # @generated by metaobjects @@ -29,20 +29,21 @@ carry an `@generated` guard comment: The runner overwrites only files bearing this header. Common layout: one Pydantic model file per entity (e.g. `generated/author.py`) and one FastAPI `APIRouter` file per writable entity (e.g. `generated/author_router.py`). There is no enforced -directory structure beyond `--out`. +directory structure beyond the target's `outDir`. --- ## Run codegen + verify ```bash -metaobjects gen ./metaobjects --out ./generated -metaobjects gen ./metaobjects --out ./generated --generators entity,routes,filter-allowlist -metaobjects verify --codegen # regenerate to temp, diff vs committed --out tree (bare default) +metaobjects gen # run every target's generators (metaobjects.config.yaml) +metaobjects gen --generators entity # narrow to declared entries +metaobjects verify --codegen # regenerate to temp, diff vs each committed outDir (bare default) metaobjects verify --templates # {{field}} refs vs payload VO ``` -`--generators` accepts stable generator names: `entity`, `routes`, `filter-allowlist`, +With no `metaobjects.config.yaml`, `gen` exits 2 and generates nothing. A target's +`generators` takes stable generator names: `entity`, `routes`, `filter-allowlist`, `names`, `output-parser`, `extractor`, `output-prompt`, `render-helper`, `trace-helper`, `template`. (There is no `payload` generator since ADR-0056: a template's payload and response types are the value objects' own `entity` models.) Schema migration + live-DB drift run through the **Node `meta` CLI** @@ -61,7 +62,7 @@ regardless of server language — see the migration reference. | `def get_all_` / `def create_` / `def update_` / `def delete_` in non-generated files | hand-rolled CRUD — compare to the generated router | | `# keep in sync with` / `# mirrors the` | second-source-of-truth comment — always a finding | | `try: ... except KeyError` / `?? ''` around format strings in prompt code | silent-degradation hack around a prompt payload — flag it | -| a table/column string in the repository implementation (SQLAlchemy Core, asyncpg/psycopg SQL) | second spelling of a declared physical name — reference `_names.py` (`AUTHOR_SOURCE_PRIMARY_TABLE` / `AUTHOR__COLUMN`, emitted when `names` is named in `--generators`); no typed handle exists on this port, so that is never the reason to waive it | +| a table/column string in the repository implementation (SQLAlchemy Core, asyncpg/psycopg SQL) | second spelling of a declared physical name — reference `_names.py` (`AUTHOR_SOURCE_PRIMARY_TABLE` / `AUTHOR__COLUMN`, emitted when a target lists `names`); no typed handle exists on this port, so that is never the reason to waive it | --- @@ -69,14 +70,14 @@ regardless of server language — see the migration reference. Python generators are owned the same way as on every port. `metaobjects eject ` copies a reference into `codegen/generators/`, and a generator the project WRITES is wired -the same way — a `module:symbol` entry in `--generators` or in `metaobjects.config.yaml`, +the same way — a `module:symbol` entry in a target's `generators` in `metaobjects.config.yaml`, reading the model through `metaobjects.codegen.model_walk`. (`--provider module:symbol` registers **metamodel vocabulary**, not a generator — do not read it as one.) **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** -(ADR-0034 Amendment 4), or to a declarative template (`metaobjects gen --template-spec - --templates `) where the shape is what matters. Recommend it; it is the +(ADR-0034 Amendment 4), or to a declarative template (a target's `templateSpec: `, +refs under `metaobjects gen --templates `) where the shape is what matters. Recommend it; it is the primary path. Worked template example: `docs/ports/python.md`. To re-scaffold the agent-context into a Python project, use the Node `meta` CLI (the diff --git a/agent-context/skills/metaobjects-codegen/SKILL.md b/agent-context/skills/metaobjects-codegen/SKILL.md index b4fe9bcaf..0a93a084f 100644 --- a/agent-context/skills/metaobjects-codegen/SKILL.md +++ b/agent-context/skills/metaobjects-codegen/SKILL.md @@ -89,8 +89,8 @@ what it emits. | Port | Registration | Model helpers | |---|---|---| | 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/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` | +| Python | a `module:symbol` entry in 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 Codegen.Generators.YourGenerator()` in `codegen/Generators.cs` (`dotnet meta init` writes it); `dotnet meta gen` / `verify --codegen` run that list | node accessors, `ValueObjectNames.ResolveFieldRef`, `CSharpNaming.RoutePath` | | Java / Kotlin | `` plus ``, 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 diff --git a/agent-context/skills/metaobjects-codegen/references/csharp.md b/agent-context/skills/metaobjects-codegen/references/csharp.md index 0693a4aeb..601539ff7 100644 --- a/agent-context/skills/metaobjects-codegen/references/csharp.md +++ b/agent-context/skills/metaobjects-codegen/references/csharp.md @@ -22,6 +22,7 @@ Per the always-on descriptor: ```bash dotnet tool install --global MetaObjects.Cli # provides `dotnet meta` dotnet add package MetaObjects.Codegen # the codegen generators +dotnet meta init # writes codegen/: Generators.cs, Program.cs, Codegen.csproj ``` `dotnet meta` is a .NET tool invoked as `dotnet meta ` (the underlying @@ -29,18 +30,38 @@ command is `dotnet-meta`). ## Run +The generator list is `codegen/Generators.cs` (ADR-0034 Amendment 9) — every generator the +project runs, documentation included, each with its options as init properties. +`codegen/Program.cs` is two lines that run it; `dotnet meta init` writes both and +`codegen/Codegen.csproj`. Nothing is generated that the list does not declare. + +```csharp +// codegen/Generators.cs +using MetaObjects.Codegen; + +public static class Generators +{ + public static IReadOnlyList All { get; } = + [ + new MetaObjects.Codegen.Generators.EntityGenerator(), + new MetaObjects.Codegen.Generators.DbContextGenerator(), + new MetaObjects.Codegen.Generators.FilterAllowlistGenerator(), + new MetaObjects.Codegen.Generators.RoutesGenerator(), + ]; +} +``` + ```bash -dotnet meta gen metaobjects \ - --out Generated \ - --namespace Acme.Generated +dotnet meta gen metaobjects --out Generated --namespace Acme.Generated # runs the list dotnet meta gen --list # list registered generators -dotnet meta gen metaobjects --out Generated --generators entity,db-context,routes # select a subset +dotnet meta gen metaobjects --out Generated --generators entity # narrow to declared entries dotnet meta verify metaobjects --codegen --out Generated # codegen-drift gate (regenerate + diff vs committed) ``` -`dotnet meta verify --codegen` re-runs the SELECTION and diffs, so it takes the same -`--generators` the `gen` that produced the output used; with none named it reports that -there is nothing to check. +`gen` and `verify --codegen` always run the list through `dotnet run --project codegen`; with +no `codegen/Generators.cs` they exit 2 and write nothing. `--generators` narrows a run to the +declared entries named and never adds one: naming an entry the list does not declare is +refused. `verify --codegen` regenerates the same list, so it never disagrees with `gen`. `dotnet meta verify` defaults to `--templates` (the FR-004 prompt/template drift gate, see the prompts reference); `--codegen` is the codegen-output drift gate. @@ -58,7 +79,7 @@ set and no separate docs command. `--generators ` narrows a run to entrie 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 +`--out` unless a generator's own init properties say otherwise — documentation does (below). | Stable name | Output | @@ -69,7 +90,7 @@ does (below). | `filter-allowlist` | per-entity `FilterAllowlist` (FR-009 — the server-side field+operator allowlist the routes validate against). | | `callable` | `.callable.g.cs` — an FR-015 calling method for a `source.rdb @kind="storedProc"|"tableFunction"`, via EF `FromSqlInterpolated` (args from the `@parameterRef` value object in declaration order). | | `output-parser` / `extractor` / `output-prompt` / `render-helper` | the prompt-pillar artifacts for a **responding `template.prompt`** — one carrying `@responseRef` (ADR-0052: these tiers are INBOUND; `template.output` is outbound only and emits no parser). The strict parser, the tolerant `extract`, the **output-format prompt fragment** (`output-prompt`; presentation via `@promptStyle: guide`/`inline`/`exampleOnly`), and the typed render helper. They reference the value objects' POCOs from `entity` and declare no payload type — wire `entity` too (`--list` says so). See the **prompts** reference. | -| `names` | `Names.g.cs` — `public abstract class Names` of `const` physical database names, MIRRORING THE METADATA TREE: the object's own `Type`/`SubType`/`Name` (`Name` is the OBJECT's name, never a physical one), then per `source.rdb` keyed by `@role` — `SourcePrimaryType`/`SubType`/`Kind`, `SourcePrimarySchema` when declared, and the physical name under the alias for its `@kind` (`SourcePrimaryTable` / `SourceReplicaView` / `SourcePrimaryProc` / `…Function` / `…MaterializedView`) — then a `Field`/`Column` pair per field, then each identity and index (`IdentityPkName`, and `IdentityUqCustEmailIndex` / `IndexIxCustStatusIndex` — the DATABASE name, carried for `identity.secondary` and `index.lookup` only), then a complete `ColumnsByField`. There is no `ReadOnly`: it is a derivation over `@kind`, so ask `SourcePrimaryKind`. Emitted for every object with a declared or inherited primary source, PLUS a fragment for any abstract base such an object extends (its own identity + columns, and NO source members — it has no table). A class whose object extends another one **inherits** it (`class CopayAuthNames : AuthNames`) rather than restating its constants — a C# `const` is inherited, so `CopayAuthNames.IdColumn` and `CopayAuthNames.SourcePrimaryTable` resolve through the base (`Type`/`SubType`/`Name` are redeclared with `new`, since every artifact carries its own). `abstract`, not `static`, precisely so it can be inherited; it still cannot be instantiated. In the run, `entity` and `db-context` reference the constants (`[Table(OrderNames.SourcePrimaryTable)]`, `[Column(OrderNames.StatusColumn)]`, `.ToView(OrderNames.SourceReplicaView)`); a `--generators` selection omitting `names` falls back to literals. | +| `names` | `Names.g.cs` — `public abstract class Names` of `const` physical database names, MIRRORING THE METADATA TREE: the object's own `Type`/`SubType`/`Name` (`Name` is the OBJECT's name, never a physical one), then per `source.rdb` keyed by `@role` — `SourcePrimaryType`/`SubType`/`Kind`, `SourcePrimarySchema` when declared, and the physical name under the alias for its `@kind` (`SourcePrimaryTable` / `SourceReplicaView` / `SourcePrimaryProc` / `…Function` / `…MaterializedView`) — then a `Field`/`Column` pair per field, then each identity and index (`IdentityPkName`, and `IdentityUqCustEmailIndex` / `IndexIxCustStatusIndex` — the DATABASE name, carried for `identity.secondary` and `index.lookup` only), then a complete `ColumnsByField`. There is no `ReadOnly`: it is a derivation over `@kind`, so ask `SourcePrimaryKind`. Emitted for every object with a declared or inherited primary source, PLUS a fragment for any abstract base such an object extends (its own identity + columns, and NO source members — it has no table). A class whose object extends another one **inherits** it (`class CopayAuthNames : AuthNames`) rather than restating its constants — a C# `const` is inherited, so `CopayAuthNames.IdColumn` and `CopayAuthNames.SourcePrimaryTable` resolve through the base (`Type`/`SubType`/`Name` are redeclared with `new`, since every artifact carries its own). `abstract`, not `static`, precisely so it can be inherited; it still cannot be instantiated. In the run, `entity` and `db-context` reference the constants (`[Table(OrderNames.SourcePrimaryTable)]`, `[Column(OrderNames.StatusColumn)]`, `.ToView(OrderNames.SourceReplicaView)`); a run without `names` falls back to literals. | | `template` | the generic Mustache `templateGenerator()` primitive. | Metadata lives under `metaobjects/` (or wherever you point `--metadata-dir`) in the @@ -121,11 +142,13 @@ on every run, so never edit them: the project's code goes in one witness class (`.RequirementWitnesses` by default) that implements every generated interface. Implement each member EXPLICITLY (`void Requirements__Witnesses.req_…() { … }`): a witness whose requirement is retired or deleted then stops compiling (CS0539), where an -implicit `public void req_…()` goes stale silently. Give the generator its own -`dotnet meta gen … --out --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 init properties set on the entry in an owned +implicit `public void req_…()` goes stale silently. List it in `codegen/Generators.cs` with +its own `OutDir`, the test project's directory, since the tests need xunit: +`new MetaObjects.Codegen.Generators.RequirementTestsGenerator { OutDir = "tests/Acme.Shop.Tests/Generated" }`. +The plain `dotnet meta gen ./metadata --out ./Generated` then writes them there and +`dotnet meta verify --codegen` checks them there, in the same run as everything else. +Options (`OutDir`, `TestNamespace`, `WitnessClass`, `Grain`, `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 @@ -144,7 +167,7 @@ new ApiDocsGenerator { OutDir = "Docs" } // → Docs/api/csharp (AGENT-API.md `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`.** -Options (init properties): `OutDir` (required), `ApiSubDir` (default `api/csharp`), +Options (init properties): `OutDir` (give it a directory of generated files only; unset, the pages land under `--out`), `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#". @@ -159,10 +182,8 @@ what runs. (The other ports leave a repository seam; C# does not.) ## Write your own generator For any output the model describes and no reference emits, write an `IGenerator` in the -owned console project `codegen/`. `dotnet meta gen` and `dotnet meta verify --codegen` -hand off to it whenever `codegen/Codegen.csproj` exists; `dotnet meta eject ` -scaffolds that project (or write `Codegen.csproj` — an `Exe` referencing -`MetaObjects.Codegen` at your tool's version — and `Program.cs` yourself). +console project `codegen/` (`dotnet meta init` writes it) and list it in +`codegen/Generators.cs`; `dotnet meta gen` and `dotnet meta verify --codegen` run that list. ```csharp // codegen/generators/FieldListGenerator.cs @@ -182,10 +203,8 @@ public sealed class FieldListGenerator : IGenerator } ``` -List it in `codegen/Generators.cs` — `IReadOnlyList 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)`, +List it in `codegen/Generators.cs` as `new Codegen.Generators.FieldListGenerator()`; every +entry runs, and `--generators` only narrows. 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 diff --git a/agent-context/skills/metaobjects-codegen/references/owning-generators.md b/agent-context/skills/metaobjects-codegen/references/owning-generators.md index d67ccc2f3..30845017e 100644 --- a/agent-context/skills/metaobjects-codegen/references/owning-generators.md +++ b/agent-context/skills/metaobjects-codegen/references/owning-generators.md @@ -19,7 +19,7 @@ it there, in the same change, and keep going: |---|---| | **TypeScript** | Your ejected copy under `codegen/generators/` — and, for routes, the HTTP adapter it calls: `meta eject routes` / `routes-hono` / `entity` also copy that adapter's source (mount helpers, filter parser, error envelopes, pagination) into `codegen/runtime/`, and the ejected output imports it from there, so a bug in a route's behaviour is fixed in that copy. If you are running a generator you never ejected (imported from its package), `meta eject ` it first and swap the import for the local one eject prints. Every stock generator can be ejected except the `template` primitive, which has no emit logic of its own to own, and `docs-site`, whose builder stays in the package (`meta eject docs-site` copies the site's templates and assets into `codegen/docs-site/`); an ejected generator comes with the template modules that write its output (`codegen/templates/`): the text of what it emits is yours to change too. An ejected `docs` or `api-docs` goes in `generators`, like any other owned copy — there is no separate docs list, and `meta gen` runs it. A generator `meta gen --list` marks `package-only` has no reference template: replace it in `generators` with your own `Generator`, starting from its source in the installed package. | | **Java / Kotlin** | Your own generator class. Subclass the reference generator and override the `protected` method that emits the wrong piece; when that piece is `private`, copy the reference generator's source (Apache-2.0) into your own package and edit it. Put the class in a codegen module that the module running `metaobjects:generate` depends on — the plugin loads `` from that module's compile classpath, and `provided` scope keeps it out of your packaged app — then point `` at it. Full steps: the Java and Kotlin references. | -| **C# / Python** | Your owned copy (`dotnet meta eject ` / `metaobjects eject `, then edit it), or a generator of your own that replaces the artifact — an `IGenerator` in `codegen/Generators.cs` on C#, a `module:symbol` entry on Python — or a template spec (`--template-spec`). | +| **C# / Python** | Your owned copy (`dotnet meta eject ` / `metaobjects eject `, then edit it), or a generator of your own that replaces the artifact — an `IGenerator` in `codegen/Generators.cs` on C#, a `module:symbol` entry in a target's `generators` on Python — or a template spec. | Then do **not**: diff --git a/agent-context/skills/metaobjects-codegen/references/python.md b/agent-context/skills/metaobjects-codegen/references/python.md index 8adbe6cdd..d2ff1bab7 100644 --- a/agent-context/skills/metaobjects-codegen/references/python.md +++ b/agent-context/skills/metaobjects-codegen/references/python.md @@ -30,41 +30,54 @@ pip install "pydantic[email]>=2" fastapi ## Run +The generator list is `metaobjects.config.yaml` (ADR-0034 Amendment 9): each target names +its `outDir` and the generators that write into it, documentation included. Nothing is +generated that the config does not declare. + +```yaml +# metaobjects.config.yaml +metadata: metadata +targets: + app: + outDir: generated + generators: [entity, filter-allowlist, routes] +``` + ```bash -metaobjects gen ./metadata --out ./generated [--package ] -metaobjects gen ./metadata --out ./generated --generators entity,routes,filter-allowlist -metaobjects verify ./metadata --codegen # codegen-drift gate (regenerate into a - # temp dir + diff vs the committed --out tree) +metaobjects gen # runs every target's generators into its outDir +metaobjects gen --generators entity # narrow to declared entries (never adds one) +metaobjects verify --codegen # regenerate the same list into a temp dir + diff ``` -`metaobjects verify` defaults to `--codegen` (the codegen-output drift gate; it shares -the exact `gen` code path so the two can't diverge). `--templates` is the prompt/template -drift gate (see the prompts reference). Schema migration + live-DB drift are **not** -`metaobjects` — they run through the Node `meta` tool (see the migration reference). +With no `metaobjects.config.yaml`, `gen` and `verify --codegen` exit 2 and write nothing. +The 1.0 form `metaobjects gen --out --generators ` is not read; +`--target`, `--entities`, `--column-naming`, `--provider`, `--templates` and `--baseline` +still apply to the list. `metaobjects verify` defaults to `--codegen` (it shares the exact +`gen` code path so the two can't diverge). `--templates` is the prompt/template drift gate +(see the prompts reference). Schema migration + live-DB drift are **not** `metaobjects` — +they run through the Node `meta` tool (see the migration reference). ## Docs — one more entry in a target's `generators` There is no `metaobjects docs` command (removed in 1.1.0, ADR-0034 Amendment 9): the api -reference is the `api-docs` generator, named in a target's `generators` in -`metaobjects.config.yaml` like any other: +reference is the `api-docs` generator, named in a target's `generators` like any other, in a +target whose `outDir` holds generated files only: ```yaml targets: docs: - outDir: ./docs + outDir: docs # → docs/api/python generators: [api-docs] -apiDocs: - apiSubDir: api/python # default +apiDocs: # optional: apiSubDir (default api/python), projectName, modelBaseUrl + projectName: acme ``` -`metaobjects gen` writes it (→ `./docs/api/python`: `AGENT-API.md` + per-entity pages), -alongside everything else that target's `generators` declares. `AGENT-API.md` has the -exact imports, signatures, and payload field shapes for the generated code. **Before -calling any generated code, read `api/python/AGENT-API.md`.** Options live in the -top-level `apiDocs` block: `apiSubDir` (default `api/python`), `projectName`, -`modelBaseUrl`. To write only the documentation, `metaobjects gen --generators api-docs`. -Migrating a project that ran `metaobjects docs`: -`docs/features/migrations/docs-are-generators.md`, section "Python". +`metaobjects gen` writes it (`AGENT-API.md` + per-entity pages), alongside everything else +the config declares, and `verify --codegen` checks the pages like code. `AGENT-API.md` has +the exact imports, signatures, and payload field shapes for the generated code. **Before +calling any generated code, read `api/python/AGENT-API.md`.** To write only the +documentation, `metaobjects gen --generators api-docs`. Migrating a project that ran +`metaobjects docs`: `docs/features/migrations/docs-are-generators.md`, section "Python". **The neutral model pages are a Node `meta gen` generator, not something this port emits.** `meta docs`'s language-neutral model surface was retired along with the rest of @@ -102,9 +115,8 @@ def field_list(): # the module:symbol target: an instance, or a function return return FieldList() ``` -Wire it as `codegen.generators.field_list:field_list` in `--generators` (beside any stable -names) or in a target's `generators` in `metaobjects.config.yaml`; `metaobjects verify ---codegen` with the same selection gates it. Read the model through +Wire it as `codegen.generators.field_list:field_list` in a target's `generators` in +`metaobjects.config.yaml`, beside any stable names; `metaobjects verify --codegen` gates it. Read the model through `metaobjects.codegen.model_walk` (`is_required`, `max_length`, `field_is_array`, `enum_values`, `object_ref_target`, `description`, `primary_key_fields`, `package_of`, `route_path`, `per_entity` / `per_package` / `per_model`) — never through a field's `attr(n)`, @@ -113,12 +125,10 @@ which is OWN-ONLY in Python and drops what a field inherits. Output can be any f ## Generators -Wire generators by their stable name — **`--generators ` is REQUIRED**. There is -no default set: a run that names none is a usage error and writes nothing (ADR-0034 -Amendment 2). `metaobjects gen --list` is the catalog. `verify --codegen` re-runs the -SELECTION and diffs, so it takes the same `--generators`; with none named it reports that -there is nothing to check. -Output lands under `--out` (with the `@generated` guard header). Metadata is the same +List generators by their stable name in a target's `generators`. There is no default set +(ADR-0034 Amendment 2): a target whose list is empty writes nothing and says so. +`metaobjects gen --list` is the catalog. `verify --codegen` regenerates the same list and diffs. +Output lands under the target's `outDir` (with the `@generated` guard header). Metadata is the same canonical JSON every port reads (fused-key form, `source.rdb` + `@table`, `@column` for a renamed physical column). diff --git a/agent-context/skills/metaobjects-codegen/references/template-or-code.md b/agent-context/skills/metaobjects-codegen/references/template-or-code.md index 70e4ac937..a81e972a5 100644 --- a/agent-context/skills/metaobjects-codegen/references/template-or-code.md +++ b/agent-context/skills/metaobjects-codegen/references/template-or-code.md @@ -54,20 +54,17 @@ path is `TemplateScopeGenerator`, wired the same way with `