@skill-map/spec 0.53.0 → 0.54.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/telemetry.md CHANGED
@@ -1,19 +1,17 @@
1
1
  # Telemetry
2
2
 
3
- skill-map is a local-first tool. By default it sends **nothing** off the
4
- operator's machine. This document is the normative contract for the optional
5
- exceptions: two independently-consented, anonymous telemetry surfaces, both
6
- **OFF by default**.
3
+ skill-map is local-first; by default it sends **nothing** off the operator's
4
+ machine. This is the normative contract for the optional exceptions: two
5
+ independently-consented, anonymous telemetry surfaces, both **OFF by default**.
7
6
 
8
- - **Error reporting** (Sentry), so crashes happening in installations the
9
- maintainers do not control can be learned about and fixed.
10
- - **Usage analytics** (PostHog), so the maintainers can learn which verbs and
11
- built-in extensions are actually used in the wild and prioritise the
12
- roadmap accordingly.
7
+ - **Error reporting** (Sentry), so crashes in installations the maintainers do
8
+ not control can be learned about and fixed.
9
+ - **Usage analytics** (PostHog), so the maintainers learn which verbs and
10
+ built-in extensions are used in the wild and prioritise the roadmap.
13
11
 
14
- The two surfaces share one consent prompt, one kill switch, and one
15
- scrubber, but each has its own carrier, its own toggle, and its own stability
16
- contract. Either can be shipped, or not, independently.
12
+ The two surfaces share one consent prompt, one kill switch, and one scrubber,
13
+ but each has its own carrier, toggle, and stability contract. Either can be
14
+ shipped independently.
17
15
 
18
16
  ## Scope and non-goals
19
17
 
@@ -24,10 +22,10 @@ In scope:
24
22
  runtime errors in the browser UI, plus a small fixed set of triage tags
25
23
  (`surface`, `verb`, `phase`, `plugin_id` for built-ins, `extension_kind`,
26
24
  `route`, `method`, `status`).
27
- - **Usage.** Which `sm` verb ran and the NAMES of the flags it was given;
28
- the set of built-in extension ids that executed during a scan (presence,
29
- not volume); which UI view or feature was opened. Plus environment facts
30
- (`cli_version`, `node_major`, `os`, `arch`).
25
+ - **Usage.** Which `sm` verb ran and the NAMES of its flags; the set of
26
+ built-in extension ids that executed during a scan (presence, not volume);
27
+ which UI view or feature was opened. Plus environment facts (`cli_version`,
28
+ `node_major`, `os`, `arch`).
31
29
 
32
30
  Out of scope (MUST NOT be collected under this contract, on either surface):
33
31
 
@@ -40,82 +38,75 @@ Out of scope (MUST NOT be collected under this contract, on either surface):
40
38
  - **Any cross-session or cross-install correlation identifier**, with one
41
39
  documented exception: the single anonymous usage `distinct_id`
42
40
  (`telemetry.anonymousId`, below), which carries no identity and exists only
43
- so usage events from the same install can be de-duplicated. The error
44
- surface carries no correlation id at all.
41
+ to de-duplicate usage events from the same install. The error surface
42
+ carries no correlation id at all.
45
43
 
46
44
  ## Consent contract (shared)
47
45
 
