@skill-map/spec 0.8.0 → 0.10.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,160 @@
1
1
  # Spec changelog
2
2
 
3
+ ## 0.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f8a7125: Open `Node.kind` to any Provider-declared string (Phase A — spec only).
8
+
9
+ The kernel always documented `IProvider.kinds` as "open by design" so future Cursor / Obsidian / Roo Providers can declare their own kinds. The spec, however, had three layers underneath that closed it back to the original five-value Claude Provider catalog (`skill` / `agent` / `command` / `hook` / `note`):
10
+
11
+ - `node.schema.json#/properties/kind` carried `enum: [<5 values>]` — AJV-rejected anything else.
12
+ - `db-schema.md` § `scan_nodes` and § `state_summaries` mandated `CHECK in (<5 values>)` SQL constraints.
13
+ - `extensions/action.schema.json#/.../filter/kind` had the same closed list for the per-action applicability filter.
14
+
15
+ This phase opens the spec end:
16
+
17
+ - `node.schema.json#/properties/kind` → `{ "type": "string", "minLength": 1 }` with a description naming the built-in Claude catalog so consumers know the default contract.
18
+ - `db-schema.md` drops both `CHECK in (...)` constraint rows. Both columns stay `TEXT NOT NULL`.
19
+ - `extensions/action.schema.json#/.../filter/kind` widens to `{ items: { "type": "string", "minLength": 1 } }`.
20
+
21
+ The TS side (`Node.kind: string`, `IProvider.classify(...): { kind: string; ... }`, Kysely `TNodeKind = string`) and the SQL `002_open_node_kinds` migration that drops the live CHECK constraints land in follow-up phases under `@skill-map/cli`. Phase A is a safe checkpoint: shipping the spec change alone changes nothing at runtime (the kernel still emits closed kinds, the live DB still enforces the existing CHECK), but it unblocks the rest of the refactor and aligns the source-of-truth artifact with the design intent.
22
+
23
+ Migration for consumers:
24
+
25
+ - Anyone validating an exported `Node` JSON against `node.schema.json` now accepts external-Provider kinds.
26
+ - Any UI / dashboard / script that hard-coded the closed enum elsewhere (filter chips, assertion sets) needs to widen to `string` and accept whatever an enabled Provider declares.
27
+
28
+ Pre-1.0 minor bump per `spec/versioning.md` § Pre-1.0 (this is breaking for consumers that relied on the enum, but pre-1.0 breakings ship as minor).
29
+
30
+ ## 0.9.0
31
+
32
+ ### Minor Changes
33
+
34
+ - 88afe24: Cleanup pass post-v0.8.0 — finishing the renames and wiring the
35
+ conformance kill-switches.
36
+
37
+ **Pre-1.0 minor bump** per `spec/versioning.md` § Pre-1.0. The schema
38
+ field rename below is technically breaking, but ships as a minor while
39
+ the spec stays `0.Y.Z`.
40
+
41
+ ## Spec changes (`@skill-map/spec`)
42
+
43
+ ### Breaking — `conformance-case.schema.json`
44
+
45
+ - **Rename `setup.disableAllDetectors` → `setup.disableAllExtractors`.**
46
+ Finishes the kind rename Detector → Extractor introduced in 0.8.0
47
+ (Phase 2 of the plug-in model overhaul). The previous name was the
48
+ last residue and it never reached a release where anything consumed
49
+ it.
50
+ - **`setup.disableAll{Providers,Extractors,Rules}` are now consumed
51
+ end-to-end.** Until this release the three toggles were declared in
52
+ the schema and accepted by the runner, but the runner never threaded
53
+ them anywhere — the `kernel-empty-boot` case happened to pass
54
+ because its fixture is empty. The runner now injects
55
+ `SKILL_MAP_DISABLE_ALL_{PROVIDERS,EXTRACTORS,RULES}=1` into the
56
+ child process environment when the matching toggle is `true`, and
57
+ the CLI's scan composer drops every extension of the disabled kind
58
+ from the in-scan pipeline regardless of granularity gates and
59
+ `--no-built-ins`. Each toggle now has a docstring on the schema
60
+ property pointing at the env-var convention.
61
+ - `kernel-empty-boot` case updated for the rename.
62
+ - `conformance/README.md` example updated.
63
+
64
+ ### Non-breaking — copy fixes
65
+
66
+ - Comments and docstrings across `architecture.md` and friends already
67
+ refer to "Extractor" everywhere; only the schema field stayed on the
68
+ old name. No prose changes in this bump.
69
+
70
+ ## CLI changes (`@skill-map/cli`)
71
+
72
+ ### Breaking — `IDiscoveredPlugin.status` enum
73
+
74
+ - **Rename `'loaded'` → `'enabled'`.** The schema enum
75
+ (`plugins-registry.schema.json`) already used `enabled` since 0.8.0;
76
+ the runtime drifted to `loaded` and has now been pulled back so the
77
+ runtime status matches the spec contract. `'disabled'`, the
78
+ semantic pair, was already aligned. Every consumer (`sm plugins
79
+ list`, `sm plugins doctor`, `sm db prune` plugin filter, runtime
80
+ plugin composer) updated. No published consumers exist.
81
+
82
+ ### Non-breaking — sweep cleanup
83
+
84
+ - Old `Detector` / `detector` references (kind name, manifest field
85
+ names, JSDoc, comments, test fixture filenames, test variable
86
+ names) replaced with `Extractor` / `extractor` across the
87
+ production code and test suite. Excludes historical CHANGELOG
88
+ entries, explicit migration notes ("Renamed from Detector"), and
89
+ test data strings whose semantics are independent of the kind
90
+ name (e.g. `'@FooDetector'` in trigger normalization tests).
91
+ - A residual reference to "an audit reading `ScanResult.issues`" in
92
+ `validate-all`'s docstring rewritten without the removed kind name.
93
+
94
+ ## Tests
95
+
96
+ - `plugin-runtime-branches.test.ts` — five new unit tests covering
97
+ the env-var kill-switch in `composeScanExtensions` (per kind, all
98
+ three together, and stray-value resilience).
99
+ - `conformance-disable-flags.test.ts` — four new e2e tests pointing
100
+ the runner at a populated fixture with each toggle in turn (and a
101
+ baseline) so a regression in the env-var pipeline shows up
102
+ structurally rather than relying on the empty-fixture coincidence.
103
+
104
+ ## [Unreleased]
105
+
106
+ ### Minor (breaking, pre-1.0)
107
+
108
+ - **`Node.kind` opens to any non-empty string (was the closed enum
109
+ `skill` / `agent` / `command` / `hook` / `note`).** The kernel always
110
+ permitted external Providers — `IProvider.kinds` is documented as
111
+ "open by design" so a future Cursor / Obsidian / Roo Provider can
112
+ declare its own kinds — but the `node.schema.json` enum + the
113
+ `scan_nodes.kind` SQL CHECK + the closed TS `NodeKind` union closed
114
+ three layers underneath. Effects:
115
+ - `node.schema.json#/properties/kind` switches from `enum: [...5
116
+ values]` to `{ "type": "string", "minLength": 1 }`. The
117
+ description still names the built-in Claude Provider catalog so
118
+ consumers know what to expect from the default install.
119
+ - `db-schema.md` drops the `CHECK in (...)` constraint on
120
+ `scan_nodes.kind` and `state_summaries.kind`. Both columns stay
121
+ `TEXT NOT NULL`.
122
+ - `extensions/action.schema.json#/.../filter/kind` (the per-kind
123
+ filter for action applicability) widens the same way: `items:
124
+ { type: 'string', minLength: 1 }` instead of the closed enum.
125
+ Migration: consumers who validate exported `Node` JSON against
126
+ `node.schema.json` will now accept external-Provider kinds. Any
127
+ consumer that hard-coded the closed enum elsewhere (UI filter chip
128
+ set, scripted assertions) needs to widen to "string". The TS +
129
+ SQL counterpart lands in `@skill-map/cli` (kernel TS contract +
130
+ migration `002_open_node_kinds`).
131
+ - **`conformance-case.schema.json` — rename `setup.disableAllDetectors`
132
+ → `setup.disableAllExtractors`.** Finishes the kind rename Detector →
133
+ Extractor introduced in 0.8.0 (Phase 2 of the plug-in model
134
+ overhaul). The previous name was a residue from an unfinished sweep
135
+ and never reached a release that consumed it.
136
+ - **`setup.disableAll{Providers,Extractors,Rules}` are now wired
137
+ end-to-end.** Until this release the toggles were declared in the
138
+ schema but the runner threaded them nowhere; the `kernel-empty-boot`
139
+ case happened to pass because its fixture is empty. The runner now
140
+ injects `SKILL_MAP_DISABLE_ALL_{PROVIDERS,EXTRACTORS,RULES}=1` into
141
+ the child process environment per toggle, and the CLI's scan
142
+ composer drops every extension of the disabled kind from the
143
+ in-scan pipeline (overriding granularity gates and `--no-built-ins`).
144
+ Migration: any case JSON authored against the unwired schema needs
145
+ to swap `disableAllDetectors` for `disableAllExtractors`; behaviour
146
+ changes only when the toggles were already `true` (those cases will
147
+ now actually disable the kind, where previously they relied on
148
+ fixture content for the same outcome).
149
+
150
+ ### Patch
151
+
152
+ - Updated `conformance/cases/kernel-empty-boot.json` for the field
153
+ rename above.
154
+ - Updated `conformance/README.md` example for the field rename above.
155
+ - Schema docstrings added to each `disableAll*` property documenting
156
+ the env-var convention the runner uses.
157
+
3
158
  ## 0.8.0
