@metaobjectsdev/sdk 0.24.4 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +1 -7
  2. package/agent-context/servers/csharp.meta.json +3 -1
  3. package/agent-context/servers/java.meta.json +3 -1
  4. package/agent-context/servers/kotlin.meta.json +3 -1
  5. package/agent-context/servers/python.meta.json +3 -1
  6. package/agent-context/servers/typescript.meta.json +3 -1
  7. package/agent-context/skills/metaobjects-audit/SKILL.md +15 -8
  8. package/agent-context/skills/metaobjects-audit/references/capability-checklist.md +6 -1
  9. package/agent-context/skills/metaobjects-audit/references/csharp.md +10 -1
  10. package/agent-context/skills/metaobjects-audit/references/java.md +10 -0
  11. package/agent-context/skills/metaobjects-audit/references/kotlin.md +11 -0
  12. package/agent-context/skills/metaobjects-audit/references/python.md +12 -2
  13. package/agent-context/skills/metaobjects-audit/references/typescript.md +7 -2
  14. package/agent-context/skills/metaobjects-authoring/SKILL.md +35 -6
  15. package/agent-context/skills/metaobjects-codegen/SKILL.md +249 -10
  16. package/agent-context/skills/metaobjects-codegen/references/csharp.md +1 -0
  17. package/agent-context/skills/metaobjects-codegen/references/java.md +1 -0
  18. package/agent-context/skills/metaobjects-codegen/references/kotlin.md +1 -0
  19. package/agent-context/skills/metaobjects-codegen/references/python.md +1 -0
  20. package/agent-context/skills/metaobjects-codegen/references/typescript.md +190 -6
  21. package/agent-context/skills/metaobjects-prompts/references/typescript.md +10 -1
  22. package/agent-context/skills/metaobjects-runtime-ui/SKILL.md +22 -0
  23. package/agent-context/skills/metaobjects-runtime-ui/references/csharp.md +15 -0
  24. package/agent-context/skills/metaobjects-runtime-ui/references/java.md +33 -0
  25. package/agent-context/skills/metaobjects-runtime-ui/references/kotlin.md +23 -0
  26. package/agent-context/skills/metaobjects-runtime-ui/references/python.md +25 -0
  27. package/agent-context/skills/metaobjects-runtime-ui/references/tanstack.md +34 -7
  28. package/agent-context/skills/metaobjects-runtime-ui/references/typescript.md +20 -0
  29. package/agent-context/skills/metaobjects-verify/SKILL.md +28 -0
  30. package/agent-context/templates/always-on.md.mustache +45 -4
  31. package/dist/agent-context/assemble.d.ts.map +1 -1
  32. package/dist/agent-context/assemble.js +52 -4
  33. package/dist/agent-context/assemble.js.map +1 -1
  34. package/dist/agent-context/scaffold.d.ts +16 -1
  35. package/dist/agent-context/scaffold.d.ts.map +1 -1
  36. package/dist/agent-context/scaffold.js +81 -4
  37. package/dist/agent-context/scaffold.js.map +1 -1
  38. package/dist/forge-types.d.ts.map +1 -1
  39. package/dist/forge-types.js +19 -1
  40. package/dist/forge-types.js.map +1 -1
  41. package/package.json +4 -9
  42. package/src/agent-context/assemble.ts +86 -6
  43. package/src/agent-context/scaffold.ts +91 -5
  44. package/src/forge-types.ts +21 -0
  45. package/dist/agent-docs/body.d.ts +0 -6
  46. package/dist/agent-docs/body.d.ts.map +0 -1
  47. package/dist/agent-docs/body.js +0 -593
  48. package/dist/agent-docs/body.js.map +0 -1
  49. package/dist/agent-docs/content-hash.d.ts +0 -8
  50. package/dist/agent-docs/content-hash.d.ts.map +0 -1
  51. package/dist/agent-docs/content-hash.js +0 -23
  52. package/dist/agent-docs/content-hash.js.map +0 -1
  53. package/dist/agent-docs/index.d.ts +0 -3
  54. package/dist/agent-docs/index.d.ts.map +0 -1
  55. package/dist/agent-docs/index.js +0 -4
  56. package/dist/agent-docs/index.js.map +0 -1
  57. package/src/agent-docs/body.ts +0 -592
  58. package/src/agent-docs/content-hash.ts +0 -25
  59. package/src/agent-docs/index.ts +0 -8
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @metaobjectsdev/sdk
2
2
 