48
- Both surfaces are **OFF by default**. They run only after the operator has
49
- explicitly opted in. Consent state lives in the user-settings file at
50
- `~/.skill-map/settings.json` under the `telemetry` object (see
46
+ Both surfaces are **OFF by default**, running only after the operator opts in.
47
+ Consent state lives in the user-settings file at `~/.skill-map/settings.json`
48
+ under the `telemetry` object (see
51
49
  [`user-settings.schema.json`](./schemas/user-settings.schema.json) and the
52
50
  narrow `$HOME` exception in [`cli-contract.md`](./cli-contract.md) §User-settings file):
53
51
 
54
- - `telemetry.errorsEnabled` (boolean). Opt-in for error reporting. Absent or
55
- `false` MUST be treated as OFF.
56
- - `telemetry.usageCliEnabled` (boolean). Opt-in for CLI usage analytics.
57
- Absent or `false` MUST be treated as OFF.
58
- - `telemetry.usageUiEnabled` (boolean). Opt-in for UI usage analytics. Absent
59
- or `false` MUST be treated as OFF.
52
+ - `telemetry.errorsEnabled`, `telemetry.usageCliEnabled`,
53
+ `telemetry.usageUiEnabled` (booleans). Opt-in for error reporting, CLI usage
54
+ analytics, and UI usage analytics respectively. For each, absent or `false`
55
+ MUST be treated as OFF.
60
56
  - `telemetry.anonymousId` (string UUID, or null). The PostHog `distinct_id`
61
57
  for the usage surface. Minted once when any usage toggle first becomes
62
58
  `true`; never regenerated. The single allowed anonymous correlation id,
63
59
  scoped to usage only.
64
- - `telemetry.firstRunAt` (integer milliseconds, or null). Records the first
65
- run on which the prompt was eligible, so the prompt can be deferred to the
66
- next eligible run.
67
- - `telemetry.promptedAt` (integer milliseconds, or null). Records when the
68
- consent prompt was shown so it is never shown twice.
60
+ - `telemetry.firstRunAt` (integer milliseconds, or null). The first run on
61
+ which the prompt was eligible, so it can be deferred to the next eligible run.
62
+ - `telemetry.promptedAt` (integer milliseconds, or null). When the consent
63
+ prompt was shown, so it is never shown twice.
69
64
 
70
65
  Rules:
71
66
 
72
67
  1. **Default OFF.** When a toggle is absent or `false`, the matching SDK is
73
68
  not initialised, no endpoint is contacted, and there is zero added latency.
74
- This MUST hold on every surface (CLI, BFF, UI).
69
+ MUST hold on every surface (CLI, BFF, UI).
75
70
  2. **One shared consent prompt, TTY only, deferred to the second eligible
76
71
  run.** A run is "eligible" when the prompt could appear: an interactive
77
72
  terminal (`process.stdout.isTTY` true), at least one carrier configured
78
73
  (a Sentry DSN or the PostHog key non-empty), the kill switch unset, and
79
74
  `promptedAt` absent. The CLI MUST NOT prompt on the FIRST eligible run, it
80
- only stamps `firstRunAt` and stays silent, so the operator's first `sm`
81
- invocation is not asked two things at once (a first `sm scan` may already
82
- prompt for the provider lens). The NEXT eligible run shows the interactive
83
- prompt (yes (default) / no / details). A single **yes** sets
84
- `errorsEnabled`, `usageCliEnabled`, and `usageUiEnabled` all to `true` and
85
- mints `anonymousId`; a **no** sets all three to `false` and mints nothing.
86
- Either way it stamps `promptedAt`. On a non-eligible run (non-TTY CI,
87
- pipes) nothing is asked or recorded and every surface stays OFF.
75
+ only stamps `firstRunAt`, so the operator's first `sm` invocation is not
76
+ asked two things at once (a first `sm scan` may already prompt for the
77
+ provider lens). The NEXT eligible run shows the interactive prompt
78
+ (yes (default) / no / details). A single **yes** sets `errorsEnabled`,
79
+ `usageCliEnabled`, and `usageUiEnabled` all to `true` and mints
80
+ `anonymousId`; a **no** sets all three to `false` and mints nothing. Either
81
+ way it stamps `promptedAt`. On a non-eligible run (non-TTY CI, pipes)
82
+ nothing is asked or recorded and every surface stays OFF.
88
83
  3. **Asked once.** Once `promptedAt` is set, the prompt MUST NOT be shown
89
- again. The persisted toggles are authoritative thereafter.
90
- 4. **Env override.** The `SKILL_MAP_TELEMETRY=0` environment variable forces
91
- OFF on every surface (errors and both usage toggles) regardless of the
92
- persisted settings. It is a kill switch, not a toggle: there is no value of
93
- the variable that forces ON. There is exactly one kill-switch variable for
94
- all surfaces.
84
+ again; the persisted toggles are authoritative thereafter.
85
+ 4. **Env override.** `SKILL_MAP_TELEMETRY=0` forces OFF on every surface
86
+ (errors and both usage toggles) regardless of persisted settings. It is a
87
+ kill switch, not a toggle: no value of it forces ON. Exactly one
88
+ kill-switch variable covers all surfaces.
95
89
  5. **Independent toggles.** After the first run, the operator changes consent
96
- through the Settings UI (persisted via the BFF), the same way the
97
- update-check toggle works today. The three toggles are independent:
98
- `usageCliEnabled` and `usageUiEnabled` can each be turned off without
99
- affecting the other or `errorsEnabled`. Because the CLI reads
100
- `~/.skill-map/settings.json` fresh on every invocation, turning CLI usage
101
- off from the browser is honoured on the next `sm` run. There is
102
- intentionally no dedicated `sm config` key, because `sm config` writes
103
- project-local settings and these flags are per-machine. A future
104
- `sm telemetry` verb family MAY expose status and toggling from the CLI.
105
- 6. **Anonymous id.** `anonymousId` is a random UUID v4 with no personal data.
106
- It is minted exactly once, the first time any usage toggle becomes `true`
107
- (through the consent prompt or a Settings enable), and is never
108
- regenerated for the life of the install. It is the PostHog `distinct_id`
109
- shared by the CLI and UI usage surfaces so the two are attributed to one
110
- install. The BFF exposes it read-only (see below) so the browser uses the
111
- same id; it MUST NOT be writable over the wire.
90
+ through the Settings UI (persisted via the BFF), like the update-check
91
+ toggle. The three toggles are independent: `usageCliEnabled` and
92
+ `usageUiEnabled` can each be turned off without affecting the other or
93
+ `errorsEnabled`. Because the CLI reads `~/.skill-map/settings.json` fresh
94
+ per invocation, turning CLI usage off from the browser is honoured on the
95
+ next `sm` run. There is intentionally no dedicated `sm config` key:
96
+ `sm config` writes project-local settings, these flags are per-machine.
97
+ A future `sm telemetry` verb family MAY expose status and toggling from
98
+ the CLI.
99
+ 6. **Anonymous id.** `anonymousId` is a random UUID v4 with no personal data,
100
+ minted once the first time any usage toggle becomes `true` (consent prompt
101
+ or Settings enable), never regenerated for the life of the install. It is
102
+ the PostHog `distinct_id` shared by the CLI and UI usage surfaces. The BFF
103
+ exposes it read-only (see below) so the browser uses the same id; it MUST
104
+ NOT be writable over the wire.
112
105
 
113
106
  ## Surface: Errors (Sentry)
114
107
 
115
- Three surfaces report independently so a crash can be attributed to the right
116
- layer. They report to **two** Sentry projects: the two Node surfaces (CLI and
117
- BFF) share one project and are told apart by a `surface` tag, the browser UI
118
- reports to its own project.
108
+ Three surfaces report independently so a crash is attributed to the right
109
+ layer, across **two** Sentry projects.
119
110
 
120
111
  | Surface | Runtime | Discriminator | Project |
121
112
  |---|---|---|---|
@@ -123,21 +114,19 @@ reports to its own project.
123
114
  | `sm serve` BFF | Node (Hono) | `surface: bff` tag | shared Node project |
124
115
  | UI | Browser (Angular) | own project | `skill-map-ui` |
125
116
 
126
- The two Node surfaces share one project because they are the same workspace
127
- code in the same runtime; the `surface` tag, plus the per-event `route` /
128
- `method` tags, separate a CLI crash from a BFF request-path crash. The UI has
129
- its own project, so it needs no `surface` tag. Each project carries a
130
- hardcoded DSN (`SENTRY_DSN_NODE` for the shared Node project, `SENTRY_DSN_UI`
131
- for the UI), centralized in `src/public-config.ts` and
117
+ The two Node surfaces share one project (same workspace code, same runtime); the
118
+ `surface` tag plus the per-event `route` / `method` tags separate a CLI crash
119
+ from a BFF request-path crash. The UI has its own project and needs no `surface`
120
+ tag. Each project carries a hardcoded DSN (`SENTRY_DSN_NODE` for the shared Node
121
+ project, `SENTRY_DSN_UI` for the UI), centralized in `src/public-config.ts` and
132
122
  `ui/src/app/core/public-config.ts`. Sentry DSNs are public by design (they
133
- identify an ingest endpoint, they are not secrets) and are safe to ship in
134
- the published artifact. The BFF MUST NOT emit usage events; it reports only
135
- unhandled errors in the request path.
123
+ identify an ingest endpoint, not secrets) and safe to ship. The BFF MUST NOT
124
+ emit usage events; it reports only unhandled errors in the request path.
136
125
 
