@skill-map/spec 0.12.0 → 0.13.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.
package/CHANGELOG.md CHANGED
@@ -1,1984 +1,109 @@
1
1
  # Spec changelog
2
2
 
3
- ## 0.12.0
4
-
5
- ### Minor Changes
6
-
7
- - 68c5e28: Step 14.1 — `sm serve` + Hono BFF skeleton
8
-
9
- Adds `src/server/` Hono workspace with single-port wiring (`/api/health` real,
10
- `/api/*` 404 stubs, `/ws` no-op upgrade, `serveStatic` + SPA fallback). Real
11
- `ServeCommand` extracted from stub at `cli/commands/stubs.ts` to dedicated
12
- `cli/commands/serve.ts` extending `SmCommand`. Loopback-only through v0.6.0
13
- (Decision #119). Boot resilient to missing DB — `/api/health` reports
14
- `db: 'missing'`. Spec `cli-contract.md` `sm serve` row updated to full flag
15
- set; new `### Server` subsection (skeleton — endpoints fill at 14.2).
16
-
17
- **Files added (server)**
18
-
19
- - `src/server/index.ts` — `createServer(opts)` factory returning `ServerHandle` (`{ address, close }`); resolves spec version, builds the Hono app, instantiates a `WebSocketServer({ noServer: true })`, hands both to `@hono/node-server`'s `serve({ websocket: { server: wss } })`. Closing the http server tears down the WSS automatically (node-server registers the `'close'` hook internally); `close()` calls `wss.close()` defensively for forward-compatibility.
20
- - `src/server/app.ts` — Hono app construction. Routes registered in single-port order: `GET /api/health` → real, `ALL /api/*` → structured 404, `GET /ws` via the injected `attachWs` registrar, static handler + SPA fallback. Global `app.onError` formats every uncaught throw into the error envelope.
21
- - `src/server/options.ts` — `IServerOptions` + `validateServerOptions(input)`. Loopback-only check for `--dev-cors`; port range check `[0, 65535]`; scope validation.
22
- - `src/server/paths.ts` — `resolveDefaultUiDist(ctx)` walks upwards from cwd looking for `ui/dist/browser/index.html`; `resolveExplicitUiDist(ctx, raw)` honours absolute paths for `--ui-dist`.
23
- - `src/server/static.ts` — wraps `@hono/node-server`'s `serveStatic` middleware with the SPA-fallback layer (`serveStatic` does not do SPA fallback — it `next()`s on miss, which is exactly the seam we hook into). Absolute `root` paths work on POSIX in node-server@2.0.1 (verified runtime probe — implementation is `path.join(root, filename)`); the `.d.ts` "Absolute paths are not supported" string is stale (upstream issue honojs/node-server#187 still open). When the bundle is missing (`uiDist === null`), a tiny placeholder middleware serves the boot-without-bundle hint at `/`.
24
- - `src/server/ws.ts` — `noopWebSocketRoute(app)` registers `GET /ws` via the official `upgradeWebSocket` re-exported from `@hono/node-server@2.x`. The 14.1 handler closes the connection in `onOpen` with code 1000 + reason `'no broadcaster yet'`. 14.4 swaps this registrar for the chokidar-fed broadcaster — one-line change in `index.ts`, `app.ts` untouched.
25
- - `src/server/health.ts` — `buildHealth(deps)` synchronous; `resolveSpecVersion()` async, called once at boot.
26
- - `src/server/i18n/server.texts.ts` — `SERVER_TEXTS` catalog.
27
-
28
- **Files added (CLI)**
29
-
30
- - `src/cli/commands/serve.ts` — `ServeCommand extends SmCommand`. Parses flags, validates, calls `createServer`, registers SIGINT/SIGTERM handlers, awaits shutdown. `protected emitElapsed = false` (long-running daemon).
31
- - `src/cli/i18n/serve.texts.ts` — `SERVE_TEXTS` catalog.
32
-
33
- **Tests added (15)**
34
-
35
- - `src/test/server-boot.test.ts` (7) — boot/listen/health JSON, custom port, db state present/missing, structured 404, /ws upgrade closes with code 1000 + reason 'no broadcaster yet' (uses real `WebSocket` client from `ws`), shutdown < 1s + idempotent close, inline placeholder when uiDist null.
36
- - `src/test/server-flags.test.ts` (6) — host non-loopback + dev-cors rejection, port out-of-range, port non-numeric, scope invalid, ui-dist missing, ui-dist with valid bundle.
37
- - `src/test/server-db-missing.test.ts` (2) — `--db <missing>` exits 5, default boots cleanly with db:missing.
38
-
39
- **Files edited**
40
-
41
- - `src/cli/commands/stubs.ts` — `ServeCommand` removed; replaced with a comment pointer.
42
- - `src/cli/entry.ts` — registers the new `ServeCommand`.
43
- - `src/package.json` — adds `hono@4.12.16`, `@hono/node-server@2.0.1`, `ws@8.20.0` (deps); `@types/ws@8.18.1` (dev). All exact-pinned per AGENTS.md.
44
- - `spec/cli-contract.md` — `sm serve` row replaced with the full 14.1 flag set; new `#### Server` subsection (stability: experimental).
45
- - `spec/CHANGELOG.md` — `[Unreleased]` `### Minor` entry for the spec change.
46
- - `spec/index.json` — regenerated (40 files hashed; previous head was 215 lines).
47
-
48
- **Decisions during implementation (flag for orchestrator)**
49
-
50
- - WebSocket support uses `@hono/node-server@2.x`'s built-in `upgradeWebSocket` plus the canonical `ws@8.20.0` Node WebSocket library, per the official README pattern. The previously-published `@hono/node-ws` adapter was deprecated when node-server@2.0 absorbed WebSocket support natively (PR honojs/node-server#328). The 14.4 broadcaster will replace `noopWebSocketRoute` with its own one-line registrar — no API churn between 14.1 and 14.4.
51
- - The `/api/*` catch-all is wired with `app.all('/api/*', ...)` BEFORE the `/ws` registrar and the static handler so neither a `serveStatic` filesystem hit nor the SPA fallback can shadow API endpoints. `/ws` is registered BEFORE the static handler so a literal `/ws` path on disk inside `uiDist` cannot accidentally shadow the upgrade route.
52
- - `serveStatic` from `@hono/node-server/serve-static` accepts absolute root paths at runtime on POSIX (its implementation is `path.join(root, filename)`); the `.d.ts` string saying otherwise is documentation drift, not a runtime contract. Verified with a runtime probe and cross-referenced against the open upstream issue (honojs/node-server#187). Documented in `src/server/static.ts` so future contributors don't re-investigate.
53
-
54
- ## 0.11.0
55
-
56
- ### Minor Changes
57
-
58
- - f8fca25: Step 10 prep — job artifacts move into the database (B2: content-addressed storage)
59
-
60
- Removes the on-disk `.skill-map/jobs/<id>.md` and `.skill-map/reports/<id>.json` artifacts from the spec. Rendered job content and report payloads now live in the kernel database; the filesystem is no longer a normative layer of the job lifecycle. Pre-1.0 minor breaking per `versioning.md` § Pre-1.0.
61
-
62
- **Why**: every other piece of operational state (`state_summaries`, `state_enrichments`, `state_plugin_kvs`, `node_enrichments`) already lives in the DB. Jobs and reports were the only outliers — and being outliers cost real complexity (orphan-file detection, partial backups, two-source-of-truth GC). With B2 (content-addressed dedup keyed on the existing `content_hash`), retries / `--force` / cross-node fan-out reuse a single content blob, so DB-only does not blow up storage on heavy users.
63
-
64
- **Schema changes**
65
-
66
- - New table `state_job_contents` (`content_hash` PK, `content` TEXT, `created_at`). Content-addressed: multiple `state_jobs` rows MAY reference the same row.
67
- - `state_jobs.file_path` removed. The rendered content is fetched via `state_job_contents.content_hash` join.
68
- - `state_executions.report_path` → `state_executions.report_json` (TEXT, parsed-JSON-on-read per the `_json` naming convention).
69
-
70
- **Schema-typed contract changes**
71
-
72
- - `Job.filePath` removed.
73
- - `ExecutionRecord.reportPath` → `ExecutionRecord.report` (object/null — the parsed JSON payload).
74
- - `Job.failureReason` and `ExecutionRecord.failureReason` enums: `job-file-missing` → `content-missing` (defensive failure-mode label for DB corruption where a job row outlives its content row; the runtime invariant should keep this state unreachable).
75
- - `history-stats.schema.json` `perFailureReason` mirrors the rename.
76
-
77
- **CLI surface changes**
78
-
79
- - `sm job preview <id>` now prints the rendered content from `state_job_contents` (no file). Same output, different source.
80
- - `sm job claim --json` is the contracted Skill-agent handover: returns `{id, nonce, content}` so the agent can call `sm record` afterwards with the nonce in hand. The plain-stdout form (id only) is preserved for legacy scripts.
81
- - `sm record --report <path-or-dash>` accepts a file path OR `-` (stdin); the kernel reads the payload and stores it inline in `report_json`. The on-disk report file becomes operationally ephemeral — implementations SHOULD remove it after the kernel acknowledges the callback (courtesy GC, not normative).
82
- - `sm job prune --orphan-files` removed. Replaced by automatic `state_job_contents` GC inside `sm job prune`: deletes terminal jobs past retention, then collects orphan content rows in the same transaction.
83
- - `sm doctor` checks change accordingly: drops the "orphan job files / orphan DB rows pointing at missing files" pair; adds two DB-internal checks (`state_jobs` rows whose `content_hash` is missing from `state_job_contents`; `state_job_contents` rows referenced by zero `state_jobs` rows).
84
-
85
- **Event stream changes**
86
-
87
- - `job.spawning.data.jobFilePath` → `job.spawning.data.contentHash` (references the content row instead of a file path).
88
- - `job.callback.received.data.reportPath` and `job.completed.data.reportPath` → `executionId` (references the `state_executions` row that holds the inline report payload). Reports are intentionally NOT inlined in events — consumers query the row when they need the body.
89
-
90
- **Architecture changes**
91
-
92
- - `RunnerPort.run(jobFilePath, options)` → `run(jobContent, options)` returning `{report, ...}` instead of `{reportPath, ...}`. Path-based reporting is no longer part of the port contract. Runners that need an actual file (the canonical case being `claude -p` reading stdin from a path) materialize a temp file inside `run()` and remove it after spawn — temp files are operational, not normative.
93
-
94
- **Atomicity edge cases consolidated**
95
-
96
- `spec/job-lifecycle.md` §Atomicity edge cases drops the four file-related rows. Two new DB-internal cases take their place: `state_jobs` row outliving its `state_job_contents` row (failure: `content-missing`); `state_job_contents` row with no live job references (GC straggler — `sm job prune` collects).
97
-
98
- **Files touched**
99
-
100
- - `spec/db-schema.md` — new `state_job_contents` section, `state_jobs.file_path` removed, `state_executions.report_path` → `report_json`, integrity section rewritten.
101
- - `spec/job-lifecycle.md` — §Submit step 8 rewritten (DB store), §Atomic claim documents `--json` shape, §Atomicity edge cases consolidated, §Record callback rewritten for `--report` path-or-stdin semantics, §Retention extended to cover `state_job_contents` GC, failure-reason rename.
102
- - `spec/cli-contract.md` — `sm job preview` / `sm job claim` / `sm job prune` rows updated, `sm job prune --orphan-files` row removed, `sm record` block rewritten with `<path-or-dash>`, `sm doctor` integrity bullets updated.
103
- - `spec/prompt-preamble.md` — §How the kernel applies step 5 rewritten (DB store, no file).
104
- - `spec/architecture.md` — §`RunnerPort` operations + reference impls updated for content-string + parsed-report shape.
105
- - `spec/job-events.md` — `job.spawning` / `job.callback.received` / `job.completed` payloads changed.
106
- - `spec/conformance/README.md` + `coverage.md` — `preamble-bitwise-match` references updated to `sm job preview` stdout.
107
- - `spec/schemas/job.schema.json` — `filePath` property removed, failure-reason enum rename.
108
- - `spec/schemas/execution-record.schema.json` — `reportPath` → `report` (object/null), failure-reason enum rename.
109
- - `spec/schemas/history-stats.schema.json` — `perFailureReason` enum rename.
110
- - `spec/index.json` regenerated (40 files hashed); `npm run spec:check` green.
111
-
112
- **Migration for consumers**
113
-
114
- - Any consumer reading `state_jobs.file_path` or `state_executions.report_path` reads from the renamed columns / DB-only paths instead.
115
- - Any tooling that watched `.skill-map/jobs/*.md` or `.skill-map/reports/*.json` needs to query the DB or call the relevant `sm` verb.
116
- - `--orphan-files` flag callers must drop the flag; `sm job prune` already does the equivalent automatically.
117
- - Skill agents drain via `sm job claim --json` (id + nonce + content together) instead of `sm job claim` + reading a file.
118
-
119
- **Out of scope**
120
-
121
- The reference impl side of this (migration that adds `state_job_contents` + drops `state_jobs.file_path`; storage-adapter helpers; runtime piping in `ClaudeCliRunner` for the temp-file dance) lands in follow-up changesets under `@skill-map/cli`. The spec change above is self-contained: shipping it alone changes nothing at runtime, but unblocks the implementation phases.
122
-
123
- ## 0.10.0
124
-
125
- ### Minor Changes
126
-
127
- - f8a7125: Open `Node.kind` to any Provider-declared string (Phase A — spec only).
128
-
129
- 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`):
130
-
131
- - `node.schema.json#/properties/kind` carried `enum: [<5 values>]` — AJV-rejected anything else.
132
- - `db-schema.md` § `scan_nodes` and § `state_summaries` mandated `CHECK in (<5 values>)` SQL constraints.
133
- - `extensions/action.schema.json#/.../filter/kind` had the same closed list for the per-action applicability filter.
134
-
135
- This phase opens the spec end:
136
-
137
- - `node.schema.json#/properties/kind` → `{ "type": "string", "minLength": 1 }` with a description naming the built-in Claude catalog so consumers know the default contract.
138
- - `db-schema.md` drops both `CHECK in (...)` constraint rows. Both columns stay `TEXT NOT NULL`.
139
- - `extensions/action.schema.json#/.../filter/kind` widens to `{ items: { "type": "string", "minLength": 1 } }`.
140
-
141
- 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.
142
-
143
- Migration for consumers:
144
-
145
- - Anyone validating an exported `Node` JSON against `node.schema.json` now accepts external-Provider kinds.
146
- - 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.
147
-
148
- 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).
149
-
150
- ## 0.9.0
151
-
152
- ### Minor Changes
3
+ ## 0.13.1
153
4
 
154
- - 88afe24: Cleanup pass post-v0.8.0 — finishing the renames and wiring the
155
- conformance kill-switches.
156
-
157
- **Pre-1.0 minor bump** per `spec/versioning.md` § Pre-1.0. The schema
158
- field rename below is technically breaking, but ships as a minor while
159
- the spec stays `0.Y.Z`.
160
-
161
- ## Spec changes (`@skill-map/spec`)
162
-
163
- ### Breaking — `conformance-case.schema.json`
5
+ ### Patch Changes
164
6
 
165
- - **Rename `setup.disableAllDetectors` `setup.disableAllExtractors`.**
166
- Finishes the kind rename Detector → Extractor introduced in 0.8.0
167
- (Phase 2 of the plug-in model overhaul). The previous name was the
168
- last residue and it never reached a release where anything consumed
169
- it.
170
- - **`setup.disableAll{Providers,Extractors,Rules}` are now consumed
171
- end-to-end.** Until this release the three toggles were declared in
172
- the schema and accepted by the runner, but the runner never threaded
173
- them anywhere — the `kernel-empty-boot` case happened to pass
174
- because its fixture is empty. The runner now injects
175
- `SKILL_MAP_DISABLE_ALL_{PROVIDERS,EXTRACTORS,RULES}=1` into the
176
- child process environment when the matching toggle is `true`, and
177
- the CLI's scan composer drops every extension of the disabled kind
178
- from the in-scan pipeline regardless of granularity gates and
179
- `--no-built-ins`. Each toggle now has a docstring on the schema
180
- property pointing at the env-var convention.
181
- - `kernel-empty-boot` case updated for the rename.
182
- - `conformance/README.md` example updated.
7
+ - 103fc1a: Doc revision pass — greenfield framing across READMEs, spec prose, ROADMAP, AGENTS, web, and workspace landing pages.
183
8
 
184
- ### Non-breaking copy fixes
9
+ Pure documentation changes; no normative schema or code changes.
185
10
 
186
- - Comments and docstrings across `architecture.md` and friends already
187
- refer to "Extractor" everywhere; only the schema field stayed on the
188
- old name. No prose changes in this bump.
11
+ `@skill-map/spec`:
189
12
 
190
- ## CLI changes (`@skill-map/cli`)
13
+ - `architecture.md` terse rewrite of §Provider · `kinds` catalog (now lists three required fields: `schema`, `defaultRefreshAction`, `ui`); new §Provider · `ui` presentation section documenting the label / color / colorDark / emoji / icon contract; §Stability section updated for the six extension kinds + Hook trigger set.
14
+ - `plugin-author-guide.md` — Provider section gains the `ui` block documentation alongside `schema` and `defaultRefreshAction`; example manifest carries both icon variants (`pi` + `svg`); migration notes stripped under greenfield framing.
15
+ - `cli-contract.md` — §Server documents the `kindRegistry` envelope field on every payload-bearing variant (sentinel envelopes — health/scan/graph — exempt).
16
+ - `conformance/coverage.md` — row 18 (`extensions/provider.schema.json`) flipped 🔴 → 🟡, points at the new `plugin-missing-ui-rejected` case; new §Stability section.
17
+ - `conformance/README.md` — drop "(Phase 5 / A.13 of spec 0.8.0)" historical phase markers.
18
+ - `db-schema.md`, `plugin-author-guide.md` — fix `pisar` typo (Spanish leaked into English) → "are simply overwritten".
19
+ - `CHANGELOG.md` — aggressive sweep: 2114 → 77 lines (96% reduction). Every release gets a 1–3 line greenfield summary. Drops the `Files touched`, `Migration for consumers`, `Out of scope`, `Why`, and per-step decision sub-sections. Drops commit-hash prefixes and `Pre-1.0 minor per versioning.md` boilerplate from every entry. The `[Unreleased]` section preserves the three in-flight Step 14 entries.
20
+ - `conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/{plugin.json,provider.js}` — recovered (lost in the merge from `main` due to `.gitignore` masking gitignored-but-tracked files; `git add -f` brings them back into the index).
191
21
 
192
- ### Breaking — `IDiscoveredPlugin.status` enum
22
+ `@skill-map/cli`:
193
23
 
194
- - **Rename `'loaded'` `'enabled'`.** The schema enum
195
- (`plugins-registry.schema.json`) already used `enabled` since 0.8.0;
196
- the runtime drifted to `loaded` and has now been pulled back so the
197
- runtime status matches the spec contract. `'disabled'`, the
198
- semantic pair, was already aligned. Every consumer (`sm plugins
199
- list`, `sm plugins doctor`, `sm db prune` plugin filter, runtime
200
- plugin composer) updated. No published consumers exist.
24
+ - `src/README.md` Status section greenfield (terse: pre-1.0, what's next, what's after); usage examples expanded with `sm serve` + monorepo dev scripts.
25
+ - `src/built-in-plugins/README.md` drop the contradictory "empty on purpose" framing; document the actual built-in inventory (Claude Provider + Extractors + Rules + Formatter + `validate-all`).
201
26
 
202
- ### Non-breaking — sweep cleanup
27
+ `@skill-map/testkit`:
203
28
 
204
- - Old `Detector` / `detector` references (kind name, manifest field
205
- names, JSDoc, comments, test fixture filenames, test variable
206
- names) replaced with `Extractor` / `extractor` across the
207
- production code and test suite. Excludes historical CHANGELOG
208
- entries, explicit migration notes ("Renamed from Detector"), and
209
- test data strings whose semantics are independent of the kind
210
- name (e.g. `'@FooDetector'` in trigger normalization tests).
211
- - A residual reference to "an audit reading `ScanResult.issues`" in
212
- `validate-all`'s docstring rewritten without the removed kind name.
29
+ - `testkit/README.md` — rewrite end-to-end against the actual exported helper names (`runExtractorOnFixture` instead of the long-renamed `runDetectorOnFixture`); align example with the `extract(ctx) void` Extractor shape and the `enabled` plugin status enum.
213
30
 
214
- ## Tests
31
+ Plus `ui/` README rewrite, root README + ES mirror Status / badge bumps + `sm serve` mention + Star History embed, AGENTS.md greenfield BFF section, CONTRIBUTING.md refresh, ROADMAP.md greenfield sweep (`Earlier prose` blocks stripped, decision log reframed without rename history, 14.6+ content preserved), web copy revision (How-it-works section), examples/hello-world rewritten to the Extractor model with passing tests, and the spec/index.json regeneration that goes with it.
215
32
 
216
- - `plugin-runtime-branches.test.ts` five new unit tests covering
217
- the env-var kill-switch in `composeScanExtensions` (per kind, all
218
- three together, and stray-value resilience).
219
- - `conformance-disable-flags.test.ts` — four new e2e tests pointing
220
- the runner at a populated fixture with each toggle in turn (and a
221
- baseline) so a regression in the env-var pipeline shows up
222
- structurally rather than relying on the empty-fixture coincidence.
33
+ Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
223
34
 
224
35
  ## [Unreleased]
225
36
 
226
37
  ### Minor
227
38
 
228
- - **`sm serve` row + `### Server` subsection** in `cli-contract.md`
229
- Step 14.1 promotes `sm serve` from an implementation-defined stub to a
230
- documented surface. The verb row at `§Verb catalog` › `### Server`
231
- expands the flag set to the full 14.1 contract: `--port` (default
232
- `4242`), `--host` (default `127.0.0.1`, loopback-only through v0.6.0),
233
- `--scope project|global`, `--db <path>`, `--no-built-ins`,
234
- `--no-plugins`, `--open` / `--no-open`, `--dev-cors`, `--ui-dist
235
- <path>` (hidden). New `#### Server` subsection documents the
236
- single-port mandate, the boot-with-missing-DB resilience contract
237
- (`/api/health` returns `db: 'missing'`), the v14.1 endpoint surface
238
- (`GET /api/health` real, `ALL /api/*` 404 stubs, `GET /ws` upgrade-only,
239
- static + SPA fallback), the structured error envelope shape, and the
240
- flag table. Marked `*(Stability: experimental — locks at v0.6.0.)*` —
241
- endpoints fill at v14.2, broadcaster at v14.4. Additive minor per
242
- `versioning.md` § Pre-1.0 (no breaking change to the existing row's
243
- semantics; the old wording was strictly less specific).
244
-
245
- ### Minor (breaking, pre-1.0)
246
-
247
- - **`Node.kind` opens to any non-empty string (was the closed enum
248
- `skill` / `agent` / `command` / `hook` / `note`).** The kernel always
249
- permitted external Providers — `IProvider.kinds` is documented as
250
- "open by design" so a future Cursor / Obsidian / Roo Provider can
251
- declare its own kinds — but the `node.schema.json` enum + the
252
- `scan_nodes.kind` SQL CHECK + the closed TS `NodeKind` union closed
253
- three layers underneath. Effects:
254
- - `node.schema.json#/properties/kind` switches from `enum: [...5
255
- values]` to `{ "type": "string", "minLength": 1 }`. The
256
- description still names the built-in Claude Provider catalog so
257
- consumers know what to expect from the default install.
258
- - `db-schema.md` drops the `CHECK in (...)` constraint on
259
- `scan_nodes.kind` and `state_summaries.kind`. Both columns stay
260
- `TEXT NOT NULL`.
261
- - `extensions/action.schema.json#/.../filter/kind` (the per-kind
262
- filter for action applicability) widens the same way: `items:
263
- { type: 'string', minLength: 1 }` instead of the closed enum.
264
- Migration: consumers who validate exported `Node` JSON against
265
- `node.schema.json` will now accept external-Provider kinds. Any
266
- consumer that hard-coded the closed enum elsewhere (UI filter chip
267
- set, scripted assertions) needs to widen to "string". The TS +
268
- SQL counterpart lands in `@skill-map/cli` (kernel TS contract +
269
- migration `002_open_node_kinds`).
270
- - **`conformance-case.schema.json` — rename `setup.disableAllDetectors`
271
- → `setup.disableAllExtractors`.** Finishes the kind rename Detector →
272
- Extractor introduced in 0.8.0 (Phase 2 of the plug-in model
273
- overhaul). The previous name was a residue from an unfinished sweep
274
- and never reached a release that consumed it.
275
- - **`setup.disableAll{Providers,Extractors,Rules}` are now wired
276
- end-to-end.** Until this release the toggles were declared in the
277
- schema but the runner threaded them nowhere; the `kernel-empty-boot`
278
- case happened to pass because its fixture is empty. The runner now
279
- injects `SKILL_MAP_DISABLE_ALL_{PROVIDERS,EXTRACTORS,RULES}=1` into
280
- the child process environment per toggle, and the CLI's scan
281
- composer drops every extension of the disabled kind from the
282
- in-scan pipeline (overriding granularity gates and `--no-built-ins`).
283
- Migration: any case JSON authored against the unwired schema needs
284
- to swap `disableAllDetectors` for `disableAllExtractors`; behaviour
285
- changes only when the toggles were already `true` (those cases will
286
- now actually disable the kind, where previously they relied on
287
- fixture content for the same outcome).
288
-
289
- ### Patch
290
-
291
- - Updated `conformance/cases/kernel-empty-boot.json` for the field
292
- rename above.
293
- - Updated `conformance/README.md` example for the field rename above.
294
- - Schema docstrings added to each `disableAll*` property documenting
295
- the env-var convention the runner uses.
296
-
297
- ## 0.8.0
298
-
299
- ### Minor Changes
300
-
301
- - 6dad772: v0.8.0 — Pre-1.0 stabilization pass.
302
-
303
- This release combines two coherent pre-1.0 cleanup pieces that
304
- both push the project closer to v1.0 stability: the cli-architect
305
- audit review pass and the plugin model overhaul.
306
-
307
- Pre-1.0 minor bumps per `versioning.md` § Pre-1.0; breaking
308
- changes allowed within minor while in `0.Y.Z`. No real downstream
309
- ecosystem exists yet, so the breaking surface costs nothing
310
- today.
311
-
312
- ## Part 1 — Pre-1.0 audit review pass
313
-
314
- Pre-1.0 review pass — `cli-architect` audit findings.
315
-
316
- Internal audit run by the `cli-architect` agent in REVIEW mode
317
- produced a Critical / High / Medium / Low / Nit catalog. This
318
- pass bundles the implementation of every actionable finding into
319
- one unit so the review can be read end-to-end. **Pre-1.0 minor
320
- bump**: a few breaking surface changes ride along (CLI sub-verb
321
- split, exit-code enum exposed, plugin loader option). No
322
- published downstream consumers exist yet.
323
-
324
- ### Spec changes (`@skill-map/spec`)
325
-
326
- - **`cli-contract.md`** — `sm scan compare-with <dump> [roots...]`
327
- is now a sub-verb instead of a `--compare-with <path>` flag on
328
- `sm scan`. Read-only delta report against a saved `ScanResult`
329
- JSON dump. Read-only — does not modify the DB. Same exit codes
330
- (`0` empty delta / `1` drift / `2` operational error). Old flag
331
- form removed.
332
- - **`cli-contract.md`** — exit-code `2` "Operational error" row
333
- clarified to mention environment / runtime mismatches (wrong
334
- Node version, missing native dependency) explicitly. The
335
- "unhandled exception" catch-all already covered the case; this
336
- just removes ambiguity for future implementers.
337
- - **`cli-contract.md`** — new normative section **§Dry-run**
338
- between §Exit codes and §Verb catalog defining the contract for
339
- any verb exposing `-n` / `--dry-run`: no observable side effects
340
- (DB / FS / config / network / spawns), no auto-provisioning of
341
- scope directories, output mirrors the live mode with explicit
342
- "would …" framing, exit codes mirror the live mode, dry-run
343
- MUST short-circuit `--yes` / `--force` confirmation prompts.
344
- Per-verb opt-in: the flag is not global, verbs that don't
345
- declare it MUST reject it as an unknown option. Verb catalog
346
- rows for `sm init`, `sm db reset` (default + `--state` +
347
- `--hard`), and `sm db restore` amended to declare and describe
348
- their `--dry-run` previews.
349
-
350
- ### CLI changes (`@skill-map/cli`)
351
-
352
- #### Critical — kernel & adapter hygiene
353
-
354
- - **C1 — `runScanInternal` decomposed.** The 290-line monolith in
355
- `kernel/orchestrator.ts` split into a thin composer + four pure
356
- functions: `validateRoots`, `indexPriorSnapshot`,
357
- `walkAndDetect`, `runRules`. Composer is now 89 lines reading
358
- top-to-bottom through the pipeline phases. Zero behavioural
359
- change.
360
- - **C2 — `withSqlite(options, fn)` helper.** Single utility at
361
- `cli/util/with-sqlite.ts` standardises the open / use / close
362
- idiom every read-side command was open-coding. Eliminates four
363
- classes of boilerplate bugs (forgotten close, `autoBackup`
364
- drift, double-close, missing `try/finally`). Migrated 20 call
365
- sites across `check`, `export`, `graph`, `history`, `init`,
366
- `jobs`, `list`, `orphans`, `plugins`, `scan`, `show`, `watch`,
367
- plus `cli/util/plugin-runtime.ts`. Companion `tryWithSqlite`
368
- short-circuits when the DB file does not exist, replacing the
369
- `if (existsSync) { withSqlite(...) }` chain. In `scan.ts` the
370
- read-prior + persist double-open consolidated into a single
371
- `withSqlite` callback that brackets read prior → run scan →
372
- guard → persist when `willPersist`. Saves one migration
373
- discovery pass + one WAL setup per normal scan (~50–100ms).
374
-
375
- #### High — UX & contract integrity
376
-
377
- - **H3 — `--dry-run` semantics unified across `init` / `db reset`
378
- / `db restore`.** The new spec §Dry-run codifies the "no
379
- writes, reads OK" contract; three verbs that did not previously
380
- expose a preview now do: - `sm init --dry-run` — previews the would-create lines for
381
- `.skill-map/`, `settings.json`, `settings.local.json`,
382
- `.skill-mapignore`, the `.gitignore` entries that would be
383
- appended (deduped against the existing file), the DB
384
- provisioning, and the first-scan trigger. Honours `--force`
385
- for the would-overwrite preview. Re-init over an existing
386
- scope without `--force` still exits 2 (same gate as live). - `sm db reset --dry-run` (default + `--state`) — opens the DB
387
- read-only, computes the row count per `scan_*` (and `state_*`
388
- when `--state`) table, and prints them. No `DELETE`
389
- statements issued. Bypasses the `--state` confirmation prompt
390
- entirely. - `sm db reset --hard --dry-run` — reports the DB file path and
391
- size that would be unlinked; missing-file case prints a clear
392
- no-op line instead of an error. - `sm db restore <src> --dry-run` — validates the source exists
393
- (still exits 5 if missing), reports the source size and
394
- whether the target would be created or overwritten, plus the
395
- WAL / SHM sidecars that would be dropped. Bypasses the
396
- confirmation prompt.
397
- Implementation: new helper `previewGitignoreEntries(scopeRoot,
398
- entries)` in `init.ts` mirrors `ensureGitignoreEntries` parsing
399
- so the preview tracks the live outcome exactly. Texts moved
400
- into `cli/i18n/init.texts.ts` and `cli/i18n/db.texts.ts` per
401
- the N4 pattern. **9 new tests** under `init-cli.test.ts` (5
402
- cases) and `db-cli.test.ts` (9 cases) cover the previews + the
403
- spec invariants ("DB file checksum unchanged after dry-run",
404
- "scope directory absent after dry-run", "source-not-found
405
- still exits 5", "confirmation prompt skipped under dry-run").
406
- - **H1 — Centralised exit codes.** New `cli/util/exit-codes.ts`
407
- exporting `ExitCode` (`Ok` / `Issues` / `Error` / `Duplicate` /
408
- `NonceMismatch` / `NotFound`) and the type alias `TExitCode`.
409
- Every `Command#execute()` migrated from numeric literals (123
410
- sites across 17 files) to the enum. Single source of truth
411
- aligned with `spec/cli-contract.md` §Exit codes. **Bug fix
412
- surfaced en passant:** `sm job prune` returned `2` for "DB
413
- missing" while every other read-side verb returned `5` via
414
- `assertDbExists`; corrected to use the shared helper and return
415
- `NotFound`. Companion test updated to expect `5`.
416
- - **H2 — Plugin loader timeout.** `IPluginLoaderOptions.loadTimeoutMs`
417
- (default `5000`, exported as `DEFAULT_PLUGIN_IMPORT_TIMEOUT_MS`).
418
- Each dynamic `import()` now races against a timer; on timeout
419
- the plugin is reported as `load-error` with a message naming
420
- the elapsed budget and pointing at top-level side effects as
421
- the likely cause (network call, infinite loop, large blocking
422
- work). Without this a plugin with a hanging top-level `await`
423
- blocks every host CLI command indefinitely.
424
- - **H4 — `--strict` self-validates `--json` output.** When
425
- `sm scan --strict --json` is invoked, the produced `ScanResult`
426
- is validated against `scan-result.schema.json` before stdout.
427
- Catches the case where a custom detector emits a Link that
428
- passes the shallow `validateLink` guard but fails the full
429
- schema, which would silently land in stdout and break a
430
- downstream `sm scan compare-with -`.
431
- - **H5 — External-link discrimination uses URL-shape regex.**
432
- `isExternalUrlLink` was string-matching `http://` / `https://`
433
- only; any other URL scheme (`mailto:`, `data:`, `file:///`,
434
- `ftp://`) was silently classified as internal and polluted the
435
- graph as a fake internal link with `byPath` lookups that always
436
- missed. Replaced with the RFC 3986 scheme regex
437
- (`/^[a-z][a-z0-9+\-.]+:/i`), guarding against Windows-style
438
- absolute paths via the ≥ 2-char scheme constraint.
439
- - **H6 — Prior snapshot validated under `--strict`.** Both
440
- `sm scan` and `sm watch`, when run with `--strict`, validate
441
- the DB-resident `ScanResult` against the spec schema before
442
- handing it to the orchestrator. A DB corrupted manually or
443
- mid-rollback used to slip nodes with malformed `bodyHash` /
444
- `frontmatterHash` into the rename heuristic, where the
445
- dereference would silently produce spurious matches.
446
-
447
- #### Medium — surface & extensibility
448
-
449
- - **M1 — `sm scan compare-with` sub-verb.** New
450
- `ScanCompareCommand` in `cli/commands/scan-compare.ts`; the
451
- `--compare-with` flag is removed from `ScanCommand`. The
452
- sub-verb form structurally rejects flag combos that used to
453
- require runtime guards (`--changed`, `--no-built-ins`,
454
- `--allow-empty`, `--watch`): Clipanion rejects them at parse
455
- time as unknown options.
456
- - **M2 — `kernel/index.ts` enumerated exports.** Replaced the two
457
- `export type *` wildcards (from `./types.js` and
458
- `./ports/index.js`) with explicit named exports. Same set of
459
- public types — the DTS size and tests confirm parity. Going
460
- forward, any new domain type or port change requires an
461
- explicit edit to the barrel, preventing silent surface drift.
462
- - **M3 — Build hack documented (workaround retained).** Tried to
463
- replace the post-build `restoreNodeSqliteImports` pass with
464
- `external: ['node:sqlite']` in `tsup.config.ts`. Esbuild marks
465
- the specifier as external but still strips the `node:` prefix;
466
- same outcome with `[/^node:/]` regex and `packages: 'external'`
467
- (which also externalises real npm deps). Reverted to the
468
- post-build `replaceAll` pass, with a docstring documenting
469
- every workaround attempted so the next agent does not repeat
470
- the spike.
471
- - **M4 — `tryWithSqlite` helper.** See C2.
472
- - **M5 — `CamelCasePlugin` trap documented.** Added a
473
- trap-warning block to `SqliteStorageAdapter`'s docstring:
474
- `sql.raw` / `sql\`...\``template literals do NOT pass through
475
- the`CamelCasePlugin`; raw SQL fragments must use snake_case to
476
- match the migrations.
477
- - **M6 — Per-extension error reporting.** When the orchestrator
478
- drops a link emitted with an undeclared kind or an issue with
479
- an invalid severity, it now emits a `type: 'extension.error'`
480
- `ProgressEvent` instead of silently swallowing. The CLI
481
- subscribes via the new `createCliProgressEmitter(stderr)`
482
- helper and renders those events as `extension.error: <message>`
483
- on stderr. Plugin authors finally see WHY their link / issue
484
- disappears from the result. Wired in `scan` (normal +
485
- compare-with), `watch`, and `init`.
486
- - **M7 — Type naming convention documented (no rename).** Top-of-
487
- file docstring in `kernel/types.ts` and a new section in
488
- `AGENTS.md` describe the four-bucket convention the codebase
489
- has always implicitly followed: domain types (no prefix,
490
- mirrors spec schemas), hexagonal ports (`Port` suffix), runtime
491
- extension contracts (`I` prefix), internal shapes (`I`
492
- prefix). Mass rename was rejected after a cost-benefit pass —
493
- naming changes are cheap to write but expensive to review;
494
- existing names are mostly coherent. The agent base
495
- (`_plugins/minions/shared/architect.md`) gained a "Naming
496
- conventions check" sub-section in REVIEW mode so future audits
497
- reach the same conclusion.
498
-
499
- #### Low / nit — cleanup
500
-
501
- - **L1 — `omitModule` JSON replacer precision.** Identifies the
502
- ESM namespace by `[Symbol.toStringTag] === 'Module'` instead of
503
- matching every `module` key blindly. A plugin manifest that
504
- legitimately ships an unrelated `module` field (e.g. a string
505
- property in `metadata`) is no longer silently dropped from
506
- `sm plugins list --json` output.
507
- - **L2 — Stub verbs flagged in `--help`.** Every
508
- `not-yet-implemented` verb in `cli/commands/stubs.ts` carries a
509
- `(planned)` suffix on its `description`, surfaced in
510
- `sm --help`. The `notImplemented` helper now writes
511
- `<verb>: not yet implemented (planned).` on stderr instead of
512
- promising a specific Step number — roadmap step numbers shift
513
- mid-flight, stale promises in `--help` are worse than no
514
- promise.
515
- - **L3 — Dead `eslint-disable` removed** from
516
- `cli/util/plugin-runtime.ts`.
517
- - **N1 — `Link.source` vs `Link.sources` doc clarified.** Both
518
- fields now carry inline doc-comments calling out the singular /
519
- plural naming trap. Spec-frozen, but the ambiguity is the
520
- easiest way to misread the type for new contributors.
521
- - **N2 — `sm check` Usage examples expanded.** The `-g/--global`
522
- and `--db <path>` flags were declared but missing from the
523
- `Usage.examples` block — asymmetry with `sm scan` and the rest
524
- of the read-side verbs that ship the same flags. Two examples
525
- added: `sm check --global` and `sm check --db
526
- /path/to/skill-map.db`.
527
- - **N4 — Error / hint strings extracted to `*.texts.ts` modules
528
- with `{{name}}` template interpolation.** Pre-1.0 is the
529
- natural moment to seed the pattern before the string set grows.
530
- The workspace `ui/` already has a sibling layout at
531
- `ui/src/i18n/` (functions returning template literals); CLI
532
- takes a deliberately different shape — flat string templates
533
- with `{{name}}` placeholders, interpolated by a tiny
534
- `tx(template, vars)` helper. Rationale: the template form is
535
- **drop-in compatible with Transloco / Mustache / Handlebars**
536
- (the syntax they all share) so the day this project migrates to
537
- a real i18n library, the strings move as-is. Functions would
538
- have to be re-shaped first.
539
-
540
- Helper at `kernel/util/tx.ts`. Contract:
541
-
542
- - Every `{{name}}` token MUST have a matching key in the vars
543
- object — missing key throws (silent fallback hides
544
- forgotten args in production).
545
- - `null` / `undefined` values throw — caller coerces
546
- upstream.
547
- - Whitespace inside the braces tolerated (`{{ name }}`) so
548
- long templates wrap cleanly across `+`-joined lines.
549
- - Plural / conditional logic does NOT live in the template;
550
- the caller picks `*_singular` vs `*_plural` keys.
551
-
552
- Files created:
553
-
554
- - `kernel/util/tx.ts` — the helper itself, with 13 tests in
555
- `test/tx.test.ts` (single / multi token, whitespace,
556
- missing / null / undefined keys, identifier shapes, error
557
- truncation).
558
- - `kernel/i18n/orchestrator.texts.ts` — frontmatter
559
- malformed/invalid templates, `extension.error` payloads,
560
- root validation errors.
561
- - `kernel/i18n/plugin-loader.texts.ts` — every `load-error` /
562
- `invalid-manifest` / `incompatible-spec` reason, plus the
563
- import timeout message.
564
- - `cli/i18n/scan.texts.ts` — `sm scan` flag-clash / scan
565
- failure / guard / summary templates, plus the `sm scan
566
-
567
- compare-with`dump-load errors.
568
-
569
- -`cli/i18n/watch.texts.ts`—`sm watch`lifecycle templates. -`cli/i18n/init.texts.ts`—`sm init`templates including
570
- the`--dry-run`previews and the singular/plural pair for
571
- gitignore updates. -`cli/i18n/db.texts.ts`—`sm db reset`/`sm db restore` templates including their`--dry-run`previews. -`cli/i18n/cli-progress-emitter.texts.ts`— the
572
- `extension.error: ...` stderr line.
573
-
574
- String content moved verbatim — every existing test that
575
- matches on stderr / stdout content keeps passing. Trivial
576
- single-token strings (`'No issues.\n'`) and rare per-handler
577
- bespoke phrases stay inline; the pattern is now established
578
- for whoever wants to migrate them in a follow-up.
579
-
580
- Note on `ui/` divergence: today the two workspaces use
581
- different shapes for their text tables (functions in `ui/`,
582
- templates in `cli/`). Aligning them is a follow-up — the day a
583
- real i18n library lands, both converge on its native shape.
584
- The CLI shape is closer to the eventual destination.
585
-
586
- - **N6 — `TIssueSeverity` aliased to `Severity`.** SQLite schema
587
- type now reads `type TIssueSeverity = Severity` instead of
588
- duplicating the union literal. Keeps DB and runtime in
589
- lock-step if the union ever evolves.
590
-
591
- ### Migrations consolidation (kernel DB)
592
-
593
- - **`src/migrations/001_initial.sql` + `002_scan_meta.sql`**
594
- consolidated into a single `001_initial.sql`. Pre-1.0 with no
595
- released DBs to forward-migrate, the two-file split was a
596
- historical accident from an incremental shipment. After
597
- consolidation: same 12 tables, same constraints, same indexes;
598
- `PRAGMA user_version` of a freshly-initialised DB is now `1`
599
- instead of `2`. Migration runner is unchanged (it tolerates any
600
- count of `NNN_*.sql` files).
601
-
602
- ### Test coverage (Part 1)
603
-
604
- - New tests for H2 (plugin loader timeout — 2 cases),
605
- M6 (orchestrator `extension.error` emission — 3 cases),
606
- CLI progress emitter wiring (4 cases). The compare-with suite
607
- (`scan-compare.test.ts`, 9 cases) was migrated to
608
- `ScanCompareCommand` and the three flag-clash tests dropped
609
- (the flags are now structurally absent on the sub-verb). Test
610
- totals: 479 (start of pass) → 488 (after H2/M6 tests) → 485
611
- (after the three flag-clash deletions).
612
-
613
- ### Deferred / out of scope
614
-
615
- The findings below were reviewed but did not warrant code
616
- changes; each has its own resolution noted alongside.
617
-
618
- - **L4 — `runScan` / `runScanWithRenames` unification.** Already
619
- resolved by C1 (both are thin wrappers around
620
- `runScanInternal`).
621
- - **L5 — Node-version-guard exit code.** Reviewed against the
622
- updated exit-code table; existing `2` is correct under
623
- "operational error / unhandled exception". Spec table got the
624
- environment-mismatch clarification (above).
625
- - **L6 — `loadSchemaValidators()` cache.** Already cached at
626
- module level since Step 5.12.
627
- - **L7 — `pkg with { type: 'json' }` portability.** Stable in
628
- Node ≥ 22; `engines.node": ">=24.0"` covers it. No fallback
629
- needed.
630
- - **N3 — `compare-with` "dump not found" exit code.** The error
631
- paths in `ScanCompareCommand` already use the `ExitCode.Error`
632
- enum (= 2) for dump load failures, matching the spec clause for
633
- operational errors.
634
- - **N5 — Exit-code list completeness.** Verified the comment in
635
- `cli/entry.ts` against `spec/cli-contract.md` §Exit codes —
636
- identical, no edit needed.
637
-
638
- ## Part 2 — Plugin model overhaul (5-phase implementation)
639
-
640
- ### Summary
641
-
642
- The plugin model received a comprehensive overhaul before
643
- stabilizing at v1.0. Plugin kinds total after this bump: **6**
644
- (Provider, Extractor, Rule, Action, Formatter, Hook). All
645
- breakings are pre-1.0 minor per `versioning.md` § Pre-1.0.
646
-
647
- ### Phase 1 (commit 7354c26) — Foundation
648
-
649
- Five sub-phases, additive or pre-1.0 minor breakings:
650
-
651
- - **A.4** — three-tier frontmatter validation model documented in
652
- `plugin-author-guide.md` (default permissive + `unknown-field`
653
- rule + `scan.strict` promote-to-error). Behavior unchanged.
654
- - **A.5** — plugin id global uniqueness: `directory ==
655
- manifest.id` rule, new status `id-collision` (sixth),
656
- validation in boot/scan/doctor. Cross-root collisions block
657
- both involved plugins; user resolves by renaming.
658
- - **A.6** — extension ids qualified `<plugin-id>/<ext-id>` in
659
- registry. Built-ins classified into `claude/*` (4 Claude-
660
- specific) and `core/*` (7 kernel built-ins) bundles. New
661
- `Registry.get/find` APIs; `defaultRefreshAction` schema
662
- requires the qualified pattern; `extension.error` events emit
663
- qualified ids.
664
- - **A.10** — optional `applicableKinds` filter on Detector
665
- manifest; fail-fast skip for non-matching kinds (zero CPU/LLM
666
- cost); doctor warning for kinds not declared by any installed
667
- Provider. Empty array invalid; absence preserves apply-to-all
668
- default.
669
- - **Granularity** — Built-ins now respect `config_plugins`
670
- enable/disable via granularity-aware filtering. New
671
- `IBuiltInBundle` shape with `granularity: 'bundle' |
672
- 'extension'`; `claude` ships as bundle (all-or-nothing), `core`
673
- as extension (each toggleable). User plugins default to bundle;
674
- opt in via `granularity` in `plugin.json`. Both plugin ids and
675
- qualified extension ids accepted as keys in `config_plugins`
676
- and `settings.json#/plugins` (no schema change needed).
677
-
678
- 550/550 tests pass (+33 vs baseline 517).
679
-
680
- ### Phase 2 (commit ae3eaa6) — Renames
39
+ - **Provider-driven kind presentation + `kindRegistry` envelope.** The Provider extension surface gains a required `kinds[*].ui` block (`label`, `color`, optional `colorDark`, optional `emoji`, optional discriminated icon `{ kind: 'pi', id }` or `{ kind: 'svg', path }`). Every payload-bearing REST envelope variant embeds a required `kindRegistry` field; sentinel envelopes (`health`, `scan`, `graph`) stay exempt. New conformance case `plugin-missing-ui-rejected` locks the loader's behaviour against drop-in Providers that omit the `ui` block.
681
40
 
682
- Four sub-phases, all breaking but allowed in minor pre-1.0:
41
+ - **`/api/nodes/:pathB64?include=body` body opt-in.** The single-node detail endpoint accepts `?include=body` to add `item.body: string | null` (read from disk on demand; `null` when the source file is missing or unreadable). Single-node response shape is `{ schemaVersion, kind: 'node', item, links: { incoming, outgoing }, issues }`. The body reader refuses absolute paths and any relative path that resolves outside the scope root.
683
42
 
684
- - **2a (Renderer Formatter)** Kind, types, files renamed.
685
- Method `render(ctx)` → `format(ctx)`; manifest field `format`
686
- → `formatId` (TS clash resolution). Same contract: graph →
687
- string, deterministic-only.
688
- - **2b (Adapter → Provider)** — New required field
689
- `explorationDir` on the manifest (e.g. `~/.claude` for the
690
- Claude Provider). DB schema migrated in-place (column
691
- `nodes.adapter` → `nodes.provider`, etc.). The
692
- hexagonal-architecture `RunnerPort.adapter` /
693
- `StoragePort.adapter` is unchanged.
694
- - **2c (Audit removed)** — Audit kind removed. The single
695
- built-in `validate-all` migrated to a Rule (qualified id
696
- `core/validate-all`, `evaluate(ctx) → Issue[]`). CLI verbs
697
- `sm audit *` removed; users invoke via `sm check --rules
698
- core/validate-all`.
699
- - **2d (Detector → Extractor)** — Method signature changes from
700
- `detect(ctx) → Link[]` to `extract(ctx) → void` — output flows
701
- through three ctx callbacks: `emitLink`, `enrichNode`, `store`.
702
- Built-ins migrated maintain functional parity using `emitLink`.
703
- Persistence of `enrichNode` deferred to Phase 4 (A.8 stale
704
- layer); orchestrator buffers in memory today.
43
+ - **`/ws` WebSocket protocol + watcher contract.** `### Server` documents the wire envelope (delegated to `job-events.md` §Common envelope), the event catalog (`scan.started` / `scan.progress` / `scan.completed` plus `extractor.completed` / `rule.completed` / `extension.error` plus the BFF-internal advisories `watcher.started` / `watcher.error`), connection lifecycle, the backpressure rule (4 MiB `bufferedAmount` → close 1009 + unregister), and the loopback-only assumption. `sm serve --no-watcher` flag added.
705
44
 
706
- 554/554 cli + 32/32 testkit pass.
707
-
708
- ### Phase 3 (commit 34f993e) — Schema relocation
709
-
710
- **A.2** — Per-kind frontmatter schemas relocate from spec to the
711
- Provider that declares them. Spec keeps only `frontmatter/base`
712
- (universal).
713
-
714
- - 5 schemas moved (`git mv`):
715
- `spec/schemas/frontmatter/{skill,agent,command,hook,note}.schema.json`
716
- → built-in Claude Provider's `schemas/` directory. New `$id`:
717
- `https://skill-map.dev/providers/claude/v1/frontmatter/<kind>`.
718
- Cross-package `$ref` resolves via the spec base's `$id`
719
- (`https://skill-map.dev/spec/v0/frontmatter/base.schema.json`);
720
- AJV resolves by `$id` when both schemas register on the same
721
- instance.
722
- - Provider manifest gains a required `kinds` map subsuming three
723
- former fields: `emits` (now derives from
724
- `Object.keys(kinds)`), the flat `defaultRefreshAction` map (now
725
- per-entry inside `kinds[<kind>].defaultRefreshAction`), and the
726
- new `schema` (path to the per-kind schema relative to the
727
- provider directory).
728
- - Built-in Claude Provider migrated: 5 kind entries (skill,
729
- agent, command, hook, note), each with `schema`, `schemaJson`
730
- (runtime field, AJV-compiled at load), and qualified
731
- `defaultRefreshAction` (`claude/summarize-<kind>`).
732
- - Kernel orchestrator parse phase asks the Provider for the
733
- schema via `IProviderFrontmatterValidator` (composed by scan
734
- via `buildProviderFrontmatterValidator`) instead of reading
735
- from spec/. Flow: validate base → look up provider → validate
736
- per-kind schema from Provider.
737
- - `schema-validators.ts` catalog loses the 5 per-kind frontmatter
738
- entries; only `frontmatter-base` remains kernel-known.
739
- `plugin-loader`'s `stripFunctionsAndPluginId` now also strips
740
- `schemaJson` (runtime-only) from each `kinds` entry before
741
- AJV-validating the manifest.
742
- - Coverage matrix: 28 → 23 schemas (the 5 per-kind frontmatter
743
- schemas are now Provider-owned and ship with their own
744
- conformance suite in Phase 5 / A.13).
745
-
746
- 556/556 cli + 32/32 testkit pass.
747
-
748
- ### Phase 4 (commit e62695f) — Probabilistic infra
749
-
750
- Five sub-phases, all breaking but allowed in minor pre-1.0:
751
-
752
- - **4a (A.9)** — fine-grained Extractor cache via new
753
- `scan_extractor_runs` table. Resolves gap where newly
754
- registered Extractors silently skipped cached nodes; cache hit
755
- logic now per-(node, extractor). Uninstalled Extractors cleaned
756
- (rows + orphan links). Migration in-place.
757
- - **4b (A.12)** — opt-in `outputSchema` for plugin custom
758
- storage. Manifest gains `storage.schema` (Mode A) and
759
- `storage.schemas` (Mode B) for AJV validation of
760
- `ctx.store.write/.set` calls. Throws on shape violation;
761
- default absent = permissive.
762
- - **4c (A.8)** — enrichment layer + stale tracking. New
763
- `node_enrichments` table persists per-(node, extractor)
764
- partials separately from author's frontmatter (immutable).
765
- Probabilistic enrichments track `body_hash_at_enrichment`; scan
766
- flags `stale=1` on body change (NOT deleted, preserves LLM
767
- cost). Helper `mergeNodeWithEnrichments` filters stale +
768
- last-write-wins. New verbs `sm refresh <node>` and
769
- `sm refresh --stale` (stubs awaiting Step 10).
770
- - **4d (A.11)** — sixth plugin kind `hook`. Declarative
771
- subscriber to a curated set of 8 lifecycle events (`scan.*`,
772
- extractor/rule/action.completed,
773
- job.spawning/completed/failed). Other events deliberately not
774
- hookable. Manifest declares `triggers[]` (load-time validated)
775
- and optional `filter`. Three new kernel events added to
776
- catalog. Dual-mode (det dispatched in-process; prob deferred to
777
- Step 10).
778
- - **4e (A.7)** — `sm check --include-prob` opt-in flag (stub).
779
- Default `sm check` unchanged: det only, CI-safe. With flag:
780
- detects prob rules, emits stderr advisory; full dispatch awaits
781
- Step 10. Combines with `--rules`, `-n`, `--no-plugins`.
782
-
783
- 591/591 cli + 32/32 testkit pass.
784
-
785
- ### Phase 5 (commit 03b5a65) — Conformance + cleanup
786
-
787
- **A.13** — Conformance fixture relocation:
788
-
789
- - 3 cases moved (`git mv`): `basic-scan`, `orphan-detection`,
790
- `rename-high` →
791
- `src/extensions/providers/claude/conformance/cases/`. 11
792
- fixture files (`minimal-claude/`, `orphan-{before,after}/`,
793
- `rename-high-{before,after}/`) moved alongside.
794
- - New `coverage.md` per-Provider listing the 5 frontmatter
795
- schemas (skill, agent, command, hook, note) and their cases.
796
- - New verb `sm conformance run [--scope spec|provider:<id>|all]`.
797
- Discovery by convention at `<plugin-dir>/conformance/`. The
798
- existing runner gains optional `fixturesRoot` (default
799
- `<specRoot>/conformance/fixtures` for compat); tooling using
800
- the public API of `@skill-map/cli/conformance` keeps working.
801
- `--json` deferred — reporter shape not yet frozen.
802
- - Spec keeps only the kernel-agnostic case (`kernel-empty-boot`)
803
- and the universal preamble fixture. Coverage matrix downgrades
804
- conservatively (rows that depended on `basic-scan` are now
805
- partial or missing, with cross-link to the Provider's matrix).
806
-
807
- ROADMAP cleanup:
808
-
809
- - The three "Status: target state for v0.8.0 — spec catch-up
810
- pending" banners on §Plugin system / §Frontmatter standard /
811
- §Enrichment are removed; prose shifts from future to present
812
- ("kinds from v0.7.0 are renamed" → "were renamed in spec
813
- 0.8.0"; Model B enrichment now describes the shipped
814
- `node_enrichments` table with `body_hash_at_enrichment` rather
815
- than "table or column set decided in PR").
816
- - Decision-log entry for the working session rewritten to
817
- reflect "shipped" rather than "pending".
818
- - Last-updated header gains an "implementation" paragraph
819
- listing the four prior phase commits.
820
-
821
- 593/593 cli + 32/32 testkit pass (+2 vs Phase 4 baseline).
822
- spec:check green (40 files hashed — down from 53 because the
823
- Claude-specific cases and fixtures left the spec's hash set).
824
-
825
- ### Breaking changes for plugin authors (Part 2)
826
-
827
- Manifest renames:
828
-
829
- - `kind: 'adapter'` → `kind: 'provider'`
830
- - `kind: 'detector'` → `kind: 'extractor'`
831
- - `kind: 'renderer'` → `kind: 'formatter'`
832
- - `kind: 'audit'` removed (migrate to `kind: 'rule'`).
833
-
834
- Method signatures:
835
-
836
- - Detector `detect(ctx) → Link[]` → Extractor `extract(ctx) →
837
- void` (output via `ctx.emitLink` / `ctx.enrichNode` /
838
- `ctx.store`).
839
- - Renderer `render(ctx) → string` → Formatter `format(ctx) →
840
- string`.
841
-
842
- Manifest fields:
843
-
844
- - Provider gains required `explorationDir`.
845
- - Provider's flat `defaultRefreshAction` map replaced by per-kind
846
- entries inside `kinds[<kind>].defaultRefreshAction` (must
847
- follow qualified pattern `<plugin-id>/<ext-id>`).
848
- - Provider's `emits` derives from `Object.keys(kinds)` (the
849
- manifest field is gone).
850
- - Provider's per-kind schemas declared via `kinds[<kind>].schema`
851
- (path relative to provider dir).
852
- - Renderer's `format` field renamed to `formatId` on the
853
- Formatter manifest (TS clash resolution).
854
- - New plugin kind `hook` with `triggers[]` + optional `filter`.
855
- - Optional `outputSchema` (`storage.schema` / `storage.schemas`)
856
- for Mode A / Mode B plugin custom storage.
857
- - Optional `applicableKinds` filter on Extractor manifest.
858
-
859
- Extension ids:
860
-
861
- - All extension ids must be qualified
862
- `<plugin-id>/<extension-id>` (built-ins classified into
863
- `claude/*` and `core/*`).
864
-
865
- DB schema:
866
-
867
- - Two new tables added in-place to `001_initial.sql` (pre-1.0
868
- consolidation, no production DBs to migrate):
869
- `scan_extractor_runs` and `node_enrichments`.
870
- - Column rename `nodes.adapter` → `nodes.provider` (and parallel
871
- in `result.adapters` → `result.providers`).
872
-
873
- ## Test stats
874
-
875
- 593/593 cli + 32/32 testkit pass (post-Phase 5).
876
- Two new DB tables (`scan_extractor_runs`, `node_enrichments`)
877
- added in-place to `001_initial.sql` (pre-1.0 consolidation, no
878
- production DBs to migrate). The 5 per-kind frontmatter schemas
879
- relocated from spec/ to the Claude Provider package.
880
-
881
- ## [Unreleased]
882
-
883
- ### Minor Changes
884
-
885
- - Conformance fixture relocation (Phase 5 / A.13). The conformance suite splits along ownership lines: spec-owned cases (kernel-agnostic, today only `kernel-empty-boot` plus the deferred `preamble-bitwise-match`) keep living under `spec/conformance/`; Provider-owned cases that exercise a Provider's own kind catalog move next to that Provider's manifest, under `<plugin-dir>/conformance/`. The reference impl's Claude Provider now hosts `basic-scan`, `rename-high`, and `orphan-detection` together with their `minimal-claude` / `orphan-{before,after}` / `rename-high-{before,after}` fixtures at `src/extensions/providers/claude/conformance/`. The split mirrors the spec 0.8.0 Phase 3 schema relocation: cases that depend on Claude-specific kinds (`skill`) belong with the Provider that declares the kind, not in the spec. New CLI verb `sm conformance run [--scope spec|provider:<id>|all]` (default `all`) drives both buckets in one invocation; `--scope spec` and `--scope provider:claude` narrow to a single suite for targeted runs and CI matrices. The reference runner gains an optional `fixturesRoot` parameter so cases can resolve their fixtures against the Provider's directory instead of the spec's. `spec/conformance/README.md` updated for the dual-ownership layout (spec-owned + Provider-owned tables, `sm conformance run` documented, runner pseudocode amended). `spec/conformance/coverage.md` retargeted: rows that used to credit `basic-scan` (now Provider-owned) downgrade to `kernel-empty-boot`-only or `🔴 missing` and point to the Provider's coverage file (`src/extensions/providers/claude/conformance/coverage.md`); the rename-heuristic non-schema row notes the Provider ownership. `spec/cli-contract.md` adds a §Conformance subsection under §Verb catalog and adds `sm conformance run` to the elapsed-time §Scope. `spec/architecture.md` opening sentence credits both buckets. Pre-1.0 minor per `versioning.md` § Pre-1.0; breaking only for tooling that hard-codes the previous case paths under `spec/conformance/cases/{basic-scan,rename-high,orphan-detection}.json` — no real ecosystem affected today (the reference impl's runner already migrates).
886
-
887
- - `sm check` gains `--include-prob` opt-in flag for probabilistic Rule dispatch (Phase 4 / A.7). Default unchanged: deterministic only, CI-safe — same status quo behaviour. With the flag, the verb loads the plugin runtime, finds Rules with `mode === 'probabilistic'` (filtered by `--rules` if set), and emits a stderr advisory naming the skipped rule ids. Full dispatch lands when the job subsystem ships at Step 10; until then the flag is a stub — prob rules never produce issues, never alter the exit code. New companion flag `--async` is reserved for the future encoding (returns job ids without waiting once jobs land); today it is a no-op the advisory mentions. Companion filters `-n <node.path>` and `--rules <ids>` (comma-separated qualified or short ids) added to `sm check` for granular reads — they restrict the persisted-issue list AND filter which prob rules surface in the advisory. Does NOT extend to `sm scan` or `sm list`. Documented in `cli-contract.md` §Browse and `plugin-author-guide.md` §Rules. Pre-1.0 minor per `versioning.md` § Pre-1.0; additive — no consumer breakage.
888
-
889
- - Sixth plugin kind `hook` added (Phase 4 / A.11). Reacts declaratively to a curated set of 8 lifecycle events — `scan.started`, `scan.completed`, `extractor.completed`, `rule.completed`, `action.completed`, `job.spawning`, `job.completed`, `job.failed`. Other events (per-node `scan.progress`, `model.delta`, `run.*`, `job.claimed`, `job.callback.received`) are deliberately NOT hookable: too verbose, internal to the runner, or covered elsewhere. Manifest declares `triggers[]` (validated against the hookable set; an unknown trigger yields `invalid-manifest` at load time with a directed reason naming the offending trigger and the full hookable list) and an optional `filter` object (top-level field equality match against the event payload; cross-field validation is best-effort in v0.x). Dual-mode: `deterministic` (default) runs `on(ctx)` in-process during the dispatch of the matching event, synchronously between emission and the next pipeline step; `probabilistic` is enqueued as a job (deferred to the job subsystem at Step 10 — probabilistic hooks load but skip dispatch with a stderr advisory until then). Hooks REACT to events; they cannot mutate the pipeline, block emission, or alter outputs. Errors are caught by the dispatcher (logged through `extension.error` with `kind: 'hook-error'`) and never block the main flow. Three new event types added to the catalog so the aggregated Extractor / Rule / Action triggers have a normative shape: `extractor.completed` (one per Extractor, after the full walk), `rule.completed` (one per Rule, after issue validation), `action.completed` (one per Action invocation, after report recording — lands alongside the job subsystem at Step 10). New schema `schemas/extensions/hook.schema.json` (`$id` `https://skill-map.dev/spec/v0/extensions/hook.schema.json`); `schemas/extensions/base.schema.json#/properties/kind/enum` extended with `hook`. Documented in `architecture.md` §Extension kinds (table extended from 5 to 6 rows), §Mode capability matrix (Hook dual-mode), §Hook · curated trigger set (new dedicated section); `plugin-author-guide.md` retitled "## The six extension kinds" with a new Hooks subsection (worked example: Slack notifier on `scan.completed`); `job-events.md` cross-links Hook from each of the 8 hookable triggers, adds the three new aggregated event entries, and updates the experimental tag scope. Coverage matrix grows from 23 to 24 rows. Pre-1.0 minor per `versioning.md` § Pre-1.0; additive — no consumer breakage. Existing extension kinds (`provider`, `extractor`, `rule`, `action`, `formatter`) are untouched.
890
-
891
- - Enrichment layer formalized (Phase 4 / A.8). New kernel table `node_enrichments(node_path, extractor_id, body_hash_at_enrichment, value_json, stale, enriched_at, is_probabilistic)` stores `ctx.enrichNode(partial)` outputs separately from the author's frontmatter (which remains IMMUTABLE from any Extractor — both deterministic and probabilistic). Per-Extractor attribution is preserved (one row per `(node, extractor)` pair). Probabilistic enrichments track `body_hash_at_enrichment`; when the scan loop sees a body change, those rows are flagged `stale = 1` (NOT deleted, so the LLM cost is preserved). Deterministic enrichments regenerate via the A.9 fine-grained cache and pisar via PRIMARY KEY conflict on the next re-extract — they are never stale-flagged. Read-side helper `mergeNodeWithEnrichments(node, enrichments)` produces a "merged view" by filtering stale rows, sorting by `enriched_at` ASC, and spread-merging onto the author frontmatter (last-write-wins per field). Stale visibility is opt-in (`includeStale: true`). Rules / `sm check` / `sm export` consume `node.frontmatter` directly (deterministic CI-safe baseline); enrichment consumption is opt-in by the caller. New verbs `sm refresh <node>` (granular) and `sm refresh --stale` (batch) re-run Extractors and upsert fresh enrichment rows — STUBBED until the job subsystem ships at Step 10: deterministic Extractors persist for real, probabilistic Extractors emit a stderr advisory and skip without touching their stale rows. Migration `001_initial.sql` updated in place per the pre-1.0 consolidation precedent (no released DBs to forward-migrate). Documented in `db-schema.md` §`node_enrichments`, `architecture.md` §Extractor · enrichment layer, `cli-contract.md` §Scan, and `plugin-author-guide.md` §Extractors. Pre-1.0 minor per `versioning.md` § Pre-1.0; additive — no consumer breakage.
892
-
893
- - Plugin manifest gains optional `storage.schemas` map (Mode B / dedicated) and `storage.schema` (Mode A / KV) for opt-in JSON Schema validation of custom storage writes. AJV-validates `ctx.store.write(table, row)` and `ctx.store.set(key, value)` before persisting; throws on shape violation. Default absent = permissive (status quo). `emitLink` and `enrichNode` keep their universal kernel validation regardless. A schema file missing on disk or failing AJV compile at load time surfaces as `load-error` with a directed reason naming the plugin, the table (Mode B), and the schema path. Pre-1.0 minor per `versioning.md` § Pre-1.0; additive — no consumer breakage. Documented in `plugin-author-guide.md` §Storage and referenced from `architecture.md` §Extractor · output callbacks.
894
-
895
- - New kernel table `scan_extractor_runs(node_path, extractor_id, body_hash_at_run, ran_at)` — fine-grained Extractor cache breadcrumbs (Phase 4 / A.9). Replaces the previous "trust the node-level body+frontmatter hash" model that silently bypassed any Extractor newly registered between scans. Cache decision per `(node, extractor)` pair: a new Extractor registered between scans yields a partial cache hit (only the newcomer runs over the cached node); an uninstalled Extractor's rows disappear via replace-all, and links whose sources are exclusively that Extractor disappear with them. Documented in `db-schema.md` §`scan_extractor_runs`. Migration `001_initial.sql` updated in place per the pre-1.0 consolidation precedent (no released DBs to forward-migrate). Pre-1.0 minor per `versioning.md` § Pre-1.0; additive — no consumer breakage.
896
-
897
- - Per-kind frontmatter schemas relocate from spec to the Provider that declares them. Spec keeps only `frontmatter/base.schema.json` (universal — fields common to every node across every Provider). The Claude Provider gains a `kinds` map declaring its catalog (`skill` / `agent` / `command` / `hook` / `note`) with per-kind `schema` + `defaultRefreshAction`. The pre-0.8 flat fields `emits: string[]` and `defaultRefreshAction: { <kind>: actionId }` collapse into the new map: `emits` is removed (derived from `Object.keys(kinds)`); each `defaultRefreshAction[<kind>]` value moves into `kinds[<kind>].defaultRefreshAction`. The kernel parse phase asks the Provider for the schema instead of reading from `spec/schemas/frontmatter/<kind>.schema.json`. Schema files moved: `spec/schemas/frontmatter/{skill,agent,command,hook,note}.schema.json` → `src/extensions/providers/claude/schemas/{skill,agent,command,hook,note}.schema.json`; their `$id` updates from `https://skill-map.dev/spec/v0/frontmatter/<kind>.schema.json` to `https://skill-map.dev/providers/claude/v1/frontmatter/<kind>.schema.json`; their `$ref: 'base.schema.json'` updates to `$ref: 'https://skill-map.dev/spec/v0/frontmatter/base.schema.json'` (absolute `$ref`-by-`$id` so AJV resolves cross-package against the spec base registered into the same instance). `spec/schemas/extensions/provider.schema.json` updated: `kinds` is required, `emits` and the old shape of `defaultRefreshAction` removed. `spec/conformance/coverage.md` matrix shrinks from 28 to 23 rows (the five per-kind frontmatter rows belong to the Provider's own conformance suite, planned in Phase 5). `spec/index.json` no longer lists the per-kind schemas. `architecture.md` §Provider section retitled `Provider · kinds catalog and explorationDir`; `plugin-author-guide.md` Provider example updated; `README.md` directory tree updated to reflect spec/frontmatter/ now holds only `base.schema.json`. Pre-1.0 minor per `versioning.md` § Pre-1.0; breaking for any plugin or test referencing `spec/schemas/frontmatter/<kind>.schema.json` paths or `$id`s, the old `provider.emits` field, or the flat `provider.defaultRefreshAction` map — no real ecosystem affected today.
898
-
899
- - Plugin kind `renderer` renamed to `formatter`. Method renamed `render(ctx) → format(ctx)`. Manifest field `format` (the identifier consumed by `--format`) renamed to `formatId` to avoid clashing with the new method name. Same contract otherwise: graph → string, deterministic-only. Aligns with industry tooling (ESLint formatter, Mocha reporter, Pandoc writer). `schemas/extensions/renderer.schema.json` renamed to `formatter.schema.json`; the `kind` const flips from `"renderer"` to `"formatter"`; `base.schema.json#/properties/kind/enum` updated. `architecture.md`, `cli-contract.md`, `plugin-author-guide.md`, `README.md` updated to match (Extension kinds table, Execution modes table, testkit helper names, worked CSV example). `conformance/coverage.md` row 28 retargeted at the new schema filename. Pre-1.0 minor per `versioning.md` § Pre-1.0; breaking for any plugin or test referencing `kind: "renderer"`, `IRenderer`, `r.format`, or `render(ctx)` — no real ecosystem affected today.
900
-
901
- - Plugin kind `'detector'` renamed to `'extractor'`. Method signature
902
- changes from `detect(ctx) → Link[]` to `extract(ctx) → void` — output
903
- flows through three new ctx callbacks: `emitLink(link)` (kernel `links`
904
- table), `enrichNode(partial)` (kernel enrichment layer, persisted into
905
- `node_enrichments` per A.8), and the existing `ctx.store` (plugin's
906
- own table). The Extractor absorbs what would have been a separate
907
- `Enricher` kind via `enrichNode`. Built-ins migrated:
908
- `claude/frontmatter`, `claude/slash`, `claude/at-directive`,
909
- `core/external-url-counter` — all use `emitLink` to maintain
910
- functional parity with their Detector ancestors. Schema files
911
- renamed: `schemas/extensions/detector.schema.json` →
912
- `schemas/extensions/extractor.schema.json`. Persisted DB rows are
913
- unaffected (link `sources` carry extractor ids verbatim — the field
914
- was always free-form). Pre-1.0 minor per `versioning.md` § Pre-1.0;
915
- breaking for any plugin or test referencing `'detector'` as the
916
- kind, `IDetector`, or the old `Link[]` return signature — no real
917
- ecosystem affected today.
918
-
919
- - Plugin kind `'audit'` removed. The single built-in `'validate-all'`
920
- migrated to a Rule (qualified id `'core/validate-all'`, method
921
- `evaluate(ctx) → Issue[]`). The kind had dual personality (composer +
922
- standalone reporter); the standalone reporter case is naturally a Rule,
923
- and the composer case is deferred to post-1.0 if a real use case
924
- appears. CLI verbs `'sm audit run'` and `'sm audit show'` removed;
925
- users invoke the rule via `sm check --rules core/validate-all`.
926
- `state_executions.kind` enum narrowed to `['action']` (audit was the
927
- only other value); the column is preserved as a forward-compatibility
928
- lever. Schema files removed: `schemas/extensions/audit.schema.json`.
929
- Coverage matrix shrinks from 29 to 28 rows. Pre-1.0 minor per
930
- `versioning.md` § Pre-1.0; breaking for any plugin or test referencing
931
- the audit kind, `IAudit`, `TAuditReport`, or `sm audit` verbs — no
932
- real ecosystem affected today.
933
-
934
- - Plugin kind `'adapter'` renamed to `'provider'`. Manifest gains required
935
- field `'explorationDir'` (filesystem directory where the Provider's
936
- content lives, e.g. `'~/.claude'` for the Claude Provider). Built-in
937
- `claudeAdapter` renamed to `claudeProvider`. The hexagonal-architecture
938
- `'adapter'` (`RunnerPort.adapter`, `StoragePort.adapter`,
939
- `FilesystemPort.adapter`, `PluginLoaderPort.adapter`) is unchanged —
940
- distinct concept, distinct namespace.
941
- Persisted schema fields renamed: `node.adapter` → `node.provider`,
942
- `scan-result.adapters` → `scan-result.providers` (pre-1.0 minor — no
943
- production DBs to migrate; `001_initial.sql` was edited in place per
944
- the consolidation precedent already established for pre-1.0).
945
- Project config field renamed: `project-config.adapters` →
946
- `project-config.providers`. Schema files renamed:
947
- `schemas/extensions/adapter.schema.json` →
948
- `schemas/extensions/provider.schema.json`. Pre-1.0 minor per
949
- `versioning.md` § Pre-1.0; breaking for any plugin or test referencing
950
- `'adapter'` as the kind, `IAdapter`, or any persisted/config schema
951
- field renamed above — no real ecosystem affected today.
952
-
953
- ## 0.7.1
954
-
955
- ### Patch Changes
956
-
957
- - 0463a0f: Step 9.4 — plugin author guide + reference plugin + diagnostics polish.
958
- **Step 9 fully closed** with this changeset.
959
-
960
- ### Spec — plugin author guide (additive prose)
961
-
962
- New document at `spec/plugin-author-guide.md` covering:
963
-
964
- - Discovery roots (`<project>/.skill-map/plugins/`,
965
- `~/.skill-map/plugins/`, `--plugin-dir <path>`).
966
- - Manifest fields with the normative schema reference.
967
- - `specCompat` strategy — narrow ranges pre-`v1.0.0`, `^1.0.0`
968
- recommendation post-`v1.0.0`.
969
- - The six extension kinds with one minimal worked example each
970
- (detector, rule, renderer in full; adapter / audit / action flagged
971
- for later expansion alongside Step 10).
972
- - Storage choice (KV vs Dedicated) cross-linking `plugin-kv-api.md`
973
- and the Step 9.2 triple-protection rule.
974
- - Execution modes (deterministic / probabilistic) cross-linking
975
- `architecture.md`.
976
- - Testkit usage with `runDetectorOnFixture`, `runRuleOnGraph`,
977
- `runRendererOnGraph`, `makeFakeRunner`.
978
- - The five plugin statuses (`loaded` / `disabled` / `incompatible-spec`
979
- / `invalid-manifest` / `load-error`) and how to read them.
980
- - Stability section (document is stable; widening additions are minor
981
- bumps; breaking edits are major).
982
-
983
- `spec/package.json#files` updated to ship the new doc; `spec/index.json`
984
- regenerated (57 → 58 hashed files). `coverage.md` unchanged because the
985
- guide is prose, not a schema.
986
-
987
- ### Reference plugin — `examples/hello-world/`
988
-
989
- Smallest viable plugin in the principal repo (Arquitecto's pick: in
990
- the main repo, not separate). One detector (`hello-world-greet`)
991
- emitting `references` links per `@greet:<name>` token in node bodies.
992
- Includes:
45
+ ## 0.12.0
993
46
 
994
- - `plugin.json` declaring one extension and pinning `specCompat: ^1.0.0`.
995
- - `extensions/greet-detector.mjs` — runtime instance with both
996
- manifest fields and the `detect` method.
997
- - `README.md` — what it does, file layout, three-step "try it
998
- locally" recipe, what's intentionally missing (storage,
999
- multi-extension, probabilistic mode), pointers for production-grade
1000
- patterns.
1001
- - `test/greet-detector.test.mjs` — four-assertion test using
1002
- `@skill-map/testkit`, runnable via `node --test` with no build step.
47
+ ### Minor
1003
48
 
1004
- Verified end-to-end: the example plugin loads cleanly under
1005
- `sm plugins list`, scans contribute its links to the persisted graph,
1006
- and the testkit-based test passes. The example is **not** registered
1007
- as a workspace — it's intentionally standalone so users can copy it.
49
+ - **`sm serve` + Hono BFF skeleton.** New `### Server` subsection in `cli-contract.md`. Endpoints at this bump: `GET /api/health` (real), `ALL /api/*` (structured 404 stub), `GET /ws` (no-op upgrade — closes with code 1000 + reason `'no broadcaster yet'`), static handler + SPA fallback. Loopback-only through v0.6.0; boot resilient to a missing DB (`/api/health` reports `db: 'missing'`). `sm serve` flag set: `--port` (default 4242), `--host` (default 127.0.0.1), `--scope`, `--db`, `--no-built-ins`, `--no-plugins`, `--open` / `--no-open`, `--dev-cors`, `--ui-dist`.
1008
50
 
1009
- ### CLI — diagnostics polish on `PluginLoader.reason`
51
+ ## 0.11.0
1010
52
 
1011
- Each failure-mode reason string now carries an actionable hint:
53
+ ### Minor
1012
54
 
1013
- - `invalid-manifest` (JSON parse): names the manifest path, suggests
1014
- validating the JSON.
1015
- - `invalid-manifest` (AJV): names the manifest path AND points at
1016
- `spec/schemas/plugins-registry.schema.json#/$defs/PluginManifest`.
1017
- - `invalid-manifest` (specCompat not a valid range): suggests a range
1018
- shape (`"^1.0.0"`).
1019
- - `incompatible-spec`: suggests two remediations (update the plugin's
1020
- `specCompat`, or pin sm to a compatible spec version).
1021
- - `load-error` (extension file not found): includes the absolute
1022
- resolved path, pointer to `plugin.json#/extensions`.
1023
- - `load-error` (default export missing kind): lists the valid kinds.
1024
- - `load-error` (unknown kind): lists the valid kinds.
1025
- - `load-error` (extension manifest schema fails): names the
1026
- per-kind schema (`spec/schemas/extensions/<kind>.schema.json`).
55
+ - **Job artifacts move into the database (content-addressed).** New `state_job_contents(content_hash PK, content, created_at)`; `state_jobs.file_path` removed (rendered content fetched via join). `state_executions.report_path` → `state_executions.report_json` (parsed-JSON-on-read). `Job.filePath` removed; `ExecutionRecord.reportPath` → `ExecutionRecord.report` (parsed JSON / null). `RunnerPort.run(jobContent, options)` returns `{ report, ... }` — path-based reporting is no longer part of the port contract. `sm job preview` reads from the DB; `sm job claim --json` returns `{ id, nonce, content }`; `sm record --report <path-or-dash>` accepts a file path or stdin; `sm job prune --orphan-files` removed (the verb auto-collects orphan content rows). `sm doctor` integrity checks updated. Event payload renames: `job.spawning.data.jobFilePath` → `contentHash`; `job.callback.received.data.reportPath` and `job.completed.data.reportPath` → `executionId`. The `job-file-missing` failure-reason enum is preserved with shifted semantics: it now flags a missing `state_job_contents` row (DB-corruption-only state).
1027
56
 
1028
- 6 new tests under `test/plugin-loader.test.ts` (`Step 9.4 diagnostics
1029
- polish` describe block) assert each hint shape is present without
1030
- pinning the full text. Test count 437 → **443 cli + 30 testkit = 473**.
57
+ ## 0.10.0
1031
58
 
1032
- ### Step 9 closed
59
+ ### Minor
1033
60
 
1034
- The four sub-steps 9.1 (plugin runtime wiring), 9.2 (plugin
1035
- migrations + triple protection), 9.3 (`@skill-map/testkit` workspace),
1036
- 9.4 (author guide + reference plugin + diagnostics polish) — together
1037
- turn `skill-map` plugins from "discovered but inert" into a
1038
- first-class authoring surface with documentation, tests, and a
1039
- working reference. Next step: **Step 10 — job subsystem + first
1040
- probabilistic extension** (wave 2 begins).
61
+ - **`Node.kind` opens to any Provider-declared string.** `node.schema.json#/properties/kind` becomes `{ type: 'string', minLength: 1 }`; the `CHECK in (...)` SQL constraints on `scan_nodes.kind` and `state_summaries.kind` drop; `extensions/action.schema.json#/.../filter/kind` widens to a string array. Providers declare their own kind catalog through the `kinds` map; the spec no longer enumerates a closed set.
1041
62
 
1042
63
  ## 0.7.0
1043
64
 
1044
- ### Minor Changes
1045
-
1046
- - d730094: Spec — Execution modes (deterministic / probabilistic) lifted to a first-class architectural property
1047
-
1048
- Frames a meta-property of skill-map that was previously implicit and scattered:
1049
- **every analytical extension is one of two modes** — `deterministic` (pure code,
1050
- runs in scan-time pipelines) or `probabilistic` (invokes an LLM through
1051
- `RunnerPort`, runs only as queued jobs). The dual-mode capability now spans four
1052
- of the six extension kinds; Adapter and Renderer remain locked to deterministic
1053
- because they sit at the system boundaries (filesystem and graph-to-string) where
1054
- non-determinism would break boot reproducibility and snapshot diffing.
1055
-
1056
- **Spec changes:**
1057
-
1058
- - `architecture.md` — new top-level section **§Execution modes** before
1059
- §Extension kinds. Defines the two modes, the per-kind capability matrix
1060
- (Detector / Rule / Action dual-mode by manifest declaration; Audit dual-mode
1061
- with mode **derived** from `composes[]`; Adapter / Renderer deterministic-only),
1062
- the runtime separation (`deterministic` runs in `sm scan` / `sm check`;
1063
- `probabilistic` runs only via `sm job submit <kind>:<id>`), and the
1064
- `RunnerPort` injection contract for probabilistic extensions.
1065
- - `architecture.md` §Extension kinds — table updated: each row clarifies the
1066
- mode posture (Adapter / Renderer marked deterministic-only; Detector / Rule /
1067
- Action marked dual-mode; Audit marked derived-mode).
1068
- - `architecture.md` §Stability — new clause: execution modes and the per-kind
1069
- capability matrix are stable as of v1.0.0; adding a third mode, changing
1070
- which kinds are dual-mode, or changing the audit's derivation rule is a major
1071
- bump.
1072
-
1073
- **Schema changes:**
1074
-
1075
- - `schemas/extensions/detector.schema.json`:
1076
- - New optional `mode` field (`deterministic` | `probabilistic`, default
1077
- `deterministic`). Omitting is equivalent to deterministic — keeps existing
1078
- detectors valid without an update.
1079
- - Description updated to spell out the dual-mode contract.
1080
- - `schemas/extensions/rule.schema.json`:
1081
- - Same shape: new optional `mode` field with default `deterministic`.
1082
- - Description rewritten — the previous "Rules MUST be deterministic" claim
1083
- moved into the deterministic-mode contract; probabilistic rules are now
1084
- explicitly allowed and run only as queued jobs.
1085
- - `schemas/extensions/action.schema.json`:
1086
- - **Breaking** — `mode` enum renamed: `local` → `deterministic`,
1087
- `invocation-template` → `probabilistic`. Pre-1.0; no consumers depend on
1088
- the old values (no third-party action plugins shipped). Description, the
1089
- two `if/then` branches, and the `expectedDurationSeconds` /
1090
- `promptTemplateRef` field descriptions updated accordingly.
1091
- - **Bug fix** — the schema previously declared `allOf` twice at the root
1092
- (lines 6–8 and 71–80); the second silently overrode the first, dropping
1093
- `$ref: base.schema.json`. Both blocks are now merged into a single `allOf`
1094
- so the action schema actually composes the base shape.
1095
- - `schemas/extensions/audit.schema.json`:
1096
- - Description rewritten — the "deterministic workflow" claim is replaced by
1097
- the **derived-mode** rule: the audit's effective mode is computed from
1098
- `composes[]` at load time. If every composed primitive is deterministic,
1099
- the audit is deterministic; if any is probabilistic, the audit is
1100
- probabilistic and dispatches as a job. Declaring `mode` directly is a
1101
- load-time error.
1102
- - `composes[]` description updated to mention that each primitive's mode
1103
- participates in derivation; dangling references stay a load-time error.
1104
- - `reportSchemaRef` description updated: probabilistic audits MUST extend
1105
- `report-base.schema.json` (carries `safety` / `confidence`); deterministic
1106
- audits MAY extend it but are not required to.
1107
- - `schemas/extensions/adapter.schema.json`:
1108
- - Description updated to state explicitly that adapters are deterministic-only
1109
- and that `mode` MUST NOT appear. Recommendation for users who want
1110
- LLM-assisted classification: write a probabilistic Detector that emits
1111
- classification hints as `Link[]`.
1112
- - `schemas/extensions/renderer.schema.json`:
1113
- - Description updated to state that renderers are deterministic-only and
1114
- that `mode` MUST NOT appear. Probabilistic narrators of the graph belong
1115
- in jobs and emit Findings, not in renderer manifests.
1116
-
1117
- **Why major (despite pre-1.0 minor norm):**
1118
-
1119
- Renaming the `Action.mode` enum (`local` → `deterministic`,
1120
- `invocation-template` → `probabilistic`) is breaking by definition. No
1121
- third-party Actions exist yet, but the rename touches the canonical surface and
1122
- deserves the bump. New optional fields on Detector / Rule and the new derived-
1123
- mode contract on Audit are additive and would have been minor on their own.
1124
-
1125
- **Implementation work intentionally NOT included here:**
1126
-
1127
- - `src/extensions/built-ins.ts` and the per-extension TS files keep working
1128
- unchanged because the new `mode` is optional with `deterministic` default.
1129
- Explicitly threading `mode: 'deterministic'` through every built-in is a
1130
- follow-up.
1131
- - `RunnerPort` injection through `ctx.runner` for probabilistic extensions is
1132
- spec'd here; the actual context plumbing lands with the first probabilistic
1133
- extension (Step 10 — first summarizer). `MockRunner` continues to satisfy
1134
- tests until then.
1135
- - Conformance case `extension-mode-derivation` (audit composes mixed
1136
- primitives → derives `probabilistic`) is mentioned in `architecture.md` and
1137
- pending under `spec/conformance/coverage.md` for the next release.
1138
- - ROADMAP.md rephrase of Steps 10–11 (from "summarizers" to "wave 2:
1139
- probabilistic extensions") and a positioning section in `README.md` follow
1140
- in separate commits to keep this changeset spec-only.
1141
-
1142
- ### Minor Changes
1143
-
1144
- - a73f3f4: Step 7.1 — File watcher (`sm watch` / `sm scan --watch`)
1145
-
1146
- Long-running watcher that subscribes to the scan roots, debounces
1147
- filesystem events, and triggers an incremental scan per batch. Reuses
1148
- the existing `runScanWithRenames` pipeline, the `IIgnoreFilter` chain
1149
- (`.skill-mapignore` + `config.ignore` + bundled defaults), and the
1150
- `scan.*` non-job events from `job-events.md` — one ScanResult per
1151
- batch, emitted as ndjson under `--json`.
1152
-
1153
- **Spec changes (minor)**:
1154
-
1155
- - `spec/schemas/project-config.schema.json` — new `scan.watch` object
1156
- with a single key `debounceMs` (integer ≥ 0, default 300). Groups
1157
- bursts of filesystem events (editor saves, branch switches, npm
1158
- installs) into a single scan pass. Set to 0 to disable debouncing.
1159
- - `spec/cli-contract.md` §Scan — documents `sm watch [roots...]` as
1160
- the primary verb and `sm scan --watch` as the alias. Watcher
1161
- respects the same ignore chain as one-shot scans, emits one
1162
- ScanResult per batch (ndjson under `--json`), closes cleanly on
1163
- `SIGINT` / `SIGTERM`, exits 0 on clean shutdown. Exit-code rule
1164
- carved out for the watcher: per-batch error issues do not flip the
1165
- exit code (the loop keeps running); operational errors still exit 2.
1166
-
1167
- No new events. No new ports. The watcher is implementation-defined
1168
- inside the kernel package; a future `WatchPort` can be added when /
1169
- if a non-Node implementation needs to swap the chokidar wrapper.
1170
-
1171
- **Runtime changes (minor — new verb + new config key)**:
1172
-
1173
- - `chokidar@5.0.0` pinned in `src/package.json` (single new runtime
1174
- dependency, MIT). Chokidar v5 requires Node ≥ 20.19; the project
1175
- already pins `engines.node: ">=24.0"` so this is a no-op for
1176
- consumers. Brings in `readdirp@5` as a transitive.
1177
- - `src/kernel/scan/watcher.ts` — `IFsWatcher` interface + concrete
1178
- `ChokidarWatcher` wrapping `chokidar.watch()` with the existing
1179
- `IIgnoreFilter` plumbed through, debouncer, batch coalescing,
1180
- and explicit `stop()` for clean teardown.
1181
- - `src/cli/commands/watch.ts` — new `WatchCommand`. `sm scan
1182
- --watch` delegates to the same code path so the two surfaces are
1183
- byte-aligned (no parallel implementations).
1184
- - `src/config/defaults.json` — new `scan.watch.debounceMs: 300`
1185
- default.
1186
-
1187
- **Why minor (not patch)**: new public verb (`sm watch`), new public
1188
- config key (`scan.watch.debounceMs`), and a new flag on an existing
1189
- verb (`sm scan --watch`). All three are surface additions, not bug
1190
- fixes — minor under both the spec and the runtime semver policies.
1191
- No breaking changes; existing `sm scan` without `--watch` is
1192
- byte-identical to before.
1193
-
1194
- **Roadmap**: Step 7 — Robustness, sub-step 7.1 (chokidar watcher).
1195
- Trigger normalization is implicit-already-landed (cabled into every
1196
- detector at Steps 3–4 with full unit tests in
1197
- `src/kernel/trigger-normalize.test.ts`); we do not write a sub-step
1198
- for it. Next sub-steps: 7.2 detector conflict resolution, 7.3 `sm
1199
- job prune` + retention enforcement.
1200
-
1201
- ### Patch Changes
1202
-
1203
- - a73f3f4: Step 7.2 — Detector conflict resolution
1204
-
1205
- Two pieces:
1206
-
1207
- 1. **New built-in rule `link-conflict`** (`src/extensions/rules/link-conflict/`).
1208
- Surfaces detector disagreement. Groups links by `(source, target)` and
1209
- emits one `warn` Issue per pair where the set of distinct `kind` values
1210
- has size ≥ 2. Agreement (single kind across multiple detectors) is
1211
- silent — by design, to avoid massive noise on real graphs.
1212
- Issue payload (`data`) carries `{ source, target, variants }` where
1213
- each `variant` is `{ kind, sources: detectorId[], confidence }`. Variant
1214
- sources are deduped + sorted; confidence is the highest across rows
1215
- of the same kind (`high` > `medium` > `low`).
1216
-
1217
- This is the kernel piece of Decision #90 read-time "consumers that
1218
- need uniqueness aggregate at read time" — the rule is one such
1219
- consumer, on the alarming side. Storage stays untouched (one row
1220
- per detector, no merge, no dedup). Severity is `warn`, not `error`:
1221
- the rule cannot pick which kind is correct, so per `cli-contract.md`
1222
- §Exit codes the verb stays exit 0.
1223
-
1224
- 2. **`sm show` pretty link aggregation** (`src/cli/commands/show.ts`).
1225
- The human renderer now groups `linksOut` / `linksIn` by `(endpoint,
1226
- kind, normalizedTrigger)` and prints one row per group with the
1227
- union of detector ids in a `sources:` field. The section header
1228
- reports both the raw row count and the unique-after-grouping count
1229
- (`Links out (12, 9 unique)`). When N > 1 detector emits the same
1230
- logical link, the row also gets a `(×N)` suffix.
1231
-
1232
- `--json` output is byte-identical to before — raw rows, no merge.
1233
- Storage is byte-identical to before. The grouping is purely a
1234
- read-time presentation choice for human eyes.
1235
-
1236
- **Spec changes (patch)**:
1237
-
1238
- - `spec/cli-contract.md` §Browse — `sm show` row clarifies that pretty
1239
- output groups identical-shape links and that `--json` emits raw rows.
1240
- Patch (not minor) because the JSON contract is unchanged; the human
1241
- output format is non-normative anyway.
1242
-
1243
- **Runtime changes (minor — new rule + new presentation)**:
1244
-
1245
- - New rule `link-conflict` registered in `src/extensions/built-ins.ts`.
1246
- - `sm show` pretty output groups links + reports unique counts.
1247
-
1248
- **UI inspector aggregation deferred to Step 13**: the current Flavor A
1249
- inspector renders the `Relations` card from `node.frontmatter.metadata.{
1250
- related, requires, supersedes, provides, conflictsWith}` directly — it
1251
- does NOT consume `linksOut` / `linksIn` rows from `scan_links`. There
1252
- is no link table to aggregate today. When Step 13's Flavor B lands (Hono
1253
- BFF + WS + full link panel from scan), the aggregation logic from
1254
- `src/cli/commands/show.ts` will need to be ported.
1255
-
1256
- **Roadmap**: Step 7 — Robustness, sub-step 7.2 (detector conflict
1257
- resolution). Closes one of the three remaining frentes; 7.3 (`sm job
1258
- prune` + retention) still pending. Decision #90 unchanged: storage
1259
- keeps raw per-detector rows. The `related` vs LLM-amplification
1260
- discussion is documented in `.tmp/skill-map-related-test/` (status
1261
- quo retained — fields stay opt-in under `metadata.*`; revisit if
1262
- real-world amplification appears).
65
+ ### Minor
1263
66
 
1264
- **Tests**: 327 335 (+8 new for the rule, no regressions).
67
+ - **Execution modes lifted to a first-class architectural property.** `architecture.md` gains §Execution modes defining the per-kind capability matrix: Extractor / Rule / Action / Hook are dual-mode (declared in manifest); Provider and Formatter are deterministic-only (boundary-positioned). Extractor / Rule schemas gain optional `mode` (default `deterministic`); Action's `mode` enum becomes `deterministic` / `probabilistic`; Provider / Formatter forbid the field.
1265
68
 
1266
69
  ## 0.6.1
1267
70
 
1268
- ### Patch Changes
1269
-
1270
- - f41dbad: Step 6.1 — Spec migration: rename the canonical config file from
1271
- `.skill-map.json` (single project-root file) to `.skill-map/settings.json`
1272
- inside the `.skill-map/` scope folder, with a sibling `.skill-map/settings.local.json`
1273
- partner for machine-specific overrides. Aligns the spec with the layered
1274
- config hierarchy described in the roadmap (library defaults → user → user-local
1275
- → project → project-local → env / flags).
1276
-
1277
- **Spec change (breaking, minor under pre-1.0 versioning policy)**:
1278
-
1279
- - `spec/schemas/project-config.schema.json` description updated to point at
1280
- `.skill-map/settings.json` and explicitly mention the `.local.json` partner
1281
- and the layered-merge contract. The schema _shape_ (keys, types, validation
1282
- rules) is unchanged — only the on-disk filename moves. Consumers that read
1283
- values without caring about the source path are unaffected; consumers that
1284
- hard-code the filename must update.
1285
- - `spec/db-schema.md` §Scopes: `history.share: true` reference updated to
1286
- `.skill-map/settings.json`.
1287
- - `spec/conformance/coverage.md` row #6 description updated to reference the
1288
- new path and the optional `settings.local.json` overlay.
1289
-
1290
- **Why minor (not major) at pre-1.0**: per `spec/versioning.md` §Pre-1.0,
1291
- breaking changes ARE allowed in minor bumps while the spec is `0.y.z`. The
1292
- shape of the data is unchanged; only the file name on disk moves.
1293
-
1294
- **No backward-compat shim**: there is no real implementation of the loader
1295
- yet (lands in 6.2), so no live consumer reads `.skill-map.json` today. The
1296
- only known prior reference is the demo `mock-collection/.claude/commands/init*.md`
1297
- fixture, which is updated together with `sm init` in 6.5.
1298
-
1299
- **Runtime change**: none in 6.1 — pure spec edit. The matching loader,
1300
- `sm init`, and `sm config` verbs land in subsequent sub-steps.
1301
-
1302
- **Roadmap update**: `ROADMAP.md` §Configuration "Spec migration" call-out
1303
- flipped from "pending" to "landed Step 6.1, 2026-04-27".
1304
-
1305
- Test count: unchanged (213 → 213 — spec-only edit).
1306
-
1307
- - 8a4667f: Step 6.6 — `sm plugins enable / disable` + the `config_plugins`
1308
- override layer they read from. The two stub verbs become real, and
1309
- the `PluginLoader` finally honours user intent: a disabled plugin
1310
- surfaces in `sm plugins list` with status `disabled`, but its
1311
- extensions are NOT imported and the kernel will not run them.
1312
-
1313
- **Decision (recorded in spec)**: enable/disable resolution favours the
1314
- DB row over `settings.json` over the installed default. The DB
1315
- override is local-machine; `settings.json` is the team-shared baseline.
1316
- A developer can locally disable a misbehaving plugin without
1317
- committing the toggle to the team's config; conversely, a baseline
1318
- that explicitly enables a plugin is overridable per-machine. The rule
1319
- is documented in `spec/db-schema.md` §`config_plugins`.
1320
-
1321
- **Spec change (additive, patch)**:
1322
-
1323
- - `spec/db-schema.md` — appended an "Effective enable/disable
1324
- resolution" subsection under `config_plugins` documenting the
1325
- three-layer precedence (DB > `settings.json` > installed default).
1326
- No schema changes; the `config_plugins` table itself was already
1327
- defined in the initial migration.
1328
-
1329
- **Runtime change**:
1330
-
1331
- - `src/kernel/types/plugin.ts` — `TPluginLoadStatus` gains a `disabled`
1332
- variant. JSDoc explains all five states.
1333
- - `src/kernel/adapters/sqlite/plugins.ts` — new file. Storage helpers
1334
- over the `config_plugins` table: `setPluginEnabled` (upsert),
1335
- `getPluginEnabled` (single read), `loadPluginOverrideMap` (bulk
1336
- read for one round-trip per process), `deletePluginOverride`
1337
- (idempotent drop, used by future `sm config reset plugins.<id>`).
1338
- - `src/kernel/config/plugin-resolver.ts` — new file.
1339
- `resolvePluginEnabled` implements the precedence above;
1340
- `makeEnabledResolver` curries the layered config and DB map into
1341
- the `(id) => boolean` shape `IPluginLoaderOptions.resolveEnabled`
1342
- expects.
1343
- - `src/kernel/adapters/plugin-loader.ts` — new optional
1344
- `resolveEnabled` callback in `IPluginLoaderOptions`. When supplied,
1345
- the loader checks AFTER manifest + specCompat validation and
1346
- short-circuits with `status: 'disabled'` (manifest preserved,
1347
- extensions array omitted, reason `"disabled by config_plugins or
1348
- settings.json"`). Omitting the callback keeps the legacy "always
1349
- load" behaviour for tests / kernel-empty-boot.
1350
- - `src/cli/commands/plugins.ts` — wires the loader to the resolver:
1351
- every read (`list / show / doctor`) loads `config_plugins` once and
1352
- feeds the resolver. Two new commands `PluginsEnableCommand` and
1353
- `PluginsDisableCommand` write to the DB. `--all` toggles every
1354
- discovered plugin; `<id>` and `--all` are mutually exclusive.
1355
- `sm plugins doctor` now treats `disabled` as intentional (does not
1356
- contribute to the issue list, does not flip exit code).
1357
- - `src/cli/commands/plugins.ts` — adds `off` to the status icon legend
1358
- in human output (`off mock-a@0.1.0 · disabled by config_plugins or
1359
- settings.json`).
1360
- - `src/cli/commands/stubs.ts` — `PluginsEnableCommand` and
1361
- `PluginsDisableCommand` removed; replaced-at-step comment kept.
1362
- - `context/cli-reference.md` — regenerated; the two new verbs appear
1363
- with their flag tables.
1364
-
1365
- **Tests**:
1366
-
1367
- - `src/test/plugin-overrides.test.ts` — 8 unit tests covering storage
1368
- round-trip (upsert + read), `loadPluginOverrideMap` bulk read,
1369
- `deletePluginOverride` idempotency, resolver precedence (default ⇒
1370
- true, `settings.json` overrides default, DB overrides
1371
- `settings.json`), `makeEnabledResolver` currying, and PluginLoader
1372
- surfacing `disabled` status with manifest preserved + no extensions
1373
- - omitting the resolver still loads.
1374
- - `src/test/plugins-cli.test.ts` — 9 end-to-end tests via the binary:
1375
- `disable <id>` writes a DB row + `sm plugins list` reflects `off`,
1376
- `enable <id>` flips back, `--all` covers every discovered plugin,
1377
- unknown id → exit 5, no-arg → exit 2, both `<id>` and `--all` →
1378
- exit 2, `settings.json` baseline overridden by DB `enable`,
1379
- `settings.json` baseline applies when DB has no row, and
1380
- `sm plugins doctor` exits 0 when the only non-loaded plugin is
1381
- intentionally disabled.
71
+ ### Patch
1382
72
 
1383
- Test count: 273 291 (+18).
73
+ - **Config folder rename** `.skill-map.json` (single project-root file) → `.skill-map/settings.json` inside the canonical `.skill-map/` scope folder, with a sibling `.skill-map/settings.local.json` for per-machine overrides.
1384
74
 
1385
75
  ## 0.6.0
1386
76
 
1387
- ### Minor Changes
1388
-
1389
- - 9a89124: Step 5.1 — Persist scan-result metadata in a new `scan_meta` table so
1390
- `loadScanResult` returns real values for `scope` / `roots` / `scannedAt` /
1391
- `scannedBy` / `adapters` / `stats.filesWalked` / `stats.filesSkipped` /
1392
- `stats.durationMs` instead of the synthetic envelope shipped at Step 4.7.
1393
-
1394
- **Spec change (additive, minor)**:
1395
-
1396
- - New `scan_meta` table in zone `scan_*`, single-row (CHECK `id = 1`).
1397
- Columns: `scope`, `roots_json`, `scanned_at`, `scanned_by_name`,
1398
- `scanned_by_version`, `scanned_by_spec_version`, `adapters_json`,
1399
- `stats_files_walked`, `stats_files_skipped`, `stats_duration_ms`.
1400
- `nodesCount` / `linksCount` / `issuesCount` are not stored — they are
1401
- derived from `COUNT(*)` of the sibling tables.
1402
- - Replaced atomically with the rest of `scan_*` on every `sm scan`.
1403
-
1404
- **Runtime change**:
1405
-
1406
- - New kernel migration `002_scan_meta.sql`.
1407
- - `IScanMetaTable` added to `src/kernel/adapters/sqlite/schema.ts` and
1408
- bound in `IDatabase`.
1409
- - `persistScanResult` writes the row (and deletes prior rows in the same
1410
- transaction).
1411
- - `loadScanResult` reads from `scan_meta` when the row exists; degrades
1412
- to the previous synthetic envelope when it does not (DB freshly
1413
- migrated, never scanned, or pre-5.1 snapshot).
1414
- - The Step 4.7 follow-up notes in `scan-load.ts` documenting the
1415
- synthetic envelope are simplified to describe both branches.
1416
-
1417
- Test count: 151 → 154 (+3 covering meta round-trip, replace-all
1418
- single-row invariant, and synthetic-fallback on empty DB).
1419
-
1420
- - 9a89124: Step 5.7 — Conformance coverage for the rename heuristic.
1421
-
1422
- **Spec change (additive, minor)**:
1423
-
1424
- - `spec/schemas/conformance-case.schema.json` gains
1425
- `setup.priorScans: Array<{ fixture, flags? }>` — an ordered list of
1426
- staging scans the runner executes BEFORE the main `invoke`. Each
1427
- step replaces every non-`.skill-map/` directory in the scope with
1428
- the named fixture and runs `sm scan` (with optional flags). The DB
1429
- persists across steps because `.skill-map/` is preserved between
1430
- swaps. After the last step, the runner copies the top-level
1431
- `fixture` and runs the case's `invoke`.
1432
-
1433
- Required to express scenarios that need a prior snapshot (rename
1434
- heuristic, future incremental cases). The schema is purely
1435
- additive — every existing case keeps passing without modification.
1436
-
1437
- - Two new conformance cases under `spec/conformance/cases/`:
1438
-
1439
- - **`rename-high`** — moving a single file with identical body
1440
- triggers a high-confidence auto-rename. Asserts:
1441
- `stats.nodesCount === 1`, `stats.issuesCount === 0`,
1442
- `nodes[0].path === skills/bar.md`. Verifies the spec invariant
1443
- that high-confidence renames emit NO issue.
1444
- - **`orphan-detection`** — deleting a file with no replacement
1445
- emits exactly one `orphan` issue (severity `info`). Asserts the
1446
- `ruleId` and `severity` directly.
1447
-
1448
- - Four new fixture directories under `spec/conformance/fixtures/`:
1449
- `rename-high-before/`, `rename-high-after/`,
1450
- `orphan-before/`, `orphan-after/`.
1451
-
1452
- - `spec/conformance/coverage.md`: row I (Rename heuristic) flips
1453
- from `🔴 missing` to `🟢 covered`. Notes the medium / ambiguous
1454
- branches stay covered by `src/test/rename-heuristic.test.ts` for
1455
- now (assertion vocabulary in the schema is not rich enough to
1456
- express "the issues array contains an item with ruleId X and
1457
- data.confidence === 'medium'" — when the conformance schema gains
1458
- array-filter assertions, those branches can land here too).
1459
-
1460
- **Runtime change**:
1461
-
1462
- - `src/conformance/index.ts` runner: implements `setup.priorScans`.
1463
- Helper `replaceFixture(scope, specRoot, fixture)` clears every
1464
- top-level entry in the scope except `.skill-map/`, then copies the
1465
- named fixture on top. Used by both staging steps and the main
1466
- `fixture` phase.
1467
- - `src/test/conformance.test.ts`: includes the two new cases in the
1468
- Step-0b subset. Total conformance cases passing in CI: 1 → 3.
1469
-
1470
- **`spec/index.json`** regenerated (50 → 57 files). `npm run spec:check`
1471
- green.
1472
-
1473
- Test count: 201 → 203 (+2 conformance cases). The Step 5 totals close
1474
- at: 151 → 203 (+52 across 7 sub-steps).
1475
-
1476
- ### Patch Changes
1477
-
1478
- - dacd4d9: Move the auto-generated CLI reference from `docs/cli-reference.md` to
1479
- `context/cli-reference.md`. Spec change is editorial: `cli-contract.md`
1480
- references the file path in three spots (`--format md` description, the
1481
- NORMATIVE introspection section, and the "Related" link list); all three
1482
- updated to the new location. No schema or behavioural change.
1483
-
1484
- Reference impl: `scripts/build-cli-reference.mjs` writes to the new path,
1485
- the `cli:reference` / `cli:check` npm scripts point there, and `sm help`
1486
- output (which embeds the path in the `--format md` flag description) is
1487
- regenerated. The `docs/` folder is gone.
1488
-
1489
- ## 0.5.1
1490
-
1491
- ### Patch Changes
77
+ ### Minor
1492
78
 
1493
- - 18d758a: Editorial pass across spec/ and src/ docs: convert relative-path text references (e.g. `plugin-kv-api.md`, `schemas/node.schema.json`) to proper markdown links, so they resolve on GitHub and in renderers. No normative or behavioural changes prose, schemas, and CLI contract are unchanged.
79
+ - **Persisted scan-result metadata.** New `scan_meta` table backs `loadScanResult` so `scope` / `roots` / `scannedAt` / `scannedBy` / `adapters` / `stats.{filesWalked,filesSkipped,durationMs}` are real values instead of synthesised on read.
1494
80
 
1495
81
  ## 0.5.0
1496
82
 
1497
- ### Minor Changes
1498
-
1499
- - 69572fd: Align `spec/index.json` with the manifest changes declared in the `0.3.0` changelog (they had been documented but never written to the file), and fix two small referential drifts surfaced in the same audit pass.
1500
-
1501
- **`spec/index.json`** — closes the gap between what `0.3.0` notes promised and what actually shipped:
1502
-
1503
- - `specVersion` top-level field renamed to `indexPayloadVersion`. The old name collided semantically with `specPackageVersion` and with every other use of `specVersion` in the spec (compat logic, `scan-result.specVersion`, `sm help --format json`). `indexPayloadVersion` describes the shape of `index.json` itself and bumps only when this manifest's structure changes — pinned at `0.0.1` today. **This is the breaking rename already announced in the `0.3.0` release notes.**
1504
- - `schemas.topLevel` gains `history-stats` (shape for `sm history stats --json`, already referenced from `cli-contract.md` §History and hashed under `integrity.files`).
1505
- - New `schemas.extensions` subsection listing the 7 kind-manifest schemas (`base`, `adapter`, `detector`, `rule`, `action`, `audit`, `renderer`) — already required by `architecture.md` §Extension kinds for load-time manifest validation and already present under `schemas/extensions/`.
1506
-
1507
- **`spec/versioning.md` §Change process step 4** — the parenthetical `(see CLAUDE.md: "Every feature: update spec/ first, then src/")` was stale. `CLAUDE.md` has been a bare `@AGENTS.md` pointer since the 18d0c20 dedup; the rule itself lives in `AGENTS.md`. Reference fixed.
1508
-
1509
- **`spec/CHANGELOG.md` 0.3.0 entry** — text-only renumber of "decision #40a" → "decision #40". The sub-letter was a leftover from an unreleased draft; the roadmap Decision log uses `40` as the canonical anchor (see companion ROADMAP edit).
1510
-
1511
- Classification: minor per §Pre-1.0 (`0.Y.Z`). The `specVersion → indexPayloadVersion` rename is breaking for any consumer that read the old field, but the old name never shipped alongside a file that spelled it `indexPayloadVersion` — the rename is being applied here for the first time, not re-applied. The `topLevel`/`extensions` additions are purely additive.
1512
-
1513
- ### Patch Changes
1514
-
1515
- - 2699276: Fix the extension-kind schemas so they actually validate against real extension manifests.
1516
-
1517
- The six kind schemas (`schemas/extensions/action.schema.json`, `adapter.schema.json`, `audit.schema.json`, `detector.schema.json`, `renderer.schema.json`, `rule.schema.json`) used `additionalProperties: false` together with `allOf: [{ $ref: "base.schema.json" }]` — a classic JSON Schema Draft 2020-12 footgun. `additionalProperties` is evaluated independently per schema in an `allOf`, so when a consumer validated `{ id, kind, version, emitsLinkKinds, defaultConfidence }` against `detector.schema.json`, detector's `additionalProperties: false` rejected `id` / `version` / `description` (defined only on `base`) and base's own `additionalProperties: false` would have rejected `emitsLinkKinds` / `defaultConfidence` — the union of both closures is empty. No real extension could ever pass validation.
1518
-
1519
- Discovered during Step 1b while wiring the AJV validators in `skill-map` (kernel plugin loader). The right fix is `unevaluatedProperties: false` — it sees through `allOf` composition and only rejects keys that no sibling schema declared.
1520
-
1521
- Changes:
1522
-
1523
- - Every kind schema: `additionalProperties: false` → `unevaluatedProperties: false` at the manifest level. Nested `additionalProperties: false` declarations inside `$defs` / `properties` were likewise replaced with `unevaluatedProperties: false` where they participate in `allOf` composition (e.g. `action.schema.json#/$defs/Parameter`, `audit.schema.json` nested items).
1524
- - `extensions/base.schema.json`: closure removed entirely. Closed-content is now enforced only on the kind schemas, which see base's properties as "evaluated" through the `allOf` — adding closure to base too would force every kind to re-list every base key to stay valid.
1525
- - `base.schema.json` description updated to spell out the new composition rule so a future reader does not accidentally re-introduce the footgun.
83
+ ### Minor
1526
84
 
1527
- Classification: patch. No normative shape changes every manifest that was _supposed_ to pass under the old schemas still passes under the new ones, and the authored intent (closed content on kind manifests, additive base fields) is preserved. Consumers that never wired strict JSON Schema validation see zero behavioural change.
85
+ - **`spec/index.json` integrity sweep.** Reconciles `index.json` with the manifest changes documented in v0.3.0 but never written to the file. No prose / schema changes.
1528
86
 
1529
87
  ## 0.4.0
1530
88
 
1531
- ### Minor Changes
1532
-
1533
- - 334c51a: Document `--all` as targeted fan-out, not a global flag, in `spec/cli-contract.md`.
1534
-
1535
- `--all` is valid only on verbs whose contract explicitly lists it:
1536
-
1537
- - `sm plugins enable <id> | --all` and `sm plugins disable <id> | --all`.
1538
- - `sm job cancel <job.id> | --all` (cancels every `queued` and `running` job).
1539
- - `sm job submit <action> --all` and `sm job run --all`.
1540
-
1541
- Unsupported `--all` usage is an operational error (exit `2`), the same as any other unknown or invalid flag.
1542
-
1543
- Classification: minor — targeted fan-out semantics are additive for the listed verbs, while avoiding a global flag contract.
1544
-
1545
- - 3e89d8f: Audit-driven alignment pass. Multiple normative additions and a casing cleanup:
1546
-
1547
- - **Extension schemas**: add `spec/schemas/extensions/{base,adapter,detector,rule,action,audit,renderer}.schema.json` (7 new files). `architecture.md` §Extension kinds now points to them and mandates manifest validation at load time. Unblocks the "contract tests for the 6 kinds" invariant.
1548
- - **Adapter `defaultRefreshAction`**: normatively required on every `Adapter` extension. Maps node `kind` → `actionId` and drives the UI's `🧠 prob` button. Previously mentioned only in ROADMAP (Decision #45); now part of the schema.
1549
- - **Triple protection for mode B**: `db-schema.md` now specifies the exact order — parse → DDL validation → prefix injection → scoped connection. Validation runs **before** the rewrite so kernel-table references are caught under their authored names.
1550
- - **Automatic rename heuristic**: new `db-schema.md` §Rename detection. On scan, `body_hash` match → high-confidence auto-rename with `state_*` FK migration; `frontmatter_hash` match → medium-confidence, same migration + `auto-rename-medium` issue; no match → orphan with issue. Replaces the prior "scan emits orphans, user runs `sm orphans reconcile` manually" flow.
1551
- - **Skill agent envelope**: `job-events.md` now mandates a synthetic `r-ext-<ts>-<hex>` run envelope (`run.started mode=external` → `job.claimed` → `job.callback.received` → `job.completed|failed` → `run.summary`) around jobs claimed by a Skill agent without entering `sm job run`. Keeps the WebSocket broadcaster contract ("every job event inside a run envelope") intact across both runner paths.
1552
- - **"Skill runner" → "Skill agent"**: `architecture.md` and `job-lifecycle.md` clarify that the Skill path is a peer driving adapter (alongside CLI and Server), NOT a `RunnerPort` implementation. Only `ClaudeCliRunner` and its test fake implement the port. Name was misleading; structure unchanged.
1553
- - **Casing**: `db-schema.md` `auto_migrate` → `autoMigrate`; `README.md` prose mention `spec-compat` → `specCompat`. Brings prose into sync with the camelCase rule already enforced by the schemas.
1554
- - **Coverage matrix**: new `spec/conformance/coverage.md` tracks each schema (and each non-schema normative artifact) against its conformance case. 28 schemas + 11 artifact invariants catalogued; 19 schemas and 10 artifacts flagged as missing, each with a step-blocker note. Release gate: v1.0.0 requires every row 🟢 or explicitly deferred.
1555
-
1556
- Classification: minor per §Pre-1.0 (`0.Y.Z`). The new required field `defaultRefreshAction` on the Adapter kind is technically breaking — no conforming Adapter ships in the reference impl yet, so the impact is zero. Post-1.0 the same change would be major.
1557
-
1558
- ### Patch Changes
1559
-
1560
- - 93ffe34: Editorial pass: remove "MVP" terminology from four prose documents.
1561
-
1562
- The project shipped two competing readings of "MVP" — sometimes "`v0.5.0`", sometimes "the whole product through `v1.0`". That drift produced contradictions in companion docs (e.g. the summarizer pattern: was `v0.8.0` or `v0.5.0` supposed to ship them?). To close the ambiguity once, `ROADMAP.md` and `AGENTS.md` standardised on explicit versioned releases and `post-v1.0` in the same audit window. This change brings the four spec prose touches that still said "MVP" into the same vocabulary.
1563
-
1564
- - **`cli-contract.md` §Jobs**: `sm job run --all` description `(MVP: sequential)` → `(sequential through v1.0; in-runner parallelism deferred)`.
1565
- - **`job-events.md` §Event catalog**: `(post-MVP)` parallel-run note → `(deferred to post-v1.0)`.
1566
- - **`job-lifecycle.md` §Concurrency**: `MVP (v0.x): one job at a time.` → `Through v1.0 (spec v0.x): one job at a time.`
1567
- - **`plugin-kv-api.md` §Backup and retention**: `sm plugins forget <id> (post-MVP)` → `sm plugins forget <id> (deferred to post-v1.0)`.
89
+ ### Minor
1568
90
 
1569
- Classification: patch. Editorial only no schema, exit code, verb signature, or MUST/SHOULD statement changes meaning. All four replacements preserve the technical content; only the label changes from project-scoped ("MVP") to version-scoped (`v1.0`), which is the convention the rest of the spec already uses. Integrity block regenerated.
91
+ - **`--all` documented as targeted fan-out** in `cli-contract.md`. Valid only on verbs whose contract explicitly lists it.
1570
92
 
1571
93
  ## 0.3.0
1572
94
 
1573
- ### Minor Changes
1574
-
1575
- - 334c51a: Promote `--all` to a normative universal flag in `spec/cli-contract.md §Global flags`.
1576
-
1577
- Any verb that accepts a target identifier (`-n <node.path>`, `<job.id>`, `<plugin.id>`) MUST accept `--all` as "apply to every eligible target matching the verb's preconditions". Mutually exclusive with a positional target or `-n <path>` on the same invocation. Verbs that inherently target everything (`sm scan` without `-n`, `sm list`, `sm check`, `sm doctor`) accept the flag as a no-op for script-composition uniformity. Verbs where fan-out is nonsensical (`sm record`, `sm init`, `sm version`, `sm help`, `sm config get/set/reset/show`, `sm db *`, `sm serve`) MUST reject `--all` with exit `2`.
1578
-
1579
- Concretely extended in this pass:
1580
-
1581
- - `sm plugins enable <id> | --all` and `sm plugins disable <id> | --all`.
1582
- - `sm job cancel <job.id> | --all` (cancels every `queued` and `running` job).
1583
-
1584
- Already normative before this change: `sm job submit <action> --all` and `sm job run --all`.
1585
-
1586
- Classification: minor — new global flag semantics, backward compatible (existing invocations without `--all` behave identically). ROADMAP Decision #60 stays as the canonical narrative; this changeset brings the spec into line with it.
1587
-
1588
- - 3e89d8f: Audit-driven alignment pass. Multiple normative additions and a casing cleanup:
1589
-
1590
- - **Extension schemas**: add `spec/schemas/extensions/{base,adapter,detector,rule,action,audit,renderer}.schema.json` (7 new files). `architecture.md` §Extension kinds now points to them and mandates manifest validation at load time. Unblocks the "contract tests for the 6 kinds" invariant.
1591
- - **Adapter `defaultRefreshAction`**: normatively required on every `Adapter` extension. Maps node `kind` → `actionId` and drives the UI's `🧠 prob` button. Previously mentioned only in ROADMAP (Decision #45); now part of the schema.
1592
- - **Triple protection for mode B**: `db-schema.md` now specifies the exact order — parse → DDL validation → prefix injection → scoped connection. Validation runs **before** the rewrite so kernel-table references are caught under their authored names.
1593
- - **Automatic rename heuristic**: new `db-schema.md` §Rename detection. On scan, `body_hash` match → high-confidence auto-rename with `state_*` FK migration; `frontmatter_hash` match → medium-confidence, same migration + `auto-rename-medium` issue; no match → orphan with issue. Replaces the prior "scan emits orphans, user runs `sm orphans reconcile` manually" flow.
1594
- - **Skill agent envelope**: `job-events.md` now mandates a synthetic `r-ext-<ts>-<hex>` run envelope (`run.started mode=external` → `job.claimed` → `job.callback.received` → `job.completed|failed` → `run.summary`) around jobs claimed by a Skill agent without entering `sm job run`. Keeps the WebSocket broadcaster contract ("every job event inside a run envelope") intact across both runner paths.
1595
- - **"Skill runner" → "Skill agent"**: `architecture.md` and `job-lifecycle.md` clarify that the Skill path is a peer driving adapter (alongside CLI and Server), NOT a `RunnerPort` implementation. Only `ClaudeCliRunner` and its test fake implement the port. Name was misleading; structure unchanged.
1596
- - **Casing**: `db-schema.md` `auto_migrate` → `autoMigrate`; `README.md` prose mention `spec-compat` → `specCompat`. Brings prose into sync with the camelCase rule already enforced by the schemas.
1597
- - **Coverage matrix**: new `spec/conformance/coverage.md` tracks each schema (and each non-schema normative artifact) against its conformance case. 28 schemas + 11 artifact invariants catalogued; 19 schemas and 10 artifacts flagged as missing, each with a step-blocker note. Release gate: v1.0.0 cut requires every row 🟢 or explicitly deferred.
1598
-
1599
- Classification: minor per §Pre-1.0 (`0.Y.Z`). The new required field `defaultRefreshAction` on the Adapter kind is technically breaking — no conforming Adapter ships in the reference impl yet, so the impact is zero. Post-1.0 the same change would be major.
1600
-
1601
- - d41b9ae: Close two gaps surfaced in the audit pass: config keys that `ROADMAP.md` promised but `project-config.schema.json` did not declare, and WebSocket event families that `ROADMAP.md §UI` mentioned ("scan updates + issue changes") but `job-events.md` did not cover.
1602
-
1603
- **`project-config.schema.json` — new optional fields, all non-breaking:**
1604
-
1605
- - `autoMigrate: boolean` (default `true`) — auto-apply pending kernel + plugin migrations at startup after auto-backup. `false` → startup fails fast if migrations are pending.
1606
- - `tokenizer: string` (default `cl100k_base`) — name of the offline tokenizer; stored alongside counts so consumers know which encoder produced them.
1607
- - `scan.maxFileSizeBytes: integer` (default `1048576`) — files larger are skipped with an `info` log.
1608
- - `jobs.ttlSeconds: integer` (default `3600`) — global fallback TTL when an action manifest omits `expectedDurationSeconds` (typically `mode: local` actions where the field is advisory).
1609
- - `jobs.perActionPriority: { <actionId>: integer }` — per-action priority overrides. Frozen on `state_jobs.priority` at submit time; overrides action manifest `defaultPriority`; overridden by CLI `--priority`. Ratifies decision #40 in the schema.
1610
- - `jobs.retention: { completed, failed }` — GC policy for `state_jobs` rows. Defaults: `completed = 2592000` (30 days), `failed = null` (never auto-prune; keep for post-mortem). `sm job prune` reads these; no implicit pruning during normal verbs.
1611
-
1612
- **`job-events.md` — new `Non-job events` section, Stability: experimental across v0.x:**
1613
-
1614
- - `scan.*`: `scan.started`, `scan.progress` (throttled ≥250 ms), `scan.completed`.
1615
- - `issue.*`: `issue.added`, `issue.resolved` — emitted after `scan.completed` when the new scan's issue set differs from the previous one. Diff key: `(ruleId, nodeIds sorted, message)`.
1616
- - Synthetic run ids follow the existing `r-<mode>-YYYYMMDD-HHMMSS-XXXX` pattern (`r-scan-...`, `r-check-...`) alongside `r-ext-...` for external Skill claims.
1617
-
1618
- These families ship at Step 13 of the reference impl alongside the WebSocket broadcaster. Marking them experimental keeps the shape mutable until real UI consumers exercise the stream; promotion to `stable` is a later minor bump.
1619
-
1620
- Classification: minor per §Pre-1.0. All additions are optional fields in a permissive config schema and new event types outside the stable job family — zero impact on existing implementations. Matching `ROADMAP.md` §Notable config keys and §Progress events updates land in the same change.
1621
-
1622
- - d41b9ae: Align the frontmatter tools story with Claude Code's own conventions (the audit pass surfaced that the spec had `tools` on agent only and no equivalent for skills, while `ROADMAP.md` decision #55 referenced a non-existent `expected-tools` field).
1623
-
1624
- **`spec/schemas/frontmatter/base.schema.json` — two new top-level optional fields:**
1625
-
1626
- - `tools: string[]` — **allowlist**. When present, the host MUST restrict the node to exactly these tools. Matches Claude Code's subagent `tools` frontmatter. Kind-specific interpretation: an `agent` uses it to lock the spawned subagent; a `skill` uses it as a declarative hint (skills typically inherit their parent's tools, but the field is carried for parity and discovery); other kinds use it as information only.
1627
- - `allowedTools: string[]` — **pre-approval**. Tools the host MAY use without per-use permission prompts while the node is active. Distinct from `tools`: every other tool remains callable, governed by the host's normal permission settings. Matches Claude Code's skill `allowed-tools` frontmatter. Accepts argument-scoped patterns where the host supports them (e.g. `Bash(git add *)`).
1628
-
1629
- **`spec/schemas/frontmatter/agent.schema.json`:** `tools` removed from the kind-specific body because it now lives on `base` and is inherited via `allOf`. The agent schema's title/description updated to reflect that only `model` remains kind-specific. Consumers reading `tools` from an agent frontmatter see no behavioural change — the field is still there, just sourced from `base`.
1630
-
1631
- `expectedTools` on `extensions/action.schema.json` is unchanged. That field is a hint from an action template to the runner (which tools the rendered prompt expects access to) — a distinct semantics from the node-level `tools` / `allowedTools` pair, and the name difference preserves the distinction.
1632
-
1633
- Classification: minor per §Pre-1.0. Additions to `base` are optional fields in a permissive schema (no break for existing frontmatter). Removing `tools` from the agent schema's own properties is compatible because `allOf: [base]` continues to supply it — any document that validated before still validates, any document that used `additionalProperties: true` is unaffected. Matching `ROADMAP.md` updates (§Frontmatter standard, decision #55) land in the same change.
1634
-
1635
- - 5935948: Add `sm history stats` schema and normative elapsed-time reporting.
1636
-
1637
- - **New schema** `spec/schemas/history-stats.schema.json`. Shape for `sm history stats --json`: `range` (configurable via `--since` / `--until`), `totals`, `tokensPerAction[]`, `executionsPerPeriod[]` (granularity via `--period day|week|month`, default `month`), `topNodes[]` (length via `--top N`, default 10), `errorRates` (global + per-action + per failure reason — all failure-reason enum values always present with `0` when unseen for predictable dashboards), and top-level `elapsedMs`. Duration stats in `tokensPerAction[]`: `durationMsMean` + `durationMsMedian` for MVP; percentiles deferred to a later minor bump.
1638
- - **cli-contract.md §Elapsed time** (new normative section). Every verb that does non-trivial work MUST report its own wall-clock:
1639
- - **Pretty (stderr)**: last line `done in <formatted>` where `<formatted>` ∈ `{ <N>ms | <N.N>s | <M>m <S>s }`. Suppressed by `--quiet`.
1640
- - **JSON stdout**: top-level `elapsedMs` when the shape is an object; schemas whose shape is an array or ndjson don't carry it (stderr is the sole carrier).
1641
- - **Exempt** verbs (sub-millisecond, informational): `sm --version`, `sm --help`, `sm version`, `sm help`, `sm config get`, `sm config list`, `sm config show`.
1642
- - Measurement spans from after arg-parsing to before terminal write.
1643
- - **cli-contract.md** `sm history stats` entry: flags enumerated (`--since`, `--until`, `--period`, `--top`) and schema referenced.
1644
- - **Coverage matrix**: row `29` for `history-stats.schema.json` (blocked by Step 5); artifact row `L` for the elapsed-time reporting invariant (blocked by Step 4).
1645
-
1646
- Classification: minor per §Pre-1.0. The elapsed-time contract introduces a SHOULD-emit line that didn't exist before — no existing consumer breaks, and the line goes to stderr where it doesn't clash with stdout JSON.
1647
-
1648
- - 1455cb1: Normative `priority` for jobs.
1649
-
1650
- The `state_jobs.priority` column (INTEGER, default `0`) existed in the schema and was used by the atomic-claim SQL (`ORDER BY priority DESC, createdAt ASC`), but no surface let the user set it. This release closes the gap:
1651
-
1652
- - **`cli-contract.md` §Jobs**: new flag `sm job submit ... --priority <n>`. Integer; higher runs first; default `0`; negatives permitted (deprioritize).
1653
- - **`job-lifecycle.md` §Submit**: new step 6 resolving priority with precedence `action manifest defaultPriority → user config jobs.perActionPriority.<actionId> → flag`. The resolved value is frozen on submit and immutable for the life of the job. Ties in the claim order break by `createdAt ASC`.
1654
- - Configuration key `jobs.perActionPriority.<actionId>`: optional per-action integer override.
1655
- - Action manifest `defaultPriority`: optional integer; defaults to `0` when omitted.
1656
-
1657
- Classification: minor per `cli-contract.md` §Stability ("adding a flag is a minor bump"). No existing consumer breaks: jobs submitted before this release default to `0`, which is the identity element of the ordering. The claim SQL already read `priority`, so the wire protocol is unchanged.
1658
-
1659
- - 1455cb1: Manifest alignment pass on `spec/index.json`: expose already-normative schemas, rename the payload-shape field, and add a stable version field consumers can rely on.
1660
-
1661
- - **Rename `specVersion` → `indexPayloadVersion`** (breaking). The old name collided semantically with every other use of `specVersion` (compat logic in `versioning.md`, `scan-result.specVersion`, `sm help --format json`). The field describes the shape of `index.json` itself, not the spec a caller implements.
1662
- - **New `specPackageVersion`** top-level field, auto-populated by `scripts/build-spec-index.mjs` from `spec/package.json.version`. This is the source of truth for "which `@skill-map/spec` release is this", previously missing from the manifest — consumers had to read `package.json` separately, and `sm version` was incorrectly reporting the payload-shape version as the spec version.
1663
- - **`schemas.topLevel`** gains `history-stats` (shape for `sm history stats --json`, already referenced in `cli-contract.md` §History).
1664
- - **New `schemas.extensions` subsection** lists the 7 kind-manifest schemas (`base`, `adapter`, `detector`, `rule`, `action`, `audit`, `renderer`) already required by `architecture.md` §Extension kinds for load-time manifest validation.
1665
- - **CHANGELOG fix** on the `[Unreleased]` v0.1.0 line: "10 event types" → "11 canonical event types plus one synthetic `emitter.error`". Text-only correction on a shipped release.
1666
- - **README example** updated to show both fields side-by-side so the distinction is obvious to first-time consumers.
1667
- - **Integrity block** regenerated.
1668
-
1669
- No schema contents change. The schema files and their normative status are unchanged since 0.1.0; the index now enumerates them all and uses unambiguous field names.
1670
-
1671
- **Migration for consumers**: any caller that reads `specIndex.specVersion` MUST switch to `specIndex.specPackageVersion` (for the release) or `specIndex.indexPayloadVersion` (for the manifest shape). The rename is the source of the `minor` bump rather than `patch` — pre-1.0 minors MAY contain breaking changes per `versioning.md` §Pre-1.0.
1672
-
1673
- Classification: minor per §Pre-1.0. One breaking rename + two additive fields + two additive schema subsections. The reference impl's `sm version` is updated in the same release to read `specPackageVersion`, so `sm version` now reports the actual npm package version (was the payload-shape version, a latent bug).
1674
-
1675
- - 1455cb1: New CLI verb `sm orphans undo-rename <new.path> [--force]` to reverse a medium-confidence auto-rename.
1676
-
1677
- The scan's rename heuristic (added in the previous spec release) migrates `state_*` FKs automatically when a deleted path and a newly-seen path share the same `frontmatter_hash` ("medium" confidence, body differs) and emits an `auto-rename-medium` issue for the user to verify. Until now the spec said "revert via `sm orphans reconcile --to <old.path>`", but `sm orphans reconcile` is defined for the forward direction (orphan path → live node) and awkward for the reverse case where both paths exist.
1678
-
1679
- This release closes the gap with a dedicated reverse verb:
1680
-
1681
- - **`cli-contract.md` §Browse**: new row `sm orphans undo-rename <new.path> [--force]`. Requires an active `auto-rename-medium` or `auto-rename-ambiguous` issue targeting `<new.path>`. Reads the prior path from `issue.data_json.from`, migrates `state_*` FKs back, resolves the issue. Exit `5` if no matching active issue.
1682
- - **`db-schema.md` §Rename detection**: issue payload now normative.
1683
- - `auto-rename-medium.data_json` MUST include `{ from, to, confidence: "medium" }`.
1684
- - `auto-rename-ambiguous.data_json` MUST include `{ to, candidates: [from_a, from_b, ...] }`. `sm orphans undo-rename` requires `--from <old.path>` to pick one.
1685
- - **Destructive verb**: prompts for confirmation unless `--force`. After undo, the prior path becomes an `orphan` (file no longer exists), emitting the normal `orphan` issue on next scan.
1686
-
1687
- Rationale: dedicated name makes intent clear (forward = reconcile, reverse = undo-rename), failure is early (no active issue → immediate exit 5 with a helpful message), and the user does not re-type paths the kernel already knows.
1688
-
1689
- Classification: minor per `cli-contract.md` §Stability ("adding a verb is a minor bump"). No existing behavior changes; `sm orphans reconcile` semantics are unaffected.
1690
-
1691
- - 334c51a: **Breaking**: rename two state-zone tables to comply with the normative plural rule in `db-schema.md §Naming conventions`.
1692
-
1693
- - `state_enrichment` → `state_enrichments`
1694
- - `state_plugin_kv` → `state_plugin_kvs`
1695
-
1696
- Index names renamed in lockstep:
1697
-
1698
- - `ix_state_enrichment_stale_after` → `ix_state_enrichments_stale_after`
1699
- - `ix_state_plugin_kv_plugin_id` → `ix_state_plugin_kvs_plugin_id`
1700
-
1701
- The two tables were the only kernel-owned state-zone tables violating the rule "Tables: `snake_case`, plural" — every other catalog entry (`state_jobs`, `state_executions`, `state_summaries`, `config_plugins`, `config_preferences`, `config_schema_versions`, `scan_nodes`, `scan_links`, `scan_issues`) was already plural. The exceptions were historical drift, not intentional.
1702
-
1703
- Updated spec artefacts:
1704
-
1705
- - `spec/db-schema.md` — table section headings, column comments, primary-key footers, index names, and the cross-reference list in §Rename heuristic.
1706
- - `spec/cli-contract.md` — `sm db reset --state` row in §Database.
1707
- - `spec/plugin-kv-api.md` — §Overview opener and every downstream reference.
1708
- - `spec/schemas/plugins-registry.schema.json` — description of the `kv` mode `const`.
1709
-
1710
- **Migration for implementations**: no reference implementation has shipped the SQLite adapter yet (Step 1a lands it), so this is a rename-on-paper change. Any future kernel migration that creates these tables MUST use the plural names. Any third-party implementation already experimenting with the spec against the old names MUST rename before targeting `@skill-map/spec ≥ 0.3.0`.
1711
-
1712
- Classification: **minor with breaking change**, per `spec/versioning.md §Pre-1.0` which allows breaking changes on minor bumps while the spec is `0.y.z`. Reference-impl touch: `src/kernel/ports/plugin-loader.ts` comment updated; no code paths read these names at runtime yet.
1713
-
1714
- Companion prose updates in `ROADMAP.md` (§Persistence, §Plugin system, §Enrichment, §Summarizer pattern, Decision #61) and `AGENTS.md` (§Persistence).
1715
-
1716
- - 93ffe34: Clean up `history.*` in `spec/schemas/project-config.schema.json`.
1717
-
1718
- **Breaking (pre-1.0 minor per `versioning.md` §Pre-1.0):**
1719
-
1720
- - **Remove** `history.retentionDays`. The field promised execution-record GC, but `ROADMAP.md` §Step 7 and the job-retention section make it explicit that `state_executions` is append-only in `v0.1` and that the kernel does not use this key. Declaring a config key whose behaviour is "silently ignored" is worse than not declaring it — consumers would wire it in and never see an effect. The field will be re-introduced in a later minor bump when the GC path actually lands, with a concrete default and enforcement semantics.
1721
-
1722
- **Editorial:**
1723
-
1724
- - `history.share.description` mentioned `./.skill-map/history.json` — an artefact of the pre-SQLite architecture. The actual DB is `./.skill-map/skill-map.db` (see `db-schema.md` §Scope and location). Description corrected; field itself unchanged.
1725
-
1726
- Classification: minor per §Pre-1.0 (`0.Y.Z` may contain breaking changes in a minor bump). Integrity block regenerated via `npm run spec:index`. Companion prose in `ROADMAP.md §Notable config keys` updated in the same change.
1727
-
1728
- **Migration for consumers**: any `.skill-map.json` that set `history.retentionDays` will now fail schema validation (`additionalProperties: false` on `history`). Remove the key; no kernel behaviour changes because nothing was consuming it.
1729
-
1730
- - 93ffe34: Promote the trigger-normalization pipeline (Decision #21) from implicit to normative in `spec/architecture.md`.
1731
-
1732
- Before this change, `link.trigger` carried `originalTrigger` and `normalizedTrigger` fields (defined in `schemas/link.schema.json`), and the `trigger-collision` rule keyed on the normalized value — but no spec prose documented **how** to normalize. The pipeline lived only in `AGENTS.md §Decisions already locked` and in `ROADMAP.md` as a one-line Step 7 bullet. That left implementations free to diverge, which silently breaks the `trigger-collision` rule across implementations (two conforming CLIs could disagree on whether `hacer-review` and `Hacer Review` collide).
1733
-
1734
- Added under `architecture.md §Extension kinds`, paralleling the existing `Adapter · defaultRefreshAction` subsection:
1735
-
1736
- - **Detector · trigger normalization** — field contract, normative 6-step pipeline, and 8 worked examples.
1737
-
1738
- Pipeline (applied in exactly this order):
1739
-
1740
- 1. Unicode NFD.
1741
- 2. Strip Unicode `Mn` (diacritics).
1742
- 3. Lowercase (locale-independent).
1743
- 4. Separator unification: hyphen / underscore / any whitespace run → single ASCII space.
1744
- 5. Collapse whitespace (run of ≥2 spaces → 1 space).
1745
- 6. Trim leading/trailing whitespace.
1746
-
1747
- Non-letter / non-digit characters outside the separator set (`/`, `@`, `:`, `.`, etc.) are **preserved** — stripping them is the detector's concern, not the normalizer's. This keeps namespaced invocations (`/skill-map:explore`, `@my-plugin/foo`) comparable in their intended form.
1748
-
1749
- §Stability in `architecture.md` updated: adding a new step at the end is a minor bump; reordering, removing, or changing any existing step (including the character classes in step 4) is a major bump. Implementations that produce different `normalizedTrigger` output for equivalent input are non-conforming.
1750
-
1751
- Classification: minor. The pipeline was always the intent (Decision #21 existed since the 2026-04-19 session) and `schemas/link.schema.json` already carried the fields, but this is the first time the spec prose binds implementations to a specific algorithm. A strict v0 implementation that did not normalize (or normalized differently) would begin failing conformance at the next spec release; worth a minor bump so plugin authors and alternative impls see it in the changelog.
1752
-
1753
- Companion prose in `ROADMAP.md §Trigger normalization` (Decision #21 now points here for full rationale + examples).
1754
-
1755
- ### Patch Changes
1756
-
1757
- - 334c51a: Clarify `sm orphans undo-rename` signature in `spec/cli-contract.md §Browse` by surfacing the `[--from <old.path>]` flag in the command cell itself.
1758
-
1759
- The flag was already documented prose-only in `spec/db-schema.md §Rename heuristic` ("`auto-rename-ambiguous` issues ... `sm orphans undo-rename` requires the user to pass `--from <old.path>` to disambiguate") but was absent from the signature in the `cli-contract.md` table. A reader consulting only the CLI contract would miss the flag and assume the command took `<new.path>` alone.
1760
-
1761
- The row now:
1762
-
1763
- - Shows `[--from <old.path>] [--force]` in the signature.
1764
- - Explicitly distinguishes the `auto-rename-medium` case (omit `--from`, previous path read from `issue.data_json`) from `auto-rename-ambiguous` (REQUIRES `--from` to pick from `data_json.candidates`).
1765
- - Adds an exit-`5` condition for `--from` referencing a path not in `candidates`.
1766
-
1767
- No behavioural change — the flag was already normative and implementations were already expected to support it. Classification: patch (clarifying drift between two spec prose docs, not a new capability).
1768
-
1769
- - 93ffe34: Split `sm db reset` into three explicit levels of destruction, each with distinct semantics.
1770
-
1771
- Before: `sm db reset` dropped BOTH `scan_*` and `state_*` in one command — so a user who wanted "please rescan from scratch" would wipe their job history, summaries, enrichment, and plugin KV data. The "reset" name suggested a soft operation; the behavior was aggressive.
1772
-
1773
- After:
1774
-
1775
- - `sm db reset` — drops `scan_*` only. Keeps `state_*` and `config_*`. Non-destructive, no prompt. Equivalent to asking for a fresh scan.
1776
- - `sm db reset --state` — also drops `state_*` and every `plugin_<normalized_id>_*` table (mode B) plus `state_plugin_kvs` (mode A). Keeps `config_*`. Destructive; requires confirmation unless `--yes` (or `--force`, kept as an alias).
1777
- - `sm db reset --hard` — deletes the DB file entirely. Keeps the plugins folder on disk. Destructive; requires confirmation unless `--yes`.
1778
-
1779
- Updated files:
1780
-
1781
- - `spec/cli-contract.md` §Database — new table rows and a rewritten confirmation paragraph.
1782
- - `spec/db-schema.md` §Zones — one-liner rewritten to list all three levels.
1783
- - `spec/plugin-kv-api.md` §Scope and lifecycle — three bullets replacing the single prior bullet, explicit about which reset level touches plugin storage.
1784
-
1785
- Classification: patch in intent but **behavior-changing for `sm db reset` without modifier**. Implementations of `v0.x` that currently drop `state_*` on `sm db reset` MUST narrow the behavior; users relying on the old "reset = wipe everything below config" workflow must switch to `sm db reset --state`. Classified as patch because the spec is pre-1.0 and no implementation has shipped the CLI yet (Step 1a lands storage + the `sm db *` verbs together — this is the first time the boundary is normative in code).
1786
-
1787
- Companion prose updates in `ROADMAP.md` §DB management commands and §Step 1a acceptance list.
1788
-
1789
- - 93ffe34: Editorial pass: remove "MVP" terminology from four prose documents.
1790
-
1791
- The project shipped two competing readings of "MVP" — sometimes "CUT 1 / `v0.5.0`", sometimes "the whole product through `v1.0`". That drift produced contradictions in companion docs (e.g. the summarizer pattern: was `v0.8.0` or `v0.5.0` supposed to ship them?). To close the ambiguity once, `ROADMAP.md` and `AGENTS.md` standardised on `CUT 1` / `CUT 2` / `CUT 3` and `post-v1.0` in the same audit window. This change brings the four spec prose touches that still said "MVP" into the same vocabulary.
1792
-
1793
- - **`cli-contract.md` §Jobs**: `sm job run --all` description `(MVP: sequential)` → `(sequential through v1.0; in-runner parallelism deferred)`.
1794
- - **`job-events.md` §Event catalog**: `(post-MVP)` parallel-run note → `(deferred to post-v1.0)`.
1795
- - **`job-lifecycle.md` §Concurrency**: `MVP (v0.x): one job at a time.` → `Through v1.0 (spec v0.x): one job at a time.`
1796
- - **`plugin-kv-api.md` §Backup and retention**: `sm plugins forget <id> (post-MVP)` → `sm plugins forget <id> (deferred to post-v1.0)`.
1797
-
1798
- Classification: patch. Editorial only — no schema, exit code, verb signature, or MUST/SHOULD statement changes meaning. All four replacements preserve the technical content; only the label changes from project-scoped ("MVP") to version-scoped (`v1.0`), which is the convention the rest of the spec already uses. Integrity block regenerated.
1799
-
1800
- - 93ffe34: Refresh the `spec/README.md` §Repo layout tree so it matches reality.
1801
-
1802
- The previous tree was frozen at the Step 0a snapshot and listed only 20 schemas (9 top-level + 6 frontmatter + 5 summaries) plus outdated `(Step 0a phase N)` annotations. The actual spec ships 29 schemas (11 top-level + 7 extension + 6 frontmatter + 5 summaries) and the package adds `index.json` and `package.json`.
1803
-
1804
- Changes:
1805
-
1806
- - Show the full set of 29 JSON Schemas with a brace grouping per bucket, making the counts and the `allOf` inheritance (frontmatter kinds → base; summaries → report-base) legible at a glance.
1807
- - Add the missing top-level schemas `conformance-case.schema.json` and `history-stats.schema.json`.
1808
- - Add the whole `schemas/extensions/` folder (base + one per extension kind) — validated at plugin load.
1809
- - List `package.json` and `index.json` explicitly so external readers know they are published assets.
1810
- - Drop `(Step 0a phase N)` annotations — Step 0a is complete, the marker is noise.
1811
- - Under `conformance/cases/`, note `basic-scan` and `kernel-empty-boot` as the two shipped cases and point at `../ROADMAP.md` for the deferred `preamble-bitwise-match` case.
1812
- - Under `interfaces/`, clarify that `security-scanner.md` is a convention over the Action kind, NOT a 7th extension kind — the six kinds remain locked.
1813
-
1814
- Classification: patch. Editorial prose only — no normative schema, rule, or contract changes. Companion updates to `ROADMAP.md` (repo layout + package layout) ship alongside; they are outside the spec package and do not need a changeset.
1815
-
1816
- - d41b9ae: Promote the casing rule from implicit (stated only in `CHANGELOG.md` §Conventions locked and in individual schema descriptions) to explicit, with a new **Naming conventions** section in `spec/README.md`. Two rules, both normative:
1817
-
1818
- - **Filesystem artefacts in kebab-case**: every file, directory, enum value, and `issue.ruleId` value. Values stay URL/filename/log-key safe without escaping.
1819
- - **JSON content in camelCase**: every key in schemas, frontmatter, configs, manifests, job records, reports, event payloads, API responses. The SQL layer (`snake_case`) is the sole exception, bridged by the storage adapter.
1820
-
1821
- Companion alignment in `spec/db-schema.md` §Rename detection: the prose mixed column names (`body_hash`, `frontmatter_hash`, `rule_id`, `data_json`) with domain-object references. The heuristic is specified against the domain types (`bodyHash`, `frontmatterHash`, `ruleId`, `data`) as defined in `node.schema.json` / `issue.schema.json`; the SQLite columns are the storage shape, not the contract. Added a one-line casing note that points back to §Naming conventions so the bridge is explicit.
1822
-
1823
- Classification: patch. The rule itself is unchanged — it was already enforced by every shipped schema and repeated in `CHANGELOG.md`. The additions are purely documentary so new implementers find the rule without digging through the changelog, and so the rename-detection prose stops looking like it references SQLite-specific identifiers when it means domain-object fields.
1824
-
1825
- - 93ffe34: Clarify the TTL resolution procedure in `spec/job-lifecycle.md`.
1826
-
1827
- The previous text defined the formula as `ttlSeconds = max(expectedDurationSeconds × graceMultiplier, minimumTtlSeconds)` and said the precedence chain was `global default → manifest → user config → flag`. Two problems:
1828
-
1829
- - When `expectedDurationSeconds` is absent from the manifest (typical for `mode: local` actions), the formula is undefined. The existing config key `jobs.ttlSeconds` was documented elsewhere as a "global fallback" but never tied into the formula.
1830
- - The word "precedence" collapsed three distinct mechanisms — base value selection, formula application, and full override — into one list, so `minimumTtlSeconds` (a floor, never a default) appeared as the first entry of a "later wins" chain.
1831
-
1832
- This patch rewrites the §TTL precedence section as §TTL resolution, split into three explicit steps:
1833
-
1834
- 1. **Base duration**: manifest `expectedDurationSeconds` OR config `jobs.ttlSeconds` (default `3600`).
1835
- 2. **Computed TTL**: `max(base × graceMultiplier, minimumTtlSeconds)`.
1836
- 3. **Overrides** (later wins, skips formula): `jobs.perActionTtl.<actionId>`, then `--ttl` flag.
1837
-
1838
- Five worked examples added. Negative / zero overrides are rejected at submit time (exit 2). A Stability note states the procedure is locked going forward — new override sources are minor, formula-shape changes are major. The §Submit checklist step 5 now references the new §TTL resolution section instead of inlining a broken one-liner.
1839
-
1840
- Classification: patch. No field or schema changed. Every existing manifest and config combination resolves to the same TTL except for the previously-undefined case (manifest without `expectedDurationSeconds`), which was silently implementation-defined; the new text makes the `jobs.ttlSeconds` fallback normative. Companion prose updates land in `ROADMAP.md §TTL per action` and §Notable config keys.
1841
-
1842
- ## 0.2.1
1843
-
1844
- ### Patch Changes
95
+ ### Minor
1845
96
 
1846
- - b827431: Clarify the comment in `spec/README.md` §"Use load a schema": `specIndex.specVersion` is the payload shape version baked into `index.json`, not the npm package version. The two may drift bumping the npm package does not bump `specVersion` unless the shape of `index.json` itself changes.
97
+ - **`--all` promoted to a normative universal flag** in `cli-contract.md §Global flags`. Any verb that accepts a target identifier (`-n <node.path>`, `<job.id>`, `<plugin.id>`) MUST accept `--all` as "apply to every eligible target matching the verb's preconditions". Mutually exclusive with a positional target on the same invocation. Verbs where fan-out is nonsensical (`sm record`, `sm init`, `sm version`, `sm help`, `sm config get/set/reset/show`, `sm db *`, `sm serve`) MUST reject `--all` with exit `2`.
1847
98
 
1848
99
  ## 0.2.0
1849
100
 
1850
- ### Minor Changes
1851
-
1852
- - 79aed4d: **Breaking**: rename `dispatch-lifecycle.md` → `job-lifecycle.md`.
1853
-
1854
- ROADMAP decision #30 renamed the domain term "dispatch" to "job" (tables `state_jobs`, artifact "job file"). The spec prose filename had lagged behind; this change closes that gap.
1855
-
1856
- All internal references updated: `architecture.md`, `cli-contract.md`, `db-schema.md`, `prompt-preamble.md`, `versioning.md`, `schemas/job.schema.json`, `README.md`, and `package.json` `files` list. `index.json` regenerated.
1857
-
1858
- **Migration**: any external consumer that links to `spec/dispatch-lifecycle.md` (by URL or filename) MUST update to `spec/job-lifecycle.md`. The canonical URL becomes `https://skill-map.dev/spec/v0/job-lifecycle.md`.
1859
-
1860
- Classification: breaking change on a normative prose doc. Per `versioning.md` §Pre-1.0, minor bumps MAY contain breaking changes while the spec is `0.Y.Z`.
1861
-
1862
- ## 0.1.2
1863
-
1864
- ### Patch Changes
1865
-
1866
- - f4214fe: Expand `spec/README.md` §Distribution with concrete install and usage snippets now that `@skill-map/spec` is live on npm: install command, loading a schema via `exports`, and a small integrity-verification example using the `index.json` sha256 block.
1867
-
1868
- ## 0.1.1
1869
-
1870
- ### Patch Changes
101
+ ### Minor
1871
102
 
1872
- - bc0b217: Update `spec/conformance/README.md` wording: drop the "v0.1.0-alpha.0" label (we shipped `0.1.0`), and reflect that the suite now carries two cases (`basic-scan`, `kernel-empty-boot`) with a shared `minimal-claude` fixture.
103
+ - **`@skill-map/spec` published on npm.** First public release of the spec package.
1873
104
 
1874
105
  ## 0.1.0
1875
106
 
1876
- ### Minor Changes
1877
-
1878
- - 5b3829a: Add conformance case `kernel-empty-boot`:
1879
-
1880
- - New file: `spec/conformance/cases/kernel-empty-boot.json`.
1881
- - Exercises the boot invariant from `architecture.md`: with every adapter, detector, and rule disabled, scanning an empty scope MUST return a valid `ScanResult` with `schemaVersion: 1` and zero-filled stats.
1882
- - Referenced in `conformance/README.md` (§"Cases explicitly referenced elsewhere in the spec"). Entry moved from "pending" to "current" in the case inventory.
1883
- - Registered in `spec/index.json` and the integrity block (SHA256 regenerated).
1884
-
1885
- The second pending case, `preamble-bitwise-match`, is deferred to Step 10 (requires `sm job preview` from the job subsystem).
1886
-
1887
- - 4e0aec4: Initial public spec surface (`v0.1.0`):
1888
-
1889
- - 21 JSON Schemas (draft 2020-12): 10 top-level, 6 frontmatter, 5 summaries.
1890
- - 7 prose contracts (architecture, cli-contract, dispatch-lifecycle, job-events, prompt-preamble, db-schema, plugin-kv-api).
1891
- - 1 interface doc (security-scanner).
1892
- - Conformance stub: `basic-scan` case, `minimal-claude` fixture, verbatim `preamble-v1.txt`.
1893
- - Machine-readable `index.json` with integrity hashes per file.
1894
-
1895
- This is the first tagged release of the skill-map specification.
1896
-
1897
- Changelog for the **skill-map specification**, tracked independently from the reference CLI. See `versioning.md` for the policy that governs what constitutes a patch / minor / major change.
1898
-
1899
- Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html) as refined in `versioning.md`.
1900
-
1901
- Each entry classifies changes into four sections:
1902
-
1903
- - **Added** — new optional fields, schemas, or contracts.
1904
- - **Changed** — modifications to existing normative content. Breaking changes are called out explicitly.
1905
- - **Deprecated** — features scheduled for removal in a future major.
1906
- - **Removed** — features removed in a major bump.
1907
-
1908
- Tag convention: `spec-vX.Y.Z` (distinct from CLI tags `cli-vX.Y.Z`).
1909
-
1910
- ---
1911
-
1912
- ## [Unreleased]
1913
-
1914
- Initial public spec bootstrap (Step 0a phases 1–3).
1915
-
1916
- ### Added
1917
-
1918
- - `cli-contract.md` — new normative section **§Dry-run** between §Exit codes and §Verb catalog. Codifies the contract every verb that exposes `-n` / `--dry-run` MUST honour: no observable side effects (DB / FS / config / network / spawns), no auto-provisioning of scope directories, output mirrors live mode with explicit "would …" framing, exit codes mirror live mode, dry-run MUST short-circuit `--yes` / `--force` confirmation prompts. Per-verb opt-in: the flag is not global and verbs that don't declare it MUST reject it as an unknown option (exit `2`). Verb catalog rows for `sm init`, `sm db reset` (default + `--state` + `--hard`), and `sm db restore` amended to declare and describe their `--dry-run` previews. Pre-1.0 minor (additive normative).
1919
- - `plugin-author-guide.md` — consolidated section on the three-tier frontmatter validation model (default permissive `additionalProperties: true` + always-active `unknown-field` rule emitting `warn` + `scan.strict` / `--strict` promoting warnings to `error`). Includes a worked example through all three tiers and an explicit note on why no "schema-extender" plugin kind exists (the path for custom validation is a deterministic Rule, not a new plugin kind). Editorial only. No normative change — the model already exists implicitly via `base.schema.json`'s permissive `additionalProperties` and `project-config.schema.json#/properties/scan/properties/strict`. Patch.
1920
- - `PluginManifest` gains optional `granularity` field (enum `bundle` / `extension`, default `bundle`). Built-in `claude` bundle is `granularity: bundle` (toggle the whole bundle); built-in `core` bundle is `granularity: extension` (each built-in toggle-able individually under `core/<ext-id>`). `sm plugins enable / disable` validates the supplied id against the bundle's declared granularity (bundle granularity rejects qualified ids; extension granularity rejects bare bundle ids) and persists in `config_plugins` with the appropriate key. `--all` operates only on bundle-granularity plugin ids; the "disable every kernel built-in" intent is served by `--no-built-ins`. `plugin-author-guide.md` adds a §Granularity — bundle vs extension section with the per-verb behaviour table and the built-in mapping; `architecture.md` §`PluginLoaderPort` documents the runtime split (loader's pre-import resolveEnabled is coarse / bundle-level; the CLI's runtime composer drops per-extension disabled extensions before they reach the orchestrator). Closes the spec-vs-impl drift between the spec promise that "no extension is privileged, removable" and the prior implementation where built-ins were always-on. Pre-1.0 minor per `versioning.md` § Pre-1.0; additive on the manifest schema, breaking only for users who relied on the `claude` adapter loading without an explicit `config_plugins` row (none today, since the row had no effect on built-ins before).
1921
- - Detector manifest gains optional `applicableKinds` filter (array, `minItems: 1`, kebab-case strings, `uniqueItems: true`). When declared, the kernel skips invocation for nodes whose `kind` is not in the list — fail-fast, before the detect context is built, so a probabilistic detector wastes zero LLM cost (and a deterministic detector zero CPU) on inapplicable nodes. Absent = applies to every kind (the default); no wildcard syntax. Empty array `[]` is rejected at load time. Unknown kinds (no installed Adapter declares them via `defaultRefreshAction`) load OK with a `sm plugins doctor` warning — the Provider may arrive later — and the doctor exit code is NOT promoted by the warning. `plugin-author-guide.md` adds a §Detector `applicableKinds` — narrow the pipeline section under Granularity with the per-shape behaviour table and a worked example; `architecture.md` adds a §Detector · `applicableKinds` filter subsection above trigger normalization; `schemas/extensions/detector.schema.json` declares the new property. Pre-1.0 minor per `versioning.md` § Pre-1.0; additive, non-breaking for existing detectors (they all behave as if `applicableKinds: undefined`).
1922
-
1923
- ### Changed
1924
-
1925
- - `cli-contract.md`: `--all` is no longer a global flag. It is valid only on verbs that explicitly document fan-out semantics: `sm job submit`, `sm job run`, `sm job cancel`, and `sm plugins enable/disable`.
1926
- - `cli-contract.md`: `sm scan compare-with <dump> [roots...]` is now a sub-verb instead of a `--compare-with <path>` flag on `sm scan`. Read-only delta report against a saved `ScanResult` JSON dump. Same exit codes (`0` empty delta / `1` drift / `2` operational error). Old flag form removed. Pre-1.0 breaking change shipped as minor per `versioning.md` § Pre-1.0.
1927
- - Plugin discovery — directory name MUST equal manifest id (else `invalid-manifest`); cross-root id collisions yield new `id-collision` status (sixth status, both collided plugins blocked, no precedence). `plugin-author-guide.md` Diagnostics table grows from five to six rows; `architecture.md` §`PluginLoaderPort` documents the two enforcement points; `schemas/plugins-registry.schema.json#/$defs/DiscoveredPlugin/status` adds `id-collision` to the enum. Pre-1.0 minor per `versioning.md` § Pre-1.0; breaking for any plugin whose directory name does not match its manifest id, but no real ecosystem affected today.
1928
- - Plugin extensions are now identified by qualified ids `<plugin-id>/<extension-id>`. Built-in extensions adopt the `core/` namespace; the Claude adapter and its kind-aware detectors (frontmatter, slash, at-directive) live under `claude/`. The loader injects `pluginId` from `plugin.json#/id` into every extension at load time; an explicit `pluginId` field on an extension that disagrees with the manifest id is `invalid-manifest`. `architecture.md` §`PluginLoaderPort` documents the qualifier composition; `plugin-author-guide.md` adds a §Qualified extension ids section with the built-in mapping table; `schemas/extensions/base.schema.json` clarifies that extension `id` stays unqualified (single kebab-case segment, no `/`); `schemas/extensions/adapter.schema.json#/properties/defaultRefreshAction` now requires qualified action ids (pattern `^<plugin-id>/<action-id>$`). Pre-1.0 minor per `versioning.md` § Pre-1.0; breaking for any plugin or test that referenced an extension by short id.
1929
- - `cli-contract.md`: exit-code `2` "Operational error" row clarified to mention runtime / environment mismatches (wrong Node version, missing native dependency) explicitly. The "unhandled exception" catch-all already covered the case; this just removes ambiguity for future implementers.
1930
- - `job-events.md`: the common `runId` envelope now explicitly documents the optional mode segment (`r-<mode>-YYYYMMDD-HHMMSS-XXXX`) used by external Skill claims, scan runs, and standalone issue recomputations.
1931
- - `versioning.md` and related prose: replace ambiguous milestone terminology with explicit versioned release language.
1932
-
1933
- ### Added
1934
-
1935
- - Foundation:
1936
- - `README.md` — human-readable introduction and repo layout.
1937
- - `versioning.md` — evolution policy, stability tags, 3-minor deprecation window.
1938
- - `CHANGELOG.md` — this file.
1939
- - JSON Schemas (21 files, all draft 2020-12, camelCase keys):
1940
- - Top-level (10): `node`, `link`, `issue`, `scan-result`, `execution-record`, `project-config`, `plugins-registry`, `job`, `report-base`, `conformance-case`.
1941
- - Frontmatter (6): `base` + per-kind `skill` / `agent` / `command` / `hook` / `note`. Per-kind schemas extend `base` via `allOf`.
1942
- - Summaries (5): per-kind `skill` / `agent` / `command` / `hook` / `note`. All extend `report-base` via `allOf`.
1943
- - Prose contracts:
1944
- - `architecture.md` — hexagonal ports & adapters; 5 ports (`StoragePort`, `FilesystemPort`, `PluginLoaderPort`, `RunnerPort`, `ProgressEmitterPort`); 6 extension kinds (Adapter, Detector, Rule, Action, Audit, Renderer); kernel boundary + forbidden/permitted imports.
1945
- - `cli-contract.md` — CLI surface: global flags, env vars, 30+ verbs (`sm init`, `sm scan`, `sm list`, `sm show`, `sm check`, `sm findings`, `sm graph`, `sm export`, `sm job *`, `sm record`, `sm history`, `sm plugins *`, `sm audit *`, `sm db *`, `sm serve`, `sm help`), exit codes (0–5 defined, 6–15 reserved), `--json` output rules, `--format json|md|human` introspection.
1946
- - `dispatch-lifecycle.md` — job state machine (queued → running → completed | failed), atomic claim (`UPDATE ... RETURNING id`), duplicate prevention via `contentHash`, TTL with auto-reap, nonce authentication for `sm record`, sequential concurrency for MVP, retention and GC.
1947
- - `job-events.md` — canonical event stream: envelope (`type`, `timestamp`, `runId`, `jobId`, `data`), 11 canonical event types (`run.started`, `run.reap.started`, `run.reap.completed`, `job.claimed`, `job.skipped`, `job.spawning`, `model.delta`, `job.callback.received`, `job.completed`, `job.failed`, `run.summary`) plus one synthetic error event (`emitter.error`, emitted only on serialization failure), three output adapters (`pretty`, `stream-output`, `json`), ordering rules.
1948
- - `prompt-preamble.md` — verbatim normative preamble text that the kernel prepends to every rendered job file; `<user-content id="...">` delimiter contract with zero-width-space escaping; `safety` + `confidence` contract on model output; conformance fixture at `conformance/fixtures/preamble-v1.txt`.
1949
- - `db-schema.md` — engine-agnostic table catalog: three zones (`scan_*`, `state_*`, `config_*`), naming conventions (snake*case, zone prefix, `_at` / `_ms` / `_hash` / `_json` / `_count` suffixes, `is*`/`has\_` prefixes), kernel table list per zone, migration rules (`.sql`files,`NNN_snake_case.sql`, up-only, auto-backup), plugin storage modes.
1950
- - `plugin-kv-api.md` — `ctx.store` contract for mode A (`KvStore.get/set/delete/list`, plugin-scoped, optional node-scoped), mode B dedicated-tables rules (prefix injection, DDL validation, scoped Database wrapper), typed errors (`KvKeyInvalidError`, `KvValueNotSerializableError`, `KvValueTooLargeError`, `KvOperationFailedError`, `ScopedDbViolationError`). Mixing modes in a plugin is forbidden.
1951
- - Interfaces:
1952
- - `interfaces/security-scanner.md` — convention over the Action kind (id prefix `security-`) for third-party security scanners (Snyk, Socket, custom). Defines `SecurityReport` shape extending `report-base.schema.json`, normative finding categories, deduplication rules, aggregation via `sm findings --security`. Marked `Stability: experimental` through v0.x.
1953
-
1954
- ### Conventions locked (normative)
1955
-
1956
- - JSON Schema dialect: draft 2020-12.
1957
- - Casing: camelCase for all JSON keys (domain, configs, manifests, reports); kebab-case for filenames.
1958
- - `$id` scheme: `https://skill-map.dev/spec/v<major>/<path>.schema.json`. `v0` throughout pre-1.0; bumps to `v1` at the first stable release.
1959
- - Identity: `node.path` (relative to scope root) is the canonical node identifier in v0. Future UUID-based `node.id` lands with write-back.
1960
- - Required frontmatter: `name`, `description`, `metadata`, `metadata.version`.
1961
- - Frontmatter: `additionalProperties: true` (rules handle unknown fields). Summaries: `additionalProperties: false` (strict).
1962
- - Id prefixes: job `d-`, execution record `e-`, run `r-` (all `PREFIX-YYYYMMDD-HHMMSS-XXXX`).
1963
- - Exit codes: 0 ok / 1 issues / 2 error / 3 duplicate / 4 nonce-mismatch / 5 not-found.
1964
- - Deprecation window: 3 minor releases between `stable → deprecated` and removal.
1965
- - Storage modes: a plugin declares exactly one (`kv` or `dedicated`). Mixing forbidden.
1966
-
1967
- ### Conformance (stub)
1968
-
1969
- - `conformance/README.md` — suite layout, case format, assertion types (`exit-code`, `json-path`, `file-exists`, `file-contains-verbatim`, `file-matches-schema`, `stderr-matches`), runner pseudocode.
1970
- - `conformance/fixtures/minimal-claude/` — 5 MDs (one per kind: skill, agent, command, hook, note) used as the first controlled corpus.
1971
- - `conformance/fixtures/preamble-v1.txt` — verbatim extraction of the preamble from `prompt-preamble.md`, checked byte-for-byte by the future `preamble-bitwise-match` case.
1972
- - `conformance/cases/basic-scan.json` — first declarative case. Scans the `minimal-claude` fixture; asserts `schemaVersion: 1`, 5 nodes, 0 issues.
1973
-
1974
- ### Packaging
1975
-
1976
- - `package.json` at the spec root. Name: `@skill-map/spec`. Version `0.0.1` (first release line; spec versioning is strict pre-1.0 per `versioning.md`). `exports` surfaces `.` → `index.json`, plus every `./schemas/*.json`.
1977
- - `index.json` at the spec root. Machine-readable manifest of schemas, prose, interfaces, and conformance. Carries an `integrity` block with a sha256 per shipped file, deterministically regenerated by `scripts/build-spec-index.mjs`. CI blocks drift via `npm run spec:check`.
1978
- - `schemas/conformance-case.schema.json` — formal schema for entries under `conformance/cases/*.json`. Defines the `invoke` object and the six assertion types (`exit-code`, `json-path`, `file-exists`, `file-contains-verbatim`, `file-matches-schema`, `stderr-matches`) as a discriminated union via `oneOf`.
1979
-
1980
- ### Notes
107
+ ### Minor
1981
108
 
1982
- - Pending for `spec-v0.1.0`: cases `kernel-empty-boot` and `preamble-bitwise-match` (referenced normatively in `architecture.md` and `prompt-preamble.md`). Land alongside Step 0b when the reference implementation exists to run them against.
1983
- - No tagged spec release yet. First tag (`spec-v0.1.0`) lands after Step 0b CI validates the implementation against this stub.
1984
- - Release pipeline: `@skill-map/spec` is published via [changesets](https://github.com/changesets/changesets). Every PR that touches `spec/` includes a `.changeset/*.md` declaring the bump; merging to `main` opens a "Version Packages" PR; merging that PR publishes to npm and tags the release. See `CONTRIBUTING.md`.
109
+ - **Initial public spec bootstrap.** Ships the JSON Schemas (draft 2020-12) for `Node` / `Link` / `Issue` / `ScanResult` / `ExecutionRecord` / `ProjectConfig` / `PluginsRegistry` / `Job` / `ReportBase` / `ConformanceCase` / `HistoryStats` plus the per-kind extension schemas (Provider / Extractor / Rule / Action / Formatter / Hook). Prose normative contracts: `cli-contract.md`, `architecture.md`, `db-schema.md`, `job-lifecycle.md`, `job-events.md`, `prompt-preamble.md`, `plugin-kv-api.md`. Conformance case `kernel-empty-boot` exercises the boot invariant (kernel boots and returns an empty `ScanResult` with zero registered extensions); `preamble-bitwise-match` is deferred to Step 10.