3
- Programmatic SDK for MetaObjects: workspace memory records, path resolution, project config loading, and the agent-docs reference content. Consumed by the `meta` CLI and by AI-collaboration tooling (MCP exposers, codegen prompts).
3
+ Programmatic SDK for MetaObjects: workspace memory records, path resolution, and project config loading. Consumed by the `meta` CLI and by AI-collaboration tooling (MCP exposers, codegen prompts).
4
4
 
5
5
  ## Install
6
6
 
@@ -17,12 +17,6 @@ const metaRoot = await resolveMetaRoot(process.cwd());
17
17
  const config = await loadConfig(metaRoot);
18
18
  ```
19
19
 
20
- The canonical agent reference docs (scaffolded by `meta init`) are available via a sub-path:
21
-
22
- ```ts
23
- import { AGENT_DOCS_BODY, withContentHash } from "@metaobjectsdev/sdk/agent-docs";
24
- ```
25
-
26
20
  ## agent-context
27
21
 
28
22
  `@metaobjectsdev/sdk/agent-context` assembles the downstream AI-assistant context
@@ -1,5 +1,7 @@
1
1
  {
2
2
  "displayName": "C#",
3
3
  "install": "install the MetaObjects .NET tool (dotnet tool install) + add the MetaObjects.Codegen package",
4
- "codegenCommand": "dotnet meta gen"
4
+ "codegenCommand": "dotnet meta gen",
5
+ "docsCommand": "dotnet meta docs",
6
+ "apiSubDir": "api/csharp"
5
7
  }
@@ -1,5 +1,7 @@
1
1
  {
2
2
  "displayName": "Java",
3
3
  "install": "add com.metaobjects:metaobjects-metadata + metaobjects-codegen-spring + metaobjects-maven-plugin to your pom.xml",
4
- "codegenCommand": "mvn metaobjects:generate"
4
+ "codegenCommand": "mvn metaobjects:generate",
5
+ "docsCommand": "mvn metaobjects:docs",
6
+ "apiSubDir": "api/java"
5
7
  }
@@ -1,5 +1,7 @@
1
1
  {
2
2
  "displayName": "Kotlin",
3
3
  "install": "add com.metaobjects:metaobjects-codegen-kotlin + metaobjects-metadata-ktx + metaobjects-maven-plugin to your pom.xml",
4
- "codegenCommand": "mvn metaobjects:generate"
4
+ "codegenCommand": "mvn metaobjects:generate",
5
+ "docsCommand": "mvn metaobjects:docs",
6
+ "apiSubDir": "api/kotlin"
5
7
  }
@@ -1,5 +1,7 @@
1
1
  {
2
2
  "displayName": "Python",
3
3
  "install": "pip install metaobjects",
4
- "codegenCommand": "metaobjects gen"
4
+ "codegenCommand": "metaobjects gen",
5
+ "docsCommand": "metaobjects docs",
6
+ "apiSubDir": "api/python"
5
7
  }
@@ -1,5 +1,7 @@
1
1
  {
2
2
  "displayName": "TypeScript",
3
3
  "install": "npm install -D @metaobjectsdev/cli @metaobjectsdev/codegen-ts",
4
- "codegenCommand": "npx meta gen"
4
+ "codegenCommand": "npx meta gen",
5
+ "docsCommand": "npx meta docs",
6
+ "apiSubDir": "api"
5
7
  }
@@ -118,19 +118,25 @@ code behind a grep hit; a "duplicate" validator's *divergence* is the finding.
118
118
  references repo-wide (grep the emitted symbol / module path for importers):
119
119
  delete it and stop generating it.
120
120
  - **Artifacts an entity doesn't need** — REST routes / TanStack grids / forms /
121
- hooks emitted for an entity that has no such surface. The per-entity opt-outs
122
- exist for exactly this (`@emitRoutes: false`, `@emitTanstack: false`, a
123
- `layout.dataGrid`-gated grid): flag the opt-out NOT used where the artifact is
124
- unused.
121
+ hooks emitted for an entity that has no such surface. The remedy is the
122
+ generator's own `filter` option (`routesFile({ filter: (e) => e.name !== "Audit" })`,
123
+ `tanstackQuery({ filter })`), which is ANDed with the generator's built-in gates
124
+ and so can only NARROW what emits; a grid additionally needs a `layout.dataGrid`
125
+ on the entity at all. Flag the unused artifact and name the filter that would
126
+ suppress it. **Never recommend an `@emit*` attribute** — `@emitRoutes`,
127
+ `@emitTanstack`, `@emitForm`, `@emitGrid` and `@emitAngular` were never
128
+ registered vocabulary, so they passed `meta gen` (open load) and FAILED
129
+ `meta verify` (strict). A project carrying one is a **finding**, not an opt-out:
130
+ report it and route to `meta upgrade --apply` plus the generator config above.
125
131
  - **Generators wired but unconsumed** — a generator in `metaobjects.config.ts`
126
132
  `generators: [...]` (or the per-port equivalent) whose whole output class no
127
133
  code imports: drop the generator rather than generate into the void.
128
134
  - **Wrong target / duplicate output** — the same logical artifact emitted to two
129
135
  places (a mis-set per-target `outDir`), one of which is orphaned.
130
- Recommend generating ONLY what is consumed — narrow the generator set + use the
131
- per-entity opt-outs. A smaller, fully-consumed generated surface beats a large one
132
- with dead files (which also make the leverage ratio lie; discount them from the
133
- census).
136
+ Recommend generating ONLY what is consumed — decide per generator: drop the ones
137
+ whose whole output class nothing imports, and narrow the ones that over-emit with
138
+ their `filter`. A smaller, fully-consumed generated surface beats a large one with
139
+ dead files (which also make the leverage ratio lie; discount them from the census).
134
140
  - [ ] **G. Runtime-contract anti-patterns.** Module-global `db` vs context-as-parameter
135
141
  (ADR-0008); wire-canonicalization in the query path vs native in-process return types
136
142
  (ADR-0019); runtime reflection to resolve a type from FQN vs generated static imports /
@@ -331,6 +337,7 @@ Per finding: `file:line` → what → generated-equivalent exists? → recommend
331
337
  - **Not expressible → carry it in `@sql` or `@unmanaged`, never a hand-edited migration (#208, ADR-0043).** When a NAMED irreducible construct blocks origin authoring — recursive CTE, window function / `OVER`, `UNION` / `INTERSECT` / `EXCEPT`, lateral join — the body still belongs in the metadata: carry the hand-written SQL in the `source.rdb` **`@sql`** escape — a read-only-`@kind` body the tool REGISTERS, fingerprints, and drift-checks (adopt a pre-existing view with `meta migrate --allow adopt-view`); `@sql` forbids `origin.*` children (two sources of truth). A DB object whose DDL is owned **entirely elsewhere** (Flyway / a hand-migration) → mark its source **`@unmanaged: true`** (legal on any `@kind` incl. `table`); `meta migrate` never creates/drops/drift-checks it and `verify --db` reports it as external. `@sql` and `@unmanaged` are mutually exclusive. **Only a view left *undeclared*** — neither modeled, nor `@sql`, nor `@unmanaged` — is truly *unmanaged*, invisible to `meta verify --db`, so this audit is the only gate that sees it. "It's an aggregation" is NOT an irreducibility justification (plain count/sum/avg/min/max rollups are `origin.aggregate`); nor is a `DISTINCT ON` pick-one-row (`origin.first`) or a non-aggregate expression column (`origin.computed`).
332
338
  9. **A closed variant-set hand-modeled per instance** — N sibling modules / classes / config blocks, one per channel / provider / target, sharing a payload + config shape and diverging only by transport. Grep for sibling-file families and switch-on-a-string dispatch; verify the set is closed and recurring (never a one-off). → axis I "New-vocabulary OPPORTUNITY" (VOCAB CANDIDATE, advisory).
333
339
  10. **N declarations of one FIELD across objects (same-name-field census)** — the field-level sibling of signature 6. Census field names across `object.*` nodes (`grep -rn 'name: <field>'` the metadata dir); a name recurring in ≥2 objects where a canonical owner exists — one whose name the field embeds (`<owner><Field>`: `wizardId` → `Wizard.id`, `orderTotal` → `Order.total`) or whose type+constraints it matches — is provenance loss → `extends: Owner.field` (dotted child targets, ADR-0029). **VERIFY by diffing the copies' attrs: a `@maxLength` / `@required` / validator divergence across them is drift already shipping — cite it.** Evidence multiplier: `extends` already used elsewhere in the repo raises confidence. Do NOT flag: generic names on unrelated concepts (`id` / `name` / `status` with no owner-embedding name and no matching constraints); required per-node attrs a loader forces (e.g. `payloadRef` / `format` on sibling `template.prompt` nodes — a product constraint, not a copy).
340
+ 11. **A physical table / column / schema name spelled as a literal outside its metadata declaration** — in a hand-written repository, raw SQL, a migration script, a log line, or a hand-maintained body-to-column map. Every port emits a per-object names artifact (`<Entity>Names` / `<entity_snake>_names.py`) from the declaration, so a literal is a second spelling of the same fact; and because the physical column is free-form, deriving it from the field name is a guess that fails silently. **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; on the JVM the names generator is opt-in, so an un-wired pom is the first finding and wiring `SpringNamesGenerator` / `KotlinNamesGenerator` the first remedy. **Do NOT flag:** a typed ORM handle in its place (a Drizzle column object, an Exposed `Column`, an EF property — replacing one with a string is a regression); a physical name inside a `source.rdb` hand-written SQL body (a metadata document cannot import a constant, which is why that body is fingerprinted); a flattened value-object composite column (it belongs to no single field of either object); a write-through entity's replica view name (the artifact holds the PRIMARY source's); a relationship-synthesized foreign-key column (derived, never declared). A hand-written repository that imports `<Entity>Names` is the gold standard — call it out as good.
334
341
 
335
342
  ---
336
343
 
@@ -66,7 +66,12 @@ classify it (using the classification scheme in `SKILL.md`) and route the cutove
66
66
  ## Source — `source.rdb`
67
67
 
68
68
  - **`source.rdb`** (`@table`, `@schema`) — hunt hard-coded physical table/schema names that
69
- diverge from the default naming the source models.
69
+ diverge from the default naming the source models, AND any physical table/column/schema
70
+ name spelled as a literal outside its declaration — raw SQL, a hand-written repository, a
71
+ migration script, a body-to-column map (drift signature 11). Every port emits a per-object
72
+ names artifact from the declaration, so a literal is a second source of truth even when it
73
+ agrees with the naming strategy today. A typed ORM handle in its place is correct; on the
74
+ JVM an un-wired names generator is the finding first.
70
75
  - **`@kind` = `view` / `materializedView`** — hunt hand-written SQL views where an authored
71
76
  read-only source belongs. Apply the **view-necessity test** (SKILL.md, drift signature 8): a
72
77
  hand-written `CREATE VIEW` (or read-only SQL mirroring a read model) is a CODEGEN CANDIDATE when
@@ -51,6 +51,7 @@ rejected (exit 2).
51
51
  | `FromSqlInterpolated(` outside `.g.cs` | stored-proc call — candidate for the `callable` generator |
52
52
  | `// keep in sync with` / `// mirrors the` | second-source-of-truth comment — always a finding |
53
53
  | `HasPrecision(` hand-coded | `field.decimal` with `@precision`/`@scale` drives this from the `entity` generator |
54
+ | 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 `<Entity>Names.g.cs` (`AuthorNames.SourcePrimaryTable` / `<Field>Column`, default suite — `Names.Name` is the OBJECT's name, not the table); an EF property inside LINQ is the typed handle — correct |
54
55
 
55
56
  ---
56
57
 
@@ -59,7 +60,15 @@ rejected (exit 2).
59
60
  C# does not scaffold-and-own generators — the generators are provided by
60
61
  `MetaObjects.Codegen` and are not meant to be copied into the consumer project.
61
62
  There is no analog to the TS `codegen/generators/*.ts` pattern here. Generator
62
- selection uses stable names via `dotnet meta gen --generators <names>`.
63
+ selection uses stable names via `dotnet meta gen --generators <names>`, over a
64
+ **closed built-in registry** — there is no seam to register a generator of your own.
65
+
66
+ **So do not score a C# project down for "not owning its generators", and do not
67
+ recommend writing one.** The customization path here is the **declarative template**:
68
+ `dotnet meta gen --template-spec <json> --template-root <dir>`, whose entries append to
69
+ the default suite. A finding of the form "the built-ins do not emit the shape this
70
+ project needs" resolves to a template-spec, not to generator code. Worked example with
71
+ the full JSON: `docs/ports/csharp.md`.
63
72
 
64
73
  To re-scaffold the agent-context into a C# project, use the Node `meta` CLI (the
65
74
  single agent-docs assembler per ADR-0033):
@@ -51,6 +51,7 @@ plugin `<configuration>` — that is the source of truth for which generators ru
51
51
  | `// keep in sync with` / `// mirrors the` | second-source-of-truth comment — always a finding |
52
52
  | `ObjectMapper.readValue(` outside a `*Parser.java` file | check if a responding `template.prompt` (`@responseRef`) exists — output-parser codegen ships in Java |
53
53
  | `LIMIT ?` / `OFFSET ?` assembled by hand | generated CRUD routes handle pagination; OMDB `getObjects` accepts `QueryOptions` |
54
+ | a table/column string in a repository implementation — JDBC SQL, jOOQ `table("authors")`, a Spring Data query | second spelling of a declared physical name — reference `<Entity>Names` (`AuthorNames.SOURCE_PRIMARY_TABLE` / `_COLUMN`; opt-in `SpringNamesGenerator`): an un-wired generator is the finding first |
54
55
 
55
56
  ---
56
57
 
@@ -60,6 +61,15 @@ Java does not scaffold-and-own generators — they are provided by
60
61
  `metaobjects-codegen-spring` and wired by FQ class name in `pom.xml`. There is no
61
62
  analog to the TS `codegen/generators/*.ts` pattern here.
62
63
 
64
+ Java does, however, have **both** authoring paths, so a project needing a shape the
65
+ built-ins do not emit has two legitimate answers and neither is a finding on its own:
66
+ a **programmatic** generator (implement `com.metaobjects.generator.Generator`, name the
67
+ class in a `<generator>` element — the plugin loads it from the project classpath), or a
68
+ **declarative template** (`TemplateScopeGenerator`, wired with `<template>` / `<scope>` /
69
+ `<outputPattern>` / `<format>` / `<templatesDir>`). Prefer the template when the output
70
+ SHAPE is the thing being iterated on or the same output is wanted in another language;
71
+ prefer code when the logic is gnarly. See `docs/features/codegen-concepts.md` §3.
72
+
63
73
  To re-scaffold the agent-context into a Java project, use the Node `meta` CLI (the
64
74
  single agent-docs assembler per ADR-0033):
65
75
 
@@ -56,6 +56,7 @@ The active generator list is declared in `pom.xml` under the plugin `<configurat
56
56
  | `@Serializable` on a payload class not in generated sources | `KotlinPayloadGenerator` should own this; check `pom.xml` generator list |
57
57
  | `// keep in sync with` / `// mirrors the` | second-source-of-truth comment — always a finding |
58
58
  | `transaction(db) {` bodies that duplicate every CRUD operation | hand-written Exposed transactions are expected (see Calibration), but if they duplicate generated-CRUD logic exactly, audit further |
59
+ | a table/column string in `exec("…")` raw SQL or a Flyway script | reference `<Entity>Names` (`KotlinNamesGenerator`, opt-in); `AuthorTable.name` inside the Exposed DSL is the typed handle — correct, not a finding |
59
60
 
60
61
  ---
61
62
 
@@ -65,6 +66,16 @@ Kotlin does not scaffold-and-own generators — they are provided by
65
66
  `metaobjects-codegen-kotlin` and wired by FQ class name in `pom.xml`. There is no
66
67
  analog to the TS `codegen/generators/*.ts` pattern here.
67
68
 
69
+ Kotlin does, however, have **both** authoring paths — it shares the JVM engine — so a
70
+ project needing a shape the built-ins do not emit has two legitimate answers and neither
71
+ is a finding on its own: a **programmatic** generator (implement
72
+ `com.metaobjects.generator.Generator`, name the class in a `<generator>` element), or a
73
+ **declarative template** (`TemplateScopeGenerator`, wired with `<template>` / `<scope>` /
74
+ `<outputPattern>` / `<format>` / `<templatesDir>` — no KotlinPoet involved). Prefer the
75
+ template when the output SHAPE is the thing being iterated on or the same output is
76
+ wanted in another language; prefer code when the logic is gnarly. See
77
+ `docs/features/codegen-concepts.md` §3.
78
+
68
79
  To re-scaffold the agent-context into a Kotlin project, use the Node `meta` CLI (the
69
80
  single agent-docs assembler per ADR-0033):
70
81
 
@@ -52,14 +52,24 @@ regardless of server language — see the migration reference.
52
52
  | `def get_all_` / `def create_` / `def update_` / `def delete_` in non-generated files | hand-rolled CRUD — compare to the generated router |
53
53
  | `# keep in sync with` / `# mirrors the` | second-source-of-truth comment — always a finding |
54
54
  | `try: ... except KeyError` / `?? ''` around format strings in prompt code | silent-degradation hack around a prompt payload — flag it |
55
+ | a table/column string in the repository implementation (SQLAlchemy Core, asyncpg/psycopg SQL) | second spelling of a declared physical name — reference `<entity>_names.py` (`AUTHOR_SOURCE_PRIMARY_TABLE` / `AUTHOR_<FIELD>_COLUMN`, default suite); no typed handle exists on this port, so that is never the reason to waive it |
55
56
 
56
57
  ---
57
58
 
58
59
  ## Owned generators
59
60
 
60
61
  Python does not scaffold-and-own generators — they are provided by the `metaobjects`
61
- package and selected by stable name via `--generators`. There is no analog to the
62
- TS `codegen/generators/*.ts` pattern here.
62
+ package and selected by stable name via `--generators`, over a **closed built-in
63
+ registry**. There is no analog to the TS `codegen/generators/*.ts` pattern here, and no
64
+ seam to register a generator of your own. (`--provider module:symbol` registers
65
+ **metamodel vocabulary**, not a generator — do not read it as one.)
66
+
67
+ **So do not score a Python project down for "not owning its generators", and do not
68
+ recommend writing one.** The customization path here is the **declarative template**:
69
+ `metaobjects gen --template-spec <json> --templates <dir>`, whose entries append to the
70
+ default suite. A finding of the form "the built-ins do not emit the shape this project
71
+ needs" resolves to a template-spec, not to generator code. Worked example with the full
72
+ JSON: `docs/ports/python.md`.
63
73
 
64
74
  To re-scaffold the agent-context into a Python project, use the Node `meta` CLI (the
65
75
  single agent-docs assembler per ADR-0033):
@@ -14,8 +14,12 @@ Generated files carry the header:
14
14
  // @generated by @metaobjectsdev/codegen-ts
15
15
  ```
16
16
 
17
- The runner **only overwrites files bearing this header** — hand-edited sibling
18
- files (`<Entity>.extra.ts`) are never touched. Locate generated output at the
17
+ The header is **informational on this port** — the write decision never reads it. What
18
+ the runner overwrites is decided by `.metaobjects/.gen-state/`: the snapshot body if the
19
+ machine has one (three-way merge), otherwise the committed `.hashes.json` (byte-for-byte
20
+ what it wrote ⇒ overwrite; anything else ⇒ refused, exit 1). A sibling file you created
21
+ (`<Entity>.extra.ts` by convention) is never touched because it is not a path the runner
22
+ records — not because of its name, and not because of the header. Locate generated output at the
19
23
  `outDir` (and per-target `outDir` values) declared in `metaobjects.config.ts`. With
20
24
  the default single-target config, generated files land at the one configured
21
25
  `outDir` (e.g. `src/generated/`).
@@ -50,6 +54,7 @@ value-sets against the constructs that replace them.
50
54
  | `// keep in sync with` / `// mirrors the` | second-source-of-truth comment — always a finding |
51
55
  | camelCase↔snake_case mapping tables | generated views handle this; diff field by field |
52
56
  | `@generated` file with hand edits | three-way merge preserves them; review at codegen; never delete |
57
+ | a table/column string in a `sql` fragment, a Kysely identifier, or a migration/seed script outside the generated dir | second spelling of a declared physical name — reference `<Entity>.names.ts` (`ProgramNames.fields.<f>.column`); a Drizzle column object in its place is correct, not a finding |
53
58
 
54
59
  ---
55
60
 
@@ -49,10 +49,19 @@ aggregate — that is almost always **metadata you have not declared yet.** In o
49
49
  1. **Search the vocabulary** — `meta types <term>`, or `meta types --all
50
50
  <what-it-does>` to search by behavior. There are field subtypes, relationships,
51
51
  projections, origins, identities, sources, and attributes you may not know exist.
52
- Find the construct that models it.
52
+ Find the construct that models it. Add `--detail` for one construct's valid
53
+ `@attrs`, or `--format json` for the same answer as one machine-readable document
54
+ — that form carries every match (`--limit` never truncates it) with each attr's
55
+ `allowedValues`, so you read the accepted values rather than guessing them.
53
56
  2. **Declare it and generate** — then *consume* the generated query/type/route;
54
57
  never reimplement it alongside.
55
- 3. **Only if no construct can express it** and you have actually looked
58
+ 3. **If the model is right but the generated OUTPUT is wrong, change your generator.**
59
+ Naming, file layout, imports, framework, signatures are generator concerns, not
60
+ reasons to hand-write. The generators are in *your* repo and are yours to edit — a
61
+ standing rule not to change the MetaObjects repo does not reach them; they are a
62
+ different repository. Editing one is ordinary work, not an escalation. (See
63
+ `metaobjects-codegen` → "Your generators are yours".)
64
+ 4. **Only if no construct can express it** — and you have actually looked —
56
65
  hand-write it, wired to generated types. Business algorithms, external
57
66
  integrations, and bespoke interactions are legitimately hand-written; CRUD,
58
67
  validation, finders, relationships, and derived/aggregate data are not.
@@ -86,10 +95,20 @@ Read the existing code and schema *first*, then model to reproduce them:
86
95
 
87
96
  **Customize the CODEGEN to match the existing code before you change the existing code.**
88
97
  If generated output doesn't match the code's shape (naming, file layout, imports,
89
- signatures), **tune the generator/template/config to reproduce it** — that is the
90
- intended adoption path (owned generators, `outputPattern`, naming strategy see the
91
- `metaobjects-codegen` skill), **not a hack**. Reshaping working call sites to satisfy
92
- the generator's defaults is the *last* resort, not the first.
98
+ signatures), **tune the generator/template/config to reproduce it** — naming strategy
99
+ first, then the generator or the template. **Which lever you have depends on the port,
100
+ so establish that before planning:** on TypeScript and the JVM you can own a generator
101
+ outright (TS scaffolds copies into your repo; the JVM loads your class from the project
102
+ classpath), while on C# and Python the generator registry is closed and a **Mustache
103
+ template** is the customization path — a real one, with `scope` and `outputPattern`
104
+ doing the walk and the file naming. The `metaobjects-codegen` skill has the per-port
105
+ matrix and defines `outputPattern`; read it before concluding a shape is unreachable.
106
+ **Whichever lever you get, it is yours to edit.** A standing instruction not to
107
+ change the MetaObjects repo says nothing about your own generators or templates —
108
+ reading it as though it did is how an adoption ends up hand-written, and it is the most
109
+ common way this step is skipped. Editing a generator or a template here is ordinary
110
+ adoption work: not an escalation, not a hack, and nothing to ask permission for. Reshaping working call sites to satisfy the
111
+ generator's defaults is the *last* resort, not the first.
93
112
 