137
126
  The error surfaces send **no proactive beacons**: no release-health sessions,
138
127
  no transactions, no performance traces. An event leaves the machine ONLY when
139
- an error is captured. In particular the browser SDK MUST drop the default
140
- session integration so no session is sent on page load or route change.
128
+ an error is captured. The browser SDK MUST drop the default session
129
+ integration so no session is sent on page load or route change.
141
130
 
142
131
  ### Error wire format
143
132
 
@@ -151,18 +140,17 @@ An error event MAY carry:
151
140
  `verb`, `phase`, `plugin_id` (built-in ids only), `extension_kind`,
152
141
  `route` (BFF), `method`, `status`.
153
142
  - The error name, error code, and a scrubbed message.
154
- - Breadcrumbs (a bounded recent-event trail) with each message scrubbed.
143
+ - Breadcrumbs (a bounded recent-event trail), each message scrubbed.
155
144
 
156
145
  ## Surface: Usage (PostHog)
157
146
 
158
147
  Usage analytics are carried by **PostHog Cloud (EU region)**, for data
159
148
  residency parity with the Sentry `.de` projects. The public PostHog project
160
149
  key is hardcoded and centralized in `src/public-config.ts` (`POSTHOG_KEY_NODE`)
161
- and `ui/src/app/core/public-config.ts` (`POSTHOG_KEY_UI`). Like a Sentry DSN,
162
- a PostHog project key is a public ingest identifier, not a secret, and is safe
163
- to ship. Setting a key to the empty string `''` forces that surface dormant
164
- (no init, no network, the SDK is not even imported), the same dormancy gate
165
- the error surface uses.
150
+ and `ui/src/app/core/public-config.ts` (`POSTHOG_KEY_UI`). Like a Sentry DSN it
151
+ is a public ingest identifier, not a secret, and safe to ship. Setting a key to
152
+ `''` forces that surface dormant (no init, no network, SDK not even imported),
153
+ the same dormancy gate the error surface uses.
166
154
 
