@metaobjectsdev/sdk 0.22.1 → 0.23.1-rc.1

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.
@@ -976,7 +976,28 @@ mapping).
976
976
 
977
977
  ## Requirements — capability ledger (opt-in)
978
978
 
979
- If this project declares `requirement.functional` / `requirement.architectural` nodes, read `references/requirements.md` for the full authoring rules.
979
+ **This capability exists whether or not the project uses it yet.** `requirement.functional` and `requirement.architectural` are registered metadata types, declared in `metaobjects/` beside the entities they describe and loaded by the same loader — no side file, no bespoke parser. They record *why* each part of the model exists, so a field with no reason to exist becomes visible as one.
980
+
981
+ Reach for it when the project needs to answer any of:
982
+
983
+ - **"Why is this field here?"** — an L5 requirement binds a claim to a specific member. Authoring these exhaustively is what surfaces columns nothing reads and vocabularies nobody documented.
984
+ - **"What is broken but known?"** — `@status: partial` plus `@disposition: accepted | deferred`. Absent disposition means *undecided*, and `meta verify` counts those: the gaps nobody has ruled on.
985
+ - **"What did we build and then retire?"** — `@status: abandoned` is the one status where a dangling `@implementedBy` is *correct*, so the record survives the deletion.
986
+ - **"What have we committed to build?"** — `@status: planned`. Its references may dangle, and it never counts toward object coverage.
987
+ - **"Which ticket covers this?"** — `@trackedBy`.
988
+
989
+ Every requirement must be **violable**: if you cannot say what breaking it looks like, it is a description, not a requirement.
990
+
991
+ If the project declares any `requirement.*` node, `references/requirements.md` is installed with the full authoring rules. If it does not and you are adding the first one, the shape is:
992
+
993
+ ```yaml
994
+ - requirement.architectural:
995
+ name: everyStoredRowIsAddressable
996
+ status: live
997
+ statement: Every persisted row declares the identity by which it is addressed.
998
+ violation: A row that can be inserted but never pointed at.
999
+ implementedBy: [acme::shop::Order]
1000
+ ```
980
1001
 
981
1002
  ---
982
1003
 
@@ -27,19 +27,69 @@ key. *"Things are persisted"* is not, and is a description rather than a require
27
27
  rule kills *"the system is reliable"*. If you cannot say what breaking it looks like,
28
28
  delete it.
29
29
 
30
- **Hierarchy is nesting, and links live at the bottom.** L1 solution, L2 segment (an
31
- application or library), L3 service these never reference the model. **L4** binds an
32
- object, **L5** binds a field, view or identity. `implementedBy` above L4 is an error.
30
+ **Four prose slots, and `statement` is the one that means "description".** A requirement can
31
+ also carry the common `title`, `description` and `notes`, and they overlap badly unless you
32
+ decide the split before writing any of them:
33
+
34
+ - `title` — a short **noun-phrase** label (`name` is an identifier; this is what an index shows)
35
+ - `statement` — **the claim**. This IS the description of what the requirement is
36
+ - `violation` — **the counterexample** that makes the claim checkable
37
+ - `description` — **the scope**: what the claim covers, what it deliberately does not, and
38
+ which sibling entry owns the rest
39
+ - `notes` — **the evidence**: how you know the `status` is true — citations, vocabularies, the
40
+ control you ran to prove an absence was real
41
+
42
+ Two failure modes, both of which look like diligence. A `description` that **paraphrases the
43
+ statement** is padding, and it makes every later reader trust the ledger less — leave it off
44
+ instead, it is optional. A `description` that **narrates the evidence** belongs in `notes`;
45
+ the tell is a fact you had to read the implementation to learn. Mechanical test for the last
46
+ line: *would this sentence have to change if the code changed but the model did not?* Then it
47
+ is `notes`.
48
+
49
+ **Hierarchy is nesting, and links live at the bottom.** L1 solution, L2 segment, L3
50
+ service — these never reference the model. **L4** binds a declared top-level node — an
51
+ `object.*` **or a `template.*`** — and **L5** binds a member of one: a field, view,
52
+ validator, identity, or a template's child. `implementedBy` above L4 is an error.
33
53
  Regrouping *moves* a node; it does not edit a parent string.
34
54
 
