@skill-map/spec 0.19.0 → 0.20.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +347 -6
  2. package/README.md +6 -6
  3. package/architecture.md +62 -55
  4. package/cli-contract.md +35 -14
  5. package/conformance/README.md +2 -2
  6. package/conformance/cases/kernel-empty-boot.json +2 -2
  7. package/conformance/cases/sidecar-end-to-end.json +3 -3
  8. package/conformance/coverage.md +5 -5
  9. package/conformance/fixtures/sidecar-end-to-end/.claude/agents/orphan.sm +1 -1
  10. package/conformance/fixtures/sidecar-example/agent-example.md +1 -1
  11. package/db-schema.md +18 -17
  12. package/index.json +36 -36
  13. package/interfaces/security-scanner.md +2 -2
  14. package/job-events.md +12 -12
  15. package/job-lifecycle.md +1 -1
  16. package/package.json +1 -1
  17. package/plugin-author-guide.md +112 -81
  18. package/plugin-kv-api.md +5 -5
  19. package/prompt-preamble.md +1 -1
  20. package/schemas/annotations.schema.json +4 -4
  21. package/schemas/api/rest-envelope.schema.json +4 -4
  22. package/schemas/conformance-case.schema.json +2 -2
  23. package/schemas/extensions/analyzer.schema.json +43 -0
  24. package/schemas/extensions/base.schema.json +5 -5
  25. package/schemas/extensions/extractor.schema.json +1 -1
  26. package/schemas/extensions/hook.schema.json +6 -4
  27. package/schemas/issue.schema.json +6 -6
  28. package/schemas/link.schema.json +2 -2
  29. package/schemas/plugins-registry.schema.json +1 -1
  30. package/schemas/project-config.schema.json +15 -1
  31. package/schemas/sidecar.schema.json +2 -2
  32. package/schemas/summaries/agent.schema.json +1 -1
  33. package/schemas/summaries/command.schema.json +1 -1
  34. package/schemas/summaries/hook.schema.json +1 -1
  35. package/schemas/{view-contracts.schema.json → view-slots.schema.json} +91 -54
  36. package/schemas/extensions/rule.schema.json +0 -43
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "$schema": "https://skill-map.dev/spec/v0/conformance-case.schema.json",
3
3
  "id": "kernel-empty-boot",
4
- "description": "With every Provider, extractor, and rule disabled, scanning an empty scope MUST return a valid, zero-filled ScanResult. Enforces the kernel boot invariant from architecture.md.",
4
+ "description": "With every Provider, extractor, and analyzer disabled, scanning an empty scope MUST return a valid, zero-filled ScanResult. Enforces the kernel boot invariant from architecture.md.",
5
5
  "setup": {
6
6
  "disableAllProviders": true,
7
7
  "disableAllExtractors": true,
8
- "disableAllRules": true
8
+ "disableAllAnalyzers": true
9
9
  },
