From a3ecef2a2a665c30c0a0a1a9c537bcd15ca1fe5b Mon Sep 17 00:00:00 2001 From: Doug Mealing Date: Sat, 10 Oct 2026 23:25:29 -0400 Subject: [PATCH 1/3] docs(skills): cost test for owned generators; audit reports one-off layers as acceptable Codegen skill gains a when-not-to-write-a-generator test (generator counted as hand-written cost). Audit skill applies it and pairs any generated-share figure with the net hand-written line change. Goldens regenerated. --- CHANGELOG.md | 8 +++++++ .../skills/metaobjects-audit/SKILL.md | 17 +++++++++++--- .../skills/metaobjects-codegen/SKILL.md | 4 +++- .../references/owning-generators.md | 22 +++++++++++++++++-- .../.claude/skills/metaobjects-audit/SKILL.md | 17 +++++++++++--- .../skills/metaobjects-codegen/SKILL.md | 4 +++- .../references/owning-generators.md | 22 +++++++++++++++++-- .../.claude/skills/metaobjects-audit/SKILL.md | 17 +++++++++++--- .../skills/metaobjects-codegen/SKILL.md | 4 +++- .../references/owning-generators.md | 22 +++++++++++++++++-- .../.claude/skills/metaobjects-audit/SKILL.md | 17 +++++++++++--- .../skills/metaobjects-codegen/SKILL.md | 4 +++- .../references/owning-generators.md | 22 +++++++++++++++++-- .../.claude/skills/metaobjects-audit/SKILL.md | 17 +++++++++++--- .../skills/metaobjects-codegen/SKILL.md | 4 +++- .../references/owning-generators.md | 22 +++++++++++++++++-- .../.claude/skills/metaobjects-audit/SKILL.md | 17 +++++++++++--- .../skills/metaobjects-codegen/SKILL.md | 4 +++- .../references/owning-generators.md | 22 +++++++++++++++++-- 19 files changed, 230 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1250cd229..dc8a1066a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -356,6 +356,14 @@ Each one changes generated code on the next `gen`, or generated docs on the next or Python api-docs build, as its entry says. A drift gate that covers that output reports it until you regenerate. +- **Agent skills: a cost test for owned generators.** The + `metaobjects-codegen` skill now says when not to write a generator: only when generator plus + output is smaller than the hand-written code it replaces (generator code counted as + hand-written cost), more instances of the shape are expected, or drift protection is the + point; a general-shape generator belongs in the library. The `metaobjects-audit` skill applies + the same test: a one-off hand-written layer that fails it is reported as acceptable, and any + generated-share figure must carry the net hand-written line change and is never a target. + Documentation only; no code or generator changed. - **TypeScript and Python: a read-only projection with no declared identity loses its item surface, even when it has a field named `id`.** It no longer gets `GET /{id}` or the three item-verb refusals, its by-id query (`findById` in TypeScript, `find_by_id` on the Python diff --git a/agent-context/skills/metaobjects-audit/SKILL.md b/agent-context/skills/metaobjects-audit/SKILL.md index bbcde472f..d3fb6666a 100644 --- a/agent-context/skills/metaobjects-audit/SKILL.md +++ b/agent-context/skills/metaobjects-audit/SKILL.md @@ -8,7 +8,9 @@ description: Use when assessing how well a project has adopted MetaObjects — g **Thesis.** Typed metadata is the durable spine; generated code is the disposable artifact. Hand-writing a layer the metadata could own creates a second source of truth for one fact — it will drift. This audit hunts those second sources of truth -and proposes folding them into the spine. +and proposes folding them into the spine — where doing so costs fewer hand-written lines +than it saves, more of that shape are coming, or silent drift is the risk. A one-off layer +that fails that cost test is acceptable, not a defect. **Boundary — read-only.** Deliverables: `.metaobjects/adoption-audit.json` (machine-readable findings) + a rendered Markdown report. The audit **never edits code, never authors @@ -47,7 +49,10 @@ Actual cutovers run through the existing skills mapped per finding tier (§ Brid **Census:** generated output line/file counts + metadata/owned-generator lines. Compute **leverage ratio** = `generated_lines / (metadata_lines + generator_lines)`; -healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). +healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). **Never report a +generated-share or leverage figure alone, and never as a target:** pair it with the **net +hand-written line change** (app lines removed minus generator and metadata lines added; +generator code is hand-written cost). A share can rise while total hand-written code grows. **Coverage matrix:** per entity/projection/value — query helpers? view? routes? UI? The gap between "modeled + query helpers" and "has view + route + UI" is the headline @@ -314,6 +319,7 @@ with full force). Full guidance: `docs/features/downstream-metadata-decisions.md | **CODEGEN CANDIDATE (high)** | Standard CRUD/list/form over a modeled or modelable entity. | Author the view + generate; parity-gate. | | **CODEGEN CANDIDATE (partial)** | Generatable data layer, bespoke presentation. | Generate data layer; keep viz hand-written. | | **DYNAMIC-RUNTIME CANDIDATE** | Behavior that could be metadata-driven at runtime. | Assess runtime-metadata feasibility. | +| **ACCEPTABLE (one-off)** | A hand-written layer the metadata could own, but an owned generator for it would cost more lines than it replaces, no more instances are expected, and nothing drifts silently (it reads typed generated properties). | Report as acceptable. Not a defect, not a recommendation to generate. Re-class if it recurs or drift appears. | | **BESPOKE (keep)** | Genuine custom: irreducible SQL (recursive CTEs, window functions, set ops — NOT plain count/sum/avg rollups, which are `origin.aggregate` on a projection), graph, SSE, auth, search, viz. | Leave hand-written — still import generated types. | | **VOCAB CANDIDATE (advisory)** | A recurring, closed hand-coded pattern (parallel integration modules / an ad-hoc string discriminator) that project-registered vocabulary — a custom subtype/attr via a provider — plus a small owned generator would own. | Propose the provider + `metadata_sketch`; apply the ADR-0037 ordered test; advisory only. | @@ -385,7 +391,12 @@ Per finding: `file:line` → what → generated-equivalent exists? → recommend · new shape → **author a declarative template-spec / custom generator from the metadata** · genuinely un-modelable → hand-write (still import the generated types). - **Generator-gap check:** missing generators that block the biggest wins? Recommend per gap: - own + customize / author a template-spec / fix upstream / stopgap. + own + customize / author a template-spec / fix upstream / stopgap. **Cost-test every + recommendation to generate** (`metaobjects-codegen` → `references/owning-generators.md`): an + owned generator is worth it only if generator + output is smaller than the code it replaces, + more instances are expected, or drift protection is the point. A layer that fails is + ACCEPTABLE, and an owned generator larger than the code it replaced is itself a finding. + A general-shape generator belongs in the library, not in each app. - **Verify the DB artifact, not just the types** — computed view columns may appear in the contract but be dropped from the view DDL; the contract may lie. - **An *undeclared* hand-authored DB view is invisible to `meta verify --db`.** A view that is diff --git a/agent-context/skills/metaobjects-codegen/SKILL.md b/agent-context/skills/metaobjects-codegen/SKILL.md index 80ecfc8b3..6efd42f91 100644 --- a/agent-context/skills/metaobjects-codegen/SKILL.md +++ b/agent-context/skills/metaobjects-codegen/SKILL.md @@ -46,7 +46,9 @@ exact config, generator names and commands for this project; read it. The other or none emits it the way this application needs it. OpenAPI, JSON Schema, Zod schemas, DTOs or a client for a framework nothing here ships, a service layer in the house style, a data dictionary, a search-index mapping, test fixtures. That is a generator, and you -write it now, in this change. Do not hand-write the output (it drifts the day the model +write it now, in this change — once it passes the cost test in `references/owning-generators.md` +(smaller than the code it replaces, generator counted; more instances expected; or drift +protection is the point). Do not hand-write the output (it drifts the day the model changes and nothing notices), do not wait for MetaObjects to ship it, and do not ask permission: a generator in this repo is ordinary source you own. diff --git a/agent-context/skills/metaobjects-codegen/references/owning-generators.md b/agent-context/skills/metaobjects-codegen/references/owning-generators.md index e847508df..314536f57 100644 --- a/agent-context/skills/metaobjects-codegen/references/owning-generators.md +++ b/agent-context/skills/metaobjects-codegen/references/owning-generators.md @@ -52,6 +52,24 @@ same target rules, and is drift-gated the same way. The runner adds no header to on TypeScript, C# and Python none is needed, because the hash manifest records what was written. +**Is it worth a generator? Run the cost test first.** Write an adopter-owned generator only +when at least one holds: + +1. **It is smaller.** The generator plus its emitted output, plus the metadata it adds, is + fewer lines than the hand-written code it replaces. Count generator code as hand-written + cost: it is code the team reads, tests and maintains. A generated-line total does not + count; the emitted files cost nothing to write but the generator that makes them does. +2. **More instances are coming.** The app will keep adding entities, routes or events of this + shape, so the generator's cost is paid back as the model grows. +3. **Drift protection is the point.** A change to the model could pass the compiler and go + wrong silently (a runtime lookup by name, a string a consumer compares), and regeneration + plus `verify` is what catches it. + +Otherwise hand-write it: a one-off layer that reads typed generated properties cannot drift +and is a fine place to stop. A generator with a general shape (usable by any app on this +model) belongs in the MetaObjects library, not copied into each app; that is an upstream +request, not a reason to grow your own. + Write a new generator when the *shape* needs to change. When a reference's shape is right and only its *target* is wrong — a different framework than the reference emits for — eject that generator and retarget it instead; see "Your framework isn't the default" below. @@ -125,10 +143,10 @@ convention, not the method name. a normal source file. Copy it and customize the copy (three-way merge preserves your edits on regen), or customize the template a built-in renders from. Reach for a custom generator when you want the change applied **consistently across every -entity** (the scale win); a one-off edit when it's genuinely one file. +entity** and it passes the cost test above; a one-off edit when it's genuinely one file. **The decision ladder:** an output the model describes that no reference emits → write -a generator (the top of the skill) · a reference is close → eject it and customize the +a generator if it passes the cost test (the top of the skill) · a reference is close → eject it and customize the copy · a reference fits → use it · only the genuinely un-modelable (business algorithms, external calls) is hand-written outside codegen — and it still imports the generated types. diff --git a/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md b/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md index bbcde472f..d3fb6666a 100644 --- a/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md +++ b/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md @@ -8,7 +8,9 @@ description: Use when assessing how well a project has adopted MetaObjects — g **Thesis.** Typed metadata is the durable spine; generated code is the disposable artifact. Hand-writing a layer the metadata could own creates a second source of truth for one fact — it will drift. This audit hunts those second sources of truth -and proposes folding them into the spine. +and proposes folding them into the spine — where doing so costs fewer hand-written lines +than it saves, more of that shape are coming, or silent drift is the risk. A one-off layer +that fails that cost test is acceptable, not a defect. **Boundary — read-only.** Deliverables: `.metaobjects/adoption-audit.json` (machine-readable findings) + a rendered Markdown report. The audit **never edits code, never authors @@ -47,7 +49,10 @@ Actual cutovers run through the existing skills mapped per finding tier (§ Brid **Census:** generated output line/file counts + metadata/owned-generator lines. Compute **leverage ratio** = `generated_lines / (metadata_lines + generator_lines)`; -healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). +healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). **Never report a +generated-share or leverage figure alone, and never as a target:** pair it with the **net +hand-written line change** (app lines removed minus generator and metadata lines added; +generator code is hand-written cost). A share can rise while total hand-written code grows. **Coverage matrix:** per entity/projection/value — query helpers? view? routes? UI? The gap between "modeled + query helpers" and "has view + route + UI" is the headline @@ -314,6 +319,7 @@ with full force). Full guidance: `docs/features/downstream-metadata-decisions.md | **CODEGEN CANDIDATE (high)** | Standard CRUD/list/form over a modeled or modelable entity. | Author the view + generate; parity-gate. | | **CODEGEN CANDIDATE (partial)** | Generatable data layer, bespoke presentation. | Generate data layer; keep viz hand-written. | | **DYNAMIC-RUNTIME CANDIDATE** | Behavior that could be metadata-driven at runtime. | Assess runtime-metadata feasibility. | +| **ACCEPTABLE (one-off)** | A hand-written layer the metadata could own, but an owned generator for it would cost more lines than it replaces, no more instances are expected, and nothing drifts silently (it reads typed generated properties). | Report as acceptable. Not a defect, not a recommendation to generate. Re-class if it recurs or drift appears. | | **BESPOKE (keep)** | Genuine custom: irreducible SQL (recursive CTEs, window functions, set ops — NOT plain count/sum/avg rollups, which are `origin.aggregate` on a projection), graph, SSE, auth, search, viz. | Leave hand-written — still import generated types. | | **VOCAB CANDIDATE (advisory)** | A recurring, closed hand-coded pattern (parallel integration modules / an ad-hoc string discriminator) that project-registered vocabulary — a custom subtype/attr via a provider — plus a small owned generator would own. | Propose the provider + `metadata_sketch`; apply the ADR-0037 ordered test; advisory only. | @@ -385,7 +391,12 @@ Per finding: `file:line` → what → generated-equivalent exists? → recommend · new shape → **author a declarative template-spec / custom generator from the metadata** · genuinely un-modelable → hand-write (still import the generated types). - **Generator-gap check:** missing generators that block the biggest wins? Recommend per gap: - own + customize / author a template-spec / fix upstream / stopgap. + own + customize / author a template-spec / fix upstream / stopgap. **Cost-test every + recommendation to generate** (`metaobjects-codegen` → `references/owning-generators.md`): an + owned generator is worth it only if generator + output is smaller than the code it replaces, + more instances are expected, or drift protection is the point. A layer that fails is + ACCEPTABLE, and an owned generator larger than the code it replaced is itself a finding. + A general-shape generator belongs in the library, not in each app. - **Verify the DB artifact, not just the types** — computed view columns may appear in the contract but be dropped from the view DDL; the contract may lie. - **An *undeclared* hand-authored DB view is invisible to `meta verify --db`.** A view that is diff --git a/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-codegen/SKILL.md b/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-codegen/SKILL.md index 80ecfc8b3..6efd42f91 100644 --- a/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-codegen/SKILL.md +++ b/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-codegen/SKILL.md @@ -46,7 +46,9 @@ exact config, generator names and commands for this project; read it. The other or none emits it the way this application needs it. OpenAPI, JSON Schema, Zod schemas, DTOs or a client for a framework nothing here ships, a service layer in the house style, a data dictionary, a search-index mapping, test fixtures. That is a generator, and you -write it now, in this change. Do not hand-write the output (it drifts the day the model +write it now, in this change — once it passes the cost test in `references/owning-generators.md` +(smaller than the code it replaces, generator counted; more instances expected; or drift +protection is the point). Do not hand-write the output (it drifts the day the model changes and nothing notices), do not wait for MetaObjects to ship it, and do not ask permission: a generator in this repo is ordinary source you own. diff --git a/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md b/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md index e847508df..314536f57 100644 --- a/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md +++ b/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md @@ -52,6 +52,24 @@ same target rules, and is drift-gated the same way. The runner adds no header to on TypeScript, C# and Python none is needed, because the hash manifest records what was written. +**Is it worth a generator? Run the cost test first.** Write an adopter-owned generator only +when at least one holds: + +1. **It is smaller.** The generator plus its emitted output, plus the metadata it adds, is + fewer lines than the hand-written code it replaces. Count generator code as hand-written + cost: it is code the team reads, tests and maintains. A generated-line total does not + count; the emitted files cost nothing to write but the generator that makes them does. +2. **More instances are coming.** The app will keep adding entities, routes or events of this + shape, so the generator's cost is paid back as the model grows. +3. **Drift protection is the point.** A change to the model could pass the compiler and go + wrong silently (a runtime lookup by name, a string a consumer compares), and regeneration + plus `verify` is what catches it. + +Otherwise hand-write it: a one-off layer that reads typed generated properties cannot drift +and is a fine place to stop. A generator with a general shape (usable by any app on this +model) belongs in the MetaObjects library, not copied into each app; that is an upstream +request, not a reason to grow your own. + Write a new generator when the *shape* needs to change. When a reference's shape is right and only its *target* is wrong — a different framework than the reference emits for — eject that generator and retarget it instead; see "Your framework isn't the default" below. @@ -125,10 +143,10 @@ convention, not the method name. a normal source file. Copy it and customize the copy (three-way merge preserves your edits on regen), or customize the template a built-in renders from. Reach for a custom generator when you want the change applied **consistently across every -entity** (the scale win); a one-off edit when it's genuinely one file. +entity** and it passes the cost test above; a one-off edit when it's genuinely one file. **The decision ladder:** an output the model describes that no reference emits → write -a generator (the top of the skill) · a reference is close → eject it and customize the +a generator if it passes the cost test (the top of the skill) · a reference is close → eject it and customize the copy · a reference fits → use it · only the genuinely un-modelable (business algorithms, external calls) is hand-written outside codegen — and it still imports the generated types. diff --git a/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-audit/SKILL.md b/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-audit/SKILL.md index bbcde472f..d3fb6666a 100644 --- a/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-audit/SKILL.md +++ b/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-audit/SKILL.md @@ -8,7 +8,9 @@ description: Use when assessing how well a project has adopted MetaObjects — g **Thesis.** Typed metadata is the durable spine; generated code is the disposable artifact. Hand-writing a layer the metadata could own creates a second source of truth for one fact — it will drift. This audit hunts those second sources of truth -and proposes folding them into the spine. +and proposes folding them into the spine — where doing so costs fewer hand-written lines +than it saves, more of that shape are coming, or silent drift is the risk. A one-off layer +that fails that cost test is acceptable, not a defect. **Boundary — read-only.** Deliverables: `.metaobjects/adoption-audit.json` (machine-readable findings) + a rendered Markdown report. The audit **never edits code, never authors @@ -47,7 +49,10 @@ Actual cutovers run through the existing skills mapped per finding tier (§ Brid **Census:** generated output line/file counts + metadata/owned-generator lines. Compute **leverage ratio** = `generated_lines / (metadata_lines + generator_lines)`; -healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). +healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). **Never report a +generated-share or leverage figure alone, and never as a target:** pair it with the **net +hand-written line change** (app lines removed minus generator and metadata lines added; +generator code is hand-written cost). A share can rise while total hand-written code grows. **Coverage matrix:** per entity/projection/value — query helpers? view? routes? UI? The gap between "modeled + query helpers" and "has view + route + UI" is the headline @@ -314,6 +319,7 @@ with full force). Full guidance: `docs/features/downstream-metadata-decisions.md | **CODEGEN CANDIDATE (high)** | Standard CRUD/list/form over a modeled or modelable entity. | Author the view + generate; parity-gate. | | **CODEGEN CANDIDATE (partial)** | Generatable data layer, bespoke presentation. | Generate data layer; keep viz hand-written. | | **DYNAMIC-RUNTIME CANDIDATE** | Behavior that could be metadata-driven at runtime. | Assess runtime-metadata feasibility. | +| **ACCEPTABLE (one-off)** | A hand-written layer the metadata could own, but an owned generator for it would cost more lines than it replaces, no more instances are expected, and nothing drifts silently (it reads typed generated properties). | Report as acceptable. Not a defect, not a recommendation to generate. Re-class if it recurs or drift appears. | | **BESPOKE (keep)** | Genuine custom: irreducible SQL (recursive CTEs, window functions, set ops — NOT plain count/sum/avg rollups, which are `origin.aggregate` on a projection), graph, SSE, auth, search, viz. | Leave hand-written — still import generated types. | | **VOCAB CANDIDATE (advisory)** | A recurring, closed hand-coded pattern (parallel integration modules / an ad-hoc string discriminator) that project-registered vocabulary — a custom subtype/attr via a provider — plus a small owned generator would own. | Propose the provider + `metadata_sketch`; apply the ADR-0037 ordered test; advisory only. | @@ -385,7 +391,12 @@ Per finding: `file:line` → what → generated-equivalent exists? → recommend · new shape → **author a declarative template-spec / custom generator from the metadata** · genuinely un-modelable → hand-write (still import the generated types). - **Generator-gap check:** missing generators that block the biggest wins? Recommend per gap: - own + customize / author a template-spec / fix upstream / stopgap. + own + customize / author a template-spec / fix upstream / stopgap. **Cost-test every + recommendation to generate** (`metaobjects-codegen` → `references/owning-generators.md`): an + owned generator is worth it only if generator + output is smaller than the code it replaces, + more instances are expected, or drift protection is the point. A layer that fails is + ACCEPTABLE, and an owned generator larger than the code it replaced is itself a finding. + A general-shape generator belongs in the library, not in each app. - **Verify the DB artifact, not just the types** — computed view columns may appear in the contract but be dropped from the view DDL; the contract may lie. - **An *undeclared* hand-authored DB view is invisible to `meta verify --db`.** A view that is diff --git a/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-codegen/SKILL.md b/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-codegen/SKILL.md index 80ecfc8b3..6efd42f91 100644 --- a/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-codegen/SKILL.md +++ b/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-codegen/SKILL.md @@ -46,7 +46,9 @@ exact config, generator names and commands for this project; read it. The other or none emits it the way this application needs it. OpenAPI, JSON Schema, Zod schemas, DTOs or a client for a framework nothing here ships, a service layer in the house style, a data dictionary, a search-index mapping, test fixtures. That is a generator, and you -write it now, in this change. Do not hand-write the output (it drifts the day the model +write it now, in this change — once it passes the cost test in `references/owning-generators.md` +(smaller than the code it replaces, generator counted; more instances expected; or drift +protection is the point). Do not hand-write the output (it drifts the day the model changes and nothing notices), do not wait for MetaObjects to ship it, and do not ask permission: a generator in this repo is ordinary source you own. diff --git a/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md b/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md index e847508df..314536f57 100644 --- a/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md +++ b/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md @@ -52,6 +52,24 @@ same target rules, and is drift-gated the same way. The runner adds no header to on TypeScript, C# and Python none is needed, because the hash manifest records what was written. +**Is it worth a generator? Run the cost test first.** Write an adopter-owned generator only +when at least one holds: + +1. **It is smaller.** The generator plus its emitted output, plus the metadata it adds, is + fewer lines than the hand-written code it replaces. Count generator code as hand-written + cost: it is code the team reads, tests and maintains. A generated-line total does not + count; the emitted files cost nothing to write but the generator that makes them does. +2. **More instances are coming.** The app will keep adding entities, routes or events of this + shape, so the generator's cost is paid back as the model grows. +3. **Drift protection is the point.** A change to the model could pass the compiler and go + wrong silently (a runtime lookup by name, a string a consumer compares), and regeneration + plus `verify` is what catches it. + +Otherwise hand-write it: a one-off layer that reads typed generated properties cannot drift +and is a fine place to stop. A generator with a general shape (usable by any app on this +model) belongs in the MetaObjects library, not copied into each app; that is an upstream +request, not a reason to grow your own. + Write a new generator when the *shape* needs to change. When a reference's shape is right and only its *target* is wrong — a different framework than the reference emits for — eject that generator and retarget it instead; see "Your framework isn't the default" below. @@ -125,10 +143,10 @@ convention, not the method name. a normal source file. Copy it and customize the copy (three-way merge preserves your edits on regen), or customize the template a built-in renders from. Reach for a custom generator when you want the change applied **consistently across every -entity** (the scale win); a one-off edit when it's genuinely one file. +entity** and it passes the cost test above; a one-off edit when it's genuinely one file. **The decision ladder:** an output the model describes that no reference emits → write -a generator (the top of the skill) · a reference is close → eject it and customize the +a generator if it passes the cost test (the top of the skill) · a reference is close → eject it and customize the copy · a reference fits → use it · only the genuinely un-modelable (business algorithms, external calls) is hand-written outside codegen — and it still imports the generated types. diff --git a/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-audit/SKILL.md b/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-audit/SKILL.md index bbcde472f..d3fb6666a 100644 --- a/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-audit/SKILL.md +++ b/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-audit/SKILL.md @@ -8,7 +8,9 @@ description: Use when assessing how well a project has adopted MetaObjects — g **Thesis.** Typed metadata is the durable spine; generated code is the disposable artifact. Hand-writing a layer the metadata could own creates a second source of truth for one fact — it will drift. This audit hunts those second sources of truth -and proposes folding them into the spine. +and proposes folding them into the spine — where doing so costs fewer hand-written lines +than it saves, more of that shape are coming, or silent drift is the risk. A one-off layer +that fails that cost test is acceptable, not a defect. **Boundary — read-only.** Deliverables: `.metaobjects/adoption-audit.json` (machine-readable findings) + a rendered Markdown report. The audit **never edits code, never authors @@ -47,7 +49,10 @@ Actual cutovers run through the existing skills mapped per finding tier (§ Brid **Census:** generated output line/file counts + metadata/owned-generator lines. Compute **leverage ratio** = `generated_lines / (metadata_lines + generator_lines)`; -healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). +healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). **Never report a +generated-share or leverage figure alone, and never as a target:** pair it with the **net +hand-written line change** (app lines removed minus generator and metadata lines added; +generator code is hand-written cost). A share can rise while total hand-written code grows. **Coverage matrix:** per entity/projection/value — query helpers? view? routes? UI? The gap between "modeled + query helpers" and "has view + route + UI" is the headline @@ -314,6 +319,7 @@ with full force). Full guidance: `docs/features/downstream-metadata-decisions.md | **CODEGEN CANDIDATE (high)** | Standard CRUD/list/form over a modeled or modelable entity. | Author the view + generate; parity-gate. | | **CODEGEN CANDIDATE (partial)** | Generatable data layer, bespoke presentation. | Generate data layer; keep viz hand-written. | | **DYNAMIC-RUNTIME CANDIDATE** | Behavior that could be metadata-driven at runtime. | Assess runtime-metadata feasibility. | +| **ACCEPTABLE (one-off)** | A hand-written layer the metadata could own, but an owned generator for it would cost more lines than it replaces, no more instances are expected, and nothing drifts silently (it reads typed generated properties). | Report as acceptable. Not a defect, not a recommendation to generate. Re-class if it recurs or drift appears. | | **BESPOKE (keep)** | Genuine custom: irreducible SQL (recursive CTEs, window functions, set ops — NOT plain count/sum/avg rollups, which are `origin.aggregate` on a projection), graph, SSE, auth, search, viz. | Leave hand-written — still import generated types. | | **VOCAB CANDIDATE (advisory)** | A recurring, closed hand-coded pattern (parallel integration modules / an ad-hoc string discriminator) that project-registered vocabulary — a custom subtype/attr via a provider — plus a small owned generator would own. | Propose the provider + `metadata_sketch`; apply the ADR-0037 ordered test; advisory only. | @@ -385,7 +391,12 @@ Per finding: `file:line` → what → generated-equivalent exists? → recommend · new shape → **author a declarative template-spec / custom generator from the metadata** · genuinely un-modelable → hand-write (still import the generated types). - **Generator-gap check:** missing generators that block the biggest wins? Recommend per gap: - own + customize / author a template-spec / fix upstream / stopgap. + own + customize / author a template-spec / fix upstream / stopgap. **Cost-test every + recommendation to generate** (`metaobjects-codegen` → `references/owning-generators.md`): an + owned generator is worth it only if generator + output is smaller than the code it replaces, + more instances are expected, or drift protection is the point. A layer that fails is + ACCEPTABLE, and an owned generator larger than the code it replaced is itself a finding. + A general-shape generator belongs in the library, not in each app. - **Verify the DB artifact, not just the types** — computed view columns may appear in the contract but be dropped from the view DDL; the contract may lie. - **An *undeclared* hand-authored DB view is invisible to `meta verify --db`.** A view that is diff --git a/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-codegen/SKILL.md b/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-codegen/SKILL.md index 80ecfc8b3..6efd42f91 100644 --- a/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-codegen/SKILL.md +++ b/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-codegen/SKILL.md @@ -46,7 +46,9 @@ exact config, generator names and commands for this project; read it. The other or none emits it the way this application needs it. OpenAPI, JSON Schema, Zod schemas, DTOs or a client for a framework nothing here ships, a service layer in the house style, a data dictionary, a search-index mapping, test fixtures. That is a generator, and you -write it now, in this change. Do not hand-write the output (it drifts the day the model +write it now, in this change — once it passes the cost test in `references/owning-generators.md` +(smaller than the code it replaces, generator counted; more instances expected; or drift +protection is the point). Do not hand-write the output (it drifts the day the model changes and nothing notices), do not wait for MetaObjects to ship it, and do not ask permission: a generator in this repo is ordinary source you own. diff --git a/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md b/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md index e847508df..314536f57 100644 --- a/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md +++ b/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md @@ -52,6 +52,24 @@ same target rules, and is drift-gated the same way. The runner adds no header to on TypeScript, C# and Python none is needed, because the hash manifest records what was written. +**Is it worth a generator? Run the cost test first.** Write an adopter-owned generator only +when at least one holds: + +1. **It is smaller.** The generator plus its emitted output, plus the metadata it adds, is + fewer lines than the hand-written code it replaces. Count generator code as hand-written + cost: it is code the team reads, tests and maintains. A generated-line total does not + count; the emitted files cost nothing to write but the generator that makes them does. +2. **More instances are coming.** The app will keep adding entities, routes or events of this + shape, so the generator's cost is paid back as the model grows. +3. **Drift protection is the point.** A change to the model could pass the compiler and go + wrong silently (a runtime lookup by name, a string a consumer compares), and regeneration + plus `verify` is what catches it. + +Otherwise hand-write it: a one-off layer that reads typed generated properties cannot drift +and is a fine place to stop. A generator with a general shape (usable by any app on this +model) belongs in the MetaObjects library, not copied into each app; that is an upstream +request, not a reason to grow your own. + Write a new generator when the *shape* needs to change. When a reference's shape is right and only its *target* is wrong — a different framework than the reference emits for — eject that generator and retarget it instead; see "Your framework isn't the default" below. @@ -125,10 +143,10 @@ convention, not the method name. a normal source file. Copy it and customize the copy (three-way merge preserves your edits on regen), or customize the template a built-in renders from. Reach for a custom generator when you want the change applied **consistently across every -entity** (the scale win); a one-off edit when it's genuinely one file. +entity** and it passes the cost test above; a one-off edit when it's genuinely one file. **The decision ladder:** an output the model describes that no reference emits → write -a generator (the top of the skill) · a reference is close → eject it and customize the +a generator if it passes the cost test (the top of the skill) · a reference is close → eject it and customize the copy · a reference fits → use it · only the genuinely un-modelable (business algorithms, external calls) is hand-written outside codegen — and it still imports the generated types. diff --git a/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md b/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md index bbcde472f..d3fb6666a 100644 --- a/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md +++ b/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md @@ -8,7 +8,9 @@ description: Use when assessing how well a project has adopted MetaObjects — g **Thesis.** Typed metadata is the durable spine; generated code is the disposable artifact. Hand-writing a layer the metadata could own creates a second source of truth for one fact — it will drift. This audit hunts those second sources of truth -and proposes folding them into the spine. +and proposes folding them into the spine — where doing so costs fewer hand-written lines +than it saves, more of that shape are coming, or silent drift is the risk. A one-off layer +that fails that cost test is acceptable, not a defect. **Boundary — read-only.** Deliverables: `.metaobjects/adoption-audit.json` (machine-readable findings) + a rendered Markdown report. The audit **never edits code, never authors @@ -47,7 +49,10 @@ Actual cutovers run through the existing skills mapped per finding tier (§ Brid **Census:** generated output line/file counts + metadata/owned-generator lines. Compute **leverage ratio** = `generated_lines / (metadata_lines + generator_lines)`; -healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). +healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). **Never report a +generated-share or leverage figure alone, and never as a target:** pair it with the **net +hand-written line change** (app lines removed minus generator and metadata lines added; +generator code is hand-written cost). A share can rise while total hand-written code grows. **Coverage matrix:** per entity/projection/value — query helpers? view? routes? UI? The gap between "modeled + query helpers" and "has view + route + UI" is the headline @@ -314,6 +319,7 @@ with full force). Full guidance: `docs/features/downstream-metadata-decisions.md | **CODEGEN CANDIDATE (high)** | Standard CRUD/list/form over a modeled or modelable entity. | Author the view + generate; parity-gate. | | **CODEGEN CANDIDATE (partial)** | Generatable data layer, bespoke presentation. | Generate data layer; keep viz hand-written. | | **DYNAMIC-RUNTIME CANDIDATE** | Behavior that could be metadata-driven at runtime. | Assess runtime-metadata feasibility. | +| **ACCEPTABLE (one-off)** | A hand-written layer the metadata could own, but an owned generator for it would cost more lines than it replaces, no more instances are expected, and nothing drifts silently (it reads typed generated properties). | Report as acceptable. Not a defect, not a recommendation to generate. Re-class if it recurs or drift appears. | | **BESPOKE (keep)** | Genuine custom: irreducible SQL (recursive CTEs, window functions, set ops — NOT plain count/sum/avg rollups, which are `origin.aggregate` on a projection), graph, SSE, auth, search, viz. | Leave hand-written — still import generated types. | | **VOCAB CANDIDATE (advisory)** | A recurring, closed hand-coded pattern (parallel integration modules / an ad-hoc string discriminator) that project-registered vocabulary — a custom subtype/attr via a provider — plus a small owned generator would own. | Propose the provider + `metadata_sketch`; apply the ADR-0037 ordered test; advisory only. | @@ -385,7 +391,12 @@ Per finding: `file:line` → what → generated-equivalent exists? → recommend · new shape → **author a declarative template-spec / custom generator from the metadata** · genuinely un-modelable → hand-write (still import the generated types). - **Generator-gap check:** missing generators that block the biggest wins? Recommend per gap: - own + customize / author a template-spec / fix upstream / stopgap. + own + customize / author a template-spec / fix upstream / stopgap. **Cost-test every + recommendation to generate** (`metaobjects-codegen` → `references/owning-generators.md`): an + owned generator is worth it only if generator + output is smaller than the code it replaces, + more instances are expected, or drift protection is the point. A layer that fails is + ACCEPTABLE, and an owned generator larger than the code it replaced is itself a finding. + A general-shape generator belongs in the library, not in each app. - **Verify the DB artifact, not just the types** — computed view columns may appear in the contract but be dropped from the view DDL; the contract may lie. - **An *undeclared* hand-authored DB view is invisible to `meta verify --db`.** A view that is diff --git a/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-codegen/SKILL.md b/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-codegen/SKILL.md index 80ecfc8b3..6efd42f91 100644 --- a/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-codegen/SKILL.md +++ b/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-codegen/SKILL.md @@ -46,7 +46,9 @@ exact config, generator names and commands for this project; read it. The other or none emits it the way this application needs it. OpenAPI, JSON Schema, Zod schemas, DTOs or a client for a framework nothing here ships, a service layer in the house style, a data dictionary, a search-index mapping, test fixtures. That is a generator, and you -write it now, in this change. Do not hand-write the output (it drifts the day the model +write it now, in this change — once it passes the cost test in `references/owning-generators.md` +(smaller than the code it replaces, generator counted; more instances expected; or drift +protection is the point). Do not hand-write the output (it drifts the day the model changes and nothing notices), do not wait for MetaObjects to ship it, and do not ask permission: a generator in this repo is ordinary source you own. diff --git a/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md b/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md index e847508df..314536f57 100644 --- a/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md +++ b/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md @@ -52,6 +52,24 @@ same target rules, and is drift-gated the same way. The runner adds no header to on TypeScript, C# and Python none is needed, because the hash manifest records what was written. +**Is it worth a generator? Run the cost test first.** Write an adopter-owned generator only +when at least one holds: + +1. **It is smaller.** The generator plus its emitted output, plus the metadata it adds, is + fewer lines than the hand-written code it replaces. Count generator code as hand-written + cost: it is code the team reads, tests and maintains. A generated-line total does not + count; the emitted files cost nothing to write but the generator that makes them does. +2. **More instances are coming.** The app will keep adding entities, routes or events of this + shape, so the generator's cost is paid back as the model grows. +3. **Drift protection is the point.** A change to the model could pass the compiler and go + wrong silently (a runtime lookup by name, a string a consumer compares), and regeneration + plus `verify` is what catches it. + +Otherwise hand-write it: a one-off layer that reads typed generated properties cannot drift +and is a fine place to stop. A generator with a general shape (usable by any app on this +model) belongs in the MetaObjects library, not copied into each app; that is an upstream +request, not a reason to grow your own. + Write a new generator when the *shape* needs to change. When a reference's shape is right and only its *target* is wrong — a different framework than the reference emits for — eject that generator and retarget it instead; see "Your framework isn't the default" below. @@ -125,10 +143,10 @@ convention, not the method name. a normal source file. Copy it and customize the copy (three-way merge preserves your edits on regen), or customize the template a built-in renders from. Reach for a custom generator when you want the change applied **consistently across every -entity** (the scale win); a one-off edit when it's genuinely one file. +entity** and it passes the cost test above; a one-off edit when it's genuinely one file. **The decision ladder:** an output the model describes that no reference emits → write -a generator (the top of the skill) · a reference is close → eject it and customize the +a generator if it passes the cost test (the top of the skill) · a reference is close → eject it and customize the copy · a reference fits → use it · only the genuinely un-modelable (business algorithms, external calls) is hand-written outside codegen — and it still imports the generated types. diff --git a/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-audit/SKILL.md b/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-audit/SKILL.md index bbcde472f..d3fb6666a 100644 --- a/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-audit/SKILL.md +++ b/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-audit/SKILL.md @@ -8,7 +8,9 @@ description: Use when assessing how well a project has adopted MetaObjects — g **Thesis.** Typed metadata is the durable spine; generated code is the disposable artifact. Hand-writing a layer the metadata could own creates a second source of truth for one fact — it will drift. This audit hunts those second sources of truth -and proposes folding them into the spine. +and proposes folding them into the spine — where doing so costs fewer hand-written lines +than it saves, more of that shape are coming, or silent drift is the risk. A one-off layer +that fails that cost test is acceptable, not a defect. **Boundary — read-only.** Deliverables: `.metaobjects/adoption-audit.json` (machine-readable findings) + a rendered Markdown report. The audit **never edits code, never authors @@ -47,7 +49,10 @@ Actual cutovers run through the existing skills mapped per finding tier (§ Brid **Census:** generated output line/file counts + metadata/owned-generator lines. Compute **leverage ratio** = `generated_lines / (metadata_lines + generator_lines)`; -healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). +healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). **Never report a +generated-share or leverage figure alone, and never as a target:** pair it with the **net +hand-written line change** (app lines removed minus generator and metadata lines added; +generator code is hand-written cost). A share can rise while total hand-written code grows. **Coverage matrix:** per entity/projection/value — query helpers? view? routes? UI? The gap between "modeled + query helpers" and "has view + route + UI" is the headline @@ -314,6 +319,7 @@ with full force). Full guidance: `docs/features/downstream-metadata-decisions.md | **CODEGEN CANDIDATE (high)** | Standard CRUD/list/form over a modeled or modelable entity. | Author the view + generate; parity-gate. | | **CODEGEN CANDIDATE (partial)** | Generatable data layer, bespoke presentation. | Generate data layer; keep viz hand-written. | | **DYNAMIC-RUNTIME CANDIDATE** | Behavior that could be metadata-driven at runtime. | Assess runtime-metadata feasibility. | +| **ACCEPTABLE (one-off)** | A hand-written layer the metadata could own, but an owned generator for it would cost more lines than it replaces, no more instances are expected, and nothing drifts silently (it reads typed generated properties). | Report as acceptable. Not a defect, not a recommendation to generate. Re-class if it recurs or drift appears. | | **BESPOKE (keep)** | Genuine custom: irreducible SQL (recursive CTEs, window functions, set ops — NOT plain count/sum/avg rollups, which are `origin.aggregate` on a projection), graph, SSE, auth, search, viz. | Leave hand-written — still import generated types. | | **VOCAB CANDIDATE (advisory)** | A recurring, closed hand-coded pattern (parallel integration modules / an ad-hoc string discriminator) that project-registered vocabulary — a custom subtype/attr via a provider — plus a small owned generator would own. | Propose the provider + `metadata_sketch`; apply the ADR-0037 ordered test; advisory only. | @@ -385,7 +391,12 @@ Per finding: `file:line` → what → generated-equivalent exists? → recommend · new shape → **author a declarative template-spec / custom generator from the metadata** · genuinely un-modelable → hand-write (still import the generated types). - **Generator-gap check:** missing generators that block the biggest wins? Recommend per gap: - own + customize / author a template-spec / fix upstream / stopgap. + own + customize / author a template-spec / fix upstream / stopgap. **Cost-test every + recommendation to generate** (`metaobjects-codegen` → `references/owning-generators.md`): an + owned generator is worth it only if generator + output is smaller than the code it replaces, + more instances are expected, or drift protection is the point. A layer that fails is + ACCEPTABLE, and an owned generator larger than the code it replaced is itself a finding. + A general-shape generator belongs in the library, not in each app. - **Verify the DB artifact, not just the types** — computed view columns may appear in the contract but be dropped from the view DDL; the contract may lie. - **An *undeclared* hand-authored DB view is invisible to `meta verify --db`.** A view that is diff --git a/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-codegen/SKILL.md b/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-codegen/SKILL.md index 80ecfc8b3..6efd42f91 100644 --- a/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-codegen/SKILL.md +++ b/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-codegen/SKILL.md @@ -46,7 +46,9 @@ exact config, generator names and commands for this project; read it. The other or none emits it the way this application needs it. OpenAPI, JSON Schema, Zod schemas, DTOs or a client for a framework nothing here ships, a service layer in the house style, a data dictionary, a search-index mapping, test fixtures. That is a generator, and you -write it now, in this change. Do not hand-write the output (it drifts the day the model +write it now, in this change — once it passes the cost test in `references/owning-generators.md` +(smaller than the code it replaces, generator counted; more instances expected; or drift +protection is the point). Do not hand-write the output (it drifts the day the model changes and nothing notices), do not wait for MetaObjects to ship it, and do not ask permission: a generator in this repo is ordinary source you own. diff --git a/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md b/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md index e847508df..314536f57 100644 --- a/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md +++ b/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md @@ -52,6 +52,24 @@ same target rules, and is drift-gated the same way. The runner adds no header to on TypeScript, C# and Python none is needed, because the hash manifest records what was written. +**Is it worth a generator? Run the cost test first.** Write an adopter-owned generator only +when at least one holds: + +1. **It is smaller.** The generator plus its emitted output, plus the metadata it adds, is + fewer lines than the hand-written code it replaces. Count generator code as hand-written + cost: it is code the team reads, tests and maintains. A generated-line total does not + count; the emitted files cost nothing to write but the generator that makes them does. +2. **More instances are coming.** The app will keep adding entities, routes or events of this + shape, so the generator's cost is paid back as the model grows. +3. **Drift protection is the point.** A change to the model could pass the compiler and go + wrong silently (a runtime lookup by name, a string a consumer compares), and regeneration + plus `verify` is what catches it. + +Otherwise hand-write it: a one-off layer that reads typed generated properties cannot drift +and is a fine place to stop. A generator with a general shape (usable by any app on this +model) belongs in the MetaObjects library, not copied into each app; that is an upstream +request, not a reason to grow your own. + Write a new generator when the *shape* needs to change. When a reference's shape is right and only its *target* is wrong — a different framework than the reference emits for — eject that generator and retarget it instead; see "Your framework isn't the default" below. @@ -125,10 +143,10 @@ convention, not the method name. a normal source file. Copy it and customize the copy (three-way merge preserves your edits on regen), or customize the template a built-in renders from. Reach for a custom generator when you want the change applied **consistently across every -entity** (the scale win); a one-off edit when it's genuinely one file. +entity** and it passes the cost test above; a one-off edit when it's genuinely one file. **The decision ladder:** an output the model describes that no reference emits → write -a generator (the top of the skill) · a reference is close → eject it and customize the +a generator if it passes the cost test (the top of the skill) · a reference is close → eject it and customize the copy · a reference fits → use it · only the genuinely un-modelable (business algorithms, external calls) is hand-written outside codegen — and it still imports the generated types. From 492f4a33ad847d8e81545837648d1ed3557a0d31 Mon Sep 17 00:00:00 2001 From: Doug Mealing Date: Sat, 10 Oct 2026 23:26:46 -0400 Subject: [PATCH 2/3] docs(changelog): move skill-revision entry under Added --- CHANGELOG.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc8a1066a..bc12bf120 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,13 @@ generator catalog and the Atlas evaluation are 1.2 work. ### Added +- **Agent skills: a cost test for owned generators.** The `metaobjects-codegen` skill now says when not to write a generator: only when generator plus + output is smaller than the hand-written code it replaces (generator code counted as + hand-written cost), more instances of the shape are expected, or drift protection is the + point; a general-shape generator belongs in the library. The `metaobjects-audit` skill applies + the same test: a one-off hand-written layer that fails it is reported as acceptable, and any + generated-share figure must carry the net hand-written line change and is never a target. + Documentation only; no code or generator changed. - **TypeScript: the `cube-model` reference generator (FR-044 Plan 4).** `cubeModel()` writes the reporting vocabulary as Cube data-model files, `model/cubes/.yml`: a cube for each table-backed entity that declares dimensions, measures or segments, a join for each to-one @@ -356,14 +363,6 @@ Each one changes generated code on the next `gen`, or generated docs on the next or Python api-docs build, as its entry says. A drift gate that covers that output reports it until you regenerate. -- **Agent skills: a cost test for owned generators.** The - `metaobjects-codegen` skill now says when not to write a generator: only when generator plus - output is smaller than the hand-written code it replaces (generator code counted as - hand-written cost), more instances of the shape are expected, or drift protection is the - point; a general-shape generator belongs in the library. The `metaobjects-audit` skill applies - the same test: a one-off hand-written layer that fails it is reported as acceptable, and any - generated-share figure must carry the net hand-written line change and is never a target. - Documentation only; no code or generator changed. - **TypeScript and Python: a read-only projection with no declared identity loses its item surface, even when it has a field named `id`.** It no longer gets `GET /{id}` or the three item-verb refusals, its by-id query (`findById` in TypeScript, `find_by_id` on the Python From 5aa3718289d47bf2285442a1feea1513d9f64bb1 Mon Sep 17 00:00:00 2001 From: Doug Mealing Date: Sat, 10 Oct 2026 23:28:17 -0400 Subject: [PATCH 3/3] no-mistakes(review): Unify generator cost test wording across skills Assisted-by: no-mistakes:claude:claude-sonnet-5-5 --- CHANGELOG.md | 9 +++++---- agent-context/skills/metaobjects-audit/SKILL.md | 10 ++++++---- .../references/owning-generators.md | 6 ++---- .../expected/.claude/skills/metaobjects-audit/SKILL.md | 10 ++++++---- .../references/owning-generators.md | 6 ++---- .../expected/.claude/skills/metaobjects-audit/SKILL.md | 10 ++++++---- .../references/owning-generators.md | 6 ++---- .../expected/.claude/skills/metaobjects-audit/SKILL.md | 10 ++++++---- .../references/owning-generators.md | 6 ++---- .../expected/.claude/skills/metaobjects-audit/SKILL.md | 10 ++++++---- .../references/owning-generators.md | 6 ++---- .../expected/.claude/skills/metaobjects-audit/SKILL.md | 10 ++++++---- .../references/owning-generators.md | 6 ++---- 13 files changed, 53 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc12bf120..514b4743a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,12 +60,13 @@ generator catalog and the Atlas evaluation are 1.2 work. ### Added -- **Agent skills: a cost test for owned generators.** The `metaobjects-codegen` skill now says when not to write a generator: only when generator plus - output is smaller than the hand-written code it replaces (generator code counted as - hand-written cost), more instances of the shape are expected, or drift protection is the +- **Agent skills: a cost test for owned generators.** The `metaobjects-codegen` skill now says when not to write a generator: only when generator code plus + its generated output and added metadata is fewer lines than the hand-written code it + replaces, more instances of the shape are expected, or drift protection is the point; a general-shape generator belongs in the library. The `metaobjects-audit` skill applies the same test: a one-off hand-written layer that fails it is reported as acceptable, and any - generated-share figure must carry the net hand-written line change and is never a target. + generated-share figure must carry the net hand-written line change (a figure to report, not + the test) and is never a target. Documentation only; no code or generator changed. - **TypeScript: the `cube-model` reference generator (FR-044 Plan 4).** `cubeModel()` writes the reporting vocabulary as Cube data-model files, `model/cubes/.yml`: a cube for each diff --git a/agent-context/skills/metaobjects-audit/SKILL.md b/agent-context/skills/metaobjects-audit/SKILL.md index d3fb6666a..ecf5cc3ac 100644 --- a/agent-context/skills/metaobjects-audit/SKILL.md +++ b/agent-context/skills/metaobjects-audit/SKILL.md @@ -8,8 +8,9 @@ description: Use when assessing how well a project has adopted MetaObjects — g **Thesis.** Typed metadata is the durable spine; generated code is the disposable artifact. Hand-writing a layer the metadata could own creates a second source of truth for one fact — it will drift. This audit hunts those second sources of truth -and proposes folding them into the spine — where doing so costs fewer hand-written lines -than it saves, more of that shape are coming, or silent drift is the risk. A one-off layer +and proposes folding them into the spine — where the generator code plus its generated output +and added metadata is fewer lines than the hand-written code it replaces, more of that shape +are coming, or silent drift is the risk. A one-off layer that fails that cost test is acceptable, not a defect. **Boundary — read-only.** Deliverables: `.metaobjects/adoption-audit.json` (machine-readable @@ -51,8 +52,9 @@ Actual cutovers run through the existing skills mapped per finding tier (§ Brid Compute **leverage ratio** = `generated_lines / (metadata_lines + generator_lines)`; healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). **Never report a generated-share or leverage figure alone, and never as a target:** pair it with the **net -hand-written line change** (app lines removed minus generator and metadata lines added; -generator code is hand-written cost). A share can rise while total hand-written code grows. +hand-written line change**, a figure to report and not the cost test (hand-written lines +removed minus generator and metadata lines added). A share can rise while total hand-written +code grows. **Coverage matrix:** per entity/projection/value — query helpers? view? routes? UI? The gap between "modeled + query helpers" and "has view + route + UI" is the headline diff --git a/agent-context/skills/metaobjects-codegen/references/owning-generators.md b/agent-context/skills/metaobjects-codegen/references/owning-generators.md index 314536f57..8af3875e2 100644 --- a/agent-context/skills/metaobjects-codegen/references/owning-generators.md +++ b/agent-context/skills/metaobjects-codegen/references/owning-generators.md @@ -55,10 +55,8 @@ written. **Is it worth a generator? Run the cost test first.** Write an adopter-owned generator only when at least one holds: -1. **It is smaller.** The generator plus its emitted output, plus the metadata it adds, is - fewer lines than the hand-written code it replaces. Count generator code as hand-written - cost: it is code the team reads, tests and maintains. A generated-line total does not - count; the emitted files cost nothing to write but the generator that makes them does. +1. **It is smaller.** The generator code plus its generated output, plus the metadata it + adds, is fewer lines than the hand-written code it replaces. 2. **More instances are coming.** The app will keep adding entities, routes or events of this shape, so the generator's cost is paid back as the model grows. 3. **Drift protection is the point.** A change to the model could pass the compiler and go diff --git a/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md b/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md index d3fb6666a..ecf5cc3ac 100644 --- a/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md +++ b/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md @@ -8,8 +8,9 @@ description: Use when assessing how well a project has adopted MetaObjects — g **Thesis.** Typed metadata is the durable spine; generated code is the disposable artifact. Hand-writing a layer the metadata could own creates a second source of truth for one fact — it will drift. This audit hunts those second sources of truth -and proposes folding them into the spine — where doing so costs fewer hand-written lines -than it saves, more of that shape are coming, or silent drift is the risk. A one-off layer +and proposes folding them into the spine — where the generator code plus its generated output +and added metadata is fewer lines than the hand-written code it replaces, more of that shape +are coming, or silent drift is the risk. A one-off layer that fails that cost test is acceptable, not a defect. **Boundary — read-only.** Deliverables: `.metaobjects/adoption-audit.json` (machine-readable @@ -51,8 +52,9 @@ Actual cutovers run through the existing skills mapped per finding tier (§ Brid Compute **leverage ratio** = `generated_lines / (metadata_lines + generator_lines)`; healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). **Never report a generated-share or leverage figure alone, and never as a target:** pair it with the **net -hand-written line change** (app lines removed minus generator and metadata lines added; -generator code is hand-written cost). A share can rise while total hand-written code grows. +hand-written line change**, a figure to report and not the cost test (hand-written lines +removed minus generator and metadata lines added). A share can rise while total hand-written +code grows. **Coverage matrix:** per entity/projection/value — query helpers? view? routes? UI? The gap between "modeled + query helpers" and "has view + route + UI" is the headline diff --git a/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md b/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md index 314536f57..8af3875e2 100644 --- a/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md +++ b/fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md @@ -55,10 +55,8 @@ written. **Is it worth a generator? Run the cost test first.** Write an adopter-owned generator only when at least one holds: -1. **It is smaller.** The generator plus its emitted output, plus the metadata it adds, is - fewer lines than the hand-written code it replaces. Count generator code as hand-written - cost: it is code the team reads, tests and maintains. A generated-line total does not - count; the emitted files cost nothing to write but the generator that makes them does. +1. **It is smaller.** The generator code plus its generated output, plus the metadata it + adds, is fewer lines than the hand-written code it replaces. 2. **More instances are coming.** The app will keep adding entities, routes or events of this shape, so the generator's cost is paid back as the model grows. 3. **Drift protection is the point.** A change to the model could pass the compiler and go diff --git a/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-audit/SKILL.md b/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-audit/SKILL.md index d3fb6666a..ecf5cc3ac 100644 --- a/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-audit/SKILL.md +++ b/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-audit/SKILL.md @@ -8,8 +8,9 @@ description: Use when assessing how well a project has adopted MetaObjects — g **Thesis.** Typed metadata is the durable spine; generated code is the disposable artifact. Hand-writing a layer the metadata could own creates a second source of truth for one fact — it will drift. This audit hunts those second sources of truth -and proposes folding them into the spine — where doing so costs fewer hand-written lines -than it saves, more of that shape are coming, or silent drift is the risk. A one-off layer +and proposes folding them into the spine — where the generator code plus its generated output +and added metadata is fewer lines than the hand-written code it replaces, more of that shape +are coming, or silent drift is the risk. A one-off layer that fails that cost test is acceptable, not a defect. **Boundary — read-only.** Deliverables: `.metaobjects/adoption-audit.json` (machine-readable @@ -51,8 +52,9 @@ Actual cutovers run through the existing skills mapped per finding tier (§ Brid Compute **leverage ratio** = `generated_lines / (metadata_lines + generator_lines)`; healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). **Never report a generated-share or leverage figure alone, and never as a target:** pair it with the **net -hand-written line change** (app lines removed minus generator and metadata lines added; -generator code is hand-written cost). A share can rise while total hand-written code grows. +hand-written line change**, a figure to report and not the cost test (hand-written lines +removed minus generator and metadata lines added). A share can rise while total hand-written +code grows. **Coverage matrix:** per entity/projection/value — query helpers? view? routes? UI? The gap between "modeled + query helpers" and "has view + route + UI" is the headline diff --git a/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md b/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md index 314536f57..8af3875e2 100644 --- a/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md +++ b/fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md @@ -55,10 +55,8 @@ written. **Is it worth a generator? Run the cost test first.** Write an adopter-owned generator only when at least one holds: -1. **It is smaller.** The generator plus its emitted output, plus the metadata it adds, is - fewer lines than the hand-written code it replaces. Count generator code as hand-written - cost: it is code the team reads, tests and maintains. A generated-line total does not - count; the emitted files cost nothing to write but the generator that makes them does. +1. **It is smaller.** The generator code plus its generated output, plus the metadata it + adds, is fewer lines than the hand-written code it replaces. 2. **More instances are coming.** The app will keep adding entities, routes or events of this shape, so the generator's cost is paid back as the model grows. 3. **Drift protection is the point.** A change to the model could pass the compiler and go diff --git a/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-audit/SKILL.md b/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-audit/SKILL.md index d3fb6666a..ecf5cc3ac 100644 --- a/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-audit/SKILL.md +++ b/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-audit/SKILL.md @@ -8,8 +8,9 @@ description: Use when assessing how well a project has adopted MetaObjects — g **Thesis.** Typed metadata is the durable spine; generated code is the disposable artifact. Hand-writing a layer the metadata could own creates a second source of truth for one fact — it will drift. This audit hunts those second sources of truth -and proposes folding them into the spine — where doing so costs fewer hand-written lines -than it saves, more of that shape are coming, or silent drift is the risk. A one-off layer +and proposes folding them into the spine — where the generator code plus its generated output +and added metadata is fewer lines than the hand-written code it replaces, more of that shape +are coming, or silent drift is the risk. A one-off layer that fails that cost test is acceptable, not a defect. **Boundary — read-only.** Deliverables: `.metaobjects/adoption-audit.json` (machine-readable @@ -51,8 +52,9 @@ Actual cutovers run through the existing skills mapped per finding tier (§ Brid Compute **leverage ratio** = `generated_lines / (metadata_lines + generator_lines)`; healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). **Never report a generated-share or leverage figure alone, and never as a target:** pair it with the **net -hand-written line change** (app lines removed minus generator and metadata lines added; -generator code is hand-written cost). A share can rise while total hand-written code grows. +hand-written line change**, a figure to report and not the cost test (hand-written lines +removed minus generator and metadata lines added). A share can rise while total hand-written +code grows. **Coverage matrix:** per entity/projection/value — query helpers? view? routes? UI? The gap between "modeled + query helpers" and "has view + route + UI" is the headline diff --git a/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md b/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md index 314536f57..8af3875e2 100644 --- a/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md +++ b/fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md @@ -55,10 +55,8 @@ written. **Is it worth a generator? Run the cost test first.** Write an adopter-owned generator only when at least one holds: -1. **It is smaller.** The generator plus its emitted output, plus the metadata it adds, is - fewer lines than the hand-written code it replaces. Count generator code as hand-written - cost: it is code the team reads, tests and maintains. A generated-line total does not - count; the emitted files cost nothing to write but the generator that makes them does. +1. **It is smaller.** The generator code plus its generated output, plus the metadata it + adds, is fewer lines than the hand-written code it replaces. 2. **More instances are coming.** The app will keep adding entities, routes or events of this shape, so the generator's cost is paid back as the model grows. 3. **Drift protection is the point.** A change to the model could pass the compiler and go diff --git a/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md b/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md index d3fb6666a..ecf5cc3ac 100644 --- a/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md +++ b/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-audit/SKILL.md @@ -8,8 +8,9 @@ description: Use when assessing how well a project has adopted MetaObjects — g **Thesis.** Typed metadata is the durable spine; generated code is the disposable artifact. Hand-writing a layer the metadata could own creates a second source of truth for one fact — it will drift. This audit hunts those second sources of truth -and proposes folding them into the spine — where doing so costs fewer hand-written lines -than it saves, more of that shape are coming, or silent drift is the risk. A one-off layer +and proposes folding them into the spine — where the generator code plus its generated output +and added metadata is fewer lines than the hand-written code it replaces, more of that shape +are coming, or silent drift is the risk. A one-off layer that fails that cost test is acceptable, not a defect. **Boundary — read-only.** Deliverables: `.metaobjects/adoption-audit.json` (machine-readable @@ -51,8 +52,9 @@ Actual cutovers run through the existing skills mapped per finding tier (§ Brid Compute **leverage ratio** = `generated_lines / (metadata_lines + generator_lines)`; healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). **Never report a generated-share or leverage figure alone, and never as a target:** pair it with the **net -hand-written line change** (app lines removed minus generator and metadata lines added; -generator code is hand-written cost). A share can rise while total hand-written code grows. +hand-written line change**, a figure to report and not the cost test (hand-written lines +removed minus generator and metadata lines added). A share can rise while total hand-written +code grows. **Coverage matrix:** per entity/projection/value — query helpers? view? routes? UI? The gap between "modeled + query helpers" and "has view + route + UI" is the headline diff --git a/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md b/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md index 314536f57..8af3875e2 100644 --- a/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md +++ b/fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md @@ -55,10 +55,8 @@ written. **Is it worth a generator? Run the cost test first.** Write an adopter-owned generator only when at least one holds: -1. **It is smaller.** The generator plus its emitted output, plus the metadata it adds, is - fewer lines than the hand-written code it replaces. Count generator code as hand-written - cost: it is code the team reads, tests and maintains. A generated-line total does not - count; the emitted files cost nothing to write but the generator that makes them does. +1. **It is smaller.** The generator code plus its generated output, plus the metadata it + adds, is fewer lines than the hand-written code it replaces. 2. **More instances are coming.** The app will keep adding entities, routes or events of this shape, so the generator's cost is paid back as the model grows. 3. **Drift protection is the point.** A change to the model could pass the compiler and go diff --git a/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-audit/SKILL.md b/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-audit/SKILL.md index d3fb6666a..ecf5cc3ac 100644 --- a/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-audit/SKILL.md +++ b/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-audit/SKILL.md @@ -8,8 +8,9 @@ description: Use when assessing how well a project has adopted MetaObjects — g **Thesis.** Typed metadata is the durable spine; generated code is the disposable artifact. Hand-writing a layer the metadata could own creates a second source of truth for one fact — it will drift. This audit hunts those second sources of truth -and proposes folding them into the spine — where doing so costs fewer hand-written lines -than it saves, more of that shape are coming, or silent drift is the risk. A one-off layer +and proposes folding them into the spine — where the generator code plus its generated output +and added metadata is fewer lines than the hand-written code it replaces, more of that shape +are coming, or silent drift is the risk. A one-off layer that fails that cost test is acceptable, not a defect. **Boundary — read-only.** Deliverables: `.metaobjects/adoption-audit.json` (machine-readable @@ -51,8 +52,9 @@ Actual cutovers run through the existing skills mapped per finding tier (§ Brid Compute **leverage ratio** = `generated_lines / (metadata_lines + generator_lines)`; healthy = multi-× (example: ~4.7k spine → ~15.7k generated ≈ 3.3×). **Never report a generated-share or leverage figure alone, and never as a target:** pair it with the **net -hand-written line change** (app lines removed minus generator and metadata lines added; -generator code is hand-written cost). A share can rise while total hand-written code grows. +hand-written line change**, a figure to report and not the cost test (hand-written lines +removed minus generator and metadata lines added). A share can rise while total hand-written +code grows. **Coverage matrix:** per entity/projection/value — query helpers? view? routes? UI? The gap between "modeled + query helpers" and "has view + route + UI" is the headline diff --git a/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md b/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md index 314536f57..8af3875e2 100644 --- a/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md +++ b/fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-codegen/references/owning-generators.md @@ -55,10 +55,8 @@ written. **Is it worth a generator? Run the cost test first.** Write an adopter-owned generator only when at least one holds: -1. **It is smaller.** The generator plus its emitted output, plus the metadata it adds, is - fewer lines than the hand-written code it replaces. Count generator code as hand-written - cost: it is code the team reads, tests and maintains. A generated-line total does not - count; the emitted files cost nothing to write but the generator that makes them does. +1. **It is smaller.** The generator code plus its generated output, plus the metadata it + adds, is fewer lines than the hand-written code it replaces. 2. **More instances are coming.** The app will keep adding entities, routes or events of this shape, so the generator's cost is paid back as the model grows. 3. **Drift protection is the point.** A change to the model could pass the compiler and go