94
113
  **Minimize churn to code the generator is not replacing.** The ONLY existing code that
95
114
  should change is the hand-written layer codegen now **owns** (the hand-rolled
@@ -743,6 +762,16 @@ Multi-source: multiple `source.rdb` children, each with a `@role`, exactly one
743
762
  { "source.rdb": { "@kind": "view", "@view": "v_author", "@schema": "blog" } }
744
763
  ```
745
764
 
765
+ **This declaration is the only place a physical name is ever spelled.** Codegen emits a
766
+ per-object names artifact from it (`<Entity>Names` — `ProgramNames.fields.createdAt.column`,
767
+ `ProgramNames.CreatedAtColumn`, `PROGRAM_CREATED_AT_COLUMN`, per port), the generated table
768
+ binding reads that artifact, and hand-written SQL, repositories and migration scripts
769
+ reference it — so a consumer never restates `v_author` or `created_at`, and a rename here
770
+ propagates. Declare `@column` explicitly whenever the physical name is not the naming
771
+ strategy's answer (`callPurpose` → `purpose_code`): nothing downstream can recover that
772
+ mapping by derivation, and the constant is what carries it. (See `metaobjects-codegen` →
773
+ "Never hand-write a physical name".)
774
+
746
775
  **An entity's PRIMARY source must be writable** (`table`) — read-only kinds are
747
776
  legal only in non-primary roles.
748
777