@skill-map/spec 0.40.0 → 0.42.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 CHANGED
@@ -1,5 +1,41 @@
1
1
  # Spec changelog
2
2
 
3
+ ## 0.42.0
4
+
5
+ ### Minor Changes
6
+
7
+ - `sm tutorial` now materializes the walkthrough skill into the chosen agent's territory instead of always `.claude/skills/`. Providers declare an optional `scaffold` block (`skillDir` plus display-only `aka` names); the destination comes from `--for <provider>` or a prompt defaulting to Claude. It now also requires an empty cwd, seeding a self-contained scenario the tester can later delete wholesale, so a non-empty directory is refused (exit 2) unless `--force` is passed.
8
+
9
+ ## User-facing
10
+
11
+ `sm tutorial` can now target other agents: `--for agent-skills` (open-standard layout, used by Antigravity and OpenAI Codex) or `--for claude` (default). It now requires an empty directory: run it in a fresh folder, or pass `--force` to seed into the current one.
12
+
13
+ ## 0.41.0
14
+
15
+ ### Minor Changes
16
+
17
+ - Reserved-name detection gains a lens scope: when a Provider is the active lens, its `reservedNames` catalog also applies to the `agent-skills` skill nodes its runtime consumes, matched by kind. This activates Google Antigravity's catalog, refreshed from `agy /help` (v1.0.3) and now declared under `skill`, so a `.agents/skills/<name>` skill shadowing a built-in like `/goal` is flagged by `core/name-reserved` under the antigravity lens. Claude is unchanged.
18
+
19
+ ## User-facing
20
+
21
+ Under the Antigravity lens, `sm scan` now warns when a `.agents/skills` skill shadows a built-in `agy` slash command (e.g. a skill named `goal` collides with `/goal`), so you can rename it before the runtime silently ignores the file.
22
+
23
+ - Add opt-in, anonymous error reporting (Sentry) across the CLI, BFF, and UI, OFF by default. Consent lives in `~/.skill-map/settings.json` (`telemetry.errorsEnabled`), surfaced through `GET/PATCH /api/preferences` and a new Settings Privacy toggle; `SKILL_MAP_TELEMETRY=0` force-disables every surface. A pure, deny-by-default scrubber strips home paths and host identity from every event before it leaves the machine. The normative contract is `spec/telemetry.md`.
24
+
25
+ ## User-facing
26
+
27
+ skill-map can now report crashes anonymously to help fix bugs, and it is OFF by default. Turn it on or off in Settings, or set `SKILL_MAP_TELEMETRY=0` to force it off. File contents, paths, and your settings are never sent.
28
+
29
+ - Add opt-in, anonymous usage analytics (PostHog) for the CLI and UI, OFF by default. Three independent toggles in `~/.skill-map/settings.json` (`telemetry.usageCliEnabled`, `usageUiEnabled`, alongside `errorsEnabled`); one shared first-run prompt consents to all and mints an anonymous install id used as the PostHog `distinct_id`, exposed read-only via `GET/PATCH /api/preferences`. `SKILL_MAP_TELEMETRY=0` force-disables every surface. Contract: `spec/telemetry.md`.
30
+
31
+ ## User-facing
32
+
33
+ skill-map can now share anonymous usage (which commands and views you use) to guide development, OFF by default. Toggle CLI usage, UI usage, and error reports independently in Settings, or set `SKILL_MAP_TELEMETRY=0` to force all off. Files and paths are never sent.
34
+
35
+ ### Patch Changes
36
+
37
+ - Sync the plugin author guide and architecture spec to the structure-as-truth manifest model (`annotation` singular, `ui` view map, on-disk Provider kinds, `precondition` filter, deterministic-only hooks); the guide now delegates instead of duplicating. Fix stale field names and the slot count (14) across architecture.md, db-schema.md and the conformance coverage, and fold the architecture diagram into architecture.md, dropping the generated CLI-reference mirror for `sm help --format md`.
38
+
3
39
  ## 0.40.0
4
40
 
5
41
  ### Minor Changes
package/README.md CHANGED
@@ -60,8 +60,9 @@ spec/ ← published as @skill-map/spec
60
60
  ├── [db-schema.md](./db-schema.md) ← table catalog (kernel-owned)
61
61
  ├── [plugin-kv-api.md](./plugin-kv-api.md) ← ctx.store contract for storage mode A
62
62
  ├── [job-lifecycle.md](./job-lifecycle.md) ← queued → running → completed | failed
63
+ ├── [telemetry.md](./telemetry.md) ← opt-in error reporting (default OFF)
63
64
 
64
- ├── schemas/ ← 29 JSON Schemas, draft 2020-12, camelCase keys
65
+ ├── schemas/ ← JSON Schemas, draft 2020-12, camelCase keys (authoritative list + sha256 in index.json)
65
66
  │ ├── node.schema.json ┐
66
67
  │ ├── link.schema.json │
67
68
  │ ├── issue.schema.json │
@@ -74,13 +75,9 @@ spec/ ← published as @skill-map/spec
74
75
  │ ├── conformance-case.schema.json │
75
76
  │ ├── history-stats.schema.json ┘
76
77
  │ │
77
- │ ├── extensions/ ← one per extension kind; validated at plugin load
78
- │ │ ├── base.schema.json ┐
79
- │ │ ├── provider.schema.json
80
- │ │ ├── extractor.schema.json │ 6 extension schemas
81
- │ │ ├── analyzer.schema.json │ (base + 5 kinds)
82
- │ │ ├── action.schema.json │
83
- │ │ └── formatter.schema.json ┘
78
+ │ ├── extensions/ ← base + one per kind (provider, extractor,
79
+ │ │ analyzer, action, formatter, hook) +
80
+ │ │ provider-kind.schema.json; validated at plugin load
84
81
  │ │
85
82
  │ ├── frontmatter/ ← user-authored; additionalProperties: true
86
83
  │ │ └── base.schema.json ← universal shape; per-kind schemas live with
@@ -137,7 +134,7 @@ npm i @skill-map/spec
137
134
  import specIndex from '@skill-map/spec';
138
135
  import nodeSchema from '@skill-map/spec/schemas/node.schema.json' with { type: 'json' };
139
136
 
140
- console.log(specIndex.specPackageVersion); // → "0.2.0" (npm package version; source of truth for `spec` in `sm version`)
137
+ console.log(specIndex.specPackageVersion); // npm package version; source of truth for `spec` in `sm version`
141
138
  console.log(specIndex.indexPayloadVersion); // → "0.0.1" (payload shape of `index.json` itself; bumps only when this manifest's structure changes)
142
139
  console.log(specIndex.integrity.algorithm); // → "sha256"