167
155
  Only **two** runtimes emit usage events:
168
156
 
@@ -171,36 +159,35 @@ Only **two** runtimes emit usage events:
171
159
  | `sm <verb>` | Node (CLI) | `usageCliEnabled` | PostHog (server SDK) |
172
160
  | UI | Browser (Angular) | `usageUiEnabled` | PostHog (browser SDK) |
173
161
 
174
- The **BFF MUST NOT emit usage events** (the BFF's activity is the UI's
175
- activity, already covered by the UI surface; double-emitting would
176
- double-count). The BFF participates only by reading/writing consent and by
177
- exposing `anonymousId` read-only on `GET /api/preferences` so the browser uses
178
- the same `distinct_id` as the CLI.
162
+ The **BFF MUST NOT emit usage events** (its activity is the UI's, already
163
+ covered by the UI surface; double-emitting would double-count). The BFF
164
+ participates only by reading/writing consent and by exposing `anonymousId`
165
+ read-only on `GET /api/preferences` so the browser uses the same `distinct_id`
166
+ as the CLI.
179
167
 
180
- Both usage SDKs are configured to send nothing beyond the allow-list below:
181
- PostHog autocapture, pageview/pageleave capture, session recording, and
182
- client IP / geo-IP enrichment are all disabled.
168
+ Both usage SDKs send nothing beyond the allow-list below: PostHog autocapture,
169
+ pageview/pageleave capture, session recording, and client IP / geo-IP
170
+ enrichment are all disabled.
183
171
 
