@skill-map/spec 0.13.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 +46 -2542
- package/architecture.md +24 -7
- package/cli-contract.md +2 -0
- package/conformance/README.md +2 -2
- package/conformance/coverage.md +12 -4
- package/db-schema.md +1 -1
- package/index.json +8 -8
- package/package.json +1 -1
- package/plugin-author-guide.md +21 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,2605 +1,109 @@
|
|
|
1
1
|
# Spec changelog
|
|
2
2
|
|
|
3
|
-
## 0.13.
|
|
3
|
+
## 0.13.1
|
|
4
4
|
|
|
5
|
-
###
|
|
6
|
-
|
|
7
|
-
- e0fb57e: Step 14.2 — REST read-side endpoints + DataSource contract
|
|
8
|
-
|
|
9
|
-
Fills the `### Server` subsection's endpoint catalogue from the v14.1 stub
|
|
10
|
-
(`/api/health` real, `/api/*` 404) to the eight read-side endpoints the
|
|
11
|
-
Angular SPA at 14.3 will consume. New `spec/schemas/api/rest-envelope.schema.json`
|
|
12
|
-
formalises the list-envelope shape. Test totals 764 → 832 (+68).
|
|
13
|
-
|
|
14
|
-
**Files added (server)**
|
|
15
|
-
|
|
16
|
-
- `src/server/path-codec.ts` — `encodeNodePath` / `decodeNodePath`. Base64url (RFC 4648 §5, no padding). Mirrored at `ui/src/services/data-source/path-codec.ts` in 14.3.
|
|
17
|
-
- `src/server/envelope.ts` — list / single / value envelope builders. `REST_ENVELOPE_SCHEMA_VERSION = '1'`. Hardcoded to track `spec/schemas/api/rest-envelope.schema.json#/properties/schemaVersion/const`.
|
|
18
|
-
- `src/server/query-adapter.ts` — `urlParamsToExportQuery(params)` lifts URL search params into the kernel's `IExportQuery` via `parseExportQuery` (one grammar, two transports). `filterNodesWithoutIssues` post-filter handles `hasIssues=false` (the one filter the kernel grammar can't express).
|
|
19
|
-
- `src/server/routes/deps.ts` — shared `IRouteDeps` bag (`options`, `runtimeContext`).
|
|
20
|
-
- `src/server/routes/health.ts` — extracted from `app.ts` for symmetry with the other routes (no behavior change).
|
|
21
|
-
- `src/server/routes/scan.ts` — `/api/scan` + `/api/scan?fresh=1`. DB absent → returns the empty `ScanResult` shape (matches the `loadScanResult` synthetic fallback). `?fresh=1` rejects when the server was started with `--no-built-ins` or `--no-plugins`.
|
|
22
|
-
- `src/server/routes/nodes.ts` — `/api/nodes/:pathB64` (single) registered BEFORE `/api/nodes` (list) so the param doesn't shadow the literal prefix. Pagination defaults `offset=0`, `limit=100`; max `limit=1000`.
|
|
23
|
-
- `src/server/routes/links.ts` — `/api/links?kind=&from=&to=`.
|
|
24
|
-
- `src/server/routes/issues.ts` — `/api/issues?severity=&ruleId=&node=`. `ruleId` filter mirrors `sm check`'s qualified-or-suffix match.
|
|
25
|
-
- `src/server/routes/graph.ts` — `/api/graph?format=ascii|json|md`. Per-format content-type. Unknown format → `bad-query` 400.
|
|
26
|
-
- `src/server/routes/config.ts` — `/api/config`. Wraps `loadConfig` from the kernel. Layered-loader warnings forwarded to `process.stderr`.
|
|
27
|
-
- `src/server/routes/plugins.ts` — `/api/plugins`. Built-ins (gated by `noBuiltIns`) + drop-ins (gated by `noPlugins`). `source: 'built-in' | 'project' | 'global'` derived from the plugin's filesystem path against `defaultProjectPluginsDir`.
|
|
28
|
-
|
|
29
|
-
**Files edited (server)**
|
|
30
|
-
|
|
31
|
-
- `src/server/app.ts` — `IAppDeps` gains `runtimeContext` (mandatory). Routes registered via the new `routes/*` registrars BEFORE the `/api/*` 404 catch-all. `app.onError` extended to map `ExportQueryError` → 400 `bad-query` (alongside the existing HTTPException + uncaught-Error branches).
|
|
32
|
-
- `src/server/index.ts` — `createServer(options, extra?)` accepts an optional `extra.runtimeContext` so tests can drive against a tempdir scope; production callers (the `sm serve` verb) leave it undefined and the composition root falls back to `defaultRuntimeContext()`.
|
|
33
|
-
- `src/server/i18n/server.texts.ts` — adds error message templates: `dbMissingHint`, `freshScanRequiresPipeline`, `graphUnknownFormat`, `paginationLimitTooLarge`, `paginationInvalidInteger`, `nodeNotFound`, `pathB64Malformed`.
|
|
34
|
-
|
|
35
|
-
**Tests added (68)**
|
|
36
|
-
|
|
37
|
-
- `src/test/server-endpoints.test.ts` (24) — happy + error path per endpoint. Uses real `runScan` + `persistScanResult` against a `mkdtempSync` fixture (no `:memory:` per `feedback_sqlite_in_memory_workaround.md`).
|
|
38
|
-
- `src/test/server-pagination.test.ts` (10) — default page caps at 100, `?limit=1000` accepted, `?limit=1001` rejected, offset/limit boundaries, `?offset=-1` and `?offset=foo` rejected, offset past total returns empty + preserves total.
|
|
39
|
-
- `src/test/server-errors.test.ts` (8) — every `code` value maps to the documented HTTP status; canonical envelope shape on every error response.
|
|
40
|
-
- `src/test/server-query-adapter.test.ts` (16) — URL-param → IExportQuery matrix; `filterNodesWithoutIssues` post-filter behaviour.
|
|
41
|
-
- `src/test/server-path-codec.test.ts` (10) — round-trip on POSIX / unicode / spaces / very long paths; rejection of empty, non-alphabet, single-char inputs; uniqueness for distinct inputs.
|
|
42
|
-
|
|
43
|
-
**Spec**
|
|
44
|
-
|
|
45
|
-
- `spec/schemas/api/rest-envelope.schema.json` — new schema. `$id: https://skill-map.dev/spec/v0/api/rest-envelope.schema.json`. `oneOf` enforces that an envelope carries exactly one of `items` / `item` / `value` per kind (with sentinel kinds `health` / `scan` / `graph` reserved for routes that don't use the envelope).
|
|
46
|
-
- `spec/cli-contract.md` `### Server` — endpoint table expanded from 4 rows (v14.1 surface) to 12 rows (v14.2 surface) with full filters / status / shape per row. Error code source enumeration added (`not-found` / `bad-query` / `internal` / reserved `db-missing`). Stability stays `experimental — locks at v0.6.0`.
|
|
47
|
-
- `spec/CHANGELOG.md` `[Unreleased]` `### Minor` — entry for BFF endpoints + envelope schema.
|
|
48
|
-
- `spec/conformance/coverage.md` — row 25 added for `api/rest-envelope.schema.json` (status: 🔴 missing — implementation-side coverage exists in `src/test/server-endpoints.test.ts`; a kernel-agnostic conformance case is still required before v1.0.0 ships).
|
|
49
|
-
- `spec/index.json` — regenerated (40 → 41 files hashed).
|
|
50
|
-
|
|
51
|
-
**Decisions during implementation (flag for orchestrator)**
|
|
52
|
-
|
|
53
|
-
- The `db-missing` error code is kept in the documented enum but no v14.2 route currently emits it — `/api/scan` returns the empty `ScanResult` when the DB is absent, list routes return zero items, and `/api/health` already advertises `db: 'missing'`. Documented in the spec as "reserved for future endpoints (post-v0.6.0 mutations) where degradation is not safe". Removing the code would be a breaking change to the envelope contract; keeping it costs nothing.
|
|
54
|
-
- `ExportQueryError` from `parseExportQuery` is funneled to `bad-query` 400 via a new branch in `app.onError`. The brief listed it as a route-level concern; centralising in the global handler means future routes that go through the kernel grammar (e.g. a future `/api/export?q=...`) inherit the same envelope mapping for free.
|
|
55
|
-
- `urlParamsToExportQuery` builds a canonical raw query string and re-parses it through `parseExportQuery` instead of constructing `IExportQuery` directly. The extra parse is microseconds and guarantees the BFF and `sm export` can never drift on what counts as a valid filter token. When the grammar grows (e.g. `has=findings` post-Step 11), only `parseExportQuery` changes.
|
|
56
|
-
- `/api/scan?fresh=1` rejection on `--no-built-ins` / `--no-plugins` matches Decision §14.1's intent: the BFF surface should not silently produce empty results that look indistinguishable from "your project has no nodes". The `bad-query` envelope tells the operator they're holding a knife by the blade.
|
|
57
|
-
- Tests use `noPlugins: true` by default to keep them deterministic against `process.cwd()` — `loadPluginRuntime` walks the live cwd's plugins dir, which would surface ambient plugins from the test runner's host (none in CI today, but a developer running tests locally with their own plugins installed would see flake).
|
|
58
|
-
- The route registration order in `app.ts` is documented in the file's header comment. `/api/nodes/:pathB64` MUST register before `/api/nodes` (Hono matches in declaration order; the literal prefix wins otherwise).
|
|
59
|
-
|
|
60
|
-
- d5488bf: Step 14.4.a — BFF WS broadcaster + chokidar wiring + scan event emission
|
|
61
|
-
|
|
62
|
-
First half of Step 14.4 lands. The BFF's `/ws` endpoint flips from
|
|
63
|
-
"upgrade-only stub" to a real broadcaster fed by a chokidar
|
|
64
|
-
filesystem watcher: every debounced batch runs the same
|
|
65
|
-
`runScanWithRenames` + persistence pipeline `sm watch` uses, and the
|
|
66
|
-
kernel's `ProgressEmitterPort` is bridged directly to the broadcaster
|
|
67
|
-
so `scan.*` / `extractor.completed` / `rule.completed` / `extension.error`
|
|
68
|
-
events reach every connected client verbatim — no envelope
|
|
69
|
-
construction in the BFF for the routine cases. Tests 832 → 854 (+22).
|
|
70
|
-
|
|
71
|
-
The UI-side consumer (`WsEventStreamService`) ships separately as
|
|
72
|
-
14.4.b.
|
|
73
|
-
|
|
74
|
-
**Files added (server)**
|
|
75
|
-
|
|
76
|
-
- `src/server/broadcaster.ts` — `WsBroadcaster` class. Owns the
|
|
77
|
-
connected-clients Set, fans `JSON.stringify(envelope)` once across
|
|
78
|
-
every open socket, evicts on backpressure (`bufferedAmount > 4 MiB`
|
|
79
|
-
→ close 1009 + unregister), drains every client with code 1001 +
|
|
80
|
-
reason `'server shutdown'` on `shutdown()`. `IBroadcasterClient`
|
|
81
|
-
interface is structural so unit tests inject fakes without a real
|
|
82
|
-
`WebSocket`.
|
|
83
|
-
- `src/server/watcher.ts` — `createWatcherService(deps)` factory.
|
|
84
|
-
Wraps `createChokidarWatcher` with `scan.watch.debounceMs` from
|
|
85
|
-
config (override via `--watcher-debounce-ms`), runs the kernel scan
|
|
86
|
-
pipeline per debounced batch, persists via `withSqlite(...).scans.persist(...)`.
|
|
87
|
-
The per-batch `ProgressEmitterPort` bridges every event the kernel
|
|
88
|
-
orchestrator emits during the scan to `broadcaster.broadcast(envelope)`.
|
|
89
|
-
Per-batch failures log + continue (transient FS errors must not
|
|
90
|
-
kill the broadcaster); chokidar instance errors broadcast a
|
|
91
|
-
`watcher.error` advisory.
|
|
92
|
-
- `src/server/events.ts` — envelope helpers (`IWsEventEnvelope` shape,
|
|
93
|
-
`buildWatcherStartedEvent`, `buildWatcherErrorEvent`). The
|
|
94
|
-
`watcher.*` events are BFF-internal advisories — non-normative,
|
|
95
|
-
prefixed with `watcher.` to flag their non-spec status. Spec-mandated
|
|
96
|
-
shapes (`scan.*`, `extractor.completed`, `rule.completed`) are
|
|
97
|
-
forwarded verbatim from the kernel emitter, so this file does not
|
|
98
|
-
build them.
|
|
99
|
-
|
|
100
|
-
**Files added (tests)**
|
|
101
|
-
|
|
102
|
-
- `src/test/server-ws-broadcaster.test.ts` (15 tests) — broadcaster
|
|
103
|
-
unit tests against fake `IBroadcasterClient` instances. Coverage:
|
|
104
|
-
register/unregister/clientCount accounting, broadcast fan-out + JSON
|
|
105
|
-
stringify, readyState filter (skip closing/closed), per-client
|
|
106
|
-
`send()` failure isolation, backpressure eviction at the documented
|
|
107
|
-
threshold (`WS_BACKPRESSURE_BYTES = 4 MiB`), shutdown idempotency
|
|
108
|
-
- close-code/reason assertions, post-shutdown register immediate
|
|
109
|
-
close, post-shutdown broadcast no-op, circular-envelope serialization
|
|
110
|
-
failure handling.
|
|
111
|
-
- `src/test/server-ws-integration.test.ts` (7 tests) — end-to-end
|
|
112
|
-
against a real server. Boots `createServer({...})` with
|
|
113
|
-
`noWatcher: false`, watches a `mkdtempSync` cwd via the
|
|
114
|
-
`runtimeContext` override (production callers' cwd would point at the
|
|
115
|
-
test runner's repo root). Exercises: initial-batch `scan.completed`
|
|
116
|
-
observed by a connected client; multi-client fan-out (one batch fires
|
|
117
|
-
to two open clients); `clientCount` decrement on disconnect;
|
|
118
|
-
`handle.close()` shuts the watcher cleanly under 2s;
|
|
119
|
-
`validateServerOptions` rejects `--no-built-ins + watcher on`;
|
|
120
|
-
`--no-watcher` confirms no `scan.*` events fire.
|
|
121
|
-
|
|
122
|
-
**Files edited (server)**
|
|
123
|
-
|
|
124
|
-
- `src/server/ws.ts` — `noopWebSocketRoute(app)` deleted, replaced
|
|
125
|
-
with `attachBroadcasterRoute(app, broadcaster)`. Pulls the underlying
|
|
126
|
-
`ws` library `WebSocket` off `WSContext.raw` and registers it on
|
|
127
|
-
`onOpen`; unregisters on `onClose` / `onError`. Server-push only —
|
|
128
|
-
`onMessage` intentionally not registered at v14.4.a.
|
|
129
|
-
- `src/server/index.ts` — `createServer` composition root grows the
|
|
130
|
-
broadcaster + watcher lifecycle: instantiate `WsBroadcaster` →
|
|
131
|
-
build app (broadcaster threaded into `IAppDeps`) → bind listener →
|
|
132
|
-
start watcher (unless `--no-watcher`); `handle.close()` shuts in
|
|
133
|
-
order: `watcherService.stop()` → `broadcaster.shutdown()` → http
|
|
134
|
-
close → `wss.close()`. `ServerHandle` exposes the `broadcaster`
|
|
135
|
-
field for tests asserting `clientCount`.
|
|
136
|
-
- `src/server/app.ts` — `IAppDeps.attachWs: TWsRegistrar` removed;
|
|
137
|
-
replaced with `IAppDeps.broadcaster: WsBroadcaster`. The BFF wires
|
|
138
|
-
`attachBroadcasterRoute` directly inside `createApp` now (route
|
|
139
|
-
registrar pattern was the v14.1 scaffolding to allow swap-in at
|
|
140
|
-
v14.4 — that work is done, no need for the indirection).
|
|
141
|
-
- `src/server/options.ts` — adds `noWatcher: boolean` (default `false`
|
|
142
|
-
per Decision #121: a server with stale DB is a footgun) and
|
|
143
|
-
`watcherDebounceMs?: number` (override the config value).
|
|
144
|
-
Validator gains `watcher-requires-pipeline` (rejects
|
|
145
|
-
`--no-built-ins + watcher on` — would persist empty scans on every
|
|
146
|
-
batch) and `watcher-debounce-invalid` (non-integer / negative).
|
|
147
|
-
- `src/server/i18n/server.texts.ts` — eight new keys for watcher /
|
|
148
|
-
broadcaster lifecycle log lines.
|
|
149
|
-
|
|
150
|
-
**Files edited (CLI)**
|
|
151
|
-
|
|
152
|
-
- `src/cli/commands/serve.ts` — plumbs `--no-watcher` (documented) +
|
|
153
|
-
hidden `--watcher-debounce-ms` flag through to `IServerOptionsInput`.
|
|
154
|
-
- `src/cli/i18n/serve.texts.ts` — two new keys
|
|
155
|
-
(`watcherRequiresPipeline`, `watcherDebounceInvalid`).
|
|
156
|
-
|
|
157
|
-
**Files edited (tests)**
|
|
158
|
-
|
|
159
|
-
- `src/test/server-boot.test.ts` — the no-broadcaster-yet
|
|
160
|
-
close-1000-on-`onOpen` assertion is replaced with a "connection
|
|
161
|
-
stays open + registers" assertion. Default options grow
|
|
162
|
-
`noWatcher: true` (the watcher is exercised in the dedicated
|
|
163
|
-
integration file).
|
|
164
|
-
- `src/test/server-{db-missing,endpoints,errors,pagination}.test.ts`
|
|
165
|
-
— default options grow `noWatcher: true` so chokidar doesn't
|
|
166
|
-
subscribe to the test runner's cwd. No behavior change for these
|
|
167
|
-
tests; they exercise the REST surface, not the watcher.
|
|
168
|
-
|
|
169
|
-
**Spec**
|
|
170
|
-
|
|
171
|
-
- `spec/cli-contract.md` `### Server` — new **WebSocket protocol**
|
|
172
|
-
subsection. Documents the wire envelope (delegated to
|
|
173
|
-
`job-events.md` §Common envelope), the v14.4.a event catalog
|
|
174
|
-
(`scan.started` / `scan.progress` / `scan.completed` plus the
|
|
175
|
-
side-effect events `extractor.completed` / `rule.completed` /
|
|
176
|
-
`extension.error`, plus the BFF-internal advisories
|
|
177
|
-
`watcher.started` / `watcher.error`), the connection lifecycle
|
|
178
|
-
(no state push on connect; client polls `/api/scan` to seed; close
|
|
179
|
-
codes 1000 / 1001 / 1009), the backpressure rule, and the
|
|
180
|
-
loopback-only assumption (no per-connection auth through v0.6.0
|
|
181
|
-
per Decision #119). The endpoint table flips `GET /ws` from
|
|
182
|
-
`upgrade-only` to `implemented (v14.4.a)`. The `sm serve` flag
|
|
183
|
-
table grows `--no-watcher`. The verb-catalog row for `sm serve`
|
|
184
|
-
mirrors the new flag.
|
|
185
|
-
- `spec/CHANGELOG.md` `[Unreleased]` `### Minor` entry.
|
|
186
|
-
- `spec/index.json` — regenerated (41 files hashed; no schema added).
|
|
187
|
-
|
|
188
|
-
**ROADMAP.md** — bumped `Last updated`, marked Step 14.4.a landed
|
|
189
|
-
(14.4 carries an explicit (a/b) split now), 14.4.b still owes the
|
|
190
|
-
UI-side consumer. Earlier 14.3 prose pushed to "Earlier prose".
|
|
191
|
-
|
|
192
|
-
**Decisions taken inline (flag for orchestrator)**
|
|
193
|
-
|
|
194
|
-
- `issue.added` / `issue.resolved` (per `spec/job-events.md` §Issue
|
|
195
|
-
events line 446) **deferred to a follow-up**. The diff requires
|
|
196
|
-
comparing the new `ScanResult.issues` set against the prior
|
|
197
|
-
persisted snapshot; the watcher already loads the prior for the
|
|
198
|
-
rename heuristic, so the data is at hand, but the diff plumbing
|
|
199
|
-
(key derivation, set comparison, two emit calls per delta) is
|
|
200
|
-
enough material that it deserves its own brief. The 14.4.a surface
|
|
201
|
-
fans out exactly what the kernel emitter already produces.
|
|
202
|
-
- `scan.failed` **deferred to a follow-up**. The shape is not yet
|
|
203
|
-
locked in `spec/job-events.md` and would need a normative
|
|
204
|
-
addition. For 14.4.a, per-batch failures log via the kernel logger
|
|
205
|
-
and the watcher loop continues — same behavior as `sm watch`'s
|
|
206
|
-
`WATCH_TEXTS.batchFailed`.
|
|
207
|
-
- `scan.progress` **emitted, not throttled**. The kernel
|
|
208
|
-
orchestrator emits one event per node walked; on a small workspace
|
|
209
|
-
this is a handful of events per batch, on a large workspace it's
|
|
210
|
-
hundreds. The brief flagged throttling as optional at 14.4.a; the
|
|
211
|
-
bridge forwards verbatim today. The integration test observed 13
|
|
212
|
-
`scan.progress` events for a 4-file fixture, which is fine. A
|
|
213
|
-
throttle (250ms aggregation) is the obvious 14.6 polish if the
|
|
214
|
-
bundle / perf pass shows the fan-out swamping the channel.
|
|
215
|
-
- `watcher.started` / `watcher.error` BFF-internal advisories
|
|
216
|
-
**emitted** rather than silent. They give the SPA event-log a
|
|
217
|
-
clear "armed" signal and a surface for chokidar errors that don't
|
|
218
|
-
fit the spec's `scan.*` shape. Prefix marks them as non-normative;
|
|
219
|
-
consumers that follow the spec's "ignore unknown event types"
|
|
220
|
-
rule will not break.
|
|
221
|
-
- `IHealthResponse.watcher: 'on' | 'off'` **NOT added**. Keeping
|
|
222
|
-
the v14.2 health response shape stable was preferable to adding
|
|
223
|
-
one field for what tests / `--no-watcher` already cover. The
|
|
224
|
-
broadcaster's `clientCount` is exposed on `ServerHandle.broadcaster`
|
|
225
|
-
for test introspection without polluting the public health surface.
|
|
226
|
-
- The validator rejects `--no-built-ins + watcher on` because the
|
|
227
|
-
watcher would persist empty scans on every batch, silently wiping
|
|
228
|
-
the DB. `--no-plugins + watcher on` is OK (the built-in pipeline
|
|
229
|
-
is still complete on its own).
|
|
230
|
-
- `attachBroadcasterRoute` does NOT register `onMessage`. v14.4.a
|
|
231
|
-
is server-push only. A future client-initiated heartbeat / filter
|
|
232
|
-
request lands at 14.4.b or later.
|
|
233
|
-
- `WsBroadcaster` is a class (not a factory) per AGENTS.md
|
|
234
|
-
§Adapter wiring rule 5: factories scope to "adapters consumed via
|
|
235
|
-
ports", and the broadcaster is a plain BFF helper with no kernel
|
|
236
|
-
port to satisfy. The class is grandfathered no-`I*`-prefix per
|
|
237
|
-
§Type naming convention category 4.
|
|
238
|
-
|
|
239
|
-
**Smoke (live BFF, one-shot per AGENTS.md)**
|
|
240
|
-
|
|
241
|
-
The integration tests cover the live boot + WS upgrade + chokidar
|
|
242
|
-
batch + broadcast end-to-end against a `mkdtempSync` scope. The
|
|
243
|
-
diagnostic line `ws events received: scan.started, scan.progress
|
|
244
|
-
× 13, extractor.completed × 4, rule.completed × 5, scan.completed`
|
|
245
|
-
confirms the full event sequence reaches a connected client during
|
|
246
|
-
a real scan against a 4-file fixture.
|
|
247
|
-
|
|
248
|
-
- 4ff3f38: Step 14.5.d — Provider-driven kind presentation + envelope kindRegistry
|
|
249
|
-
|
|
250
|
-
Pre-1.0 minor breaking per `versioning.md` § Pre-1.0.
|
|
251
|
-
|
|
252
|
-
The Provider extension surface gains the required `kinds[*].ui` field
|
|
253
|
-
so each kind a Provider declares carries the presentation metadata the
|
|
254
|
-
UI needs to render it (label, base color, optional dark-theme color,
|
|
255
|
-
optional emoji, optional icon). The icon is a discriminated union —
|
|
256
|
-
`{ kind: 'pi'; id: 'pi-…' }` for PrimeIcons or `{ kind: 'svg'; path:
|
|
257
|
-
'…' }` for raw SVG path data. The UI derives `bg` / `fg` tints from
|
|
258
|
-
`color` per theme via a deterministic helper, so the Provider declares
|
|
259
|
-
one base color per theme rather than four hex values.
|
|
260
|
-
|
|
261
|
-
The REST envelope shape (`spec/schemas/api/rest-envelope.schema.json`)
|
|
262
|
-
gains a new required `kindRegistry` field on every payload-bearing
|
|
263
|
-
variant (`nodes` / `links` / `issues` / `plugins` / `node` / `config`);
|
|
264
|
-
sentinel envelopes (`health` / `scan` / `graph`) stay exempt. The
|
|
265
|
-
registry is keyed by kind name and carries `{ providerId, label,
|
|
266
|
-
color, colorDark?, emoji?, icon? }` — the BFF assembles it once at
|
|
267
|
-
boot from every enabled Provider and attaches it to every applicable
|
|
268
|
-
response so the UI can render Provider-declared kinds (built-in and
|
|
269
|
-
user-plugin alike) without hardcoding a closed kind enum. The change
|
|
270
|
-
keeps `schemaVersion` at `'1'` (greenfield — no released consumers
|
|
271
|
-
depend on the prior shape).
|
|
272
|
-
|
|
273
|
-
**Files edited (spec)**
|
|
274
|
-
|
|
275
|
-
- `spec/schemas/extensions/provider.schema.json` — adds `ui` to the
|
|
276
|
-
required field set on each `kinds[*]` entry, with discriminated
|
|
277
|
-
`oneOf` for `icon`.
|
|
278
|
-
- `spec/schemas/api/rest-envelope.schema.json` — new `kindRegistry`
|
|
279
|
-
definition; required on every payload-bearing variant; sentinel
|
|
280
|
-
variants explicitly forbid the field via `not.anyOf`. Version stays
|
|
281
|
-
at `'1'` (greenfield).
|
|
282
|
-
- `spec/CHANGELOG.md` — `[Unreleased]` `### Minor` entry.
|
|
283
|
-
|
|
284
|
-
**Files edited (kernel + built-in)**
|
|
285
|
-
|
|
286
|
-
- `src/kernel/extensions/provider.ts` — adds `IProviderKindUi` and
|
|
287
|
-
`IProviderKindIcon`; `ui` becomes required on `IProviderKind`.
|
|
288
|
-
- `src/built-in-plugins/providers/claude/index.ts` — every kind
|
|
289
|
-
(skill / agent / command / hook / note) declares its `ui` block
|
|
290
|
-
reusing the colors / labels / icons previously hardcoded in
|
|
291
|
-
`ui/src/styles.css`, `ui/src/i18n/kinds.texts.ts`, and
|
|
292
|
-
`ui/src/app/components/kind-icon/kind-icon.html`.
|
|
293
|
-
- `src/built-in-plugins/providers/claude/claude.test.ts` — new test
|
|
294
|
-
asserts every kind declares a well-formed `ui` block.
|
|
295
|
-
- `src/test/external-provider-kind.test.ts` — three mock providers
|
|
296
|
-
updated to declare `ui` on their `cursorRule` kinds.
|
|
297
|
-
- `src/test/plugins-cli.test.ts` — `dropMockProvider` helper template
|
|
298
|
-
declares `ui` on the inline mock `note` kind.
|
|
299
|
-
|
|
300
|
-
**Files added (conformance)**
|
|
301
|
-
|
|
302
|
-
- `spec/conformance/fixtures/plugin-missing-ui/` — drop-in Provider
|
|
303
|
-
fixture whose `kinds[*]` omits `ui` (plus a trivial `notes/example.md`
|
|
304
|
-
for the built-in Claude scan to grab).
|
|
305
|
-
- `spec/conformance/cases/plugin-missing-ui-rejected.json` — locks the
|
|
306
|
-
loader contract: `sm scan --json` exits 0, stderr matches
|
|
307
|
-
`plugin bad-provider:.*invalid.*must have required property 'ui'`,
|
|
308
|
-
the envelope still contains the built-in Claude provider, and the
|
|
309
|
-
one fixture node still gets scanned (one bad plugin does not take
|
|
310
|
-
down the scan).
|
|
311
|
-
|
|
312
|
-
**Decisions taken inline (flag for orchestrator)**
|
|
313
|
-
|
|
314
|
-
- `ui` is required, not optional — making it optional reintroduces the
|
|
315
|
-
pre-14.5.d trap of silently collapsing unknown kinds to `'note'`.
|
|
316
|
-
The cost (one object per kind in the manifest) is small.
|
|
317
|
-
- Icon is a discriminated union (`oneOf` with `kind` discriminator)
|
|
318
|
-
rather than two optional fields. Keeps the UI dispatch exhaustive
|
|
319
|
-
and AJV validates each variant cleanly.
|
|
320
|
-
- `schemaVersion` stays at `'1'` despite the required-field add.
|
|
321
|
-
Greenfield — no released consumers; a versioned migration buys
|
|
322
|
-
nothing today. Bumps the day a third-party consumer ships against
|
|
323
|
-
the wire.
|
|
324
|
-
- Severity (PrimeNG `<p-tag>` `severity` enum) is NOT declared by the
|
|
325
|
-
Provider. The UI tints kind tags with the registry's `color`
|
|
326
|
-
directly, avoiding a Provider-side dependency on a UI-framework
|
|
327
|
-
enum.
|
|
328
|
-
- BFF + UI sub-steps land in follow-up commits (14.5.d.iii / .iv /
|
|
329
|
-
.v) — the spec + kernel + built-in surface ship first so the
|
|
330
|
-
contract is visible before consumers wire up.
|
|
331
|
-
|
|
332
|
-
- de20bc2: Step 14.5 (a + b) — Inspector polish: markdown body opt-in + linked-nodes panel + dead-link verify hybrid
|
|
333
|
-
|
|
334
|
-
Two sub-steps land together as a single feature unit. The Inspector
|
|
335
|
-
view (UI workspace) gains a real markdown body card, a dedicated
|
|
336
|
-
linked-nodes panel fed by the BFF's `/api/links` endpoint, and a
|
|
337
|
-
hybrid dead-link checker that combines the in-memory heuristic with
|
|
338
|
-
on-demand BFF verification. The spec + server side ships the minimal
|
|
339
|
-
contract the new UI surface depends on: an opt-in `?include=body`
|
|
340
|
-
parameter on `GET /api/nodes/:pathB64`, plus a corrected single-node
|
|
341
|
-
response shape. Tests 854 → 868 (+14 server) and UI 113 → 138 (+25
|
|
342
|
-
inspector / linked-nodes specs).
|
|
343
|
-
|
|
344
|
-
**Why on-demand body reads instead of persisting bodies in the DB**:
|
|
345
|
-
the kernel persists `body_hash` only (per `db-schema.md` §scan_nodes)
|
|
346
|
-
— the body itself is human content, not machine state, and
|
|
347
|
-
duplicating it in SQLite would inflate the DB without serving any
|
|
348
|
-
read-side query the kernel cares about. Inspector cards that DO want
|
|
349
|
-
to render the body (markdown preview at Step 14.5) opt into the
|
|
350
|
-
filesystem re-read; the list / graph / kind-palette views never need
|
|
351
|
-
it.
|
|
5
|
+
### Patch Changes
|
|
352
6
|
|
|
353
|
-
|
|
7
|
+
- 103fc1a: Doc revision pass — greenfield framing across READMEs, spec prose, ROADMAP, AGENTS, web, and workspace landing pages.
|
|
354
8
|
|
|
355
|
-
|
|
356
|
-
`readNodeBody(cwd, relPath)` (returns `string | null`; `null` on
|
|
357
|
-
ENOENT / EACCES / EISDIR / ENOTDIR) and `stripFrontmatter(body)`
|
|
358
|
-
(drops the leading `---\n…\n---\n` block when present, leaves
|
|
359
|
-
fences in mid-document untouched). Path-traversal hardened: refuses
|
|
360
|
-
absolute paths and any relative path that resolves outside `cwd`.
|
|
361
|
-
- `src/test/server-node-body.test.ts` (11 unit cases) — covers
|
|
362
|
-
`stripFrontmatter` edge cases (empty, no frontmatter, missing
|
|
363
|
-
closing fence, fence in mid-document) and `readNodeBody` traversal
|
|
364
|
-
rejection + the four `null`-returning errno branches.
|
|
9
|
+
Pure documentation changes; no normative schema or code changes.
|
|
365
10
|
|
|
366
|
-
|
|
11
|
+
`@skill-map/spec`:
|
|
367
12
|
|
|
368
|
-
- `
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
already expected. No prod consumer ran against the legacy shape
|
|
377
|
-
(the UI was internally branching on the legacy shape before the
|
|
378
|
-
REST adapter landed at 14.3.a), so the corrected shape ships as a
|
|
379
|
-
minor.
|
|
380
|
-
- `src/test/server-endpoints.test.ts` — assertions corrected to the
|
|
381
|
-
documented shape; 2 new cases for `?include=body` (returns body
|
|
382
|
-
on present file, returns `null` when the file is missing).
|
|
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).
|
|
383
21
|
|
|
384
|
-
|
|
22
|
+
`@skill-map/cli`:
|
|
385
23
|
|
|
386
|
-
- `
|
|
387
|
-
|
|
388
|
-
`openPath`. Internally fires `dataSource.listLinks({from})` +
|
|
389
|
-
`listLinks({to})` in parallel; state machine
|
|
390
|
-
`idle/loading/ready/error`. Subscribes to `events()` filtered on
|
|
391
|
-
`scan.completed` for reactive refresh, plus a manual refresh
|
|
392
|
-
button in the card header. Token guard handles rapid path
|
|
393
|
-
changes. Renders rows with kind tag + clickable path +
|
|
394
|
-
confidence chip + sources. 10 spec cases.
|
|
395
|
-
- `ui/src/i18n/linked-nodes-panel.texts.ts` — i18n catalog.
|
|
396
|
-
- `ui/src/app/views/inspector-view/inspector-view.spec.ts` (15
|
|
397
|
-
cases) — first inspector-view spec. Covers empty / loading /
|
|
398
|
-
body-card states, stale-fetch token guard, kind-card smoke,
|
|
399
|
-
dead-link verify icon flow (heuristic-dead renders icon,
|
|
400
|
-
click → 404 confirms, click → 200 flips to live).
|
|
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`).
|
|
401
26
|
|
|
402
|
-
|
|
27
|
+
`@skill-map/testkit`:
|
|
403
28
|
|
|
404
|
-
- `
|
|
405
|
-
- `ui/src/services/data-source/data-source.port.ts` —
|
|
406
|
-
`IDataSourcePort.getNode(path, opts?: {includeBody?: boolean})`.
|
|
407
|
-
- `ui/src/services/data-source/rest-data-source.ts` — propagates
|
|
408
|
-
`includeBody` to `?include=body`.
|
|
409
|
-
- `ui/src/services/data-source/static-data-source.ts` — ignores
|
|
410
|
-
the flag (demo bundle ships bodies inline; see
|
|
411
|
-
`scripts/build-demo-dataset.js` below).
|
|
412
|
-
- `ui/src/services/collection-loader.ts` — minor signature touch
|
|
413
|
-
for the `getNode` opts pass-through.
|
|
414
|
-
- `ui/src/models/node.ts` — `INodeView` loses three fields:
|
|
415
|
-
`body`, `raw`, `mockSummary`. The "Summary" mock card is
|
|
416
|
-
retired (description already lives in `inspector__desc`).
|
|
417
|
-
- `ui/src/app/views/inspector-view/inspector-view.ts` — body card
|
|
418
|
-
switches from `<pre>{{ n.body }}</pre>` to a `@switch` over a
|
|
419
|
-
`bodyState` signal (idle / loading / empty / unavailable /
|
|
420
|
-
error / ready) with token-guarded fetch via `effect()` keyed on
|
|
421
|
-
`path()`; markdown rendered via `MarkdownRenderer` and
|
|
422
|
-
`[innerHTML]`. Mounts `<sm-linked-nodes-panel>` as a separate
|
|
423
|
-
card between Relations and Body. Dead-link verify hybrid: the
|
|
424
|
-
Relations card chips (`supersededBy` / `supersedes` / `requires`
|
|
425
|
-
/ `related`) keep the in-memory heuristic but now carry a verify
|
|
426
|
-
icon (`pi-question-circle`) that fires `getNode(path)` against
|
|
427
|
-
the BFF; three visual states `live` / `dead-confirmed` (404 → red
|
|
428
|
-
dashed border + `pi-times-circle`) / `dead-heuristic` (not in
|
|
429
|
-
scope, not yet verified). Per-node signals
|
|
430
|
-
`verifiedAlive` / `verifiedDead` / `verifyInFlight` reset on
|
|
431
|
-
`path()` change. Template refactor consolidates 4 inline
|
|
432
|
-
duplicated chip blocks into a single `<ng-template #pathChip>`
|
|
433
|
-
shared via `*ngTemplateOutlet`.
|
|
434
|
-
- `ui/src/app/views/inspector-view/inspector-view.{html,css}` —
|
|
435
|
-
templates + styles for the new body / verify states.
|
|
436
|
-
- `ui/src/i18n/inspector-view.texts.ts` — drops `summary*`, adds
|
|
437
|
-
`body.*` (loading / empty / unavailable / renderError),
|
|
438
|
-
`relations.verifyHint`, `relations.deadConfirmed`. `body: 'Body'`
|
|
439
|
-
(was `'Body (raw markdown)'`).
|
|
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.
|
|
440
30
|
|
|
441
|
-
|
|
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.
|
|
442
32
|
|
|
443
|
-
-
|
|
444
|
-
fixtureDir)` post-processor reads each fixture's body from disk,
|
|
445
|
-
strips frontmatter, attaches to the demo `data.json` so the
|
|
446
|
-
demo experience matches the live BFF (~40 KB extra for 21
|
|
447
|
-
fixtures; bodies-on-bundle is the explicit demo-mode tradeoff).
|
|
33
|
+
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
448
34
|
|
|
449
|
-
|
|
35
|
+
## [Unreleased]
|
|
450
36
|
|
|
451
|
-
|
|
452
|
-
flips its shape column from the legacy bundle to the documented
|
|
453
|
-
`{ item, links: { incoming, outgoing }, issues }` and gains the
|
|
454
|
-
`?include=body` filter column.
|
|
455
|
-
- `spec/CHANGELOG.md` `[Unreleased]` `### Minor` — entry covering
|
|
456
|
-
the `?include=body` opt-in, the corrected response shape, and
|
|
457
|
-
the path-traversal defense.
|
|
458
|
-
- `spec/index.json` — regenerated (41 files hashed; no schema
|
|
459
|
-
added).
|
|
37
|
+
### Minor
|
|
460
38
|
|
|
461
|
-
|
|
462
|
-
completeness marker now lists 14.5.a + 14.5.b as complete; "Next"
|
|
463
|
-
points at 14.5.c.
|
|
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.
|
|
464
40
|
|
|
465
|
-
|
|
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.
|
|
466
42
|
|
|
467
|
-
|
|
468
|
-
the contract surface) rather than a major. Rationale: no public
|
|
469
|
-
consumer ran against the legacy shape; the UI was decoding the
|
|
470
|
-
legacy shape internally before the REST adapter at 14.3.a
|
|
471
|
-
introduced the documented shape; and the spec table already
|
|
472
|
-
documented the new shape (the bug was in the implementation,
|
|
473
|
-
not the spec). Keeping the bump minor avoids burning a major
|
|
474
|
-
on a never-shipped wire format.
|
|
475
|
-
- `parseIncludes` is CSV-tolerant from day one (`?include=body`
|
|
476
|
-
and `?include=body,foo` both parse) so the second include can
|
|
477
|
-
land without a parser refactor. Unknown include values are
|
|
478
|
-
silently ignored — the BFF surface mirrors the spec's
|
|
479
|
-
"ignore unknown event types" rule for forward compatibility.
|
|
480
|
-
- Bodies are fetched per-node on inspector open, not pre-fetched
|
|
481
|
-
in the list endpoint. Keeps the list `/api/nodes` response
|
|
482
|
-
small (the list view never renders bodies) and matches the
|
|
483
|
-
read-side hot path: most nodes are listed but few are inspected.
|
|
484
|
-
- The dead-link verify is opt-in per chip click, not auto-fired
|
|
485
|
-
on inspector open. Heuristic-dead nodes are common in scoped
|
|
486
|
-
scans (a workspace that scans `docs/` but references `src/`);
|
|
487
|
-
auto-firing would burn one BFF round-trip per such reference.
|
|
488
|
-
- Per-node verification signals reset on `path()` change to avoid
|
|
489
|
-
stale state bleeding between inspector navigations. The signals
|
|
490
|
-
are scoped to the component instance; no global cache (the
|
|
491
|
-
cost is one BFF call per re-verify on revisit, which the user
|
|
492
|
-
triggers intentionally by clicking the icon).
|
|
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.
|
|
493
44
|
|
|
494
45
|
## 0.12.0
|
|
495
46
|
|
|
496
|
-
### Minor
|
|
497
|
-
|
|
498
|
-
- 68c5e28: Step 14.1 — `sm serve` + Hono BFF skeleton
|
|
499
|
-
|
|
500
|
-
Adds `src/server/` Hono workspace with single-port wiring (`/api/health` real,
|
|
501
|
-
`/api/*` 404 stubs, `/ws` no-op upgrade, `serveStatic` + SPA fallback). Real
|
|
502
|
-
`ServeCommand` extracted from stub at `cli/commands/stubs.ts` to dedicated
|
|
503
|
-
`cli/commands/serve.ts` extending `SmCommand`. Loopback-only through v0.6.0
|
|
504
|
-
(Decision #119). Boot resilient to missing DB — `/api/health` reports
|
|
505
|
-
`db: 'missing'`. Spec `cli-contract.md` `sm serve` row updated to full flag
|
|
506
|
-
set; new `### Server` subsection (skeleton — endpoints fill at 14.2).
|
|
507
|
-
|
|
508
|
-
**Files added (server)**
|
|
509
|
-
|
|
510
|
-
- `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.
|
|
511
|
-
- `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.
|
|
512
|
-
- `src/server/options.ts` — `IServerOptions` + `validateServerOptions(input)`. Loopback-only check for `--dev-cors`; port range check `[0, 65535]`; scope validation.
|
|
513
|
-
- `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`.
|
|
514
|
-
- `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 `/`.
|
|
515
|
-
- `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.
|
|
516
|
-
- `src/server/health.ts` — `buildHealth(deps)` synchronous; `resolveSpecVersion()` async, called once at boot.
|
|
517
|
-
- `src/server/i18n/server.texts.ts` — `SERVER_TEXTS` catalog.
|
|
518
|
-
|
|
519
|
-
**Files added (CLI)**
|
|
520
|
-
|
|
521
|
-
- `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).
|
|
522
|
-
- `src/cli/i18n/serve.texts.ts` — `SERVE_TEXTS` catalog.
|
|
523
|
-
|
|
524
|
-
**Tests added (15)**
|
|
525
|
-
|
|
526
|
-
- `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.
|
|
527
|
-
- `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.
|
|
528
|
-
- `src/test/server-db-missing.test.ts` (2) — `--db <missing>` exits 5, default boots cleanly with db:missing.
|
|
529
|
-
|
|
530
|
-
**Files edited**
|
|
531
|
-
|
|
532
|
-
- `src/cli/commands/stubs.ts` — `ServeCommand` removed; replaced with a comment pointer.
|
|
533
|
-
- `src/cli/entry.ts` — registers the new `ServeCommand`.
|
|
534
|
-
- `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.
|
|
535
|
-
- `spec/cli-contract.md` — `sm serve` row replaced with the full 14.1 flag set; new `#### Server` subsection (stability: experimental).
|
|
536
|
-
- `spec/CHANGELOG.md` — `[Unreleased]` `### Minor` entry for the spec change.
|
|
537
|
-
- `spec/index.json` — regenerated (40 files hashed; previous head was 215 lines).
|
|
538
|
-
|
|
539
|
-
**Decisions during implementation (flag for orchestrator)**
|
|
47
|
+
### Minor
|
|
540
48
|
|
|
541
|
-
|
|
542
|
-
- 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.
|
|
543
|
-
- `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.
|
|
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`.
|
|
544
50
|
|
|
545
51
|
## 0.11.0
|
|
546
52
|
|
|
547
|
-
### Minor
|
|
548
|
-
|
|
549
|
-
- f8fca25: Step 10 prep — job artifacts move into the database (B2: content-addressed storage)
|
|
550
|
-
|
|
551
|
-
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.
|
|
552
|
-
|
|
553
|
-
**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.
|
|
554
|
-
|
|
555
|
-
**Schema changes**
|
|
556
|
-
|
|
557
|
-
- New table `state_job_contents` (`content_hash` PK, `content` TEXT, `created_at`). Content-addressed: multiple `state_jobs` rows MAY reference the same row.
|
|
558
|
-
- `state_jobs.file_path` removed. The rendered content is fetched via `state_job_contents.content_hash` join.
|
|
559
|
-
- `state_executions.report_path` → `state_executions.report_json` (TEXT, parsed-JSON-on-read per the `_json` naming convention).
|
|
560
|
-
|
|
561
|
-
**Schema-typed contract changes**
|
|
562
|
-
|
|
563
|
-
- `Job.filePath` removed.
|
|
564
|
-
- `ExecutionRecord.reportPath` → `ExecutionRecord.report` (object/null — the parsed JSON payload).
|
|
565
|
-
- `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).
|
|
566
|
-
- `history-stats.schema.json` `perFailureReason` mirrors the rename.
|
|
567
|
-
|
|
568
|
-
**CLI surface changes**
|
|
569
|
-
|
|
570
|
-
- `sm job preview <id>` now prints the rendered content from `state_job_contents` (no file). Same output, different source.
|
|
571
|
-
- `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.
|
|
572
|
-
- `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).
|
|
573
|
-
- `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.
|
|
574
|
-
- `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).
|
|
575
|
-
|
|
576
|
-
**Event stream changes**
|
|
577
|
-
|
|
578
|
-
- `job.spawning.data.jobFilePath` → `job.spawning.data.contentHash` (references the content row instead of a file path).
|
|
579
|
-
- `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.
|
|
580
|
-
|
|
581
|
-
**Architecture changes**
|
|
582
|
-
|
|
583
|
-
- `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.
|
|
584
|
-
|
|
585
|
-
**Atomicity edge cases consolidated**
|
|
586
|
-
|
|
587
|
-
`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).
|
|
588
|
-
|
|
589
|
-
**Files touched**
|
|
590
|
-
|
|
591
|
-
- `spec/db-schema.md` — new `state_job_contents` section, `state_jobs.file_path` removed, `state_executions.report_path` → `report_json`, integrity section rewritten.
|
|
592
|
-
- `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.
|
|
593
|
-
- `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.
|
|
594
|
-
- `spec/prompt-preamble.md` — §How the kernel applies step 5 rewritten (DB store, no file).
|
|
595
|
-
- `spec/architecture.md` — §`RunnerPort` operations + reference impls updated for content-string + parsed-report shape.
|
|
596
|
-
- `spec/job-events.md` — `job.spawning` / `job.callback.received` / `job.completed` payloads changed.
|
|
597
|
-
- `spec/conformance/README.md` + `coverage.md` — `preamble-bitwise-match` references updated to `sm job preview` stdout.
|
|
598
|
-
- `spec/schemas/job.schema.json` — `filePath` property removed, failure-reason enum rename.
|
|
599
|
-
- `spec/schemas/execution-record.schema.json` — `reportPath` → `report` (object/null), failure-reason enum rename.
|
|
600
|
-
- `spec/schemas/history-stats.schema.json` — `perFailureReason` enum rename.
|
|
601
|
-
- `spec/index.json` regenerated (40 files hashed); `npm run spec:check` green.
|
|
602
|
-
|
|
603
|
-
**Migration for consumers**
|
|
604
|
-
|
|
605
|
-
- Any consumer reading `state_jobs.file_path` or `state_executions.report_path` reads from the renamed columns / DB-only paths instead.
|
|
606
|
-
- Any tooling that watched `.skill-map/jobs/*.md` or `.skill-map/reports/*.json` needs to query the DB or call the relevant `sm` verb.
|
|
607
|
-
- `--orphan-files` flag callers must drop the flag; `sm job prune` already does the equivalent automatically.
|
|
608
|
-
- Skill agents drain via `sm job claim --json` (id + nonce + content together) instead of `sm job claim` + reading a file.
|
|
609
|
-
|
|
610
|
-
**Out of scope**
|
|
53
|
+
### Minor
|
|
611
54
|
|
|
612
|
-
|
|
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).
|
|
613
56
|
|
|
614
57
|
## 0.10.0
|
|
615
58
|
|
|
616
|
-
### Minor Changes
|
|
617
|
-
|
|
618
|
-
- f8a7125: Open `Node.kind` to any Provider-declared string (Phase A — spec only).
|
|
619
|
-
|
|
620
|
-
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`):
|
|
621
|
-
|
|
622
|
-
- `node.schema.json#/properties/kind` carried `enum: [<5 values>]` — AJV-rejected anything else.
|
|
623
|
-
- `db-schema.md` § `scan_nodes` and § `state_summaries` mandated `CHECK in (<5 values>)` SQL constraints.
|
|
624
|
-
- `extensions/action.schema.json#/.../filter/kind` had the same closed list for the per-action applicability filter.
|
|
625
|
-
|
|
626
|
-
This phase opens the spec end:
|
|
627
|
-
|
|
628
|
-
- `node.schema.json#/properties/kind` → `{ "type": "string", "minLength": 1 }` with a description naming the built-in Claude catalog so consumers know the default contract.
|
|
629
|
-
- `db-schema.md` drops both `CHECK in (...)` constraint rows. Both columns stay `TEXT NOT NULL`.
|
|
630
|
-
- `extensions/action.schema.json#/.../filter/kind` widens to `{ items: { "type": "string", "minLength": 1 } }`.
|
|
631
|
-
|
|
632
|
-
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.
|
|
633
|
-
|
|
634
|
-
Migration for consumers:
|
|
635
|
-
|
|
636
|
-
- Anyone validating an exported `Node` JSON against `node.schema.json` now accepts external-Provider kinds.
|
|
637
|
-
- 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.
|
|
638
|
-
|
|
639
|
-
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).
|
|
640
|
-
|
|
641
|
-
## 0.9.0
|
|
642
|
-
|
|
643
|
-
### Minor Changes
|
|
644
|
-
|
|
645
|
-
- 88afe24: Cleanup pass post-v0.8.0 — finishing the renames and wiring the
|
|
646
|
-
conformance kill-switches.
|
|
647
|
-
|
|
648
|
-
**Pre-1.0 minor bump** per `spec/versioning.md` § Pre-1.0. The schema
|
|
649
|
-
field rename below is technically breaking, but ships as a minor while
|
|
650
|
-
the spec stays `0.Y.Z`.
|
|
651
|
-
|
|
652
|
-
## Spec changes (`@skill-map/spec`)
|
|
653
|
-
|
|
654
|
-
### Breaking — `conformance-case.schema.json`
|
|
655
|
-
|
|
656
|
-
- **Rename `setup.disableAllDetectors` → `setup.disableAllExtractors`.**
|
|
657
|
-
Finishes the kind rename Detector → Extractor introduced in 0.8.0
|
|
658
|
-
(Phase 2 of the plug-in model overhaul). The previous name was the
|
|
659
|
-
last residue and it never reached a release where anything consumed
|
|
660
|
-
it.
|
|
661
|
-
- **`setup.disableAll{Providers,Extractors,Rules}` are now consumed
|
|
662
|
-
end-to-end.** Until this release the three toggles were declared in
|
|
663
|
-
the schema and accepted by the runner, but the runner never threaded
|
|
664
|
-
them anywhere — the `kernel-empty-boot` case happened to pass
|
|
665
|
-
because its fixture is empty. The runner now injects
|
|
666
|
-
`SKILL_MAP_DISABLE_ALL_{PROVIDERS,EXTRACTORS,RULES}=1` into the
|
|
667
|
-
child process environment when the matching toggle is `true`, and
|
|
668
|
-
the CLI's scan composer drops every extension of the disabled kind
|
|
669
|
-
from the in-scan pipeline regardless of granularity gates and
|
|
670
|
-
`--no-built-ins`. Each toggle now has a docstring on the schema
|
|
671
|
-
property pointing at the env-var convention.
|
|
672
|
-
- `kernel-empty-boot` case updated for the rename.
|
|
673
|
-
- `conformance/README.md` example updated.
|
|
674
|
-
|
|
675
|
-
### Non-breaking — copy fixes
|
|
676
|
-
|
|
677
|
-
- Comments and docstrings across `architecture.md` and friends already
|
|
678
|
-
refer to "Extractor" everywhere; only the schema field stayed on the
|
|
679
|
-
old name. No prose changes in this bump.
|
|
680
|
-
|
|
681
|
-
## CLI changes (`@skill-map/cli`)
|
|
682
|
-
|
|
683
|
-
### Breaking — `IDiscoveredPlugin.status` enum
|
|
684
|
-
|
|
685
|
-
- **Rename `'loaded'` → `'enabled'`.** The schema enum
|
|
686
|
-
(`plugins-registry.schema.json`) already used `enabled` since 0.8.0;
|
|
687
|
-
the runtime drifted to `loaded` and has now been pulled back so the
|
|
688
|
-
runtime status matches the spec contract. `'disabled'`, the
|
|
689
|
-
semantic pair, was already aligned. Every consumer (`sm plugins
|
|
690
|
-
list`, `sm plugins doctor`, `sm db prune` plugin filter, runtime
|
|
691
|
-
plugin composer) updated. No published consumers exist.
|
|
692
|
-
|
|
693
|
-
### Non-breaking — sweep cleanup
|
|
694
|
-
|
|
695
|
-
- Old `Detector` / `detector` references (kind name, manifest field
|
|
696
|
-
names, JSDoc, comments, test fixture filenames, test variable
|
|
697
|
-
names) replaced with `Extractor` / `extractor` across the
|
|
698
|
-
production code and test suite. Excludes historical CHANGELOG
|
|
699
|
-
entries, explicit migration notes ("Renamed from Detector"), and
|
|
700
|
-
test data strings whose semantics are independent of the kind
|
|
701
|
-
name (e.g. `'@FooDetector'` in trigger normalization tests).
|
|
702
|
-
- A residual reference to "an audit reading `ScanResult.issues`" in
|
|
703
|
-
`validate-all`'s docstring rewritten without the removed kind name.
|
|
704
|
-
|
|
705
|
-
## Tests
|
|
706
|
-
|
|
707
|
-
- `plugin-runtime-branches.test.ts` — five new unit tests covering
|
|
708
|
-
the env-var kill-switch in `composeScanExtensions` (per kind, all
|
|
709
|
-
three together, and stray-value resilience).
|
|
710
|
-
- `conformance-disable-flags.test.ts` — four new e2e tests pointing
|
|
711
|
-
the runner at a populated fixture with each toggle in turn (and a
|
|
712
|
-
baseline) so a regression in the env-var pipeline shows up
|
|
713
|
-
structurally rather than relying on the empty-fixture coincidence.
|
|
714
|
-
|
|
715
|
-
## [Unreleased]
|
|
716
|
-
|
|
717
59
|
### Minor
|
|
718
60
|
|
|
719
|
-
-
|
|
720
|
-
Step 14.5.d. The Provider extension surface gains the required
|
|
721
|
-
`kinds[*].ui` field (label, color, optional dark-theme color, optional
|
|
722
|
-
emoji, optional icon) so each kind a Provider declares carries the
|
|
723
|
-
presentation metadata the UI needs to render it. The icon is a
|
|
724
|
-
discriminated union — `{ kind: 'pi'; id: 'pi-…' }` for PrimeIcons or
|
|
725
|
-
`{ kind: 'svg'; path: '…' }` for raw SVG path data wrapped in a
|
|
726
|
-
`viewBox="0 0 24 24"` tinted with `currentColor`. The UI derives bg /
|
|
727
|
-
fg tints from `color` per theme via a deterministic helper, so the
|
|
728
|
-
Provider declares one base color per theme rather than four hex
|
|
729
|
-
values.
|
|
730
|
-
|
|
731
|
-
The REST envelope shape (`schemas/api/rest-envelope.schema.json`)
|
|
732
|
-
gains a new required `kindRegistry` field on every payload-bearing
|
|
733
|
-
variant (`nodes` / `links` / `issues` / `plugins` lists, the `node`
|
|
734
|
-
single, and the `config` value envelope); sentinel envelopes
|
|
735
|
-
(`health` / `scan` / `graph`) stay exempt because they don't carry a
|
|
736
|
-
payload at the wire level either. The registry is keyed by kind
|
|
737
|
-
name and carries `{ providerId, label, color, colorDark?, emoji?,
|
|
738
|
-
icon? }` — the BFF assembles it once at boot from every enabled
|
|
739
|
-
Provider and attaches it to every applicable response so the UI can
|
|
740
|
-
render Provider-declared kinds (built-in and user-plugin alike)
|
|
741
|
-
without hardcoding a closed kind enum.
|
|
742
|
-
|
|
743
|
-
**Why required, not optional**: making `ui` optional reintroduces the
|
|
744
|
-
trap the UI had pre-14.5.d (silently collapsing unknown kinds to
|
|
745
|
-
`'note'`). Forcing every Provider to declare presentation up-front
|
|
746
|
-
means the UI never has to invent visuals; the cost is one small
|
|
747
|
-
object per kind in the manifest.
|
|
748
|
-
|
|
749
|
-
**Why discriminated icon instead of two optional fields**: the
|
|
750
|
-
`oneOf` shape (with `kind: 'pi' | 'svg'` discriminator) keeps the UI
|
|
751
|
-
dispatch exhaustive without string-sniffing the payload, and AJV
|
|
752
|
-
validates each variant cleanly — a manifest cannot ship both `id` and
|
|
753
|
-
`path` simultaneously.
|
|
754
|
-
|
|
755
|
-
**Why `schemaVersion` stays at `'1'`**: the BFF is greenfield — no
|
|
756
|
-
released consumers depend on the previous (kindRegistry-less)
|
|
757
|
-
shape, so a versioned migration would only add ceremony. The
|
|
758
|
-
shape change is documented under this changelog entry; the version
|
|
759
|
-
bumps the day a third-party consumer ships against the wire.
|
|
760
|
-
|
|
761
|
-
Pre-1.0 minor breaking per `versioning.md` § Pre-1.0. The built-in
|
|
762
|
-
Claude Provider migrates in the same step (every kind declares its
|
|
763
|
-
`ui` block reusing the visuals previously hardcoded in the UI).
|
|
764
|
-
|
|
765
|
-
Conformance: new case `plugin-missing-ui-rejected` (with fixture
|
|
766
|
-
`plugin-missing-ui/`) locks the loader's behaviour against a drop-in
|
|
767
|
-
Provider that omits `ui` — `sm scan --json` exits 0, stderr matches
|
|
768
|
-
the canonical `must have required property 'ui'` diagnostic, and the
|
|
769
|
-
rest of the pipeline (built-in Claude) keeps running. Suite total:
|
|
770
|
-
5/5 passing across 2 scopes.
|
|
771
|
-
|
|
772
|
-
- **BFF `/api/nodes/:pathB64` body opt-in** — Step 14.5.a extends the
|
|
773
|
-
single-node detail endpoint with the optional `?include=body` query
|
|
774
|
-
parameter. When set, the response's `item` carries `body: string |
|
|
775
|
-
null` (the post-frontmatter file content read from disk on demand);
|
|
776
|
-
`null` indicates the source file was missing or unreadable when the
|
|
777
|
-
request landed. Without the flag, `item.body` stays `undefined` and
|
|
778
|
-
the handler does not touch the filesystem. The single-node response
|
|
779
|
-
shape is also corrected to the documented `{ schemaVersion, kind:
|
|
780
|
-
'node', item: Node, links: { incoming: Link[], outgoing: Link[] },
|
|
781
|
-
issues: Issue[] }` (the `### Server` table previously declared the
|
|
782
|
-
shape as the legacy `{ node, linksOut, linksIn, issues }` bundle —
|
|
783
|
-
see prose for the bug-fix rationale). No prod consumer ran against
|
|
784
|
-
the legacy shape, so the corrected shape ships as a minor.
|
|
785
|
-
|
|
786
|
-
**Why on-demand instead of persisting bodies in `scan_nodes`**: the
|
|
787
|
-
kernel persists `body_hash` only (per `db-schema.md` §scan_nodes) —
|
|
788
|
-
the body itself is human content, not machine state, and duplicating
|
|
789
|
-
it in SQLite would inflate the DB without serving any read-side
|
|
790
|
-
query the kernel cares about. Inspector cards that DO want to render
|
|
791
|
-
the body (markdown preview at Step 14.5) opt into the filesystem
|
|
792
|
-
re-read; the list / graph / kind-palette views never need it.
|
|
793
|
-
|
|
794
|
-
**Path-traversal defense**: the body reader (`src/server/node-body.ts`)
|
|
795
|
-
refuses absolute paths and any relative path that resolves outside
|
|
796
|
-
the scope root. A corrupted DB row or a future Provider that
|
|
797
|
-
forgets to sanitise its node paths cannot use this endpoint to leak
|
|
798
|
-
arbitrary files.
|
|
799
|
-
|
|
800
|
-
- **BFF `/ws` protocol + watcher contract** — Step 14.4.a documents
|
|
801
|
-
the WebSocket surface. The `### Server` subsection grows a
|
|
802
|
-
**WebSocket protocol** block enumerating the wire envelope (delegated
|
|
803
|
-
to `job-events.md` §Common envelope), the v14.4.a event catalog
|
|
804
|
-
(`scan.started` / `scan.progress` / `scan.completed` plus the
|
|
805
|
-
side-effect events `extractor.completed` / `rule.completed` /
|
|
806
|
-
`extension.error`, plus the BFF-internal advisories
|
|
807
|
-
`watcher.started` / `watcher.error`), the connection lifecycle (no
|
|
808
|
-
state push on connect; client polls `/api/scan` to seed; close codes
|
|
809
|
-
used: 1000 / 1001 / 1009), the backpressure rule (4 MiB `bufferedAmount`
|
|
810
|
-
→ close 1009 + unregister), and the loopback-only assumption (no
|
|
811
|
-
per-connection auth through v0.6.0 per Decision #119). The endpoint
|
|
812
|
-
table flips `GET /ws` from `upgrade-only` to `implemented (v14.4.a)`.
|
|
813
|
-
The `sm serve` flag table grows `--no-watcher` (default off — watcher
|
|
814
|
-
on per Decision #121); combining `--no-watcher` is documented;
|
|
815
|
-
combining `--no-built-ins` with the watcher is rejected (would
|
|
816
|
-
persist empty scans on every batch). The verb-catalog row for
|
|
817
|
-
`sm serve` mirrors the new flag.
|
|
818
|
-
|
|
819
|
-
`issue.added` / `issue.resolved` (the diff-based events from
|
|
820
|
-
`job-events.md` §Issue events) and `scan.failed` are explicitly
|
|
821
|
-
flagged as deferred — the 14.4.a surface fans out only what the
|
|
822
|
-
kernel emitter already produces; per-batch failure events and the
|
|
823
|
-
diff-based issue stream require additional plumbing in the BFF
|
|
824
|
-
watcher loop and land in a follow-up.
|
|
825
|
-
|
|
826
|
-
Additive minor per `versioning.md` § Pre-1.0; no breaking change to
|
|
827
|
-
any prior `/ws` behavior (the v14.1 / v14.2 / v14.3 surfaces all had
|
|
828
|
-
`/ws` documented as "upgrade-only — broadcaster lands at v14.4").
|
|
829
|
-
|
|
830
|
-
- **BFF read-side endpoints** — Step 14.2 fills the `### Server`
|
|
831
|
-
subsection's endpoint catalogue from the v14.1 stub
|
|
832
|
-
(`GET /api/health` real, `ALL /api/*` 404) to the eight read-side
|
|
833
|
-
endpoints the Web UI consumes:
|
|
834
|
-
`GET /api/scan` (latest persisted `ScanResult`; `?fresh=1` for an
|
|
835
|
-
in-memory scan), `GET /api/nodes` (paginated, filtered list — same
|
|
836
|
-
query grammar as `sm export`), `GET /api/nodes/:pathB64` (single-node
|
|
837
|
-
bundle; `pathB64` is base64url of `node.path`), `GET /api/links`,
|
|
838
|
-
`GET /api/issues`, `GET /api/graph?format=...` (formatter rendering),
|
|
839
|
-
`GET /api/config`, `GET /api/plugins`. New
|
|
840
|
-
[`schemas/api/rest-envelope.schema.json`](schemas/api/rest-envelope.schema.json)
|
|
841
|
-
formalises the list-envelope shape (`{ schemaVersion, kind, items \|
|
|
842
|
-
item \| value, filters, counts }`). The error envelope subsection
|
|
843
|
-
enumerates the v14.2 sources for each `code` value (`not-found` /
|
|
844
|
-
`bad-query` / `internal` / reserved `db-missing`). Stability stays
|
|
845
|
-
`experimental — locks at v0.6.0` (no change). Additive minor per
|
|
846
|
-
`versioning.md` § Pre-1.0; no breaking change — the v14.1 endpoint
|
|
847
|
-
catalogue was a stub explicitly slated to fill at v14.2.
|
|
848
|
-
|
|
849
|
-
- **`sm serve` row + `### Server` subsection** in `cli-contract.md` —
|
|
850
|
-
Step 14.1 promotes `sm serve` from an implementation-defined stub to a
|
|
851
|
-
documented surface. The verb row at `§Verb catalog` › `### Server`
|
|
852
|
-
expands the flag set to the full 14.1 contract: `--port` (default
|
|
853
|
-
`4242`), `--host` (default `127.0.0.1`, loopback-only through v0.6.0),
|
|
854
|
-
`--scope project|global`, `--db <path>`, `--no-built-ins`,
|
|
855
|
-
`--no-plugins`, `--open` / `--no-open`, `--dev-cors`, `--ui-dist
|
|
856
|
-
<path>` (hidden). New `#### Server` subsection documents the
|
|
857
|
-
single-port mandate, the boot-with-missing-DB resilience contract
|
|
858
|
-
(`/api/health` returns `db: 'missing'`), the v14.1 endpoint surface
|
|
859
|
-
(`GET /api/health` real, `ALL /api/*` 404 stubs, `GET /ws` upgrade-only,
|
|
860
|
-
static + SPA fallback), the structured error envelope shape, and the
|
|
861
|
-
flag table. Marked `*(Stability: experimental — locks at v0.6.0.)*` —
|
|
862
|
-
endpoints fill at v14.2, broadcaster at v14.4. Additive minor per
|
|
863
|
-
`versioning.md` § Pre-1.0 (no breaking change to the existing row's
|
|
864
|
-
semantics; the old wording was strictly less specific).
|
|
865
|
-
|
|
866
|
-
### Minor (breaking, pre-1.0)
|
|
867
|
-
|
|
868
|
-
- **`Node.kind` opens to any non-empty string (was the closed enum
|
|
869
|
-
`skill` / `agent` / `command` / `hook` / `note`).** The kernel always
|
|
870
|
-
permitted external Providers — `IProvider.kinds` is documented as
|
|
871
|
-
"open by design" so a future Cursor / Obsidian / Roo Provider can
|
|
872
|
-
declare its own kinds — but the `node.schema.json` enum + the
|
|
873
|
-
`scan_nodes.kind` SQL CHECK + the closed TS `NodeKind` union closed
|
|
874
|
-
three layers underneath. Effects:
|
|
875
|
-
- `node.schema.json#/properties/kind` switches from `enum: [...5
|
|
876
|
-
values]` to `{ "type": "string", "minLength": 1 }`. The
|
|
877
|
-
description still names the built-in Claude Provider catalog so
|
|
878
|
-
consumers know what to expect from the default install.
|
|
879
|
-
- `db-schema.md` drops the `CHECK in (...)` constraint on
|
|
880
|
-
`scan_nodes.kind` and `state_summaries.kind`. Both columns stay
|
|
881
|
-
`TEXT NOT NULL`.
|
|
882
|
-
- `extensions/action.schema.json#/.../filter/kind` (the per-kind
|
|
883
|
-
filter for action applicability) widens the same way: `items:
|
|
884
|
-
{ type: 'string', minLength: 1 }` instead of the closed enum.
|
|
885
|
-
Migration: consumers who validate exported `Node` JSON against
|
|
886
|
-
`node.schema.json` will now accept external-Provider kinds. Any
|
|
887
|
-
consumer that hard-coded the closed enum elsewhere (UI filter chip
|
|
888
|
-
set, scripted assertions) needs to widen to "string". The TS +
|
|
889
|
-
SQL counterpart lands in `@skill-map/cli` (kernel TS contract +
|
|
890
|
-
migration `002_open_node_kinds`).
|
|
891
|
-
- **`conformance-case.schema.json` — rename `setup.disableAllDetectors`
|
|
892
|
-
→ `setup.disableAllExtractors`.** Finishes the kind rename Detector →
|
|
893
|
-
Extractor introduced in 0.8.0 (Phase 2 of the plug-in model
|
|
894
|
-
overhaul). The previous name was a residue from an unfinished sweep
|
|
895
|
-
and never reached a release that consumed it.
|
|
896
|
-
- **`setup.disableAll{Providers,Extractors,Rules}` are now wired
|
|
897
|
-
end-to-end.** Until this release the toggles were declared in the
|
|
898
|
-
schema but the runner threaded them nowhere; the `kernel-empty-boot`
|
|
899
|
-
case happened to pass because its fixture is empty. The runner now
|
|
900
|
-
injects `SKILL_MAP_DISABLE_ALL_{PROVIDERS,EXTRACTORS,RULES}=1` into
|
|
901
|
-
the child process environment per toggle, and the CLI's scan
|
|
902
|
-
composer drops every extension of the disabled kind from the
|
|
903
|
-
in-scan pipeline (overriding granularity gates and `--no-built-ins`).
|
|
904
|
-
Migration: any case JSON authored against the unwired schema needs
|
|
905
|
-
to swap `disableAllDetectors` for `disableAllExtractors`; behaviour
|
|
906
|
-
changes only when the toggles were already `true` (those cases will
|
|
907
|
-
now actually disable the kind, where previously they relied on
|
|
908
|
-
fixture content for the same outcome).
|
|
909
|
-
|
|
910
|
-
### Patch
|
|
911
|
-
|
|
912
|
-
- Updated `conformance/cases/kernel-empty-boot.json` for the field
|
|
913
|
-
rename above.
|
|
914
|
-
- Updated `conformance/README.md` example for the field rename above.
|
|
915
|
-
- Schema docstrings added to each `disableAll*` property documenting
|
|
916
|
-
the env-var convention the runner uses.
|
|
917
|
-
|
|
918
|
-
## 0.8.0
|
|
919
|
-
|
|
920
|
-
### Minor Changes
|
|
921
|
-
|
|
922
|
-
- 6dad772: v0.8.0 — Pre-1.0 stabilization pass.
|
|
923
|
-
|
|
924
|
-
This release combines two coherent pre-1.0 cleanup pieces that
|
|
925
|
-
both push the project closer to v1.0 stability: the cli-architect
|
|
926
|
-
audit review pass and the plugin model overhaul.
|
|
927
|
-
|
|
928
|
-
Pre-1.0 minor bumps per `versioning.md` § Pre-1.0; breaking
|
|
929
|
-
changes allowed within minor while in `0.Y.Z`. No real downstream
|
|
930
|
-
ecosystem exists yet, so the breaking surface costs nothing
|
|
931
|
-
today.
|
|
932
|
-
|
|
933
|
-
## Part 1 — Pre-1.0 audit review pass
|
|
934
|
-
|
|
935
|
-
Pre-1.0 review pass — `cli-architect` audit findings.
|
|
936
|
-
|
|
937
|
-
Internal audit run by the `cli-architect` agent in REVIEW mode
|
|
938
|
-
produced a Critical / High / Medium / Low / Nit catalog. This
|
|
939
|
-
pass bundles the implementation of every actionable finding into
|
|
940
|
-
one unit so the review can be read end-to-end. **Pre-1.0 minor
|
|
941
|
-
bump**: a few breaking surface changes ride along (CLI sub-verb
|
|
942
|
-
split, exit-code enum exposed, plugin loader option). No
|
|
943
|
-
published downstream consumers exist yet.
|
|
944
|
-
|
|
945
|
-
### Spec changes (`@skill-map/spec`)
|
|
946
|
-
|
|
947
|
-
- **`cli-contract.md`** — `sm scan compare-with <dump> [roots...]`
|
|
948
|
-
is now a sub-verb instead of a `--compare-with <path>` flag on
|
|
949
|
-
`sm scan`. Read-only delta report against a saved `ScanResult`
|
|
950
|
-
JSON dump. Read-only — does not modify the DB. Same exit codes
|
|
951
|
-
(`0` empty delta / `1` drift / `2` operational error). Old flag
|
|
952
|
-
form removed.
|
|
953
|
-
- **`cli-contract.md`** — exit-code `2` "Operational error" row
|
|
954
|
-
clarified to mention environment / runtime mismatches (wrong
|
|
955
|
-
Node version, missing native dependency) explicitly. The
|
|
956
|
-
"unhandled exception" catch-all already covered the case; this
|
|
957
|
-
just removes ambiguity for future implementers.
|
|
958
|
-
- **`cli-contract.md`** — new normative section **§Dry-run**
|
|
959
|
-
between §Exit codes and §Verb catalog defining the contract for
|
|
960
|
-
any verb exposing `-n` / `--dry-run`: no observable side effects
|
|
961
|
-
(DB / FS / config / network / spawns), no auto-provisioning of
|
|
962
|
-
scope directories, output mirrors the live mode with explicit
|
|
963
|
-
"would …" framing, exit codes mirror the live mode, dry-run
|
|
964
|
-
MUST short-circuit `--yes` / `--force` confirmation prompts.
|
|
965
|
-
Per-verb opt-in: the flag is not global, verbs that don't
|
|
966
|
-
declare it MUST reject it as an unknown option. Verb catalog
|
|
967
|
-
rows for `sm init`, `sm db reset` (default + `--state` +
|
|
968
|
-
`--hard`), and `sm db restore` amended to declare and describe
|
|
969
|
-
their `--dry-run` previews.
|
|
970
|
-
|
|
971
|
-
### CLI changes (`@skill-map/cli`)
|
|
972
|
-
|
|
973
|
-
#### Critical — kernel & adapter hygiene
|
|
974
|
-
|
|
975
|
-
- **C1 — `runScanInternal` decomposed.** The 290-line monolith in
|
|
976
|
-
`kernel/orchestrator.ts` split into a thin composer + four pure
|
|
977
|
-
functions: `validateRoots`, `indexPriorSnapshot`,
|
|
978
|
-
`walkAndDetect`, `runRules`. Composer is now 89 lines reading
|
|
979
|
-
top-to-bottom through the pipeline phases. Zero behavioural
|
|
980
|
-
change.
|
|
981
|
-
- **C2 — `withSqlite(options, fn)` helper.** Single utility at
|
|
982
|
-
`cli/util/with-sqlite.ts` standardises the open / use / close
|
|
983
|
-
idiom every read-side command was open-coding. Eliminates four
|
|
984
|
-
classes of boilerplate bugs (forgotten close, `autoBackup`
|
|
985
|
-
drift, double-close, missing `try/finally`). Migrated 20 call
|
|
986
|
-
sites across `check`, `export`, `graph`, `history`, `init`,
|
|
987
|
-
`jobs`, `list`, `orphans`, `plugins`, `scan`, `show`, `watch`,
|
|
988
|
-
plus `cli/util/plugin-runtime.ts`. Companion `tryWithSqlite`
|
|
989
|
-
short-circuits when the DB file does not exist, replacing the
|
|
990
|
-
`if (existsSync) { withSqlite(...) }` chain. In `scan.ts` the
|
|
991
|
-
read-prior + persist double-open consolidated into a single
|
|
992
|
-
`withSqlite` callback that brackets read prior → run scan →
|
|
993
|
-
guard → persist when `willPersist`. Saves one migration
|
|
994
|
-
discovery pass + one WAL setup per normal scan (~50–100ms).
|
|
995
|
-
|
|
996
|
-
#### High — UX & contract integrity
|
|
997
|
-
|
|
998
|
-
- **H3 — `--dry-run` semantics unified across `init` / `db reset`
|
|
999
|
-
/ `db restore`.** The new spec §Dry-run codifies the "no
|
|
1000
|
-
writes, reads OK" contract; three verbs that did not previously
|
|
1001
|
-
expose a preview now do: - `sm init --dry-run` — previews the would-create lines for
|
|
1002
|
-
`.skill-map/`, `settings.json`, `settings.local.json`,
|
|
1003
|
-
`.skill-mapignore`, the `.gitignore` entries that would be
|
|
1004
|
-
appended (deduped against the existing file), the DB
|
|
1005
|
-
provisioning, and the first-scan trigger. Honours `--force`
|
|
1006
|
-
for the would-overwrite preview. Re-init over an existing
|
|
1007
|
-
scope without `--force` still exits 2 (same gate as live). - `sm db reset --dry-run` (default + `--state`) — opens the DB
|
|
1008
|
-
read-only, computes the row count per `scan_*` (and `state_*`
|
|
1009
|
-
when `--state`) table, and prints them. No `DELETE`
|
|
1010
|
-
statements issued. Bypasses the `--state` confirmation prompt
|
|
1011
|
-
entirely. - `sm db reset --hard --dry-run` — reports the DB file path and
|
|
1012
|
-
size that would be unlinked; missing-file case prints a clear
|
|
1013
|
-
no-op line instead of an error. - `sm db restore <src> --dry-run` — validates the source exists
|
|
1014
|
-
(still exits 5 if missing), reports the source size and
|
|
1015
|
-
whether the target would be created or overwritten, plus the
|
|
1016
|
-
WAL / SHM sidecars that would be dropped. Bypasses the
|
|
1017
|
-
confirmation prompt.
|
|
1018
|
-
Implementation: new helper `previewGitignoreEntries(scopeRoot,
|
|
1019
|
-
entries)` in `init.ts` mirrors `ensureGitignoreEntries` parsing
|
|
1020
|
-
so the preview tracks the live outcome exactly. Texts moved
|
|
1021
|
-
into `cli/i18n/init.texts.ts` and `cli/i18n/db.texts.ts` per
|
|
1022
|
-
the N4 pattern. **9 new tests** under `init-cli.test.ts` (5
|
|
1023
|
-
cases) and `db-cli.test.ts` (9 cases) cover the previews + the
|
|
1024
|
-
spec invariants ("DB file checksum unchanged after dry-run",
|
|
1025
|
-
"scope directory absent after dry-run", "source-not-found
|
|
1026
|
-
still exits 5", "confirmation prompt skipped under dry-run").
|
|
1027
|
-
- **H1 — Centralised exit codes.** New `cli/util/exit-codes.ts`
|
|
1028
|
-
exporting `ExitCode` (`Ok` / `Issues` / `Error` / `Duplicate` /
|
|
1029
|
-
`NonceMismatch` / `NotFound`) and the type alias `TExitCode`.
|
|
1030
|
-
Every `Command#execute()` migrated from numeric literals (123
|
|
1031
|
-
sites across 17 files) to the enum. Single source of truth
|
|
1032
|
-
aligned with `spec/cli-contract.md` §Exit codes. **Bug fix
|
|
1033
|
-
surfaced en passant:** `sm job prune` returned `2` for "DB
|
|
1034
|
-
missing" while every other read-side verb returned `5` via
|
|
1035
|
-
`assertDbExists`; corrected to use the shared helper and return
|
|
1036
|
-
`NotFound`. Companion test updated to expect `5`.
|
|
1037
|
-
- **H2 — Plugin loader timeout.** `IPluginLoaderOptions.loadTimeoutMs`
|
|
1038
|
-
(default `5000`, exported as `DEFAULT_PLUGIN_IMPORT_TIMEOUT_MS`).
|
|
1039
|
-
Each dynamic `import()` now races against a timer; on timeout
|
|
1040
|
-
the plugin is reported as `load-error` with a message naming
|
|
1041
|
-
the elapsed budget and pointing at top-level side effects as
|
|
1042
|
-
the likely cause (network call, infinite loop, large blocking
|
|
1043
|
-
work). Without this a plugin with a hanging top-level `await`
|
|
1044
|
-
blocks every host CLI command indefinitely.
|
|
1045
|
-
- **H4 — `--strict` self-validates `--json` output.** When
|
|
1046
|
-
`sm scan --strict --json` is invoked, the produced `ScanResult`
|
|
1047
|
-
is validated against `scan-result.schema.json` before stdout.
|
|
1048
|
-
Catches the case where a custom detector emits a Link that
|
|
1049
|
-
passes the shallow `validateLink` guard but fails the full
|
|
1050
|
-
schema, which would silently land in stdout and break a
|
|
1051
|
-
downstream `sm scan compare-with -`.
|
|
1052
|
-
- **H5 — External-link discrimination uses URL-shape regex.**
|
|
1053
|
-
`isExternalUrlLink` was string-matching `http://` / `https://`
|
|
1054
|
-
only; any other URL scheme (`mailto:`, `data:`, `file:///`,
|
|
1055
|
-
`ftp://`) was silently classified as internal and polluted the
|
|
1056
|
-
graph as a fake internal link with `byPath` lookups that always
|
|
1057
|
-
missed. Replaced with the RFC 3986 scheme regex
|
|
1058
|
-
(`/^[a-z][a-z0-9+\-.]+:/i`), guarding against Windows-style
|
|
1059
|
-
absolute paths via the ≥ 2-char scheme constraint.
|
|
1060
|
-
- **H6 — Prior snapshot validated under `--strict`.** Both
|
|
1061
|
-
`sm scan` and `sm watch`, when run with `--strict`, validate
|
|
1062
|
-
the DB-resident `ScanResult` against the spec schema before
|
|
1063
|
-
handing it to the orchestrator. A DB corrupted manually or
|
|
1064
|
-
mid-rollback used to slip nodes with malformed `bodyHash` /
|
|
1065
|
-
`frontmatterHash` into the rename heuristic, where the
|
|
1066
|
-
dereference would silently produce spurious matches.
|
|
1067
|
-
|
|
1068
|
-
#### Medium — surface & extensibility
|
|
1069
|
-
|
|
1070
|
-
- **M1 — `sm scan compare-with` sub-verb.** New
|
|
1071
|
-
`ScanCompareCommand` in `cli/commands/scan-compare.ts`; the
|
|
1072
|
-
`--compare-with` flag is removed from `ScanCommand`. The
|
|
1073
|
-
sub-verb form structurally rejects flag combos that used to
|
|
1074
|
-
require runtime guards (`--changed`, `--no-built-ins`,
|
|
1075
|
-
`--allow-empty`, `--watch`): Clipanion rejects them at parse
|
|
1076
|
-
time as unknown options.
|
|
1077
|
-
- **M2 — `kernel/index.ts` enumerated exports.** Replaced the two
|
|
1078
|
-
`export type *` wildcards (from `./types.js` and
|
|
1079
|
-
`./ports/index.js`) with explicit named exports. Same set of
|
|
1080
|
-
public types — the DTS size and tests confirm parity. Going
|
|
1081
|
-
forward, any new domain type or port change requires an
|
|
1082
|
-
explicit edit to the barrel, preventing silent surface drift.
|
|
1083
|
-
- **M3 — Build hack documented (workaround retained).** Tried to
|
|
1084
|
-
replace the post-build `restoreNodeSqliteImports` pass with
|
|
1085
|
-
`external: ['node:sqlite']` in `tsup.config.ts`. Esbuild marks
|
|
1086
|
-
the specifier as external but still strips the `node:` prefix;
|
|
1087
|
-
same outcome with `[/^node:/]` regex and `packages: 'external'`
|
|
1088
|
-
(which also externalises real npm deps). Reverted to the
|
|
1089
|
-
post-build `replaceAll` pass, with a docstring documenting
|
|
1090
|
-
every workaround attempted so the next agent does not repeat
|
|
1091
|
-
the spike.
|
|
1092
|
-
- **M4 — `tryWithSqlite` helper.** See C2.
|
|
1093
|
-
- **M5 — `CamelCasePlugin` trap documented.** Added a
|
|
1094
|
-
trap-warning block to `SqliteStorageAdapter`'s docstring:
|
|
1095
|
-
`sql.raw` / `sql\`...\``template literals do NOT pass through
|
|
1096
|
-
the`CamelCasePlugin`; raw SQL fragments must use snake_case to
|
|
1097
|
-
match the migrations.
|
|
1098
|
-
- **M6 — Per-extension error reporting.** When the orchestrator
|
|
1099
|
-
drops a link emitted with an undeclared kind or an issue with
|
|
1100
|
-
an invalid severity, it now emits a `type: 'extension.error'`
|
|
1101
|
-
`ProgressEvent` instead of silently swallowing. The CLI
|
|
1102
|
-
subscribes via the new `createCliProgressEmitter(stderr)`
|
|
1103
|
-
helper and renders those events as `extension.error: <message>`
|
|
1104
|
-
on stderr. Plugin authors finally see WHY their link / issue
|
|
1105
|
-
disappears from the result. Wired in `scan` (normal +
|
|
1106
|
-
compare-with), `watch`, and `init`.
|
|
1107
|
-
- **M7 — Type naming convention documented (no rename).** Top-of-
|
|
1108
|
-
file docstring in `kernel/types.ts` and a new section in
|
|
1109
|
-
`AGENTS.md` describe the four-bucket convention the codebase
|
|
1110
|
-
has always implicitly followed: domain types (no prefix,
|
|
1111
|
-
mirrors spec schemas), hexagonal ports (`Port` suffix), runtime
|
|
1112
|
-
extension contracts (`I` prefix), internal shapes (`I`
|
|
1113
|
-
prefix). Mass rename was rejected after a cost-benefit pass —
|
|
1114
|
-
naming changes are cheap to write but expensive to review;
|
|
1115
|
-
existing names are mostly coherent. The agent base
|
|
1116
|
-
(`_plugins/minions/shared/architect.md`) gained a "Naming
|
|
1117
|
-
conventions check" sub-section in REVIEW mode so future audits
|
|
1118
|
-
reach the same conclusion.
|
|
1119
|
-
|
|
1120
|
-
#### Low / nit — cleanup
|
|
1121
|
-
|
|
1122
|
-
- **L1 — `omitModule` JSON replacer precision.** Identifies the
|
|
1123
|
-
ESM namespace by `[Symbol.toStringTag] === 'Module'` instead of
|
|
1124
|
-
matching every `module` key blindly. A plugin manifest that
|
|
1125
|
-
legitimately ships an unrelated `module` field (e.g. a string
|
|
1126
|
-
property in `metadata`) is no longer silently dropped from
|
|
1127
|
-
`sm plugins list --json` output.
|
|
1128
|
-
- **L2 — Stub verbs flagged in `--help`.** Every
|
|
1129
|
-
`not-yet-implemented` verb in `cli/commands/stubs.ts` carries a
|
|
1130
|
-
`(planned)` suffix on its `description`, surfaced in
|
|
1131
|
-
`sm --help`. The `notImplemented` helper now writes
|
|
1132
|
-
`<verb>: not yet implemented (planned).` on stderr instead of
|
|
1133
|
-
promising a specific Step number — roadmap step numbers shift
|
|
1134
|
-
mid-flight, stale promises in `--help` are worse than no
|
|
1135
|
-
promise.
|
|
1136
|
-
- **L3 — Dead `eslint-disable` removed** from
|
|
1137
|
-
`cli/util/plugin-runtime.ts`.
|
|
1138
|
-
- **N1 — `Link.source` vs `Link.sources` doc clarified.** Both
|
|
1139
|
-
fields now carry inline doc-comments calling out the singular /
|
|
1140
|
-
plural naming trap. Spec-frozen, but the ambiguity is the
|
|
1141
|
-
easiest way to misread the type for new contributors.
|
|
1142
|
-
- **N2 — `sm check` Usage examples expanded.** The `-g/--global`
|
|
1143
|
-
and `--db <path>` flags were declared but missing from the
|
|
1144
|
-
`Usage.examples` block — asymmetry with `sm scan` and the rest
|
|
1145
|
-
of the read-side verbs that ship the same flags. Two examples
|
|
1146
|
-
added: `sm check --global` and `sm check --db
|
|
1147
|
-
/path/to/skill-map.db`.
|
|
1148
|
-
- **N4 — Error / hint strings extracted to `*.texts.ts` modules
|
|
1149
|
-
with `{{name}}` template interpolation.** Pre-1.0 is the
|
|
1150
|
-
natural moment to seed the pattern before the string set grows.
|
|
1151
|
-
The workspace `ui/` already has a sibling layout at
|
|
1152
|
-
`ui/src/i18n/` (functions returning template literals); CLI
|
|
1153
|
-
takes a deliberately different shape — flat string templates
|
|
1154
|
-
with `{{name}}` placeholders, interpolated by a tiny
|
|
1155
|
-
`tx(template, vars)` helper. Rationale: the template form is
|
|
1156
|
-
**drop-in compatible with Transloco / Mustache / Handlebars**
|
|
1157
|
-
(the syntax they all share) so the day this project migrates to
|
|
1158
|
-
a real i18n library, the strings move as-is. Functions would
|
|
1159
|
-
have to be re-shaped first.
|
|
1160
|
-
|
|
1161
|
-
Helper at `kernel/util/tx.ts`. Contract:
|
|
1162
|
-
|
|
1163
|
-
- Every `{{name}}` token MUST have a matching key in the vars
|
|
1164
|
-
object — missing key throws (silent fallback hides
|
|
1165
|
-
forgotten args in production).
|
|
1166
|
-
- `null` / `undefined` values throw — caller coerces
|
|
1167
|
-
upstream.
|
|
1168
|
-
- Whitespace inside the braces tolerated (`{{ name }}`) so
|
|
1169
|
-
long templates wrap cleanly across `+`-joined lines.
|
|
1170
|
-
- Plural / conditional logic does NOT live in the template;
|
|
1171
|
-
the caller picks `*_singular` vs `*_plural` keys.
|
|
1172
|
-
|
|
1173
|
-
Files created:
|
|
1174
|
-
|
|
1175
|
-
- `kernel/util/tx.ts` — the helper itself, with 13 tests in
|
|
1176
|
-
`test/tx.test.ts` (single / multi token, whitespace,
|
|
1177
|
-
missing / null / undefined keys, identifier shapes, error
|
|
1178
|
-
truncation).
|
|
1179
|
-
- `kernel/i18n/orchestrator.texts.ts` — frontmatter
|
|
1180
|
-
malformed/invalid templates, `extension.error` payloads,
|
|
1181
|
-
root validation errors.
|
|
1182
|
-
- `kernel/i18n/plugin-loader.texts.ts` — every `load-error` /
|
|
1183
|
-
`invalid-manifest` / `incompatible-spec` reason, plus the
|
|
1184
|
-
import timeout message.
|
|
1185
|
-
- `cli/i18n/scan.texts.ts` — `sm scan` flag-clash / scan
|
|
1186
|
-
failure / guard / summary templates, plus the `sm scan
|
|
1187
|
-
|
|
1188
|
-
compare-with`dump-load errors.
|
|
1189
|
-
|
|
1190
|
-
-`cli/i18n/watch.texts.ts`—`sm watch`lifecycle templates. -`cli/i18n/init.texts.ts`—`sm init`templates including
|
|
1191
|
-
the`--dry-run`previews and the singular/plural pair for
|
|
1192
|
-
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
|
|
1193
|
-
`extension.error: ...` stderr line.
|
|
1194
|
-
|
|
1195
|
-
String content moved verbatim — every existing test that
|
|
1196
|
-
matches on stderr / stdout content keeps passing. Trivial
|
|
1197
|
-
single-token strings (`'No issues.\n'`) and rare per-handler
|
|
1198
|
-
bespoke phrases stay inline; the pattern is now established
|
|
1199
|
-
for whoever wants to migrate them in a follow-up.
|
|
1200
|
-
|
|
1201
|
-
Note on `ui/` divergence: today the two workspaces use
|
|
1202
|
-
different shapes for their text tables (functions in `ui/`,
|
|
1203
|
-
templates in `cli/`). Aligning them is a follow-up — the day a
|
|
1204
|
-
real i18n library lands, both converge on its native shape.
|
|
1205
|
-
The CLI shape is closer to the eventual destination.
|
|
1206
|
-
|
|
1207
|
-
- **N6 — `TIssueSeverity` aliased to `Severity`.** SQLite schema
|
|
1208
|
-
type now reads `type TIssueSeverity = Severity` instead of
|
|
1209
|
-
duplicating the union literal. Keeps DB and runtime in
|
|
1210
|
-
lock-step if the union ever evolves.
|
|
1211
|
-
|
|
1212
|
-
### Migrations consolidation (kernel DB)
|
|
1213
|
-
|
|
1214
|
-
- **`src/migrations/001_initial.sql` + `002_scan_meta.sql`**
|
|
1215
|
-
consolidated into a single `001_initial.sql`. Pre-1.0 with no
|
|
1216
|
-
released DBs to forward-migrate, the two-file split was a
|
|
1217
|
-
historical accident from an incremental shipment. After
|
|
1218
|
-
consolidation: same 12 tables, same constraints, same indexes;
|
|
1219
|
-
`PRAGMA user_version` of a freshly-initialised DB is now `1`
|
|
1220
|
-
instead of `2`. Migration runner is unchanged (it tolerates any
|
|
1221
|
-
count of `NNN_*.sql` files).
|
|
1222
|
-
|
|
1223
|
-
### Test coverage (Part 1)
|
|
1224
|
-
|
|
1225
|
-
- New tests for H2 (plugin loader timeout — 2 cases),
|
|
1226
|
-
M6 (orchestrator `extension.error` emission — 3 cases),
|
|
1227
|
-
CLI progress emitter wiring (4 cases). The compare-with suite
|
|
1228
|
-
(`scan-compare.test.ts`, 9 cases) was migrated to
|
|
1229
|
-
`ScanCompareCommand` and the three flag-clash tests dropped
|
|
1230
|
-
(the flags are now structurally absent on the sub-verb). Test
|
|
1231
|
-
totals: 479 (start of pass) → 488 (after H2/M6 tests) → 485
|
|
1232
|
-
(after the three flag-clash deletions).
|
|
1233
|
-
|
|
1234
|
-
### Deferred / out of scope
|
|
1235
|
-
|
|
1236
|
-
The findings below were reviewed but did not warrant code
|
|
1237
|
-
changes; each has its own resolution noted alongside.
|
|
1238
|
-
|
|
1239
|
-
- **L4 — `runScan` / `runScanWithRenames` unification.** Already
|
|
1240
|
-
resolved by C1 (both are thin wrappers around
|
|
1241
|
-
`runScanInternal`).
|
|
1242
|
-
- **L5 — Node-version-guard exit code.** Reviewed against the
|
|
1243
|
-
updated exit-code table; existing `2` is correct under
|
|
1244
|
-
"operational error / unhandled exception". Spec table got the
|
|
1245
|
-
environment-mismatch clarification (above).
|
|
1246
|
-
- **L6 — `loadSchemaValidators()` cache.** Already cached at
|
|
1247
|
-
module level since Step 5.12.
|
|
1248
|
-
- **L7 — `pkg with { type: 'json' }` portability.** Stable in
|
|
1249
|
-
Node ≥ 22; `engines.node": ">=24.0"` covers it. No fallback
|
|
1250
|
-
needed.
|
|
1251
|
-
- **N3 — `compare-with` "dump not found" exit code.** The error
|
|
1252
|
-
paths in `ScanCompareCommand` already use the `ExitCode.Error`
|
|
1253
|
-
enum (= 2) for dump load failures, matching the spec clause for
|
|
1254
|
-
operational errors.
|
|
1255
|
-
- **N5 — Exit-code list completeness.** Verified the comment in
|
|
1256
|
-
`cli/entry.ts` against `spec/cli-contract.md` §Exit codes —
|
|
1257
|
-
identical, no edit needed.
|
|
1258
|
-
|
|
1259
|
-
## Part 2 — Plugin model overhaul (5-phase implementation)
|
|
1260
|
-
|
|
1261
|
-
### Summary
|
|
1262
|
-
|
|
1263
|
-
The plugin model received a comprehensive overhaul before
|
|
1264
|
-
stabilizing at v1.0. Plugin kinds total after this bump: **6**
|
|
1265
|
-
(Provider, Extractor, Rule, Action, Formatter, Hook). All
|
|
1266
|
-
breakings are pre-1.0 minor per `versioning.md` § Pre-1.0.
|
|
1267
|
-
|
|
1268
|
-
### Phase 1 (commit 7354c26) — Foundation
|
|
1269
|
-
|
|
1270
|
-
Five sub-phases, additive or pre-1.0 minor breakings:
|
|
1271
|
-
|
|
1272
|
-
- **A.4** — three-tier frontmatter validation model documented in
|
|
1273
|
-
`plugin-author-guide.md` (default permissive + `unknown-field`
|
|
1274
|
-
rule + `scan.strict` promote-to-error). Behavior unchanged.
|
|
1275
|
-
- **A.5** — plugin id global uniqueness: `directory ==
|
|
1276
|
-
manifest.id` rule, new status `id-collision` (sixth),
|
|
1277
|
-
validation in boot/scan/doctor. Cross-root collisions block
|
|
1278
|
-
both involved plugins; user resolves by renaming.
|
|
1279
|
-
- **A.6** — extension ids qualified `<plugin-id>/<ext-id>` in
|
|
1280
|
-
registry. Built-ins classified into `claude/*` (4 Claude-
|
|
1281
|
-
specific) and `core/*` (7 kernel built-ins) bundles. New
|
|
1282
|
-
`Registry.get/find` APIs; `defaultRefreshAction` schema
|
|
1283
|
-
requires the qualified pattern; `extension.error` events emit
|
|
1284
|
-
qualified ids.
|
|
1285
|
-
- **A.10** — optional `applicableKinds` filter on Detector
|
|
1286
|
-
manifest; fail-fast skip for non-matching kinds (zero CPU/LLM
|
|
1287
|
-
cost); doctor warning for kinds not declared by any installed
|
|
1288
|
-
Provider. Empty array invalid; absence preserves apply-to-all
|
|
1289
|
-
default.
|
|
1290
|
-
- **Granularity** — Built-ins now respect `config_plugins`
|
|
1291
|
-
enable/disable via granularity-aware filtering. New
|
|
1292
|
-
`IBuiltInBundle` shape with `granularity: 'bundle' |
|
|
1293
|
-
'extension'`; `claude` ships as bundle (all-or-nothing), `core`
|
|
1294
|
-
as extension (each toggleable). User plugins default to bundle;
|
|
1295
|
-
opt in via `granularity` in `plugin.json`. Both plugin ids and
|
|
1296
|
-
qualified extension ids accepted as keys in `config_plugins`
|
|
1297
|
-
and `settings.json#/plugins` (no schema change needed).
|
|
1298
|
-
|
|
1299
|
-
550/550 tests pass (+33 vs baseline 517).
|
|
1300
|
-
|
|
1301
|
-
### Phase 2 (commit ae3eaa6) — Renames
|
|
1302
|
-
|
|
1303
|
-
Four sub-phases, all breaking but allowed in minor pre-1.0:
|
|
1304
|
-
|
|
1305
|
-
- **2a (Renderer → Formatter)** — Kind, types, files renamed.
|
|
1306
|
-
Method `render(ctx)` → `format(ctx)`; manifest field `format`
|
|
1307
|
-
→ `formatId` (TS clash resolution). Same contract: graph →
|
|
1308
|
-
string, deterministic-only.
|
|
1309
|
-
- **2b (Adapter → Provider)** — New required field
|
|
1310
|
-
`explorationDir` on the manifest (e.g. `~/.claude` for the
|
|
1311
|
-
Claude Provider). DB schema migrated in-place (column
|
|
1312
|
-
`nodes.adapter` → `nodes.provider`, etc.). The
|
|
1313
|
-
hexagonal-architecture `RunnerPort.adapter` /
|
|
1314
|
-
`StoragePort.adapter` is unchanged.
|
|
1315
|
-
- **2c (Audit removed)** — Audit kind removed. The single
|
|
1316
|
-
built-in `validate-all` migrated to a Rule (qualified id
|
|
1317
|
-
`core/validate-all`, `evaluate(ctx) → Issue[]`). CLI verbs
|
|
1318
|
-
`sm audit *` removed; users invoke via `sm check --rules
|
|
1319
|
-
core/validate-all`.
|
|
1320
|
-
- **2d (Detector → Extractor)** — Method signature changes from
|
|
1321
|
-
`detect(ctx) → Link[]` to `extract(ctx) → void` — output flows
|
|
1322
|
-
through three ctx callbacks: `emitLink`, `enrichNode`, `store`.
|
|
1323
|
-
Built-ins migrated maintain functional parity using `emitLink`.
|
|
1324
|
-
Persistence of `enrichNode` deferred to Phase 4 (A.8 stale
|
|
1325
|
-
layer); orchestrator buffers in memory today.
|
|
1326
|
-
|
|
1327
|
-
554/554 cli + 32/32 testkit pass.
|
|
1328
|
-
|
|
1329
|
-
### Phase 3 (commit 34f993e) — Schema relocation
|
|
1330
|
-
|
|
1331
|
-
**A.2** — Per-kind frontmatter schemas relocate from spec to the
|
|
1332
|
-
Provider that declares them. Spec keeps only `frontmatter/base`
|
|
1333
|
-
(universal).
|
|
1334
|
-
|
|
1335
|
-
- 5 schemas moved (`git mv`):
|
|
1336
|
-
`spec/schemas/frontmatter/{skill,agent,command,hook,note}.schema.json`
|
|
1337
|
-
→ built-in Claude Provider's `schemas/` directory. New `$id`:
|
|
1338
|
-
`https://skill-map.dev/providers/claude/v1/frontmatter/<kind>`.
|
|
1339
|
-
Cross-package `$ref` resolves via the spec base's `$id`
|
|
1340
|
-
(`https://skill-map.dev/spec/v0/frontmatter/base.schema.json`);
|
|
1341
|
-
AJV resolves by `$id` when both schemas register on the same
|
|
1342
|
-
instance.
|
|
1343
|
-
- Provider manifest gains a required `kinds` map subsuming three
|
|
1344
|
-
former fields: `emits` (now derives from
|
|
1345
|
-
`Object.keys(kinds)`), the flat `defaultRefreshAction` map (now
|
|
1346
|
-
per-entry inside `kinds[<kind>].defaultRefreshAction`), and the
|
|
1347
|
-
new `schema` (path to the per-kind schema relative to the
|
|
1348
|
-
provider directory).
|
|
1349
|
-
- Built-in Claude Provider migrated: 5 kind entries (skill,
|
|
1350
|
-
agent, command, hook, note), each with `schema`, `schemaJson`
|
|
1351
|
-
(runtime field, AJV-compiled at load), and qualified
|
|
1352
|
-
`defaultRefreshAction` (`claude/summarize-<kind>`).
|
|
1353
|
-
- Kernel orchestrator parse phase asks the Provider for the
|
|
1354
|
-
schema via `IProviderFrontmatterValidator` (composed by scan
|
|
1355
|
-
via `buildProviderFrontmatterValidator`) instead of reading
|
|
1356
|
-
from spec/. Flow: validate base → look up provider → validate
|
|
1357
|
-
per-kind schema from Provider.
|
|
1358
|
-
- `schema-validators.ts` catalog loses the 5 per-kind frontmatter
|
|
1359
|
-
entries; only `frontmatter-base` remains kernel-known.
|
|
1360
|
-
`plugin-loader`'s `stripFunctionsAndPluginId` now also strips
|
|
1361
|
-
`schemaJson` (runtime-only) from each `kinds` entry before
|
|
1362
|
-
AJV-validating the manifest.
|
|
1363
|
-
- Coverage matrix: 28 → 23 schemas (the 5 per-kind frontmatter
|
|
1364
|
-
schemas are now Provider-owned and ship with their own
|
|
1365
|
-
conformance suite in Phase 5 / A.13).
|
|
1366
|
-
|
|
1367
|
-
556/556 cli + 32/32 testkit pass.
|
|
1368
|
-
|
|
1369
|
-
### Phase 4 (commit e62695f) — Probabilistic infra
|
|
1370
|
-
|
|
1371
|
-
Five sub-phases, all breaking but allowed in minor pre-1.0:
|
|
1372
|
-
|
|
1373
|
-
- **4a (A.9)** — fine-grained Extractor cache via new
|
|
1374
|
-
`scan_extractor_runs` table. Resolves gap where newly
|
|
1375
|
-
registered Extractors silently skipped cached nodes; cache hit
|
|
1376
|
-
logic now per-(node, extractor). Uninstalled Extractors cleaned
|
|
1377
|
-
(rows + orphan links). Migration in-place.
|
|
1378
|
-
- **4b (A.12)** — opt-in `outputSchema` for plugin custom
|
|
1379
|
-
storage. Manifest gains `storage.schema` (Mode A) and
|
|
1380
|
-
`storage.schemas` (Mode B) for AJV validation of
|
|
1381
|
-
`ctx.store.write/.set` calls. Throws on shape violation;
|
|
1382
|
-
default absent = permissive.
|
|
1383
|
-
- **4c (A.8)** — enrichment layer + stale tracking. New
|
|
1384
|
-
`node_enrichments` table persists per-(node, extractor)
|
|
1385
|
-
partials separately from author's frontmatter (immutable).
|
|
1386
|
-
Probabilistic enrichments track `body_hash_at_enrichment`; scan
|
|
1387
|
-
flags `stale=1` on body change (NOT deleted, preserves LLM
|
|
1388
|
-
cost). Helper `mergeNodeWithEnrichments` filters stale +
|
|
1389
|
-
last-write-wins. New verbs `sm refresh <node>` and
|
|
1390
|
-
`sm refresh --stale` (stubs awaiting Step 10).
|
|
1391
|
-
- **4d (A.11)** — sixth plugin kind `hook`. Declarative
|
|
1392
|
-
subscriber to a curated set of 8 lifecycle events (`scan.*`,
|
|
1393
|
-
extractor/rule/action.completed,
|
|
1394
|
-
job.spawning/completed/failed). Other events deliberately not
|
|
1395
|
-
hookable. Manifest declares `triggers[]` (load-time validated)
|
|
1396
|
-
and optional `filter`. Three new kernel events added to
|
|
1397
|
-
catalog. Dual-mode (det dispatched in-process; prob deferred to
|
|
1398
|
-
Step 10).
|
|
1399
|
-
- **4e (A.7)** — `sm check --include-prob` opt-in flag (stub).
|
|
1400
|
-
Default `sm check` unchanged: det only, CI-safe. With flag:
|
|
1401
|
-
detects prob rules, emits stderr advisory; full dispatch awaits
|
|
1402
|
-
Step 10. Combines with `--rules`, `-n`, `--no-plugins`.
|
|
1403
|
-
|
|
1404
|
-
591/591 cli + 32/32 testkit pass.
|
|
1405
|
-
|
|
1406
|
-
### Phase 5 (commit 03b5a65) — Conformance + cleanup
|
|
1407
|
-
|
|
1408
|
-
**A.13** — Conformance fixture relocation:
|
|
1409
|
-
|
|
1410
|
-
- 3 cases moved (`git mv`): `basic-scan`, `orphan-detection`,
|
|
1411
|
-
`rename-high` →
|
|
1412
|
-
`src/extensions/providers/claude/conformance/cases/`. 11
|
|
1413
|
-
fixture files (`minimal-claude/`, `orphan-{before,after}/`,
|
|
1414
|
-
`rename-high-{before,after}/`) moved alongside.
|
|
1415
|
-
- New `coverage.md` per-Provider listing the 5 frontmatter
|
|
1416
|
-
schemas (skill, agent, command, hook, note) and their cases.
|
|
1417
|
-
- New verb `sm conformance run [--scope spec|provider:<id>|all]`.
|
|
1418
|
-
Discovery by convention at `<plugin-dir>/conformance/`. The
|
|
1419
|
-
existing runner gains optional `fixturesRoot` (default
|
|
1420
|
-
`<specRoot>/conformance/fixtures` for compat); tooling using
|
|
1421
|
-
the public API of `@skill-map/cli/conformance` keeps working.
|
|
1422
|
-
`--json` deferred — reporter shape not yet frozen.
|
|
1423
|
-
- Spec keeps only the kernel-agnostic case (`kernel-empty-boot`)
|
|
1424
|
-
and the universal preamble fixture. Coverage matrix downgrades
|
|
1425
|
-
conservatively (rows that depended on `basic-scan` are now
|
|
1426
|
-
partial or missing, with cross-link to the Provider's matrix).
|
|
1427
|
-
|
|
1428
|
-
ROADMAP cleanup:
|
|
1429
|
-
|
|
1430
|
-
- The three "Status: target state for v0.8.0 — spec catch-up
|
|
1431
|
-
pending" banners on §Plugin system / §Frontmatter standard /
|
|
1432
|
-
§Enrichment are removed; prose shifts from future to present
|
|
1433
|
-
("kinds from v0.7.0 are renamed" → "were renamed in spec
|
|
1434
|
-
0.8.0"; Model B enrichment now describes the shipped
|
|
1435
|
-
`node_enrichments` table with `body_hash_at_enrichment` rather
|
|
1436
|
-
than "table or column set decided in PR").
|
|
1437
|
-
- Decision-log entry for the working session rewritten to
|
|
1438
|
-
reflect "shipped" rather than "pending".
|
|
1439
|
-
- Last-updated header gains an "implementation" paragraph
|
|
1440
|
-
listing the four prior phase commits.
|
|
1441
|
-
|
|
1442
|
-
593/593 cli + 32/32 testkit pass (+2 vs Phase 4 baseline).
|
|
1443
|
-
spec:check green (40 files hashed — down from 53 because the
|
|
1444
|
-
Claude-specific cases and fixtures left the spec's hash set).
|
|
1445
|
-
|
|
1446
|
-
### Breaking changes for plugin authors (Part 2)
|
|
1447
|
-
|
|
1448
|
-
Manifest renames:
|
|
1449
|
-
|
|
1450
|
-
- `kind: 'adapter'` → `kind: 'provider'`
|
|
1451
|
-
- `kind: 'detector'` → `kind: 'extractor'`
|
|
1452
|
-
- `kind: 'renderer'` → `kind: 'formatter'`
|
|
1453
|
-
- `kind: 'audit'` removed (migrate to `kind: 'rule'`).
|
|
1454
|
-
|
|
1455
|
-
Method signatures:
|
|
1456
|
-
|
|
1457
|
-
- Detector `detect(ctx) → Link[]` → Extractor `extract(ctx) →
|
|
1458
|
-
void` (output via `ctx.emitLink` / `ctx.enrichNode` /
|
|
1459
|
-
`ctx.store`).
|
|
1460
|
-
- Renderer `render(ctx) → string` → Formatter `format(ctx) →
|
|
1461
|
-
string`.
|
|
1462
|
-
|
|
1463
|
-
Manifest fields:
|
|
1464
|
-
|
|
1465
|
-
- Provider gains required `explorationDir`.
|
|
1466
|
-
- Provider's flat `defaultRefreshAction` map replaced by per-kind
|
|
1467
|
-
entries inside `kinds[<kind>].defaultRefreshAction` (must
|
|
1468
|
-
follow qualified pattern `<plugin-id>/<ext-id>`).
|
|
1469
|
-
- Provider's `emits` derives from `Object.keys(kinds)` (the
|
|
1470
|
-
manifest field is gone).
|
|
1471
|
-
- Provider's per-kind schemas declared via `kinds[<kind>].schema`
|
|
1472
|
-
(path relative to provider dir).
|
|
1473
|
-
- Renderer's `format` field renamed to `formatId` on the
|
|
1474
|
-
Formatter manifest (TS clash resolution).
|
|
1475
|
-
- New plugin kind `hook` with `triggers[]` + optional `filter`.
|
|
1476
|
-
- Optional `outputSchema` (`storage.schema` / `storage.schemas`)
|
|
1477
|
-
for Mode A / Mode B plugin custom storage.
|
|
1478
|
-
- Optional `applicableKinds` filter on Extractor manifest.
|
|
1479
|
-
|
|
1480
|
-
Extension ids:
|
|
1481
|
-
|
|
1482
|
-
- All extension ids must be qualified
|
|
1483
|
-
`<plugin-id>/<extension-id>` (built-ins classified into
|
|
1484
|
-
`claude/*` and `core/*`).
|
|
1485
|
-
|
|
1486
|
-
DB schema:
|
|
1487
|
-
|
|
1488
|
-
- Two new tables added in-place to `001_initial.sql` (pre-1.0
|
|
1489
|
-
consolidation, no production DBs to migrate):
|
|
1490
|
-
`scan_extractor_runs` and `node_enrichments`.
|
|
1491
|
-
- Column rename `nodes.adapter` → `nodes.provider` (and parallel
|
|
1492
|
-
in `result.adapters` → `result.providers`).
|
|
1493
|
-
|
|
1494
|
-
## Test stats
|
|
1495
|
-
|
|
1496
|
-
593/593 cli + 32/32 testkit pass (post-Phase 5).
|
|
1497
|
-
Two new DB tables (`scan_extractor_runs`, `node_enrichments`)
|
|
1498
|
-
added in-place to `001_initial.sql` (pre-1.0 consolidation, no
|
|
1499
|
-
production DBs to migrate). The 5 per-kind frontmatter schemas
|
|
1500
|
-
relocated from spec/ to the Claude Provider package.
|
|
1501
|
-
|
|
1502
|
-
## [Unreleased]
|
|
1503
|
-
|
|
1504
|
-
### Minor Changes
|
|
1505
|
-
|
|
1506
|
-
- 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).
|
|
1507
|
-
|
|
1508
|
-
- `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.
|
|
1509
|
-
|
|
1510
|
-
- 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.
|
|
1511
|
-
|
|
1512
|
-
- 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.
|
|
1513
|
-
|
|
1514
|
-
- 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.
|
|
1515
|
-
|
|
1516
|
-
- 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.
|
|
1517
|
-
|
|
1518
|
-
- 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.
|
|
1519
|
-
|
|
1520
|
-
- 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.
|
|
1521
|
-
|
|
1522
|
-
- Plugin kind `'detector'` renamed to `'extractor'`. Method signature
|
|
1523
|
-
changes from `detect(ctx) → Link[]` to `extract(ctx) → void` — output
|
|
1524
|
-
flows through three new ctx callbacks: `emitLink(link)` (kernel `links`
|
|
1525
|
-
table), `enrichNode(partial)` (kernel enrichment layer, persisted into
|
|
1526
|
-
`node_enrichments` per A.8), and the existing `ctx.store` (plugin's
|
|
1527
|
-
own table). The Extractor absorbs what would have been a separate
|
|
1528
|
-
`Enricher` kind via `enrichNode`. Built-ins migrated:
|
|
1529
|
-
`claude/frontmatter`, `claude/slash`, `claude/at-directive`,
|
|
1530
|
-
`core/external-url-counter` — all use `emitLink` to maintain
|
|
1531
|
-
functional parity with their Detector ancestors. Schema files
|
|
1532
|
-
renamed: `schemas/extensions/detector.schema.json` →
|
|
1533
|
-
`schemas/extensions/extractor.schema.json`. Persisted DB rows are
|
|
1534
|
-
unaffected (link `sources` carry extractor ids verbatim — the field
|
|
1535
|
-
was always free-form). Pre-1.0 minor per `versioning.md` § Pre-1.0;
|
|
1536
|
-
breaking for any plugin or test referencing `'detector'` as the
|
|
1537
|
-
kind, `IDetector`, or the old `Link[]` return signature — no real
|
|
1538
|
-
ecosystem affected today.
|
|
1539
|
-
|
|
1540
|
-
- Plugin kind `'audit'` removed. The single built-in `'validate-all'`
|
|
1541
|
-
migrated to a Rule (qualified id `'core/validate-all'`, method
|
|
1542
|
-
`evaluate(ctx) → Issue[]`). The kind had dual personality (composer +
|
|
1543
|
-
standalone reporter); the standalone reporter case is naturally a Rule,
|
|
1544
|
-
and the composer case is deferred to post-1.0 if a real use case
|
|
1545
|
-
appears. CLI verbs `'sm audit run'` and `'sm audit show'` removed;
|
|
1546
|
-
users invoke the rule via `sm check --rules core/validate-all`.
|
|
1547
|
-
`state_executions.kind` enum narrowed to `['action']` (audit was the
|
|
1548
|
-
only other value); the column is preserved as a forward-compatibility
|
|
1549
|
-
lever. Schema files removed: `schemas/extensions/audit.schema.json`.
|
|
1550
|
-
Coverage matrix shrinks from 29 to 28 rows. Pre-1.0 minor per
|
|
1551
|
-
`versioning.md` § Pre-1.0; breaking for any plugin or test referencing
|
|
1552
|
-
the audit kind, `IAudit`, `TAuditReport`, or `sm audit` verbs — no
|
|
1553
|
-
real ecosystem affected today.
|
|
1554
|
-
|
|
1555
|
-
- Plugin kind `'adapter'` renamed to `'provider'`. Manifest gains required
|
|
1556
|
-
field `'explorationDir'` (filesystem directory where the Provider's
|
|
1557
|
-
content lives, e.g. `'~/.claude'` for the Claude Provider). Built-in
|
|
1558
|
-
`claudeAdapter` renamed to `claudeProvider`. The hexagonal-architecture
|
|
1559
|
-
`'adapter'` (`RunnerPort.adapter`, `StoragePort.adapter`,
|
|
1560
|
-
`FilesystemPort.adapter`, `PluginLoaderPort.adapter`) is unchanged —
|
|
1561
|
-
distinct concept, distinct namespace.
|
|
1562
|
-
Persisted schema fields renamed: `node.adapter` → `node.provider`,
|
|
1563
|
-
`scan-result.adapters` → `scan-result.providers` (pre-1.0 minor — no
|
|
1564
|
-
production DBs to migrate; `001_initial.sql` was edited in place per
|
|
1565
|
-
the consolidation precedent already established for pre-1.0).
|
|
1566
|
-
Project config field renamed: `project-config.adapters` →
|
|
1567
|
-
`project-config.providers`. Schema files renamed:
|
|
1568
|
-
`schemas/extensions/adapter.schema.json` →
|
|
1569
|
-
`schemas/extensions/provider.schema.json`. Pre-1.0 minor per
|
|
1570
|
-
`versioning.md` § Pre-1.0; breaking for any plugin or test referencing
|
|
1571
|
-
`'adapter'` as the kind, `IAdapter`, or any persisted/config schema
|
|
1572
|
-
field renamed above — no real ecosystem affected today.
|
|
1573
|
-
|
|
1574
|
-
## 0.7.1
|
|
1575
|
-
|
|
1576
|
-
### Patch Changes
|
|
1577
|
-
|
|
1578
|
-
- 0463a0f: Step 9.4 — plugin author guide + reference plugin + diagnostics polish.
|
|
1579
|
-
**Step 9 fully closed** with this changeset.
|
|
1580
|
-
|
|
1581
|
-
### Spec — plugin author guide (additive prose)
|
|
1582
|
-
|
|
1583
|
-
New document at `spec/plugin-author-guide.md` covering:
|
|
1584
|
-
|
|
1585
|
-
- Discovery roots (`<project>/.skill-map/plugins/`,
|
|
1586
|
-
`~/.skill-map/plugins/`, `--plugin-dir <path>`).
|
|
1587
|
-
- Manifest fields with the normative schema reference.
|
|
1588
|
-
- `specCompat` strategy — narrow ranges pre-`v1.0.0`, `^1.0.0`
|
|
1589
|
-
recommendation post-`v1.0.0`.
|
|
1590
|
-
- The six extension kinds with one minimal worked example each
|
|
1591
|
-
(detector, rule, renderer in full; adapter / audit / action flagged
|
|
1592
|
-
for later expansion alongside Step 10).
|
|
1593
|
-
- Storage choice (KV vs Dedicated) cross-linking `plugin-kv-api.md`
|
|
1594
|
-
and the Step 9.2 triple-protection rule.
|
|
1595
|
-
- Execution modes (deterministic / probabilistic) cross-linking
|
|
1596
|
-
`architecture.md`.
|
|
1597
|
-
- Testkit usage with `runDetectorOnFixture`, `runRuleOnGraph`,
|
|
1598
|
-
`runRendererOnGraph`, `makeFakeRunner`.
|
|
1599
|
-
- The five plugin statuses (`loaded` / `disabled` / `incompatible-spec`
|
|
1600
|
-
/ `invalid-manifest` / `load-error`) and how to read them.
|
|
1601
|
-
- Stability section (document is stable; widening additions are minor
|
|
1602
|
-
bumps; breaking edits are major).
|
|
1603
|
-
|
|
1604
|
-
`spec/package.json#files` updated to ship the new doc; `spec/index.json`
|
|
1605
|
-
regenerated (57 → 58 hashed files). `coverage.md` unchanged because the
|
|
1606
|
-
guide is prose, not a schema.
|
|
1607
|
-
|
|
1608
|
-
### Reference plugin — `examples/hello-world/`
|
|
1609
|
-
|
|
1610
|
-
Smallest viable plugin in the principal repo (Arquitecto's pick: in
|
|
1611
|
-
the main repo, not separate). One detector (`hello-world-greet`)
|
|
1612
|
-
emitting `references` links per `@greet:<name>` token in node bodies.
|
|
1613
|
-
Includes:
|
|
1614
|
-
|
|
1615
|
-
- `plugin.json` declaring one extension and pinning `specCompat: ^1.0.0`.
|
|
1616
|
-
- `extensions/greet-detector.mjs` — runtime instance with both
|
|
1617
|
-
manifest fields and the `detect` method.
|
|
1618
|
-
- `README.md` — what it does, file layout, three-step "try it
|
|
1619
|
-
locally" recipe, what's intentionally missing (storage,
|
|
1620
|
-
multi-extension, probabilistic mode), pointers for production-grade
|
|
1621
|
-
patterns.
|
|
1622
|
-
- `test/greet-detector.test.mjs` — four-assertion test using
|
|
1623
|
-
`@skill-map/testkit`, runnable via `node --test` with no build step.
|
|
1624
|
-
|
|
1625
|
-
Verified end-to-end: the example plugin loads cleanly under
|
|
1626
|
-
`sm plugins list`, scans contribute its links to the persisted graph,
|
|
1627
|
-
and the testkit-based test passes. The example is **not** registered
|
|
1628
|
-
as a workspace — it's intentionally standalone so users can copy it.
|
|
1629
|
-
|
|
1630
|
-
### CLI — diagnostics polish on `PluginLoader.reason`
|
|
1631
|
-
|
|
1632
|
-
Each failure-mode reason string now carries an actionable hint:
|
|
1633
|
-
|
|
1634
|
-
- `invalid-manifest` (JSON parse): names the manifest path, suggests
|
|
1635
|
-
validating the JSON.
|
|
1636
|
-
- `invalid-manifest` (AJV): names the manifest path AND points at
|
|
1637
|
-
`spec/schemas/plugins-registry.schema.json#/$defs/PluginManifest`.
|
|
1638
|
-
- `invalid-manifest` (specCompat not a valid range): suggests a range
|
|
1639
|
-
shape (`"^1.0.0"`).
|
|
1640
|
-
- `incompatible-spec`: suggests two remediations (update the plugin's
|
|
1641
|
-
`specCompat`, or pin sm to a compatible spec version).
|
|
1642
|
-
- `load-error` (extension file not found): includes the absolute
|
|
1643
|
-
resolved path, pointer to `plugin.json#/extensions`.
|
|
1644
|
-
- `load-error` (default export missing kind): lists the valid kinds.
|
|
1645
|
-
- `load-error` (unknown kind): lists the valid kinds.
|
|
1646
|
-
- `load-error` (extension manifest schema fails): names the
|
|
1647
|
-
per-kind schema (`spec/schemas/extensions/<kind>.schema.json`).
|
|
1648
|
-
|
|
1649
|
-
6 new tests under `test/plugin-loader.test.ts` (`Step 9.4 diagnostics
|
|
1650
|
-
polish` describe block) assert each hint shape is present without
|
|
1651
|
-
pinning the full text. Test count 437 → **443 cli + 30 testkit = 473**.
|
|
1652
|
-
|
|
1653
|
-
### Step 9 closed
|
|
1654
|
-
|
|
1655
|
-
The four sub-steps — 9.1 (plugin runtime wiring), 9.2 (plugin
|
|
1656
|
-
migrations + triple protection), 9.3 (`@skill-map/testkit` workspace),
|
|
1657
|
-
9.4 (author guide + reference plugin + diagnostics polish) — together
|
|
1658
|
-
turn `skill-map` plugins from "discovered but inert" into a
|
|
1659
|
-
first-class authoring surface with documentation, tests, and a
|
|
1660
|
-
working reference. Next step: **Step 10 — job subsystem + first
|
|
1661
|
-
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.
|
|
1662
62
|
|
|
1663
63
|
## 0.7.0
|
|
1664
64
|
|
|
1665
|
-
### Minor
|
|
1666
|
-
|
|
1667
|
-
- d730094: Spec — Execution modes (deterministic / probabilistic) lifted to a first-class architectural property
|
|
1668
|
-
|
|
1669
|
-
Frames a meta-property of skill-map that was previously implicit and scattered:
|
|
1670
|
-
**every analytical extension is one of two modes** — `deterministic` (pure code,
|
|
1671
|
-
runs in scan-time pipelines) or `probabilistic` (invokes an LLM through
|
|
1672
|
-
`RunnerPort`, runs only as queued jobs). The dual-mode capability now spans four
|
|
1673
|
-
of the six extension kinds; Adapter and Renderer remain locked to deterministic
|
|
1674
|
-
because they sit at the system boundaries (filesystem and graph-to-string) where
|
|
1675
|
-
non-determinism would break boot reproducibility and snapshot diffing.
|
|
1676
|
-
|
|
1677
|
-
**Spec changes:**
|
|
1678
|
-
|
|
1679
|
-
- `architecture.md` — new top-level section **§Execution modes** before
|
|
1680
|
-
§Extension kinds. Defines the two modes, the per-kind capability matrix
|
|
1681
|
-
(Detector / Rule / Action dual-mode by manifest declaration; Audit dual-mode
|
|
1682
|
-
with mode **derived** from `composes[]`; Adapter / Renderer deterministic-only),
|
|
1683
|
-
the runtime separation (`deterministic` runs in `sm scan` / `sm check`;
|
|
1684
|
-
`probabilistic` runs only via `sm job submit <kind>:<id>`), and the
|
|
1685
|
-
`RunnerPort` injection contract for probabilistic extensions.
|
|
1686
|
-
- `architecture.md` §Extension kinds — table updated: each row clarifies the
|
|
1687
|
-
mode posture (Adapter / Renderer marked deterministic-only; Detector / Rule /
|
|
1688
|
-
Action marked dual-mode; Audit marked derived-mode).
|
|
1689
|
-
- `architecture.md` §Stability — new clause: execution modes and the per-kind
|
|
1690
|
-
capability matrix are stable as of v1.0.0; adding a third mode, changing
|
|
1691
|
-
which kinds are dual-mode, or changing the audit's derivation rule is a major
|
|
1692
|
-
bump.
|
|
1693
|
-
|
|
1694
|
-
**Schema changes:**
|
|
1695
|
-
|
|
1696
|
-
- `schemas/extensions/detector.schema.json`:
|
|
1697
|
-
- New optional `mode` field (`deterministic` | `probabilistic`, default
|
|
1698
|
-
`deterministic`). Omitting is equivalent to deterministic — keeps existing
|
|
1699
|
-
detectors valid without an update.
|
|
1700
|
-
- Description updated to spell out the dual-mode contract.
|
|
1701
|
-
- `schemas/extensions/rule.schema.json`:
|
|
1702
|
-
- Same shape: new optional `mode` field with default `deterministic`.
|
|
1703
|
-
- Description rewritten — the previous "Rules MUST be deterministic" claim
|
|
1704
|
-
moved into the deterministic-mode contract; probabilistic rules are now
|
|
1705
|
-
explicitly allowed and run only as queued jobs.
|
|
1706
|
-
- `schemas/extensions/action.schema.json`:
|
|
1707
|
-
- **Breaking** — `mode` enum renamed: `local` → `deterministic`,
|
|
1708
|
-
`invocation-template` → `probabilistic`. Pre-1.0; no consumers depend on
|
|
1709
|
-
the old values (no third-party action plugins shipped). Description, the
|
|
1710
|
-
two `if/then` branches, and the `expectedDurationSeconds` /
|
|
1711
|
-
`promptTemplateRef` field descriptions updated accordingly.
|
|
1712
|
-
- **Bug fix** — the schema previously declared `allOf` twice at the root
|
|
1713
|
-
(lines 6–8 and 71–80); the second silently overrode the first, dropping
|
|
1714
|
-
`$ref: base.schema.json`. Both blocks are now merged into a single `allOf`
|
|
1715
|
-
so the action schema actually composes the base shape.
|
|
1716
|
-
- `schemas/extensions/audit.schema.json`:
|
|
1717
|
-
- Description rewritten — the "deterministic workflow" claim is replaced by
|
|
1718
|
-
the **derived-mode** rule: the audit's effective mode is computed from
|
|
1719
|
-
`composes[]` at load time. If every composed primitive is deterministic,
|
|
1720
|
-
the audit is deterministic; if any is probabilistic, the audit is
|
|
1721
|
-
probabilistic and dispatches as a job. Declaring `mode` directly is a
|
|
1722
|
-
load-time error.
|
|
1723
|
-
- `composes[]` description updated to mention that each primitive's mode
|
|
1724
|
-
participates in derivation; dangling references stay a load-time error.
|
|
1725
|
-
- `reportSchemaRef` description updated: probabilistic audits MUST extend
|
|
1726
|
-
`report-base.schema.json` (carries `safety` / `confidence`); deterministic
|
|
1727
|
-
audits MAY extend it but are not required to.
|
|
1728
|
-
- `schemas/extensions/adapter.schema.json`:
|
|
1729
|
-
- Description updated to state explicitly that adapters are deterministic-only
|
|
1730
|
-
and that `mode` MUST NOT appear. Recommendation for users who want
|
|
1731
|
-
LLM-assisted classification: write a probabilistic Detector that emits
|
|
1732
|
-
classification hints as `Link[]`.
|
|
1733
|
-
- `schemas/extensions/renderer.schema.json`:
|
|
1734
|
-
- Description updated to state that renderers are deterministic-only and
|
|
1735
|
-
that `mode` MUST NOT appear. Probabilistic narrators of the graph belong
|
|
1736
|
-
in jobs and emit Findings, not in renderer manifests.
|
|
1737
|
-
|
|
1738
|
-
**Why major (despite pre-1.0 minor norm):**
|
|
1739
|
-
|
|
1740
|
-
Renaming the `Action.mode` enum (`local` → `deterministic`,
|
|
1741
|
-
`invocation-template` → `probabilistic`) is breaking by definition. No
|
|
1742
|
-
third-party Actions exist yet, but the rename touches the canonical surface and
|
|
1743
|
-
deserves the bump. New optional fields on Detector / Rule and the new derived-
|
|
1744
|
-
mode contract on Audit are additive and would have been minor on their own.
|
|
1745
|
-
|
|
1746
|
-
**Implementation work intentionally NOT included here:**
|
|
1747
|
-
|
|
1748
|
-
- `src/extensions/built-ins.ts` and the per-extension TS files keep working
|
|
1749
|
-
unchanged because the new `mode` is optional with `deterministic` default.
|
|
1750
|
-
Explicitly threading `mode: 'deterministic'` through every built-in is a
|
|
1751
|
-
follow-up.
|
|
1752
|
-
- `RunnerPort` injection through `ctx.runner` for probabilistic extensions is
|
|
1753
|
-
spec'd here; the actual context plumbing lands with the first probabilistic
|
|
1754
|
-
extension (Step 10 — first summarizer). `MockRunner` continues to satisfy
|
|
1755
|
-
tests until then.
|
|
1756
|
-
- Conformance case `extension-mode-derivation` (audit composes mixed
|
|
1757
|
-
primitives → derives `probabilistic`) is mentioned in `architecture.md` and
|
|
1758
|
-
pending under `spec/conformance/coverage.md` for the next release.
|
|
1759
|
-
- ROADMAP.md rephrase of Steps 10–11 (from "summarizers" to "wave 2:
|
|
1760
|
-
probabilistic extensions") and a positioning section in `README.md` follow
|
|
1761
|
-
in separate commits to keep this changeset spec-only.
|
|
1762
|
-
|
|
1763
|
-
### Minor Changes
|
|
1764
|
-
|
|
1765
|
-
- a73f3f4: Step 7.1 — File watcher (`sm watch` / `sm scan --watch`)
|
|
1766
|
-
|
|
1767
|
-
Long-running watcher that subscribes to the scan roots, debounces
|
|
1768
|
-
filesystem events, and triggers an incremental scan per batch. Reuses
|
|
1769
|
-
the existing `runScanWithRenames` pipeline, the `IIgnoreFilter` chain
|
|
1770
|
-
(`.skill-mapignore` + `config.ignore` + bundled defaults), and the
|
|
1771
|
-
`scan.*` non-job events from `job-events.md` — one ScanResult per
|
|
1772
|
-
batch, emitted as ndjson under `--json`.
|
|
1773
|
-
|
|
1774
|
-
**Spec changes (minor)**:
|
|
1775
|
-
|
|
1776
|
-
- `spec/schemas/project-config.schema.json` — new `scan.watch` object
|
|
1777
|
-
with a single key `debounceMs` (integer ≥ 0, default 300). Groups
|
|
1778
|
-
bursts of filesystem events (editor saves, branch switches, npm
|
|
1779
|
-
installs) into a single scan pass. Set to 0 to disable debouncing.
|
|
1780
|
-
- `spec/cli-contract.md` §Scan — documents `sm watch [roots...]` as
|
|
1781
|
-
the primary verb and `sm scan --watch` as the alias. Watcher
|
|
1782
|
-
respects the same ignore chain as one-shot scans, emits one
|
|
1783
|
-
ScanResult per batch (ndjson under `--json`), closes cleanly on
|
|
1784
|
-
`SIGINT` / `SIGTERM`, exits 0 on clean shutdown. Exit-code rule
|
|
1785
|
-
carved out for the watcher: per-batch error issues do not flip the
|
|
1786
|
-
exit code (the loop keeps running); operational errors still exit 2.
|
|
1787
|
-
|
|
1788
|
-
No new events. No new ports. The watcher is implementation-defined
|
|
1789
|
-
inside the kernel package; a future `WatchPort` can be added when /
|
|
1790
|
-
if a non-Node implementation needs to swap the chokidar wrapper.
|
|
1791
|
-
|
|
1792
|
-
**Runtime changes (minor — new verb + new config key)**:
|
|
1793
|
-
|
|
1794
|
-
- `chokidar@5.0.0` pinned in `src/package.json` (single new runtime
|
|
1795
|
-
dependency, MIT). Chokidar v5 requires Node ≥ 20.19; the project
|
|
1796
|
-
already pins `engines.node: ">=24.0"` so this is a no-op for
|
|
1797
|
-
consumers. Brings in `readdirp@5` as a transitive.
|
|
1798
|
-
- `src/kernel/scan/watcher.ts` — `IFsWatcher` interface + concrete
|
|
1799
|
-
`ChokidarWatcher` wrapping `chokidar.watch()` with the existing
|
|
1800
|
-
`IIgnoreFilter` plumbed through, debouncer, batch coalescing,
|
|
1801
|
-
and explicit `stop()` for clean teardown.
|
|
1802
|
-
- `src/cli/commands/watch.ts` — new `WatchCommand`. `sm scan
|
|
1803
|
-
--watch` delegates to the same code path so the two surfaces are
|
|
1804
|
-
byte-aligned (no parallel implementations).
|
|
1805
|
-
- `src/config/defaults.json` — new `scan.watch.debounceMs: 300`
|
|
1806
|
-
default.
|
|
1807
|
-
|
|
1808
|
-
**Why minor (not patch)**: new public verb (`sm watch`), new public
|
|
1809
|
-
config key (`scan.watch.debounceMs`), and a new flag on an existing
|
|
1810
|
-
verb (`sm scan --watch`). All three are surface additions, not bug
|
|
1811
|
-
fixes — minor under both the spec and the runtime semver policies.
|
|
1812
|
-
No breaking changes; existing `sm scan` without `--watch` is
|
|
1813
|
-
byte-identical to before.
|
|
1814
|
-
|
|
1815
|
-
**Roadmap**: Step 7 — Robustness, sub-step 7.1 (chokidar watcher).
|
|
1816
|
-
Trigger normalization is implicit-already-landed (cabled into every
|
|
1817
|
-
detector at Steps 3–4 with full unit tests in
|
|
1818
|
-
`src/kernel/trigger-normalize.test.ts`); we do not write a sub-step
|
|
1819
|
-
for it. Next sub-steps: 7.2 detector conflict resolution, 7.3 `sm
|
|
1820
|
-
job prune` + retention enforcement.
|
|
1821
|
-
|
|
1822
|
-
### Patch Changes
|
|
1823
|
-
|
|
1824
|
-
- a73f3f4: Step 7.2 — Detector conflict resolution
|
|
1825
|
-
|
|
1826
|
-
Two pieces:
|
|
1827
|
-
|
|
1828
|
-
1. **New built-in rule `link-conflict`** (`src/extensions/rules/link-conflict/`).
|
|
1829
|
-
Surfaces detector disagreement. Groups links by `(source, target)` and
|
|
1830
|
-
emits one `warn` Issue per pair where the set of distinct `kind` values
|
|
1831
|
-
has size ≥ 2. Agreement (single kind across multiple detectors) is
|
|
1832
|
-
silent — by design, to avoid massive noise on real graphs.
|
|
1833
|
-
Issue payload (`data`) carries `{ source, target, variants }` where
|
|
1834
|
-
each `variant` is `{ kind, sources: detectorId[], confidence }`. Variant
|
|
1835
|
-
sources are deduped + sorted; confidence is the highest across rows
|
|
1836
|
-
of the same kind (`high` > `medium` > `low`).
|
|
1837
|
-
|
|
1838
|
-
This is the kernel piece of Decision #90 read-time "consumers that
|
|
1839
|
-
need uniqueness aggregate at read time" — the rule is one such
|
|
1840
|
-
consumer, on the alarming side. Storage stays untouched (one row
|
|
1841
|
-
per detector, no merge, no dedup). Severity is `warn`, not `error`:
|
|
1842
|
-
the rule cannot pick which kind is correct, so per `cli-contract.md`
|
|
1843
|
-
§Exit codes the verb stays exit 0.
|
|
1844
|
-
|
|
1845
|
-
2. **`sm show` pretty link aggregation** (`src/cli/commands/show.ts`).
|
|
1846
|
-
The human renderer now groups `linksOut` / `linksIn` by `(endpoint,
|
|
1847
|
-
kind, normalizedTrigger)` and prints one row per group with the
|
|
1848
|
-
union of detector ids in a `sources:` field. The section header
|
|
1849
|
-
reports both the raw row count and the unique-after-grouping count
|
|
1850
|
-
(`Links out (12, 9 unique)`). When N > 1 detector emits the same
|
|
1851
|
-
logical link, the row also gets a `(×N)` suffix.
|
|
1852
|
-
|
|
1853
|
-
`--json` output is byte-identical to before — raw rows, no merge.
|
|
1854
|
-
Storage is byte-identical to before. The grouping is purely a
|
|
1855
|
-
read-time presentation choice for human eyes.
|
|
1856
|
-
|
|
1857
|
-
**Spec changes (patch)**:
|
|
1858
|
-
|
|
1859
|
-
- `spec/cli-contract.md` §Browse — `sm show` row clarifies that pretty
|
|
1860
|
-
output groups identical-shape links and that `--json` emits raw rows.
|
|
1861
|
-
Patch (not minor) because the JSON contract is unchanged; the human
|
|
1862
|
-
output format is non-normative anyway.
|
|
1863
|
-
|
|
1864
|
-
**Runtime changes (minor — new rule + new presentation)**:
|
|
1865
|
-
|
|
1866
|
-
- New rule `link-conflict` registered in `src/extensions/built-ins.ts`.
|
|
1867
|
-
- `sm show` pretty output groups links + reports unique counts.
|
|
1868
|
-
|
|
1869
|
-
**UI inspector aggregation deferred to Step 13**: the current Flavor A
|
|
1870
|
-
inspector renders the `Relations` card from `node.frontmatter.metadata.{
|
|
1871
|
-
related, requires, supersedes, provides, conflictsWith}` directly — it
|
|
1872
|
-
does NOT consume `linksOut` / `linksIn` rows from `scan_links`. There
|
|
1873
|
-
is no link table to aggregate today. When Step 13's Flavor B lands (Hono
|
|
1874
|
-
BFF + WS + full link panel from scan), the aggregation logic from
|
|
1875
|
-
`src/cli/commands/show.ts` will need to be ported.
|
|
1876
|
-
|
|
1877
|
-
**Roadmap**: Step 7 — Robustness, sub-step 7.2 (detector conflict
|
|
1878
|
-
resolution). Closes one of the three remaining frentes; 7.3 (`sm job
|
|
1879
|
-
prune` + retention) still pending. Decision #90 unchanged: storage
|
|
1880
|
-
keeps raw per-detector rows. The `related` vs LLM-amplification
|
|
1881
|
-
discussion is documented in `.tmp/skill-map-related-test/` (status
|
|
1882
|
-
quo retained — fields stay opt-in under `metadata.*`; revisit if
|
|
1883
|
-
real-world amplification appears).
|
|
65
|
+
### Minor
|
|
1884
66
|
|
|
1885
|
-
|
|
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.
|
|
1886
68
|
|
|
1887
69
|
## 0.6.1
|
|
1888
70
|
|
|
1889
|
-
### Patch
|
|
1890
|
-
|
|
1891
|
-
- f41dbad: Step 6.1 — Spec migration: rename the canonical config file from
|
|
1892
|
-
`.skill-map.json` (single project-root file) to `.skill-map/settings.json`
|
|
1893
|
-
inside the `.skill-map/` scope folder, with a sibling `.skill-map/settings.local.json`
|
|
1894
|
-
partner for machine-specific overrides. Aligns the spec with the layered
|
|
1895
|
-
config hierarchy described in the roadmap (library defaults → user → user-local
|
|
1896
|
-
→ project → project-local → env / flags).
|
|
1897
|
-
|
|
1898
|
-
**Spec change (breaking, minor under pre-1.0 versioning policy)**:
|
|
1899
|
-
|
|
1900
|
-
- `spec/schemas/project-config.schema.json` description updated to point at
|
|
1901
|
-
`.skill-map/settings.json` and explicitly mention the `.local.json` partner
|
|
1902
|
-
and the layered-merge contract. The schema _shape_ (keys, types, validation
|
|
1903
|
-
rules) is unchanged — only the on-disk filename moves. Consumers that read
|
|
1904
|
-
values without caring about the source path are unaffected; consumers that
|
|
1905
|
-
hard-code the filename must update.
|
|
1906
|
-
- `spec/db-schema.md` §Scopes: `history.share: true` reference updated to
|
|
1907
|
-
`.skill-map/settings.json`.
|
|
1908
|
-
- `spec/conformance/coverage.md` row #6 description updated to reference the
|
|
1909
|
-
new path and the optional `settings.local.json` overlay.
|
|
1910
|
-
|
|
1911
|
-
**Why minor (not major) at pre-1.0**: per `spec/versioning.md` §Pre-1.0,
|
|
1912
|
-
breaking changes ARE allowed in minor bumps while the spec is `0.y.z`. The
|
|
1913
|
-
shape of the data is unchanged; only the file name on disk moves.
|
|
1914
|
-
|
|
1915
|
-
**No backward-compat shim**: there is no real implementation of the loader
|
|
1916
|
-
yet (lands in 6.2), so no live consumer reads `.skill-map.json` today. The
|
|
1917
|
-
only known prior reference is the demo `mock-collection/.claude/commands/init*.md`
|
|
1918
|
-
fixture, which is updated together with `sm init` in 6.5.
|
|
1919
|
-
|
|
1920
|
-
**Runtime change**: none in 6.1 — pure spec edit. The matching loader,
|
|
1921
|
-
`sm init`, and `sm config` verbs land in subsequent sub-steps.
|
|
1922
|
-
|
|
1923
|
-
**Roadmap update**: `ROADMAP.md` §Configuration "Spec migration" call-out
|
|
1924
|
-
flipped from "pending" to "landed Step 6.1, 2026-04-27".
|
|
1925
|
-
|
|
1926
|
-
Test count: unchanged (213 → 213 — spec-only edit).
|
|
1927
|
-
|
|
1928
|
-
- 8a4667f: Step 6.6 — `sm plugins enable / disable` + the `config_plugins`
|
|
1929
|
-
override layer they read from. The two stub verbs become real, and
|
|
1930
|
-
the `PluginLoader` finally honours user intent: a disabled plugin
|
|
1931
|
-
surfaces in `sm plugins list` with status `disabled`, but its
|
|
1932
|
-
extensions are NOT imported and the kernel will not run them.
|
|
1933
|
-
|
|
1934
|
-
**Decision (recorded in spec)**: enable/disable resolution favours the
|
|
1935
|
-
DB row over `settings.json` over the installed default. The DB
|
|
1936
|
-
override is local-machine; `settings.json` is the team-shared baseline.
|
|
1937
|
-
A developer can locally disable a misbehaving plugin without
|
|
1938
|
-
committing the toggle to the team's config; conversely, a baseline
|
|
1939
|
-
that explicitly enables a plugin is overridable per-machine. The rule
|
|
1940
|
-
is documented in `spec/db-schema.md` §`config_plugins`.
|
|
1941
|
-
|
|
1942
|
-
**Spec change (additive, patch)**:
|
|
1943
|
-
|
|
1944
|
-
- `spec/db-schema.md` — appended an "Effective enable/disable
|
|
1945
|
-
resolution" subsection under `config_plugins` documenting the
|
|
1946
|
-
three-layer precedence (DB > `settings.json` > installed default).
|
|
1947
|
-
No schema changes; the `config_plugins` table itself was already
|
|
1948
|
-
defined in the initial migration.
|
|
1949
|
-
|
|
1950
|
-
**Runtime change**:
|
|
1951
|
-
|
|
1952
|
-
- `src/kernel/types/plugin.ts` — `TPluginLoadStatus` gains a `disabled`
|
|
1953
|
-
variant. JSDoc explains all five states.
|
|
1954
|
-
- `src/kernel/adapters/sqlite/plugins.ts` — new file. Storage helpers
|
|
1955
|
-
over the `config_plugins` table: `setPluginEnabled` (upsert),
|
|
1956
|
-
`getPluginEnabled` (single read), `loadPluginOverrideMap` (bulk
|
|
1957
|
-
read for one round-trip per process), `deletePluginOverride`
|
|
1958
|
-
(idempotent drop, used by future `sm config reset plugins.<id>`).
|
|
1959
|
-
- `src/kernel/config/plugin-resolver.ts` — new file.
|
|
1960
|
-
`resolvePluginEnabled` implements the precedence above;
|
|
1961
|
-
`makeEnabledResolver` curries the layered config and DB map into
|
|
1962
|
-
the `(id) => boolean` shape `IPluginLoaderOptions.resolveEnabled`
|
|
1963
|
-
expects.
|
|
1964
|
-
- `src/kernel/adapters/plugin-loader.ts` — new optional
|
|
1965
|
-
`resolveEnabled` callback in `IPluginLoaderOptions`. When supplied,
|
|
1966
|
-
the loader checks AFTER manifest + specCompat validation and
|
|
1967
|
-
short-circuits with `status: 'disabled'` (manifest preserved,
|
|
1968
|
-
extensions array omitted, reason `"disabled by config_plugins or
|
|
1969
|
-
settings.json"`). Omitting the callback keeps the legacy "always
|
|
1970
|
-
load" behaviour for tests / kernel-empty-boot.
|
|
1971
|
-
- `src/cli/commands/plugins.ts` — wires the loader to the resolver:
|
|
1972
|
-
every read (`list / show / doctor`) loads `config_plugins` once and
|
|
1973
|
-
feeds the resolver. Two new commands `PluginsEnableCommand` and
|
|
1974
|
-
`PluginsDisableCommand` write to the DB. `--all` toggles every
|
|
1975
|
-
discovered plugin; `<id>` and `--all` are mutually exclusive.
|
|
1976
|
-
`sm plugins doctor` now treats `disabled` as intentional (does not
|
|
1977
|
-
contribute to the issue list, does not flip exit code).
|
|
1978
|
-
- `src/cli/commands/plugins.ts` — adds `off` to the status icon legend
|
|
1979
|
-
in human output (`off mock-a@0.1.0 · disabled by config_plugins or
|
|
1980
|
-
settings.json`).
|
|
1981
|
-
- `src/cli/commands/stubs.ts` — `PluginsEnableCommand` and
|
|
1982
|
-
`PluginsDisableCommand` removed; replaced-at-step comment kept.
|
|
1983
|
-
- `context/cli-reference.md` — regenerated; the two new verbs appear
|
|
1984
|
-
with their flag tables.
|
|
1985
|
-
|
|
1986
|
-
**Tests**:
|
|
1987
|
-
|
|
1988
|
-
- `src/test/plugin-overrides.test.ts` — 8 unit tests covering storage
|
|
1989
|
-
round-trip (upsert + read), `loadPluginOverrideMap` bulk read,
|
|
1990
|
-
`deletePluginOverride` idempotency, resolver precedence (default ⇒
|
|
1991
|
-
true, `settings.json` overrides default, DB overrides
|
|
1992
|
-
`settings.json`), `makeEnabledResolver` currying, and PluginLoader
|
|
1993
|
-
surfacing `disabled` status with manifest preserved + no extensions
|
|
1994
|
-
- omitting the resolver still loads.
|
|
1995
|
-
- `src/test/plugins-cli.test.ts` — 9 end-to-end tests via the binary:
|
|
1996
|
-
`disable <id>` writes a DB row + `sm plugins list` reflects `off`,
|
|
1997
|
-
`enable <id>` flips back, `--all` covers every discovered plugin,
|
|
1998
|
-
unknown id → exit 5, no-arg → exit 2, both `<id>` and `--all` →
|
|
1999
|
-
exit 2, `settings.json` baseline overridden by DB `enable`,
|
|
2000
|
-
`settings.json` baseline applies when DB has no row, and
|
|
2001
|
-
`sm plugins doctor` exits 0 when the only non-loaded plugin is
|
|
2002
|
-
intentionally disabled.
|
|
71
|
+
### Patch
|
|
2003
72
|
|
|
2004
|
-
|
|
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.
|
|
2005
74
|
|
|
2006
75
|
## 0.6.0
|
|
2007
76
|
|
|
2008
|
-
### Minor
|
|
2009
|
-
|
|
2010
|
-
- 9a89124: Step 5.1 — Persist scan-result metadata in a new `scan_meta` table so
|
|
2011
|
-
`loadScanResult` returns real values for `scope` / `roots` / `scannedAt` /
|
|
2012
|
-
`scannedBy` / `adapters` / `stats.filesWalked` / `stats.filesSkipped` /
|
|
2013
|
-
`stats.durationMs` instead of the synthetic envelope shipped at Step 4.7.
|
|
2014
|
-
|
|
2015
|
-
**Spec change (additive, minor)**:
|
|
2016
|
-
|
|
2017
|
-
- New `scan_meta` table in zone `scan_*`, single-row (CHECK `id = 1`).
|
|
2018
|
-
Columns: `scope`, `roots_json`, `scanned_at`, `scanned_by_name`,
|
|
2019
|
-
`scanned_by_version`, `scanned_by_spec_version`, `adapters_json`,
|
|
2020
|
-
`stats_files_walked`, `stats_files_skipped`, `stats_duration_ms`.
|
|
2021
|
-
`nodesCount` / `linksCount` / `issuesCount` are not stored — they are
|
|
2022
|
-
derived from `COUNT(*)` of the sibling tables.
|
|
2023
|
-
- Replaced atomically with the rest of `scan_*` on every `sm scan`.
|
|
2024
|
-
|
|
2025
|
-
**Runtime change**:
|
|
2026
|
-
|
|
2027
|
-
- New kernel migration `002_scan_meta.sql`.
|
|
2028
|
-
- `IScanMetaTable` added to `src/kernel/adapters/sqlite/schema.ts` and
|
|
2029
|
-
bound in `IDatabase`.
|
|
2030
|
-
- `persistScanResult` writes the row (and deletes prior rows in the same
|
|
2031
|
-
transaction).
|
|
2032
|
-
- `loadScanResult` reads from `scan_meta` when the row exists; degrades
|
|
2033
|
-
to the previous synthetic envelope when it does not (DB freshly
|
|
2034
|
-
migrated, never scanned, or pre-5.1 snapshot).
|
|
2035
|
-
- The Step 4.7 follow-up notes in `scan-load.ts` documenting the
|
|
2036
|
-
synthetic envelope are simplified to describe both branches.
|
|
2037
|
-
|
|
2038
|
-
Test count: 151 → 154 (+3 covering meta round-trip, replace-all
|
|
2039
|
-
single-row invariant, and synthetic-fallback on empty DB).
|
|
2040
|
-
|
|
2041
|
-
- 9a89124: Step 5.7 — Conformance coverage for the rename heuristic.
|
|
2042
|
-
|
|
2043
|
-
**Spec change (additive, minor)**:
|
|
2044
|
-
|
|
2045
|
-
- `spec/schemas/conformance-case.schema.json` gains
|
|
2046
|
-
`setup.priorScans: Array<{ fixture, flags? }>` — an ordered list of
|
|
2047
|
-
staging scans the runner executes BEFORE the main `invoke`. Each
|
|
2048
|
-
step replaces every non-`.skill-map/` directory in the scope with
|
|
2049
|
-
the named fixture and runs `sm scan` (with optional flags). The DB
|
|
2050
|
-
persists across steps because `.skill-map/` is preserved between
|
|
2051
|
-
swaps. After the last step, the runner copies the top-level
|
|
2052
|
-
`fixture` and runs the case's `invoke`.
|
|
2053
|
-
|
|
2054
|
-
Required to express scenarios that need a prior snapshot (rename
|
|
2055
|
-
heuristic, future incremental cases). The schema is purely
|
|
2056
|
-
additive — every existing case keeps passing without modification.
|
|
2057
|
-
|
|
2058
|
-
- Two new conformance cases under `spec/conformance/cases/`:
|
|
2059
|
-
|
|
2060
|
-
- **`rename-high`** — moving a single file with identical body
|
|
2061
|
-
triggers a high-confidence auto-rename. Asserts:
|
|
2062
|
-
`stats.nodesCount === 1`, `stats.issuesCount === 0`,
|
|
2063
|
-
`nodes[0].path === skills/bar.md`. Verifies the spec invariant
|
|
2064
|
-
that high-confidence renames emit NO issue.
|
|
2065
|
-
- **`orphan-detection`** — deleting a file with no replacement
|
|
2066
|
-
emits exactly one `orphan` issue (severity `info`). Asserts the
|
|
2067
|
-
`ruleId` and `severity` directly.
|
|
2068
|
-
|
|
2069
|
-
- Four new fixture directories under `spec/conformance/fixtures/`:
|
|
2070
|
-
`rename-high-before/`, `rename-high-after/`,
|
|
2071
|
-
`orphan-before/`, `orphan-after/`.
|
|
2072
|
-
|
|
2073
|
-
- `spec/conformance/coverage.md`: row I (Rename heuristic) flips
|
|
2074
|
-
from `🔴 missing` to `🟢 covered`. Notes the medium / ambiguous
|
|
2075
|
-
branches stay covered by `src/test/rename-heuristic.test.ts` for
|
|
2076
|
-
now (assertion vocabulary in the schema is not rich enough to
|
|
2077
|
-
express "the issues array contains an item with ruleId X and
|
|
2078
|
-
data.confidence === 'medium'" — when the conformance schema gains
|
|
2079
|
-
array-filter assertions, those branches can land here too).
|
|
2080
|
-
|
|
2081
|
-
**Runtime change**:
|
|
2082
|
-
|
|
2083
|
-
- `src/conformance/index.ts` runner: implements `setup.priorScans`.
|
|
2084
|
-
Helper `replaceFixture(scope, specRoot, fixture)` clears every
|
|
2085
|
-
top-level entry in the scope except `.skill-map/`, then copies the
|
|
2086
|
-
named fixture on top. Used by both staging steps and the main
|
|
2087
|
-
`fixture` phase.
|
|
2088
|
-
- `src/test/conformance.test.ts`: includes the two new cases in the
|
|
2089
|
-
Step-0b subset. Total conformance cases passing in CI: 1 → 3.
|
|
2090
|
-
|
|
2091
|
-
**`spec/index.json`** regenerated (50 → 57 files). `npm run spec:check`
|
|
2092
|
-
green.
|
|
2093
|
-
|
|
2094
|
-
Test count: 201 → 203 (+2 conformance cases). The Step 5 totals close
|
|
2095
|
-
at: 151 → 203 (+52 across 7 sub-steps).
|
|
2096
|
-
|
|
2097
|
-
### Patch Changes
|
|
2098
|
-
|
|
2099
|
-
- dacd4d9: Move the auto-generated CLI reference from `docs/cli-reference.md` to
|
|
2100
|
-
`context/cli-reference.md`. Spec change is editorial: `cli-contract.md`
|
|
2101
|
-
references the file path in three spots (`--format md` description, the
|
|
2102
|
-
NORMATIVE introspection section, and the "Related" link list); all three
|
|
2103
|
-
updated to the new location. No schema or behavioural change.
|
|
2104
|
-
|
|
2105
|
-
Reference impl: `scripts/build-cli-reference.mjs` writes to the new path,
|
|
2106
|
-
the `cli:reference` / `cli:check` npm scripts point there, and `sm help`
|
|
2107
|
-
output (which embeds the path in the `--format md` flag description) is
|
|
2108
|
-
regenerated. The `docs/` folder is gone.
|
|
2109
|
-
|
|
2110
|
-
## 0.5.1
|
|
2111
|
-
|
|
2112
|
-
### Patch Changes
|
|
77
|
+
### Minor
|
|
2113
78
|
|
|
2114
|
-
-
|
|
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.
|
|
2115
80
|
|
|
2116
81
|
## 0.5.0
|
|
2117
82
|
|
|
2118
|
-
### Minor
|
|
2119
|
-
|
|
2120
|
-
- 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.
|
|
2121
|
-
|
|
2122
|
-
**`spec/index.json`** — closes the gap between what `0.3.0` notes promised and what actually shipped:
|
|
2123
|
-
|
|
2124
|
-
- `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.**
|
|
2125
|
-
- `schemas.topLevel` gains `history-stats` (shape for `sm history stats --json`, already referenced from `cli-contract.md` §History and hashed under `integrity.files`).
|
|
2126
|
-
- 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/`.
|
|
2127
|
-
|
|
2128
|
-
**`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.
|
|
2129
|
-
|
|
2130
|
-
**`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).
|
|
2131
|
-
|
|
2132
|
-
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.
|
|
2133
|
-
|
|
2134
|
-
### Patch Changes
|
|
2135
|
-
|
|
2136
|
-
- 2699276: Fix the extension-kind schemas so they actually validate against real extension manifests.
|
|
2137
|
-
|
|
2138
|
-
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.
|
|
2139
|
-
|
|
2140
|
-
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.
|
|
2141
|
-
|
|
2142
|
-
Changes:
|
|
2143
|
-
|
|
2144
|
-
- 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).
|
|
2145
|
-
- `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.
|
|
2146
|
-
- `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
|
|
2147
84
|
|
|
2148
|
-
|
|
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.
|
|
2149
86
|
|
|
2150
87
|
## 0.4.0
|
|
2151
88
|
|
|
2152
|
-
### Minor
|
|
2153
|
-
|
|
2154
|
-
- 334c51a: Document `--all` as targeted fan-out, not a global flag, in `spec/cli-contract.md`.
|
|
2155
|
-
|
|
2156
|
-
`--all` is valid only on verbs whose contract explicitly lists it:
|
|
2157
|
-
|
|
2158
|
-
- `sm plugins enable <id> | --all` and `sm plugins disable <id> | --all`.
|
|
2159
|
-
- `sm job cancel <job.id> | --all` (cancels every `queued` and `running` job).
|
|
2160
|
-
- `sm job submit <action> --all` and `sm job run --all`.
|
|
2161
|
-
|
|
2162
|
-
Unsupported `--all` usage is an operational error (exit `2`), the same as any other unknown or invalid flag.
|
|
2163
|
-
|
|
2164
|
-
Classification: minor — targeted fan-out semantics are additive for the listed verbs, while avoiding a global flag contract.
|
|
2165
|
-
|
|
2166
|
-
- 3e89d8f: Audit-driven alignment pass. Multiple normative additions and a casing cleanup:
|
|
2167
|
-
|
|
2168
|
-
- **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.
|
|
2169
|
-
- **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.
|
|
2170
|
-
- **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.
|
|
2171
|
-
- **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.
|
|
2172
|
-
- **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.
|
|
2173
|
-
- **"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.
|
|
2174
|
-
- **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.
|
|
2175
|
-
- **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.
|
|
2176
|
-
|
|
2177
|
-
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.
|
|
2178
|
-
|
|
2179
|
-
### Patch Changes
|
|
2180
|
-
|
|
2181
|
-
- 93ffe34: Editorial pass: remove "MVP" terminology from four prose documents.
|
|
2182
|
-
|
|
2183
|
-
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.
|
|
2184
|
-
|
|
2185
|
-
- **`cli-contract.md` §Jobs**: `sm job run --all` description `(MVP: sequential)` → `(sequential through v1.0; in-runner parallelism deferred)`.
|
|
2186
|
-
- **`job-events.md` §Event catalog**: `(post-MVP)` parallel-run note → `(deferred to post-v1.0)`.
|
|
2187
|
-
- **`job-lifecycle.md` §Concurrency**: `MVP (v0.x): one job at a time.` → `Through v1.0 (spec v0.x): one job at a time.`
|
|
2188
|
-
- **`plugin-kv-api.md` §Backup and retention**: `sm plugins forget <id> (post-MVP)` → `sm plugins forget <id> (deferred to post-v1.0)`.
|
|
89
|
+
### Minor
|
|
2189
90
|
|
|
2190
|
-
|
|
91
|
+
- **`--all` documented as targeted fan-out** in `cli-contract.md`. Valid only on verbs whose contract explicitly lists it.
|
|
2191
92
|
|
|
2192
93
|
## 0.3.0
|
|
2193
94
|
|
|
2194
|
-
### Minor
|
|
2195
|
-
|
|
2196
|
-
- 334c51a: Promote `--all` to a normative universal flag in `spec/cli-contract.md §Global flags`.
|
|
2197
|
-
|
|
2198
|
-
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`.
|
|
2199
|
-
|
|
2200
|
-
Concretely extended in this pass:
|
|
2201
|
-
|
|
2202
|
-
- `sm plugins enable <id> | --all` and `sm plugins disable <id> | --all`.
|
|
2203
|
-
- `sm job cancel <job.id> | --all` (cancels every `queued` and `running` job).
|
|
2204
|
-
|
|
2205
|
-
Already normative before this change: `sm job submit <action> --all` and `sm job run --all`.
|
|
2206
|
-
|
|
2207
|
-
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.
|
|
2208
|
-
|
|
2209
|
-
- 3e89d8f: Audit-driven alignment pass. Multiple normative additions and a casing cleanup:
|
|
2210
|
-
|
|
2211
|
-
- **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.
|
|
2212
|
-
- **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.
|
|
2213
|
-
- **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.
|
|
2214
|
-
- **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.
|
|
2215
|
-
- **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.
|
|
2216
|
-
- **"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.
|
|
2217
|
-
- **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.
|
|
2218
|
-
- **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.
|
|
2219
|
-
|
|
2220
|
-
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.
|
|
2221
|
-
|
|
2222
|
-
- 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.
|
|
2223
|
-
|
|
2224
|
-
**`project-config.schema.json` — new optional fields, all non-breaking:**
|
|
2225
|
-
|
|
2226
|
-
- `autoMigrate: boolean` (default `true`) — auto-apply pending kernel + plugin migrations at startup after auto-backup. `false` → startup fails fast if migrations are pending.
|
|
2227
|
-
- `tokenizer: string` (default `cl100k_base`) — name of the offline tokenizer; stored alongside counts so consumers know which encoder produced them.
|
|
2228
|
-
- `scan.maxFileSizeBytes: integer` (default `1048576`) — files larger are skipped with an `info` log.
|
|
2229
|
-
- `jobs.ttlSeconds: integer` (default `3600`) — global fallback TTL when an action manifest omits `expectedDurationSeconds` (typically `mode: local` actions where the field is advisory).
|
|
2230
|
-
- `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.
|
|
2231
|
-
- `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.
|
|
2232
|
-
|
|
2233
|
-
**`job-events.md` — new `Non-job events` section, Stability: experimental across v0.x:**
|
|
2234
|
-
|
|
2235
|
-
- `scan.*`: `scan.started`, `scan.progress` (throttled ≥250 ms), `scan.completed`.
|
|
2236
|
-
- `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)`.
|
|
2237
|
-
- Synthetic run ids follow the existing `r-<mode>-YYYYMMDD-HHMMSS-XXXX` pattern (`r-scan-...`, `r-check-...`) alongside `r-ext-...` for external Skill claims.
|
|
2238
|
-
|
|
2239
|
-
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.
|
|
2240
|
-
|
|
2241
|
-
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.
|
|
2242
|
-
|
|
2243
|
-
- 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).
|
|
2244
|
-
|
|
2245
|
-
**`spec/schemas/frontmatter/base.schema.json` — two new top-level optional fields:**
|
|
2246
|
-
|
|
2247
|
-
- `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.
|
|
2248
|
-
- `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 *)`).
|
|
2249
|
-
|
|
2250
|
-
**`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`.
|
|
2251
|
-
|
|
2252
|
-
`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.
|
|
2253
|
-
|
|
2254
|
-
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.
|
|
2255
|
-
|
|
2256
|
-
- 5935948: Add `sm history stats` schema and normative elapsed-time reporting.
|
|
2257
|
-
|
|
2258
|
-
- **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.
|
|
2259
|
-
- **cli-contract.md §Elapsed time** (new normative section). Every verb that does non-trivial work MUST report its own wall-clock:
|
|
2260
|
-
- **Pretty (stderr)**: last line `done in <formatted>` where `<formatted>` ∈ `{ <N>ms | <N.N>s | <M>m <S>s }`. Suppressed by `--quiet`.
|
|
2261
|
-
- **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).
|
|
2262
|
-
- **Exempt** verbs (sub-millisecond, informational): `sm --version`, `sm --help`, `sm version`, `sm help`, `sm config get`, `sm config list`, `sm config show`.
|
|
2263
|
-
- Measurement spans from after arg-parsing to before terminal write.
|
|
2264
|
-
- **cli-contract.md** `sm history stats` entry: flags enumerated (`--since`, `--until`, `--period`, `--top`) and schema referenced.
|
|
2265
|
-
- **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).
|
|
2266
|
-
|
|
2267
|
-
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.
|
|
2268
|
-
|
|
2269
|
-
- 1455cb1: Normative `priority` for jobs.
|
|
2270
|
-
|
|
2271
|
-
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:
|
|
2272
|
-
|
|
2273
|
-
- **`cli-contract.md` §Jobs**: new flag `sm job submit ... --priority <n>`. Integer; higher runs first; default `0`; negatives permitted (deprioritize).
|
|
2274
|
-
- **`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`.
|
|
2275
|
-
- Configuration key `jobs.perActionPriority.<actionId>`: optional per-action integer override.
|
|
2276
|
-
- Action manifest `defaultPriority`: optional integer; defaults to `0` when omitted.
|
|
2277
|
-
|
|
2278
|
-
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.
|
|
2279
|
-
|
|
2280
|
-
- 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.
|
|
2281
|
-
|
|
2282
|
-
- **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.
|
|
2283
|
-
- **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.
|
|
2284
|
-
- **`schemas.topLevel`** gains `history-stats` (shape for `sm history stats --json`, already referenced in `cli-contract.md` §History).
|
|
2285
|
-
- **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.
|
|
2286
|
-
- **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.
|
|
2287
|
-
- **README example** updated to show both fields side-by-side so the distinction is obvious to first-time consumers.
|
|
2288
|
-
- **Integrity block** regenerated.
|
|
2289
|
-
|
|
2290
|
-
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.
|
|
2291
|
-
|
|
2292
|
-
**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.
|
|
2293
|
-
|
|
2294
|
-
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).
|
|
2295
|
-
|
|
2296
|
-
- 1455cb1: New CLI verb `sm orphans undo-rename <new.path> [--force]` to reverse a medium-confidence auto-rename.
|
|
2297
|
-
|
|
2298
|
-
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.
|
|
2299
|
-
|
|
2300
|
-
This release closes the gap with a dedicated reverse verb:
|
|
2301
|
-
|
|
2302
|
-
- **`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.
|
|
2303
|
-
- **`db-schema.md` §Rename detection**: issue payload now normative.
|
|
2304
|
-
- `auto-rename-medium.data_json` MUST include `{ from, to, confidence: "medium" }`.
|
|
2305
|
-
- `auto-rename-ambiguous.data_json` MUST include `{ to, candidates: [from_a, from_b, ...] }`. `sm orphans undo-rename` requires `--from <old.path>` to pick one.
|
|
2306
|
-
- **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.
|
|
2307
|
-
|
|
2308
|
-
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.
|
|
2309
|
-
|
|
2310
|
-
Classification: minor per `cli-contract.md` §Stability ("adding a verb is a minor bump"). No existing behavior changes; `sm orphans reconcile` semantics are unaffected.
|
|
2311
|
-
|
|
2312
|
-
- 334c51a: **Breaking**: rename two state-zone tables to comply with the normative plural rule in `db-schema.md §Naming conventions`.
|
|
2313
|
-
|
|
2314
|
-
- `state_enrichment` → `state_enrichments`
|
|
2315
|
-
- `state_plugin_kv` → `state_plugin_kvs`
|
|
2316
|
-
|
|
2317
|
-
Index names renamed in lockstep:
|
|
2318
|
-
|
|
2319
|
-
- `ix_state_enrichment_stale_after` → `ix_state_enrichments_stale_after`
|
|
2320
|
-
- `ix_state_plugin_kv_plugin_id` → `ix_state_plugin_kvs_plugin_id`
|
|
2321
|
-
|
|
2322
|
-
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.
|
|
2323
|
-
|
|
2324
|
-
Updated spec artefacts:
|
|
2325
|
-
|
|
2326
|
-
- `spec/db-schema.md` — table section headings, column comments, primary-key footers, index names, and the cross-reference list in §Rename heuristic.
|
|
2327
|
-
- `spec/cli-contract.md` — `sm db reset --state` row in §Database.
|
|
2328
|
-
- `spec/plugin-kv-api.md` — §Overview opener and every downstream reference.
|
|
2329
|
-
- `spec/schemas/plugins-registry.schema.json` — description of the `kv` mode `const`.
|
|
2330
|
-
|
|
2331
|
-
**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`.
|
|
2332
|
-
|
|
2333
|
-
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.
|
|
2334
|
-
|
|
2335
|
-
Companion prose updates in `ROADMAP.md` (§Persistence, §Plugin system, §Enrichment, §Summarizer pattern, Decision #61) and `AGENTS.md` (§Persistence).
|
|
2336
|
-
|
|
2337
|
-
- 93ffe34: Clean up `history.*` in `spec/schemas/project-config.schema.json`.
|
|
2338
|
-
|
|
2339
|
-
**Breaking (pre-1.0 minor per `versioning.md` §Pre-1.0):**
|
|
2340
|
-
|
|
2341
|
-
- **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.
|
|
2342
|
-
|
|
2343
|
-
**Editorial:**
|
|
2344
|
-
|
|
2345
|
-
- `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.
|
|
2346
|
-
|
|
2347
|
-
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.
|
|
2348
|
-
|
|
2349
|
-
**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.
|
|
2350
|
-
|
|
2351
|
-
- 93ffe34: Promote the trigger-normalization pipeline (Decision #21) from implicit to normative in `spec/architecture.md`.
|
|
2352
|
-
|
|
2353
|
-
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).
|
|
2354
|
-
|
|
2355
|
-
Added under `architecture.md §Extension kinds`, paralleling the existing `Adapter · defaultRefreshAction` subsection:
|
|
2356
|
-
|
|
2357
|
-
- **Detector · trigger normalization** — field contract, normative 6-step pipeline, and 8 worked examples.
|
|
2358
|
-
|
|
2359
|
-
Pipeline (applied in exactly this order):
|
|
2360
|
-
|
|
2361
|
-
1. Unicode NFD.
|
|
2362
|
-
2. Strip Unicode `Mn` (diacritics).
|
|
2363
|
-
3. Lowercase (locale-independent).
|
|
2364
|
-
4. Separator unification: hyphen / underscore / any whitespace run → single ASCII space.
|
|
2365
|
-
5. Collapse whitespace (run of ≥2 spaces → 1 space).
|
|
2366
|
-
6. Trim leading/trailing whitespace.
|
|
2367
|
-
|
|
2368
|
-
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.
|
|
2369
|
-
|
|
2370
|
-
§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.
|
|
2371
|
-
|
|
2372
|
-
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.
|
|
2373
|
-
|
|
2374
|
-
Companion prose in `ROADMAP.md §Trigger normalization` (Decision #21 now points here for full rationale + examples).
|
|
2375
|
-
|
|
2376
|
-
### Patch Changes
|
|
2377
|
-
|
|
2378
|
-
- 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.
|
|
2379
|
-
|
|
2380
|
-
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.
|
|
2381
|
-
|
|
2382
|
-
The row now:
|
|
2383
|
-
|
|
2384
|
-
- Shows `[--from <old.path>] [--force]` in the signature.
|
|
2385
|
-
- 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`).
|
|
2386
|
-
- Adds an exit-`5` condition for `--from` referencing a path not in `candidates`.
|
|
2387
|
-
|
|
2388
|
-
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).
|
|
2389
|
-
|
|
2390
|
-
- 93ffe34: Split `sm db reset` into three explicit levels of destruction, each with distinct semantics.
|
|
2391
|
-
|
|
2392
|
-
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.
|
|
2393
|
-
|
|
2394
|
-
After:
|
|
2395
|
-
|
|
2396
|
-
- `sm db reset` — drops `scan_*` only. Keeps `state_*` and `config_*`. Non-destructive, no prompt. Equivalent to asking for a fresh scan.
|
|
2397
|
-
- `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).
|
|
2398
|
-
- `sm db reset --hard` — deletes the DB file entirely. Keeps the plugins folder on disk. Destructive; requires confirmation unless `--yes`.
|
|
2399
|
-
|
|
2400
|
-
Updated files:
|
|
2401
|
-
|
|
2402
|
-
- `spec/cli-contract.md` §Database — new table rows and a rewritten confirmation paragraph.
|
|
2403
|
-
- `spec/db-schema.md` §Zones — one-liner rewritten to list all three levels.
|
|
2404
|
-
- `spec/plugin-kv-api.md` §Scope and lifecycle — three bullets replacing the single prior bullet, explicit about which reset level touches plugin storage.
|
|
2405
|
-
|
|
2406
|
-
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).
|
|
2407
|
-
|
|
2408
|
-
Companion prose updates in `ROADMAP.md` §DB management commands and §Step 1a acceptance list.
|
|
2409
|
-
|
|
2410
|
-
- 93ffe34: Editorial pass: remove "MVP" terminology from four prose documents.
|
|
2411
|
-
|
|
2412
|
-
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.
|
|
2413
|
-
|
|
2414
|
-
- **`cli-contract.md` §Jobs**: `sm job run --all` description `(MVP: sequential)` → `(sequential through v1.0; in-runner parallelism deferred)`.
|
|
2415
|
-
- **`job-events.md` §Event catalog**: `(post-MVP)` parallel-run note → `(deferred to post-v1.0)`.
|
|
2416
|
-
- **`job-lifecycle.md` §Concurrency**: `MVP (v0.x): one job at a time.` → `Through v1.0 (spec v0.x): one job at a time.`
|
|
2417
|
-
- **`plugin-kv-api.md` §Backup and retention**: `sm plugins forget <id> (post-MVP)` → `sm plugins forget <id> (deferred to post-v1.0)`.
|
|
2418
|
-
|
|
2419
|
-
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.
|
|
2420
|
-
|
|
2421
|
-
- 93ffe34: Refresh the `spec/README.md` §Repo layout tree so it matches reality.
|
|
2422
|
-
|
|
2423
|
-
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`.
|
|
2424
|
-
|
|
2425
|
-
Changes:
|
|
2426
|
-
|
|
2427
|
-
- 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.
|
|
2428
|
-
- Add the missing top-level schemas `conformance-case.schema.json` and `history-stats.schema.json`.
|
|
2429
|
-
- Add the whole `schemas/extensions/` folder (base + one per extension kind) — validated at plugin load.
|
|
2430
|
-
- List `package.json` and `index.json` explicitly so external readers know they are published assets.
|
|
2431
|
-
- Drop `(Step 0a phase N)` annotations — Step 0a is complete, the marker is noise.
|
|
2432
|
-
- 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.
|
|
2433
|
-
- Under `interfaces/`, clarify that `security-scanner.md` is a convention over the Action kind, NOT a 7th extension kind — the six kinds remain locked.
|
|
2434
|
-
|
|
2435
|
-
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.
|
|
2436
|
-
|
|
2437
|
-
- 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:
|
|
2438
|
-
|
|
2439
|
-
- **Filesystem artefacts in kebab-case**: every file, directory, enum value, and `issue.ruleId` value. Values stay URL/filename/log-key safe without escaping.
|
|
2440
|
-
- **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.
|
|
2441
|
-
|
|
2442
|
-
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.
|
|
2443
|
-
|
|
2444
|
-
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.
|
|
2445
|
-
|
|
2446
|
-
- 93ffe34: Clarify the TTL resolution procedure in `spec/job-lifecycle.md`.
|
|
2447
|
-
|
|
2448
|
-
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:
|
|
2449
|
-
|
|
2450
|
-
- 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.
|
|
2451
|
-
- 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.
|
|
2452
|
-
|
|
2453
|
-
This patch rewrites the §TTL precedence section as §TTL resolution, split into three explicit steps:
|
|
2454
|
-
|
|
2455
|
-
1. **Base duration**: manifest `expectedDurationSeconds` OR config `jobs.ttlSeconds` (default `3600`).
|
|
2456
|
-
2. **Computed TTL**: `max(base × graceMultiplier, minimumTtlSeconds)`.
|
|
2457
|
-
3. **Overrides** (later wins, skips formula): `jobs.perActionTtl.<actionId>`, then `--ttl` flag.
|
|
2458
|
-
|
|
2459
|
-
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.
|
|
2460
|
-
|
|
2461
|
-
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.
|
|
2462
|
-
|
|
2463
|
-
## 0.2.1
|
|
2464
|
-
|
|
2465
|
-
### Patch Changes
|
|
95
|
+
### Minor
|
|
2466
96
|
|
|
2467
|
-
-
|
|
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`.
|
|
2468
98
|
|
|
2469
99
|
## 0.2.0
|
|
2470
100
|
|
|
2471
|
-
### Minor
|
|
2472
|
-
|
|
2473
|
-
- 79aed4d: **Breaking**: rename `dispatch-lifecycle.md` → `job-lifecycle.md`.
|
|
2474
|
-
|
|
2475
|
-
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.
|
|
2476
|
-
|
|
2477
|
-
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.
|
|
2478
|
-
|
|
2479
|
-
**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`.
|
|
2480
|
-
|
|
2481
|
-
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`.
|
|
2482
|
-
|
|
2483
|
-
## 0.1.2
|
|
2484
|
-
|
|
2485
|
-
### Patch Changes
|
|
2486
|
-
|
|
2487
|
-
- 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.
|
|
2488
|
-
|
|
2489
|
-
## 0.1.1
|
|
2490
|
-
|
|
2491
|
-
### Patch Changes
|
|
101
|
+
### Minor
|
|
2492
102
|
|
|
2493
|
-
-
|
|
103
|
+
- **`@skill-map/spec` published on npm.** First public release of the spec package.
|
|
2494
104
|
|
|
2495
105
|
## 0.1.0
|
|
2496
106
|
|
|
2497
|
-
### Minor
|
|
2498
|
-
|
|
2499
|
-
- 5b3829a: Add conformance case `kernel-empty-boot`:
|
|
2500
|
-
|
|
2501
|
-
- New file: `spec/conformance/cases/kernel-empty-boot.json`.
|
|
2502
|
-
- 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.
|
|
2503
|
-
- Referenced in `conformance/README.md` (§"Cases explicitly referenced elsewhere in the spec"). Entry moved from "pending" to "current" in the case inventory.
|
|
2504
|
-
- Registered in `spec/index.json` and the integrity block (SHA256 regenerated).
|
|
2505
|
-
|
|
2506
|
-
The second pending case, `preamble-bitwise-match`, is deferred to Step 10 (requires `sm job preview` from the job subsystem).
|
|
2507
|
-
|
|
2508
|
-
- 4e0aec4: Initial public spec surface (`v0.1.0`):
|
|
2509
|
-
|
|
2510
|
-
- 21 JSON Schemas (draft 2020-12): 10 top-level, 6 frontmatter, 5 summaries.
|
|
2511
|
-
- 7 prose contracts (architecture, cli-contract, dispatch-lifecycle, job-events, prompt-preamble, db-schema, plugin-kv-api).
|
|
2512
|
-
- 1 interface doc (security-scanner).
|
|
2513
|
-
- Conformance stub: `basic-scan` case, `minimal-claude` fixture, verbatim `preamble-v1.txt`.
|
|
2514
|
-
- Machine-readable `index.json` with integrity hashes per file.
|
|
2515
|
-
|
|
2516
|
-
This is the first tagged release of the skill-map specification.
|
|
2517
|
-
|
|
2518
|
-
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.
|
|
2519
|
-
|
|
2520
|
-
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`.
|
|
2521
|
-
|
|
2522
|
-
Each entry classifies changes into four sections:
|
|
2523
|
-
|
|
2524
|
-
- **Added** — new optional fields, schemas, or contracts.
|
|
2525
|
-
- **Changed** — modifications to existing normative content. Breaking changes are called out explicitly.
|
|
2526
|
-
- **Deprecated** — features scheduled for removal in a future major.
|
|
2527
|
-
- **Removed** — features removed in a major bump.
|
|
2528
|
-
|
|
2529
|
-
Tag convention: `spec-vX.Y.Z` (distinct from CLI tags `cli-vX.Y.Z`).
|
|
2530
|
-
|
|
2531
|
-
---
|
|
2532
|
-
|
|
2533
|
-
## [Unreleased]
|
|
2534
|
-
|
|
2535
|
-
Initial public spec bootstrap (Step 0a phases 1–3).
|
|
2536
|
-
|
|
2537
|
-
### Added
|
|
2538
|
-
|
|
2539
|
-
- `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).
|
|
2540
|
-
- `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.
|
|
2541
|
-
- `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).
|
|
2542
|
-
- 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`).
|
|
2543
|
-
|
|
2544
|
-
### Changed
|
|
2545
|
-
|
|
2546
|
-
- `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`.
|
|
2547
|
-
- `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.
|
|
2548
|
-
- 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.
|
|
2549
|
-
- 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.
|
|
2550
|
-
- `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.
|
|
2551
|
-
- `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.
|
|
2552
|
-
- `versioning.md` and related prose: replace ambiguous milestone terminology with explicit versioned release language.
|
|
2553
|
-
|
|
2554
|
-
### Added
|
|
2555
|
-
|
|
2556
|
-
- Foundation:
|
|
2557
|
-
- `README.md` — human-readable introduction and repo layout.
|
|
2558
|
-
- `versioning.md` — evolution policy, stability tags, 3-minor deprecation window.
|
|
2559
|
-
- `CHANGELOG.md` — this file.
|
|
2560
|
-
- JSON Schemas (21 files, all draft 2020-12, camelCase keys):
|
|
2561
|
-
- Top-level (10): `node`, `link`, `issue`, `scan-result`, `execution-record`, `project-config`, `plugins-registry`, `job`, `report-base`, `conformance-case`.
|
|
2562
|
-
- Frontmatter (6): `base` + per-kind `skill` / `agent` / `command` / `hook` / `note`. Per-kind schemas extend `base` via `allOf`.
|
|
2563
|
-
- Summaries (5): per-kind `skill` / `agent` / `command` / `hook` / `note`. All extend `report-base` via `allOf`.
|
|
2564
|
-
- Prose contracts:
|
|
2565
|
-
- `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.
|
|
2566
|
-
- `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.
|
|
2567
|
-
- `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.
|
|
2568
|
-
- `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.
|
|
2569
|
-
- `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`.
|
|
2570
|
-
- `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.
|
|
2571
|
-
- `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.
|
|
2572
|
-
- Interfaces:
|
|
2573
|
-
- `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.
|
|
2574
|
-
|
|
2575
|
-
### Conventions locked (normative)
|
|
2576
|
-
|
|
2577
|
-
- JSON Schema dialect: draft 2020-12.
|
|
2578
|
-
- Casing: camelCase for all JSON keys (domain, configs, manifests, reports); kebab-case for filenames.
|
|
2579
|
-
- `$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.
|
|
2580
|
-
- Identity: `node.path` (relative to scope root) is the canonical node identifier in v0. Future UUID-based `node.id` lands with write-back.
|
|
2581
|
-
- Required frontmatter: `name`, `description`, `metadata`, `metadata.version`.
|
|
2582
|
-
- Frontmatter: `additionalProperties: true` (rules handle unknown fields). Summaries: `additionalProperties: false` (strict).
|
|
2583
|
-
- Id prefixes: job `d-`, execution record `e-`, run `r-` (all `PREFIX-YYYYMMDD-HHMMSS-XXXX`).
|
|
2584
|
-
- Exit codes: 0 ok / 1 issues / 2 error / 3 duplicate / 4 nonce-mismatch / 5 not-found.
|
|
2585
|
-
- Deprecation window: 3 minor releases between `stable → deprecated` and removal.
|
|
2586
|
-
- Storage modes: a plugin declares exactly one (`kv` or `dedicated`). Mixing forbidden.
|
|
2587
|
-
|
|
2588
|
-
### Conformance (stub)
|
|
2589
|
-
|
|
2590
|
-
- `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.
|
|
2591
|
-
- `conformance/fixtures/minimal-claude/` — 5 MDs (one per kind: skill, agent, command, hook, note) used as the first controlled corpus.
|
|
2592
|
-
- `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.
|
|
2593
|
-
- `conformance/cases/basic-scan.json` — first declarative case. Scans the `minimal-claude` fixture; asserts `schemaVersion: 1`, 5 nodes, 0 issues.
|
|
2594
|
-
|
|
2595
|
-
### Packaging
|
|
2596
|
-
|
|
2597
|
-
- `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`.
|
|
2598
|
-
- `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`.
|
|
2599
|
-
- `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`.
|
|
2600
|
-
|
|
2601
|
-
### Notes
|
|
107
|
+
### Minor
|
|
2602
108
|
|
|
2603
|
-
-
|
|
2604
|
-
- No tagged spec release yet. First tag (`spec-v0.1.0`) lands after Step 0b CI validates the implementation against this stub.
|
|
2605
|
-
- 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.
|