143
140
  console.log(nodeSchema.$id); // → "https://skill-map.ai/spec/v0/node.schema.json"
package/architecture.md CHANGED
@@ -8,30 +8,57 @@ Any conforming implementation, reference or third-party, MUST respect these boun
8
8
 
9
9
  ## Layering
10
10
 
11
+ ```mermaid
12
+ flowchart TB
13
+ subgraph DRIVERS["Driving adapters (primary)"]
14
+ direction LR
15
+ CLI["CLI<br/><i>sm command</i>"]
16
+ SERVER["Server<br/><i>Hono BFF (src/server/)</i>"]
17
+ SKILL["Skill<br/><i>agent / IDE</i>"]
18
+ end
19
+
20
+ UI["UI · Angular SPA<br/><i>(ui/)</i>"]:::ui
21
+ UI -.->|"HTTP / WS"| SERVER
22
+
23
+ subgraph KERNEL["Kernel (domain-pure, hexagonal)"]
24
+ direction LR
25
+ REG["Registry"]
26
+ ORCH["Orchestrator"]
27
+ UC["Use cases<br/><i>scan · refresh · action · watch</i>"]
28
+ CONFIG["Config layering<br/><i>defaults → project → project-local → override</i>"]
29
+ end
30
+
31
+ CLI ==>|"ports"| KERNEL
32
+ SERVER ==>|"ports"| KERNEL
33
+ SKILL ==>|"ports"| KERNEL
34
+
35
+ subgraph DRIVEN["Driven adapters (secondary)"]
36
+ direction LR
37
+ STORAGE["Storage<br/><i>SQLite</i>"]
38
+ FS["FS<br/><i>walker · watcher (chokidar)</i>"]
39
+ subgraph PLUGINS["Plugins (closed catalog, 6 kinds)"]
40
+ direction TB
41
+ EXT["extractors"]
42
+ ANA["analyzers"]
43
+ ACT["actions"]
44
+ HOOK["hooks"]
45
+ FMT["formatters"]
46
+ PROV["providers"]
47
+ end
48
+ end
49
+
50
+ KERNEL ==> STORAGE
51
+ KERNEL ==> FS
52
+ KERNEL ==> PLUGINS
53
+
54
+ classDef ui fill:#bac8ff,stroke:#3b5bdb,stroke-width:1px,color:#000,stroke-dasharray: 5 3
55
+ class CLI,SERVER,SKILL driver
56
+ class REG,ORCH,UC,CONFIG kernel
57
+ class STORAGE,FS adapter
58
+ class EXT,ANA,ACT,HOOK,FMT,PROV plugin
11
59
  ```
12
- Driving adapters (primary)
13
-
14
- ┌─────────┐ ┌─────────┐ ┌──────┐
15
- │ CLI │ │ Server │ │Skill │
16
- └────┬────┘ └────┬────┘ └───┬──┘
17
- │ │ │
18
- └─────────────────┼────────────────┘
19
-
20
- ┌──────────────┐
21
- │ Kernel │ ← domain core
22
- │ │
23
- │ Registry │
24
- │ Orchestrator│
25
- │ Use cases │
26
- └──┬───┬───┬───┘
27
- │ │ │
28
- ┌─────────────┘ │ └──────────────┐
29
- ▼ ▼ ▼
30
- ┌────────┐ ┌─────────┐ ┌─────────┐
31
- │ Storage│ │ FS │ │ Plugins │
32
- └────────┘ └─────────┘ └─────────┘
33
- Driven adapters (secondary)
34
- ```
60
+
61
+ The UI is **not** a driving adapter; it is an HTTP/WS client of the Server. Exactly one Provider is active per project (see §Active Provider Lens), and config layering is always project-scoped (see §Config layering).
35
62
 
36
63
  - **Driving adapters** call into the kernel. The spec defines three: `CLI`, `Server`, `Skill`. A fourth driving adapter MAY be built by third parties (IDE extension, VSCode command palette, TUI) without spec changes.
37
64
  - **Driven adapters** implement ports the kernel declares. An implementation MUST ship adapters for every port, no port may be left unimplemented at runtime.
@@ -322,13 +349,18 @@ The lookup uses the ACTIVE PROVIDER LENS deliberately, mirroring the extractor g
322
349
 
323
350
  Each Provider MAY declare an optional `reservedNames: Record<kind, string[]>` map listing, for each `node.kind` the runtime owns, the set of invocation names the runtime itself consumes. Anthropic's Claude CLI reserves `/help`, `/clear`, `/init`, `/agents`, `/model`, `/cost`, `/compact`, `/login`, `/logout`, … under `command`, and `general-purpose`, `output-style-setup`, `statusline-setup` under `agent`; a user-authored `.claude/commands/help.md` is silently shadowed at runtime (the built-in runs, the file is ignored).
324
351
 
325
- The kernel intersects each Provider's `reservedNames[kind]` catalog with the scanned graph at orchestrator time: for every node, the post-walk pipeline derives its normalised identifiers via the §Provider · kind identifiers contract and asks "does any identifier fall in `reservedNames[node.kind]` for this node's Provider?". Matches land in a per-scan `Set<nodePath>` consumed by two surfaces:
352
+ The kernel intersects each Provider's `reservedNames[kind]` catalog with the scanned graph at orchestrator time. For every node the post-walk pipeline derives its normalised identifiers via the §Provider · kind identifiers contract, then tests them against a reserved set resolved under **two scopes**:
353
+
354
+ 1. **Self scope.** `reservedNames[node.kind]` of the node's OWN Provider (`node.provider`). The self-contained case: Claude classifies `.claude/commands/help.md` as `claude`/`command` and reserves `help` under `command`, so the file is flagged.
355
+ 2. **Lens scope.** When a Provider is the active lens (`activeProvider === provider.id`) it ALSO lends its catalog to nodes that a *universal* (ungated) Provider classified, matched by `node.kind`. This is required by runtimes that adopt the open `.agents/skills/` standard instead of a vendor directory: their user invocables are owned by the neutral `agent-skills` Provider (`kind: skill`), not by the vendor Provider, so self scope alone would never reach them. Google's Antigravity is exactly this shape, it is metadata-only (classifies nothing) and reserves its `agy` built-in slash commands under `skill`; when `activeProvider === 'antigravity'`, a user `.agents/skills/goal/SKILL.md` is flagged because `/goal` is a built-in. Lens scope is skipped when `node.provider === activeProvider` (it would duplicate self scope) and when no lens is resolved (`activeProvider === null`).
356
+
357
+ A node's identifiers are always derived from its OWN kind contract; only the reserved-set lookup widens under the lens. A node landing in either scope's set joins a per-scan `Set<nodePath>` consumed by two surfaces:
326
358
 
327
359
  1. **The `core/name-reserved` analyzer projects one `warn` issue per reserved-shadow node** (`severity: 'warn'`, message points at the offending file and suggests renaming). The analyzer is a pure projector, detection lives in the orchestrator so the same set drives the next surface.
328
360
 
329
361
  2. **The post-walk confidence-lift transform downgrades any link that resolves to a reserved target** (by path OR by name match) to `RESERVED_TARGET_CONFIDENCE = 0.1` instead of bumping it to `1.0`. The visual weight in the graph drops well below the `0.5` / `0.8` extractor emit floors so the operator sees at a glance that the edge resolves to a file the runtime ignores. When the trigger has multiple candidates (name index collision) and the strict-kind filter accepts more than one, the resolver picks the first allowed candidate, if it is non-reserved, the link bumps to `1.0` normally; only when EVERY accepted candidate is reserved does the downgrade apply.
330
362
 
331
- The lookup normalises both sides through the §Extractor · trigger normalization pipeline, so a literal `Init-Project` in the manifest still matches a user `name: init project` or filename `Init-Project.md`. The catalog is intentionally per-kind, not global: a name reserved for commands (`/help`) MAY legitimately appear as a skill (a "help" skill triggered through a non-command channel).
363
+ The lookup normalises both sides through the §Extractor · trigger normalization pipeline, so a literal `Init-Project` in the manifest still matches a user `name: init project` or filename `Init-Project.md`. The catalog is intentionally per-kind, not global: a name reserved for commands (`/help`) MAY legitimately appear as a skill (a "help" skill triggered through a non-command channel). Lens scope respects the same per-kind boundary: Antigravity declares its reserved names under `skill` (not `command`) precisely because the invocable they shadow is a skill file, so only `skill`-kind nodes are tested against it.
332
364
 
333
365
  **Update policy.** Built-in catalogs drift as vendor runtimes evolve. Each catalog change ships as a kernel patch with a changeset entry; the catalog is considered API surface that users rely on the analyzer to reflect. User-installed Providers MAY declare their own `reservedNames` with the same shape; the analyzer and the downgrade run uniformly across built-in and user-installed Providers.
334
366
 
@@ -338,7 +370,7 @@ Default `undefined` ≡ empty map ≡ no reserved names. Path matches against no
338
370
 
339
371
  The `Extractor` runtime contract is `extract(ctx) → void`. The extractor emits its work through three callbacks the kernel binds onto `ctx`:
340
372
 
341
- - `ctx.emitLink(link)`, append a `Link` to the kernel's `links` table. The kernel validates the link against the extractor's declared `emitsLinkKinds` before persistence; off-contract links are dropped and surface as `extension.error` events. URL-shaped targets (`http(s)://…`) are partitioned out into `node.externalRefsCount` and never persisted.
373
+ - `ctx.emitLink(link)`, append a `Link` to the kernel's `links` table. The kernel validates `link.kind` against the **global closed enum** of link kinds (`invokes`, `references`, `mentions`, `supersedes`) before persistence; off-enum links are dropped and surface as `extension.error` events (the per-extractor `emitsLinkKinds` allowlist was retired with the structure-as-truth refactor; confidence is declared per emit, default `'medium'`). URL-shaped targets (`http(s)://…`) are partitioned out into `node.externalRefsCount` and never persisted.
342
374
  - `ctx.enrichNode(partial)`, merge canonical, kernel-curated properties onto the current node's enrichment layer (persisted into [`node_enrichments`](./db-schema.md#node_enrichments)). **Strictly separate from the author-supplied frontmatter** (the latter remains immutable across scans). The enrichment layer is the right home for kernel-derived facts (computed titles, summaries, signals an Extractor inferred from the body) without polluting what the user wrote on disk. See §Enrichment layer below for the full lifecycle (per-extractor attribution, refresh verbs).
