You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A full accuracy + gap-fill + quality pass on the seven agent-context skills that ship
to adopters, driven by four Fable reviews (one caught a class of defect two mechanical
audits missed). Highlights:
Actively-harmful fixes:
- Fabricated error code `ERR_UNKNOWN_EXTENDS` → the real `ERR_UNRESOLVED_SUPER` (4 files:
2 skills + entities.md + abstracts-and-inheritance.md; agents are told to parse the code).
- Invalid-YAML flagship `@sql` example in metaobjects-verify/migration.md (a block scalar
inside a flow mapping — an agent copying it shipped an unparseable file) → valid block style.
- Audit filter-op calibration bug repeated across 6 files that told auditors to WAIVE
hand-rolled filter code on every non-TS port → corrected (the 9-op grammar ships all 5 ports;
only ?search/or-and-combinators/leading-wildcard are TS-only). Same for the output-parser
calibration (ships all 5 ports; Kotlin uses a generated kotlinx call, not hand-written).
Consistency lag closed (the 0.17.0 features had landed in authoring+docs but not the other
skill bodies): metaobjects-codegen no longer prescribes the hand-edited-migration anti-pattern
#208 retired; #195 origins (any/all/collect/computed/first + @distinct/@orderby), #207
projection @filter, #208 @sql/@Unmanaged, and #214 entity read-view are now consistent across
codegen/verify/audit/fit-assessment.
New coverage:
- 2 new runtime-ui language refs (python.md ObjectManager + csharp.md EF Core) — closes the
5-language gap; source-grounded, Fable-verified.
- authoring: full relationship vocabulary (all 3 subtypes + subtype-derived @onDelete defaults
— fixes an unintended-CASCADE footgun — + FR-018 M:N), field.float/time/map, @autoset,
origin.first's required @orderby, structured @expr/@filter object shapes.
- prompts: the FR-010 output-format prompt fragment (all 5 langs), template.output @kind
document|email, trace-recorder shipped-vs-TS-only split, template.toolcall.
- verify: @sql/@Unmanaged in the SKILL body, per-port verify command matrix, removed stale
ADR-0015 JVM-auto-create claims.
All front-matter valid; agent-context conformance goldens regenerated; sdk agent-context
suite 42/42 (grounding, size-gate, drift, conformance); CLI scaffold tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGQ7oSuNcjhsMHWwZzhBwr
Copy file name to clipboardExpand all lines: agent-context/skills/metaobjects-audit/SKILL.md
+25-12Lines changed: 25 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -291,9 +291,10 @@ Per finding: `file:line` → what → generated-equivalent exists? → recommend
291
291
5.**Runtime schema patching** (`ALTER TABLE … ADD COLUMN IF NOT EXISTS`, `_ensure_schema()`) — N schema owners.
292
292
6.**N declarations of one shape** — same entity as Drizzle table + Zod schema + Pydantic model + hand dataclass; target is 1 + N generated.
293
293
7.**`own*()` accessor read of an effective property** (ADR-0039) — `ownAttr` / `ownFields` / `own_children` / bare Python `attr(` / `getMetaAttr(name, false)` / native `IsArray` used to read a value or iterate members outside the sanctioned subclass-emit / own-serializer / `@dbColumnType` cases → silently drops `extends`-inherited values. A **correctness defect** (axis G2), not advisory.
294
-
8.**Hand-written `CREATE VIEW` / read-only SQL standing in for a projection (view-necessity test).** Grep migrations, checked-in `.sql`, and repository/query code for `CREATE [OR REPLACE] [MATERIALIZED] VIEW`, and for hand-rolled read-only queries that mirror a read model — a pure-`SELECT` repository/service method with joins or `GROUP BY` feeding a DTO, or a raw-SQL escape (`db.execute(sql…)`, `FromSqlRaw`, a JPA @Query with hand-written SQL). For each, run the **necessity test** — can `object.projection` + origins express this shape? It can when every output column is (a) a base-entity or relationship-joined column → `origin.passthrough` (`@from` / `@via`), (b) a count/sum/avg/min/max over related rows → `origin.aggregate` (`@agg` / `@of` / `@via`, optionally row-scoped with `@filter`), (c) a child collection → `origin.collection` (`@via`), or (d) a column borrowed via `extends` — and the joins follow declared relationships / `identity.reference` FKs.
295
-
-**Expressible → CODEGEN CANDIDATE (high):** convert to an `object.projection` with a read-only `source.rdb``@kind: view` child, let `meta migrate` emit the `CREATE VIEW`, and consume the generated read-only query — the hand-written view is a second source of truth for a derivable shape. Flag in the finding that **`meta verify --db` cannot catch this**: an unmodeled DB view is *unmanaged*, so this audit is the only gate that sees it. Parity-gate: the generated view returns row-identical results before the hand-written SQL is deleted.
296
-
-**Not expressible → BESPOKE (keep), with a NAMED justification:** record the construct that makes it irreducible (recursive CTE, window function / `OVER`, `UNION` / `INTERSECT` / `EXCEPT`, `DISTINCT ON`, lateral join, non-aggregate expression column). "It's an aggregation" is NOT a justification — plain count/sum/avg/min/max rollups are `origin.aggregate`.
294
+
8. **Hand-written `CREATE VIEW` / read-only SQL standing in for a projection or entity read-view (view-necessity test).** Grep migrations, checked-in `.sql`, and repository/query code for `CREATE [OR REPLACE] [MATERIALIZED] VIEW`, and for hand-rolled read-only queries that mirror a read model — a pure-`SELECT` repository/service method with joins or `GROUP BY` feeding a DTO, or a raw-SQL escape (`db.execute(sql…)`, `FromSqlRaw`, a JPA @Query with hand-written SQL). For each, run the **necessity test** — can origins express this shape? A column is derivable when it is (a) a base-entity or relationship-joined column → `origin.passthrough` (`@from` / `@via`), (b) a count/sum/avg/min/max over related rows → `origin.aggregate` (`@agg` / `@of` / `@via`), or an `EXISTS` / `array_agg` → `origin.aggregate` `any` / `all` / `collect` — any of them optionally row-scoped with `@filter`, (c) a child collection → `origin.collection` (`@via`), (d) a computed scalar / **non-aggregate expression column** → `origin.computed` (`@expr`, #195), (e) one related row's column picked by an ordering — an **argmax / `DISTINCT ON … ORDER BY` / correlated `ORDER BY … LIMIT 1`** → `origin.first` (`@of` / `@via` / `@orderBy`, #195), or (f) a column borrowed via `extends` — and the joins follow declared relationships / `identity.reference` FKs.
295
+
-**Entity-shaped (`SELECT own.* + derived`) → an entity read-view, NOT a projection (#214).** The single most common legacy view is an entity's OWN columns plus a joined/derived extra (`SELECT o.*, c.name AS customer_name`). This is the `Order`**entity with a read route**, not an exposure contract — route it to an **entity read-view**: keep the writable `@role: primary``@kind: table` source and add a **non-primary `@role: replica``@kind: view`** source, declaring only the *extra* as a derived `origin.*` field on the entity (the own field set already covers `o.*`). Reads route to the replica view, writes to the table. Reach for a **projection** instead only when the view renames base columns or **row-filters** (`WHERE status='active'`, soft-delete) — the latter is a projection with an object-level `@filter` (#207) that lowers to the outer `WHERE`.
296
+
-**Exposure contract, expressible → CODEGEN CANDIDATE (high):** a subset / renamed / versioned / multi-base read model → convert to an `object.projection` with a read-only `source.rdb``@kind: view` child, let `meta migrate` emit the `CREATE VIEW`, and consume the generated read-only query — the hand-written view is a second source of truth for a derivable shape. Parity-gate: the generated view returns row-identical results before the hand-written SQL is deleted.
297
+
- **Not expressible → carry it in `@sql` or `@unmanaged`, never a hand-edited migration (#208, ADR-0043).** When a NAMED irreducible construct blocks origin authoring — recursive CTE, window function / `OVER`, `UNION` / `INTERSECT` / `EXCEPT`, lateral join — the body still belongs in the metadata: carry the hand-written SQL in the `source.rdb` **`@sql`** escape — a read-only-`@kind` body the tool REGISTERS, fingerprints, and drift-checks (adopt a pre-existing view with `meta migrate --allow adopt-view`); `@sql` forbids `origin.*` children (two sources of truth). A DB object whose DDL is owned **entirely elsewhere** (Flyway / a hand-migration) → mark its source **`@unmanaged: true`** (legal on any `@kind` incl. `table`); `meta migrate` never creates/drops/drift-checks it and `verify --db` reports it as external. `@sql` and `@unmanaged` are mutually exclusive. **Only a view left *undeclared*** — neither modeled, nor `@sql`, nor `@unmanaged` — is truly *unmanaged*, invisible to `meta verify --db`, so this audit is the only gate that sees it. "It's an aggregation" is NOT an irreducibility justification (plain count/sum/avg/min/max rollups are `origin.aggregate`); nor is a `DISTINCT ON` pick-one-row (`origin.first`) or a non-aggregate expression column (`origin.computed`).
297
298
9.**A closed variant-set hand-modeled per instance** — N sibling modules / classes / config blocks, one per channel / provider / target, sharing a payload + config shape and diverging only by transport. Grep for sibling-file families and switch-on-a-string dispatch; verify the set is closed and recurring (never a one-off). → axis I "New-vocabulary OPPORTUNITY" (VOCAB CANDIDATE, advisory).
298
299
299
300
---
@@ -313,10 +314,12 @@ Per finding: `file:line` → what → generated-equivalent exists? → recommend
313
314
own + customize / author a template-spec / fix upstream / stopgap.
314
315
-**Verify the DB artifact, not just the types** — computed view columns may appear in the
315
316
contract but be dropped from the view DDL; the contract may lie.
316
-
-**A hand-authored DB view is invisible to `meta verify --db`.** An unmodeled view is
317
-
*unmanaged* (informational only — never actionable drift, never auto-dropped), so a
318
-
hand-written view standing in for an expressible `object.projection` can never be
319
-
outsourced to the drift gate; hunt it here (drift signature 8, below).
317
+
-**An *undeclared* hand-authored DB view is invisible to `meta verify --db`.** A view that is
318
+
neither modeled nor carried in the `source.rdb``@sql` / `@unmanaged` escapes is *unmanaged*
319
+
(informational only — never actionable drift, never auto-dropped), so a hand-written view
320
+
standing in for an expressible `object.projection` / entity read-view can never be outsourced
321
+
to the drift gate; hunt it here (drift signature 8, below). Once carried in `@sql` (#208) it IS
322
+
registered, fingerprinted, and drift-checked — no longer audit-only.
320
323
-**Version skew:** check *actually-resolved* package versions, not declared; consuming a fix
321
324
requires a coordinated lockstep bump, not a source-file copy.
322
325
@@ -329,8 +332,9 @@ Per finding: `file:line` → what → generated-equivalent exists? → recommend
329
332
`object.value` with `origin.*` (`passthrough` / `aggregate` / `collection`) fields.
330
333
- Silent-degradation hack (`try/except KeyError` or `?? ''` around formatting) — flag every instance.
331
334
- Hand-rolled output parsing (regex / XML / ad-hoc JSON) vs declared `template.output` +
332
-
generated `parse*` / `safeParse*` / `extract*` parser. **Java hand-writes the Jackson
333
-
one-liner — do NOT flag it** (§ Calibration).
335
+
generated `parse*` / `safeParse*` / `extract*` parser — **generated in all five ports**
336
+
(Java's generated `<Name>Parser` owns the Jackson `readValue`); flag a hand-rolled parser
337
+
in a **non-generated** file where a `template.output` node exists.
0 commit comments