184
172
  ## Usage event taxonomy
185
173
 
186
174
  Usage collection is **deny by default**: only the events and properties named
187
175
  here may be sent. Every event carries `distinct_id = telemetry.anonymousId`,
188
176
  the common environment facts (`cli_version`, `node_major`, `os`, `arch`; the UI
189
- additionally carries browser family/version where the SDK provides it), and
190
- `environment` (`dev` / `prod`, see below). The UI also attaches
191
- the active theme as super-properties on every event: `theme_base` (`light` /
192
- `dark`) and `theme_extra` (the active extra theme id, or `none`); future extra
193
- themes flow through by value with no spec change. No other identity property is
194
- ever attached.
177
+ also carries browser family/version where the SDK provides it), and
178
+ `environment` (`dev` / `prod`, see below). The UI also attaches the active
179
+ theme as super-properties on every event: `theme_base` (`light` / `dark`) and
180
+ `theme_extra` (the active extra theme id, or `none`); future extra themes flow
181
+ through by value with no spec change. No other identity property is ever
182
+ attached.
195
183
 
196
184
  The `environment` tag lets the maintainers filter their own dogfooding out of
197
- the real-world data. It is `dev` when the `SKILL_MAP_TELEMETRY_ENV` environment
198
- variable is set to any non-empty value other than a production marker
199
- (`prod` / `production`); the dev tooling sets it. It is `prod` when the
200
- variable is absent, empty, or a production marker. It is NOT a kill switch (it
201
- never disables telemetry, only labels the source) and rides on both surfaces:
202
- usage events as above, and Sentry's native `environment` field on error
203
- events.
185
+ real-world data. It is `dev` when `SKILL_MAP_TELEMETRY_ENV` is set to any
186
+ non-empty value other than a production marker (`prod` / `production`); the dev
187
+ tooling sets it. It is `prod` when the variable is absent, empty, or a
188
+ production marker. It is NOT a kill switch (it labels the source, never disables
189
+ telemetry) and rides on both surfaces: usage events as above, and Sentry's
190
+ native `environment` field on error events.
204
191
 
205
192
  | Event | Surface | Properties |
206
193
  |---|---|---|
@@ -214,30 +201,28 @@ Rules:
214
201
  - **Flag names only, never values.** `--max-nodes 500` reports the name
215
202
  `max-nodes`, never `500`.
216
203
  - **Extractor ids are presence, not counts.** `extensions` is a set; it never
217
- carries how many nodes an extractor processed or how large the project is.
218
- Only extractors that ran in the walk appear (cached extractors on an
219
- incremental scan do not), so the signal is "which extractors this project
220
- exercises", aggregated across runs.
204
+ carries how many nodes an extractor processed or project size. Only
205
+ extractors that ran in the walk appear (cached extractors on an incremental
206
+ scan do not), so the signal is "which extractors this project exercises",
207
+ aggregated across runs.
221
208
  - **Third-party ids collapse.** Any extension id whose plugin is not a
222
209
  built-in (`claude`, `antigravity`, `openai`, `agent-skills`, `core`) MUST be
223
- replaced with the literal `external_plugin` before the event leaves the
224
- machine.
210
+ replaced with `external_plugin` before the event leaves the machine.
225
211
  - **No node paths, titles, or content** in any UI event; the view / feature is