343
375
  - `ctx.store`, plugin-scoped persistence. Optional, present only when the plugin declares `storage.mode` in `plugin.json`. Shape depends on the mode (`KvStore` for mode A, scoped `Database` for mode B). See [`plugin-kv-api.md`](./plugin-kv-api.md). The plugin author MAY opt into shape validation for their own writes by declaring `storage.schema` (Mode A) or `storage.schemas` (Mode B) in the manifest, JSON Schemas the kernel AJV-compiles at load time and runs against every `ctx.store.set(key, value)` / `ctx.store.write(table, row)` call. Absent = permissive (status quo). `emitLink` and `enrichNode` keep their universal validation against `link.schema.json` / `node.schema.json` regardless of this opt-in. See [`plugin-author-guide.md` §`outputSchema`](./plugin-author-guide.md#outputschema--opt-in-correctness-for-custom-storage-writes).
344
376
 
@@ -380,9 +412,9 @@ Analyzers / `sm check` / `sm export` consume `node.frontmatter` directly (determ
380
412
 
381
413
  Refresh verbs (`sm refresh <node>` and `sm refresh --stale`) re-run the Extractor pipeline against a node or the stale set and upsert fresh enrichment rows, see [`cli-contract.md` §Scan](./cli-contract.md#scan). With Extractors deterministic-only, `--stale` is a no-op today (no rows are stale-flagged); it remains in the contract for the future Action-prob enrichment revision noted above.
382
414
 
383
- ### Extractor · `applicableKinds` filter
415
+ ### Extractor · `precondition` filter
384
416
 
385
- Extractors MAY declare an optional `applicableKinds: string[]` on their manifest. When declared, the kernel filters fail-fast: `extract()` is invoked **only** for nodes whose `kind` appears in the list. The skip happens BEFORE the extractor context is built so the extractor wastes zero CPU on inapplicable nodes. Absent (`undefined`) is the default and means "applies to every kind"; there is no wildcard syntax. An empty array (`[]`) is invalid (`minItems: 1` in the schema). Unknown kinds (no installed Provider declares them in its `kinds` catalog) are non-blocking: the extractor keeps `loaded` status and `sm plugins doctor` surfaces an informational warning so the author sees typos and missing-Provider cases, but the doctor's exit code is NOT promoted by this warning. See [`plugin-author-guide.md` §Extractor `applicableKinds`](./plugin-author-guide.md#extractor-applicablekinds--narrow-the-pipeline) for the full author-side contract.
417
+ Extractors MAY declare an optional `precondition` block (`{ kind?: string[]; provider?: string[] }`, the same shape Analyzers and Actions share). When declared, the kernel filters fail-fast: `extract()` is invoked **only** for nodes that satisfy every declared sub-filter (`kind` lists qualified `<plugin>/<kindName>` ids; `provider` lists plugin ids; both apply as AND). The skip happens BEFORE the extractor context is built so the extractor wastes zero CPU on inapplicable nodes. Absent (`undefined`) is the default and means "applies to every kind"; there is no wildcard syntax. Unknown qualified kinds (no installed Provider declares them) are non-blocking: the extractor keeps `loaded` status and `sm plugins doctor` surfaces an informational `precondition-kind-unknown` warning so the author sees typos and missing-Provider cases, but the doctor's exit code is NOT promoted by this warning. See [`plugin-author-guide.md` §`precondition`](./plugin-author-guide.md#extractor--analyzer--action-precondition-narrow-the-pipeline) for the full author-side contract.
386
418
 
387
419
  ### Extractor · fine-grained scan cache
388
420
 
@@ -433,16 +465,11 @@ Characters outside the separator set that are not letters or digits (e.g. `/`, `
433
465
  | `@FooExtractor` | `@fooextractor` |
434
466
  | `skill-map:explore` | `skill map:explore` |
435
467
 
436
- ### Analyzer · `recommendedActions` hint
437
-
438
- An Analyzer MAY declare `recommendedActions: string[]` in its manifest, listing the qualified ids (`<plugin-id>/<extension-id>`) of the per-node Actions that resolve its findings. The UI surfaces matching Actions in the node inspector under "Recommended for issues" whenever the analyzer emitted against the focused node, alongside the always-applicable list driven by the Action's own precondition (see [`schemas/extensions/action.schema.json`](./schemas/extensions/action.schema.json)).
439
-
440
- The two surfaces are deliberately split:
468
+ ### Analyzer Action relationship (Modelo B)
441
469
 
442
- - **`Action.precondition`**, declared on the Action side. Answers "which nodes does this Action apply to?". Evaluated continuously against the node the inspector is focused on, regardless of any issue.
443
- - **`Analyzer.recommendedActions`**, declared on the Analyzer side. Answers "when this analyzer fires, which Actions are the natural fix?". Surfaces only on nodes the analyzer emitted against.
470
+ The "which Action resolves this analyzer's findings?" relationship is declared from the **Action** side, not the Analyzer side (the `Analyzer.recommendedActions` map was retired with the structure-as-truth refactor). An Action's `precondition.analyzerIds: string[]` lists the qualified ids of the analyzers whose findings it is intended to resolve. The UI joins on this field: when an analyzer emitted against the focused node, the inspector surfaces every Action whose `precondition.analyzerIds` includes that analyzer, under "Recommended for issues", alongside the always-applicable list driven by the rest of the Action's `precondition`.
444
471
 
445
- Each `recommendedActions` entry MUST be the qualified id of a registered Action. The kernel logs an `extension.error` event with `kind: 'recommended-action-missing'` when a referenced action is not loaded; the analyzer stays registered and continues emitting issues, only the recommendation hint is dropped. Project-level cleanup verbs (orphan file prune, contribution relink) are CLI commands, not Actions, and are NOT linked through this field. Analyzers whose issues surface deliberate user declarations rather than fixable problems (e.g. `core/node-superseded`) omit the field.
472
+ The two surfaces stay distinct: the `kind` / `provider` sub-filters answer "which nodes does this Action apply to?" (evaluated continuously against the focused node); `analyzerIds` answers "when which analyzer fires is this Action the natural fix?" (surfaces only on nodes the named analyzer emitted against). Project-level cleanup verbs (orphan file prune, contribution relink) are CLI commands, not Actions, and are NOT linked through this field. Actions resolving deliberate user declarations rather than fixable problems omit `analyzerIds`.
446
473
 
447
474
  ### Hook · curated trigger set
448
475
 
@@ -457,7 +484,8 @@ Hooks subscribe declaratively to a curated set of kernel lifecycle events and re
457
484
  | `analyzer.completed` | Once per Analyzer, after every issue has been validated. | `analyzerId: string` (qualified). | Per-Analyzer alerting, downstream tooling. |
458
485
  | `action.completed` | Once per Action invocation, after the report has been recorded. | `actionId: string` (qualified), `node`, `jobResult`. | Per-Action notification, integration glue. |
459
486
  | `job.spawning` | Pre-spawn of a runner subprocess (job subsystem; Step 10). | `jobId`, `actionId`, spawn metadata. | Pre-flight checks, audit logging. |
460
- | `job.spawning`, `job.completed`, `job.failed` | The three job-lifecycle hookables; same payload shapes as the [`job-events.md`](./job-events.md) entries of the same name. | See [`job-events.md` §Event catalog](./job-events.md#event-catalog). | Most common Hook surface (notifications, retries, billing). |
487
+ | `job.completed` | Once per job that finishes successfully (job subsystem; Step 10). Same payload shape as the [`job-events.md`](./job-events.md) entry of the same name. | See [`job-events.md` §Event catalog](./job-events.md#event-catalog). | Most common Hook surface (notifications, retries, billing). |
488
+ | `job.failed` | Once per job that fails (job subsystem; Step 10). Same payload shape as the [`job-events.md`](./job-events.md) entry of the same name. | See [`job-events.md` §Event catalog](./job-events.md#event-catalog). | Alerting, retry triggers. |
461
489
  | `shutdown` | Once per CLI process invocation, AFTER the verb returns its exit code and BEFORE `process.exit`. The dispatcher awaits subscribed hooks so they finish before the process terminates, but every hook MUST be fast (the user already saw the verb's output and is waiting for the prompt back). The dispatcher catches every hook error so a buggy hook never alters the verb's exit code; it can only delay the exit. | `exitCode: number` (the verb's resolved exit code, `0..5`). | Cleanup, post-run telemetry, the `core/update-check` banner. |
462
490
 
463
491
  A hook MAY narrow further with an optional declarative `filter` map: keys are payload field paths (top-level only in v0.x); values are the literal expected match. The dispatcher walks `event.data` for each declared key and short-circuits the invocation when any value disagrees. Examples:
@@ -638,9 +666,9 @@ The flag lives in `project-local` (gitignored) so each collaborator consents ind
638
666
 
639
667
  ### Plugin contributions
640
668
 
641
- Plugins extend the annotation surface via the `annotationContributions` manifest field, a map of contributed key → `{ schema, ownership, location }`. Inline JSON Schema (no `$ref` to external files). Two location modes:
669
+ Plugins extend the annotation surface via the optional `annotation` block on an extension manifest (`{ schema, ownership?, location? }`, inline JSON Schema, no `$ref` to external files). It is a **single** declaration per extension and **the contributed key is the extension's id** (its folder name); an extension that needs several keys splits into several extensions, one per key. Two location modes:
642
670
 
643
- - `location: 'namespaced'` (default), writes go to the plugin's `<plugin-id>:` block at the sidecar root. Default `ownership: 'shared'`. Plugins write to their own namespace without coordination; AJV validates contributed keys against the plugin's declared schema.
671
+ - `location: 'namespaced'` (default), writes go to the plugin's `<plugin-id>:` block at the sidecar root. Default `ownership: 'shared'`. Plugins write to their own namespace without coordination; AJV validates the contributed value against the extension's declared schema.
644
672
  - `location: 'root'`, writes go to a top-level key of the sidecar (alongside `identity` / `annotations` / `settings` / `audit`). Requires `ownership: 'exclusive'` (claiming a root key is elevated trust). Two plugins claiming the same root key with `exclusive` is a **hard fatal** at orchestrator startup, the kernel refuses to boot rather than route writes ambiguously.
645
673
 
646
674
  The kernel exposes a runtime catalog (`Kernel.getRegisteredAnnotationKeys()`) listing every plugin-contributed key with its `pluginId`, `location`, `ownership`, and `schema`, consumed by the BFF (`GET /api/annotations/registered`) for UI autocomplete.
@@ -755,7 +783,7 @@ ctx.emitContribution(contributionId, payload);
755
783
  ctx.emitContribution(nodePath, contributionId, payload);
756
784
  ```
757
785
 
758
- Parallel to `ctx.emitLink(link)`. The kernel buffers the emission, validates the payload against the slot's payload schema in `$defs/payloads/<slot>` (AJV-compiled at boot), and persists the row to `scan_contributions` during `persistScanResult`. Off-shape payloads emit an `extension.error` event and drop silently, same posture as `emitLink` rejecting off-`emitsLinkKinds` links. Both Extractor and Analyzer emissions land in the same `scan_contributions` rows; the row's `extension_id` records which kind of extension produced it.
786
+ Parallel to `ctx.emitLink(link)`. The kernel buffers the emission, validates the payload against the slot's payload schema in `$defs/payloads/<slot>` (AJV-compiled at boot), and persists the row to `scan_contributions` during `persistScanResult`. Off-shape payloads emit an `extension.error` event and drop silently, same posture as `emitLink` rejecting off-enum link kinds. Both Extractor and Analyzer emissions land in the same `scan_contributions` rows; the row's `extension_id` records which kind of extension produced it.
759
787
 
760
788
  The Extractor-emit signature binds `nodePath` implicitly (the extractor runs per-node, with `ctx.node.path` available as the only sensible target). The Analyzer-emit signature requires the analyzer to declare the target node explicitly because Analyzers see the full graph at once and may emit for any subset of nodes, the canonical use case is a analyzer that derives per-node values from cross-graph aggregations (`core/link-counter` projects `linksOutCount` / `linksInCount` this way).
761
789
 
package/cli-contract.md CHANGED
@@ -56,8 +56,8 @@ Genuinely per-user, per-machine preferences live in a **single file**
56
56
  at `~/.skill-map/settings.json`, validated against
57
57
  [`user-settings.schema.json`](./schemas/user-settings.schema.json).
58
58
  The file holds preferences that have no project meaning (the update-
59
- check toggle + its throttle bookkeeping today; future locale, theme,
60
- etc.). Constraints:
59
+ check toggle + its throttle bookkeeping, and the telemetry consent
60
+ flag today; future locale, theme, etc.). Constraints:
61
61
 
62
62
  - **One file, no `.local` partner**: values here are already
63
63
  per-machine, so the project / project-local split has no meaning.
@@ -69,13 +69,41 @@ etc.). Constraints:
69
69
  (only preferences whose value is meaningless inside a project).
70
70
  Anything that belongs to a project goes in
71
71
  `<cwd>/.skill-map/settings.json` instead.
72
- - **Closed list of writers**: today only the update-check store
73
- (`src/cli/util/update-check-store.ts` in the reference impl)
74
- reads and writes the file. New user-scope features extend that
75
- module rather than opening new home access points.
72
+ - **Closed list of writers**: a single user-settings store module
73
+ (`src/cli/util/user-settings-store.ts` in the reference impl) is the
74
+ only reader / writer of the file. Every user-scope feature (the
75
+ update-check toggle, the telemetry consent flag) goes through it
76
+ rather than opening new home access points.
76
77
 
77
78
  Everything else under `$HOME` MUST NOT be touched.
78
79
 
80
+ ### Telemetry consent
81
+
82
+ skill-map sends nothing off the machine by default. Opt-in, anonymous
83
+ **error reporting** is the one documented exception, governed in full by
84
+ [`telemetry.md`](./telemetry.md). The operator-facing contract:
85
+
86
+ - **Default OFF.** The `telemetry.errorsEnabled` flag in
87
+ `~/.skill-map/settings.json` is absent until the operator decides. Absent
88
+ or `false` means no telemetry SDK is loaded and nothing is sent, on every
89
+ surface (CLI, BFF, UI), with zero added latency.
90
+ - **Consent prompt (interactive terminals only, second eligible run).** The
91
+ CLI MAY show a one-time consent prompt (yes (default) / no / details),
92
+ but NOT on the operator's first eligible run: that run only records
93
+ `telemetry.firstRunAt` and stays silent so the prompt does not stack on
94
+ the first-`sm scan` provider-lens prompt. The next eligible run shows it,
95
+ persists the choice, and stamps `telemetry.promptedAt` (so it is never
96
+ shown again). When stdout is not a TTY (CI, pipes), nothing is asked or
97
+ recorded and the state stays OFF.
98
+ - **Kill switch.** `SKILL_MAP_TELEMETRY=0` forces OFF everywhere regardless
99
+ of the persisted flag. There is no env value that forces ON.
100
+ - **No `sm config` key.** The flag is per-machine, so it lives in the
101
+ user-settings file, not in project config. `sm config` writes project-local
102
+ settings only and MUST NOT surface this key. Consent is changed after the
103
+ first run through the Settings UI (persisted via the BFF), mirroring how
104
+ the update-check toggle works. A future `sm telemetry` verb
105
+ family MAY expose CLI status / toggling; it is not part of this level.
106
+
79
107
  ### Active provider lens
80
108
 
81
109
  The project sees its filesystem through exactly one **active provider lens** at any time. The lens is persisted as `activeProvider` in `<cwd>/.skill-map/settings.json` (see [`project-config.schema.json`](./schemas/project-config.schema.json#/properties/activeProvider) and the [`architecture.md` §Active Provider Lens](./architecture.md#active-provider-lens) section for the full architectural rationale).
@@ -154,22 +182,30 @@ Exit: 0 on success, 2 on failure.
154
182
 
155
183
  #### `sm tutorial [variant]`
156
184
 
157
- Materialize an interactive tester tutorial as a single `.md` file in the current working directory. Companion to the `sm-tutorial` and `sm-master` Claude Code skills: a tester drops into an empty directory, runs `sm tutorial` (or `sm tutorial master`) to seed the tutorial source, then opens Claude Code there and triggers the skill (which reads the file as its onboarding payload).
185
+ Materialize an interactive tester tutorial as a skill folder under the chosen agent's on-disk territory. Companion to the `sm-tutorial` and `sm-master` skills: a tester drops into an empty directory, runs `sm tutorial` (or `sm tutorial master`) to seed the skill, then opens their agent there and triggers it by speaking one of its trigger phrases (the agent auto-discovers `<skillDir>/<slug>/SKILL.md` on boot).
158
186
 
159
187
  The optional positional `variant` argument selects which skill gets materialised. Valid values are:
160
188
 
161
- - `tutorial` (default, also the behaviour when no argument is passed): writes `<cwd>/sm-tutorial.md`, the basic onboarding walkthrough.
162
- - `master`: writes `<cwd>/sm-master.md`, the advanced walkthrough (plugin tour, plugin authoring, settings + view-slots).
189
+ - `tutorial` (default, also the behaviour when no argument is passed): the basic onboarding walkthrough, slug `sm-tutorial`.
190
+ - `master`: the advanced walkthrough (plugin tour, plugin authoring, settings + view-slots), slug `sm-master`, includes the `references/` sub-folder.
191
+
192
+ The destination directory is the selected Provider's `scaffold.skillDir` (e.g. `.claude/skills` for Claude, `.agents/skills` for the open standard adopted by Antigravity); the verb writes `<cwd>/<skillDir>/<slug>/`. Provider selection:
193
+
194
+ - `--for <provider-id>` selects the destination Provider explicitly (e.g. `--for claude`, `--for agent-skills`). The id MUST be a registered Provider that declares `scaffold.skillDir`; any other value is a usage error.
195
+ - Without `--for`, the default Provider is the first scaffold-capable Provider in catalog order (Claude). The verb requires an empty cwd (see below), so there is no marker to detect: provider auto-detection does not apply.
196
+ - Without `--for`, on an interactive stdin the verb prompts with a numbered list of the Providers that declare `scaffold.skillDir`, marking the default option (Claude); an empty answer accepts it. Each option shows the Provider label plus any `scaffold.aka` agents in parentheses (e.g. the open standard lists Antigravity and OpenAI Codex). The `aka` strings are display-only and are NOT accepted by `--for`.
197
+ - Without `--for`, on a non-interactive stdin (pipes, CI) the verb selects the default Provider without prompting, so the verb stays scriptable.
163
198
 
164
199
  Common behaviour for both variants:
165
200
 
166
- - Writes the chosen file at the top level (single file, no subdirectory).
167
- - Content is the canonical `SKILL.md` shipped with the implementation. Any conforming implementation MUST embed equivalent tutorial sources (the prose itself is informative; what is normative is that the verb produces a single readable file at the chosen path that a Claude Code skill can consume).
168
- - Does NOT require an initialized project, runs in any directory, including empty ones, and never reads or writes `.skill-map/`.
201
+ - Writes the full skill folder (`SKILL.md` plus any `references/` sub-folder) under the resolved `<skillDir>/<slug>/`.
202
+ - Content is the canonical skill shipped with the implementation. The `SKILL.md` payload is host-agnostic; only the destination directory varies per Provider. Any conforming implementation MUST embed equivalent tutorial sources (the prose itself is informative; what is normative is that the verb produces a readable skill folder a compatible agent can consume).
203
+ - Requires the cwd to be empty (a directory listing including dotfiles returns nothing). The tutorial seeds a self-contained scenario and the skill later lays its fixtures and `.skill-map/` directly in the cwd, so the tester can delete the whole directory afterwards without losing prior work; that guarantee only holds when the directory started empty. A non-empty cwd is refused (exit 2) unless `--force` is passed.
204
+ - Does NOT require an initialized project and never reads or writes `.skill-map/`. It is a pre-bootstrap helper: Provider selection reads the built-in Provider catalog directly, not project config.
169
205
 
170
- Flags: `--force` (overwrite the existing target file, whichever variant was selected, without prompting).
206
+ Flags: `--for <provider-id>` (destination Provider, skips the prompt); `--force` (proceed even when the cwd is not empty, overwriting any existing target folder, without prompting).
171
207
 
172
- Exit: `0` on success; `2` if the target file already exists and `--force` was not passed (operational error, refusing to clobber); `2` if the positional `variant` is set to a value other than `tutorial` or `master`; `2` on any I/O failure.
208
+ Exit: `0` on success; `2` if the cwd is not empty and `--force` was not passed (operational error, refusing to seed the tutorial into a directory that already holds content); `2` if the positional `variant` is set to a value other than `tutorial` or `master`; `2` if `--for` names a Provider that does not exist or declares no `scaffold.skillDir`; `2` on any I/O failure.
173
209
 
174
210
  #### `sm version`
175
211
 
@@ -204,7 +240,7 @@ Exit: 0 if all green, 1 if warnings, 2 if any `error`-level problem.
204
240
  Self-describing introspection.
205
241
 
206
242
  - `human` (default): pretty terminal output.
207
- - `md`: canonical markdown for documentation sites. Implementations MUST NOT hand-maintain equivalent markdown; `context/cli-reference.md` (in the reference impl) is regenerated from this output in CI.
243
+ - `md`: canonical markdown for documentation sites. Implementations MUST NOT hand-maintain equivalent markdown; it is generated on demand from this output.
208
244
  - `json`: structured surface dump. Shape:
209
245
 
210
246
  ```json
@@ -660,7 +696,7 @@ The `/ws` endpoint is the live-events channel for the SPA. Clients connect once
660
696
  ### Introspection
661
697
 
662
698
  - `sm help --format json`, structured CLI surface dump.
663
- - `sm help --format md`, canonical markdown, CI-enforced for the reference impl's `context/cli-reference.md`.
699
+ - `sm help --format md`, canonical markdown, generated on demand (not a committed artifact).
664
700
 
665
701
  These two formats are NORMATIVE: any change to verbs, flags, or exit codes MUST reflect in `--format json` output immediately. Third-party consumers rely on this.
666
702
 
@@ -744,7 +780,6 @@ The `done in …` stderr line, its format grammar, and the `elapsedMs` field con
744
780
  - [`job-lifecycle.md`](./job-lifecycle.md), state machine behind `sm job` verbs.
745
781
  - [`job-events.md`](./job-events.md), event stream emitted via `--json` and `--stream-output`.
746
782
  - [`db-schema.md`](./db-schema.md), tables behind `sm db` verbs.
747
- - [`../context/cli-reference.md`](../context/cli-reference.md), auto-generated reference from `sm help --format md`.
748
783
  - [`conformance/`](./conformance/README.md), test suite exercising CLI behavior.
749
784
 
750
785
  ---
@@ -38,7 +38,7 @@ This file is hand-maintained. A CI check before spec release compares the schema
38
38
  | 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. |
39
39
  | 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. |
40
40
  | 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. |
41
- | 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
+ | 30 | `view-slots.schema.json` |, | 🔴 missing | Closed catalog of 14 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. |
42
42
  | 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>`. |
43
43
  | 32 | `refresh-report.schema.json` |, | 🔴 missing | Machine-readable output of `sm refresh <node.path> --json` and `sm refresh --stale --json`. Reports the count of enrichment rows persisted across targeted nodes (universal enrichment layer per `architecture.md` §A.8). Direct conformance case pending: seed a fixture with one Provider-classified node, run `sm refresh <node> --json`, assert the envelope validates and `refreshed >= 0`. Implementation tests at `src/test/node-enrichments.test.ts` cover the runtime behaviour today. |
44
44
  | 33 | `plugins-doctor.schema.json` |, | 🔴 missing | Machine-readable output of `sm plugins doctor --json`. Aggregates per-status counts plus structured issue / warning lists. Direct conformance case pending: prime a scope with one healthy + one invalid-manifest drop-in plugin, run `sm plugins doctor --json`, assert the envelope validates and the invalid plugin appears under `issues[]`. Implementation tests at `src/test/plugins-cli.test.ts` cover the runtime behaviour. |
package/db-schema.md CHANGED
@@ -161,7 +161,7 @@ No indexes (single row).
161
161
 
162
162
  Fine-grained cache breadcrumbs for the incremental scan path. One row per `(node_path, extractor_id)` recording the body hash the Extractor saw the last time it ran against that node. Replace-all on every `sm scan` so rows for Extractors that were uninstalled since the last scan disappear automatically.
163
163
 
164
- The orchestrator consults this table on `sm scan --changed`: a node-level cache hit (body+frontmatter unchanged) is upgraded to a full skip ONLY when every currently-registered Extractor (filtered by `applicableKinds`) has a row matching the prior body hash. A new Extractor registered between scans is detected by the absence of its row and runs over the cached node WITHOUT requiring a full cache invalidation. Without this table the cache silently bypassed any Extractor newly registered between scans, leaving its emissions missing on the next `--changed` pass; the same machinery is what a future Action-issued probabilistic enrichment revision will leverage to reuse paid LLM output across unchanged bodies.
164
+ The orchestrator consults this table on `sm scan --changed`: a node-level cache hit (body+frontmatter unchanged) is upgraded to a full skip ONLY when every currently-registered Extractor (filtered by its `precondition`) has a row matching the prior body hash. A new Extractor registered between scans is detected by the absence of its row and runs over the cached node WITHOUT requiring a full cache invalidation. Without this table the cache silently bypassed any Extractor newly registered between scans, leaving its emissions missing on the next `--changed` pass; the same machinery is what a future Action-issued probabilistic enrichment revision will leverage to reuse paid LLM output across unchanged bodies.
165
165
 
166
166
  | Column | Type | Constraint |
167
167
  |---|---|---|
@@ -222,7 +222,7 @@ Phase 3 / View contribution system. Per-node typed payloads emitted by extractor
222
222
  | `plugin_id` | TEXT | NOT NULL | Owning plugin namespace per spec § A.6. |
223
223
  | `extension_id` | TEXT | NOT NULL | Extension id within the plugin. |
224
224
  | `node_path` | TEXT | NOT NULL | FK semantically to `scan_nodes.path`; orphan-swept on persist when the parent node disappears. |
225
- | `contribution_id` | TEXT | NOT NULL | Manifest Record key under `extension.viewContributions[<contributionId>]`. |
225
+ | `contribution_id` | TEXT | NOT NULL | Manifest Record key under `extension.ui[<contributionId>]` (the runtime catalog keeps the historical name `viewContributions`). |
226
226
  | `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. |
227
227
  | `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. |
228
228
  | `emitted_at` | INTEGER | NOT NULL | Unix milliseconds. |
package/index.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "indexPayloadVersion": "0.0.1",
3
3
  "dialect": "https://json-schema.org/draft/2020-12/schema",
4
- "canonicalUrlPrefix": "https://skill-map.ai/spec/v0",
4
+ "canonicalUrlPrefix": "https://skill-map.dev/spec/v0",
5
5
  "schemas": {
6
6
  "topLevel": [
7
7
  {
@@ -174,14 +174,14 @@
174
174
  }
175
175
  ]
176
176
  },
177
- "specPackageVersion": "0.40.0",
177
+ "specPackageVersion": "0.42.0",
178
178
  "integrity": {
179
179
  "algorithm": "sha256",
180
180
  "files": {
181
- "CHANGELOG.md": "bf36e063acb6ee636f39cd461c461a4cb9c2cc20d4097f8d1fffc803d99277df",
182
- "README.md": "936d8e20cd46dc535ab46f19812749df2ee9b81dda3062cda2995bdc292a7a7f",
183
- "architecture.md": "870446e462a0c06162528bbd496a2095f6d7e6aec620427a76648337dd524003",
184
- "cli-contract.md": "aacf84d3314df36f127696b36bc0bdd47111c5c09d57b51d8e3859ea263ec9da",
181
+ "CHANGELOG.md": "66c0b2477b5f26fa1db262ffa587c06dae7786901a4213ccaed6863f296ffd0b",
182
+ "README.md": "c3714c5e935f7d60ef9dad7bd78bb98a6b92ba1835871b0ecfa3318e5b66132d",
183
+ "architecture.md": "7f37e69e326b5b2ed2fc9708ecff63edf6eccd1d767701cf08033d1cf6b08d29",
184
+ "cli-contract.md": "3e28f67877ad3c31daaee3973dd44913e7c9f4716acd813d6e75a935691700a2",
185
185
  "conformance/README.md": "0c69bd9becf511ada9175b1e428ba183e31d1c8a49ff09eedf4c950bb831ec4d",
186
186
  "conformance/cases/extractor-emits-signal.json": "0115c7bb62a7a705f72e9d8048b3f0396e5caaeb3d04dea204415e279e58479d",
187
187
  "conformance/cases/kernel-empty-boot.json": "9b51b85ff62479cd0eee37cad260245208d94f6d79644f7ee40945a934960913",
@@ -190,7 +190,7 @@
190
190
  "conformance/cases/plugin-missing-ui-rejected.json": "7c910b74e6f718ab5c1a590cd3544602f056559251d18995a26bca0a0648a2fa",
191
191
  "conformance/cases/sidecar-end-to-end.json": "2de448c52a93139143e5290df2842d898b3984c91fbee315d7a26157efd9b123",
192
192
  "conformance/cases/signal-collision-detection.json": "c5e39a406ded6928a14c1a22b84f7b3cd49805bec56bd65de83130d9e419c09e",
193
- "conformance/coverage.md": "106468a6d9a65e5fdefbf75c8de4abecf35d5c08f2b6c44423741c60b723baea",
193
+ "conformance/coverage.md": "70cfa9a5736f1e12845da46c4c217b8a6061148f54548b67a30f1c74e3381bc5",
194
194
  "conformance/fixtures/orphan-markdown/.claude/agents/reviewer.md": "7f062731106f2d9811e4fffcf6ab44b8dfff4cfb16536a469514cc0664e832bf",
195
195
  "conformance/fixtures/orphan-markdown/ARCHITECTURE.md": "ec903666440bae65da3796b1158c92cfcdce22e0e09c3b20bb690176881a6ac4",
196
196
  "conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/kinds/markdown/kind.json": "6676a89bae5197e23cf50f1c11d596db558ac80f7334a7208fe57d8b92422251",
@@ -208,11 +208,11 @@
208
208
  "conformance/fixtures/signal-ir-collision/.claude/agents/architect.md": "acc46b5b2dff73d98a354e4d53b5041164595deae466a4e2ce41d7c5a72f28fb",
209
209
  "conformance/fixtures/signal-ir-single-signal/source.md": "1eda417b4c6eed372b66870e385c8d8cd631372b77cab7e996bb711e22218f89",
210
210
  "conformance/fixtures/signal-ir-single-signal/target.md": "527137f2b4f46c0034b0edc8932cf8613d2bf22ffaaf78f01085c82a3baaebe3",
211
- "db-schema.md": "f05c72ea74f8b32cc9909a02db1b8c93aefdf10ee804892449cab4d72a158ce9",
211
+ "db-schema.md": "32feabd79b79ec90d6a1263a82ea235accd55766481d41f64b87a39024bd97fc",
212
212
  "interfaces/security-scanner.md": "e8049712b9cf7a07c786bf19f8f775f8ef9638f063f7fba5c7a8b1431b92f38e",
213
213
  "job-events.md": "9d5b35d4c451a7f8eef9915d85316d924ac52f1c026a316cdda5f1099d496854",
214
214
  "job-lifecycle.md": "9c429121f98a07c8795f8979ed1abc5e5334e3f89db51585a8da55c527ef855b",
215
- "plugin-author-guide.md": "f0039397cd19d1f0a02c4e0691f213514216188d001cf56f287e69f6631fd39e",
215
+ "plugin-author-guide.md": "d75fa264885b92805987853567d59bd574e36624d7bcab3121b4c97d551ea768",
216
216
  "plugin-kv-api.md": "1acc69ed82433a74e35ada61d63a6d7379fb61046ff83de1e0facbe884c64704",
217
217
  "prompt-preamble.md": "9dd4f6d1bc6a425f8782fcee10cbe75909e8d64e28781fda56c2fae909b02f40",
218
218
  "schemas/annotations.schema.json": "8c639b149cad675fdd4e7d6be2b47e920cfdd24087b41361d6e1b8280f646322",
@@ -228,7 +228,7 @@
228
228
  "schemas/extensions/formatter.schema.json": "880dc379ad545a62404403533a01eda5171edba0390561fc46ec6e986e0b9bd3",
229
229
  "schemas/extensions/hook.schema.json": "f56aef59e9986ffdf7d86aa2e048dccccf217000a358b8c64737cbd911c48dad",
230
230
  "schemas/extensions/provider-kind.schema.json": "499b2418bbe6d8a84a1608e26c56b52c2652a30ce314bc2989094418797dc1e6",
231
- "schemas/extensions/provider.schema.json": "e536c3376ee5a04b6d4e971b6133a5af36e6f7e05973d88bb7236d4ab908830d",
231
+ "schemas/extensions/provider.schema.json": "75a565b8be6f1a08f0dbfec34e10c5d4d7c990489842bf338519a7d4b97dfe8f",
232
232
  "schemas/frontmatter/base.schema.json": "cff81510ed94824dfd12ab8b30ce9fbac65e42d61ae0edf3fbb6bbb6bb8bcb8c",
233
233
  "schemas/history-stats.schema.json": "436aa0ffe744bdb699000447e86b45724fbd2cc4642781074eb1527522b9058c",
234
234
  "schemas/input-types.schema.json": "cc9739aedcfdc72c1fa1285547e1392f9582169d578d72737918099acc721de0",
@@ -250,8 +250,9 @@
250
250
  "schemas/summaries/hook.schema.json": "6a1ceecda7a7173dfcd8b5f705d84be1792c4bb5a2269ff666088128c02c888a",
251
251
  "schemas/summaries/markdown.schema.json": "369d75a18710eb6c7ee4220b2899767ddbc07dada24f81b611827fe2e3a2977a",
252
252
  "schemas/summaries/skill.schema.json": "85d68056054bade62391948cc038fcfa70cdcf465e2b295f69cd520bbdba0134",
253
- "schemas/user-settings.schema.json": "b94c5235c8723a8ee023ec71b4ffd7094100cbaeccbb648712ebc331fc879ef5",
253
+ "schemas/user-settings.schema.json": "d155552ffca9c7dd4c6e31398aff4950dd9721d2a1f4b308cf0fe33000ca31b5",
254
254
  "schemas/view-slots.schema.json": "f78d0c5f0414f01dc045680b655b3d636373048eae4b75cc9dbd0fe0babd60bc",
255
+ "telemetry.md": "fa659b47c59e692f50c7a091470888d5e7c98dcf858978fa549af25b2562803f",
255
256
  "versioning.md": "28a13f165f837921fe5066f4bfce61012cd9f1b7c451f88eeb67252e39a0981a"
256
257
  }
257
258
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skill-map/spec",
3
- "version": "0.40.0",
3
+ "version": "0.42.0",
4
4
  "description": "JSON Schemas, prose contracts, and conformance suite for the skill-map specification.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -39,6 +39,7 @@
39
39
  "db-schema.md",
40
40
  "plugin-kv-api.md",
41
41
  "plugin-author-guide.md",
42
+ "telemetry.md",
42
43
  "interfaces/",
43
44
  "schemas/",
44
45
  "conformance/",