35
- **Architectural requirements are the other kind.** `requirement.architectural` carries no
36
- level a uuid-PK rule, change attribution, tenant scoping. Its check is *universality*
55
+ Claim your prompts. A `template.prompt` is a model node realising a capability exactly as
56
+ an entity is, and it is the node whose retirement is hardest to see later — a removed
57
+ prompt leaves no table behind. A prompt estate with no requirement entries is the same
58
+ blind spot this whole mechanism exists to close.
59
+
60
+ **L1–L3 are levels of abstraction and ownership in the problem domain** — whose need is
61
+ this, and at what altitude — and are NEVER a directory, package, deployable or module.
62
+ Technical constructs appear only at L4/L5, which is the allocation step. Test every node:
63
+ *if a refactor that changes no behaviour would force it to move, its level is wrong.*
64
+
65
+ **Architectural requirements are the other kind.** `requirement.architectural` is flat by
66
+ default — a uuid-PK rule, change attribution, tenant scoping. Its check is *universality*
37
67
  rather than existence, so one that is `live` and claimed by nothing fails: a policy
38
- declared and applied to nothing.
68
+ declared and applied to nothing. A `@level` is OPTIONAL here and opts the node into a tree
69
+ (for organising non-functional requirements under a quality taxonomy); once levelled, the
70
+ same nesting and link-floor rules apply as to a functional node.
39
71
 
40
72
  `@status` is a closed enum enforced by the loader, so a typo fails the load rather than
41
73
  silently disabling the entry.
42
74
 
75
+ **Record gaps rather than rounding them off.** `partial` says *this works and here is what
76
+ is wrong with it*, and it is the most useful status in the enum — a ledger with none is
77
+ usually one nobody read carefully. Then say what was DECIDED, which is a separate question:
78
+
79
+ - `@disposition: accepted` — understood, deliberately not being closed
80
+ - `@disposition: deferred` — will be closed, not now (name a ticket in `@trackedBy`, or
81
+ `verify` warns; deferring without one is how a known problem becomes an unknown one)
82
+ - **absent** — undecided, and that is a real state. `verify` counts these, because
83
+ *"which gaps has nobody ruled on?"* is the question a review exists to answer.
84
+
85
+ A `partial` nobody intends to finish is usually `abandoned` — built then deliberately
86
+ retired, the one status where a dangling reference is correct.
87
+
88
+ **`status: planned` locks in work you have not started.** Its references may dangle (write
89
+ the requirement before the entity), and it never counts toward object coverage — otherwise
90
+ declaring an intention would clear the unclaimed-entity warning and the gate would measure
91
+ ambition rather than work.
92
+
43
93
  ```yaml
44
94
  - requirement.functional:
45
95
  name: Pacing
@@ -35,9 +35,26 @@ mechanism exists to preserve.
35
35
  | `@implementedBy` above the L4 link floor | 1 |
36
36
  | live `requirement.architectural` claimed by nothing | 1 |
37
37
  | `@verifiedBy` naming a test that exists nowhere | 1 |
38
+ | `@verifiedBy` naming a name found only in an **unrecognised** test file | 0 (warning) |
38
39
  | `@verifiedBy` naming a test that is **skipped** | 0 (warning) |
39
40
  | an entity no requirement claims | 0 (warning) |
40
41
 
42
+ ## What counts as a test file is YOUR project's call
43
+
44
+ The scan ships patterns for jest/vitest/bun, JUnit, Maven Failsafe (`*IT`), xUnit/NUnit,
45
+ pytest and Kotlin. Those are a convenience, **not an authority** — a built-in list is a guess
46
+ about your repository, and a wrong guess reports a real test as a broken claim. Declare your
47
+ conventions and they are added to the built-ins:
48
+
49
+ ```ts
50
+ // metaobjects.config.ts
51
+ export default defineConfig({ verify: { testFiles: ["**/*IT.kt", "**/*.feature"] } });
52
+ ```
53
+
54
+ If a named test is missing from the corpus but present in some other source file, `verify`
55
+ warns and names that file rather than failing — an unrecognised convention is the tool's
56
+ ignorance, not your mistake.
57
+
41
58
  ## What a green run does NOT prove
42
59
 
43
60
  It proves **referential integrity**: statuses parse, levels are in range, links sit at or
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metaobjectsdev/sdk",
3
- "version": "0.22.1",
3
+ "version": "0.23.1-rc.1",
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.22.1",
59
+ "@metaobjectsdev/metadata": "0.23.1-rc.1",
60
60
  "zod": "^3.23.0"
61
61
  },
62
62
  "devDependencies": {