10
10
  "invoke": {
11
11
  "verb": "scan",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://skill-map.dev/spec/v0/conformance-case.schema.json",
3
3
  "id": "sidecar-end-to-end",
4
- "description": "Step 9.6.6 — co-located `.sm` sidecar end-to-end. Scanning a fixture that carries a stale sidecar (wrong `identity.{bodyHash,frontmatterHash}`) plus an orphan sidecar (no sibling `.md`) MUST surface `sidecar.status` on the node and emit both `annotation-stale` (per stale node) and `annotation-orphan` (per orphan `.sm`) issues from the built-in core rules.",
4
+ "description": "Step 9.6.6 — co-located `.sm` sidecar end-to-end. Scanning a fixture that carries a stale sidecar (wrong `identity.{bodyHash,frontmatterHash}`) plus an orphan sidecar (no sibling `.md`) MUST surface `sidecar.status` on the node and emit both `annotation-stale` (per stale node) and `annotation-orphan` (per orphan `.sm`) issues from the built-in core analyzers.",
5
5
  "fixture": "sidecar-end-to-end",
6
6
  "invoke": {
7
7
  "verb": "scan",
@@ -16,9 +16,9 @@
16
16
  { "type": "json-path", "path": "$.nodes[0].sidecar.status", "matches": "^stale-(body|frontmatter|both)$" },
17
17
  { "type": "json-path", "path": "$.nodes[0].sidecar.annotations.version", "equals": 7 },
18
18
  { "type": "json-path", "path": "$.stats.issuesCount", "equals": 2 },
19
- { "type": "json-path", "path": "$.issues[0].ruleId", "equals": "annotation-stale" },
19
+ { "type": "json-path", "path": "$.issues[0].analyzerId", "equals": "annotation-stale" },
20
20
  { "type": "json-path", "path": "$.issues[0].severity", "equals": "warn" },
21
- { "type": "json-path", "path": "$.issues[1].ruleId", "equals": "annotation-orphan" },
21
+ { "type": "json-path", "path": "$.issues[1].analyzerId", "equals": "annotation-orphan" },
22
22
  { "type": "json-path", "path": "$.issues[1].severity", "equals": "warn" },
23
23
  { "type": "json-path", "path": "$.issues[1].data.expectedMdPath", "equals": ".claude/agents/orphan.md" }
24
24
  ]
@@ -1,6 +1,6 @@
1
1
  # Conformance coverage
2
2
 
3
- Authoritative map of JSON Schemas in [`../schemas/`](../schemas/) to the conformance cases that exercise them. Every schema MUST have at least one case before spec v1.0.0 ships — missing case → missing release ([`../../context/spec.md`](../../context/spec.md) §Rules for AI agents editing spec/).
3
+ Authoritative map of JSON Schemas in [`../schemas/`](../schemas/) to the conformance cases that exercise them. Every schema MUST have at least one case before spec v1.0.0 ships — missing case → missing release ([`../../context/spec.md`](../../context/spec.md) §Analyzers for AI agents editing spec/).
4
4
 
5
5
  This file is hand-maintained. A CI check before spec release compares the schema inventory against this table and fails if any schema lacks a case.
6
6
 
@@ -27,17 +27,17 @@ This file is hand-maintained. A CI check before spec release compares the schema
27
27
  | 17 | `extensions/base.schema.json` | — | 🔴 missing | Meta-case: every manifest under `src/extensions/` validates against the appropriate kind schema (which extends base via `allOf`). |
28
28
  | 18 | `extensions/provider.schema.json` | `plugin-missing-ui-rejected` | 🟡 partial | A drop-in Provider whose `kinds[*]` entry omits the required `ui` block fails AJV validation with `invalid-manifest` while the rest of the pipeline keeps running (built-in Claude Provider, exit 0). The complementary positive case (canonical Claude Provider manifest validates) lives in `provider:claude` conformance. Direct cases for missing `kinds` / `explorationDir` rejection still pending. |
29
29
  | 19 | `extensions/extractor.schema.json` | — | 🔴 missing | Case: `frontmatter` + `slash` + `at-directive` extractor manifests validate; an extractor emitting a disallowed `emitsLinkKinds` value fails. |
30
- | 20 | `extensions/rule.schema.json` | — | 🔴 missing | Case: `trigger-collision`, `broken-ref`, `superseded` manifests validate. |
30
+ | 20 | `extensions/analyzer.schema.json` | — | 🔴 missing | Case: `trigger-collision`, `broken-ref`, `superseded` manifests validate. |
31
31
  | 21 | `extensions/action.schema.json` | — | 🔴 missing | Case: a `deterministic` action manifest validates; a `probabilistic` action WITHOUT `promptTemplateRef` fails. |
32
32
  | 22 | `extensions/formatter.schema.json` | — | 🔴 missing | Case: `ascii` formatter manifest validates. |
33
33
  | 23 | `history-stats.schema.json` | — | 🔴 missing | Blocked by Step 5 (history). Case: seed `state_executions` with a deterministic fixture, run `sm history stats --json --since <T0> --until <T1> --period month --top 5`, assert the document validates and that `totals.executionsCount == sum(perAction.executionsCount)` and `errorRates.global == totals.failedCount / totals.executionsCount`. Percentiles (`p95`/`p99`) intentionally omitted in v1 — add later as a minor bump without breaking consumers. |
34
34
  | 24 | `extensions/hook.schema.json` | — | 🔴 missing | Case: a `deterministic` hook manifest with `triggers: ['scan.completed']` validates; a hook declaring an unknown trigger (e.g. `scan.progress`) fails with `invalid-manifest` at load time. |
35
35
  | 25 | `api/rest-envelope.schema.json` | — | 🔴 missing | Step 14.2 BFF list-envelope shape (`{ schemaVersion, kind, items \| item \| value, filters, counts }`). Case: hit `GET /api/nodes` against a primed scope, validate the response against the schema; assert the `oneOf` rejects an envelope that carries both `items` and `item`. Implementation-side coverage exists today (`src/test/server-endpoints.test.ts`) but a kernel-agnostic conformance case is required before v1.0.0 ships. |
36
- | 26 | `sidecar.schema.json` | `sidecar-end-to-end` | 🟢 covered | Co-located YAML sidecar (`<basename>.sm`) root shape: reserved blocks `for` / `annotations` / `settings` / `audit` plus opt-in plugin namespacing. Step 9.6.2 (2026-05-05) shipped the kernel reader; Step 9.6.3 (2026-05-05) formalised the `audit:` sub-shape populated by the built-in `bump` Action; Step 9.6.6 (2026-05-06) flips this row 🟢 with the end-to-end `sidecar-end-to-end` case (fixture `sidecar-end-to-end/`): a scan over a stale-`.sm` + orphan-`.sm` corpus produces a populated `Node.sidecar` overlay with `present: true` and `status: stale-*`, denormalises `annotations.version` into the node row, and emits both `annotation-stale` and `annotation-orphan` issues from the built-in core rules. Structural sample (untouched) at `fixtures/sidecar-example/agent-example.sm`. |
37
- | 27 | `annotations.schema.json` | `sidecar-end-to-end` | 🟢 covered | Curated catalog of 15 conventional skill-map annotation fields (versioning, supersession, provenance, lifecycle, taxonomy, display, docs). `additionalProperties: true` so users / plugins extend without coordination; the `unknown-field` Tier-1 rule shipped in Step 9.6.6 emits warnings on truly unrecognized keys. Step 9.6.2 (2026-05-05) wired the kernel reader; Step 9.6.6 (2026-05-06) flips this row 🟢 via `sidecar-end-to-end`, which asserts that an `annotations.version: 7` value round-trips through `state_scan_nodes.annotations_json` and surfaces in the node's `sidecar.annotations` overlay AND in the denormalised `Node.version` column. Structural sample at `fixtures/sidecar-example/agent-example.sm`. Catalog trimmed from 31 to 15 fields on 2026-05-07 after UX review. |
36
+ | 26 | `sidecar.schema.json` | `sidecar-end-to-end` | 🟢 covered | Co-located YAML sidecar (`<basename>.sm`) root shape: reserved blocks `for` / `annotations` / `settings` / `audit` plus opt-in plugin namespacing. Step 9.6.2 (2026-05-05) shipped the kernel reader; Step 9.6.3 (2026-05-05) formalised the `audit:` sub-shape populated by the built-in `bump` Action; Step 9.6.6 (2026-05-06) flips this row 🟢 with the end-to-end `sidecar-end-to-end` case (fixture `sidecar-end-to-end/`): a scan over a stale-`.sm` + orphan-`.sm` corpus produces a populated `Node.sidecar` overlay with `present: true` and `status: stale-*`, denormalises `annotations.version` into the node row, and emits both `annotation-stale` and `annotation-orphan` issues from the built-in core analyzers. Structural sample (untouched) at `fixtures/sidecar-example/agent-example.sm`. |
37
+ | 27 | `annotations.schema.json` | `sidecar-end-to-end` | 🟢 covered | Curated catalog of 15 conventional skill-map annotation fields (versioning, supersession, provenance, lifecycle, taxonomy, display, docs). `additionalProperties: true` so users / plugins extend without coordination; the `unknown-field` Tier-1 analyzer shipped in Step 9.6.6 emits warnings on truly unrecognized keys. Step 9.6.2 (2026-05-05) wired the kernel reader; Step 9.6.6 (2026-05-06) flips this row 🟢 via `sidecar-end-to-end`, which asserts that an `annotations.version: 7` value round-trips through `state_scan_nodes.annotations_json` and surfaces in the node's `sidecar.annotations` overlay AND in the denormalised `Node.version` column. Structural sample at `fixtures/sidecar-example/agent-example.sm`. Catalog trimmed from 31 to 15 fields on 2026-05-07 after UX review. |
38
38
  | 28 | `bump-report.schema.json` | — | 🔴 missing | Report shape produced by the built-in deterministic `bump` Action (Step 9.6.3, Decision #125). Extends `report-base-deterministic.schema.json` (row 29) — the deterministic counterpart to `report-base.schema.json` (which is LLM-specific via `confidence` + `safety`). Three concrete shapes: success-with-write, silent-no-op (under `force`), and refusal (`fresh`). Direct conformance case lands together with the `sm bump` CLI verb in Step 9.6.4 — it'll exercise all three branches via `sm bump --json` against a primed fixture. Implementation tests at `src/test/bump-action.test.ts` cover the runtime behaviour today. |
39
39
  | 29 | `report-base-deterministic.schema.json` | — (indirect via row 28) | 🟡 partial | Deterministic counterpart to `report-base.schema.json`; every deterministic Action's report extends this base. Direct contract case still pending — landed when first conformance case directly validates a deterministic report against this schema. |
40
- | 30 | `view-contracts.schema.json` | — | 🔴 missing | Closed catalog of 10 view contracts + the `IViewContribution` manifest declaration shape + per-contract payload schemas. Cases required (3): (a) `plugin-view-contributions-valid` — a plugin manifest declaring contributions of every contract type validates; (b) `plugin-view-contributions-invalid-contract` — a manifest referencing a contract not in the catalog rejects with `invalid-manifest`; (c) `plugin-view-contributions-payload-mismatch` — an extractor emitting an off-contract payload triggers `extension.error` and drops silently. Implementation lands with the kernel surface in Phase 2 of the UI contributions plan; conformance fixtures land alongside. |
40
+ | 30 | `view-slots.schema.json` | — | 🔴 missing | Closed catalog of 15 view slots + the `IViewContribution` manifest declaration shape + per-slot payload schemas. Cases required (3): (a) `plugin-view-contributions-valid` — a plugin manifest declaring contributions of every slot validates; (b) `plugin-view-contributions-invalid-slot` — a manifest referencing a slot not in the catalog rejects with `invalid-manifest`; (c) `plugin-view-contributions-payload-mismatch` — an extractor emitting an off-shape payload triggers `extension.error` and drops silently. Implementation lands with the kernel surface in Phase 2 of the UI contributions plan; conformance fixtures land alongside. |
41
41
  | 31 | `input-types.schema.json` | — | 🔴 missing | Closed catalog of 10 input-types for plugin settings + the `ISettingDeclaration` discriminated-union manifest shape. Cases required (2): (a) `plugin-settings-valid` — a plugin manifest declaring at least one setting of each input-type validates; (b) `plugin-settings-invalid-type` — a manifest referencing a `type` not in the catalog rejects with `invalid-manifest`. Lands together with the spec/CLI surface for `sm plugins config <id>`. |
42
42
 
43
43
  > **Note on Provider-owned schemas.** Per-kind frontmatter schemas (`skill`, `agent`, `command`, `note` for the built-in Claude Provider; other Providers MAY declare different kinds) live with the Provider that emits them — for the built-in Claude Provider, under `src/extensions/providers/claude/schemas/`. Those schemas are NOT counted in the spec's coverage matrix above; they belong to the Provider's own conformance suite at `src/extensions/providers/claude/conformance/coverage.md`. The same split applies to the cases that exercise Provider-specific kinds (`basic-scan`, `rename-high`, `orphan-detection`) — they live in the Provider's `cases/` directory.
@@ -1,6 +1,6 @@
1
1
  # Orphan sidecar — no sibling `agents/orphan.md` exists. The kernel walker
2
2
  # discovers this via `discoverOrphanSidecars` and the built-in
3
- # `core/annotation-orphan` rule emits one `warn` issue per orphan.
3
+ # `core/annotation-orphan` analyzer emits one `warn` issue per orphan.
4
4
 
5
5
  identity:
6
6
  path: agents/orphan.md
@@ -10,7 +10,7 @@ tools:
10
10
 
11
11
  # Code reviewer
12
12
 
13
- Walks the diff, flags type holes, suggests idiomatic refactors. Pairs with the local lint suite — never duplicates rules a linter already enforces.
13
+ Walks the diff, flags type holes, suggests idiomatic refactors. Pairs with the local lint suite — never duplicates analyzers a linter already enforces.
14
14
 
15
15
  ## When to invoke
16
16
 
package/db-schema.md CHANGED
@@ -41,7 +41,7 @@ Every kernel table belongs to exactly one zone, identified by a mandatory name p
41
41
 
42
42
  ## Naming conventions (normative)
43
43
 
44
- These rules apply to every kernel table and to every plugin-authored table under its prefix.
44
+ These analyzers apply to every kernel table and to every plugin-authored table under its prefix.
45
45
 
46
46
  - **Tables**: `snake_case`, plural. Zone prefix REQUIRED. Example: `scan_nodes`, `state_jobs`.
47
47
  - **Columns**: `snake_case`. Primary key column is always `id`.
@@ -57,7 +57,7 @@ These rules apply to every kernel table and to every plugin-authored table under
57
57
  - **Constraints**: `fk_`, `uq_`, `ck_` prefixes.
58
58
  - **SQL keywords**: UPPERCASE. Identifiers lowercase.
59
59
 
60
- The kernel MUST reject any plugin migration that violates these rules at validation time (see `plugin-kv-api.md`).
60
+ The kernel MUST reject any plugin migration that violates these analyzers at validation time (see `plugin-kv-api.md`).
61
61
 
62
62
  Domain types exposed to driving adapters use `camelCase`. The SQLite reference impl uses Kysely's `CamelCasePlugin` to bridge `snake_case ↔ camelCase` at the port boundary.
63
63
 
@@ -117,12 +117,12 @@ Indexes: `ix_scan_links_source_path`, `ix_scan_links_target_path`, `ix_scan_link
117
117
 
118
118
  ### `scan_issues`
119
119
 
120
- One row per rule-emitted issue, matching [`schemas/issue.schema.json`](./schemas/issue.schema.json).
120
+ One row per analyzer-emitted issue, matching [`schemas/issue.schema.json`](./schemas/issue.schema.json).
121
121
 
122
122
  | Column | Type | Constraint | Notes |
123
123
  |---|---|---|---|
124
124
  | `id` | INTEGER | PRIMARY KEY AUTOINCREMENT | |
125
- | `rule_id` | TEXT | NOT NULL | |
125
+ | `analyzer_id` | TEXT | NOT NULL | |
126
126
  | `severity` | TEXT | NOT NULL, CHECK in (`error`, `warn`, `info`) | |
127
127
  | `node_ids_json` | TEXT | NOT NULL | JSON array. |
128
128
  | `link_indices_json` | TEXT | NULL | JSON array of `scan_links.id`. |
@@ -131,7 +131,7 @@ One row per rule-emitted issue, matching [`schemas/issue.schema.json`](./schemas
131
131
  | `fix_json` | TEXT | NULL | |
132
132
  | `data_json` | TEXT | NULL | |
133
133
 
134
- Indexes: `ix_scan_issues_rule_id`, `ix_scan_issues_severity`.
134
+ Indexes: `ix_scan_issues_analyzer_id`, `ix_scan_issues_severity`.
135
135
 
136
136
  ### `scan_meta`
137
137
 
@@ -197,7 +197,7 @@ Primary key: `(node_path, extractor_id)`. Indexes: `ix_node_enrichments_node`, `
197
197
  3. **Upsert** — for every `(node_path, extractor_id)` pair the orchestrator emitted in this scan, upsert with `stale = 0`, `is_probabilistic = 0`, and the current `body_hash`. The PRIMARY KEY conflict refreshes `body_hash_at_enrichment` / `value_json` / `enriched_at` on every re-run.
198
198
  4. **Stale flagging** — no-op in this revision (Extractors are deterministic-only; the sweep finds nothing to flag). The step is preserved in the persistence flow so the future Action-prob revision slots in without reshaping the contract.
199
199
 
200
- **Read-side `node.merged` view.** Rules / `sm check` / `sm export` consume `node.frontmatter` directly (deterministic CI-safe baseline). UI / future opt-in consumers call `mergeNodeWithEnrichments(node, enrichments)` which:
200
+ **Read-side `node.merged` view.** Analyzers / `sm check` / `sm export` consume `node.frontmatter` directly (deterministic CI-safe baseline). UI / future opt-in consumers call `mergeNodeWithEnrichments(node, enrichments)` which:
201
201
 
202
202
  1. Filters `enrichments` to rows targeting this node AND not flagged stale.
203
203
  2. Sorts by `enriched_at` ASC.
@@ -212,7 +212,7 @@ Stale row visibility is opt-in via `mergeNodeWithEnrichments(node, enrichments,
212
212
 
213
213
  ### `scan_contributions`
214
214
 
215
- Phase 3 / View contribution system. Per-node typed payloads emitted by extractors via `ctx.emitContribution(id, payload)` (and rules via `ctx.emitScopeContribution(id, payload)` for scope-level contracts). One row per `(plugin_id, extension_id, node_path, contribution_id)` tuple.
215
+ Phase 3 / View contribution system. Per-node typed payloads emitted by extractors via `ctx.emitContribution(id, payload)` (and analyzers via `ctx.emitScopeContribution(id, payload)` for scope-level slots). One row per `(plugin_id, extension_id, node_path, contribution_id)` tuple.
216
216
 
217
217
  | Column | Type | Constraint |
218
218
  |---|---|---|
@@ -220,21 +220,22 @@ Phase 3 / View contribution system. Per-node typed payloads emitted by extractor
220
220
  | `extension_id` | TEXT | NOT NULL | Extension id within the plugin. |
221
221
  | `node_path` | TEXT | NOT NULL | FK semantically to `scan_nodes.path`; orphan-swept on persist when the parent node disappears. |
222
222
  | `contribution_id` | TEXT | NOT NULL | Manifest Record key under `extension.viewContributions[<contributionId>]`. |
223
- | `contract` | TEXT | NOT NULL | Closed-enum-by-spec contract name; mirror of `view-contracts.schema.json#/$defs/ContractName`. Kept open at the SQL layer (no CHECK) so catalog evolution does not need a DDL migration; `sm plugins upgrade` handles renames at the manifest layer. |
224
- | `payload_json` | TEXT | NOT NULL | JSON-serialised payload, already validated against the contract's payload schema (`view-contracts.schema.json#/$defs/payloads/<contract>`) at emit time. Off-contract payloads emit `extension.error` and drop silently. |
223
+ | `slot` | TEXT | NOT NULL | Closed-enum-by-spec slot name; mirror of `view-slots.schema.json#/$defs/SlotName`. Kept open at the SQL layer (no CHECK) so catalog evolution does not need a DDL migration; `sm plugins upgrade` handles renames at the manifest layer. |
224
+ | `payload_json` | TEXT | NOT NULL | JSON-serialised payload, already validated against the slot's payload schema (`view-slots.schema.json#/$defs/payloads/<slot>`) at emit time. Off-shape payloads emit `extension.error` and drop silently. |
225
225
  | `emitted_at` | INTEGER | NOT NULL | Unix milliseconds. |
226
226
 
227
227
  Primary key: `(plugin_id, extension_id, node_path, contribution_id)`. Indexes: `ix_scan_contributions_node_path` (inspector lazy-fetch + orphan sweep), `ix_scan_contributions_plugin_id` (catalog sweep + `purgeByPlugin`).
228
228
 
229
- **Persistence — orphan + catalog sweep + upsert (NOT pure replace-all).** The watcher's cached pass leaves the contributions buffer empty for cached nodes — the orchestrator skips `extract()` when the per-(node, extractor) cache hits, so no `emitContribution` fires. A naive wipe-all would silently drop the prior valid rows on every watcher boot. The persist runs three passes inside the same tx as the rest of the scan zone:
229
+ **Persistence — orphan + catalog + per-tuple sweep + upsert (NOT pure replace-all).** The watcher's cached pass leaves the contributions buffer empty for cached nodes — the orchestrator skips `extract()` when the per-(node, extractor) cache hits, so no `emitContribution` fires. A naive wipe-all would silently drop the prior valid rows on every watcher boot. The persist runs four passes inside the same tx as the rest of the scan zone:
230
230
 
231
231
  1. **Orphan sweep** — drops every row whose `node_path` is NOT in the current live node set (`livePaths` derived from `result.nodes`). Disappeared nodes lose their contributions automatically.
232
232
  2. **Catalog sweep** — drops every row whose qualified id `(pluginId, extensionId, contributionId)` is NOT in the registered runtime catalog (`registeredContributionKeys` collected via `collectRegisteredContributionKeys(composed)`). Uninstalled plugins, disabled bundles, and removed contributions lose their rows on the next scan.
233
- 3. **Upsert** — `INSERT ... ON CONFLICT DO UPDATE SET payload_json = excluded.payload_json` for every row in the buffer. PK conflict refreshes `payload_json` + `emitted_at`.
233
+ 3. **Per-tuple sweep** — for every `(pluginId, extensionId, node_path)` tuple in `freshlyRunTuples` (extension actually ran against that node this scan: extractor cache miss, OR analyzer), drop any row carrying that triple whose `contribution_id` is NOT refreshed by the buffer. Catches the "extractor used to emit, now does not" case without touching cached-extractor rows. Tuple format: `<pluginId>/<extensionId>/<nodePath>`.
234
+ 4. **Upsert** — `INSERT ... ON CONFLICT DO UPDATE SET payload_json = excluded.payload_json, slot = excluded.slot` for every row in the buffer. PK conflict refreshes `payload_json` + `slot` + `emitted_at`.
234
235
 
235
- Cached nodes' rows survive untouched — they're neither orphaned (still in the live set) nor uninstalled (still in the catalog) nor in the buffer (no re-emit). The next time the body changes, the orchestrator re-runs the extractor, fresh contributions land in the buffer, and the upsert refreshes them.
236
+ Cached nodes' rows survive untouched — they're neither orphaned (still in the live set) nor uninstalled (still in the catalog) nor in `freshlyRunTuples` (extractor short-circuited via the per-(node, extractor) cache) nor in the buffer (no re-emit). The next time the body changes, the orchestrator re-runs the extractor, the tuple lands in the freshly-run set, and either the upsert refreshes the row or the per-tuple sweep drops it.
236
237
 
237
- **Backwards-compat fallbacks.** `IPersistOptions.livePaths`, `IPersistOptions.registeredContributionKeys` are both optional. Absent / empty `livePaths` falls back to wipe-all (legacy behaviour). Absent / empty `registeredContributionKeys` skips the catalog sweep (rows for disabled plugins linger until next purge).
238
+ **Backwards-compat fallbacks.** `IPersistOptions.livePaths`, `IPersistOptions.registeredContributionKeys`, `IPersistOptions.freshlyRunTuples` are all optional. Absent / empty `livePaths` falls back to wipe-all (legacy behaviour). Absent / empty `registeredContributionKeys` skips the catalog sweep (rows for disabled plugins linger until next purge). Absent / empty `freshlyRunTuples` skips the per-tuple sweep (rows that should have been dropped because an extractor stopped emitting linger until the node body, the extractor registration, or the node existence changes again — older callers preserve the pre-fix behaviour).
238
239
 
239
240
  NOT analogous to `state_plugin_kvs` (which is plugin-managed). Belongs to the `scan_*` family — sweep semantics replace pure replace-all but the data is still scan-derived.
240
241
 
@@ -521,12 +522,12 @@ Implementations MUST apply a rename heuristic at scan time **before** committing
521
522
  - Emit no issue. Log at `info` level.
522
523
  3. Remaining pairs where `newPath.frontmatterHash == deletedPath.frontmatterHash` (body differs, frontmatter is a perfect match) → classify as **medium-confidence rename**. The kernel MUST:
523
524
  - Apply the same FK migration.
524
- - Emit an issue with `ruleId: auto-rename-medium` (severity `warn`) pointing to both paths. The issue's `data` MUST include `{ from: <old.path>, to: <new.path>, confidence: "medium" }` so `sm orphans undo-rename <new.path>` can read the prior path without user input.
525
- 4. Any `deletedPath` left without a match after steps 2–3 becomes an **orphan**: the kernel emits an issue with `ruleId: orphan` (severity `info`) and keeps the `state_*` rows referencing the dead path untouched until the user runs `sm orphans reconcile <dead.path> --to <new.path>` or accepts the orphan.
525
+ - Emit an issue with `analyzerId: auto-rename-medium` (severity `warn`) pointing to both paths. The issue's `data` MUST include `{ from: <old.path>, to: <new.path>, confidence: "medium" }` so `sm orphans undo-rename <new.path>` can read the prior path without user input.
526
+ 4. Any `deletedPath` left without a match after steps 2–3 becomes an **orphan**: the kernel emits an issue with `analyzerId: orphan` (severity `info`) and keeps the `state_*` rows referencing the dead path untouched until the user runs `sm orphans reconcile <dead.path> --to <new.path>` or accepts the orphan.
526
527
 
527
528
  Matching is 1-to-1: once a `newPath` is claimed as the rename target of some `deletedPath`, no other deletion can match it in the same scan. Ambiguity (two deletions share a body hash with the same new path) → fall back to the orphan path for all candidates, with issue `auto-rename-ambiguous` listing every conflict. `auto-rename-ambiguous` issues MUST populate `data` with `{ to: <new.path>, candidates: [<old.path.a>, <old.path.b>, ...] }`; in this case `sm orphans undo-rename` requires the user to pass `--from <old.path>` to disambiguate.
528
529
 
529
- Note on casing: `bodyHash` / `frontmatterHash` / `ruleId` / `data` are the domain-object field names (per `node.schema.json` and `issue.schema.json`). The SQLite reference impl stores the same values in `body_hash` / `frontmatter_hash` / `rule_id` / `data_json` columns; the storage adapter bridges the two (see §Naming conventions above). The heuristic is specified against the domain types, not the columns.
530
+ Note on casing: `bodyHash` / `frontmatterHash` / `analyzerId` / `data` are the domain-object field names (per `node.schema.json` and `issue.schema.json`). The SQLite reference impl stores the same values in `body_hash` / `frontmatter_hash` / `analyzer_id` / `data_json` columns; the storage adapter bridges the two (see §Naming conventions above). The heuristic is specified against the domain types, not the columns.
530
531
 
531
532
  The heuristic runs inside the scan transaction, so either all renames land or none do. `sm scan` is the only surface that triggers automatic rename detection. Two manual verbs exist for cases the heuristic missed or got wrong:
532
533
 
@@ -554,7 +555,7 @@ Failures are reported with suggested remediation (e.g., "run `sm db migrate`", "
554
555
 
555
556
  ## See also
556
557
 
557
- - [`architecture.md`](./architecture.md) — `StoragePort` interface definition and dependency rules.
558
+ - [`architecture.md`](./architecture.md) — `StoragePort` interface definition and dependency analyzers.
558
559
  - [`plugin-kv-api.md`](./plugin-kv-api.md) — `ctx.store` accessor for mode A / mode B persistence.
559
560
  - [`job-lifecycle.md`](./job-lifecycle.md) — atomic claim and TTL/reap semantics that drive `state_jobs`.
560
561
  - [`cli-contract.md`](./cli-contract.md) — `sm db` verb surface (reset, backup, restore, migrate).
package/index.json CHANGED
@@ -71,8 +71,8 @@
71
71
  "path": "schemas/extensions/extractor.schema.json"
72
72
  },
73
73
  {
74
- "id": "extensions/rule",
75
- "path": "schemas/extensions/rule.schema.json"
74
+ "id": "extensions/analyzer",
75
+ "path": "schemas/extensions/analyzer.schema.json"
76
76
  },
77
77
  {
78
78
  "id": "extensions/action",
@@ -174,69 +174,69 @@
174
174
  }
175
175
  ]
176
176
  },