4
159
 
5
160
  ### Minor Changes
@@ -83,36 +238,32 @@
83
238
  - **H3 — `--dry-run` semantics unified across `init` / `db reset`
84
239
  / `db restore`.** The new spec §Dry-run codifies the "no
85
240
  writes, reads OK" contract; three verbs that did not previously
86
- expose a preview now do:
87
- - `sm init --dry-run` — previews the would-create lines for
88
- `.skill-map/`, `settings.json`, `settings.local.json`,
89
- `.skill-mapignore`, the `.gitignore` entries that would be
90
- appended (deduped against the existing file), the DB
91
- provisioning, and the first-scan trigger. Honours `--force`
92
- for the would-overwrite preview. Re-init over an existing
93
- scope without `--force` still exits 2 (same gate as live).
94
- - `sm db reset --dry-run` (default + `--state`) — opens the DB
95
- read-only, computes the row count per `scan_*` (and `state_*`
96
- when `--state`) table, and prints them. No `DELETE`
97
- statements issued. Bypasses the `--state` confirmation prompt
98
- entirely.
99
- - `sm db reset --hard --dry-run` — reports the DB file path and
100
- size that would be unlinked; missing-file case prints a clear
101
- no-op line instead of an error.
102
- - `sm db restore <src> --dry-run` — validates the source exists
103
- (still exits 5 if missing), reports the source size and
104
- whether the target would be created or overwritten, plus the
105
- WAL / SHM sidecars that would be dropped. Bypasses the
106
- confirmation prompt.
107
- Implementation: new helper `previewGitignoreEntries(scopeRoot,
241
+ expose a preview now do: - `sm init --dry-run` — previews the would-create lines for
242
+ `.skill-map/`, `settings.json`, `settings.local.json`,
243
+ `.skill-mapignore`, the `.gitignore` entries that would be
244
+ appended (deduped against the existing file), the DB
245
+ provisioning, and the first-scan trigger. Honours `--force`
246
+ for the would-overwrite preview. Re-init over an existing
247
+ scope without `--force` still exits 2 (same gate as live). - `sm db reset --dry-run` (default + `--state`) — opens the DB
248
+ read-only, computes the row count per `scan_*` (and `state_*`
249
+ when `--state`) table, and prints them. No `DELETE`
250
+ statements issued. Bypasses the `--state` confirmation prompt
251
+ entirely. - `sm db reset --hard --dry-run` reports the DB file path and
252
+ size that would be unlinked; missing-file case prints a clear
253
+ no-op line instead of an error. - `sm db restore <src> --dry-run` — validates the source exists
254
+ (still exits 5 if missing), reports the source size and
255
+ whether the target would be created or overwritten, plus the
256
+ WAL / SHM sidecars that would be dropped. Bypasses the
257
+ confirmation prompt.
258
+ Implementation: new helper `previewGitignoreEntries(scopeRoot,
108
259
  entries)` in `init.ts` mirrors `ensureGitignoreEntries` parsing
109
- so the preview tracks the live outcome exactly. Texts moved
110
- into `cli/i18n/init.texts.ts` and `cli/i18n/db.texts.ts` per
111
- the N4 pattern. **9 new tests** under `init-cli.test.ts` (5
112
- cases) and `db-cli.test.ts` (9 cases) cover the previews + the
113
- spec invariants ("DB file checksum unchanged after dry-run",
114
- "scope directory absent after dry-run", "source-not-found
115
- still exits 5", "confirmation prompt skipped under dry-run").
260
+ so the preview tracks the live outcome exactly. Texts moved
261
+ into `cli/i18n/init.texts.ts` and `cli/i18n/db.texts.ts` per
262
+ the N4 pattern. **9 new tests** under `init-cli.test.ts` (5
263
+ cases) and `db-cli.test.ts` (9 cases) cover the previews + the
264
+ spec invariants ("DB file checksum unchanged after dry-run",
265
+ "scope directory absent after dry-run", "source-not-found
266
+ still exits 5", "confirmation prompt skipped under dry-run").
116
267
  - **H1 — Centralised exit codes.** New `cli/util/exit-codes.ts`
117
268
  exporting `ExitCode` (`Ok` / `Issues` / `Error` / `Duplicate` /
118
269
  `NonceMismatch` / `NotFound`) and the type alias `TExitCode`.
@@ -247,53 +398,53 @@ the`CamelCasePlugin`; raw SQL fragments must use snake_case to
247
398
  a real i18n library, the strings move as-is. Functions would
248
399
  have to be re-shaped first.
249
400
 
250
- Helper at `kernel/util/tx.ts`. Contract:
251
-
252
- - Every `{{name}}` token MUST have a matching key in the vars
253
- object — missing key throws (silent fallback hides
254
- forgotten args in production).
255
- - `null` / `undefined` values throw — caller coerces
256
- upstream.
257
- - Whitespace inside the braces tolerated (`{{ name }}`) so
258
- long templates wrap cleanly across `+`-joined lines.
259
- - Plural / conditional logic does NOT live in the template;
260
- the caller picks `*_singular` vs `*_plural` keys.
261
-
262
- Files created:
263
-
264
- - `kernel/util/tx.ts` — the helper itself, with 13 tests in
265
- `test/tx.test.ts` (single / multi token, whitespace,
266
- missing / null / undefined keys, identifier shapes, error
267
- truncation).
268
- - `kernel/i18n/orchestrator.texts.ts` — frontmatter
269
- malformed/invalid templates, `extension.error` payloads,
270
- root validation errors.
271
- - `kernel/i18n/plugin-loader.texts.ts` — every `load-error` /
272
- `invalid-manifest` / `incompatible-spec` reason, plus the
273
- import timeout message.
274
- - `cli/i18n/scan.texts.ts` — `sm scan` flag-clash / scan
275
- failure / guard / summary templates, plus the `sm scan
276
- compare-with` dump-load errors.
277
- - `cli/i18n/watch.texts.ts` — `sm watch` lifecycle templates.
278
- - `cli/i18n/init.texts.ts` — `sm init` templates including
279
- the `--dry-run` previews and the singular/plural pair for
280
- gitignore updates.
281
- - `cli/i18n/db.texts.ts` — `sm db reset` / `sm db restore`
282
- templates including their `--dry-run` previews.
283
- - `cli/i18n/cli-progress-emitter.texts.ts` the
284
- `extension.error: ...` stderr line.
285
-
286
- String content moved verbatim — every existing test that
287
- matches on stderr / stdout content keeps passing. Trivial
288
- single-token strings (`'No issues.\n'`) and rare per-handler
289
- bespoke phrases stay inline; the pattern is now established
290
- for whoever wants to migrate them in a follow-up.
291
-
292
- Note on `ui/` divergence: today the two workspaces use
293
- different shapes for their text tables (functions in `ui/`,
294
- templates in `cli/`). Aligning them is a follow-up — the day a
295
- real i18n library lands, both converge on its native shape.
296
- The CLI shape is closer to the eventual destination.
401
+ Helper at `kernel/util/tx.ts`. Contract:
402
+
403
+ - Every `{{name}}` token MUST have a matching key in the vars
404
+ object — missing key throws (silent fallback hides
405
+ forgotten args in production).
406
+ - `null` / `undefined` values throw — caller coerces
407
+ upstream.
408
+ - Whitespace inside the braces tolerated (`{{ name }}`) so
409
+ long templates wrap cleanly across `+`-joined lines.
410
+ - Plural / conditional logic does NOT live in the template;
411
+ the caller picks `*_singular` vs `*_plural` keys.
412
+
413
+ Files created:
414
+
415
+ - `kernel/util/tx.ts` — the helper itself, with 13 tests in
416
+ `test/tx.test.ts` (single / multi token, whitespace,
417
+ missing / null / undefined keys, identifier shapes, error
418
+ truncation).
419
+ - `kernel/i18n/orchestrator.texts.ts` — frontmatter
420
+ malformed/invalid templates, `extension.error` payloads,
421
+ root validation errors.
422
+ - `kernel/i18n/plugin-loader.texts.ts` — every `load-error` /
423
+ `invalid-manifest` / `incompatible-spec` reason, plus the
424
+ import timeout message.
425
+ - `cli/i18n/scan.texts.ts` — `sm scan` flag-clash / scan
426
+ failure / guard / summary templates, plus the `sm scan
427
+
428
+ compare-with`dump-load errors.
429
+ -`cli/i18n/watch.texts.ts`—`sm watch`lifecycle templates.
430
+ -`cli/i18n/init.texts.ts`—`sm init`templates including
431
+ the`--dry-run`previews and the singular/plural pair for
432
+ gitignore updates.
433
+ -`cli/i18n/db.texts.ts`—`sm db reset`/`sm db restore` templates including their`--dry-run`previews.
434
+ -`cli/i18n/cli-progress-emitter.texts.ts`— the
435
+ `extension.error: ...` stderr line.
436
+
437
+ String content moved verbatim — every existing test that
438
+ matches on stderr / stdout content keeps passing. Trivial
439
+ single-token strings (`'No issues.\n'`) and rare per-handler
440
+ bespoke phrases stay inline; the pattern is now established
441
+ for whoever wants to migrate them in a follow-up.
442
+
443
+ Note on `ui/` divergence: today the two workspaces use
444
+ different shapes for their text tables (functions in `ui/`,
445
+ templates in `cli/`). Aligning them is a follow-up — the day a
446
+ real i18n library lands, both converge on its native shape.
447
+ The CLI shape is closer to the eventual destination.
297
448
 
298
449
  - **N6 — `TIssueSeverity` aliased to `Severity`.** SQLite schema
299
450
  type now reads `type TIssueSeverity = Severity` instead of
@@ -941,9 +1092,9 @@ kind, normalizedTrigger)` and prints one row per group with the
941
1092
  (`Links out (12, 9 unique)`). When N > 1 detector emits the same