226
- the event name, drawn from a closed set, and nothing else is attached.
212
+ the event name, from a closed set, and nothing else is attached.
227
213
 
228
214
  ## Scrubbing rules (shared)
229
215
 
230
- Scrubbing is **deny by default** and applied client-side in each SDK's
231
- pre-send hook (`beforeSend` for Sentry, `before_send` for PostHog), before any
232
- event leaves the machine. It applies to error events AND usage event
233
- properties (defense in depth: the usage collectors emit only names and enums
234
- by construction, but every event's payload is still walked). An event MUST
235
- have the following removed or replaced:
216
+ Scrubbing is **deny by default**, applied client-side in each SDK's pre-send
217
+ hook (`beforeSend` for Sentry, `before_send` for PostHog) before any event
218
+ leaves the machine. It applies to error events AND usage event properties
219
+ (defense in depth: the usage collectors emit only names and enums, but every
220
+ payload is still walked). An event MUST have the following removed or replaced:
236
221
 
237
222
  - **Absolute paths**, anywhere they appear (frame `abs_path`, frame
238
- `filename`, inside the error message, inside breadcrumb messages, inside
239
- any nested event or property field). The user's home directory is replaced
240
- with the literal `<HOME>` and the OS username with `<USER>`.
223
+ `filename`, inside the error message, breadcrumb messages, any nested event
224
+ or property field). The home directory is replaced with `<HOME>` and the OS
225
+ username with `<USER>`.
241
226
  - **File names of user content** (scanned markdown files).
242
227
  - **Markdown bodies, frontmatter values, annotation contents.** None of these
243
228
  are ever attached to an event.
@@ -251,7 +236,7 @@ have the following removed or replaced:
251
236
  The scrubber is a pure function with no SDK dependency, so it can be unit
252
237
  tested against hostile inputs (Windows paths, symlinked paths, paths embedded
253
238
  mid-message, nested `abs_path` fields, breadcrumb data, structured usage
254
- property objects) independently of the SDK wiring.
239
+ properties) independently of SDK wiring.
255
240
 
256
241
  ## Server-side guarantees
257
242
 
@@ -262,10 +247,10 @@ As a second line of defense behind the client-side scrubber:
262
247
  client scrubber. The UI error surface additionally restricts reporting to
263
248
  loopback: Sentry retired its server-side allowed-domains setting, so this is
264
249
  enforced client-side via the SDK `allowUrls` option pinned to `localhost` /
265
- `127.0.0.1` (the UI is only ever served from loopback).
250
+ `127.0.0.1` (the UI is only served from loopback).
266
251
  - The **PostHog** project MUST be configured to discard client IP addresses
267
- and disable geo-IP enrichment (the client SDKs also disable geo and
268
- autocapture, but the project setting is the backstop).
252
+ and disable geo-IP enrichment (the client SDKs disable geo and autocapture
253
+ too, but the project setting is the backstop).
269
254
 
270
255
  ## Stability
271
256
 
@@ -275,20 +260,19 @@ kill switch, prompt-once semantics) is stable as of the spec minor in which it
275
260
  lands. Loosening any default (anything other than OFF), removing the kill
276
261
  switch, or removing the consent gate is a major bump.
277
262
 
278
- The **two surfaces are independent.** Error scope and usage scope each evolve
279
- on their own minor bump. Adding a new usage event or property, or a new error
280
- tag or environment fact, is a minor bump. Performance traces remain out of
281
- scope on both surfaces and would be a third, separately-consented surface.
263
+ The **two surfaces are independent.** Error and usage scope each evolve on
264
+ their own minor bump. Adding a new usage event or property, or a new error tag
265
+ or environment fact, is a minor bump. Performance traces remain out of scope on
266
+ both and would be a third, separately-consented surface.
282
267
 
283
268
  The **`anonymousId` exception** is normatively scoped to the usage surface
