@metaobjectsdev/sdk 0.21.6 → 0.22.0-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.
@@ -9,7 +9,7 @@ the slim always-on Markdown (`.metaobjects/AGENTS.md` + `CLAUDE.md`) and the six
9
9
  - `servers/<lang>.meta.json` — per-server install + codegen command (drives the always-on).
10
10
  - `templates/always-on.md.mustache` — the slim always-on body (`{{stackLine}}`, `{{codegenCommand}}`).
11
11
  - `skills/<skill>/SKILL.md` — universal skill body.
12
- - `skills/<skill>/references/<token>.md` — language fragment; installed iff `<token>` is in the stack.
12
+ - `skills/<skill>/references/<token>.md` — language/framework or concern fragment; installed iff `<token>` is in the stack (`Stack.tokens`; concern tokens are OBSERVED from project state, e.g. `requirements` for a project declaring `requirement.*` nodes — see `SDK`'s `CONCERN_TOKENS`).
13
13
 
14
14
  The **scaffolded** set is exactly the six skills in the SDK's `SKILL_NAMES`
15
15
  (`src/agent-context/types.ts`) — the assembler emits only those. `skills/`
@@ -0,0 +1,60 @@
1
+ # Requirements — the audit dimension
2
+
3
+ This project declares `requirement.*` nodes, so the ledger is an auditable surface. Audit it
4
+ for **truthfulness**, not volume. A large ledger that lies is worse than a small one that
5
+ does not, because every later reader trusts it.
6
+
7
+ Run `meta verify` first. It settles referential integrity mechanically — do not spend audit
8
+ effort re-deriving what a green run already proves.
9
+
10
+ ## What verify has already proven (do not re-check by hand)
11
+
12
+ Links sit at or below the L4 floor, nesting agrees with levels, `@status` values are legal,
13
+ references resolve (with dangling allowed on `abandoned`/`superseded`), and named
14
+ `@verifiedBy` tests exist and are not skipped.
15
+
16
+ ## What only a human or an agent reading the code can catch
17
+
18
+ **1. Statuses that are false.** The highest-value finding in the whole dimension. A
19
+ requirement marked `live` whose implementation was gutted; one marked `partial` that is now
20
+ complete; one marked `abandoned` whose code is demonstrably still running. Sample the claims
21
+ and read the nodes. `status` is the only payload with controlled evidence behind it — the
22
+ resurrection protection rides entirely on it being true.
23
+
24
+ **2. Non-violable statements.** Every requirement must state what breaking it looks like.
25
+ *"Every entity has a uuid primary key"* is violable — point at one with a composite key.
26
+ *"Things are persisted"* is not; it is a description wearing a requirement's shape. Flag
27
+ these: they inflate the ledger while proving nothing.
28
+
29
+ **3. Claim-padding.** An entity appended to an existing unrelated requirement's
30
+ `implementedBy` purely to silence the coverage warning. The claim resolves, so verify is
31
+ happy, but it carries no information about what the entity is *for*. Look for entities whose
32
+ only claim is a high-fan-out architectural rule when they plainly have product meaning.
33
+
34
+ **4. Misfiled kind.** `functional` is checked by EXISTENCE, `architectural` by UNIVERSALITY —
35
+ opposite polarity, and nothing mechanical catches a misfile. The discriminator: did this
36
+ exist because someone asked for something (functional), or because every entity here looks
37
+ like this (architectural)? An architectural entry with one claimant is usually a misfiled
38
+ functional one.
39
+
40
+ **5. Levels used as decoration.** L1–L3 are organisational and must never reference the
41
+ model. If the tree is flat, or every entry is L3, the levels are carrying no information and
42
+ should be simplified rather than defended.
43
+
44
+ ## Scope — do NOT flag these as defects
45
+
46
+ - **Unclaimed `object.value` / `object.projection`.** Exempt by design: a value is a shape, a
47
+ projection derives from a claimable entity.
48
+ - **Fields, views, validators and identities without their own requirement.** Member-grain
49
+ coverage is explicitly rejected — plumbing members are covered by architectural
50
+ requirements with high fan-out. L5 exists so a member claim *can* be made where it means
51
+ something, never so every member must carry one.
52
+ - **Dangling links on `abandoned` / `superseded`.** Correct, and load-bearing.
53
+ - **A project with no requirements at all.** The feature is opt-in by declaration; absence is
54
+ not a finding.
55
+
56
+ ## Reporting
57
+
58
+ Report findings as claims to verify, not as a score. "These 4 requirements are marked `live`
59
+ and I could not find their implementation" is actionable; "ledger maturity: 62%" is not, and
60
+ invites optimising the number.
@@ -974,6 +974,10 @@ mapping).
974
974
  - field.int: { name: quantity, required: true }