177
- "specPackageVersion": "0.19.0",
177
+ "specPackageVersion": "0.20.0",
178
178
  "integrity": {
179
179
  "algorithm": "sha256",
180
180
  "files": {
181
- "CHANGELOG.md": "0a4cbbfb66c1f6a68393564645708128daca1e0a6c494da3145cb81b6182834c",
182
- "README.md": "3ffd34a0d55c76d53777d47244c727e2262bf5d8bcde21035e9ec0154640cee7",
183
- "architecture.md": "2f159fad4c03266a766f9c03161ef96b61486d11af8edf727990320e756af3c2",
184
- "cli-contract.md": "75f6912d3aae5a30bee3f0e3bb5088967a88661a47ca4d83f1d60598af8d6c5d",
185
- "conformance/README.md": "5f94a6ac637b7c992fcd7e53d32eed1b8887eeef05eb6ca3b5ec8a0b5045cd21",
186
- "conformance/cases/kernel-empty-boot.json": "ad4bbe9d637537625025c8bdb61285b1433568a2544b1ce0248f304ccff8c350",
181
+ "CHANGELOG.md": "c1207870c14e59ad7a34dc709e19cb4dbfe4f9fa797b85614ba2466ef8a6dd95",
182
+ "README.md": "b551522ab0c7f5ef702e9ea4d4f67fd7ad838b080d85975c2834d8d40af14a00",
183
+ "architecture.md": "181f54e12cff7b2a86e6a741520391ed828799b5b59725028eb4947b819066a7",
184
+ "cli-contract.md": "af43179f3b363801fde1ddbbaede2185eaec6a7f42b89a748e98e505799663e2",
185
+ "conformance/README.md": "70e3101104765ef359d5322d0a7c9248d2157f78a510fb2cc8005b4eba3173d6",
186
+ "conformance/cases/kernel-empty-boot.json": "2a5be9c93143d07a16d998df09dcc8fa4ea2d2f9a0bff6417573ed5a770352c1",
187
187
  "conformance/cases/orphan-markdown-fallback.json": "8ef6e49b7e6532bd845d9f54974a16e537cf98d355f0c5e4f4fb06abac3adcc5",
188
188
  "conformance/cases/plugin-missing-ui-rejected.json": "bdebee810436e6be88edf2fe38ddc6939fd3f53e6a12dc1d66da051c4922f1e9",
189
- "conformance/cases/sidecar-end-to-end.json": "7caa3dbbdcb8d2ba1017cea37d557f345433977091c51b0f42308df6fab444d8",
190
- "conformance/coverage.md": "981cf3377b50aa5535d163b6445af27e0408c826fb870ee05896750603f6bf2f",
189
+ "conformance/cases/sidecar-end-to-end.json": "24a73e7c857709d001cf7013b8fe5ccad4027e064b39533dda33697d80b56e7a",
190
+ "conformance/coverage.md": "45208fd74c5b548962025307d489deb91eaeedc57c0b10ff7c941631851b6f07",
191
191
  "conformance/fixtures/orphan-markdown/.claude/agents/reviewer.md": "7f062731106f2d9811e4fffcf6ab44b8dfff4cfb16536a469514cc0664e832bf",
192
192
  "conformance/fixtures/orphan-markdown/ARCHITECTURE.md": "d6b6e18d4b963b26a292de73348c3396fd4710ab4c4bdd6cf094e581f99ec8d6",
193
193
  "conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/plugin.json": "4d78af6f12faa9d131e2a19f1dbb8f250baacc525978f3a8c858932b95da4ff6",
194
194
  "conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/provider.js": "d1f4898b43201d24f048171ce84d433b68694457452fbc64498857f5da3e9bbb",
195
195
  "conformance/fixtures/plugin-missing-ui/notes/example.md": "55767f0aa1b6774546a99f28c58e7b732aa9cfa5dfce8d0326470f7f622f577e",
196
196
  "conformance/fixtures/preamble-v1.txt": "1e0aeef224b64477bdc13a949c3ad402e68249caf499ecdba1302371677c068b",
197
- "conformance/fixtures/sidecar-end-to-end/.claude/agents/orphan.sm": "e68d3cecb39d2da5e0a5fca22568fc264c0b16c0a0f56e199c56deb984210ad9",
197
+ "conformance/fixtures/sidecar-end-to-end/.claude/agents/orphan.sm": "3102ff10a0f08f60c014f82409d45ad4faf2cefa04d652a87676d3557ad64944",
198
198
  "conformance/fixtures/sidecar-end-to-end/.claude/agents/stale.md": "cb3a95777cba530d47e6040c5601b6dcd34b5fc653dd69f183369eb6bdd956b5",
199
199
  "conformance/fixtures/sidecar-end-to-end/.claude/agents/stale.sm": "cb04f7f3103b4218b09fd4da92f7ea429588b04c1dac6a9547ce362263b11224",
200
- "conformance/fixtures/sidecar-example/agent-example.md": "1343ebc245e1fde415c16320ce53bcfad366ae2be1bcc7084337cfceaacdad38",
200
+ "conformance/fixtures/sidecar-example/agent-example.md": "741131403e8c9580d0b7a8c2446cb4502d01f80053b7a2092663de92431aaa82",
201
201
  "conformance/fixtures/sidecar-example/agent-example.sm": "41200387e74a120c554a34dfabc50dd2151067a1c6599695c59412d8eab38bb4",
202
- "db-schema.md": "ef3de28061957f036c97384fc2cae0308f91624ab2b0940cef6d46d5c1998089",
203
- "interfaces/security-scanner.md": "4a982667008f233656f44c61ce9948e062432d3debdcbf7a134da03bd4139d7d",
204
- "job-events.md": "8f371e0991816eca2e1a55cbd8a50733546ca5e7c861588048c18be1d22dbd57",
205
- "job-lifecycle.md": "12bfc27690c92cf93682a3b6fbfeb7e2d252d33f704fd2d7de9a13db713e6281",
206
- "plugin-author-guide.md": "ace7b2df6eb54cdc92ef1ab99c0a6a2a0a88f64b229b0b1b94009d9d6458d2c0",
207
- "plugin-kv-api.md": "04b2178f46fb88adeae9240df9c9e1761b660396072001dac32cd402e11a2d7d",
208
- "prompt-preamble.md": "fb40ab510234383326f198dec82cd6d744f28b7432eebac6cbfbb7ca1c483b7d",
209
- "schemas/annotations.schema.json": "4f80b6a2122ecacc6a1dcffa0912ef462c6a082182c0ad179b355b3054f390e8",
210
- "schemas/api/rest-envelope.schema.json": "db6588cd8b01eef08e1e36ede2f7be8ced8a56965911875399b54687680edaf4",
202
+ "db-schema.md": "3e3ffdb245e68675f5a366c2ba8ad038e0233ce34105e9276e92c02cc9cdf69f",
203
+ "interfaces/security-scanner.md": "aefe9f02f190615ba18649df03c1bdd79d98691039563c659e90f34362e5f1d5",
204
+ "job-events.md": "b223bf0e576cbd481688e163ab3ce0a6e952a8a4a3912f1342237b664984e388",
205
+ "job-lifecycle.md": "1d9c42632f8e77ef58ff47ae6d9680e7ed5939760627c75253aab8c80f728fd1",
206
+ "plugin-author-guide.md": "7286ab5be91cdf2ca9f1132c64c44d9a353ee7a4bc3473f8eeeda0c57edd2a6a",
207
+ "plugin-kv-api.md": "3e932e74ad27ce4e7e6218cbbddd2437c810d12f90b1590ef2313019d9b7d82f",
208
+ "prompt-preamble.md": "4860c310ccf2823870d318993ad8f067571799dade90bddb6634c3dbedd636b7",
209
+ "schemas/annotations.schema.json": "b3a9aa66de17058ccfd890ea9ff1b9ee315a0877e9dd4a58fd8b76e26a99d00e",
210
+ "schemas/api/rest-envelope.schema.json": "0f33b58e885cd0d74682a534d24765edee88fc35a63c03e987f73bdad451c892",
211
211
  "schemas/bump-report.schema.json": "c2d853715d5f50098567bc23382a4e81baf78d589c6e1baf67d3b841e7f7d8ae",
212
- "schemas/conformance-case.schema.json": "7cd0f3aae5124f24be57cddb213d002d0466f79d06fd3da896075c8b28650410",
212
+ "schemas/conformance-case.schema.json": "f6d4c9fb92e79cb516eeeb9d042223572a3bd5ff8e7871a0becce13916f20cf6",
213
213
  "schemas/execution-record.schema.json": "9628fa557cb856402f3a5f1d1167c609e46a197c850fe8171abfddd46c1028a8",
214
214
  "schemas/extensions/action.schema.json": "262272175c06a2e33c08f819a45c3ef8260276c91a9d0542fdffc932aeb32db7",
215
- "schemas/extensions/base.schema.json": "84277972d14e35397dde60deb102a375f15ed1487540c909d40780f5a5e250b7",
216
- "schemas/extensions/extractor.schema.json": "9ce1ac00ce0e193d86c8b0fbddb2e4cf58c420ed105bab500019501cf7037c26",
215
+ "schemas/extensions/analyzer.schema.json": "6272e5959f3c94e109e6116f7ed6b5ae35e4e3aea821a3c30742d11c5ab5838f",
216
+ "schemas/extensions/base.schema.json": "528083fb7db8bd064147224999e1bb3959ee2061863f55f48e928c27222cf957",
217
+ "schemas/extensions/extractor.schema.json": "a859a53a7a5b009b1fe20d322bc1a8ff62e4b91ef938e98b1c80c802bd734b37",
217
218
  "schemas/extensions/formatter.schema.json": "2ab092aa37ae349c69b93071ed4f0e131affb7bb5799516ca82c721262631b36",
218
- "schemas/extensions/hook.schema.json": "7465c38e0765edf23e49d4f96c539d04323f1cf564af1c60ee637c79a6d39239",
219
+ "schemas/extensions/hook.schema.json": "a55cec50f6fda5b924de86359b910d22548d0a5bb61b2051edb82a80d3b36a2b",
219
220
  "schemas/extensions/provider.schema.json": "077c0c079e3965cee667019f76ee1e180d6b1f4162767d868bccc912e8dfbf89",
220
- "schemas/extensions/rule.schema.json": "8ff420bde498f50db114c352305d487c71aef2dd746fd0c24976ff6a09865c22",
221
221
  "schemas/frontmatter/base.schema.json": "ec4abde950c31639974fc078e6bdc74ed48da4d2c0a996f5248684406910a178",
222
222
  "schemas/history-stats.schema.json": "23f472d1de06d23fc775aabba821f8375f347af4dc8d89ba567980d61a11f9de",
223
223
  "schemas/input-types.schema.json": "f1f51ccda746ea3c8a404757f60c89e403619e88ec4137a50af100ec89f8f4b5",
224
- "schemas/issue.schema.json": "40f6f8abadcce0fd8eac9df27ffcc20b2fc9fda6970142ddb8e7e56b1760b9b1",
224
+ "schemas/issue.schema.json": "0bbc1783ad07cb5c3c2399d7a560f57314a9ff76ed061b4a198ddf7ce74dad78",
225
225
  "schemas/job.schema.json": "ffbdd51c54b487c44eb57fabd07f624ac1030c14ef69b46933c154092853a84c",
226
- "schemas/link.schema.json": "0a95a24849a38b9ef5bad5361519a9f9e012b5bc3001289fad29d0851fceff6b",
226
+ "schemas/link.schema.json": "7fc429d03aca7e4c0b9a28241712c1aa2a5275870cea5ed938c2f97e8cccb081",
227
227
  "schemas/node.schema.json": "2ede4385e796cbf416c494d810dcb6d6036b35e71561efee46f5675bf0a015fe",
228
- "schemas/plugins-registry.schema.json": "fc5f9a15df94ea2218dadfa2294a9d7ae9c9e6015a80c71e57fbd3ece913ffbd",
229
- "schemas/project-config.schema.json": "17b109a0843b8345ea91cb7af80d003905689b2ae6bfd5f5db422d4a6ae5972b",
228
+ "schemas/plugins-registry.schema.json": "678f476cf460d0b5876a92e72e0d572b6db265dd9fad6e95db553c56f77db5d9",
229
+ "schemas/project-config.schema.json": "f6479bc73aa58821128965a5cea957cdd979cbaa4b942d76a251218cacfdeafa",
230
230
  "schemas/report-base-deterministic.schema.json": "6f8b38c097994ee87e0639935c42b5e85d8ea4244959ca397978171b0d7d2222",
231
231
  "schemas/report-base.schema.json": "a1021e9a59b4df9f99cd92454d797e88469766e7d49f52d231c4645ffdfdad8f",
232
232
  "schemas/scan-result.schema.json": "d1a8782e198bc9bb92dad247437aefa1b02f92ff8dca8562eaf2348fd7c5cf0c",
233
- "schemas/sidecar.schema.json": "4fca64f7580384c618915e545f5d24aa823b6a47e2ffec331b1720fa19934365",
234
- "schemas/summaries/agent.schema.json": "3d22558eeb170e00c4fc32018a810d27333cc632c9e528ff386100cfdfded087",
235
- "schemas/summaries/command.schema.json": "2bffd606b24f7df9ccd13890af8725adfbfb8a2d7782fee1e0ac5250b9059117",
236
- "schemas/summaries/hook.schema.json": "36f876f3b1a60d45be97a0848c79fd18744b434dfdcefc366f033b253d56268c",
233
+ "schemas/sidecar.schema.json": "119f71e943a3d9305e25e26e1d8b3b525a391af0a7547d8f8a79de96b016e07c",
234
+ "schemas/summaries/agent.schema.json": "bf540f9a804f2b43756ab33b7deb0462620d26e88cc9379c75a5f87d3b1b47d8",
235
+ "schemas/summaries/command.schema.json": "c26f6965f77c5058608feb5e7b9f807395de8e015b0dea5efcdb44cb1820551a",
236
+ "schemas/summaries/hook.schema.json": "58420ec485e152fdd21fa3d87337ad74b0d81a48d3b83dd072d4a2d196f78573",
237
237
  "schemas/summaries/markdown.schema.json": "33e2a1a11ec08a860c0c220609235c6fbdfda9ce19b6d65238f467f132ed4e54",
238
238
  "schemas/summaries/skill.schema.json": "f01bab92c51d64ee23e61587e42cf0dc5b37a2f518f5b12b3d1d456390338aa8",
239
- "schemas/view-contracts.schema.json": "4e668f4527bc723187f516dd658587a832af66d0432a769a8aad506817c6c26f",
239
+ "schemas/view-slots.schema.json": "44e329a2d0fff8f4ed6b3c92209661b5dae39927742fb6121ce71584941d27d6",
240
240
  "versioning.md": "996e62006423edc01151a6f7869605f76c5e1454cc30b38d9f616925b5bcfb64"
241
241
  }
242
242
  }