942
1093
  logical link, the row also gets a `(×N)` suffix.
943
1094
 
944
- `--json` output is byte-identical to before — raw rows, no merge.
945
- Storage is byte-identical to before. The grouping is purely a
946
- read-time presentation choice for human eyes.
1095
+ `--json` output is byte-identical to before — raw rows, no merge.
1096
+ Storage is byte-identical to before. The grouping is purely a
1097
+ read-time presentation choice for human eyes.
947
1098
 
948
1099
  **Spec changes (patch)**:
949
1100
 
@@ -64,7 +64,7 @@ A case is a JSON document with this shape:
64
64
 
65
65
  "setup": {
66
66
  "disableAllProviders": false,
67
- "disableAllDetectors": false,
67
+ "disableAllExtractors": false,
68
68
  "disableAllRules": false
69
69
  },
70
70
 
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "$schema": "https://skill-map.dev/spec/v0/conformance-case.schema.json",
3
3
  "id": "kernel-empty-boot",
4
- "description": "With every Provider, detector, and rule disabled, scanning an empty scope MUST return a valid, zero-filled ScanResult. Enforces the kernel boot invariant from architecture.md.",
4
+ "description": "With every Provider, extractor, and rule disabled, scanning an empty scope MUST return a valid, zero-filled ScanResult. Enforces the kernel boot invariant from architecture.md.",
5
5
  "setup": {
6
6
  "disableAllProviders": true,
7
- "disableAllDetectors": true,
7
+ "disableAllExtractors": true,
8
8
  "disableAllRules": true
9
9
  },