975
975
  ```
976
976
 
977
+ ## Requirements — capability ledger (opt-in)
978
+
979
+ If this project declares `requirement.functional` / `requirement.architectural` nodes, read `references/requirements.md` for the full authoring rules.
980
+
977
981
  ---
978
982
 
979
983
  For non-trivial schema design, use `/superpowers:brainstorming` if installed;
@@ -0,0 +1,69 @@
1
+ # Requirements — `requirement.functional` / `requirement.architectural`
2
+
3
+ Capabilities are **metadata**, declared in `metaobjects/` beside the entities they
4
+ describe. Read the existing requirement nodes before designing anything. Two rules matter
5
+ more than the rest.
6
+
7
+ **1. When you retire something, record it — at that moment.** Set the requirement's
8
+ `status` to `abandoned` (built, then deliberately dropped) or `superseded` (something else
9
+ does it now — name it in `supersededBy`), in the same change that removes the code.
10
+
11
+ This is the one thing a requirement does that the rest of the model cannot. Given a brief
12
+ matching a retired feature, agents reading only the model proposed **reviving** it 24 times
13
+ out of 24, each believing it was reusing. A retired feature is *more* attractive than a
14
+ live one: purpose-built for exactly the request, never complicated by production.
15
+
16
+ Leaving a dangling `implementedBy` on an `abandoned` or `superseded` requirement is
17
+ **correct** — those nodes are supposed to be gone, and `verify` allows it deliberately. On
18
+ `live` or `partial` the same dangling reference is an error: the model moved and the
19
+ requirement went stale.
20
+
21
+ **2. When you add an entity, claim it.** Every `object.entity` should appear in some
22
+ requirement's `implementedBy`, or `verify` says so.
23
+
24
+ **Every requirement states its violation.** One sentence: what breaking it looks like.
25
+ *"Every entity has a uuid primary key"* is violable — point at one with a composite string
26
+ key. *"Things are persisted"* is not, and is a description rather than a requirement. Same
27
+ rule kills *"the system is reliable"*. If you cannot say what breaking it looks like,
28
+ delete it.
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.
33
+ Regrouping *moves* a node; it does not edit a parent string.
34
+
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*
37
+ rather than existence, so one that is `live` and claimed by nothing fails: a policy
38
+ declared and applied to nothing.
39
+
40
+ `@status` is a closed enum enforced by the loader, so a typo fails the load rather than
41
+ silently disabling the entry.
42
+
43
+ ```yaml
44
+ - requirement.functional:
45
+ name: Pacing
46
+ level: 3
47
+ status: live
48
+ statement: "Scene pacing follows story beats"
49
+ violation: "A scene that advances on a clock rather than on the story"
50
+ children:
51
+ - requirement.functional:
52
+ name: TurnTimer
53
+ level: 4
54
+ status: abandoned # retired deliberately -- do NOT revive
55
+ statement: "Pacing was driven by a per-turn wall-clock timer"
56
+ violation: "Pacing driven by elapsed time instead of beat completion"
57
+ supersededBy: BeatProgression
58
+ implementedBy: ["game::turn::TurnTimer"] # gone, and that is the point
59
+
60
+ - requirement.architectural:
61
+ name: UuidPrimaryKeys
62
+ status: live
63
+ statement: "Every entity has a uuid primary key"
64
+ violation: "An entity keyed by a composite string"
65
+ implementedBy: ["game::turn::Turn", "game::world::Location"]
66
+ ```
67
+
68
+ There is **no `satisfies:` on a field or entity** — links live on the requirement node, not
69
+ on the nodes it claims. Full reference: the repo's `spec/capability-ledger.md`.
@@ -13,6 +13,35 @@ truth. Its sibling is `metaobjects-audit` (POST-adoption; scaffolded). Design +
13
13
  retro-test validation: `docs/superpowers/specs/2026-07-12-metaobjects-fit-assessment-design.md`.
14
14
  How to run it: point a high-end LLM at a target repo + this file (e.g. paste it, or
15
15
  fetch it from GitHub / metaobjects.dev) and let it produce the report below.
16
+
17
+ DEFERRED, ON PURPOSE — capability requirements (`requirement.functional` /
18
+ `requirement.architectural`). This skill deliberately says NOTHING about them, and that is a
19
+ decision, not an omission.
20
+
21
+ Why not now: (1) the grounding rule above requires every capability claim be verifiable
22
+ against the CURRENT RELEASE, and `requirement.*` is not in one yet. (2) The signal->feature
23
+ mapping is unvalidated against this skill's own kill criterion: the controlled evidence
24
+ (0/24 model-only revivals) measures POST-adoption model-reading, and the feature's premise --
25
+ that the disproof lives nowhere in the model -- cuts against pre-adoption detectability. If
26
+ the reasoning was never written down, there is nothing for an evidence-cited assessment to
27
+ cite. (3) Whether anyone fills the ledger in is itself untested; advertising it pre-adoption
28
+ is the brochure failure this skill exists to avoid.
29
+
30
+ TRIGGER to revisit — the release carrying `requirement.*` has shipped, AND either:
31
+ Arm A a dogfooded project's `abandoned`/`superseded` entries are shown, retrospectively,
32
+ to have been discoverable PRE-adoption from repo evidence at file:line standard
33
+ (removal commit, dead flag, do-not-reintroduce comment);
34
+ Arm B a team that adopted via this assessment hits a resurrection the ledger would have
35
+ caught, or asks why the assessment never mentioned it.
36
+
37
+ ANTI-TRIGGER (defer -> never): if dogfooded entries trace only to tribal knowledge with no
38
+ repo artifact, this assessment structurally cannot speak to the feature as a finding.
39
+
40
+ SHAPE, pre-committed so it is not re-litigated as new machinery: ONE P7 hunt line in the
41
+ existing grammar (observable signal -> vocabulary + metadata_sketch), gated like every other
42
+ P7 line by "if a hunt line has hits, it must appear" -- so a repo with no retirement scar
43
+ tissue produces zero requirements content. NOT a fifth pillar, NOT a P4 rubric row, NOT an
44
+ R0 verdict line: no verdict may turn on it.
16
45
  -->
17
46
 
18
47
  # MetaObjects Fit & Migration Assessment
@@ -66,6 +66,13 @@ paired with the advisory anti-pattern pass above, never all three subverbs. Trea
66
66
  bare run as a smoke test: the real done-check is running the subverbs your project
67
67
  uses explicitly — `verify --codegen`, and, where a DB exists, `verify --db <url>`.
68
68
 
69
+ ## Requirements are checked on every run
70
+
71
+ If this project declares `requirement.functional` / `requirement.architectural` nodes, read
72
+ `references/requirements.md`: requirements are metadata, so they are checked on **every**
73
+ `meta verify` — there is no subverb — and the severity of a broken link depends on the
74
+ requirement's `@status`, which is the part that surprises people reading a failure.
75
+
69
76
  ## The `verify` subverbs
70
77
 
71
78
  `verify` has three drift checks. Run them in CI.
@@ -0,0 +1,54 @@
1
+ # Requirements — what `meta verify` checks
2
+
3
+ This project declares `requirement.*` nodes, so `verify` checks them. **There is no
4
+ subverb**: requirements are metadata, so they are checked on *every* `meta verify` run.
5
+
6
+ ## The split, and why it matters when you read a failure
7
+
8
+ | | owns |
9
+ |---|---|
10
+ | **loader** | `@status` enum, required attrs, child rules, levels — unconditional |
11
+ | **`verify`** | `@implementedBy` / `@verifiedBy` resolution — **severity depends on `@status`** |
12
+
13
+ A typo'd `@status` fails the **load** ("failed to load metadata"), before verify runs. If you
14
+ see that, no other diagnostic in the run is trustworthy — fix it first and re-run.
15
+
16
+ ## The status asymmetry — the one that surprises people
17
+
18
+ The **same** unresolved `@implementedBy` reference is:
19
+
20
+ - an **error** on `live` / `partial` — the model moved and the requirement is stale;
21
+ - **allowed** on `abandoned` / `superseded` — those nodes are *supposed* to be gone. That is
22
+ the entry doing its job, not drift.
23
+
24
+ So do not "fix" a dangling reference on an abandoned requirement by deleting it. Deleting it
25
+ destroys the record that something was deliberately retired, which is the single thing this
26
+ mechanism exists to preserve.
27
+
28
+ ## Exit codes
29
+
30
+ | situation | exit |
31
+ |---|---|
32
+ | clean tree, or no `requirement.*` nodes at all | 0 |
33
+ | dangling `@implementedBy` on `live`/`partial` | 1 |
34
+ | the same reference on `abandoned`/`superseded` | 0 |
35
+ | `@implementedBy` above the L4 link floor | 1 |
36
+ | live `requirement.architectural` claimed by nothing | 1 |
37
+ | `@verifiedBy` naming a test that exists nowhere | 1 |
38
+ | `@verifiedBy` naming a test that is **skipped** | 0 (warning) |
39
+ | an entity no requirement claims | 0 (warning) |
40
+
41
+ ## What a green run does NOT prove
42
+
43
+ It proves **referential integrity**: statuses parse, levels are in range, links sit at or
44
+ below the floor, references resolve, named tests exist and are not skipped.
45
+
46
+ It cannot prove a status is **true**, or that a node genuinely implements the requirement
47
+ claiming it. No test can. That judgement is yours.
48
+
49
+ Coverage is also narrower than the name suggests: it is checked at **entity grain only** —
50
+ `object.value` and `object.projection` are exempt, and fields, views, validators and
51
+ identities are never required to be claimed. Green means "every entity is claimed by
52
+ something", not "every node is described".
53
+
54
+ Full reference: the repo's `spec/capability-ledger.md`.
@@ -1 +1 @@
1
- {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../src/agent-context/assemble.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgD,KAAK,aAAa,EAAE,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAqC1G,qFAAqF;AACrF,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG,aAAa,EAAE,CAyCrF"}
1
+ {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../src/agent-context/assemble.ts"],"names":[],"mappings":"AAEA,OAAO,EACyD,KAAK,aAAa,EAAE,KAAK,KAAK,EAC7F,MAAM,YAAY,CAAC;AAuCpB,qFAAqF;AACrF,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG,aAAa,EAAE,CA0CrF"}
@@ -1,9 +1,11 @@
1
1
  import { readFileSync, readdirSync, existsSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { SKILL_NAMES, SERVER_LANGS, CLIENT_FRAMEWORKS } from "./types.js";
4
- // Language/framework reference fragments are stack-scoped; any other fragment
5
- // (e.g. capability-checklist) is universal and always installs.
6
- const LANGUAGE_FRAGMENT_TOKENS = new Set([...SERVER_LANGS, ...CLIENT_FRAMEWORKS]);
3
+ import { SKILL_NAMES, SERVER_LANGS, CLIENT_FRAMEWORKS, CONCERN_TOKENS, } from "./types.js";
4
+ // Language/framework/concern reference fragments are stack-scoped; any other
5
+ // fragment (e.g. capability-checklist) is universal and always installs. Adding a
6
+ // future concern token to CONCERN_TOKENS (types.ts) is all that's needed to gate
7
+ // its matching references/<token>.md fragment here.
8
+ const SCOPED_FRAGMENT_TOKENS = new Set([...SERVER_LANGS, ...CLIENT_FRAMEWORKS, ...CONCERN_TOKENS]);
7
9
  function readServerMeta(contentRoot, server) {
8
10
  const p = join(contentRoot, "servers", `${server}.meta.json`);
9
11
  if (!existsSync(p))
@@ -49,17 +51,18 @@ export function assemble(opts) {
49
51
  out.push({ path: `.claude/skills/${skill}/SKILL.md`, contents: body });
50
52
  const refDir = join(skillDir, "references");
51
53
  if (existsSync(refDir) && statSync(refDir).isDirectory()) {
52
- // Stack-scoped: a language/framework fragment installs only when its token
53
- // is in the resolved stack (Stack.tokens = servers ∪ clients ∪ {migration}).
54
- // Non-language fragments (e.g. capability-checklist) are universal and always
55
- // install. Keeps each skill's references/ to what the project actually uses,
56
- // so the SKILL.md "read every references/*.md" footer stays accurate.
54
+ // Stack-scoped: a language/framework/concern fragment installs only when its
55
+ // token is in the resolved stack (Stack.tokens = servers ∪ clients ∪ concerns
56
+ // ∪ {migration}). Non-scoped fragments (e.g. capability-checklist) are
57
+ // universal and always install. Keeps each skill's references/ to what the
58
+ // project actually uses, so the SKILL.md "read every references/*.md" footer
59
+ // stays accurate.
57
60
  const refs = readdirSync(refDir)
58
61
  .filter((f) => f.endsWith(".md"))
59
62
  .map((f) => f.replace(/\.md$/, ""))
60
63
  .sort();
61
64
  for (const token of refs) {
62
- if (LANGUAGE_FRAGMENT_TOKENS.has(token) && !stack.tokens.has(token))
65
+ if (SCOPED_FRAGMENT_TOKENS.has(token) && !stack.tokens.has(token))
63
66
  continue;
64
67
  out.push({
65
68
  path: `.claude/skills/${skill}/references/${token}.md`,
@@ -1 +1 @@
1
- {"version":3,"file":"assemble.js","sourceRoot":"","sources":["../../src/agent-context/assemble.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"assemble.js","sourceRoot":"","sources":["../../src/agent-context/assemble.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,19 @@
1
- import { type ServerLang, type ClientFramework, type Stack } from "./types.js";
1
+ import { type ServerLang, type ClientFramework, type ConcernToken, type Stack } from "./types.js";
2
2
  export interface ProjectProbe {
3
3
  hasDep(name: string): boolean;
4
4
  hasFileMatching(pattern: RegExp): boolean;
5
+ /** True if the project's declared metadata contains at least one `requirement.*` node. */
6
+ hasRequirementNodes(): boolean;
5
7
  }
6
- export declare function makeStack(servers: ServerLang[], clients: ClientFramework[]): Stack;
8
+ export declare function makeStack(servers: ServerLang[], clients: ClientFramework[], concerns?: ConcernToken[]): Stack;
7
9
  /** Best-effort detection from a project probe. Always overridable; a wrong guess
8
10
  * writes an extra fragment, never a wrong one (callers confirm before scaffolding). */
9
11
  export declare function detectStack(probe: ProjectProbe): {
10
12
  servers: ServerLang[];
11
13
  clients: ClientFramework[];
12
14
  };
15
+ /** Best-effort concern detection — OBSERVED project state, never a config flag.
16
+ * Independent of servers/clients: a project's use of a capability doesn't depend
17
+ * on which server language or client framework it runs. */
18
+ export declare function detectConcerns(probe: ProjectProbe): ConcernToken[];
13
19
  //# sourceMappingURL=resolve.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/agent-context/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,KAAK,EAClD,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3C;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,KAAK,CAIlF;AAED;uFACuF;AACvF,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG;IAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IAAC,OAAO,EAAE,eAAe,EAAE,CAAA;CAAE,CActG"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/agent-context/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,KAAK,KAAK,EACrE,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1C,0FAA0F;IAC1F,mBAAmB,IAAI,OAAO,CAAC;CAChC;AAED,wBAAgB,SAAS,CACvB,OAAO,EAAE,UAAU,EAAE,EACrB,OAAO,EAAE,eAAe,EAAE,EAC1B,QAAQ,GAAE,YAAY,EAAO,GAC5B,KAAK,CAKP;AAED;uFACuF;AACvF,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG;IAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IAAC,OAAO,EAAE,eAAe,EAAE,CAAA;CAAE,CActG;AAED;;2DAE2D;AAC3D,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY,EAAE,CAIlE"}
@@ -1,8 +1,9 @@
1
- import { SERVER_LANGS, CLIENT_FRAMEWORKS, MIGRATION_TOKEN, } from "./types.js";
2
- export function makeStack(servers, clients) {
1
+ import { SERVER_LANGS, CLIENT_FRAMEWORKS, CONCERN_TOKENS, MIGRATION_TOKEN, } from "./types.js";
2
+ export function makeStack(servers, clients, concerns = []) {
3
3
  const s = SERVER_LANGS.filter((x) => servers.includes(x));
4
4
  const c = CLIENT_FRAMEWORKS.filter((x) => clients.includes(x));
5
- return { servers: s, clients: c, tokens: new Set([...s, ...c, MIGRATION_TOKEN]) };
5
+ const k = CONCERN_TOKENS.filter((x) => concerns.includes(x));
6
+ return { servers: s, clients: c, concerns: k, tokens: new Set([...s, ...c, ...k, MIGRATION_TOKEN]) };
6
7
  }
7
8
  /** Best-effort detection from a project probe. Always overridable; a wrong guess
8
9
  * writes an extra fragment, never a wrong one (callers confirm before scaffolding). */
@@ -27,4 +28,13 @@ export function detectStack(probe) {
27
28
  clients.push("angular");
28
29
  return { servers, clients };
29
30
  }
31
+ /** Best-effort concern detection — OBSERVED project state, never a config flag.
32
+ * Independent of servers/clients: a project's use of a capability doesn't depend
33
+ * on which server language or client framework it runs. */
34
+ export function detectConcerns(probe) {
35
+ const concerns = [];
36
+ if (probe.hasRequirementNodes())
37
+ concerns.push("requirements");
38
+ return concerns;
39
+ }
30
40
  //# sourceMappingURL=resolve.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/agent-context/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAAE,iBAAiB,EAAE,eAAe,GAEjD,MAAM,YAAY,CAAC;AAOpB,MAAM,UAAU,SAAS,CAAC,OAAqB,EAAE,OAA0B;IACzE,MAAM,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,CAAC,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,GAAG,CAAS,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC;AAC5F,CAAC;AAED;uFACuF;AACvF,MAAM,UAAU,WAAW,CAAC,KAAmB;IAC7C,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,IAAI,KAAK,CAAC,MAAM,CAAC,qBAAqB,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,4BAA4B,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAClH,IAAI,KAAK,CAAC,eAAe,CAAC,8BAA8B,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClF,IAAI,KAAK,CAAC,eAAe,CAAC,iBAAiB,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnE,IAAI,KAAK,CAAC,eAAe,CAAC,WAAW,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/D,IAAI,KAAK,CAAC,eAAe,CAAC,oCAAoC,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAExF,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,IAAI,KAAK,CAAC,MAAM,CAAC,uBAAuB,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjE,IAAI,KAAK,CAAC,MAAM,CAAC,0BAA0B,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvE,IAAI,KAAK,CAAC,MAAM,CAAC,yBAAyB,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAErE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC"}
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/agent-context/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,eAAe,GAEjE,MAAM,YAAY,CAAC;AASpB,MAAM,UAAU,SAAS,CACvB,OAAqB,EACrB,OAA0B,EAC1B,WAA2B,EAAE;IAE7B,MAAM,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,CAAC,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D,MAAM,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,GAAG,CAAS,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC;AAC/G,CAAC;AAED;uFACuF;AACvF,MAAM,UAAU,WAAW,CAAC,KAAmB;IAC7C,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,IAAI,KAAK,CAAC,MAAM,CAAC,qBAAqB,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,4BAA4B,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAClH,IAAI,KAAK,CAAC,eAAe,CAAC,8BAA8B,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClF,IAAI,KAAK,CAAC,eAAe,CAAC,iBAAiB,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnE,IAAI,KAAK,CAAC,eAAe,CAAC,WAAW,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/D,IAAI,KAAK,CAAC,eAAe,CAAC,oCAAoC,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAExF,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,IAAI,KAAK,CAAC,MAAM,CAAC,uBAAuB,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjE,IAAI,KAAK,CAAC,MAAM,CAAC,0BAA0B,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvE,IAAI,KAAK,CAAC,MAAM,CAAC,yBAAyB,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAErE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC;AAED;;2DAE2D;AAC3D,MAAM,UAAU,cAAc,CAAC,KAAmB;IAChD,MAAM,QAAQ,GAAmB,EAAE,CAAC;IACpC,IAAI,KAAK,CAAC,mBAAmB,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC/D,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -2,6 +2,14 @@ export declare const SERVER_LANGS: readonly ["typescript", "java", "kotlin", "cs
2
2
  export type ServerLang = (typeof SERVER_LANGS)[number];
3
3
  export declare const CLIENT_FRAMEWORKS: readonly ["react", "tanstack", "angular"];
4
4
  export type ClientFramework = (typeof CLIENT_FRAMEWORKS)[number];
5
+ /**
6
+ * Opt-in capability concerns, detected from OBSERVED project state (never a config
7
+ * flag — a flag goes stale, observed metadata cannot). Each token gates a
8
+ * `references/<token>.md` fragment the same way a language/framework token does.
9
+ * Add a new concern here; nothing else in the assembler needs to change.
10
+ */
11
+ export declare const CONCERN_TOKENS: readonly ["requirements"];
12
+ export type ConcernToken = (typeof CONCERN_TOKENS)[number];
5
13
  /** Always-present token: schema migrations are TS-owned for every port (ADR-0015). */
6
14
  export declare const MIGRATION_TOKEN = "migration";
7
15
  export declare const SKILL_NAMES: readonly ["metaobjects-authoring", "metaobjects-codegen", "metaobjects-runtime-ui", "metaobjects-prompts", "metaobjects-verify", "metaobjects-audit"];
@@ -10,7 +18,8 @@ export type SkillName = (typeof SKILL_NAMES)[number];
10
18
  export interface Stack {
11
19
  servers: ServerLang[];
12
20
  clients: ClientFramework[];
13
- /** servers ∪ clients ∪ {"migration"} — the install-selection set for reference fragments. */
21
+ concerns: ConcernToken[];
22
+ /** servers ∪ clients ∪ concerns ∪ {"migration"} — the install-selection set for reference fragments. */
14
23
  tokens: ReadonlySet<string>;
15
24
  }
16
25
  /** A file the assembler emits, path relative to the consumer project root. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/agent-context/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,+DAAgE,CAAC;AAC1F,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,eAAO,MAAM,iBAAiB,2CAA4C,CAAC;AAC3E,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE,sFAAsF;AACtF,eAAO,MAAM,eAAe,cAAc,CAAC;AAE3C,eAAO,MAAM,WAAW,uJAOd,CAAC;AACX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,qDAAqD;AACrD,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,6FAA6F;IAC7F,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC7B;AAED,8EAA8E;AAC9E,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/agent-context/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,+DAAgE,CAAC;AAC1F,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,eAAO,MAAM,iBAAiB,2CAA4C,CAAC;AAC3E,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE;;;;;GAKG;AACH,eAAO,MAAM,cAAc,2BAA4B,CAAC;AACxD,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,sFAAsF;AACtF,eAAO,MAAM,eAAe,cAAc,CAAC;AAE3C,eAAO,MAAM,WAAW,uJAOd,CAAC;AACX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,qDAAqD;AACrD,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,wGAAwG;IACxG,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC7B;AAED,8EAA8E;AAC9E,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB"}
@@ -1,5 +1,12 @@
1
1
  export const SERVER_LANGS = ["typescript", "java", "kotlin", "csharp", "python"];
2
2
  export const CLIENT_FRAMEWORKS = ["react", "tanstack", "angular"];
3
+ /**
4
+ * Opt-in capability concerns, detected from OBSERVED project state (never a config
5
+ * flag — a flag goes stale, observed metadata cannot). Each token gates a
6
+ * `references/<token>.md` fragment the same way a language/framework token does.
7
+ * Add a new concern here; nothing else in the assembler needs to change.
8
+ */
9
+ export const CONCERN_TOKENS = ["requirements"];
3
10
  /** Always-present token: schema migrations are TS-owned for every port (ADR-0015). */
4
11
  export const MIGRATION_TOKEN = "migration";
5
12
  export const SKILL_NAMES = [
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/agent-context/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAG1F,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,CAAU,CAAC;AAG3E,sFAAsF;AACtF,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC;AAE3C,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,uBAAuB;IACvB,qBAAqB;IACrB,wBAAwB;IACxB,qBAAqB;IACrB,oBAAoB;IACpB,mBAAmB;CACX,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/agent-context/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAG1F,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,CAAU,CAAC;AAG3E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,cAAc,CAAU,CAAC;AAGxD,sFAAsF;AACtF,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC;AAE3C,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,uBAAuB;IACvB,qBAAqB;IACrB,wBAAwB;IACxB,qBAAqB;IACrB,oBAAoB;IACpB,mBAAmB;CACX,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metaobjectsdev/sdk",
3
- "version": "0.21.6",
3
+ "version": "0.22.0-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.21.6",
59
+ "@metaobjectsdev/metadata": "0.22.0-rc.1",
60
60
  "zod": "^3.23.0"
61
61
  },
62
62
  "devDependencies": {
@@ -17,8 +17,9 @@ for (const name of readdirSync(CORPUS)) {
17
17
  const spec = JSON.parse(readFileSync(join(dir, "stack.json"), "utf8")) as {
18
18
  servers: string[];
19
19
  clients: string[];
20
+ concerns?: string[];
20
21
  };
21
- const stack = makeStack(spec.servers as never, spec.clients as never);
22
+ const stack = makeStack(spec.servers as never, spec.clients as never, (spec.concerns ?? []) as never);
22
23
  const files = assemble({ contentRoot: CONTENT_ROOT, stack });
23
24
  const expDir = join(dir, "expected");
24
25
  rmSync(expDir, { recursive: true, force: true });
@@ -1,10 +1,14 @@
1
1
  import { readFileSync, readdirSync, existsSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { SKILL_NAMES, SERVER_LANGS, CLIENT_FRAMEWORKS, type AssembledFile, type Stack } from "./types.js";
3
+ import {
4
+ SKILL_NAMES, SERVER_LANGS, CLIENT_FRAMEWORKS, CONCERN_TOKENS, type AssembledFile, type Stack,
5
+ } from "./types.js";
4
6
 
5
- // Language/framework reference fragments are stack-scoped; any other fragment
6
- // (e.g. capability-checklist) is universal and always installs.
7
- const LANGUAGE_FRAGMENT_TOKENS = new Set<string>([...SERVER_LANGS, ...CLIENT_FRAMEWORKS]);
7
+ // Language/framework/concern reference fragments are stack-scoped; any other
8
+ // fragment (e.g. capability-checklist) is universal and always installs. Adding a
9
+ // future concern token to CONCERN_TOKENS (types.ts) is all that's needed to gate
10
+ // its matching references/<token>.md fragment here.
11
+ const SCOPED_FRAGMENT_TOKENS = new Set<string>([...SERVER_LANGS, ...CLIENT_FRAMEWORKS, ...CONCERN_TOKENS]);
8
12
 
9
13
  interface ServerMeta { displayName: string; install: string; codegenCommand: string; }
10
14
 
@@ -57,17 +61,18 @@ export function assemble(opts: { contentRoot: string; stack: Stack }): Assembled
57
61
 
58
62
  const refDir = join(skillDir, "references");
59
63
  if (existsSync(refDir) && statSync(refDir).isDirectory()) {
60
- // Stack-scoped: a language/framework fragment installs only when its token
61
- // is in the resolved stack (Stack.tokens = servers ∪ clients ∪ {migration}).
62
- // Non-language fragments (e.g. capability-checklist) are universal and always
63
- // install. Keeps each skill's references/ to what the project actually uses,
64
- // so the SKILL.md "read every references/*.md" footer stays accurate.
64
+ // Stack-scoped: a language/framework/concern fragment installs only when its
65
+ // token is in the resolved stack (Stack.tokens = servers ∪ clients ∪ concerns
66
+ // ∪ {migration}). Non-scoped fragments (e.g. capability-checklist) are
67
+ // universal and always install. Keeps each skill's references/ to what the
68
+ // project actually uses, so the SKILL.md "read every references/*.md" footer
69
+ // stays accurate.
65
70
  const refs = readdirSync(refDir)
66
71
  .filter((f) => f.endsWith(".md"))
67
72
  .map((f) => f.replace(/\.md$/, ""))
68
73
  .sort();
69
74
  for (const token of refs) {
70
- if (LANGUAGE_FRAGMENT_TOKENS.has(token) && !stack.tokens.has(token)) continue;
75
+ if (SCOPED_FRAGMENT_TOKENS.has(token) && !stack.tokens.has(token)) continue;
71
76
  out.push({
72
77
  path: `.claude/skills/${skill}/references/${token}.md`,
73
78
  contents: readFileSync(join(refDir, `${token}.md`), "utf8"),
@@ -1,17 +1,24 @@
1
1
  import {
2
- SERVER_LANGS, CLIENT_FRAMEWORKS, MIGRATION_TOKEN,
3
- type ServerLang, type ClientFramework, type Stack,
2
+ SERVER_LANGS, CLIENT_FRAMEWORKS, CONCERN_TOKENS, MIGRATION_TOKEN,
3
+ type ServerLang, type ClientFramework, type ConcernToken, type Stack,
4
4
  } from "./types.js";
5
5
 
6
6
  export interface ProjectProbe {
7
7
  hasDep(name: string): boolean;
8
8
  hasFileMatching(pattern: RegExp): boolean;
9
+ /** True if the project's declared metadata contains at least one `requirement.*` node. */
10
+ hasRequirementNodes(): boolean;
9
11
  }
10
12
 
11
- export function makeStack(servers: ServerLang[], clients: ClientFramework[]): Stack {
13
+ export function makeStack(
14
+ servers: ServerLang[],
15
+ clients: ClientFramework[],
16
+ concerns: ConcernToken[] = [],
17
+ ): Stack {
12
18
  const s = SERVER_LANGS.filter((x) => servers.includes(x));
13
19
  const c = CLIENT_FRAMEWORKS.filter((x) => clients.includes(x));
14
- return { servers: s, clients: c, tokens: new Set<string>([...s, ...c, MIGRATION_TOKEN]) };
20
+ const k = CONCERN_TOKENS.filter((x) => concerns.includes(x));
21
+ return { servers: s, clients: c, concerns: k, tokens: new Set<string>([...s, ...c, ...k, MIGRATION_TOKEN]) };
15
22
  }
16
23
 
17
24
  /** Best-effort detection from a project probe. Always overridable; a wrong guess
@@ -31,3 +38,12 @@ export function detectStack(probe: ProjectProbe): { servers: ServerLang[]; clien
31
38
 
32
39
  return { servers, clients };
33
40
  }
41
+
42
+ /** Best-effort concern detection — OBSERVED project state, never a config flag.
43
+ * Independent of servers/clients: a project's use of a capability doesn't depend
44
+ * on which server language or client framework it runs. */
45
+ export function detectConcerns(probe: ProjectProbe): ConcernToken[] {
46
+ const concerns: ConcernToken[] = [];
47
+ if (probe.hasRequirementNodes()) concerns.push("requirements");
48
+ return concerns;
49
+ }
@@ -4,6 +4,15 @@ export type ServerLang = (typeof SERVER_LANGS)[number];
4
4
  export const CLIENT_FRAMEWORKS = ["react", "tanstack", "angular"] as const;
5
5
  export type ClientFramework = (typeof CLIENT_FRAMEWORKS)[number];
6
6
 
7
+ /**
8
+ * Opt-in capability concerns, detected from OBSERVED project state (never a config
9
+ * flag — a flag goes stale, observed metadata cannot). Each token gates a
10
+ * `references/<token>.md` fragment the same way a language/framework token does.
11
+ * Add a new concern here; nothing else in the assembler needs to change.
12
+ */
13
+ export const CONCERN_TOKENS = ["requirements"] as const;
14
+ export type ConcernToken = (typeof CONCERN_TOKENS)[number];
15
+
7
16
  /** Always-present token: schema migrations are TS-owned for every port (ADR-0015). */
8
17
  export const MIGRATION_TOKEN = "migration";
9
18
 
@@ -21,7 +30,8 @@ export type SkillName = (typeof SKILL_NAMES)[number];
21
30
  export interface Stack {
22
31
  servers: ServerLang[]; // deduped, in SERVER_LANGS order
23
32
  clients: ClientFramework[]; // deduped, in CLIENT_FRAMEWORKS order
24
- /** servers clients {"migration"}the install-selection set for reference fragments. */
33
+ concerns: ConcernToken[]; // deduped, in CONCERN_TOKENS order observed capability usage
34
+ /** servers ∪ clients ∪ concerns ∪ {"migration"} — the install-selection set for reference fragments. */
25
35
  tokens: ReadonlySet<string>;
26
36
  }
27
37