@metaobjectsdev/sdk 0.13.1 → 0.14.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.
package/README.md CHANGED
@@ -26,7 +26,7 @@ import { AGENT_DOCS_BODY, withContentHash } from "@metaobjectsdev/sdk/agent-docs
26
26
  ## agent-context
27
27
 
28
28
  `@metaobjectsdev/sdk/agent-context` assembles the downstream AI-assistant context
29
- (the slim `.metaobjects/AGENTS.md`/`CLAUDE.md` + the five `metaobjects-*` Claude
29
+ (the slim `.metaobjects/AGENTS.md`/`CLAUDE.md` + the six `metaobjects-*` Claude
30
30
  skills with only the project's language reference fragments) from the repo-root
31
31
  `agent-context/` source tree. `makeStack`/`detectStack` resolve the project's
32
32
  server+client axes; `assemble({ contentRoot, stack })` emits the files. Design:
@@ -2,7 +2,7 @@
2
2
 
3
3
  This tree is the single source the assembler (`@metaobjectsdev/sdk`,
4
4
  `src/agent-context/`) turns into the files scaffolded into a consumer project:
5
- the slim always-on Markdown (`.metaobjects/AGENTS.md` + `CLAUDE.md`) and the five
5
+ the slim always-on Markdown (`.metaobjects/AGENTS.md` + `CLAUDE.md`) and the six
6
6
  `metaobjects-*` Claude skills (each a universal `SKILL.md` plus the
7
7
  `references/<token>.md` fragments matching the project's resolved stack).
8
8
 
@@ -0,0 +1,262 @@
1
+ ---
2
+ name: metaobjects-audit
3
+ description: Use when assessing how well a project has adopted MetaObjects — greenfield first-pass or deep double-check; produces a scored, prioritized adoption-audit report covering codegen, runtime, drift-gates, and prompts.
4
+ ---
5
+
6
+ # MetaObjects adoption audit
7
+
8
+ **Thesis.** Typed metadata is the durable spine; generated code is the disposable
9
+ artifact. Hand-writing a layer the metadata could own creates a second source of
10
+ truth for one fact — it will drift. This audit hunts those second sources of truth
11
+ and proposes folding them into the spine.
12
+
13
+ **Boundary — read-only.** Deliverables: `.metaobjects/adoption-audit.json` (machine-readable
14
+ findings) + a rendered Markdown report. The audit **never edits code, never authors
15
+ metadata** — `metadata_sketch` per finding is a read-only proposal for human review.
16
+ Actual cutovers run through the existing skills mapped per finding tier (§ Bridge).
17
+
18
+ ---
19
+
20
+ ## Phase 0 — Triage (fast, mechanical)
21
+
22
+ - [ ] MetaObjects present? (`metaobjects/` dir, metadata sources, `@metaobjectsdev/*` /
23
+ `com.metaobjects:*` / `metaobjects` / `MetaObjects.*` deps).
24
+ - [ ] Count metadata source lines + all `@generated` / `DO NOT EDIT` files repo-wide.
25
+ - [ ] **Owned-generators check:** does the project own generators at `codegen/generators/*`
26
+ (scaffold-and-own via `meta init`), or still import the **deprecated** package export
27
+ (`@metaobjectsdev/codegen-ts/generators`)? Not owning is itself a finding.
28
+ - [ ] Classify: **Greenfield** (none/minimal) · **Partial** · **Deep** → choose path below.
29
+
30
+ ---
31
+
32
+ ## Phase 1a — Greenfield path
33
+
34
+ - [ ] **Shape inventory.** Catalog modelable shapes: entities/tables, DTOs, validation
35
+ schemas, routes, UI lists/forms, prompt sites.
36
+ - [ ] **Pick wedge:** one real entity (single-column PK, standard CRUD) to model first.
37
+ - [ ] **From-zero roadmap:** `meta init` → model the wedge → `meta gen` the data layer →
38
+ author a projection view → expand to routes/UI → add prompt pillar where LLM calls exist.
39
+ Owning the generators from day 1 is part of the roadmap.
40
+
41
+ ## Phase 1b — Partial / Deep path
42
+
43
+ **Census:** generated output line/file counts + metadata/owned-generator lines.
44
+ Compute **leverage ratio** = `generated_lines / (metadata_lines + generator_lines)`;
45
+ healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×).
46
+
47
+ **Coverage matrix:** per entity/projection/value — query helpers? view? routes? UI?
48
+ The gap between "modeled + query helpers" and "has view + route + UI" is the headline
49
+ lopsidedness.
50
+
51
+ **Surface review — 8 axes (independently runnable).**
52
+ Work the full `references/capability-checklist.md` on every axis. Check calibration
53
+ guards (§ Calibration) before raising a finding. **Verify, don't assume** — read the
54
+ code behind a grep hit; a "duplicate" validator's *divergence* is the finding.
55
+
56
+ - [ ] **A. Codegen candidates — API / server routes.** Catalog + classify every handler.
57
+ - [ ] **B. Codegen candidates — web / client.** Pages, data layer (hooks, central fetch),
58
+ grids/forms/filters vs `layout.dataGrid` / form generators / filter-allowlist.
59
+ - [ ] **C. Drift hotspot — validators, mappers, runtime models.** Hand validators / DTO-mappers /
60
+ dataclasses shadowing a generated shape. Diff field-by-field; the divergence is the bug.
61
+ - [ ] **D. Prompt pillar.** Every LLM prompt-construction site (see § Prompt anti-patterns).
62
+ - [ ] **E. Owned generators & scaffold-and-own** (see § Owned-codegen assessment).
63
+ - [ ] **F. Drift-gate adoption.** Is `meta verify` wired into CI / pre-commit? Which
64
+ subverbs (`--codegen` / `--templates` / `--db`)? Committed-codegen freshness gate?
65
+ Advisories heeded? Routine `--no-verify` bypass? Loader `ERR_*` / warnings addressed?
66
+ Parse the stable `code` field, not message text (ADR-0009).
67
+ - [ ] **G. Runtime-contract anti-patterns.** Module-global `db` vs context-as-parameter
68
+ (ADR-0008); wire-canonicalization in the query path vs native in-process return types
69
+ (ADR-0019); runtime reflection to resolve a type from FQN vs generated static imports /
70
+ FQN registry (ADR-0001 / 0017); process-global registry vs per-loader (ADR-0014); code
71
+ that **mutates the loaded metadata tree** (read-only after load); JVM/Kotlin missing
72
+ startup validator; writes not routed to the `@role: primary` source.
73
+ - [ ] **H. Authoring-correctness / ADR-conformance (deep).** Invented/unregistered
74
+ `@`-attrs or post-bootstrap registration (ADR-0023 — custom attrs belong in a registered
75
+ provider or `attr.properties`); retired source-v2 forms (`source.dbTable` / `@name` /
76
+ `@dbColumn` → use `source.rdb` + `@kind` + `@table` / `@column` + `@role`, ADR-0007/0018);
77
+ taxonomy impurity (entity over read-only primary source; read model that should be
78
+ `object.projection`; `value` carrying identity/source, ADR-0028); copy-pasted base-field
79
+ blocks instead of abstract + `extends`; `@`-prefixed YAML keys (ADR-0006); relative refs
80
+ in committed canonical JSON (ADR-0032); DB-type-as-logical-subtype (ADR-0013); per-port
81
+ migration engine where schema is Node-`meta`-owned (ADR-0015).
82
+
83
+ **Phase 4 — Synthesize** into the tiered roadmap and populate both artifacts.
84
+
85
+ ---
86
+
87
+ ## Classification scheme (every surface; classify on codegen AND runtime)
88
+
89
+ | Class | Meaning | Action |
90
+ |---|---|---|
91
+ | **GENERATED** | Driven by metadata (regenerable). | Confirm it regenerates clean. |
92
+ | **OWNED-GENERATOR** | `codegen/generators/*` file the project owns. | Confirm clean regen; flag drift from reference template. |
93
+ | **CODEGEN CANDIDATE (high)** | Standard CRUD/list/form over a modeled or modelable entity. | Author the view + generate; parity-gate. |
94
+ | **CODEGEN CANDIDATE (partial)** | Generatable data layer, bespoke presentation. | Generate data layer; keep viz hand-written. |
95
+ | **DYNAMIC-RUNTIME CANDIDATE** | Behavior that could be metadata-driven at runtime. | Assess runtime-metadata feasibility. |
96
+ | **BESPOKE (keep)** | Genuine custom: aggregations, graph, SSE, auth, search, viz. | Leave hand-written — still import generated types. |
97
+
98
+ **Gold-standard exception.** A hand-written component that *derives* from generated metadata
99
+ cannot drift — flag as good. A "bespoke" component hardcoding a shape metadata knows is a
100
+ hidden candidate. **Stub trap:** demo-data routes have nothing to replace — classify
101
+ "candidate (future) — not DB-backed".
102
+
103
+ ---
104
+
105
+ ## Drift signatures (highest-value; grep-then-verify)
106
+
107
+ Per finding: `file:line` → what → generated-equivalent exists? → recommendation.
108
+
109
+ 1. **Hand validators shadowing a generated schema** — diff field-by-field; divergence is the bug.
110
+ 2. **Field-by-field serialize / deserialize / DTO↔model / row mappers** — silently drops a field when metadata grows one.
111
+ 3. **camelCase↔snake_case / body↔column maps** maintained beside a generated view that already renames.
112
+ 4. **Drift-admitting comments** — grep: `"keep in sync with"` / `"mirrors the"` / `"matching the"`.
113
+ 5. **Runtime schema patching** (`ALTER TABLE … ADD COLUMN IF NOT EXISTS`, `_ensure_schema()`) — N schema owners.
114
+ 6. **N declarations of one shape** — same entity as Drizzle table + Zod schema + Pydantic model + hand dataclass; target is 1 + N generated.
115
+
116
+ ---
117
+
118
+ ## Owned-codegen & scaffold-and-own assessment
119
+
120
+ - If config imports deprecated `@metaobjectsdev/codegen-ts/generators` instead of
121
+ owned `codegen/generators/*`, recommend the scaffold-and-own migration (`meta init`).
122
+ - Audit owned generators: (a) regenerate clean? (b) drifted from reference templates —
123
+ intentional (good) vs stale/accidental (missed upstream fix)? (c) hand-rolling a walk
124
+ that a declarative `scope` + `outputPattern` could replace? (d) bespoke shape better as
125
+ a `templateGenerator` than a forked generator?
126
+ - **Authoring ladder:** built-in fits → use it · close → **own + customize** (the default)
127
+ · new shape → **author a declarative template-spec / custom generator from the metadata**
128
+ · genuinely un-modelable → hand-write (still import the generated types).
129
+ - **Generator-gap check:** missing generators that block the biggest wins? Recommend per gap:
130
+ own + customize / author a template-spec / fix upstream / stopgap.
131
+ - **Verify the DB artifact, not just the types** — computed view columns may appear in the
132
+ contract but be dropped from the view DDL; the contract may lie.
133
+ - **Version skew:** check *actually-resolved* package versions, not declared; consuming a fix
134
+ requires a coordinated lockstep bump, not a source-file copy.
135
+
136
+ ---
137
+
138
+ ## Prompt anti-patterns (hunt per site; classify: fully-modeled / partial / fully-inline)
139
+
140
+ - Inline prompt strings (triple-quoted / template-literal constants in service code).
141
+ - Untyped payloads (`str.format(**dict)` / f-strings / ad-hoc dicts) — payload should be an
142
+ `object.value` with `origin.*` (`passthrough` / `aggregate` / `collection`) fields.
143
+ - Silent-degradation hack (`try/except KeyError` or `?? ''` around formatting) — flag every instance.
144
+ - Hand-rolled output parsing (regex / XML / ad-hoc JSON) vs declared `template.output` +
145
+ generated `parse*` / `safeParse*` / `extract*` parser. **Java hand-writes the Jackson
146
+ one-liner — do NOT flag it** (§ Calibration).
147
+ - Engine-side formatting breaking byte-identical render (prompt-cache exact-prefix hits
148
+ depend on byte-stability).
149
+ - `template.toolcall` candidates: LLM tool schemas hand-defined per call vs modeled
150
+ `toolcall @toolName/@payloadRef`.
151
+ - `@responseRef` + AI-trace: hand-parsed responses with no typed response shape; note that
152
+ `voRequest` / `voResponse` jsonb columns must be authored `field.object` — the loader
153
+ must not mutate the tree; vendor SDK client + pricing are BYO (ADR-0024).
154
+ - No `meta verify --templates` gate; no declared `@maxChars` / `@maxTokens` budget.
155
+
156
+ ---
157
+
158
+ ## Semantic-constraint ratification (prevents over-modeling)
159
+
160
+ When folding hand validators into metadata, apply human judgment per constraint.
161
+ A constraint enters **shared metadata** only if it is a **true cross-language domain
162
+ invariant**; a one-consumer preference stays in a thin local refinement layer.
163
+
164
+ Cross-field rules **are** modelable (`comparison` / `atLeastOne` / `requiredWhen` /
165
+ `presentIff`); ratification decides *which* belong in shared metadata. Output a
166
+ **ratification table**: KEEP-IN-METADATA / LOCAL-REFINEMENT / DROP + rationale —
167
+ human-approved, never applied silently. Distinguish `required` from has-a-safe-default
168
+ (`@default` often fixes the over-requiring bug). A core attr ripples cross-port; for a
169
+ one-consumer need, read it codegen-locally.
170
+
171
+ ---
172
+
173
+ ## Scoring & maturity model — three surfaces (no single global score; bands not decimals)
174
+
175
+ 1. **Headline MATURITY TIER** — Greenfield → Partial → Deep → Exemplary; worst-of with
176
+ prerequisite gating (a missing pillar can't be averaged away); rendered with **the single
177
+ next unmet check** ("you're Partial; the next rung needs `verify` in CI").
178
+ 2. **Per-pillar breakdown (never rolled into one number)** — `pillar | tier | top gap` over
179
+ codegen / runtime / drift-gate / prompts. This is the core deliverable.
180
+ 3. **Binary CI drift gate** — prominent and separate: **"Is `meta verify` drift detection
181
+ wired into CI?"** It is binary because the risk is binary.
182
+
183
+ Coarse bands only (none / some / most / all). Worst-of within a pillar. On re-run, grade
184
+ the delta. Lead with gaps, not the grade.
185
+
186
+ ---
187
+
188
+ ## Report
189
+
190
+ **Two artifacts:** `.metaobjects/adoption-audit.json` + rendered Markdown.
191
+
192
+ **Markdown sections (lead with Scorecard):** 0. Scorecard (tier + pillar table + CI gate) ·
193
+ 1. Triage + census · 2. Coverage matrix · 3. Per-surface classification tables · 4. Drift
194
+ findings (active bugs first) · 5. Owned-codegen + generator gaps · 6. Drift-gate adoption ·
195
+ 7. Runtime-contract + authoring-correctness (axes G+H) · 8. Semantic-constraint ratification ·
196
+ 9. Prompt-pillar assessment · 10. Prioritized roadmap: Tier 1 drift kill → Tier 2 existing
197
+ generators → Tier 3 new generators/projections → Tier 4 dynamic-runtime/prompts/cross-port.
198
+ Each roadmap item: LOC retired, prerequisite, **parity-gate before deleting hand-written code**.
199
+
200
+ **Each finding in `.metaobjects/adoption-audit.json`:**
201
+
202
+ | Field | Content |
203
+ |---|---|
204
+ | `id` | stable kebab id (e.g. `handwritten-crud-route`, `manual-zod-validator`) |
205
+ | `title` | "you hand-wrote X that metadata can generate / model" |
206
+ | `pillar` | `codegen` / `runtime` / `drift` / `prompt` |
207
+ | `surface` | `entity` / `route` / `validator` / `repository` / `dto` / `hooks` / `prompt` / `migration` |
208
+ | `capability` | the capability-checklist capability this maps to (e.g. `field.currency`, `relationship.@through`) |
209
+ | `locations[]` | exact `file:line` spans |
210
+ | `impact` | LOC eliminated + N call-sites + drift-risk (high/med/low) |
211
+ | `effort` | `trivial` / `small` / `medium` / `large` |
212
+ | `confidence` | bias to under-flagging (false-positive rate >15% is a kill criterion) |
213
+ | `metadata_sketch` | metadata you'd author to replace it — **read-only proposal only; never applied** |
214
+ | `next_command` | the exact command / skill that performs the cutover (see bridge below) |
215
+ | `parity_gate` | the specific check proving behavior-equivalence |
216
+ | `tier` | 1–4 |
217
+
218
+ Within each tier, sort by impact ÷ effort (quick wins first). Tier 1 leads.
219
+
220
+ ### Audit → action bridge
221
+
222
+ The audit never edits code. Pattern: **dry-run → review the diff → apply**.
223
+
224
+ - Propose metadata → `metaobjects-authoring` + brainstorming flow (human reviews).
225
+ - Generate → `meta gen`; **`meta gen --dry-run`** is the review-the-diff step → skill:
226
+ `metaobjects-codegen`.
227
+ - Prove parity → **`meta verify --codegen`** is the drift gate → skill: `metaobjects-verify`.
228
+ - Routes / runtime / web → skill: `metaobjects-runtime-ui`.
229
+ - Prompts → skill: `metaobjects-prompts`.
230
+ - Cut over **one surface at a time, one commit each**.
231
+ - A separate guided-cutover skill (not this one) reads `adoption-audit.json` and walks
232
+ findings one tier/surface at a time with human approval at each step.
233
+
234
+ ---
235
+
236
+ ## Guardrails
237
+
238
+ - **Parity-gate every cutover** — prove behavior-equivalent before deleting hand-written code; generated schemas are often looser.
239
+ - **Verify, don't assume** — read the code behind a grep hit.
240
+ - **Verify the DB artifact, not just the types** — the contract may claim a column the view DDL dropped.
241
+ - **Don't let one bespoke action block generating the entity** — generate CRUD; mount the custom action alongside.
242
+ - **Consumption ≠ a dist copy across versions** — bump + rebuild lockstep and install.
243
+
244
+ ---
245
+
246
+ ## Calibration — port gaps & non-defects (do NOT flag these as adopter fault)
247
+
248
+ - **Filter-operator route codegen** is full only in **TS**; Java/Kotlin/C#/Python generate
249
+ pagination/sort/`withCount` but defer filter ops — do not flag hand-added filter handling.
250
+ - **Output-parser codegen** ships TS/C#/Python/Kotlin; **Java hand-writes the Jackson parse** — not a defect.
251
+ - **Python** still hand-wires the FastAPI router + repository impl around a generated
252
+ `APIRouter`; relationship / non-`table` source-kind / `field.object flattened` codegen is partial.
253
+ - **C#** has no ObjectManager runtime tier (EF Core is the runtime) — hand services over the generated `DbContext` are expected.
254
+ - **Cut subtypes** — `field.byte` / `field.short` / `field.class` are removed; never recommend them.
255
+ - **TS/web-only** — `view.*` widget subtypes exist only for TS/web consumers; only `view.base` / `view.currency` are cross-port-gated.
256
+ - **Planned, not shipped** — `api.*` / `operation.*` / `binding.*` (FR-024) and MCP exposure of declared prompts/tools are not yet in the registry; their absence is not an adopter defect.
257
+ - **Cross-port version skew is by design** — TS/C#/Python `0.x` vs Java/Kotlin `7.x` Maven is correct; never flag it. Flag only *intra-port* drift (mixed versions within one port, or a runtime package in `devDependencies`).
258
+ - **Stale upstream prose** — "hand-write the Spring controller" (Java/Kotlin) is out of date; trust `meta gen --list`, not stale prose.
259
+
260
+ ---
261
+
262
+ For this project's port specifics and the exhaustive capability checklist, read every `references/*.md` in this skill's directory.
@@ -0,0 +1,189 @@
1
+ # MetaObjects capability checklist (registry-grounded)
2
+
3
+ The **exhaustive** list of every modelable MetaObjects capability, each with its one-line
4
+ audit hunt: *"find a hand-written shape the metadata already describes."* Every
5
+ `type.subtype` and every `@`-prefixed attribute named here is verbatim from the cross-port vocabulary in
6
+ `fixtures/registry-conformance/expected-registry.json` (the
7
+ `agent-context-capability-grounding.test.ts` guard fails the build if a line claims a token
8
+ the registry lacks). Work this checklist on every axis so coverage is exhaustive; respect
9
+ the **inline calibration flags** so a per-port gap is never scored as the adopter's fault.
10
+
11
+ How to use a line: the capability is what the metamodel can express; the hunt is the
12
+ hand-written second-source-of-truth that should have been derived from it. Found one →
13
+ classify it (using the classification scheme in `SKILL.md`) and route the cutover to the right sibling skill.
14
+
15
+ ---
16
+
17
+ ## Object — `object.*`
18
+
19
+ - **`object.entity`** (`@discriminator` / `@discriminatorValue` for STI/TPH) — hunt
20
+ hand-written entity classes, DTOs, and repositories whose field list duplicates a modeled
21
+ entity; hand-rolled single-table-inheritance / type-discriminator switches that
22
+ `@discriminator`+`@discriminatorValue` already model.
23
+ - **`object.value`** — hunt hand-authored request / command / payload value objects (no
24
+ identity, no source) that restate a modeled `value` shape.
25
+ - **`object.projection`** — hunt hand-written read-model DTOs and the SQL views behind them
26
+ that a derived read-only projection (extends + origin-derived fields) already describes.
27
+ - **`object.base`** — abstract base; hunt copy-pasted shared field blocks that should be an
28
+ abstract base + `extends` (see cross-cutting).
29
+
30
+ ## Field — `field.*`
31
+
32
+ - **`field.string`** (`@maxLength`) — hunt hand-validated string-length checks the field
33
+ models.
34
+ - **`field.int` / `field.long` / `field.double` / `field.float`** — hunt ad-hoc numeric
35
+ columns / parsing the subtype already types.
36
+ - **`field.decimal`** (`@precision` / `@scale`) — hunt money or quantity stored as `float`/
37
+ `double` (lossy); the decimal subtype carries exact precision/scale.
38
+ - **`field.boolean`** — hunt int-or-string flags standing in for a boolean.
39
+ - **`field.currency`** (`@currency`, + `view.currency` `@locale`) — hunt money as float,
40
+ hand `*100` / `/100` minor-unit math, or server-side `Intl.NumberFormat`; storage is
41
+ integer minor units, formatting is client-side.
42
+ - **`field.date` / `field.time` / `field.timestamp`** (`@autoSet`) — hunt hand-stamped
43
+ `createdAt` / `updatedAt` assignments and ad-hoc temporal parsing; `@autoSet` stamps them.
44
+ - **`field.enum`** (`@values`) — hunt hand-written TS unions, language `enum`s, or DB
45
+ `CHECK ... IN (...)` lists that restate a modeled enum's members.
46
+ - **`field.uuid`** — hunt UUIDs typed as bare strings / hand-validated.
47
+ - **`field.object`** (`@objectRef`, `@storage`) — hunt hand-flattened owned columns or
48
+ hand-rolled jsonb (de)serialization the `@storage` mode (`flattened`/`jsonb`/`subdocument`)
49
+ already drives.
50
+ - **`field.map`** (`@valueType`) — hunt ad-hoc open-keyed key/value bags stuffed into a jsonb
51
+ column by hand.
52
+ - **Common field attrs** — `@column` (hand column-name mapping), `@default` (hand default
53
+ assignment), `@required` (hand presence checks), `@unique` (hand uniqueness), `@readOnly`
54
+ (hand write-guards), `@filterable` / `@sortable` (hand filter/sort allowlists),
55
+ `@dbColumnType` (hand native-type override), `@example` / `@instruction` (hand prompt
56
+ hints), `@xmlText` (hand XML-text mapping). Indexed-without-filter suppression is the
57
+ `db.indexed` attr (cite without the `@` sigil — it is a dotted attr name).
58
+ - **CALIBRATION — cut subtypes:** `field.byte`, `field.short`, `field.class` are
59
+ non-functional removed stubs. **Do NOT audit for them and never recommend them.**
60
+
61
+ ## Source — `source.rdb`
62
+
63
+ - **`source.rdb`** (`@table`, `@schema`) — hunt hard-coded physical table/schema names that
64
+ diverge from the default naming the source models.
65
+ - **`@kind` = `view` / `materializedView`** — hunt hand-written SQL views where an authored
66
+ projection source (read-only `@kind`) belongs.
67
+ - **`@kind` = `storedProc` / `tableFunction`** (`@parameterRef`) — hunt hand-called procs /
68
+ table functions that a modeled callable source with `@parameterRef` already describes.
69
+ - **`@role` = `primary`** (multi-source write-through) — hunt manual CQRS / write-through
70
+ wiring; exactly one `primary` source per object models it.
71
+ - **`source.base`** — abstract source base (no audit target of its own).
72
+
73
+ ## Relationship — `relationship.*`
74
+
75
+ - **1:N / N:1** (`@cardinality`, `@objectRef`) — hunt hand-written FK joins and type-unsafe
76
+ finders the cardinality + target reference already model.
77
+ - **M:N** (`@through`) — hunt hand junction-table queries where `@through` + the junction's
78
+ two `identity.reference` children generate the traversal.
79
+ - **Self-join** (`@symmetric`, `@sourceRefField`) — hunt hand-coded self-join / graph queries
80
+ that an undirected (`@symmetric`) or directed (`@sourceRefField`) self-relationship models.
81
+ - **Referential actions** (`@onDelete`, `@onUpdate`) — hunt app-code cascade/null-out logic
82
+ the relationship's referential actions express.
83
+ - **`relationship.association` / `relationship.aggregation` / `relationship.composition`** —
84
+ hunt ownership/lifecycle semantics (delete-with-parent, shared vs owned) coded by hand
85
+ instead of by the relationship subtype.
86
+ - **`relationship.base`** — abstract base.
87
+
88
+ ## Identity — `identity.*`
89
+
90
+ - **`identity.primary`** (`@generation`) — hunt hand-assigned primary keys / ID generation
91
+ the primary identity's `@generation` strategy models.
92
+ - **`identity.secondary`** (`@unique`, `@where`, `@expr`) — hunt raw-SQL partial or functional
93
+ unique indexes a secondary identity with `@where` / `@expr` describes.
94
+ - **`identity.reference`** (`@references`, `@enforce`) — hunt hand-written FK constraints /
95
+ reference enforcement the reference identity already declares.
96
+
97
+ ## Origin — `origin.*` (projection-field derivation)
98
+
99
+ - **`origin.aggregate`** (`@agg`, `@of`, `@via`) — hunt hand `COUNT` / `SUM` / `AVG`
100
+ subqueries or in-app rollups a derived aggregate field models.
101
+ - **`origin.passthrough`** (`@from`, `@via`) — hunt denormalized-by-hand copied fields that a
102
+ passthrough origin pulls across a relationship.
103
+ - **`origin.collection`** (`@via`) — hunt hand-assembled child-collection loading a collection
104
+ origin derives.
105
+ - **`origin.base`** — abstract base.
106
+
107
+ ## Validator — `validator.*`
108
+
109
+ - **`validator.required` / `validator.length` / `validator.numeric` / `validator.array` /
110
+ `validator.regex`** — hunt hand field-level validation (presence, length, numeric range,
111
+ array bounds, `@pattern` regex) the validator subtypes model.
112
+ - **Cross-field validators** — `validator.comparison` (`@left`/`@op`/`@right`, e.g.
113
+ "end ≥ start"), `validator.atLeastOne` (one-of-N present), `validator.requiredWhen`
114
+ (conditional-required), `validator.presentIff` (mutual presence). Hunt these as hand-coded
115
+ multi-field rules — they ARE modelable (see the Semantic-constraint ratification section in
116
+ `SKILL.md` to decide what belongs in shared metadata vs port-local).
117
+ - **`validator.base`** — abstract base.
118
+
119
+ ## View / Layout — `view.*`, `layout.*`
120
+
121
+ - **`view.currency`** (`@locale`) — hunt hand-passed currency locale / `Intl.NumberFormat`
122
+ options the currency view models. **Cross-port-gated** (with `view.base`).
123
+ - **`layout.dataGrid`** (`@columns`, `@defaultSortField`, `@defaultSortOrder`, `@pageSize`) —
124
+ hunt hand-written grid column definitions + data hooks a data-grid layout generates.
125
+ - **CALIBRATION — TS/web-only:** the `view.*` widget subtypes exist only for TS/web consumers
126
+ and are NOT in the cross-port registry — `view.text`, `view.textarea`, `view.date`,
127
+ `view.month`, `view.hotlink`, `view.dropdown`, `view.radio`, `view.checkbox`, `view.number`,
128
+ `view.password`, `view.hidden`, `view.web`. **Audit these only for TS adopters.** Only
129
+ `view.base` / `view.currency` are cross-port-gated.
130
+
131
+ ## Template — `template.*` (prompt pillar)
132
+
133
+ - **`template.prompt`** (`@payloadRef`, `@textRef`, `@responseRef`, `@requiredSlots`,
134
+ `@maxTokens`, `@maxChars`, `@format`, `@model`, `@promptStyle`) — hunt prompt strings
135
+ assembled inline in services, payloads built ad-hoc, output parsing without a typed
136
+ `@responseRef`, or token/char budgets enforced by hand.
137
+ - **`template.output`** (`@kind` = `document` | `email`; `@subjectRef`, `@htmlBodyRef`,
138
+ `@textBodyRef`) — hunt hand-built document/email rendering + hand-written
139
+ parse-on-receipt the output template + generated render helper/parser cover.
140
+ - **`template.toolcall`** (`@toolName`, `@payloadRef`) — hunt hand-declared LLM tool schemas
141
+ a modeled tool call describes.
142
+ - **`template.base`** — abstract base.
143
+
144
+ ## Attr — `attr.*`
145
+
146
+ - **`attr.properties`** — the sanctioned author key/value escape hatch; hunt ad-hoc metadata
147
+ stuffed into code comments / side-maps that could instead ride the properties bag.
148
+ - **`attr.filter`** — hunt hand-maintained preset filter definitions a modeled filter attr
149
+ holds.
150
+ - **`attr.class`** — binding facet (`field.class` binding); hunt hand-wired type-binding
151
+ facets. (`attr.base`, `attr.string`, `attr.int`, `attr.long`, `attr.double`, `attr.boolean`
152
+ are the value-type primitives behind typed attrs — not direct audit targets.)
153
+
154
+ ## Common documentation attrs (any node)
155
+
156
+ - `@description`, `@title`, `@summary`, `@notes`, `@deprecated`, `@replacedBy`, `@seeAlso`,
157
+ `@aliases` — hunt weak/absent generated docs and deprecation tracked only in code comments;
158
+ these doc attrs flow into JSDoc / XML-doc / Postgres `COMMENT` / Mermaid doc-gen, and
159
+ `@deprecated` / `@replacedBy` model lifecycle the codebase tracks by hand. (`@notes` is the
160
+ internal-only rationale slot — never emitted to user-facing doc-gen.)
161
+
162
+ ## Cross-cutting
163
+
164
+ - **`extends`** (any depth, cross-package `::`) — hunt copy-pasted base-entity field blocks
165
+ that should be an abstract base inherited via `extends` (the inheritance mechanism;
166
+ `origin.*` never inherits).
167
+ - **Filter + sort + pagination REST layer** — hunt hand-written query parsing, `LIMIT`/
168
+ `OFFSET` pagination, total-count queries, and filter/sort handling the generated CRUD layer
169
+ (8 filter operators + sort + `?limit=N&offset=N` + count) already provides.
170
+ - **CALIBRATION — per-port codegen gaps:** filter-operator route codegen is full only in
171
+ **TS**; Java / Kotlin / C# / Python generate pagination/sort/count but **defer filter
172
+ ops** — do NOT flag hand-added filter handling there. Output-parser codegen ships
173
+ TS/C#/Python/Kotlin; **Java hand-writes** the Jackson parse (acceptable). **Python**
174
+ still hand-wires the FastAPI router around a generated `APIRouter` (relationship /
175
+ non-`table` source-kind / flattened-object codegen is partial). **C#** has no
176
+ ObjectManager runtime tier (EF Core *is* the runtime) — hand services over the generated
177
+ `DbContext` are expected.
178
+ - **Single-source config** — `apiPrefix` (URL prefix wired into routes + hooks) and
179
+ `columnNamingStrategy` (snake_case / literal / kebab-case) and per-target output dirs:
180
+ hunt these values hard-coded in multiple places instead of resolved from config.
181
+ - **CALIBRATION — planned, not yet shipped:** the declared-API surface (`api.base`,
182
+ `api.operational`, `operation.query`, `operation.command`, `binding.rest`) and MCP exposure
183
+ of declared prompts/tools are **not yet in the registry** — their absence is not an adopter
184
+ defect; do NOT audit for them.
185
+ - **CALIBRATION — cross-port version skew is by design:** TS/C#/Python on the `0.x` line vs
186
+ Java/Kotlin on the `7.x` Maven line is correct — **never flag it.** Flag only *intra-port*
187
+ version drift (mixed package versions within one port, or a runtime package in
188
+ `devDependencies`). Trust the port docs + `meta gen --list`, not stale upstream prose
189
+ (e.g. the out-of-date "hand-write the Spring controller" note — controllers ARE generated).
@@ -0,0 +1,104 @@
1
+ # C# port — audit reference
2
+
3
+ The C# port targets .NET consumers (EF Core + ASP.NET Minimal API). Codegen runs
4
+ through the `dotnet meta` .NET tool (`MetaObjects.Cli`). Schema migration and
5
+ live-DB drift are **Node-`meta`-only** (ADR-0015) — `dotnet meta` has no `migrate`
6
+ subcommand and `dotnet meta verify --db` is rejected with exit 2.
7
+
8
+ ---
9
+
10
+ ## Finding generated files
11
+
12
+ Generated files are named `<Entity>.g.cs` and carry an `@generated` guard comment:
13
+
14
+ ```csharp
15
+ // @generated by MetaObjects.Codegen
16
+ ```
17
+
18
+ The metadata source directory is the first positional arg (`<metadataDir>`); `--out`
19
+ is required (errors if absent, no default). The runner overwrites only files bearing
20
+ this header; it refuses to touch files without it. Locate the output directory from
21
+ how the team invokes `dotnet meta gen` (or a CI script) — the arg is explicit, there
22
+ is no config file equivalent on the C# side.
23
+
24
+ ---
25
+
26
+ ## Run codegen + verify
27
+
28
+ ```bash
29
+ dotnet meta gen metaobjects --out Generated --namespace Acme.Generated
30
+
31
+ dotnet meta gen --list # list all registered generators
32
+ dotnet meta gen --generators entity,db-context,routes # select a subset
33
+
34
+ dotnet meta verify --codegen # regenerate to temp, diff vs committed output
35
+ dotnet meta verify --templates # check {{field}} refs vs payload VO (bare default)
36
+ ```
37
+
38
+ Schema migration + live-DB drift run through the **Node `meta` CLI** regardless of
39
+ server language — see the migration reference. `dotnet meta verify --db` is
40
+ rejected (exit 2).
41
+
42
+ ---
43
+
44
+ ## Drift signatures (what to grep for)
45
+
46
+ | Signature | What it means |
47
+ |---|---|
48
+ | `[Column(...)]` / `[Table(...)]` in hand-written `.cs` (not `.g.cs`) | hand-authored EF Core entity — candidate to generate |
49
+ | `DbSet<T>` in a hand-written `DbContext` subclass | generated `AppDbContext` (`db-context` generator) should own this |
50
+ | `HasConversion<string>()` by hand | the `entity` generator emits this for `field.enum`; duplicate if entity is modeled |
51
+ | `FromSqlInterpolated(` outside `.g.cs` | stored-proc call — candidate for the `callable` generator |
52
+ | `// keep in sync with` / `// mirrors the` | second-source-of-truth comment — always a finding |
53
+ | `HasPrecision(` hand-coded | `field.decimal` with `@precision`/`@scale` drives this from the `entity` generator |
54
+
55
+ ---
56
+
57
+ ## Owned generators
58
+
59
+ C# does not scaffold-and-own generators — the generators are provided by
60
+ `MetaObjects.Codegen` and are not meant to be copied into the consumer project.
61
+ 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
+
64
+ To re-scaffold the agent-context into a C# project, use the Node `meta` CLI (the
65
+ single agent-docs assembler per ADR-0033):
66
+
67
+ ```bash
68
+ npx meta agent-docs --server csharp [--out <dir>]
69
+ ```
70
+
71
+ (`dotnet meta agent-docs` is a non-executing stub — it prints this redirect and exits 1.)
72
+
73
+ ---
74
+
75
+ ## Version-skew check
76
+
77
+ The resolved NuGet package version is what actually ran codegen — check the lock file
78
+ or the restore log, not the `.csproj` range:
79
+
80
+ ```bash
81
+ dotnet list package --include-transitive | grep -i MetaObjects
82
+ ```
83
+
84
+ `MetaObjects` (loader), `MetaObjects.Codegen`, `MetaObjects.Render`, and
85
+ `MetaObjects.Cli` all publish in lockstep on the C# `0.x` line. A mixed resolved
86
+ version across these packages is an intra-port skew finding.
87
+
88
+ ---
89
+
90
+ ## Calibration gaps (do NOT flag these)
91
+
92
+ - **No ObjectManager runtime tier.** C# has no `ObjectManager` equivalent — EF Core
93
+ (the generated `AppDbContext` + generated entity classes) **is** the runtime persistence
94
+ layer. Hand-written service classes over the generated `AppDbContext` are the expected
95
+ pattern, not a defect.
96
+ - **No C# migrate command.** Schema migration is Node-`meta`-owned for every port
97
+ (ADR-0015). `dotnet meta` has no migrate subcommand; `meta migrate` is correct.
98
+ - **Filter-operator route codegen deferred.** The generated `<Entity>Routes.cs` (Minimal
99
+ API) supports `?sort`, `?limit`/`?offset`, and `?withCount=1` envelope, but filter
100
+ operators (`?filter[field][op]=value`) are not yet generated. Do NOT flag hand-added
101
+ C# filter handling as an adopter defect.
102
+ - **Output-parser codegen ships in C#.** `output-parser` / `extractor` / `render-helper`
103
+ generators are available (`dotnet meta gen --generators output-parser`). Absence of
104
+ wired output parsers where `template.output` nodes exist IS a finding.