10
10
  "invoke": {
@@ -60,4 +60,4 @@ These have their own conformance cases even though they are not JSON Schemas.
60
60
 
61
61
  - **spec v0.x**: partial coverage acceptable. Every case added as the reference impl lands the verb that makes it runnable.
62
62
  - **spec v1.0.0 release**: all rows above MUST be 🟢 covered or explicitly 🟠 deferred to v1.1 with a linked issue.
63
- - **CI check**: [`scripts/check-coverage.mjs`](../../scripts/check-coverage.mjs) compares `spec/schemas/**/*.schema.json` against the matrix above on every PR. A schema without a row here, or a row pointing at a missing schema, fails CI (exit 1 with a `::error::` annotation). Wired into `ci.yml` §validate and into `npm run spec:check`.
63
+ - **CI check**: [`scripts/check-coverage.js`](../../scripts/check-coverage.js) compares `spec/schemas/**/*.schema.json` against the matrix above on every PR. A schema without a row here, or a row pointing at a missing schema, fails CI (exit 1 with a `::error::` annotation). Wired into `ci.yml` §validate and into `npm run spec:check`.
package/db-schema.md CHANGED
@@ -72,7 +72,7 @@ One row per detected node, matching [`schemas/node.schema.json`](./schemas/node.
72
72
  | Column | Type | Constraint | Notes |
73
73
  |---|---|---|---|
74
74
  | `path` | TEXT | PRIMARY KEY | Relative path from scope root. Canonical node identifier. |
75
- | `kind` | TEXT | NOT NULL, CHECK in (`skill`, `agent`, `command`, `hook`, `note`) | |
75
+ | `kind` | TEXT | NOT NULL | Open-by-design (`node.schema.json#/properties/kind`): the value is whatever the classifying Provider declares. Built-in Claude Provider catalog: `skill` / `agent` / `command` / `hook` / `note`. External Providers MAY emit their own. |
76
76
  | `provider` | TEXT | NOT NULL | Provider extension id. |
77
77
  | `title` | TEXT | NULL | |
78
78
  | `description` | TEXT | NULL | |
@@ -274,7 +274,7 @@ One row per `(node_id, summarizer_action_id)`. See [`schemas/summaries/`](./sche
274
274
  | Column | Type | Constraint |
275
275
  |---|---|---|
276
276
  | `node_id` | TEXT | NOT NULL |
277
- | `kind` | TEXT | NOT NULL, CHECK in kind enum |
277
+ | `kind` | TEXT | NOT NULL |
278
278
  | `summarizer_action_id` | TEXT | NOT NULL |
279
279
  | `summarizer_version` | TEXT | NOT NULL |
280
280
  | `body_hash_at_generation` | TEXT | NOT NULL |
package/index.json CHANGED
@@ -166,28 +166,28 @@
166
166
  }
167
167
  ]
168
168
  },
