@metaobjectsdev/sdk 0.24.1 → 0.24.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-context/skills/metaobjects-audit/SKILL.md +23 -0
- package/agent-context/skills/metaobjects-audit/references/capability-checklist.md +10 -2
- package/agent-context/skills/metaobjects-audit/references/requirements.md +40 -2
- package/agent-context/skills/metaobjects-authoring/SKILL.md +5 -0
- package/agent-context/skills/metaobjects-authoring/references/requirements.md +49 -11
- package/agent-context/skills/metaobjects-codegen/references/typescript.md +11 -3
- package/agent-context/skills/metaobjects-fit-assessment/SKILL.md +47 -20
- package/agent-context/skills/metaobjects-verify/SKILL.md +22 -0
- package/agent-context/skills/metaobjects-verify/references/requirements.md +67 -0
- package/package.json +2 -2
|
@@ -71,10 +71,33 @@ code behind a grep hit; a "duplicate" validator's *divergence* is the finding.
|
|
|
71
71
|
design, and a real estate reports most of itself unclaimed on day one). Score the prose
|
|
72
72
|
it would replace, and prefer ONE concrete claim an author can check over a
|
|
73
73
|
whole-estate migration.
|
|
74
|
+
**First establish the estate LOADS.** An older ledger fails the load outright, so
|
|
75
|
+
`meta verify` never runs and everything in `references/requirements.md` is unavailable:
|
|
76
|
+
`@violation` and `@verifiedBy` were retired in **0.24.0** (no deprecation shim — ADR-0023
|
|
77
|
+
seals the registry); an index key declaring both `@fields` and `@expr` is refused since
|
|
78
|
+
**0.24.1**; and **0.24.2** turned `@status: abandoned | superseded` into **`retired`**. Run
|
|
79
|
+
**`meta upgrade`** (previews by default; `--apply` writes) before auditing, and audit the
|
|
80
|
+
upgraded tree. It rewrites only what has one correct answer and **refuses the rest, exiting
|
|
81
|
+
non-zero** — a refusal is itself a finding, naming the entries whose disposition nobody
|
|
82
|
+
recorded.
|
|
83
|
+
**Then audit the `retired` entries specifically.** They are where the ledger earns its
|
|
84
|
+
keep — agents proposing a retired capability's rebuild is the one failure this mechanism
|
|
85
|
+
has controlled evidence against — and they are also where it decays quietly. Check that
|
|
86
|
+
each `@statement` reads as a PROHIBITION rather than a diary entry ("X is never done",
|
|
87
|
+
not "we used to do X"), and that `@counterexample` describes the REVIVAL. A retired entry
|
|
88
|
+
whose statement narrates history is invisible to the reader it exists to stop.
|
|
74
89
|
- [ ] **F. Drift-gate adoption.** Is `meta verify` wired into CI / pre-commit? Which
|
|
75
90
|
subverbs (`--codegen` / `--templates` / `--db`)? Committed-codegen freshness gate?
|
|
76
91
|
Advisories heeded? Routine `--no-verify` bypass? Loader `ERR_*` / warnings addressed?
|
|
77
92
|
Parse the stable `code` field, not message text (ADR-0009).
|
|
93
|
+
**Is any advisory half switched OFF?** A gate can be wired, green, and muted. The
|
|
94
|
+
requirements authoring lint is advisory and mutable — `--no-requirement-lint` or
|
|
95
|
+
`META_NO_REQUIREMENT_LINT=1` silences it while the gate above it still runs and can still
|
|
96
|
+
exit 1, so a green pipeline says nothing about the seven `WARN_REQUIREMENT_*` authoring
|
|
97
|
+
checks. Grep CI config and scripts for both spellings. Muted is not automatically a defect —
|
|
98
|
+
a project that made a deliberate call is fine — but it moves those checks back onto the
|
|
99
|
+
auditor (see `references/requirements.md`), so **say which it is** rather than reporting the
|
|
100
|
+
ledger as lint-clean.
|
|
78
101
|
**Coverage completeness — "wired" is not "covers it all."** A gate can be present
|
|
79
102
|
yet blind to a whole artifact class. Confirm each subverb actually covers what the
|
|
80
103
|
project HAS: (a) **templates** — `verify --templates` on a CLI before the #193 fix
|
|
@@ -114,8 +114,10 @@ classify it (using the classification scheme in `SKILL.md`) and route the cutove
|
|
|
114
114
|
|
|
115
115
|
- **`identity.primary`** (`@generation`) — hunt hand-assigned primary keys / ID generation
|
|
116
116
|
the primary identity's `@generation` strategy models.
|
|
117
|
-
- **`identity.secondary`** (`@fields
|
|
118
|
-
UNIQUE alternate key (uniqueness is the type — the
|
|
117
|
+
- **`identity.secondary`** (keys off `@fields` XOR `@expr` — exactly one, never both; physical
|
|
118
|
+
escapes `@using`/`@where`/`@orders`) — a UNIQUE alternate key (uniqueness is the type — the
|
|
119
|
+
legacy `@unique` attr was removed from it, and the same XOR rule as `index.lookup` applies here
|
|
120
|
+
because ADR-0040 puts uniqueness in the TYPE: a secondary identity IS a unique index);
|
|
119
121
|
hunt hand-rolled unique constraints or raw-SQL partial/functional unique indexes it models.
|
|
120
122
|
- **`identity.reference`** (`@references`, `@enforce`) — hunt hand-written FK constraints /
|
|
121
123
|
reference enforcement the reference identity already declares.
|
|
@@ -238,6 +240,12 @@ subtypes with opposite polarity: `requirement.functional` fails when NOTHING imp
|
|
|
238
240
|
recorded as a TODO, or a ticket number in a comment. **ABSENT disposition means UNDECIDED**,
|
|
239
241
|
which is the point: a gap nobody has ruled on reads differently from one deliberately
|
|
240
242
|
accepted.
|
|
243
|
+
- **The doc slots behave differently on this node type** — do not audit them by the generic
|
|
244
|
+
rule below. `title` is CHARTERED here (a requirement's `name` is an identifier, so the label
|
|
245
|
+
lives in `title`) and is **rendered** by `meta docs`, heading each entry after its dotted
|
|
246
|
+
path; `summary` is INERT (nothing reads it, and `@statement` is already the required
|
|
247
|
+
one-liner) and `meta verify` warns on one. So an absent `summary` is correct, a populated
|
|
248
|
+
one is the finding, and a populated `title` is correct.
|
|
241
249
|
- **CALIBRATION — entirely opt-in and warning-only where it counts.** A project declaring no
|
|
242
250
|
`requirement.*` nodes sees no diagnostics at all, and object coverage ("entities claimed
|
|
243
251
|
by nothing") ships as a WARNING deliberately — a real estate carrying one requirement will
|
|
@@ -9,8 +9,38 @@ effort re-deriving what a green run already proves.
|
|
|
9
9
|
|
|
10
10
|
## What verify has already proven (do not re-check by hand)
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
A run answers two different questions in two separate sections. Both are already-proven
|
|
13
|
+
ground; neither is worth an auditor's time.
|
|
14
|
+
|
|
15
|
+
**The gate** settles referential integrity: links sit at or below the L4 floor, nesting
|
|
16
|
+
agrees with levels, `@status` values are legal, and references resolve (with dangling allowed
|
|
17
|
+
on `planned`, whose nodes do not exist yet).
|
|
18
|
+
|
|
19
|
+
**The authoring lint** — printed under its own heading, advisory, and unable to change the
|
|
20
|
+
exit code — settles the naming and prose defects you would otherwise find by reading every
|
|
21
|
+
entry:
|
|
22
|
+
|
|
23
|
+
| code | what a clean run has already proven |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `WARN_REQUIREMENT_NAME_NOT_ADDRESSABLE` | no `name` is blank, space-padded, or carries a `.` / `/` / `:` that breaks the dotted path or the generated stub's filename |
|
|
26
|
+
| `WARN_REQUIREMENT_NAME_READS_AS_PROSE` | no `name` is a sentence sitting in the address slot |
|
|
27
|
+
| `WARN_REQUIREMENT_NAME_RESTATES_STATEMENT` | no `name` and `@statement` are the same sentence written twice |
|
|
28
|
+
| `WARN_REQUIREMENT_PROSE_EMPTY` | no `@statement` / `@counterexample` is declared-but-blank (the loader enforces presence, never content) |
|
|
29
|
+
| `WARN_REQUIREMENT_PROSE_DUPLICATED` | no `description` repeats `@statement` whole or as its opening sentence, and no `@counterexample` repeats it |
|
|
30
|
+
| `WARN_REQUIREMENT_INERT_DOC_SLOT` | no `summary` is set — nothing reads it, and `@statement` is already the required one-liner |
|
|
31
|
+
| `WARN_REQUIREMENT_TITLE_IS_AN_ID` | no `title` leads with a catalogue or ticket id |
|
|
32
|
+
|
|
33
|
+
Two limits, and each puts something back on your list:
|
|
34
|
+
|
|
35
|
+
- **The lint is mutable.** `--no-requirement-lint` / `META_NO_REQUIREMENT_LINT=1` silences the
|
|
36
|
+
advisory half while the gate still runs. **Establish whether the project mutes it** (§F of
|
|
37
|
+
the checklist asks this) — against a muted lint the whole table above proves nothing.
|
|
38
|
+
- **It reports only EXACT repeats.** A paraphrase — `description` restating `@statement` in
|
|
39
|
+
different words, a `@counterexample` that merely negates it — is invisible to the tool and
|
|
40
|
+
is a legitimate hand finding.
|
|
41
|
+
|
|
42
|
+
`title` is deliberately NOT flagged as an inert slot; it is chartered as the entry's label and
|
|
43
|
+
is rendered (see item 6). Do not report a populated `title` as a defect.
|
|
14
44
|
|
|
15
45
|
## What only a human or an agent reading the code can catch
|
|
16
46
|
|
|
@@ -40,6 +70,14 @@ functional one.
|
|
|
40
70
|
model. If the tree is flat, or every entry is L3, the levels are carrying no information and
|
|
41
71
|
should be simplified rather than defended.
|
|
42
72
|
|
|
73
|
+
**6. Titles that are not labels.** `title` is now **rendered** — `meta docs` heads each entry
|
|
74
|
+
`## checkout.payment — Payment capture`, the path first and the label after it — so a slot
|
|
75
|
+
that used to be inert is read by every reader of the generated page. The lint catches only
|
|
76
|
+
the id-shaped case (`FR-448 …`); it cannot tell a *useless* label from a good one. Read the
|
|
77
|
+
titles: one that restates `@statement`, or repeats the path in prose, is now visible noise in
|
|
78
|
+
a heading rather than a private authoring habit. An absent `title` is NOT a finding — the
|
|
79
|
+
entry heads by its path alone, which is what every sibling surface addresses it by.
|
|
80
|
+
|
|
43
81
|
## Scope — do NOT flag these as defects
|
|
44
82
|
|
|
45
83
|
- **Unclaimed `object.value` / `object.projection`.** Exempt by design: a value is a shape, a
|
|
@@ -596,6 +596,11 @@ Declaring **neither** — or **both** — is `ERR_INVALID_INDEX`. This applies t
|
|
|
596
596
|
`identity.secondary` as well as `index.lookup`: uniqueness lives in the type, so a unique
|
|
597
597
|
index keys itself the same way.
|
|
598
598
|
|
|
599
|
+
Legacy metadata declaring both used to load, with `@fields` **silently discarded**. If you
|
|
600
|
+
meet one, `meta upgrade --apply` drops `@fields` — do not hand-pick the survivor: the index
|
|
601
|
+
in the database is the expression one, so keeping `@expr` reproduces it and keeping
|
|
602
|
+
`@fields` would emit a migration against live data.
|
|
603
|
+
|
|
599
604
|
The db provider contributes physical-tuning attrs alongside either form: `@orders`
|
|
600
605
|
(per-column sort direction), `@using` (access method — `gin`/`gist`/`hash`; default
|
|
601
606
|
`btree`), and `@where` (partial-index predicate).
|
|
@@ -5,13 +5,31 @@ describe. Read the existing requirement nodes before designing anything. Two rul
|
|
|
5
5
|
more than the rest.
|
|
6
6
|
|
|
7
7
|
**1. A requirement is PRESCRIPTIVE — it states what should be true, never what happened.**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
`status` is `planned | live | partial | retired`, and every one of them states something
|
|
9
|
+
meant to be true. `retired` is not the exception: it means **"this was built, then
|
|
10
|
+
deliberately removed, and must not be rebuilt"** — a prohibition in force, falsifiable by
|
|
11
|
+
exactly one observable, the capability reappearing.
|
|
12
|
+
|
|
13
|
+
So write a retired entry as a rule, not a diary. `statement: "An unpaid order is never
|
|
14
|
+
expired by a wall-clock timer"` is a prohibition; `"We used to expire orders on a timer"` is
|
|
15
|
+
history wearing a requirement's shape, and no gate will catch it. On a retired entry
|
|
16
|
+
`counterexample` describes **the revival** — the thing a future reader is about to propose.
|
|
17
|
+
|
|
18
|
+
**Do not delete a retired capability's entry.** Keeping it is the single most load-bearing
|
|
19
|
+
thing this vocabulary does: given a brief for a capability that had been retired, agents
|
|
20
|
+
working from the model alone proposed rebuilding it 24 times out of 24. The entry is what
|
|
21
|
+
stops that, so a retirement is a status change plus a rewritten statement, never a deletion.
|
|
22
|
+
|
|
23
|
+
Three rules the loader enforces on `retired`:
|
|
24
|
+
|
|
25
|
+
- **`implementedBy` is REFUSED** — a retired capability has no implementation by definition.
|
|
26
|
+
What used to implement it goes in `notes`. If deleting the references feels wrong, the
|
|
27
|
+
capability is probably not retired: an entry whose nodes are still there is `live` or
|
|
28
|
+
`partial`.
|
|
29
|
+
- **`supersededBy` names the requirement that replaced it**, legal here only, and RESOLVED —
|
|
30
|
+
so the chain still works when the replacement is itself retired later.
|
|
31
|
+
- It never counts toward object coverage, and is exempt from the architectural universality
|
|
32
|
+
check.
|
|
15
33
|
|
|
16
34
|
Leaving a dangling `implementedBy` is **correct only on `planned`**: the entry precedes the
|
|
17
35
|
nodes. On `live` or `partial` the same dangling reference is an error — the model moved and
|
|
@@ -27,10 +45,9 @@ rule kills *"the system is reliable"*. If you cannot say what breaking it looks
|
|
|
27
45
|
delete it.
|
|
28
46
|
|
|
29
47
|
**Four prose slots, and `statement` is the one that means "description".** A requirement can
|
|
30
|
-
also carry the common `
|
|
31
|
-
|
|
48
|
+
also carry the common `description` and `notes`, and they overlap badly unless you decide the
|
|
49
|
+
split before writing any of them:
|
|
32
50
|
|
|
33
|
-
- `title` — a short **noun-phrase** label (`name` is an identifier; this is what an index shows)
|
|
34
51
|
- `statement` — **the claim**. This IS the description of what the requirement is
|
|
35
52
|
- `counterexample` — **what would falsify the claim**, which is what makes it checkable
|
|
36
53
|
- `description` — **the scope**: what the claim covers, what it deliberately does not, and
|
|
@@ -38,12 +55,33 @@ decide the split before writing any of them:
|
|
|
38
55
|
- `notes` — **the evidence**: how you know the `status` is true — citations, vocabularies, the
|
|
39
56
|
control you ran to prove an absence was real
|
|
40
57
|
|
|
58
|
+
**`title` is a LABEL and is chartered here; `summary` is not.** A requirement's `name` is an
|
|
59
|
+
identifier and its address renders as a dotted camelCase path, so a short noun-phrase `title` is
|
|
60
|
+
what an index shows — the requirement attribute table in `spec/capability-ledger.md` says so by
|
|
61
|
+
name, and the generated requirements page renders it in the heading after the path
|
|
62
|
+
(`## checkout.payment — Payment capture`). `summary` is different: `statement` is already the
|
|
63
|
+
required one-line sentence, so a `summary` can only repeat it and nothing reads it (`verify` warns,
|
|
64
|
+
`WARN_REQUIREMENT_INERT_DOC_SLOT`). `notes` is unrendered on purpose — chartered internal-only.
|
|
65
|
+
|
|
66
|
+
**Never put a catalogue or ticket id in `title`.** A title is a noun phrase; an id is not a name.
|
|
67
|
+
`title: "FR-467 — Order recording"` is two things in one slot — put the id in `trackedBy` and
|
|
68
|
+
keep the phrase as the title. `verify` warns (`WARN_REQUIREMENT_TITLE_IS_AN_ID`).
|
|
69
|
+
|
|
70
|
+
**The `name` is an address, so write it as an identifier.** It is the segment of the dotted
|
|
71
|
+
path (`Ordering.Placement.Recorded`) and the filename of the generated test stub. A `.` in a
|
|
72
|
+
name is indistinguishable from nesting — `Orders.Recorded` and `Orders` containing `Recorded`
|
|
73
|
+
produce the same path — and a sentence for a name puts the claim somewhere nothing reads it.
|
|
74
|
+
Both load; `verify` warns (`WARN_REQUIREMENT_NAME_NOT_ADDRESSABLE`,
|
|
75
|
+
`WARN_REQUIREMENT_NAME_READS_AS_PROSE`, `WARN_REQUIREMENT_NAME_RESTATES_STATEMENT`).
|
|
76
|
+
|
|
41
77
|
Two failure modes, both of which look like diligence. A `description` that **paraphrases the
|
|
42
78
|
statement** is padding, and it makes every later reader trust the ledger less — leave it off
|
|
43
79
|
instead, it is optional. A `description` that **narrates the evidence** belongs in `notes`;
|
|
44
80
|
the tell is a fact you had to read the implementation to learn. Mechanical test for the last
|
|
45
81
|
line: *would this sentence have to change if the code changed but the model did not?* Then it
|
|
46
|
-
is `notes`.
|
|
82
|
+
is `notes`. `verify` warns on the exact repeats (`WARN_REQUIREMENT_PROSE_DUPLICATED`), and on
|
|
83
|
+
a `statement` or `counterexample` that is present but blank (`WARN_REQUIREMENT_PROSE_EMPTY`) —
|
|
84
|
+
the loader requires those attrs to exist, never to say anything.
|
|
47
85
|
|
|
48
86
|
**Hierarchy is nesting, and links live at the bottom.** L1 solution, L2 segment, L3
|
|
49
87
|
service — these never reference the model. **L4** binds a declared top-level node — an
|
|
@@ -133,10 +133,10 @@ cross-subtype 404. Conformance-gated by `fixtures/api-contract-conformance/tph`
|
|
|
133
133
|
(HTTP wire shape) and `fixtures/persistence-conformance/tph-*` (single-table
|
|
134
134
|
runtime semantics).
|
|
135
135
|
|
|
136
|
-
## Docs — `meta docs` (one door,
|
|
136
|
+
## Docs — `meta docs` (one door, three surfaces)
|
|
137
137
|
|
|
138
138
|
Documentation is NOT a `meta gen` generator. The single door is the `meta docs`
|
|
139
|
-
command, which emits
|
|
139
|
+
command, which emits three cross-linked **surfaces** under one output dir (default
|
|
140
140
|
`./docs`):
|
|
141
141
|
|
|
142
142
|
- **model surface** (`./docs/<Entity>.md`, `./docs/<Template>.md`) — the neutral
|
|
@@ -146,11 +146,19 @@ command, which emits two cross-linked **surfaces** under one output dir (default
|
|
|
146
146
|
`./docs/api/AGENT-API.md`) — the SDK/API reference: the concrete imports,
|
|
147
147
|
function signatures, payload field shapes, and runnable examples for *this*
|
|
148
148
|
project's generated code.
|
|
149
|
+
- **requirements surface** (`./docs/requirements.md`, `./docs/requirements.toon`) —
|
|
150
|
+
the declared `requirement.*` ledger as documentation, with each entry headed by its
|
|
151
|
+
dotted path and its `title` where it has one, and each entity page naming the
|
|
152
|
+
requirements that claim it. Metadata-alone like the model surface, so it needs no
|
|
153
|
+
gen config. **On by default since 0.24.0** — a project declaring no `requirement.*`
|
|
154
|
+
nodes writes no requirements file and the run says nothing about the surface at all,
|
|
155
|
+
deliberately: reporting "0 requirement pages" would advertise a surface that never ran.
|
|
149
156
|
|
|
150
157
|
```bash
|
|
151
|
-
npx meta docs #
|
|
158
|
+
npx meta docs # all three → ./docs (model) + ./docs/api + ./docs/requirements.*
|
|
152
159
|
npx meta docs --model # model surface only
|
|
153
160
|
npx meta docs --api # api surface only
|
|
161
|
+
npx meta docs --requirements # requirement ledger only
|
|
154
162
|
npx meta docs --out ./site-docs # write under a different root
|
|
155
163
|
```
|
|
156
164
|
|
|
@@ -18,13 +18,14 @@ DEFERRED, ON PURPOSE — capability requirements (`requirement.functional` /
|
|
|
18
18
|
`requirement.architectural`). This skill deliberately says NOTHING about them, and that is a
|
|
19
19
|
decision, not an omission.
|
|
20
20
|
|
|
21
|
-
Why not now: (1)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
Why not now: (1) SPENT -- this read "not in a release yet", and it is now. `requirement.*`
|
|
22
|
+
shipped in 0.22.0 and has evolved twice since (0.23.0 added `planned` / `@disposition` /
|
|
23
|
+
`@trackedBy`; 0.24.0 made the vocabulary prescriptive-only). Do not repeat this reason.
|
|
24
|
+
(2) The signal->feature mapping is unvalidated against this skill's own kill criterion: the
|
|
25
|
+
controlled evidence (0/24 model-only revivals) measures POST-adoption model-reading, and the
|
|
26
|
+
feature's premise -- that the disproof lives nowhere in the model -- cuts against pre-adoption
|
|
27
|
+
detectability. If the reasoning was never written down, there is nothing for an evidence-cited
|
|
28
|
+
assessment to cite. (3) Whether anyone fills the ledger in is itself untested; advertising it pre-adoption
|
|
28
29
|
is the brochure failure this skill exists to avoid.
|
|
29
30
|
|
|
30
31
|
TRIGGER to revisit — the release carrying `requirement.*` has shipped, AND either:
|
|
@@ -34,6 +35,23 @@ TRIGGER to revisit — the release carrying `requirement.*` has shipped, AND eit
|
|
|
34
35
|
Arm B a team that adopted via this assessment hits a resurrection the ledger would have
|
|
35
36
|
caught, or asks why the assessment never mentioned it.
|
|
36
37
|
|
|
38
|
+
ARM A HAS FIRED (dogfooded adopter estate, 2026-08-13). Three retired or reversed capability
|
|
39
|
+
decisions were each citable PRE-adoption at file:line -- a removal commit, two explicit
|
|
40
|
+
do-not-reintroduce comments on the very constant an agent would revive, an .env.example line
|
|
41
|
+
saying the knob does not exist. The ANTI-TRIGGER did NOT fire: every one traced to committed
|
|
42
|
+
prose. Bound it honestly -- n=1, and that estate is unusually disciplined about recording
|
|
43
|
+
reversals in co-located prose, which is precisely the manual work `requirement.*` systematizes,
|
|
44
|
+
so it is a confound rather than a clean sample. It also partly falsifies the feature's own
|
|
45
|
+
premise that the disproof lives nowhere in the model.
|
|
46
|
+
|
|
47
|
+
SO THE DEFERRAL NOW RESTS ON (3) ALONE, which makes it a judgement about VALUE rather than a
|
|
48
|
+
fact about the release. Unprompted uptake is still unmeasured, and a later cross-estate reading
|
|
49
|
+
sharpened the question: ledger value tracks whether the ledger is EXECUTABLE -- whether some
|
|
50
|
+
mechanism can falsify a claim -- not when it was written. The largest ledger measured carried
|
|
51
|
+
no harness and produced no defect found by any mechanism. Advertising that to a team that will
|
|
52
|
+
hand-maintain it is the brochure failure this skill exists to avoid. Revisiting means deciding
|
|
53
|
+
the SHAPE below is worth spending, not re-checking whether the capability exists.
|
|
54
|
+
|
|
37
55
|
ANTI-TRIGGER (defer -> never): if dogfooded entries trace only to tribal knowledge with no
|
|
38
56
|
repo artifact, this assessment structurally cannot speak to the feature as a finding.
|
|
39
57
|
|
|
@@ -46,8 +64,10 @@ R0 verdict line: no verdict may turn on it.
|
|
|
46
64
|
|
|
47
65
|
# MetaObjects Fit & Migration Assessment
|
|
48
66
|
|
|
49
|
-
_Assessment prompt v1 (post-Phase-0 refinement).
|
|
50
|
-
|
|
67
|
+
_Assessment prompt v1 (post-Phase-0 refinement). Body originally grounded against MetaObjects
|
|
68
|
+
npm `0.17.x` / Maven `7.9.x`; capability claims re-swept against npm `0.24.x` / Maven `7.24.x`
|
|
69
|
+
on 2026-08-25, but not every line has been re-derived since — verify every capability claim
|
|
70
|
+
against the current release before asserting it._
|
|
51
71
|
|
|
52
72
|
You are an AI assistant running a **pre-adoption fit assessment** for MetaObjects
|
|
53
73
|
(https://github.com/metaobjectsdev/metaobjects — the cross-language metadata standard:
|
|
@@ -265,11 +285,13 @@ signature class. Hunt all ten classes:
|
|
|
265
285
|
**necessity test**: expressible when every output column is a passthrough
|
|
266
286
|
(`origin.passthrough @from/@via`), a count/sum/avg/min/max (`origin.aggregate
|
|
267
287
|
@agg/@of/@via`, row-scoped with `@filter`), a predicate quantifier (`origin.aggregate
|
|
268
|
-
@agg: any|all`), an array rollup (`origin.aggregate @agg: collect`
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
a
|
|
272
|
-
|
|
288
|
+
@agg: any|all`), an array rollup (`origin.aggregate @agg: collect` — with `@of`
|
|
289
|
+
naming one column, or `@of` OMITTED since 0.24.1 to collect each related row as the
|
|
290
|
+
carrying field's declared value object, which is the `json_agg(row_to_json(...))` shape),
|
|
291
|
+
a non-aggregate derived scalar (`origin.computed @expr`), an argmax-style "one related
|
|
292
|
+
row's column" pick (`origin.first @via` — covers the common `DISTINCT ON` / lateral-join
|
|
293
|
+
shape), a soft-delete/status/type row-scope (an object-level `@filter` on
|
|
294
|
+
`object.projection`), or `extends`-borrowed — and joins
|
|
273
295
|
follow declared relationships/`identity.reference` FKs. Expressible → projection
|
|
274
296
|
candidate (note: an unmodeled hand view is *unmanaged* — invisible to `verify --db`;
|
|
275
297
|
modeling it is what makes it gateable). `DISTINCT ON` and lateral join are **not**
|
|
@@ -313,7 +335,7 @@ concrete — lead with it.
|
|
|
313
335
|
| schema vs model (5 + the DDL copy in 6) | spine owns DDL via `meta migrate`; **`meta verify --db`** (Node `meta` only; PG/SQLite/D1) |
|
|
314
336
|
| opaque JSON columns (6-implicit) | `object.value` + `field.object @storage: jsonb` — the shape gets ONE checkable declaration + generated codecs |
|
|
315
337
|
| read-model SQL (8) | `object.projection` + `origin.*` generate the view DDL |
|
|
316
|
-
| scattered prompts (10) | `template.prompt` + typed payload VO + external text; **`meta verify --templates`** / `Renderer.verify` fails when a `{{field}}` no longer matches the payload; `template.
|
|
338
|
+
| scattered prompts (10) | `template.prompt` + typed payload VO + external text; **`meta verify --templates`** / `Renderer.verify` fails when a `{{field}}` no longer matches the payload; a **responding** `template.prompt` — one carrying `@responseRef` — generates parser-on-receipt (FR-006) plus the output-format fragment and tolerant `extract` (FR-010), i.e. both sides of the tag contract. A `template.output` is **outbound only** and emits no parser (ADR-0052, 0.24.0) |
|
|
317
339
|
| the metadata itself | strict provenance (ADR-0023): unknown attrs fail load |
|
|
318
340
|
|
|
319
341
|
State the honest limits in the same section: `verify` cannot catch semantic mismodeling
|
|
@@ -428,8 +450,11 @@ promising it or counting it in benefits:
|
|
|
428
450
|
count them).
|
|
429
451
|
- email/URL/IP regexes → `@stringFormat: email` / `field.uri` / `field.inet`.
|
|
430
452
|
- opaque JSON columns → `object.value` + `field.object @storage: jsonb` (from P2-b).
|
|
431
|
-
- inline prompts / ad-hoc payload dicts
|
|
432
|
-
|
|
453
|
+
- inline prompts / ad-hoc payload dicts → `template.prompt` / `template.toolcall`; a
|
|
454
|
+
hand-rolled regex or JSON-scrape of an LLM **reply** → a *responding* `template.prompt`
|
|
455
|
+
(one carrying `@responseRef`), never `template.output` — the subtype's axis is DIRECTION
|
|
456
|
+
and an output is outbound only (ADR-0052). Rendered emails / documents / config exports
|
|
457
|
+
are what `template.output` is for.
|
|
433
458
|
- doc comments hand-written in migrations (`COMMENT ON`) → the common `description` attr.
|
|
434
459
|
- a recurring closed variant-set as N sibling modules → project-registered provider
|
|
435
460
|
subtype — VOCAB CANDIDATE, advisory only, never load-bearing for the verdict.
|
|
@@ -572,9 +597,11 @@ Every prose prediction gets a claim. Ceiling statements (P5-b) MUST carry
|
|
|
572
597
|
runtime; consumer hand-wires the FastAPI router + repository impl; `metaobjects`
|
|
573
598
|
console script `gen`/`verify` (no migrate).
|
|
574
599
|
- **Prompt pillar** (all five ports): render + payload-VO codegen + `verify` templates +
|
|
575
|
-
|
|
576
|
-
`extract` (FR-010).
|
|
577
|
-
|
|
600
|
+
parser-on-receipt for a **responding `template.prompt`** (FR-006) + output-format fragment
|
|
601
|
+
& tolerant `extract` (FR-010). Since 0.24.0 that whole inbound tier keys off `@responseRef`
|
|
602
|
+
on a `template.prompt`; a `template.output` is outbound-only and emits none of it
|
|
603
|
+
(ADR-0052) — do not promise a parser for one. MCP exposure of declared
|
|
604
|
+
prompts/tools: not shipped — never promise it.
|
|
578
605
|
- **Not shipped, never promise**: `api.*`/`operation.*`/`binding.*` declared-API surface;
|
|
579
606
|
the cut `byte`/`short`/`class` field stubs (non-functional, removed from the registry);
|
|
580
607
|
`index.fulltext`/`vector`/`spatial` (reserved, unregistered); native PG enums /
|
|
@@ -46,6 +46,28 @@ Two more are caught structurally rather than by a command: **generated-edited**
|
|
|
46
46
|
**migration-vs-metadata** (migrations are emitted *from* metadata diffs, so they
|
|
47
47
|
can't drift by construction).
|
|
48
48
|
|
|
49
|
+
## Before any of this: the metadata has to LOAD — `meta upgrade` for retired vocabulary
|
|
50
|
+
|
|
51
|
+
`verify` cannot report drift in a model it could not read. The registry is sealed
|
|
52
|
+
(ADR-0023), so a retired name has **no deprecation shim** — it is a load error, and
|
|
53
|
+
the run stops before the first drift check. Retirements that bite an existing estate:
|
|
54
|
+
`@readOnly` → `@mutability` and `origin.collection` → `origin.aggregate @agg: collect`
|
|
55
|
+
(0.24.0); `@violation` → `@counterexample`, plus `@verifiedBy`, `@supersededBy` and
|
|
56
|
+
`@status: abandoned | superseded` on `requirement.*` (0.24.0, FR-038); and an index key
|
|
57
|
+
declaring **both** `@fields` and `@expr`, refused since 0.24.1.
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
meta upgrade # previews every rewrite; writes nothing
|
|
61
|
+
meta upgrade --apply # makes them
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Node-only, because it edits the metadata documents every port shares — a Java, Python
|
|
65
|
+
or C# project runs `npx meta upgrade` against its own sources. Canonical JSON and YAML
|
|
66
|
+
alike. It rewrites from the same table the loader's errors are generated from, fixes
|
|
67
|
+
only what has one correct answer, and **refuses the rest with a non-zero exit** so a
|
|
68
|
+
pipeline cannot record a partial migration as done. See
|
|
69
|
+
`references/requirements.md` for what it refuses on a ledger and why.
|
|
70
|
+
|
|
49
71
|
## Run `meta verify` before you call a build done
|
|
50
72
|
|
|
51
73
|
Make a bare `meta verify` the last step before you consider any MetaObjects work
|
|
@@ -13,6 +13,37 @@ subverb**: requirements are metadata, so they are checked on *every* `meta verif
|
|
|
13
13
|
A typo'd `@status` fails the **load** ("failed to load metadata"), before verify runs. If you
|
|
14
14
|
see that, no other diagnostic in the run is trustworthy — fix it first and re-run.
|
|
15
15
|
|
|
16
|
+
## A load failure on RETIRED vocabulary — run `meta upgrade`, do not hand-sweep
|
|
17
|
+
|
|
18
|
+
`@status` is not the only way the load fails before any of this runs. **0.24.0** retired
|
|
19
|
+
`@violation` (→ `@counterexample`) and `@verifiedBy`; **0.24.1** made an index key `@fields`
|
|
20
|
+
**XOR** `@expr`, so a node declaring both is now `ERR_INVALID_INDEX`. **0.24.2 reversed half
|
|
21
|
+
of the 0.24.0 cut**: `abandoned` and `superseded` become **`@status: retired`**, and
|
|
22
|
+
`@supersededBy` is registered again — this time as a reference the loader resolves. There is no deprecation shim for any of them —
|
|
23
|
+
the registry is sealed (ADR-0023), so a legacy document does not load at all and every check on
|
|
24
|
+
this page is unreachable until it does.
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
meta upgrade # previews every rewrite; writes nothing
|
|
28
|
+
meta upgrade --apply # makes them
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
It rewrites from the same table the loader's error text is generated from, so the fix you are
|
|
32
|
+
told about and the edit the tool makes cannot drift apart. It fixes only what has one correct
|
|
33
|
+
answer and **refuses the rest, exiting non-zero** — a partial migration can never be recorded
|
|
34
|
+
as finished by CI. Two refusals are expected on a real ledger and both are yours to decide:
|
|
35
|
+
|
|
36
|
+
- **`@status: abandoned` / `superseded` are no longer refused** — they rewrite to `retired`,
|
|
37
|
+
and the same run drops the `@implementedBy` that a retired entry may not carry. This was the
|
|
38
|
+
canonical judgement case in 0.24.0; FR-039 made it determinate.
|
|
39
|
+
- **`origin.collection`.** Retired to `origin.aggregate @agg: collect`; the attribute sets
|
|
40
|
+
differ, so the tool will not guess.
|
|
41
|
+
|
|
42
|
+
`@fields` beside `@expr` **is** rewritten, and the survivor is not a coin toss: that pair loaded
|
|
43
|
+
before 0.24.1 with `@fields` silently discarded, so the index already in your database is the
|
|
44
|
+
expression one. `upgrade` drops `@fields` — which reproduces the object that exists and emits no
|
|
45
|
+
DDL change. Do not hand-pick the other survivor; that invents a new index and migrates live data.
|
|
46
|
+
|
|
16
47
|
## The status asymmetry — the one that surprises people
|
|
17
48
|
|
|
18
49
|
The **same** unresolved `@implementedBy` reference is:
|
|
@@ -49,6 +80,8 @@ name the remedy; this table exists so you can act on one without re-deriving the
|
|
|
49
80
|
| `ERR_REQUIREMENT_ARCH_NO_IMPLEMENTERS` | a `live`/`partial` `requirement.architectural` that nothing implements | a policy declared and applied to nothing. Claim the nodes it governs, or drop it to `planned` — which is exempt, because it is not applied yet by definition. |
|
|
50
81
|
| `ERR_REQUIREMENT_LEVEL_NESTING` | a node's `@level` disagrees with the level of the parent it nests under | nesting IS the hierarchy. Move the node to the right parent rather than editing the level to match where it happens to sit. |
|
|
51
82
|
| `ERR_REQUIREMENT_BAD_LEVEL` | `@level` is not an integer inside the allowed range | levels are L1–L5 and nothing else. |
|
|
83
|
+
| `ERR_REQUIREMENT_RETIRED_HAS_IMPLEMENTORS` | a `@status: retired` entry declares `@implementedBy` | a retired capability has no implementation by definition. Delete the attribute and put what used to implement it in `notes`; if the nodes are still there, the capability is `live` or `partial`, not retired. |
|
|
84
|
+
| `ERR_REQUIREMENT_SUPERSEDED_BY_NOT_RETIRED` | `@supersededBy` on a status other than `retired` | it names what REPLACED a withdrawn capability, so on a live one there is nothing for it to say. |
|
|
52
85
|
| `ERR_MISSING_REQUIRED_ATTR` | a required attr is absent | `@statement`, `@status` and `@counterexample` are required on both subtypes; `@level` is required on `functional` and optional on `architectural`. |
|
|
53
86
|
| `ERR_BAD_ATTR_VALUE` | a closed-enum attr has an unknown value | `@status` and `@disposition` are enforced by the LOADER, so a typo fails the load in every port rather than passing in some. |
|
|
54
87
|
|
|
@@ -56,6 +89,40 @@ name the remedy; this table exists so you can act on one without re-deriving the
|
|
|
56
89
|
reasons, so reading only the code you hit can send you the wrong way. The question is not
|
|
57
90
|
"is this ref valid?" — usually it is — but "does the LEVEL match the shape of the ref?"
|
|
58
91
|
|
|
92
|
+
## The authoring lint — a second section, never an error
|
|
93
|
+
|
|
94
|
+
`verify` also prints an **authoring lint** under its own heading, after the gate's own
|
|
95
|
+
warnings:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
meta verify — requirements: 6 authoring warning(s) (advisory — does not fail the build):
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Read it as a different claim from everything above. The gate says the ledger **disagrees with
|
|
102
|
+
the model**; the lint says it agrees but **records less than its author thinks**. Every finding
|
|
103
|
+
is a warning and none can change the exit code, so a lint-only run is a passing run.
|
|
104
|
+
|
|
105
|
+
| code | what it means | fix |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `WARN_REQUIREMENT_NAME_NOT_ADDRESSABLE` | the `name` holds a character that breaks the dotted path or the generated stub filename | rename it. A `.` is the common one and the worst: `Orders.Recorded` and `Orders` containing `Recorded` produce the **same** path, so the address stops identifying one node. |
|
|
108
|
+
| `WARN_REQUIREMENT_NAME_READS_AS_PROSE` | the `name` is a sentence | the name is an address — make it an identifier and put the prose in `@statement`. |
|
|
109
|
+
| `WARN_REQUIREMENT_NAME_RESTATES_STATEMENT` | `name` and `@statement` say the same thing | the claim is written twice. Keep `@statement` (every surface reads it) and shorten the name. |
|
|
110
|
+
| `WARN_REQUIREMENT_PROSE_EMPTY` | `@statement` or `@counterexample` is present but blank | the loader requires the attr to EXIST, never to say anything. Write the sentence, or delete the entry. |
|
|
111
|
+
| `WARN_REQUIREMENT_PROSE_DUPLICATED` | `description` repeats `@statement` (whole, or as its opening sentence), or `@counterexample` does | `@statement` is already the description. `description` holds the SCOPE; drop it entirely if the scope is obvious. |
|
|
112
|
+
| `WARN_REQUIREMENT_INERT_DOC_SLOT` | `summary` is set on a requirement | `@statement` is already the required one-line sentence, so a summary can only repeat it, and nothing reads it. Delete it. (`title` is NOT flagged — it is chartered as the entry's label.) |
|
|
113
|
+
| `WARN_REQUIREMENT_TITLE_IS_AN_ID` | `title` holds a catalogue or ticket id | a title is a noun phrase and an id is not a name. **Split** it — the id to `@trackedBy`, the phrase stays the title. Do not move the whole string; that throws the label away. |
|
|
114
|
+
|
|
115
|
+
Findings from both sections are addressed by the requirement's **dotted path**, never its
|
|
116
|
+
bare name — two branches of a ledger may reuse a name, so a bare one can be ambiguous.
|
|
117
|
+
|
|
118
|
+
Mute the lint with `--no-requirement-lint` or `META_NO_REQUIREMENT_LINT=1`. That silences the
|
|
119
|
+
advisory half **only** — the gate above still runs and can still exit 1.
|
|
120
|
+
|
|
121
|
+
Two deliberate silences. The lint reports only **exact** repeats, never a paraphrase — a
|
|
122
|
+
similarity threshold on prose produces findings you can argue with. And it never judges
|
|
123
|
+
whether a statement is true or a counterexample sufficient; those are the judgements the
|
|
124
|
+
ledger exists to record.
|
|
125
|
+
|
|
59
126
|
## `verify` does not look at your tests
|
|
60
127
|
|
|
61
128
|
It used to. `@verifiedBy` asked you to name a test, and `verify` checked that the **name**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metaobjectsdev/sdk",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.3",
|
|
4
4
|
"description": "Workspace helpers and agent-docs utilities for MetaObjects projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"access": "public"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@metaobjectsdev/metadata": "0.24.
|
|
59
|
+
"@metaobjectsdev/metadata": "0.24.3",
|
|
60
60
|
"zod": "^3.23.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|