@@ -147,7 +147,7 @@ A `category` value SHOULD be one of these for interoperability:
147
147
  - `injection-risk` — pattern likely to enable prompt injection, SQL injection, command injection.
148
148
  - `license-violation` — incompatible license terms for a dependency or referenced asset.
149
149
  - `outdated` — version pinned well below current, not exploited but due for upgrade.
150
- - `policy-violation` — organization-level rule (naming, banned words, required disclaimer).
150
+ - `policy-violation` — organization-level analyzer (naming, banned words, required disclaimer).
151
151
 
152
152
  Vendors MAY introduce their own category with the prefix `vendor:<slug>` (e.g. `vendor:socket:supply-chain`). Consumers that don't understand a vendor category MUST treat it as opaque but still display it.
153
153
 
@@ -158,7 +158,7 @@ Vendors MAY introduce their own category with the prefix `vendor:<slug>` (e.g. `
158
158
  - Scanners are invoked through the standard job system: `sm job submit security-snyk -n <node.path>` or `sm job submit security-snyk --all`.
159
159
  - The report is persisted through the normal action report mechanism ([`state_executions`](../db-schema.md)`.report_path` points to the JSON file).
160
160
  - `sm findings --security` aggregates findings from reports whose action id starts with `security-`, merging across scanners, deduplicating by `finding.id`.
161
- - Implementations MAY also surface findings at scan time via a companion Rule (e.g. `security-findings-stale` flags nodes whose last security scan is older than a threshold). This is recommended but not normative.
161
+ - Implementations MAY also surface findings at scan time via a companion Analyzer (e.g. `security-findings-stale` flags nodes whose last security scan is older than a threshold). This is recommended but not normative.
162
162
 
163
163
  ---
164
164
 
package/job-events.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Canonical event stream emitted during job execution. Every implementation MUST emit these events in the order described, with the shapes defined below. Consumers include the CLI pretty printer, the `--json` ndjson output, the Server's WebSocket broadcaster, and any third-party integration.
4
4
 
5
- This document is **normative**. The set of event types, their payload shapes, and their ordering rules are stable contracts.
5
+ This document is **normative**. The set of event types, their payload shapes, and their ordering analyzers are stable contracts.
6
6
 
7
7
  ---
8
8
 
@@ -291,7 +291,7 @@ Emitted once at the end of `sm job run`, after the last job event.
291
291
 
292
292
  ---
293
293
 
294
- ## Ordering rules
294
+ ## Ordering analyzers
295
295
 
296
296
  For each job, the normative order is:
297
297
 
@@ -401,25 +401,25 @@ Emitted once per registered Extractor, after the full walk completes. Aggregated
401
401
 
402
402
  > **Hookable** — see [`architecture.md` §Hook · curated trigger set](./architecture.md#hook--curated-trigger-set). Per-Extractor metrics, audit. Filter by `data.extractorId` to scope to a single Extractor.
403
403
 
404
- #### `rule.completed`
404
+ #### `analyzer.completed`
405
405
 
406
- Emitted once per registered Rule, after every issue has been validated.
406
+ Emitted once per registered Analyzer, after every issue has been validated.
407
407
 
408
408
  ```json
409
409
  {
410
- "type": "rule.completed",
410
+ "type": "analyzer.completed",
411
411
  "timestamp": 1745159455950,
412
412
  "runId": "...",
413
413
  "jobId": null,
414
414
  "data": {
415
- "ruleId": "core/superseded"
415
+ "analyzerId": "core/superseded"
416
416
  }
417
417
  }
418
418
  ```
419
419
 
420
- `ruleId` is the qualified extension id.
420
+ `analyzerId` is the qualified extension id.
421
421
 
422
- > **Hookable** — see [`architecture.md` §Hook · curated trigger set](./architecture.md#hook--curated-trigger-set). Per-Rule alerting, downstream tooling. Filter by `data.ruleId`.
422
+ > **Hookable** — see [`architecture.md` §Hook · curated trigger set](./architecture.md#hook--curated-trigger-set). Per-Analyzer alerting, downstream tooling. Filter by `data.analyzerId`.
423
423
 
424
424
  #### `action.completed`
425
425
 
@@ -456,7 +456,7 @@ Emitted by the scan after `scan.completed` when the new scan's issue set differs
456
456
  "runId": "...",
457
457
  "jobId": null,
458
458
  "data": {
459
- "ruleId": "trigger-collision",
459
+ "analyzerId": "trigger-collision",
460
460
  "severity": "warn",
461
461
  "nodeIds": ["skills/a.md", "skills/b.md"],
462
462
  "message": "..."
@@ -475,13 +475,13 @@ Emitted when an issue present in the previous scan is absent from the new one.
475
475
  "runId": "...",
476
476
  "jobId": null,
477
477
  "data": {
478
- "ruleId": "broken-ref",
478
+ "analyzerId": "broken-ref",
479
479
  "nodeIds": ["skills/c.md"]
480
480
  }
481
481
  }
482
482
  ```
483
483
 
484
- Issue diffing is keyed on `(ruleId, nodeIds sorted, message)` — same key → same issue. A payload change on the same key emits no event; consumers re-read full issue detail from `sm check` when needed.
484
+ Issue diffing is keyed on `(analyzerId, nodeIds sorted, message)` — same key → same issue. A payload change on the same key emits no event; consumers re-read full issue detail from `sm check` when needed.
485
485
 
486
486
  ---
487
487
 
@@ -523,6 +523,6 @@ Consumers MUST ignore unknown fields (forward compatibility).
523
523
 
524
524
  The envelope (`type`, `timestamp`, `runId`, `jobId`, `data`) is stable. Adding an envelope field is a major bump because every consumer would need to handle it.
525
525
 
526
- The **non-job event families** (`scan.*`, `issue.*`, `extractor.completed`, `rule.completed`, `action.completed`) are marked **experimental** across spec v0.x. They ship alongside the WebSocket broadcaster at Step 13 of the reference impl; shapes may tighten before a stable tag lands. Once promoted to `stable` (a minor spec bump), the same add/remove/rename semantics as the job events apply.
526
+ The **non-job event families** (`scan.*`, `issue.*`, `extractor.completed`, `analyzer.completed`, `action.completed`) are marked **experimental** across spec v0.x. They ship alongside the WebSocket broadcaster at Step 13 of the reference impl; shapes may tighten before a stable tag lands. Once promoted to `stable` (a minor spec bump), the same add/remove/rename semantics as the job events apply.
527
527
 
528
528
  The **Hook curated trigger set** (eight hookable lifecycle events; see [`architecture.md` §Hook · curated trigger set](./architecture.md#hook--curated-trigger-set)) is itself stable as of the same minor in which it lands: adding a hookable trigger is a minor bump, removing or renaming one is a major bump. The curation policy ("a hook subscribes only to a deliberately small set") is normative — surface noise reduction is the entire point.
package/job-lifecycle.md CHANGED
@@ -242,7 +242,7 @@ Config controls (`jobs.retention.completed`, `jobs.retention.failed`):
242
242
 
243
243
  ## See also
244
244
 
245
- - [`architecture.md`](./architecture.md) — `RunnerPort` definition; driving-adapter peer rule for Skill agents.
245
+ - [`architecture.md`](./architecture.md) — `RunnerPort` definition; driving-adapter peer analyzer for Skill agents.
246
246
  - [`job-events.md`](./job-events.md) — canonical event stream emitted during job execution.
247
247
  - [`prompt-preamble.md`](./prompt-preamble.md) — verbatim preamble prepended to every rendered job content row.
248
248
  - [`db-schema.md`](./db-schema.md) — `state_jobs` and `state_executions` table catalogs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skill-map/spec",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "JSON Schemas, prose contracts, and conformance suite for the skill-map specification.",
5
5
  "license": "MIT",
6
6
  "type": "module",