169
- "specPackageVersion": "0.8.0",
169
+ "specPackageVersion": "0.10.0",
170
170
  "integrity": {
171
171
  "algorithm": "sha256",
172
172
  "files": {
173
- "CHANGELOG.md": "04766216f59e532c4c4b144087be5b47da8097f4f45aba847a033d5cc01e01cf",
173
+ "CHANGELOG.md": "a7712801b4513c3212cbefb8b1e7540accd40e22c4aa09234b98f6b044bd42c9",
174
174
  "README.md": "bd30780525e75379eaeb5f8a903bdc601daf3862f3ec69dffc96c437e8d476fc",
175
175
  "architecture.md": "c69a50e3e9b7d091799bd19cd9efe854a924c83bc2c8e79e0fcb727196151f6c",
176
176
  "cli-contract.md": "1d09d047e07fd8793968259660012ebf64ab136967afead2d2666a59a40a020a",
177
- "conformance/README.md": "4a77b4251ebe708692f5c14d2420f740dd4f1743b6f080f0b6e0c1faac179a50",
178
- "conformance/cases/kernel-empty-boot.json": "e56ed102f4ff745acb4ee4c3ebdea20a84b4bf17c8bec85eb1ef4bd471f2afbe",
179
- "conformance/coverage.md": "317636edefeed5d6488bcc8f228a4c8bf72f02087a9afcfc142cd102c761c31b",
177
+ "conformance/README.md": "07970f06c467e34413f07f6d8bec09ece892d1a903fa039d25b34a77e91187d2",
178
+ "conformance/cases/kernel-empty-boot.json": "ad4bbe9d637537625025c8bdb61285b1433568a2544b1ce0248f304ccff8c350",
179
+ "conformance/coverage.md": "eb57cd979bca59a252e3cd49796e068ac601169f859f32cdf37634486574c44c",
180
180
  "conformance/fixtures/preamble-v1.txt": "1e0aeef224b64477bdc13a949c3ad402e68249caf499ecdba1302371677c068b",
181
- "db-schema.md": "f570b7ec123a1bbe8936e111a3e3b0597855b2aff0cac82b89fa88d4233a4d73",
182
- "interfaces/security-scanner.md": "e46d33d6e39b15672c8f7350f1cbd4755534510fe57c679c2b1d0be57577d818",
181
+ "db-schema.md": "6542311118d2e74bdb7c7a48c208eccd5e55cf574f7cb38e976d6d7c3c666745",
182
+ "interfaces/security-scanner.md": "4a982667008f233656f44c61ce9948e062432d3debdcbf7a134da03bd4139d7d",
183
183
  "job-events.md": "831501bd696a2801e2d160b314eb49794d0ba553da4487e15c7dcc72a1c230f6",
184
184
  "job-lifecycle.md": "1fe88b1a2ed204e41bb41ac172fbb3e912dccd0dd8a1f8ea8e21a681b336d6ee",
185
- "plugin-author-guide.md": "c2633043c423935ec45ef2e1f11dd153762b6b01b45820d9963ff68bd135b67a",
185
+ "plugin-author-guide.md": "2dcdf8c570342d94c2c8f8d47594715254e3956d7939443023f1d6420e2b30d0",
186
186
  "plugin-kv-api.md": "04b2178f46fb88adeae9240df9c9e1761b660396072001dac32cd402e11a2d7d",
187
187
  "prompt-preamble.md": "23a8eff0477fbbc46192a27781bc781bda4202bb9c669b7a7a002b0d668146b0",
188
- "schemas/conformance-case.schema.json": "ca35a1303c48043f60bbe5fc6c9d9bb88dac0f7fd381f42f596fc6dbb1d4a532",
188
+ "schemas/conformance-case.schema.json": "7cd0f3aae5124f24be57cddb213d002d0466f79d06fd3da896075c8b28650410",
189
189
  "schemas/execution-record.schema.json": "607e939bfcac4e18385ef93e27bbe28987ba35d5a7c67f3d6e4377ca819a9425",
190
- "schemas/extensions/action.schema.json": "ec4c2fd74b73a140744c195da91d287a88bfecac206d029910ae0dcb0ab9ce35",
190
+ "schemas/extensions/action.schema.json": "262272175c06a2e33c08f819a45c3ef8260276c91a9d0542fdffc932aeb32db7",
191
191
  "schemas/extensions/base.schema.json": "e5c3406b88b0496a89791890b05083929429319d96b1f8cea0bec3ec9e3de8af",
192
192
  "schemas/extensions/extractor.schema.json": "122d3f81ef91edcde9798e7dc8fcbf442a2996deea65aa4b03c9d5cb01ba2519",
193
193
  "schemas/extensions/formatter.schema.json": "2ab092aa37ae349c69b93071ed4f0e131affb7bb5799516ca82c721262631b36",
@@ -199,7 +199,7 @@
199
199
  "schemas/issue.schema.json": "40f6f8abadcce0fd8eac9df27ffcc20b2fc9fda6970142ddb8e7e56b1760b9b1",
200
200
  "schemas/job.schema.json": "582999899f8846f70c4d745d2813e53b97a4f5ccd3d8d163eeb68b201e7124e4",
201
201
  "schemas/link.schema.json": "0a95a24849a38b9ef5bad5361519a9f9e012b5bc3001289fad29d0851fceff6b",
202
- "schemas/node.schema.json": "ef80251524ed8a295eb7e98e4399b36cdd1daf3eacd605cacd1ba25edd6f39fc",
202
+ "schemas/node.schema.json": "f9a0cc5d5a7f581915719e91ae401e46a82688e0865fd3a2eb64bd42798a6b2b",
203
203
  "schemas/plugins-registry.schema.json": "5ca1d4970ae64f064f05c237a649d9f82d5edbeb7c121ec50cb4aaca13f4bc51",
204
204
  "schemas/project-config.schema.json": "4f5b1ce01446d78ebf524d11c36e0a3ae101e69008c922a2b3f53fccfd1cb87f",
205
205
  "schemas/report-base.schema.json": "a1021e9a59b4df9f99cd92454d797e88469766e7d49f52d231c4645ffdfdad8f",
@@ -28,7 +28,7 @@ Example manifest:
28
28
  "id": "security-snyk",
29
29
  "version": "1.0.0",
30
30
  "specCompat": "^1.0.0",
31
- "extensions": ["extensions/snyk.action.mjs"],
31
+ "extensions": ["extensions/snyk.action.js"],
32
32
  "tags": ["kind:scanner", "vendor:snyk"]
33
33
  }
34
34
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skill-map/spec",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "JSON Schemas, prose contracts, and conformance suite for the skill-map specification.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,7 +14,7 @@ This guide is **descriptive prose**, not the normative contract. The normative p
14
14
  my-plugin/
15
15
  ├── plugin.json ← manifest (required)
16
16
  └── extensions/
17
- └── extractor.mjs ← one file per declared extension
17
+ └── extractor.js ← one file per declared extension
18
18
  ```
19
19
 
20
20
  ```jsonc
@@ -23,12 +23,12 @@ my-plugin/
23
23
  "id": "my-plugin",
24
24
  "version": "1.0.0",
25
25
  "specCompat": "^1.0.0",
26
- "extensions": ["./extensions/extractor.mjs"]
26
+ "extensions": ["./extensions/extractor.js"]
27
27
  }