284
- only: it is the one anonymous correlation id the contract permits, and the
285
- error surface MUST remain free of any cross-session or cross-install id.
286
- Widening the id beyond usage, or attaching any identity to it, is a major
287
- bump.
269
+ only: the one anonymous correlation id the contract permits, and the error
270
+ surface MUST remain free of any cross-session or cross-install id. Widening it
271
+ beyond usage, or attaching any identity, is a major bump.
288
272
 
289
273
  The scrubbing exclusion list (what MUST NOT leave the machine) is the stable,
290
274
  normative core and may only grow, never shrink, without a major bump.
291
275
 
292
- Consumers and alternate implementations MAY choose not to ship either surface;
293
- both are optional. An implementation that does ship a surface MUST honor the
294
- consent contract and the scrubbing rules in full.
276
+ Consumers and alternate implementations MAY ship neither surface; both are
277
+ optional. An implementation that ships a surface MUST honor the consent
278
+ contract and the scrubbing rules in full.
package/versioning.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Spec versioning
2
2
 
3
- The skill-map **spec** and the skill-map **reference CLI** evolve on independent semver tracks. A spec version and a CLI version are related through a `specCompat` range declared by each implementation and each plugin.
3
+ The skill-map **spec** and the skill-map **reference CLI** evolve on independent semver tracks, related through a `specCompat` range declared by each implementation and each plugin.
4
4
 
5
5
  ## Two tracks
6
6
 
@@ -9,11 +9,11 @@ The skill-map **spec** and the skill-map **reference CLI** evolve on independent
9
9
  | Spec | `spec-v1.2.0` | Schemas + contracts in `spec/`. Consumed by any implementation. |
10
10
  | Reference CLI | `cli-v0.8.3` | The `sm` binary and its built-in extensions in `src/`. |
11
11
 
12
- A given CLI release declares the spec range it implements (e.g. `"specCompat": "^1.0.0"`). A plugin declares the spec range it targets. At load time the implementation runs `semver.satisfies(specVersion, plugin.specCompat)`; mismatch → plugin disabled with reason `incompatible-spec`.
12
+ A CLI release declares the spec range it implements (e.g. `"specCompat": "^1.0.0"`); a plugin declares the spec range it targets. At load time the implementation runs `semver.satisfies(specVersion, plugin.specCompat)`; mismatch → plugin disabled with reason `incompatible-spec`.
13
13
 
14
14
  ## Semver for the spec
15
15
 
16
- Patch, minor, major have precise meaning for a specification, different from code.
16
+ Patch, minor, major have precise meaning for a specification, distinct from code.
17
17
 
18
18
  | Bump | Allowed changes | Examples |
19
19
  |---|---|---|
@@ -30,7 +30,7 @@ All of the following are normative and governed by this policy:
30
30
  - Every JSON Schema in `schemas/` (fields, types, required, enums, defaults, `additionalProperties`).
31
31
  - Every MUST / SHOULD / MAY statement in prose documents ([`architecture.md`](./architecture.md), [`cli-contract.md`](./cli-contract.md), [`job-events.md`](./job-events.md), [`prompt-preamble.md`](./prompt-preamble.md), [`db-schema.md`](./db-schema.md), [`plugin-kv-api.md`](./plugin-kv-api.md), [`job-lifecycle.md`](./job-lifecycle.md)).
32
32
  - Exit codes, verb names, required flags, canonical error messages marked "normative".
33
- - Conformance fixtures and cases, removing or tightening a case is major.
33
+ - Conformance fixtures and cases; removing or tightening a case is major.
34
34
 
35
35
  The following are **non-normative** and can change at any time without a version bump:
36
36
 
@@ -41,7 +41,7 @@ The following are **non-normative** and can change at any time without a version
41
41
 
42
42
  ## Stability tags
43
43
 
44
- Fields and features inside the spec carry a stability tag. Tag drives what the version policy allows.
44
+ Fields and features carry a stability tag. The tag drives what the version policy allows.
45
45
 
