@skill-map/spec 0.13.0 → 0.14.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 +69 -2534
- package/README.md +2 -2
- package/architecture.md +24 -7
- package/cli-contract.md +4 -1
- package/conformance/README.md +2 -2
- package/conformance/coverage.md +13 -5
- package/db-schema.md +1 -1
- package/index.json +9 -9
- package/package.json +1 -1
- package/plugin-author-guide.md +21 -11
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# skill-map spec
|
|
2
2
|
|
|
3
|
-
The **skill-map specification** defines a vendor-neutral standard for mapping, inspecting, and managing collections of interrelated Markdown files — skills, agents, commands, hooks, and notes that compose AI-agent ecosystems (Claude Code, Codex, Gemini,
|
|
3
|
+
The **skill-map specification** defines a vendor-neutral standard for mapping, inspecting, and managing collections of interrelated Markdown files — skills, agents, commands, hooks, and notes that compose AI-agent ecosystems (Claude Code, Codex, Gemini, docs sites, and any future platform).
|
|
4
4
|
|
|
5
5
|
This document is the **source of truth**. The reference implementation under `../src/` conforms to this spec. Third parties can build alternative implementations (any language, any UI, any CLI) using only `spec/`, without reading the reference source.
|
|
6
6
|
|
|
@@ -31,7 +31,7 @@ These are implementation decisions. The reference impl picks them (see [`../AGEN
|
|
|
31
31
|
- **Machine-readable**: all domain shapes are JSON Schemas. Validate from any language that has a JSON Schema validator.
|
|
32
32
|
- **Human-readable**: prose documents for each subsystem, with examples.
|
|
33
33
|
- **Independently versioned**: spec `v1.0.0` can be implemented by CLI `v0.3.2`. See [`versioning.md`](./versioning.md).
|
|
34
|
-
- **Platform-neutral**: no platform
|
|
34
|
+
- **Platform-neutral**: no platform is privileged. Each is expressed as an adapter extension.
|
|
35
35
|
- **Conformance-tested**: every conforming implementation passes the suite under [`conformance/`](./conformance/README.md). Pass/fail is binary.
|
|
36
36
|
|
|
37
37
|
## Naming conventions
|
package/architecture.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Architecture
|
|
2
2
|
|
|
3
|
-
Normative description of skill-map's internal boundaries: the **kernel**, the **ports** it exposes, the **adapters** that drive and serve it, and the
|
|
3
|
+
Normative description of skill-map's internal boundaries: the **kernel**, the **ports** it exposes, the **adapters** that drive and serve it, and the six **extension kinds** that live outside the kernel.
|
|
4
4
|
|
|
5
5
|
Any conforming implementation — reference or third-party — MUST respect these boundaries. The conformance suite under [`conformance/`](./conformance/README.md) enforces the kernel-agnostic invariants; per-Provider suites (e.g. `src/extensions/providers/claude/conformance/` for the reference impl's Claude Provider) enforce the kind-catalog cases. Both are driven via `sm conformance run`.
|
|
6
6
|
|
|
@@ -150,7 +150,7 @@ Mode is a property of the extension as a whole, not of an individual call. **An
|
|
|
150
150
|
|
|
151
151
|
Provider and Formatter are locked to deterministic because they sit at the **boundaries** of the system. A Provider resolves `path → kind` during boot; probabilistic classification would make the boot phase slow, costly, and non-reproducible. A formatter must produce diffable output (`sm scan` snapshots round-trip in CI). Probabilistic narrators of the graph are a valid product but they live in jobs and emit Findings, not in formatters.
|
|
152
152
|
|
|
153
|
-
> **Naming note — `Provider` vs hexagonal `adapter`.**
|
|
153
|
+
> **Naming note — `Provider` vs hexagonal `adapter`.** A `Provider` is an **extension** authored by plugins (it recognises a platform and declares its kind catalog). The hexagonal-architecture term `adapter` refers to **port implementations** internal to the kernel package — `RunnerPort.adapter`, `StoragePort.adapter`, `FilesystemPort.adapter`, `PluginLoaderPort.adapter` — and lives under `kernel/adapters/`. The two concepts share an architectural lineage (both bridge two worlds) but live in deliberately disjoint namespaces so plugin authors and impl maintainers never confuse them.
|
|
154
154
|
|
|
155
155
|
### When each mode runs
|
|
156
156
|
|
|
@@ -182,12 +182,27 @@ Six kinds, all first-class, all loaded through the same registry. Each kind has
|
|
|
182
182
|
|
|
183
183
|
### Provider · `kinds` catalog
|
|
184
184
|
|
|
185
|
-
Every `Provider`
|
|
185
|
+
Every `Provider` MUST declare a non-empty map `kinds: { <kind>: { schema, defaultRefreshAction, ui } }` covering every `kind` it classifies into. Each entry carries three required fields:
|
|
186
186
|
|
|
187
|
-
- **`schema`** — path to the
|
|
188
|
-
- **`defaultRefreshAction`** — qualified action id (`<plugin-id>/<action-id>`) the UI's probabilistic-refresh surface (`🧠 prob`) dispatches for nodes of this kind. The
|
|
187
|
+
- **`schema`** — path (relative to the Provider package) to the kind's frontmatter JSON Schema. The schema MUST extend [`frontmatter/base.schema.json`](./schemas/frontmatter/base.schema.json) via `allOf` + `$ref` to base's `$id`. The kernel registers it with AJV at boot and validates every node's frontmatter against the entry matching its classified kind.
|
|
188
|
+
- **`defaultRefreshAction`** — qualified action id (`<plugin-id>/<action-id>`) the UI's probabilistic-refresh surface (`🧠 prob`) dispatches for nodes of this kind. The action MUST exist in the registry; a dangling reference disables the Provider with status `invalid-manifest`. Plugins MAY override per-node via `metadata.refreshAction`; the Provider default is normative.
|
|
189
|
+
- **`ui`** — presentation block: `{ label, color, colorDark?, emoji?, icon? }`. See §Provider · `ui` presentation below.
|
|
189
190
|
|
|
190
|
-
The catalog is the single source of truth for "which kinds does this Provider emit" — the `IProvider` runtime contract derives the kind set from `Object.keys(kinds)`.
|
|
191
|
+
The catalog is the single source of truth for "which kinds does this Provider emit" — the `IProvider` runtime contract derives the kind set from `Object.keys(kinds)`.
|
|
192
|
+
|
|
193
|
+
### Provider · `ui` presentation
|
|
194
|
+
|
|
195
|
+
Each `kinds[*].ui` entry declares how the UI renders nodes of that kind:
|
|
196
|
+
|
|
197
|
+
- **`label`** — short human name (e.g. `'Skill'`, `'Agent'`). Used in palette chips, list view, inspector header.
|
|
198
|
+
- **`color`** — base color (any CSS color string) for the kind. The UI derives bg / fg tints per theme via a deterministic helper, so the Provider declares one base color per theme rather than four hex values.
|
|
199
|
+
- **`colorDark?`** — optional dark-theme override. Defaults to `color` when omitted.
|
|
200
|
+
- **`emoji?`** — optional single-glyph emoji rendered alongside the label.
|
|
201
|
+
- **`icon?`** — optional discriminated union: either `{ kind: 'pi'; id: 'pi-…' }` (a PrimeIcons class id) or `{ kind: 'svg'; path: '…' }` (raw SVG path data wrapped by the UI in `viewBox="0 0 24 24"` and tinted with `currentColor`). The discriminator keeps UI dispatch exhaustive without string-sniffing; AJV validates each variant cleanly.
|
|
202
|
+
|
|
203
|
+
The `ui` block is required (not optional) by design: making it optional would force the UI to invent visuals for missing entries, silently collapsing unknown kinds to a default rendering and hiding manifest gaps. Forcing the Provider to declare presentation up-front means the UI never guesses.
|
|
204
|
+
|
|
205
|
+
The kernel ships every Provider's `ui` block to the BFF at boot; the BFF aggregates them into a `kindRegistry` map and embeds it in every payload-bearing REST envelope (see [`cli-contract.md` §Server](./cli-contract.md#server)). The UI consumes `kindRegistry` directly — built-in and user-plugin kinds render identically.
|
|
191
206
|
|
|
192
207
|
### Provider · `explorationDir`
|
|
193
208
|
|
|
@@ -400,7 +415,9 @@ This is what makes "CLI-first" a coherent rule: every CLI verb is a kernel funct
|
|
|
400
415
|
|
|
401
416
|
The **port list** is stable as of spec v1.0.0. Adding a sixth port is a major bump.
|
|
402
417
|
|
|
403
|
-
The **extension kind list** (
|
|
418
|
+
The **extension kind list** (6 kinds: Provider, Extractor, Rule, Action, Formatter, Hook) is stable as of spec v1.0.0. Adding a seventh kind is a major bump. Removing or renaming a kind is a major bump.
|
|
419
|
+
|
|
420
|
+
The **Hook curated trigger set** (eight events: `scan.started`, `scan.completed`, `extractor.completed`, `rule.completed`, `action.completed`, `job.spawning`, `job.completed`, `job.failed`) is stable as of spec v1.0.0. Adding a ninth trigger is a minor bump; removing or renaming any of the eight is a major bump.
|
|
404
421
|
|
|
405
422
|
The **execution modes** (`deterministic` / `probabilistic`) and the per-kind mode capability matrix above are stable as of spec v1.0.0. Adding a third mode or changing which kinds are dual-mode is a major bump. Renaming or repurposing the mode enum values is a major bump.
|
|
406
423
|
|
package/cli-contract.md
CHANGED
|
@@ -8,7 +8,8 @@ Normative description of the `sm` CLI surface: verbs, flags, exit codes, machine
|
|
|
8
8
|
|
|
9
9
|
- Primary: `sm`.
|
|
10
10
|
- Long alias: `skill-map`. MUST resolve to the same binary. A symlink, shim, or alias in `bin` field of `package.json` is acceptable.
|
|
11
|
-
- Help invocation: `sm
|
|
11
|
+
- Help invocation: `sm --help` and `sm -h` MUST print top-level help and exit with code 0.
|
|
12
|
+
- Bare invocation: `sm` with no arguments starts the Web UI server (equivalent to `sm serve`) when a `.skill-map/` project is initialized in the current working directory. When no project is found in the cwd, it MUST print a one-line hint to stderr pointing the user to `sm init` and `sm --help`, then exit with code `2`.
|
|
12
13
|
|
|
13
14
|
---
|
|
14
15
|
|
|
@@ -346,6 +347,8 @@ The reference implementation ships a Hono BFF rooted at `src/server/`. One Node
|
|
|
346
347
|
|
|
347
348
|
List endpoints conform to [`schemas/api/rest-envelope.schema.json`](schemas/api/rest-envelope.schema.json). The `/api/scan` and `/api/health` responses carry their underlying `ScanResult` / `IHealthResponse` shapes directly (no envelope wrap). The `/api/graph` response carries the formatter's native textual output.
|
|
348
349
|
|
|
350
|
+
**`kindRegistry` envelope field.** Every payload-bearing variant of the REST envelope (`nodes` / `links` / `issues` / `plugins` lists, the `node` single, the `config` value envelope) embeds a required `kindRegistry: { [kindName]: { providerId, label, color, colorDark?, emoji?, icon? } }` field. Sentinel envelopes (`health`, `scan`, `graph`) are exempt — they carry no payload at the wire level. The BFF assembles the registry once at boot from every enabled Provider's `kinds[*].ui` block (see [`architecture.md` §Provider · `ui` presentation](architecture.md#provider--ui-presentation)) and attaches the same map to every applicable response. The UI consumes `kindRegistry` directly to render kind palettes, list rows, and inspector headers — built-in and user-plugin kinds render identically. A kind appearing in a response payload (e.g. `node.kind`) without a matching `kindRegistry` entry is a contract violation; the kernel rejects Providers without a `ui` block at load time so the registry is always complete for whatever kinds appear in the response.
|
|
351
|
+
|
|
349
352
|
**Error envelope** (mirrors `§Machine-readable output rules`):
|
|
350
353
|
|
|
351
354
|
```json
|
package/conformance/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Language-neutral test suite the specification demands. A conforming implementation passes every case; failing any case is a conformance bug.
|
|
4
4
|
|
|
5
|
-
The suite splits across two ownership boundaries
|
|
5
|
+
The suite splits across two ownership boundaries:
|
|
6
6
|
|
|
7
7
|
- **Spec-owned cases** — kernel-agnostic. They live in this directory and ship with `@skill-map/spec`. Today: `kernel-empty-boot` (boot invariant) and the `preamble-bitwise-match` deferred case. The universal preamble fixture (`preamble-v1.txt`) lives here too.
|
|
8
8
|
- **Provider-owned cases** — exercise a Provider's own `kinds` catalog. They live next to the Provider's manifest, under `<plugin-dir>/conformance/`. The reference impl ships one such suite at [`src/extensions/providers/claude/conformance/`](../../src/extensions/providers/claude/conformance/) covering Claude's five kinds (`skill` / `agent` / `command` / `hook` / `note`) via cases `basic-scan`, `rename-high`, `orphan-detection`.
|
|
@@ -154,7 +154,7 @@ for (const caseFile of await readdir('spec/conformance/cases')) {
|
|
|
154
154
|
}
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
-
A Provider-owned runner mirrors the loop with a different cases / fixtures root — `<plugin-dir>/conformance/cases/` and `<plugin-dir>/conformance/fixtures/`. The reference CLI ships both as `sm conformance run
|
|
157
|
+
A Provider-owned runner mirrors the loop with a different cases / fixtures root — `<plugin-dir>/conformance/cases/` and `<plugin-dir>/conformance/fixtures/`. The reference CLI ships both as `sm conformance run`; the verb resolves the spec scope via `@skill-map/spec` and discovers Provider scopes by walking each built-in plugin's `conformance/` directory.
|
|
158
158
|
|
|
159
159
|
The reference implementation's runner ships under `src/conformance/index.ts`; the verb lives at `src/cli/commands/conformance.ts` and uses the runner one case at a time.
|
|
160
160
|
|
package/conformance/coverage.md
CHANGED
|
@@ -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 ([`../../
|
|
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/).
|
|
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
|
|
|
@@ -18,14 +18,14 @@ This file is hand-maintained. A CI check before spec release compares the schema
|
|
|
18
18
|
| 8 | `job.schema.json` | — | 🔴 missing | Blocked by Step 10 (job system). Needs a case that submits a local action (no LLM), inspects `sm job show --json`. |
|
|
19
19
|
| 9 | `report-base.schema.json` | — | 🔴 missing | Indirect coverage once any summarizer case lands. Direct contract case: validate a handcrafted minimal report ({confidence, safety}) against the base schema. |
|
|
20
20
|
| 10 | `conformance-case.schema.json` | — | 🔴 missing | Self-referential: every `*.json` under `cases/` MUST validate against this schema. Add a meta-case that enumerates + validates all cases. |
|
|
21
|
-
| 11 | `frontmatter/base.schema.json` | — | 🔴 missing | Universal frontmatter shape. Per-kind schemas (skill / agent / command / hook / note)
|
|
21
|
+
| 11 | `frontmatter/base.schema.json` | — | 🔴 missing | Universal frontmatter shape. Per-kind schemas (`skill` / `agent` / `command` / `hook` / `note`) live with the Provider that emits them (the Claude Provider ships them under `src/extensions/providers/claude/schemas/`) and extend this base via `$ref`-by-`$id`. Direct spec-level case still pending: fixture with min-required frontmatter only, no Provider needed (Provider-disabled mode + a single `notes/<file>.md` with `name: ...` + `description: ...`). |
|
|
22
22
|
| 12 | `summaries/skill.schema.json` | — | 🔴 missing | Blocked by Step 10 (`skill-summarizer`). Case: submit summarizer, validate report. |
|
|
23
23
|
| 13 | `summaries/agent.schema.json` | — | 🔴 missing | Blocked by Step 11. |
|
|
24
24
|
| 14 | `summaries/command.schema.json` | — | 🔴 missing | Blocked by Step 11. |
|
|
25
25
|
| 15 | `summaries/hook.schema.json` | — | 🔴 missing | Blocked by Step 11. |
|
|
26
26
|
| 16 | `summaries/note.schema.json` | — | 🔴 missing | Blocked by Step 11. |
|
|
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
|
-
| 18 | `extensions/provider.schema.json` |
|
|
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
30
|
| 20 | `extensions/rule.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. |
|
|
@@ -34,7 +34,7 @@ This file is hand-maintained. A CI check before spec release compares the schema
|
|
|
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
36
|
|
|
37
|
-
> **Note on Provider-owned schemas.** Per
|
|
37
|
+
> **Note on Provider-owned schemas.** Per-kind frontmatter schemas (`skill`, `agent`, `command`, `hook`, `note`) 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.
|
|
38
38
|
|
|
39
39
|
Status legend: 🟢 covered (at least one case asserts the schema end-to-end) · 🟡 partial (covered only indirectly or via a sub-shape) · 🔴 missing.
|
|
40
40
|
|
|
@@ -52,7 +52,7 @@ These have their own conformance cases even though they are not JSON Schemas.
|
|
|
52
52
|
| F | Nonce mismatch | — | 🔴 missing | Blocked by Step 10. `sm record` with wrong nonce → exit 4. |
|
|
53
53
|
| G | Reap | — | 🔴 missing | Blocked by Step 10. Set TTL to 1s; claim; wait; next `sm job run` reaps with reason `abandoned`. |
|
|
54
54
|
| H | `run.*` event envelope for Skill agent | — | 🔴 missing | Blocked by Step 10. Skill-agent flow emits synthetic `r-ext-*` run envelope around one job. |
|
|
55
|
-
| I | Rename heuristic | `rename-high`, `orphan-detection` (Provider-owned) | 🟢 covered | High-confidence rename emits no issue and the new path is the sole node. Orphan branch emits exactly one `orphan` issue (severity `info`) when a deleted node has no replacement. Cases
|
|
55
|
+
| I | Rename heuristic | `rename-high`, `orphan-detection` (Provider-owned) | 🟢 covered | High-confidence rename emits no issue and the new path is the sole node. Orphan branch emits exactly one `orphan` issue (severity `info`) when a deleted node has no replacement. Cases live with the Claude Provider (they reach a Provider's `kinds` catalog by construction); see [`src/extensions/providers/claude/conformance/`](../../src/extensions/providers/claude/conformance/). Medium / ambiguous branches are exercised by `src/test/rename-heuristic.test.ts` until the conformance schema grows richer assertions. |
|
|
56
56
|
| J | Plugin DDL rejection | — | 🔴 missing | Blocked by Step 9. Plugin migration referencing `state_jobs` → disabled with `invalid-manifest`. |
|
|
57
57
|
| K | Plugin prefix injection | — | 🔴 missing | Blocked by Step 9. Plugin declares `CREATE TABLE foo` → kernel applies as `plugin_<id>_foo`. |
|
|
58
58
|
| L | Elapsed-time reporting | — | 🔴 missing | Blocked by Step 4 (first real verb work). Run any in-scope verb; stderr last line MUST match `/^done in (\d+ms\|\d+\.\d+s\|\d+m \d+s)$/`. In-scope verb with `--json` returning an object MUST carry `elapsedMs`. Exempt verb (`sm version`) MUST NOT emit the line. |
|
|
@@ -62,3 +62,11 @@ These have their own conformance cases even though they are not JSON Schemas.
|
|
|
62
62
|
- **spec v0.x**: partial coverage acceptable. Every case added as the reference impl lands the verb that makes it runnable.
|
|
63
63
|
- **spec v1.0.0 release**: all rows above MUST be 🟢 covered or explicitly 🟠 deferred to v1.1 with a linked issue.
|
|
64
64
|
- **CI check**: [`scripts/check-coverage.js`](../../scripts/check-coverage.js) compares `spec/schemas/**/*.schema.json` against the matrix above on every PR. A schema without a row here, or a row pointing at a missing schema, fails CI (exit 1 with a `::error::` annotation). Wired into `ci.yml` §validate and into `npm run spec:check`.
|
|
65
|
+
|
|
66
|
+
## Stability
|
|
67
|
+
|
|
68
|
+
The **coverage matrix gating policy** (every shipped schema MUST have a row; every row MUST be 🟢 covered or 🟠 deferred-to-v1.1 before the spec v1.0.0 tag) is stable as of spec v1.0.0. Relaxing the v1.0 release gate (e.g. allowing 🔴 missing rows to ship) is a major bump. Tightening the gate further (e.g. forbidding 🟠 deferrals) is a minor bump.
|
|
69
|
+
|
|
70
|
+
The **assertion-type vocabulary** (`exit-code`, `json-path`, `file-exists`, `file-contains-verbatim`, `file-matches-schema`, `stderr-matches`) is stable as of spec v1.0.0. Adding a new assertion type is a minor bump; renaming or removing one is a major bump.
|
|
71
|
+
|
|
72
|
+
The **per-row schema and case set** above is mutable through every spec release: rows are added when a new schema or normative artifact lands, marked deferred when the runtime that exercises them ships in a later step, and flipped to 🟢 covered when the case file is committed. Adding rows is non-breaking; deleting rows MUST coincide with deleting the corresponding schema (which is itself a major bump).
|
package/db-schema.md
CHANGED
|
@@ -177,7 +177,7 @@ Primary key: `(node_path, extractor_id)`. Indexes: `ix_scan_extractor_runs_node`
|
|
|
177
177
|
|
|
178
178
|
Universal enrichment layer (A.8). Stores `ctx.enrichNode(partial)` outputs separately from the author-supplied frontmatter on `scan_nodes.frontmatter_json`, which the Extractor pipeline NEVER mutates.
|
|
179
179
|
|
|
180
|
-
One row per `(node_path, extractor_id)` pair an Extractor enriched. Both deterministic and probabilistic Extractors write here; only probabilistic rows participate in stale tracking — when a body changes between scans, the kernel flags the surviving probabilistic row `stale = 1` (NOT deleted, preserving the LLM cost paid to produce it). Deterministic rows simply
|
|
180
|
+
One row per `(node_path, extractor_id)` pair an Extractor enriched. Both deterministic and probabilistic Extractors write here; only probabilistic rows participate in stale tracking — when a body changes between scans, the kernel flags the surviving probabilistic row `stale = 1` (NOT deleted, preserving the LLM cost paid to produce it). Deterministic rows are simply overwritten via PRIMARY KEY conflict on the next re-extract through the A.9 cache.
|
|
181
181
|
|
|
182
182
|
| Column | Type | Constraint |
|
|
183
183
|
|---|---|---|
|
package/index.json
CHANGED
|
@@ -166,27 +166,27 @@
|
|
|
166
166
|
}
|
|
167
167
|
]
|
|
168
168
|
},
|
|
169
|
-
"specPackageVersion": "0.
|
|
169
|
+
"specPackageVersion": "0.14.0",
|
|
170
170
|
"integrity": {
|
|
171
171
|
"algorithm": "sha256",
|
|
172
172
|
"files": {
|
|
173
|
-
"CHANGELOG.md": "
|
|
174
|
-
"README.md": "
|
|
175
|
-
"architecture.md": "
|
|
176
|
-
"cli-contract.md": "
|
|
177
|
-
"conformance/README.md": "
|
|
173
|
+
"CHANGELOG.md": "98938dd49d4698f899c953afb6fa19c5e031d07f07ce84a177239fe6f78b8788",
|
|
174
|
+
"README.md": "97fd3079092182c677669c25f44e08b0f6579faaa3406d8cb5a884e37e9eef97",
|
|
175
|
+
"architecture.md": "c14e69faa7ce7f657d6a2790762daaea8a5ff350375de8c254cd870b5494b896",
|
|
176
|
+
"cli-contract.md": "b1bcc891e9d645afe06daeb5f6b54025cae46e090628c926ab112e1e9e641ff7",
|
|
177
|
+
"conformance/README.md": "5f94a6ac637b7c992fcd7e53d32eed1b8887eeef05eb6ca3b5ec8a0b5045cd21",
|
|
178
178
|
"conformance/cases/kernel-empty-boot.json": "ad4bbe9d637537625025c8bdb61285b1433568a2544b1ce0248f304ccff8c350",
|
|
179
179
|
"conformance/cases/plugin-missing-ui-rejected.json": "c6ce8f62a430d662aea33dec8ebf6493be6455037be3114e0d93d0eb57777287",
|
|
180
|
-
"conformance/coverage.md": "
|
|
180
|
+
"conformance/coverage.md": "35bac01af3a922ce580deb1f83fc890c668d3d3cf93747f5097340a09303eb43",
|
|
181
181
|
"conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/plugin.json": "4d78af6f12faa9d131e2a19f1dbb8f250baacc525978f3a8c858932b95da4ff6",
|
|
182
182
|
"conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/provider.js": "da40b134d70f8bc8175cfa9c380ecb55d26b2240c8b467f22f3fcfab750c8747",
|
|
183
183
|
"conformance/fixtures/plugin-missing-ui/notes/example.md": "55767f0aa1b6774546a99f28c58e7b732aa9cfa5dfce8d0326470f7f622f577e",
|
|
184
184
|
"conformance/fixtures/preamble-v1.txt": "1e0aeef224b64477bdc13a949c3ad402e68249caf499ecdba1302371677c068b",
|
|
185
|
-
"db-schema.md": "
|
|
185
|
+
"db-schema.md": "730b423d844d7b10aecaec59a78f254b600f0e16b9300305c49aa8c01d03d68f",
|
|
186
186
|
"interfaces/security-scanner.md": "4a982667008f233656f44c61ce9948e062432d3debdcbf7a134da03bd4139d7d",
|
|
187
187
|
"job-events.md": "8f371e0991816eca2e1a55cbd8a50733546ca5e7c861588048c18be1d22dbd57",
|
|
188
188
|
"job-lifecycle.md": "12bfc27690c92cf93682a3b6fbfeb7e2d252d33f704fd2d7de9a13db713e6281",
|
|
189
|
-
"plugin-author-guide.md": "
|
|
189
|
+
"plugin-author-guide.md": "ada6af03cc26439fd890efc9fa6939d1757f4625ea88f4be6f5e07497244f42e",
|
|
190
190
|
"plugin-kv-api.md": "04b2178f46fb88adeae9240df9c9e1761b660396072001dac32cd402e11a2d7d",
|
|
191
191
|
"prompt-preamble.md": "fb40ab510234383326f198dec82cd6d744f28b7432eebac6cbfbb7ca1c483b7d",
|
|
192
192
|
"schemas/api/rest-envelope.schema.json": "7d9d74bcb2158019cb6e30306d40b9c7ffc67e9d202fb8210fe4e4a9e8fa4dab",
|
package/package.json
CHANGED
package/plugin-author-guide.md
CHANGED
|
@@ -264,7 +264,7 @@ Pure single-node analysis. **Never** read another node, the graph, or the databa
|
|
|
264
264
|
The runtime method is `extract(ctx) → void`. Output flows through three callbacks the kernel binds onto the context:
|
|
265
265
|
|
|
266
266
|
- **`ctx.emitLink(link)`** — append a `Link` to the kernel's `links` table. The kernel validates against the extractor's declared `emitsLinkKinds` before persistence; off-contract kinds are dropped and surface as `extension.error` events. URL-shaped targets are partitioned into `node.externalRefsCount` and never persisted.
|
|
267
|
-
- **`ctx.enrichNode(partial)`** — merge canonical, kernel-curated properties onto the node's enrichment layer (persisted into `node_enrichments` per `db-schema.md`). **Strictly separate from the author-supplied frontmatter** — the latter is IMMUTABLE from any Extractor. Use the enrichment layer for facts the author did not write but the
|
|
267
|
+
- **`ctx.enrichNode(partial)`** — merge canonical, kernel-curated properties onto the node's enrichment layer (persisted into `node_enrichments` per `db-schema.md`). **Strictly separate from the author-supplied frontmatter** — the latter is IMMUTABLE from any Extractor. Use the enrichment layer for facts the author did not write but the Extractor inferred (computed titles, summaries, signals from probabilistic Extractors). Probabilistic enrichments track `body_hash_at_enrichment`; when the scan loop sees a body change, those rows are flagged `stale = 1` (NOT deleted, preserving the LLM cost paid to produce them) and surface for refresh via `sm refresh <node>` or `sm refresh --stale`. Deterministic enrichments are simply overwritten via PRIMARY KEY conflict on the next re-extract through the A.9 cache and are never stale-flagged.
|
|
268
268
|
- **`ctx.store`** — plugin-scoped persistence. Optional, only present when your `plugin.json` declares `storage.mode`. Shape depends on the mode (`KvStore` for mode A, scoped `Database` for mode B). See [`plugin-kv-api.md`](./plugin-kv-api.md).
|
|
269
269
|
|
|
270
270
|
A probabilistic extractor additionally receives `ctx.runner` (the `RunnerPort`) for LLM dispatch.
|
|
@@ -300,7 +300,6 @@ export default {
|
|
|
300
300
|
};
|
|
301
301
|
```
|
|
302
302
|
|
|
303
|
-
> **Migration note (spec 0.8.x).** This kind was previously named `Detector` with a `detect(ctx) → Link[]` signature. The rename to `Extractor` and the move to callback-based output landed as a single breaking minor in the pre-1.0 line. The mechanical migration: rename `kind: 'detector'` → `kind: 'extractor'`, rename `detect` → `extract`, replace `return links` with `for (const l of links) ctx.emitLink(l)`. The `applicableKinds`, `emitsLinkKinds`, `defaultConfidence`, and `scope` fields are unchanged.
|
|
304
303
|
|
|
305
304
|
### Rules
|
|
306
305
|
|
|
@@ -407,11 +406,15 @@ These ship later in the v1.x line as bundled built-ins; the spec already pins th
|
|
|
407
406
|
|
|
408
407
|
#### Provider — `kinds` catalog and `explorationDir`
|
|
409
408
|
|
|
410
|
-
Every Provider declares two required fields beyond the manifest base
|
|
409
|
+
Every Provider declares two required top-level fields beyond the manifest base: `kinds` and `explorationDir`.
|
|
411
410
|
|
|
412
|
-
**`kinds` catalog.** Maps each kind the Provider emits to its frontmatter schema
|
|
411
|
+
**`kinds` catalog.** Maps each kind the Provider emits to its frontmatter schema, its qualified `defaultRefreshAction`, and its `ui` presentation block. The kernel derives the supported kind set from `Object.keys(kinds)`. Each entry has three required fields:
|
|
413
412
|
|
|
414
|
-
**`
|
|
413
|
+
- **`schema`** — path (relative to the Provider package) to the kind's frontmatter JSON Schema. MUST extend [`schemas/frontmatter/base.schema.json`](./schemas/frontmatter/base.schema.json) via `allOf` + `$ref` to base's `$id`.
|
|
414
|
+
- **`defaultRefreshAction`** — qualified action id (`<plugin-id>/<action-id>`) the UI's `🧠 prob` button dispatches. The action MUST exist in the registry; a dangling reference disables the Provider with `invalid-manifest`.
|
|
415
|
+
- **`ui`** — presentation block: `{ label, color, colorDark?, emoji?, icon? }`. The UI ships every `ui` block to the front-end via the `kindRegistry` envelope so built-in and user-plugin kinds render identically. `icon` is a discriminated union (`{ kind: 'pi'; id }` for PrimeIcons, `{ kind: 'svg'; path }` for raw SVG). The `ui` block is required (not optional) so the UI never has to invent visuals for unknown kinds. See [`architecture.md` §Provider · `ui` presentation](./architecture.md#provider--ui-presentation) for the field-by-field contract.
|
|
416
|
+
|
|
417
|
+
**`explorationDir`.** Filesystem directory the kernel walks at boot/scan time to discover candidate files. `sm doctor` checks the resolved path exists and emits a non-blocking warning when it does not — the user may legitimately install the matching platform later. Bare `~` and `~/...` resolve against the current user's home (shell convention); relative paths fall back to the cwd.
|
|
415
418
|
|
|
416
419
|
```jsonc
|
|
417
420
|
{
|
|
@@ -422,20 +425,27 @@ Every Provider declares two required fields beyond the manifest base.
|
|
|
422
425
|
"kinds": {
|
|
423
426
|
"skill": {
|
|
424
427
|
"schema": "./schemas/skill.schema.json",
|
|
425
|
-
"defaultRefreshAction": "cursor/summarize-skill"
|
|
428
|
+
"defaultRefreshAction": "cursor/summarize-skill",
|
|
429
|
+
"ui": {
|
|
430
|
+
"label": "Skill",
|
|
431
|
+
"color": "#7c3aed",
|
|
432
|
+
"colorDark": "#a78bfa",
|
|
433
|
+
"icon": { "kind": "pi", "id": "pi-bolt" }
|
|
434
|
+
}
|
|
426
435
|
},
|
|
427
436
|
"command": {
|
|
428
437
|
"schema": "./schemas/command.schema.json",
|
|
429
|
-
"defaultRefreshAction": "cursor/summarize-command"
|
|
438
|
+
"defaultRefreshAction": "cursor/summarize-command",
|
|
439
|
+
"ui": {
|
|
440
|
+
"label": "Command",
|
|
441
|
+
"color": "#0ea5e9",
|
|
442
|
+
"icon": { "kind": "svg", "path": "M3 6h18M3 12h18M3 18h18" }
|
|
443
|
+
}
|
|
430
444
|
}
|
|
431
445
|
}
|
|
432
446
|
}
|
|
433
447
|
```
|
|
434
448
|
|
|
435
|
-
Bare `~` and `~/...` prefixes in `explorationDir` resolve against the current user's home (the same convention the shell applies); relative paths fall back to the cwd. Keep `explorationDir` short and platform-canonical; the doctor warning is the only place the user sees the field, so misleading values create confusion later.
|
|
436
|
-
|
|
437
|
-
> **Migration note (spec 0.8.x).** Pre-0.8 Providers declared two separate fields, `emits: string[]` and a flat `defaultRefreshAction: { <kind>: actionId }`. Both collapsed into the `kinds` map in 0.8.0 (Phase 3 of plug-in model overhaul); the per-kind frontmatter schema (which previously lived under `spec/schemas/frontmatter/<kind>.schema.json`) joined the same map entry. Migration: drop `emits` (replaced by `Object.keys(kinds)`); move each `defaultRefreshAction[<kind>]` value into `kinds[<kind>].defaultRefreshAction`; ship your per-kind schemas inside the plugin package and reference them via `kinds[<kind>].schema`.
|
|
438
|
-
|
|
439
449
|
---
|
|
440
450
|
|
|
441
451
|
## Frontmatter validation — three-tier model
|