28
28
  ```
29
29
 
30
30
  ```javascript
31
- // my-plugin/extensions/extractor.mjs
31
+ // my-plugin/extensions/extractor.js
32
32
  export default {
33
33
  id: 'my-extractor',
34
34
  kind: 'extractor',
@@ -150,8 +150,8 @@ In your own plugin's `plugin.json`, set `granularity` only when you opt into the
150
150
  "specCompat": "^1.0.0",
151
151
  "granularity": "extension",
152
152
  "extensions": [
153
- "./extensions/orphan-skill-rule.mjs",
154
- "./extensions/csv-formatter.mjs"
153
+ "./extensions/orphan-skill-rule.js",
154
+ "./extensions/csv-formatter.js"
155
155
  ]
156
156
  }
157
157
  ```
@@ -625,7 +625,7 @@ import { test } from 'node:test';
625
625
  import { strictEqual } from 'node:assert';
626
626
  import { runExtractorOnFixture, node } from '@skill-map/testkit';
627
627
 
628
- import extractor from '../extensions/extractor.mjs';
628
+ import extractor from '../extensions/extractor.js';
629
629
 
630
630
  test('emits one reference per [[ref:<name>]] token', async () => {
631
631
  const { links } = await runExtractorOnFixture(extractor, {
@@ -31,9 +31,18 @@
31
31
  "description": "Pre-invocation toggles and ordered staging steps. All toggles default to `false`. `priorScans`, when present, run BEFORE the main `invoke` so a case can establish a prior snapshot the heuristic-driven verbs (e.g. `sm scan` rename detection) can react to.",
32
32
  "additionalProperties": false,
33
33
  "properties": {
34
- "disableAllProviders": { "type": "boolean" },
35
- "disableAllDetectors": { "type": "boolean" },
36
- "disableAllRules": { "type": "boolean" },
34
+ "disableAllProviders": {
35
+ "type": "boolean",
36
+ "description": "When true, the runner injects `SKILL_MAP_DISABLE_ALL_PROVIDERS=1` into the child process environment, dropping every Provider extension (built-in and user-plugin) before scan composition."
37
+ },
38
+ "disableAllExtractors": {
39
+ "type": "boolean",
40
+ "description": "When true, the runner injects `SKILL_MAP_DISABLE_ALL_EXTRACTORS=1` into the child process environment, dropping every Extractor extension before scan composition."
41
+ },
42
+ "disableAllRules": {
43
+ "type": "boolean",
44
+ "description": "When true, the runner injects `SKILL_MAP_DISABLE_ALL_RULES=1` into the child process environment, dropping every Rule extension before scan composition."
45
+ },
37
46
  "priorScans": {
38
47
  "type": "array",
39
48
  "description": "Ordered staging scans, each running before the next. For step N, the runner first replaces the scope's Provider content with `priorScans[N].fixture`, then invokes `sm scan` with the supplied flags. After the last step, the runner copies the top-level `fixture` (overwriting again) and runs the main `invoke`. The DB persists across all steps because `.skill-map/` is preserved between fixture swaps.",
@@ -33,8 +33,8 @@
33
33
  "properties": {
34
34
  "kind": {
35
35
  "type": "array",
36
- "items": { "type": "string", "enum": ["skill", "agent", "command", "hook", "note"] },
37
- "description": "Node kinds this action accepts. Omitted → any kind."
36
+ "items": { "type": "string", "minLength": 1 },
37
+ "description": "Node kinds this action accepts. Open-by-design (matches `node.schema.json#/properties/kind`): an action declared with `kind: ['cursorRule']` is valid as long as some Provider classifies into `cursorRule`. Omitted → any kind."
38
38
  },