46
46
  | Tag | Meaning | Policy |
47
47
  |---|---|---|
@@ -55,7 +55,7 @@ Tags live inline in schema `description` fields and in prose via a leading `**St
55
55
 
56
56
  - `stable` → `deprecated` requires a minor bump.
57
57
  - `deprecated` → removed requires a major bump.
58
- - Between the two, at least three minor releases must ship with the field marked `deprecated`. This gives plugin authors a release window to migrate.
58
+ - Between the two, at least three minor releases must ship with the field marked `deprecated`, giving plugin authors a window to migrate.
59
59
  - Rationale for the deprecation and the replacement field/flag must live in `CHANGELOG.md`.
60
60
 
61
61
  ## Pre-1.0
@@ -63,23 +63,23 @@ Tags live inline in schema `description` fields and in prose via a leading `**St
63
63
  While the spec is `0.Y.Z`:
64
64
 
65
65
  - Minor bumps may contain breaking changes (documented as such in `CHANGELOG.md`).
66
- - Conformance is advisory, failing a conformance case is a bug report, not a spec violation.
66
+ - Conformance is advisory; failing a conformance case is a bug report, not a spec violation.
67
67
  - `specCompat` in plugins should pin a minor range (`"^0.3.0"` means `>=0.3.0 <0.4.0`), not a major range.
68
68
 
69
69
  The first stable commitment is `spec-v1.0.0`. In the current reference roadmap, that tag ships with `cli-v1.0.0`.
70
70
 
71
71
  ## Independence in practice
72
72
 
73
- - **Spec `1.0.0` + CLI `0.1.0`**, spec is stabilized before the CLI ships its v1. Normal case during early life of the project.
74
- - **Spec `1.2.0` + CLI `0.8.0`**, spec gained an optional feature; CLI hasn't implemented it yet. Fine. Plugins needing that feature must declare `"specCompat": "^1.2.0"`.
73
+ - **Spec `1.0.0` + CLI `0.1.0`**, spec stabilized before the CLI ships its v1. Normal during early life of the project.
74
+ - **Spec `1.2.0` + CLI `0.8.0`**, spec gained an optional feature the CLI hasn't implemented yet. Fine. Plugins needing it must declare `"specCompat": "^1.2.0"`.
75
75
  - **Spec `2.0.0` + CLI `1.4.0`**, CLI still targets spec v1. Operator must upgrade CLI before installing v2-targeting plugins.
76
76
 
77
77
  ## Change process
78
78
 
79
- 1. PR proposes a spec change. Include rationale and classification (patch/minor/major).
79
+ 1. PR proposes a spec change with rationale and classification (patch/minor/major).
80
80
  2. If major, PR includes a migration note draft for [`CHANGELOG.md`](./CHANGELOG.md).
81
81
  3. If the change affects reference-impl behavior, a companion PR in `src/` lands the implementation behind the bumped `specCompat`.
82
- 4. Merge order: spec change first, implementation second. An implementation MUST NOT ship a feature that is not yet in the spec (see [`../AGENTS.md`](../AGENTS.md): "Every feature: update spec/ first, then src/").
82
+ 4. Merge order: spec change first, implementation second. An implementation MUST NOT ship a feature not yet in the spec (see [`../AGENTS.md`](../AGENTS.md): "Every feature: update spec/ first, then src/").
83
83
  5. Tag spec release (`spec-vX.Y.Z`) independent from any CLI tag.
84
84
 
85
85
  ## Canonical URLs
@@ -91,4 +91,4 @@ https://skill-map.ai/spec/v1/node.schema.json
91
91
  https://skill-map.ai/spec/v1.2/node.schema.json
92
92
  ```
93
93
 
94
- Major version is always present in the path. Implementations MUST NOT rely on `latest`.
94
+ The major version is always present in the path. Implementations MUST NOT rely on `latest`.