@skill-map/spec 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +777 -3
- package/README.md +11 -13
- package/architecture.md +118 -35
- package/cli-contract.md +38 -25
- package/conformance/README.md +43 -14
- package/conformance/cases/kernel-empty-boot.json +3 -3
- package/conformance/coverage.md +20 -24
- package/db-schema.md +61 -6
- package/index.json +35 -77
- package/interfaces/security-scanner.md +1 -1
- package/job-events.md +75 -1
- package/package.json +1 -1
- package/plugin-author-guide.md +409 -51
- package/schemas/conformance-case.schema.json +14 -5
- package/schemas/execution-record.schema.json +8 -8
- package/schemas/extensions/action.schema.json +2 -2
- package/schemas/extensions/base.schema.json +5 -5
- package/schemas/extensions/extractor.schema.json +48 -0
- package/schemas/extensions/formatter.schema.json +29 -0
- package/schemas/extensions/hook.schema.json +44 -0
- package/schemas/extensions/provider.schema.json +51 -0
- package/schemas/extensions/rule.schema.json +1 -1
- package/schemas/frontmatter/base.schema.json +2 -2
- package/schemas/link.schema.json +4 -4
- package/schemas/node.schema.json +4 -4
- package/schemas/plugins-registry.schema.json +19 -4
- package/schemas/project-config.schema.json +2 -2
- package/schemas/scan-result.schema.json +3 -3
- package/conformance/cases/basic-scan.json +0 -17
- package/conformance/cases/orphan-detection.json +0 -22
- package/conformance/cases/rename-high.json +0 -21
- package/conformance/fixtures/minimal-claude/agents/reviewer.md +0 -16
- package/conformance/fixtures/minimal-claude/commands/status.md +0 -17
- package/conformance/fixtures/minimal-claude/hooks/pre-commit.md +0 -13
- package/conformance/fixtures/minimal-claude/notes/architecture.md +0 -11
- package/conformance/fixtures/minimal-claude/skills/hello.md +0 -22
- package/conformance/fixtures/orphan-after/skills/keep.md +0 -13
- package/conformance/fixtures/orphan-before/skills/keep.md +0 -13
- package/conformance/fixtures/orphan-before/skills/lonely.md +0 -13
- package/conformance/fixtures/rename-high-after/skills/bar.md +0 -14
- package/conformance/fixtures/rename-high-before/skills/foo.md +0 -14
- package/schemas/extensions/adapter.schema.json +0 -40
- package/schemas/extensions/audit.schema.json +0 -47
- package/schemas/extensions/detector.schema.json +0 -41
- package/schemas/extensions/renderer.schema.json +0 -29
- package/schemas/frontmatter/agent.schema.json +0 -17
- package/schemas/frontmatter/command.schema.json +0 -39
- package/schemas/frontmatter/hook.schema.json +0 -29
- package/schemas/frontmatter/note.schema.json +0 -11
- package/schemas/frontmatter/skill.schema.json +0 -37
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,768 @@
|
|
|
1
1
|
# Spec changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 88afe24: Cleanup pass post-v0.8.0 — finishing the renames and wiring the
|
|
8
|
+
conformance kill-switches.
|
|
9
|
+
|
|
10
|
+
**Pre-1.0 minor bump** per `spec/versioning.md` § Pre-1.0. The schema
|
|
11
|
+
field rename below is technically breaking, but ships as a minor while
|
|
12
|
+
the spec stays `0.Y.Z`.
|
|
13
|
+
|
|
14
|
+
## Spec changes (`@skill-map/spec`)
|
|
15
|
+
|
|
16
|
+
### Breaking — `conformance-case.schema.json`
|
|
17
|
+
|
|
18
|
+
- **Rename `setup.disableAllDetectors` → `setup.disableAllExtractors`.**
|
|
19
|
+
Finishes the kind rename Detector → Extractor introduced in 0.8.0
|
|
20
|
+
(Phase 2 of the plug-in model overhaul). The previous name was the
|
|
21
|
+
last residue and it never reached a release where anything consumed
|
|
22
|
+
it.
|
|
23
|
+
- **`setup.disableAll{Providers,Extractors,Rules}` are now consumed
|
|
24
|
+
end-to-end.** Until this release the three toggles were declared in
|
|
25
|
+
the schema and accepted by the runner, but the runner never threaded
|
|
26
|
+
them anywhere — the `kernel-empty-boot` case happened to pass
|
|
27
|
+
because its fixture is empty. The runner now injects
|
|
28
|
+
`SKILL_MAP_DISABLE_ALL_{PROVIDERS,EXTRACTORS,RULES}=1` into the
|
|
29
|
+
child process environment when the matching toggle is `true`, and
|
|
30
|
+
the CLI's scan composer drops every extension of the disabled kind
|
|
31
|
+
from the in-scan pipeline regardless of granularity gates and
|
|
32
|
+
`--no-built-ins`. Each toggle now has a docstring on the schema
|
|
33
|
+
property pointing at the env-var convention.
|
|
34
|
+
- `kernel-empty-boot` case updated for the rename.
|
|
35
|
+
- `conformance/README.md` example updated.
|
|
36
|
+
|
|
37
|
+
### Non-breaking — copy fixes
|
|
38
|
+
|
|
39
|
+
- Comments and docstrings across `architecture.md` and friends already
|
|
40
|
+
refer to "Extractor" everywhere; only the schema field stayed on the
|
|
41
|
+
old name. No prose changes in this bump.
|
|
42
|
+
|
|
43
|
+
## CLI changes (`@skill-map/cli`)
|
|
44
|
+
|
|
45
|
+
### Breaking — `IDiscoveredPlugin.status` enum
|
|
46
|
+
|
|
47
|
+
- **Rename `'loaded'` → `'enabled'`.** The schema enum
|
|
48
|
+
(`plugins-registry.schema.json`) already used `enabled` since 0.8.0;
|
|
49
|
+
the runtime drifted to `loaded` and has now been pulled back so the
|
|
50
|
+
runtime status matches the spec contract. `'disabled'`, the
|
|
51
|
+
semantic pair, was already aligned. Every consumer (`sm plugins
|
|
52
|
+
list`, `sm plugins doctor`, `sm db prune` plugin filter, runtime
|
|
53
|
+
plugin composer) updated. No published consumers exist.
|
|
54
|
+
|
|
55
|
+
### Non-breaking — sweep cleanup
|
|
56
|
+
|
|
57
|
+
- Old `Detector` / `detector` references (kind name, manifest field
|
|
58
|
+
names, JSDoc, comments, test fixture filenames, test variable
|
|
59
|
+
names) replaced with `Extractor` / `extractor` across the
|
|
60
|
+
production code and test suite. Excludes historical CHANGELOG
|
|
61
|
+
entries, explicit migration notes ("Renamed from Detector"), and
|
|
62
|
+
test data strings whose semantics are independent of the kind
|
|
63
|
+
name (e.g. `'@FooDetector'` in trigger normalization tests).
|
|
64
|
+
- A residual reference to "an audit reading `ScanResult.issues`" in
|
|
65
|
+
`validate-all`'s docstring rewritten without the removed kind name.
|
|
66
|
+
|
|
67
|
+
## Tests
|
|
68
|
+
|
|
69
|
+
- `plugin-runtime-branches.test.ts` — five new unit tests covering
|
|
70
|
+
the env-var kill-switch in `composeScanExtensions` (per kind, all
|
|
71
|
+
three together, and stray-value resilience).
|
|
72
|
+
- `conformance-disable-flags.test.ts` — four new e2e tests pointing
|
|
73
|
+
the runner at a populated fixture with each toggle in turn (and a
|
|
74
|
+
baseline) so a regression in the env-var pipeline shows up
|
|
75
|
+
structurally rather than relying on the empty-fixture coincidence.
|
|
76
|
+
|
|
77
|
+
## [Unreleased]
|
|
78
|
+
|
|
79
|
+
### Minor (breaking, pre-1.0)
|
|
80
|
+
|
|
81
|
+
- **`conformance-case.schema.json` — rename `setup.disableAllDetectors`
|
|
82
|
+
→ `setup.disableAllExtractors`.** Finishes the kind rename Detector →
|
|
83
|
+
Extractor introduced in 0.8.0 (Phase 2 of the plug-in model
|
|
84
|
+
overhaul). The previous name was a residue from an unfinished sweep
|
|
85
|
+
and never reached a release that consumed it.
|
|
86
|
+
- **`setup.disableAll{Providers,Extractors,Rules}` are now wired
|
|
87
|
+
end-to-end.** Until this release the toggles were declared in the
|
|
88
|
+
schema but the runner threaded them nowhere; the `kernel-empty-boot`
|
|
89
|
+
case happened to pass because its fixture is empty. The runner now
|
|
90
|
+
injects `SKILL_MAP_DISABLE_ALL_{PROVIDERS,EXTRACTORS,RULES}=1` into
|
|
91
|
+
the child process environment per toggle, and the CLI's scan
|
|
92
|
+
composer drops every extension of the disabled kind from the
|
|
93
|
+
in-scan pipeline (overriding granularity gates and `--no-built-ins`).
|
|
94
|
+
Migration: any case JSON authored against the unwired schema needs
|
|
95
|
+
to swap `disableAllDetectors` for `disableAllExtractors`; behaviour
|
|
96
|
+
changes only when the toggles were already `true` (those cases will
|
|
97
|
+
now actually disable the kind, where previously they relied on
|
|
98
|
+
fixture content for the same outcome).
|
|
99
|
+
|
|
100
|
+
### Patch
|
|
101
|
+
|
|
102
|
+
- Updated `conformance/cases/kernel-empty-boot.json` for the field
|
|
103
|
+
rename above.
|
|
104
|
+
- Updated `conformance/README.md` example for the field rename above.
|
|
105
|
+
- Schema docstrings added to each `disableAll*` property documenting
|
|
106
|
+
the env-var convention the runner uses.
|
|
107
|
+
|
|
108
|
+
## 0.8.0
|
|
109
|
+
|
|
110
|
+
### Minor Changes
|
|
111
|
+
|
|
112
|
+
- 6dad772: v0.8.0 — Pre-1.0 stabilization pass.
|
|
113
|
+
|
|
114
|
+
This release combines two coherent pre-1.0 cleanup pieces that
|
|
115
|
+
both push the project closer to v1.0 stability: the cli-architect
|
|
116
|
+
audit review pass and the plugin model overhaul.
|
|
117
|
+
|
|
118
|
+
Pre-1.0 minor bumps per `versioning.md` § Pre-1.0; breaking
|
|
119
|
+
changes allowed within minor while in `0.Y.Z`. No real downstream
|
|
120
|
+
ecosystem exists yet, so the breaking surface costs nothing
|
|
121
|
+
today.
|
|
122
|
+
|
|
123
|
+
## Part 1 — Pre-1.0 audit review pass
|
|
124
|
+
|
|
125
|
+
Pre-1.0 review pass — `cli-architect` audit findings.
|
|
126
|
+
|
|
127
|
+
Internal audit run by the `cli-architect` agent in REVIEW mode
|
|
128
|
+
produced a Critical / High / Medium / Low / Nit catalog. This
|
|
129
|
+
pass bundles the implementation of every actionable finding into
|
|
130
|
+
one unit so the review can be read end-to-end. **Pre-1.0 minor
|
|
131
|
+
bump**: a few breaking surface changes ride along (CLI sub-verb
|
|
132
|
+
split, exit-code enum exposed, plugin loader option). No
|
|
133
|
+
published downstream consumers exist yet.
|
|
134
|
+
|
|
135
|
+
### Spec changes (`@skill-map/spec`)
|
|
136
|
+
|
|
137
|
+
- **`cli-contract.md`** — `sm scan compare-with <dump> [roots...]`
|
|
138
|
+
is now a sub-verb instead of a `--compare-with <path>` flag on
|
|
139
|
+
`sm scan`. Read-only delta report against a saved `ScanResult`
|
|
140
|
+
JSON dump. Read-only — does not modify the DB. Same exit codes
|
|
141
|
+
(`0` empty delta / `1` drift / `2` operational error). Old flag
|
|
142
|
+
form removed.
|
|
143
|
+
- **`cli-contract.md`** — exit-code `2` "Operational error" row
|
|
144
|
+
clarified to mention environment / runtime mismatches (wrong
|
|
145
|
+
Node version, missing native dependency) explicitly. The
|
|
146
|
+
"unhandled exception" catch-all already covered the case; this
|
|
147
|
+
just removes ambiguity for future implementers.
|
|
148
|
+
- **`cli-contract.md`** — new normative section **§Dry-run**
|
|
149
|
+
between §Exit codes and §Verb catalog defining the contract for
|
|
150
|
+
any verb exposing `-n` / `--dry-run`: no observable side effects
|
|
151
|
+
(DB / FS / config / network / spawns), no auto-provisioning of
|
|
152
|
+
scope directories, output mirrors the live mode with explicit
|
|
153
|
+
"would …" framing, exit codes mirror the live mode, dry-run
|
|
154
|
+
MUST short-circuit `--yes` / `--force` confirmation prompts.
|
|
155
|
+
Per-verb opt-in: the flag is not global, verbs that don't
|
|
156
|
+
declare it MUST reject it as an unknown option. Verb catalog
|
|
157
|
+
rows for `sm init`, `sm db reset` (default + `--state` +
|
|
158
|
+
`--hard`), and `sm db restore` amended to declare and describe
|
|
159
|
+
their `--dry-run` previews.
|
|
160
|
+
|
|
161
|
+
### CLI changes (`@skill-map/cli`)
|
|
162
|
+
|
|
163
|
+
#### Critical — kernel & adapter hygiene
|
|
164
|
+
|
|
165
|
+
- **C1 — `runScanInternal` decomposed.** The 290-line monolith in
|
|
166
|
+
`kernel/orchestrator.ts` split into a thin composer + four pure
|
|
167
|
+
functions: `validateRoots`, `indexPriorSnapshot`,
|
|
168
|
+
`walkAndDetect`, `runRules`. Composer is now 89 lines reading
|
|
169
|
+
top-to-bottom through the pipeline phases. Zero behavioural
|
|
170
|
+
change.
|
|
171
|
+
- **C2 — `withSqlite(options, fn)` helper.** Single utility at
|
|
172
|
+
`cli/util/with-sqlite.ts` standardises the open / use / close
|
|
173
|
+
idiom every read-side command was open-coding. Eliminates four
|
|
174
|
+
classes of boilerplate bugs (forgotten close, `autoBackup`
|
|
175
|
+
drift, double-close, missing `try/finally`). Migrated 20 call
|
|
176
|
+
sites across `check`, `export`, `graph`, `history`, `init`,
|
|
177
|
+
`jobs`, `list`, `orphans`, `plugins`, `scan`, `show`, `watch`,
|
|
178
|
+
plus `cli/util/plugin-runtime.ts`. Companion `tryWithSqlite`
|
|
179
|
+
short-circuits when the DB file does not exist, replacing the
|
|
180
|
+
`if (existsSync) { withSqlite(...) }` chain. In `scan.ts` the
|
|
181
|
+
read-prior + persist double-open consolidated into a single
|
|
182
|
+
`withSqlite` callback that brackets read prior → run scan →
|
|
183
|
+
guard → persist when `willPersist`. Saves one migration
|
|
184
|
+
discovery pass + one WAL setup per normal scan (~50–100ms).
|
|
185
|
+
|
|
186
|
+
#### High — UX & contract integrity
|
|
187
|
+
|
|
188
|
+
- **H3 — `--dry-run` semantics unified across `init` / `db reset`
|
|
189
|
+
/ `db restore`.** The new spec §Dry-run codifies the "no
|
|
190
|
+
writes, reads OK" contract; three verbs that did not previously
|
|
191
|
+
expose a preview now do: - `sm init --dry-run` — previews the would-create lines for
|
|
192
|
+
`.skill-map/`, `settings.json`, `settings.local.json`,
|
|
193
|
+
`.skill-mapignore`, the `.gitignore` entries that would be
|
|
194
|
+
appended (deduped against the existing file), the DB
|
|
195
|
+
provisioning, and the first-scan trigger. Honours `--force`
|
|
196
|
+
for the would-overwrite preview. Re-init over an existing
|
|
197
|
+
scope without `--force` still exits 2 (same gate as live). - `sm db reset --dry-run` (default + `--state`) — opens the DB
|
|
198
|
+
read-only, computes the row count per `scan_*` (and `state_*`
|
|
199
|
+
when `--state`) table, and prints them. No `DELETE`
|
|
200
|
+
statements issued. Bypasses the `--state` confirmation prompt
|
|
201
|
+
entirely. - `sm db reset --hard --dry-run` — reports the DB file path and
|
|
202
|
+
size that would be unlinked; missing-file case prints a clear
|
|
203
|
+
no-op line instead of an error. - `sm db restore <src> --dry-run` — validates the source exists
|
|
204
|
+
(still exits 5 if missing), reports the source size and
|
|
205
|
+
whether the target would be created or overwritten, plus the
|
|
206
|
+
WAL / SHM sidecars that would be dropped. Bypasses the
|
|
207
|
+
confirmation prompt.
|
|
208
|
+
Implementation: new helper `previewGitignoreEntries(scopeRoot,
|
|
209
|
+
entries)` in `init.ts` mirrors `ensureGitignoreEntries` parsing
|
|
210
|
+
so the preview tracks the live outcome exactly. Texts moved
|
|
211
|
+
into `cli/i18n/init.texts.ts` and `cli/i18n/db.texts.ts` per
|
|
212
|
+
the N4 pattern. **9 new tests** under `init-cli.test.ts` (5
|
|
213
|
+
cases) and `db-cli.test.ts` (9 cases) cover the previews + the
|
|
214
|
+
spec invariants ("DB file checksum unchanged after dry-run",
|
|
215
|
+
"scope directory absent after dry-run", "source-not-found
|
|
216
|
+
still exits 5", "confirmation prompt skipped under dry-run").
|
|
217
|
+
- **H1 — Centralised exit codes.** New `cli/util/exit-codes.ts`
|
|
218
|
+
exporting `ExitCode` (`Ok` / `Issues` / `Error` / `Duplicate` /
|
|
219
|
+
`NonceMismatch` / `NotFound`) and the type alias `TExitCode`.
|
|
220
|
+
Every `Command#execute()` migrated from numeric literals (123
|
|
221
|
+
sites across 17 files) to the enum. Single source of truth
|
|
222
|
+
aligned with `spec/cli-contract.md` §Exit codes. **Bug fix
|
|
223
|
+
surfaced en passant:** `sm job prune` returned `2` for "DB
|
|
224
|
+
missing" while every other read-side verb returned `5` via
|
|
225
|
+
`assertDbExists`; corrected to use the shared helper and return
|
|
226
|
+
`NotFound`. Companion test updated to expect `5`.
|
|
227
|
+
- **H2 — Plugin loader timeout.** `IPluginLoaderOptions.loadTimeoutMs`
|
|
228
|
+
(default `5000`, exported as `DEFAULT_PLUGIN_IMPORT_TIMEOUT_MS`).
|
|
229
|
+
Each dynamic `import()` now races against a timer; on timeout
|
|
230
|
+
the plugin is reported as `load-error` with a message naming
|
|
231
|
+
the elapsed budget and pointing at top-level side effects as
|
|
232
|
+
the likely cause (network call, infinite loop, large blocking
|
|
233
|
+
work). Without this a plugin with a hanging top-level `await`
|
|
234
|
+
blocks every host CLI command indefinitely.
|
|
235
|
+
- **H4 — `--strict` self-validates `--json` output.** When
|
|
236
|
+
`sm scan --strict --json` is invoked, the produced `ScanResult`
|
|
237
|
+
is validated against `scan-result.schema.json` before stdout.
|
|
238
|
+
Catches the case where a custom detector emits a Link that
|
|
239
|
+
passes the shallow `validateLink` guard but fails the full
|
|
240
|
+
schema, which would silently land in stdout and break a
|
|
241
|
+
downstream `sm scan compare-with -`.
|
|
242
|
+
- **H5 — External-link discrimination uses URL-shape regex.**
|
|
243
|
+
`isExternalUrlLink` was string-matching `http://` / `https://`
|
|
244
|
+
only; any other URL scheme (`mailto:`, `data:`, `file:///`,
|
|
245
|
+
`ftp://`) was silently classified as internal and polluted the
|
|
246
|
+
graph as a fake internal link with `byPath` lookups that always
|
|
247
|
+
missed. Replaced with the RFC 3986 scheme regex
|
|
248
|
+
(`/^[a-z][a-z0-9+\-.]+:/i`), guarding against Windows-style
|
|
249
|
+
absolute paths via the ≥ 2-char scheme constraint.
|
|
250
|
+
- **H6 — Prior snapshot validated under `--strict`.** Both
|
|
251
|
+
`sm scan` and `sm watch`, when run with `--strict`, validate
|
|
252
|
+
the DB-resident `ScanResult` against the spec schema before
|
|
253
|
+
handing it to the orchestrator. A DB corrupted manually or
|
|
254
|
+
mid-rollback used to slip nodes with malformed `bodyHash` /
|
|
255
|
+
`frontmatterHash` into the rename heuristic, where the
|
|
256
|
+
dereference would silently produce spurious matches.
|
|
257
|
+
|
|
258
|
+
#### Medium — surface & extensibility
|
|
259
|
+
|
|
260
|
+
- **M1 — `sm scan compare-with` sub-verb.** New
|
|
261
|
+
`ScanCompareCommand` in `cli/commands/scan-compare.ts`; the
|
|
262
|
+
`--compare-with` flag is removed from `ScanCommand`. The
|
|
263
|
+
sub-verb form structurally rejects flag combos that used to
|
|
264
|
+
require runtime guards (`--changed`, `--no-built-ins`,
|
|
265
|
+
`--allow-empty`, `--watch`): Clipanion rejects them at parse
|
|
266
|
+
time as unknown options.
|
|
267
|
+
- **M2 — `kernel/index.ts` enumerated exports.** Replaced the two
|
|
268
|
+
`export type *` wildcards (from `./types.js` and
|
|
269
|
+
`./ports/index.js`) with explicit named exports. Same set of
|
|
270
|
+
public types — the DTS size and tests confirm parity. Going
|
|
271
|
+
forward, any new domain type or port change requires an
|
|
272
|
+
explicit edit to the barrel, preventing silent surface drift.
|
|
273
|
+
- **M3 — Build hack documented (workaround retained).** Tried to
|
|
274
|
+
replace the post-build `restoreNodeSqliteImports` pass with
|
|
275
|
+
`external: ['node:sqlite']` in `tsup.config.ts`. Esbuild marks
|
|
276
|
+
the specifier as external but still strips the `node:` prefix;
|
|
277
|
+
same outcome with `[/^node:/]` regex and `packages: 'external'`
|
|
278
|
+
(which also externalises real npm deps). Reverted to the
|
|
279
|
+
post-build `replaceAll` pass, with a docstring documenting
|
|
280
|
+
every workaround attempted so the next agent does not repeat
|
|
281
|
+
the spike.
|
|
282
|
+
- **M4 — `tryWithSqlite` helper.** See C2.
|
|
283
|
+
- **M5 — `CamelCasePlugin` trap documented.** Added a
|
|
284
|
+
trap-warning block to `SqliteStorageAdapter`'s docstring:
|
|
285
|
+
`sql.raw` / `sql\`...\``template literals do NOT pass through
|
|
286
|
+
the`CamelCasePlugin`; raw SQL fragments must use snake_case to
|
|
287
|
+
match the migrations.
|
|
288
|
+
- **M6 — Per-extension error reporting.** When the orchestrator
|
|
289
|
+
drops a link emitted with an undeclared kind or an issue with
|
|
290
|
+
an invalid severity, it now emits a `type: 'extension.error'`
|
|
291
|
+
`ProgressEvent` instead of silently swallowing. The CLI
|
|
292
|
+
subscribes via the new `createCliProgressEmitter(stderr)`
|
|
293
|
+
helper and renders those events as `extension.error: <message>`
|
|
294
|
+
on stderr. Plugin authors finally see WHY their link / issue
|
|
295
|
+
disappears from the result. Wired in `scan` (normal +
|
|
296
|
+
compare-with), `watch`, and `init`.
|
|
297
|
+
- **M7 — Type naming convention documented (no rename).** Top-of-
|
|
298
|
+
file docstring in `kernel/types.ts` and a new section in
|
|
299
|
+
`AGENTS.md` describe the four-bucket convention the codebase
|
|
300
|
+
has always implicitly followed: domain types (no prefix,
|
|
301
|
+
mirrors spec schemas), hexagonal ports (`Port` suffix), runtime
|
|
302
|
+
extension contracts (`I` prefix), internal shapes (`I`
|
|
303
|
+
prefix). Mass rename was rejected after a cost-benefit pass —
|
|
304
|
+
naming changes are cheap to write but expensive to review;
|
|
305
|
+
existing names are mostly coherent. The agent base
|
|
306
|
+
(`_plugins/minions/shared/architect.md`) gained a "Naming
|
|
307
|
+
conventions check" sub-section in REVIEW mode so future audits
|
|
308
|
+
reach the same conclusion.
|
|
309
|
+
|
|
310
|
+
#### Low / nit — cleanup
|
|
311
|
+
|
|
312
|
+
- **L1 — `omitModule` JSON replacer precision.** Identifies the
|
|
313
|
+
ESM namespace by `[Symbol.toStringTag] === 'Module'` instead of
|
|
314
|
+
matching every `module` key blindly. A plugin manifest that
|
|
315
|
+
legitimately ships an unrelated `module` field (e.g. a string
|
|
316
|
+
property in `metadata`) is no longer silently dropped from
|
|
317
|
+
`sm plugins list --json` output.
|
|
318
|
+
- **L2 — Stub verbs flagged in `--help`.** Every
|
|
319
|
+
`not-yet-implemented` verb in `cli/commands/stubs.ts` carries a
|
|
320
|
+
`(planned)` suffix on its `description`, surfaced in
|
|
321
|
+
`sm --help`. The `notImplemented` helper now writes
|
|
322
|
+
`<verb>: not yet implemented (planned).` on stderr instead of
|
|
323
|
+
promising a specific Step number — roadmap step numbers shift
|
|
324
|
+
mid-flight, stale promises in `--help` are worse than no
|
|
325
|
+
promise.
|
|
326
|
+
- **L3 — Dead `eslint-disable` removed** from
|
|
327
|
+
`cli/util/plugin-runtime.ts`.
|
|
328
|
+
- **N1 — `Link.source` vs `Link.sources` doc clarified.** Both
|
|
329
|
+
fields now carry inline doc-comments calling out the singular /
|
|
330
|
+
plural naming trap. Spec-frozen, but the ambiguity is the
|
|
331
|
+
easiest way to misread the type for new contributors.
|
|
332
|
+
- **N2 — `sm check` Usage examples expanded.** The `-g/--global`
|
|
333
|
+
and `--db <path>` flags were declared but missing from the
|
|
334
|
+
`Usage.examples` block — asymmetry with `sm scan` and the rest
|
|
335
|
+
of the read-side verbs that ship the same flags. Two examples
|
|
336
|
+
added: `sm check --global` and `sm check --db
|
|
337
|
+
/path/to/skill-map.db`.
|
|
338
|
+
- **N4 — Error / hint strings extracted to `*.texts.ts` modules
|
|
339
|
+
with `{{name}}` template interpolation.** Pre-1.0 is the
|
|
340
|
+
natural moment to seed the pattern before the string set grows.
|
|
341
|
+
The workspace `ui/` already has a sibling layout at
|
|
342
|
+
`ui/src/i18n/` (functions returning template literals); CLI
|
|
343
|
+
takes a deliberately different shape — flat string templates
|
|
344
|
+
with `{{name}}` placeholders, interpolated by a tiny
|
|
345
|
+
`tx(template, vars)` helper. Rationale: the template form is
|
|
346
|
+
**drop-in compatible with Transloco / Mustache / Handlebars**
|
|
347
|
+
(the syntax they all share) so the day this project migrates to
|
|
348
|
+
a real i18n library, the strings move as-is. Functions would
|
|
349
|
+
have to be re-shaped first.
|
|
350
|
+
|
|
351
|
+
Helper at `kernel/util/tx.ts`. Contract:
|
|
352
|
+
|
|
353
|
+
- Every `{{name}}` token MUST have a matching key in the vars
|
|
354
|
+
object — missing key throws (silent fallback hides
|
|
355
|
+
forgotten args in production).
|
|
356
|
+
- `null` / `undefined` values throw — caller coerces
|
|
357
|
+
upstream.
|
|
358
|
+
- Whitespace inside the braces tolerated (`{{ name }}`) so
|
|
359
|
+
long templates wrap cleanly across `+`-joined lines.
|
|
360
|
+
- Plural / conditional logic does NOT live in the template;
|
|
361
|
+
the caller picks `*_singular` vs `*_plural` keys.
|
|
362
|
+
|
|
363
|
+
Files created:
|
|
364
|
+
|
|
365
|
+
- `kernel/util/tx.ts` — the helper itself, with 13 tests in
|
|
366
|
+
`test/tx.test.ts` (single / multi token, whitespace,
|
|
367
|
+
missing / null / undefined keys, identifier shapes, error
|
|
368
|
+
truncation).
|
|
369
|
+
- `kernel/i18n/orchestrator.texts.ts` — frontmatter
|
|
370
|
+
malformed/invalid templates, `extension.error` payloads,
|
|
371
|
+
root validation errors.
|
|
372
|
+
- `kernel/i18n/plugin-loader.texts.ts` — every `load-error` /
|
|
373
|
+
`invalid-manifest` / `incompatible-spec` reason, plus the
|
|
374
|
+
import timeout message.
|
|
375
|
+
- `cli/i18n/scan.texts.ts` — `sm scan` flag-clash / scan
|
|
376
|
+
failure / guard / summary templates, plus the `sm scan
|
|
377
|
+
|
|
378
|
+
compare-with`dump-load errors.
|
|
379
|
+
-`cli/i18n/watch.texts.ts`—`sm watch`lifecycle templates.
|
|
380
|
+
-`cli/i18n/init.texts.ts`—`sm init`templates including
|
|
381
|
+
the`--dry-run`previews and the singular/plural pair for
|
|
382
|
+
gitignore updates.
|
|
383
|
+
-`cli/i18n/db.texts.ts`—`sm db reset`/`sm db restore` templates including their`--dry-run`previews.
|
|
384
|
+
-`cli/i18n/cli-progress-emitter.texts.ts`— the
|
|
385
|
+
`extension.error: ...` stderr line.
|
|
386
|
+
|
|
387
|
+
String content moved verbatim — every existing test that
|
|
388
|
+
matches on stderr / stdout content keeps passing. Trivial
|
|
389
|
+
single-token strings (`'No issues.\n'`) and rare per-handler
|
|
390
|
+
bespoke phrases stay inline; the pattern is now established
|
|
391
|
+
for whoever wants to migrate them in a follow-up.
|
|
392
|
+
|
|
393
|
+
Note on `ui/` divergence: today the two workspaces use
|
|
394
|
+
different shapes for their text tables (functions in `ui/`,
|
|
395
|
+
templates in `cli/`). Aligning them is a follow-up — the day a
|
|
396
|
+
real i18n library lands, both converge on its native shape.
|
|
397
|
+
The CLI shape is closer to the eventual destination.
|
|
398
|
+
|
|
399
|
+
- **N6 — `TIssueSeverity` aliased to `Severity`.** SQLite schema
|
|
400
|
+
type now reads `type TIssueSeverity = Severity` instead of
|
|
401
|
+
duplicating the union literal. Keeps DB and runtime in
|
|
402
|
+
lock-step if the union ever evolves.
|
|
403
|
+
|
|
404
|
+
### Migrations consolidation (kernel DB)
|
|
405
|
+
|
|
406
|
+
- **`src/migrations/001_initial.sql` + `002_scan_meta.sql`**
|
|
407
|
+
consolidated into a single `001_initial.sql`. Pre-1.0 with no
|
|
408
|
+
released DBs to forward-migrate, the two-file split was a
|
|
409
|
+
historical accident from an incremental shipment. After
|
|
410
|
+
consolidation: same 12 tables, same constraints, same indexes;
|
|
411
|
+
`PRAGMA user_version` of a freshly-initialised DB is now `1`
|
|
412
|
+
instead of `2`. Migration runner is unchanged (it tolerates any
|
|
413
|
+
count of `NNN_*.sql` files).
|
|
414
|
+
|
|
415
|
+
### Test coverage (Part 1)
|
|
416
|
+
|
|
417
|
+
- New tests for H2 (plugin loader timeout — 2 cases),
|
|
418
|
+
M6 (orchestrator `extension.error` emission — 3 cases),
|
|
419
|
+
CLI progress emitter wiring (4 cases). The compare-with suite
|
|
420
|
+
(`scan-compare.test.ts`, 9 cases) was migrated to
|
|
421
|
+
`ScanCompareCommand` and the three flag-clash tests dropped
|
|
422
|
+
(the flags are now structurally absent on the sub-verb). Test
|
|
423
|
+
totals: 479 (start of pass) → 488 (after H2/M6 tests) → 485
|
|
424
|
+
(after the three flag-clash deletions).
|
|
425
|
+
|
|
426
|
+
### Deferred / out of scope
|
|
427
|
+
|
|
428
|
+
The findings below were reviewed but did not warrant code
|
|
429
|
+
changes; each has its own resolution noted alongside.
|
|
430
|
+
|
|
431
|
+
- **L4 — `runScan` / `runScanWithRenames` unification.** Already
|
|
432
|
+
resolved by C1 (both are thin wrappers around
|
|
433
|
+
`runScanInternal`).
|
|
434
|
+
- **L5 — Node-version-guard exit code.** Reviewed against the
|
|
435
|
+
updated exit-code table; existing `2` is correct under
|
|
436
|
+
"operational error / unhandled exception". Spec table got the
|
|
437
|
+
environment-mismatch clarification (above).
|
|
438
|
+
- **L6 — `loadSchemaValidators()` cache.** Already cached at
|
|
439
|
+
module level since Step 5.12.
|
|
440
|
+
- **L7 — `pkg with { type: 'json' }` portability.** Stable in
|
|
441
|
+
Node ≥ 22; `engines.node": ">=24.0"` covers it. No fallback
|
|
442
|
+
needed.
|
|
443
|
+
- **N3 — `compare-with` "dump not found" exit code.** The error
|
|
444
|
+
paths in `ScanCompareCommand` already use the `ExitCode.Error`
|
|
445
|
+
enum (= 2) for dump load failures, matching the spec clause for
|
|
446
|
+
operational errors.
|
|
447
|
+
- **N5 — Exit-code list completeness.** Verified the comment in
|
|
448
|
+
`cli/entry.ts` against `spec/cli-contract.md` §Exit codes —
|
|
449
|
+
identical, no edit needed.
|
|
450
|
+
|
|
451
|
+
## Part 2 — Plugin model overhaul (5-phase implementation)
|
|
452
|
+
|
|
453
|
+
### Summary
|
|
454
|
+
|
|
455
|
+
The plugin model received a comprehensive overhaul before
|
|
456
|
+
stabilizing at v1.0. Plugin kinds total after this bump: **6**
|
|
457
|
+
(Provider, Extractor, Rule, Action, Formatter, Hook). All
|
|
458
|
+
breakings are pre-1.0 minor per `versioning.md` § Pre-1.0.
|
|
459
|
+
|
|
460
|
+
### Phase 1 (commit 7354c26) — Foundation
|
|
461
|
+
|
|
462
|
+
Five sub-phases, additive or pre-1.0 minor breakings:
|
|
463
|
+
|
|
464
|
+
- **A.4** — three-tier frontmatter validation model documented in
|
|
465
|
+
`plugin-author-guide.md` (default permissive + `unknown-field`
|
|
466
|
+
rule + `scan.strict` promote-to-error). Behavior unchanged.
|
|
467
|
+
- **A.5** — plugin id global uniqueness: `directory ==
|
|
468
|
+
manifest.id` rule, new status `id-collision` (sixth),
|
|
469
|
+
validation in boot/scan/doctor. Cross-root collisions block
|
|
470
|
+
both involved plugins; user resolves by renaming.
|
|
471
|
+
- **A.6** — extension ids qualified `<plugin-id>/<ext-id>` in
|
|
472
|
+
registry. Built-ins classified into `claude/*` (4 Claude-
|
|
473
|
+
specific) and `core/*` (7 kernel built-ins) bundles. New
|
|
474
|
+
`Registry.get/find` APIs; `defaultRefreshAction` schema
|
|
475
|
+
requires the qualified pattern; `extension.error` events emit
|
|
476
|
+
qualified ids.
|
|
477
|
+
- **A.10** — optional `applicableKinds` filter on Detector
|
|
478
|
+
manifest; fail-fast skip for non-matching kinds (zero CPU/LLM
|
|
479
|
+
cost); doctor warning for kinds not declared by any installed
|
|
480
|
+
Provider. Empty array invalid; absence preserves apply-to-all
|
|
481
|
+
default.
|
|
482
|
+
- **Granularity** — Built-ins now respect `config_plugins`
|
|
483
|
+
enable/disable via granularity-aware filtering. New
|
|
484
|
+
`IBuiltInBundle` shape with `granularity: 'bundle' |
|
|
485
|
+
'extension'`; `claude` ships as bundle (all-or-nothing), `core`
|
|
486
|
+
as extension (each toggleable). User plugins default to bundle;
|
|
487
|
+
opt in via `granularity` in `plugin.json`. Both plugin ids and
|
|
488
|
+
qualified extension ids accepted as keys in `config_plugins`
|
|
489
|
+
and `settings.json#/plugins` (no schema change needed).
|
|
490
|
+
|
|
491
|
+
550/550 tests pass (+33 vs baseline 517).
|
|
492
|
+
|
|
493
|
+
### Phase 2 (commit ae3eaa6) — Renames
|
|
494
|
+
|
|
495
|
+
Four sub-phases, all breaking but allowed in minor pre-1.0:
|
|
496
|
+
|
|
497
|
+
- **2a (Renderer → Formatter)** — Kind, types, files renamed.
|
|
498
|
+
Method `render(ctx)` → `format(ctx)`; manifest field `format`
|
|
499
|
+
→ `formatId` (TS clash resolution). Same contract: graph →
|
|
500
|
+
string, deterministic-only.
|
|
501
|
+
- **2b (Adapter → Provider)** — New required field
|
|
502
|
+
`explorationDir` on the manifest (e.g. `~/.claude` for the
|
|
503
|
+
Claude Provider). DB schema migrated in-place (column
|
|
504
|
+
`nodes.adapter` → `nodes.provider`, etc.). The
|
|
505
|
+
hexagonal-architecture `RunnerPort.adapter` /
|
|
506
|
+
`StoragePort.adapter` is unchanged.
|
|
507
|
+
- **2c (Audit removed)** — Audit kind removed. The single
|
|
508
|
+
built-in `validate-all` migrated to a Rule (qualified id
|
|
509
|
+
`core/validate-all`, `evaluate(ctx) → Issue[]`). CLI verbs
|
|
510
|
+
`sm audit *` removed; users invoke via `sm check --rules
|
|
511
|
+
core/validate-all`.
|
|
512
|
+
- **2d (Detector → Extractor)** — Method signature changes from
|
|
513
|
+
`detect(ctx) → Link[]` to `extract(ctx) → void` — output flows
|
|
514
|
+
through three ctx callbacks: `emitLink`, `enrichNode`, `store`.
|
|
515
|
+
Built-ins migrated maintain functional parity using `emitLink`.
|
|
516
|
+
Persistence of `enrichNode` deferred to Phase 4 (A.8 stale
|
|
517
|
+
layer); orchestrator buffers in memory today.
|
|
518
|
+
|
|
519
|
+
554/554 cli + 32/32 testkit pass.
|
|
520
|
+
|
|
521
|
+
### Phase 3 (commit 34f993e) — Schema relocation
|
|
522
|
+
|
|
523
|
+
**A.2** — Per-kind frontmatter schemas relocate from spec to the
|
|
524
|
+
Provider that declares them. Spec keeps only `frontmatter/base`
|
|
525
|
+
(universal).
|
|
526
|
+
|
|
527
|
+
- 5 schemas moved (`git mv`):
|
|
528
|
+
`spec/schemas/frontmatter/{skill,agent,command,hook,note}.schema.json`
|
|
529
|
+
→ built-in Claude Provider's `schemas/` directory. New `$id`:
|
|
530
|
+
`https://skill-map.dev/providers/claude/v1/frontmatter/<kind>`.
|
|
531
|
+
Cross-package `$ref` resolves via the spec base's `$id`
|
|
532
|
+
(`https://skill-map.dev/spec/v0/frontmatter/base.schema.json`);
|
|
533
|
+
AJV resolves by `$id` when both schemas register on the same
|
|
534
|
+
instance.
|
|
535
|
+
- Provider manifest gains a required `kinds` map subsuming three
|
|
536
|
+
former fields: `emits` (now derives from
|
|
537
|
+
`Object.keys(kinds)`), the flat `defaultRefreshAction` map (now
|
|
538
|
+
per-entry inside `kinds[<kind>].defaultRefreshAction`), and the
|
|
539
|
+
new `schema` (path to the per-kind schema relative to the
|
|
540
|
+
provider directory).
|
|
541
|
+
- Built-in Claude Provider migrated: 5 kind entries (skill,
|
|
542
|
+
agent, command, hook, note), each with `schema`, `schemaJson`
|
|
543
|
+
(runtime field, AJV-compiled at load), and qualified
|
|
544
|
+
`defaultRefreshAction` (`claude/summarize-<kind>`).
|
|
545
|
+
- Kernel orchestrator parse phase asks the Provider for the
|
|
546
|
+
schema via `IProviderFrontmatterValidator` (composed by scan
|
|
547
|
+
via `buildProviderFrontmatterValidator`) instead of reading
|
|
548
|
+
from spec/. Flow: validate base → look up provider → validate
|
|
549
|
+
per-kind schema from Provider.
|
|
550
|
+
- `schema-validators.ts` catalog loses the 5 per-kind frontmatter
|
|
551
|
+
entries; only `frontmatter-base` remains kernel-known.
|
|
552
|
+
`plugin-loader`'s `stripFunctionsAndPluginId` now also strips
|
|
553
|
+
`schemaJson` (runtime-only) from each `kinds` entry before
|
|
554
|
+
AJV-validating the manifest.
|
|
555
|
+
- Coverage matrix: 28 → 23 schemas (the 5 per-kind frontmatter
|
|
556
|
+
schemas are now Provider-owned and ship with their own
|
|
557
|
+
conformance suite in Phase 5 / A.13).
|
|
558
|
+
|
|
559
|
+
556/556 cli + 32/32 testkit pass.
|
|
560
|
+
|
|
561
|
+
### Phase 4 (commit e62695f) — Probabilistic infra
|
|
562
|
+
|
|
563
|
+
Five sub-phases, all breaking but allowed in minor pre-1.0:
|
|
564
|
+
|
|
565
|
+
- **4a (A.9)** — fine-grained Extractor cache via new
|
|
566
|
+
`scan_extractor_runs` table. Resolves gap where newly
|
|
567
|
+
registered Extractors silently skipped cached nodes; cache hit
|
|
568
|
+
logic now per-(node, extractor). Uninstalled Extractors cleaned
|
|
569
|
+
(rows + orphan links). Migration in-place.
|
|
570
|
+
- **4b (A.12)** — opt-in `outputSchema` for plugin custom
|
|
571
|
+
storage. Manifest gains `storage.schema` (Mode A) and
|
|
572
|
+
`storage.schemas` (Mode B) for AJV validation of
|
|
573
|
+
`ctx.store.write/.set` calls. Throws on shape violation;
|
|
574
|
+
default absent = permissive.
|
|
575
|
+
- **4c (A.8)** — enrichment layer + stale tracking. New
|
|
576
|
+
`node_enrichments` table persists per-(node, extractor)
|
|
577
|
+
partials separately from author's frontmatter (immutable).
|
|
578
|
+
Probabilistic enrichments track `body_hash_at_enrichment`; scan
|
|
579
|
+
flags `stale=1` on body change (NOT deleted, preserves LLM
|
|
580
|
+
cost). Helper `mergeNodeWithEnrichments` filters stale +
|
|
581
|
+
last-write-wins. New verbs `sm refresh <node>` and
|
|
582
|
+
`sm refresh --stale` (stubs awaiting Step 10).
|
|
583
|
+
- **4d (A.11)** — sixth plugin kind `hook`. Declarative
|
|
584
|
+
subscriber to a curated set of 8 lifecycle events (`scan.*`,
|
|
585
|
+
extractor/rule/action.completed,
|
|
586
|
+
job.spawning/completed/failed). Other events deliberately not
|
|
587
|
+
hookable. Manifest declares `triggers[]` (load-time validated)
|
|
588
|
+
and optional `filter`. Three new kernel events added to
|
|
589
|
+
catalog. Dual-mode (det dispatched in-process; prob deferred to
|
|
590
|
+
Step 10).
|
|
591
|
+
- **4e (A.7)** — `sm check --include-prob` opt-in flag (stub).
|
|
592
|
+
Default `sm check` unchanged: det only, CI-safe. With flag:
|
|
593
|
+
detects prob rules, emits stderr advisory; full dispatch awaits
|
|
594
|
+
Step 10. Combines with `--rules`, `-n`, `--no-plugins`.
|
|
595
|
+
|
|
596
|
+
591/591 cli + 32/32 testkit pass.
|
|
597
|
+
|
|
598
|
+
### Phase 5 (commit 03b5a65) — Conformance + cleanup
|
|
599
|
+
|
|
600
|
+
**A.13** — Conformance fixture relocation:
|
|
601
|
+
|
|
602
|
+
- 3 cases moved (`git mv`): `basic-scan`, `orphan-detection`,
|
|
603
|
+
`rename-high` →
|
|
604
|
+
`src/extensions/providers/claude/conformance/cases/`. 11
|
|
605
|
+
fixture files (`minimal-claude/`, `orphan-{before,after}/`,
|
|
606
|
+
`rename-high-{before,after}/`) moved alongside.
|
|
607
|
+
- New `coverage.md` per-Provider listing the 5 frontmatter
|
|
608
|
+
schemas (skill, agent, command, hook, note) and their cases.
|
|
609
|
+
- New verb `sm conformance run [--scope spec|provider:<id>|all]`.
|
|
610
|
+
Discovery by convention at `<plugin-dir>/conformance/`. The
|
|
611
|
+
existing runner gains optional `fixturesRoot` (default
|
|
612
|
+
`<specRoot>/conformance/fixtures` for compat); tooling using
|
|
613
|
+
the public API of `@skill-map/cli/conformance` keeps working.
|
|
614
|
+
`--json` deferred — reporter shape not yet frozen.
|
|
615
|
+
- Spec keeps only the kernel-agnostic case (`kernel-empty-boot`)
|
|
616
|
+
and the universal preamble fixture. Coverage matrix downgrades
|
|
617
|
+
conservatively (rows that depended on `basic-scan` are now
|
|
618
|
+
partial or missing, with cross-link to the Provider's matrix).
|
|
619
|
+
|
|
620
|
+
ROADMAP cleanup:
|
|
621
|
+
|
|
622
|
+
- The three "Status: target state for v0.8.0 — spec catch-up
|
|
623
|
+
pending" banners on §Plugin system / §Frontmatter standard /
|
|
624
|
+
§Enrichment are removed; prose shifts from future to present
|
|
625
|
+
("kinds from v0.7.0 are renamed" → "were renamed in spec
|
|
626
|
+
0.8.0"; Model B enrichment now describes the shipped
|
|
627
|
+
`node_enrichments` table with `body_hash_at_enrichment` rather
|
|
628
|
+
than "table or column set decided in PR").
|
|
629
|
+
- Decision-log entry for the working session rewritten to
|
|
630
|
+
reflect "shipped" rather than "pending".
|
|
631
|
+
- Last-updated header gains an "implementation" paragraph
|
|
632
|
+
listing the four prior phase commits.
|
|
633
|
+
|
|
634
|
+
593/593 cli + 32/32 testkit pass (+2 vs Phase 4 baseline).
|
|
635
|
+
spec:check green (40 files hashed — down from 53 because the
|
|
636
|
+
Claude-specific cases and fixtures left the spec's hash set).
|
|
637
|
+
|
|
638
|
+
### Breaking changes for plugin authors (Part 2)
|
|
639
|
+
|
|
640
|
+
Manifest renames:
|
|
641
|
+
|
|
642
|
+
- `kind: 'adapter'` → `kind: 'provider'`
|
|
643
|
+
- `kind: 'detector'` → `kind: 'extractor'`
|
|
644
|
+
- `kind: 'renderer'` → `kind: 'formatter'`
|
|
645
|
+
- `kind: 'audit'` removed (migrate to `kind: 'rule'`).
|
|
646
|
+
|
|
647
|
+
Method signatures:
|
|
648
|
+
|
|
649
|
+
- Detector `detect(ctx) → Link[]` → Extractor `extract(ctx) →
|
|
650
|
+
void` (output via `ctx.emitLink` / `ctx.enrichNode` /
|
|
651
|
+
`ctx.store`).
|
|
652
|
+
- Renderer `render(ctx) → string` → Formatter `format(ctx) →
|
|
653
|
+
string`.
|
|
654
|
+
|
|
655
|
+
Manifest fields:
|
|
656
|
+
|
|
657
|
+
- Provider gains required `explorationDir`.
|
|
658
|
+
- Provider's flat `defaultRefreshAction` map replaced by per-kind
|
|
659
|
+
entries inside `kinds[<kind>].defaultRefreshAction` (must
|
|
660
|
+
follow qualified pattern `<plugin-id>/<ext-id>`).
|
|
661
|
+
- Provider's `emits` derives from `Object.keys(kinds)` (the
|
|
662
|
+
manifest field is gone).
|
|
663
|
+
- Provider's per-kind schemas declared via `kinds[<kind>].schema`
|
|
664
|
+
(path relative to provider dir).
|
|
665
|
+
- Renderer's `format` field renamed to `formatId` on the
|
|
666
|
+
Formatter manifest (TS clash resolution).
|
|
667
|
+
- New plugin kind `hook` with `triggers[]` + optional `filter`.
|
|
668
|
+
- Optional `outputSchema` (`storage.schema` / `storage.schemas`)
|
|
669
|
+
for Mode A / Mode B plugin custom storage.
|
|
670
|
+
- Optional `applicableKinds` filter on Extractor manifest.
|
|
671
|
+
|
|
672
|
+
Extension ids:
|
|
673
|
+
|
|
674
|
+
- All extension ids must be qualified
|
|
675
|
+
`<plugin-id>/<extension-id>` (built-ins classified into
|
|
676
|
+
`claude/*` and `core/*`).
|
|
677
|
+
|
|
678
|
+
DB schema:
|
|
679
|
+
|
|
680
|
+
- Two new tables added in-place to `001_initial.sql` (pre-1.0
|
|
681
|
+
consolidation, no production DBs to migrate):
|
|
682
|
+
`scan_extractor_runs` and `node_enrichments`.
|
|
683
|
+
- Column rename `nodes.adapter` → `nodes.provider` (and parallel
|
|
684
|
+
in `result.adapters` → `result.providers`).
|
|
685
|
+
|
|
686
|
+
## Test stats
|
|
687
|
+
|
|
688
|
+
593/593 cli + 32/32 testkit pass (post-Phase 5).
|
|
689
|
+
Two new DB tables (`scan_extractor_runs`, `node_enrichments`)
|
|
690
|
+
added in-place to `001_initial.sql` (pre-1.0 consolidation, no
|
|
691
|
+
production DBs to migrate). The 5 per-kind frontmatter schemas
|
|
692
|
+
relocated from spec/ to the Claude Provider package.
|
|
693
|
+
|
|
694
|
+
## [Unreleased]
|
|
695
|
+
|
|
696
|
+
### Minor Changes
|
|
697
|
+
|
|
698
|
+
- 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).
|
|
699
|
+
|
|
700
|
+
- `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.
|
|
701
|
+
|
|
702
|
+
- 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.
|
|
703
|
+
|
|
704
|
+
- 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.
|
|
705
|
+
|
|
706
|
+
- 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.
|
|
707
|
+
|
|
708
|
+
- 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.
|
|
709
|
+
|
|
710
|
+
- 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.
|
|
711
|
+
|
|
712
|
+
- 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.
|
|
713
|
+
|
|
714
|
+
- Plugin kind `'detector'` renamed to `'extractor'`. Method signature
|
|
715
|
+
changes from `detect(ctx) → Link[]` to `extract(ctx) → void` — output
|
|
716
|
+
flows through three new ctx callbacks: `emitLink(link)` (kernel `links`
|
|
717
|
+
table), `enrichNode(partial)` (kernel enrichment layer, persisted into
|
|
718
|
+
`node_enrichments` per A.8), and the existing `ctx.store` (plugin's
|
|
719
|
+
own table). The Extractor absorbs what would have been a separate
|
|
720
|
+
`Enricher` kind via `enrichNode`. Built-ins migrated:
|
|
721
|
+
`claude/frontmatter`, `claude/slash`, `claude/at-directive`,
|
|
722
|
+
`core/external-url-counter` — all use `emitLink` to maintain
|
|
723
|
+
functional parity with their Detector ancestors. Schema files
|
|
724
|
+
renamed: `schemas/extensions/detector.schema.json` →
|
|
725
|
+
`schemas/extensions/extractor.schema.json`. Persisted DB rows are
|
|
726
|
+
unaffected (link `sources` carry extractor ids verbatim — the field
|
|
727
|
+
was always free-form). Pre-1.0 minor per `versioning.md` § Pre-1.0;
|
|
728
|
+
breaking for any plugin or test referencing `'detector'` as the
|
|
729
|
+
kind, `IDetector`, or the old `Link[]` return signature — no real
|
|
730
|
+
ecosystem affected today.
|
|
731
|
+
|
|
732
|
+
- Plugin kind `'audit'` removed. The single built-in `'validate-all'`
|
|
733
|
+
migrated to a Rule (qualified id `'core/validate-all'`, method
|
|
734
|
+
`evaluate(ctx) → Issue[]`). The kind had dual personality (composer +
|
|
735
|
+
standalone reporter); the standalone reporter case is naturally a Rule,
|
|
736
|
+
and the composer case is deferred to post-1.0 if a real use case
|
|
737
|
+
appears. CLI verbs `'sm audit run'` and `'sm audit show'` removed;
|
|
738
|
+
users invoke the rule via `sm check --rules core/validate-all`.
|
|
739
|
+
`state_executions.kind` enum narrowed to `['action']` (audit was the
|
|
740
|
+
only other value); the column is preserved as a forward-compatibility
|
|
741
|
+
lever. Schema files removed: `schemas/extensions/audit.schema.json`.
|
|
742
|
+
Coverage matrix shrinks from 29 to 28 rows. Pre-1.0 minor per
|
|
743
|
+
`versioning.md` § Pre-1.0; breaking for any plugin or test referencing
|
|
744
|
+
the audit kind, `IAudit`, `TAuditReport`, or `sm audit` verbs — no
|
|
745
|
+
real ecosystem affected today.
|
|
746
|
+
|
|
747
|
+
- Plugin kind `'adapter'` renamed to `'provider'`. Manifest gains required
|
|
748
|
+
field `'explorationDir'` (filesystem directory where the Provider's
|
|
749
|
+
content lives, e.g. `'~/.claude'` for the Claude Provider). Built-in
|
|
750
|
+
`claudeAdapter` renamed to `claudeProvider`. The hexagonal-architecture
|
|
751
|
+
`'adapter'` (`RunnerPort.adapter`, `StoragePort.adapter`,
|
|
752
|
+
`FilesystemPort.adapter`, `PluginLoaderPort.adapter`) is unchanged —
|
|
753
|
+
distinct concept, distinct namespace.
|
|
754
|
+
Persisted schema fields renamed: `node.adapter` → `node.provider`,
|
|
755
|
+
`scan-result.adapters` → `scan-result.providers` (pre-1.0 minor — no
|
|
756
|
+
production DBs to migrate; `001_initial.sql` was edited in place per
|
|
757
|
+
the consolidation precedent already established for pre-1.0).
|
|
758
|
+
Project config field renamed: `project-config.adapters` →
|
|
759
|
+
`project-config.providers`. Schema files renamed:
|
|
760
|
+
`schemas/extensions/adapter.schema.json` →
|
|
761
|
+
`schemas/extensions/provider.schema.json`. Pre-1.0 minor per
|
|
762
|
+
`versioning.md` § Pre-1.0; breaking for any plugin or test referencing
|
|
763
|
+
`'adapter'` as the kind, `IAdapter`, or any persisted/config schema
|
|
764
|
+
field renamed above — no real ecosystem affected today.
|
|
765
|
+
|
|
3
766
|
## 0.7.1
|
|
4
767
|
|
|
5
768
|
### Patch Changes
|
|
@@ -279,9 +1042,9 @@ kind, normalizedTrigger)` and prints one row per group with the
|
|
|
279
1042
|
(`Links out (12, 9 unique)`). When N > 1 detector emits the same
|
|
280
1043
|
logical link, the row also gets a `(×N)` suffix.
|
|
281
1044
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
1045
|
+
`--json` output is byte-identical to before — raw rows, no merge.
|
|
1046
|
+
Storage is byte-identical to before. The grouping is purely a
|
|
1047
|
+
read-time presentation choice for human eyes.
|
|
285
1048
|
|
|
286
1049
|
**Spec changes (patch)**:
|
|
287
1050
|
|
|
@@ -963,9 +1726,20 @@ Tag convention: `spec-vX.Y.Z` (distinct from CLI tags `cli-vX.Y.Z`).
|
|
|
963
1726
|
|
|
964
1727
|
Initial public spec bootstrap (Step 0a phases 1–3).
|
|
965
1728
|
|
|
1729
|
+
### Added
|
|
1730
|
+
|
|
1731
|
+
- `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).
|
|
1732
|
+
- `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.
|
|
1733
|
+
- `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).
|
|
1734
|
+
- 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`).
|
|
1735
|
+
|
|
966
1736
|
### Changed
|
|
967
1737
|
|
|
968
1738
|
- `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`.
|
|
1739
|
+
- `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.
|
|
1740
|
+
- 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.
|
|
1741
|
+
- 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.
|
|
1742
|
+
- `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.
|
|
969
1743
|
- `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.
|
|
970
1744
|
- `versioning.md` and related prose: replace ambiguous milestone terminology with explicit versioned release language.
|
|
971
1745
|
|