39
39
  "provider": {
40
40
  "type": "array",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://skill-map.dev/spec/v0/node.schema.json",
4
4
  "title": "Node",
5
- "description": "A single markdown file in the graph (skill, agent, command, hook, note). Identified by its relative path from the scope root.",
5
+ "description": "A single markdown file in the graph. Identified by its relative path from the scope root. The `kind` is whatever the classifying Provider declares; built-in kinds today are `skill`, `agent`, `command`, `hook`, `note`, but external Providers (Cursor, Obsidian, …) MAY emit their own.",
6
6
  "type": "object",
7
7
  "required": ["path", "kind", "provider", "bodyHash", "frontmatterHash", "bytes", "linksOutCount", "linksInCount", "externalRefsCount"],
8
8
  "additionalProperties": false,
@@ -13,8 +13,8 @@
13
13
  },
14
14
  "kind": {
15
15
  "type": "string",
16
- "enum": ["skill", "agent", "command", "hook", "note"],
17
- "description": "Category assigned by the Provider. Stability: stable. New kinds may be added in a minor bump."
16
+ "minLength": 1,
17
+ "description": "Category assigned by the Provider. Open-by-design — any non-empty string an enabled Provider declares is valid (built-in Claude Provider catalog: `skill` / `agent` / `command` / `hook` / `note`; external Providers MAY declare their own). Per-kind frontmatter schemas live with the Provider that emits the kind. Stability: stable."
18
18
  },
19
19
  "provider": {
20
20
  "type": "string",