@skill-map/spec 0.22.0 → 0.24.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 +91 -0
- package/README.md +4 -4
- package/architecture.md +130 -119
- package/cli-contract.md +102 -96
- package/conformance/README.md +13 -13
- package/conformance/coverage.md +41 -38
- package/db-schema.md +45 -45
- package/index.json +40 -37
- package/interfaces/security-scanner.md +20 -20
- package/job-events.md +21 -21
- package/job-lifecycle.md +21 -21
- package/package.json +1 -1
- package/plugin-author-guide.md +133 -108
- package/plugin-kv-api.md +10 -10
- package/prompt-preamble.md +8 -8
- package/schemas/annotations.schema.json +3 -3
- package/schemas/api/rest-envelope.schema.json +10 -10
- package/schemas/conformance-result.schema.json +120 -0
- package/schemas/execution-record.schema.json +2 -2
- package/schemas/extensions/analyzer.schema.json +9 -0
- package/schemas/extensions/base.schema.json +4 -4
- package/schemas/extensions/extractor.schema.json +4 -4
- package/schemas/extensions/formatter.schema.json +1 -1
- package/schemas/extensions/hook.schema.json +3 -3
- package/schemas/extensions/provider.schema.json +5 -5
- package/schemas/frontmatter/base.schema.json +1 -1
- package/schemas/history-stats.schema.json +4 -4
- package/schemas/input-types.schema.json +3 -3
- package/schemas/issue.schema.json +1 -1
- package/schemas/job.schema.json +2 -2
- package/schemas/node.schema.json +6 -5
- package/schemas/plugins-doctor.schema.json +97 -0
- package/schemas/plugins-registry.schema.json +2 -2
- package/schemas/project-config.schema.json +9 -9
- package/schemas/refresh-report.schema.json +52 -0
- package/schemas/report-base-deterministic.schema.json +1 -1
- package/schemas/sidecar.schema.json +3 -3
- package/schemas/summaries/markdown.schema.json +1 -1
- package/schemas/summaries/skill.schema.json +1 -1
- package/schemas/view-slots.schema.json +7 -7
- package/versioning.md +7 -7
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://skill-map.dev/spec/v0/history-stats.schema.json",
|
|
4
4
|
"title": "HistoryStats",
|
|
5
|
-
"description": "Machine-readable output of `sm history stats --json`. Aggregates over `state_executions` within a time window. camelCase keys throughout. The `elapsedMs` top-level field is the command's own wall-clock (see `cli-contract.md` §Elapsed time)
|
|
5
|
+
"description": "Machine-readable output of `sm history stats --json`. Aggregates over `state_executions` within a time window. camelCase keys throughout. The `elapsedMs` top-level field is the command's own wall-clock (see `cli-contract.md` §Elapsed time), distinct from `totals.durationMsTotal`, which is the sum of every execution record's duration.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": [
|
|
8
8
|
"schemaVersion",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"since": {
|
|
29
29
|
"type": ["string", "null"],
|
|
30
30
|
"format": "date-time",
|
|
31
|
-
"description": "Inclusive lower bound (ISO-8601). `null` means all-time
|
|
31
|
+
"description": "Inclusive lower bound (ISO-8601). `null` means all-time, the earliest execution record is used."
|
|
32
32
|
},
|
|
33
33
|
"until": {
|
|
34
34
|
"type": "string",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"failedCount": { "type": "integer", "minimum": 0 },
|
|
55
55
|
"tokensIn": { "type": "integer", "minimum": 0, "description": "Sum of `state_executions.tokens_in`. `null` values in the source are treated as 0." },
|
|
56
56
|
"tokensOut": { "type": "integer", "minimum": 0 },
|
|
57
|
-
"durationMsTotal": { "type": "integer", "minimum": 0, "description": "Sum of every execution's `duration_ms`. NOT the command's wall-clock
|
|
57
|
+
"durationMsTotal": { "type": "integer", "minimum": 0, "description": "Sum of every execution's `duration_ms`. NOT the command's wall-clock, see `elapsedMs`." }
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
"tokensPerAction": {
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
},
|
|
143
143
|
"perFailureReason": {
|
|
144
144
|
"type": "object",
|
|
145
|
-
"description": "Counts (not ratios) per failure reason. Every enum value of `state_executions.failure_reason` appears, with `0` when no occurrences
|
|
145
|
+
"description": "Counts (not ratios) per failure reason. Every enum value of `state_executions.failure_reason` appears, with `0` when no occurrences, predictable shape for dashboards.",
|
|
146
146
|
"required": [
|
|
147
147
|
"runner-error",
|
|
148
148
|
"report-invalid",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://skill-map.dev/spec/v0/input-types.schema.json",
|
|
4
4
|
"title": "InputTypes",
|
|
5
|
-
"description": "Closed catalog of input-types for plugin settings. The plugin author declares each user-configurable setting in the manifest's `settings` map by picking an `input-type` from this catalog; the kernel knows the schema for each type, the UI ships a generated form per type, and the CLI's `sm plugins config <id>` command exposes the same surface. Plugin authors NEVER write JSON Schema for settings
|
|
5
|
+
"description": "Closed catalog of input-types for plugin settings. The plugin author declares each user-configurable setting in the manifest's `settings` map by picking an `input-type` from this catalog; the kernel knows the schema for each type, the UI ships a generated form per type, and the CLI's `sm plugins config <id>` command exposes the same surface. Plugin authors NEVER write JSON Schema for settings, they pick a type by name and supply per-type parameters (label, default, min/max, options for enums, etc.). Closed catalog by design: every new input-type requires spec + UI form + CLI prompter + tests. Versioned via the manifest field `catalogCompat` (semver against the catalog as a whole). For the rationale and open issues, see ROADMAP.md §UI contribution system.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"$defs": {
|
|
8
8
|
"InputTypeName": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"description": "Closed enum of input-type identifiers. Adding an entry requires the full spec/UI/CLI/tests round-trip. Removing or renaming an entry is a catalog-major-bump and triggers `sm plugins upgrade` migration."
|
|
23
23
|
},
|
|
24
24
|
"ISettingDeclaration": {
|
|
25
|
-
"description": "Manifest-side declaration of a single setting, keyed in `IPluginManifest.settings[<settingId>]`. Discriminated by `type`; per-type parameters live in the `oneOf` branches below. The plugin author NEVER writes JSON Schema
|
|
25
|
+
"description": "Manifest-side declaration of a single setting, keyed in `IPluginManifest.settings[<settingId>]`. Discriminated by `type`; per-type parameters live in the `oneOf` branches below. The plugin author NEVER writes JSON Schema, `type` is a name from `InputTypeName`, the kernel validates the user-supplied value against the per-type value schema.",
|
|
26
26
|
"oneOf": [
|
|
27
27
|
{ "$ref": "#/$defs/Setting_StringList" },
|
|
28
28
|
{ "$ref": "#/$defs/Setting_SingleString" },
|
|
@@ -206,7 +206,7 @@
|
|
|
206
206
|
"type": "string",
|
|
207
207
|
"pattern": "^[gimsuy]*$",
|
|
208
208
|
"default": "",
|
|
209
|
-
"description": "ECMAScript regex flags allowed. Subset: `g`, `i`, `m`, `s`, `u`, `y`. Author chooses which flags the user-supplied pattern compiles with
|
|
209
|
+
"description": "ECMAScript regex flags allowed. Subset: `g`, `i`, `m`, `s`, `u`, `y`. Author chooses which flags the user-supplied pattern compiles with, the user supplies only the body."
|
|
210
210
|
}
|
|
211
211
|
},
|
|
212
212
|
"description": "ECMAScript regex pattern (the body, no `/` delimiters). Validated by attempting `new RegExp(value, flags)` at form submit and at extractor invocation. Compilation failure → form error / `invalid-settings` at runtime. Value type at runtime: `string`."
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"fix": {
|
|
40
40
|
"type": ["object", "null"],
|
|
41
|
-
"description": "Machine-readable fix hint. Stability: experimental
|
|
41
|
+
"description": "Machine-readable fix hint. Stability: experimental, shape may change before v1.",
|
|
42
42
|
"additionalProperties": false,
|
|
43
43
|
"properties": {
|
|
44
44
|
"summary": { "type": "string" },
|
package/schemas/job.schema.json
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"nonce": {
|
|
32
32
|
"type": "string",
|
|
33
|
-
"description": "Unique per-job token. `sm record` requires it to close the job
|
|
33
|
+
"description": "Unique per-job token. `sm record` requires it to close the job, mismatch rejects with exit code 4."
|
|
34
34
|
},
|
|
35
35
|
"priority": {
|
|
36
36
|
"type": "integer",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"failureReason": {
|
|
46
46
|
"type": ["string", "null"],
|
|
47
47
|
"enum": ["runner-error", "report-invalid", "timeout", "abandoned", "job-file-missing", "user-cancelled", null],
|
|
48
|
-
"description": "Populated when `status = failed`. `job-file-missing` (legacy name preserved across the disk-to-DB shift) covers DB corruption where `state_jobs.content_hash` no longer resolves in `state_job_contents
|
|
48
|
+
"description": "Populated when `status = failed`. `job-file-missing` (legacy name preserved across the disk-to-DB shift) covers DB corruption where `state_jobs.content_hash` no longer resolves in `state_job_contents`, the runtime invariant should keep this state unreachable; the enum value exists as a defensive failure-mode label."
|
|
49
49
|
},
|
|
50
50
|
"runner": {
|
|
51
51
|
"type": ["string", "null"],
|
package/schemas/node.schema.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://skill-map.dev/spec/v0/node.schema.json",
|
|
4
4
|
"title": "Node",
|
|
5
|
-
"description": "A single markdown file in the graph. Identified by its relative path from the scope root. The `kind` is whatever the classifying Provider declares
|
|
5
|
+
"description": "A single markdown file in the graph. Identified by its relative path from the scope root. The `kind` is whatever the classifying Provider declares, open by design; the **built-in Claude Provider** emits `skill` / `agent` / `command` / `markdown` today, but external Providers (Cursor, Obsidian, …) MAY emit their own. Format-named kinds (`markdown`, future `toml`, future `json`) are reserved for the generic fallback only, when a file matches a specific role (agent / command / skill) that classification prevails over format naming.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": ["path", "kind", "provider", "bodyHash", "frontmatterHash", "bytes", "linksOutCount", "linksInCount", "externalRefsCount"],
|
|
8
8
|
"additionalProperties": false,
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"kind": {
|
|
15
15
|
"type": "string",
|
|
16
16
|
"minLength": 1,
|
|
17
|
-
"
|
|
17
|
+
"pattern": "^[a-zA-Z][a-zA-Z0-9_-]{0,63}$",
|
|
18
|
+
"description": "Category assigned by the Provider. Open-by-design, any non-empty string an enabled Provider declares is valid (built-in Claude Provider catalog: `skill` / `agent` / `command` / `markdown`; external Providers MAY declare their own). The pattern restricts kind names to ASCII letters, digits, underscore, and hyphen, starting with a letter, up to 64 chars. The restriction is a security boundary: the UI uses the kind name as a fragment of CSS custom-property identifiers (`--sm-kind-<name>`) injected into a `<style>` tag, so values that would break out of the declaration context (semicolons, braces, whitespace) MUST be rejected at the kernel boundary. Per-kind frontmatter schemas live with the Provider that emits the kind. Stability: stable."
|
|
18
19
|
},
|
|
19
20
|
"provider": {
|
|
20
21
|
"type": "string",
|
|
@@ -60,11 +61,11 @@
|
|
|
60
61
|
},
|
|
61
62
|
"sidecar": {
|
|
62
63
|
"$ref": "#/$defs/sidecarOverlay",
|
|
63
|
-
"description": "Step 9.6.2
|
|
64
|
+
"description": "Step 9.6.2, co-located `.sm` sidecar overlay. Carries presence flag, drift status (null when no sidecar), and the parsed `annotations:` block (null when absent or empty). The kernel re-derives `status` on every scan from the live hashes; clients should treat it as authoritative for the snapshot but never persist it across scans."
|
|
64
65
|
},
|
|
65
66
|
"isFavorite": {
|
|
66
67
|
"type": "boolean",
|
|
67
|
-
"description": "Per-node favorite flag set by the local user from the UI. Sourced from `state_node_favorites` (zone `state_`, persistent across scans). Decorated by the BFF on every `/api/nodes` response via in-memory Set lookup against the favorites table
|
|
68
|
+
"description": "Per-node favorite flag set by the local user from the UI. Sourced from `state_node_favorites` (zone `state_`, persistent across scans). Decorated by the BFF on every `/api/nodes` response via in-memory Set lookup against the favorites table, no SQL JOIN against `scan_nodes`. Absent on emissions that don't carry per-user state (e.g. `sm export --json`); consumers that don't recognise the field MUST ignore it."
|
|
68
69
|
}
|
|
69
70
|
},
|
|
70
71
|
"$defs": {
|
|
@@ -100,7 +101,7 @@
|
|
|
100
101
|
"root": {
|
|
101
102
|
"type": ["object", "null"],
|
|
102
103
|
"additionalProperties": true,
|
|
103
|
-
"description": "Parsed YAML root of the matching `.sm` sidecar. Mirrors the shape of `sidecar.schema.json` (top-level reserved blocks `identity` / `annotations` / `settings` / `audit` plus opt-in `<plugin-id>:` namespaces). Surfaced for the UI inspector's audit panel, plugin-contributions panel, and debug panel; NULL when the sidecar is absent or failed to parse. Note the duplication with `annotations` at this overlay level
|
|
104
|
+
"description": "Parsed YAML root of the matching `.sm` sidecar. Mirrors the shape of `sidecar.schema.json` (top-level reserved blocks `identity` / `annotations` / `settings` / `audit` plus opt-in `<plugin-id>:` namespaces). Surfaced for the UI inspector's audit panel, plugin-contributions panel, and debug panel; NULL when the sidecar is absent or failed to parse. Note the duplication with `annotations` at this overlay level, existing consumers read from `annotations`, new consumers read structured sub-fields off `root` (`root.identity.*`, `root.audit.*`, etc.). The duplication is intentional and documented; do NOT remove the top-level `annotations` field."
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://skill-map.dev/spec/v0/plugins-doctor.schema.json",
|
|
4
|
+
"title": "PluginsDoctorReport",
|
|
5
|
+
"description": "Machine-readable output of `sm plugins doctor --json`. Aggregates per-status counts across built-in and drop-in plugins plus the structured issue / warning lists the human renderer produces. The `elapsedMs` top-level field is the command's own wall-clock (see `cli-contract.md` §Elapsed time).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["ok", "kind", "counts", "issues", "warnings", "elapsedMs"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"ok": {
|
|
11
|
+
"type": "boolean",
|
|
12
|
+
"const": true,
|
|
13
|
+
"description": "Always `true` on the happy path. Error envelopes use the shared `{ ok: false, error: { code, message } }` shape from `cli-contract.md` §Error envelope and do NOT carry this schema's other fields. A doctor run that surfaces issues still returns `ok: true` (the verb succeeded; the issues live under `issues[]` and gate the exit code)."
|
|
14
|
+
},
|
|
15
|
+
"kind": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"const": "plugins.doctor",
|
|
18
|
+
"description": "Discriminator pinning this envelope to the plugins-doctor verb."
|
|
19
|
+
},
|
|
20
|
+
"counts": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"required": ["enabled", "disabled", "loaded", "incompatible", "invalid", "loadError", "warnings"],
|
|
23
|
+
"additionalProperties": false,
|
|
24
|
+
"description": "Aggregate counts across both built-in and drop-in plugins. `loaded` is the count of plugins whose runtime imported cleanly (status=enabled); `incompatible` folds the `incompatible-spec` and `incompatible-catalog` buckets; `invalid` is `invalid-manifest`; `loadError` is `load-error` + `id-collision`. The split mirrors the human table without committing to its label catalog.",
|
|
25
|
+
"properties": {
|
|
26
|
+
"enabled": { "type": "integer", "minimum": 0 },
|
|
27
|
+
"disabled": { "type": "integer", "minimum": 0 },
|
|
28
|
+
"loaded": { "type": "integer", "minimum": 0 },
|
|
29
|
+
"incompatible": { "type": "integer", "minimum": 0 },
|
|
30
|
+
"invalid": { "type": "integer", "minimum": 0 },
|
|
31
|
+
"loadError": { "type": "integer", "minimum": 0 },
|
|
32
|
+
"warnings": { "type": "integer", "minimum": 0 }
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"issues": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"description": "One entry per plugin in a non-success state (`incompatible-spec`, `incompatible-catalog`, `invalid-manifest`, `load-error`, `id-collision`). Empty when every plugin is `enabled` or intentionally `disabled`. Iteration order matches the human renderer.",
|
|
38
|
+
"items": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"required": ["id", "status", "reason"],
|
|
41
|
+
"additionalProperties": false,
|
|
42
|
+
"properties": {
|
|
43
|
+
"id": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"minLength": 1,
|
|
46
|
+
"description": "Plugin id (bundle granularity) or qualified id (`<bundle>/<ext>`) for extension granularity."
|
|
47
|
+
},
|
|
48
|
+
"status": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"enum": [
|
|
51
|
+
"incompatible-spec",
|
|
52
|
+
"incompatible-catalog",
|
|
53
|
+
"invalid-manifest",
|
|
54
|
+
"load-error",
|
|
55
|
+
"id-collision"
|
|
56
|
+
],
|
|
57
|
+
"description": "Failure mode. Matches the `IDiscoveredPlugin.status` enum minus `enabled` / `disabled` (those never land in `issues[]`)."
|
|
58
|
+
},
|
|
59
|
+
"reason": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"description": "Sanitised human-readable explanation. May be empty when the loader supplied none."
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"warnings": {
|
|
67
|
+
"type": "array",
|
|
68
|
+
"description": "Informational warnings that do NOT gate the exit code: `applicable-kind-unknown` (an extractor declares a `applicableKinds` referencing a kind no Provider emits) and `unknown-slot` (a view contribution targets a slot id absent from the closed catalog). Iteration order matches the human renderer.",
|
|
69
|
+
"items": {
|
|
70
|
+
"type": "object",
|
|
71
|
+
"required": ["id", "kind", "message"],
|
|
72
|
+
"additionalProperties": false,
|
|
73
|
+
"properties": {
|
|
74
|
+
"id": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"minLength": 1,
|
|
77
|
+
"description": "Qualified extension id (`<plugin>/<ext>`) for `applicable-kind-unknown`, or `<plugin>/<ext>/<contributionId>` for `unknown-slot`."
|
|
78
|
+
},
|
|
79
|
+
"kind": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"enum": ["applicable-kind-unknown", "unknown-slot"],
|
|
82
|
+
"description": "Warning discriminator."
|
|
83
|
+
},
|
|
84
|
+
"message": {
|
|
85
|
+
"type": "string",
|
|
86
|
+
"description": "Sanitised human-readable explanation."
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"elapsedMs": {
|
|
92
|
+
"type": "integer",
|
|
93
|
+
"minimum": 0,
|
|
94
|
+
"description": "Command's own wall-clock duration in milliseconds (see `cli-contract.md` §Elapsed time)."
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"type": "string",
|
|
45
45
|
"enum": ["bundle", "extension"],
|
|
46
46
|
"default": "bundle",
|
|
47
|
-
"description": "Toggle granularity for this plugin. `bundle` (default)
|
|
47
|
+
"description": "Toggle granularity for this plugin. `bundle` (default), the plugin id is the only enable/disable key; the whole set of extensions follows the toggle. `extension`, each extension is independently toggle-able under its qualified id `<plugin-id>/<extension-id>`. Built-in bundles use the same field: the `claude` bundle is `bundle` (the Provider and its kind-aware extractors form a coherent provider); the `core` bundle is `extension` (every kernel built-in is removable per the spec promise that no extension is privileged). Plugin authors should keep the default unless their plugin ships several orthogonal capabilities a user might reasonably want piecemeal."
|
|
48
48
|
},
|
|
49
49
|
"storage": {
|
|
50
50
|
"type": "object",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"propertyNames": {
|
|
84
84
|
"pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$"
|
|
85
85
|
},
|
|
86
|
-
"description": "Plugin user-configurable settings. Each entry picks an `input-type` from the closed catalog at `input-types.schema.json#/$defs/InputTypeName`. The plugin author NEVER writes JSON Schema for settings
|
|
86
|
+
"description": "Plugin user-configurable settings. Each entry picks an `input-type` from the closed catalog at `input-types.schema.json#/$defs/InputTypeName`. The plugin author NEVER writes JSON Schema for settings, they pick by `type` name and provide per-type parameters (label, default, min/max, options for enums, etc.). The kernel exposes the resolved settings to extractors via `ctx.settings.<settingId>` (or via the runtime as `IPluginSettings`); the UI generates a form per declaration; the CLI's `sm plugins config <id>` exposes the same surface. Settings are read once at extractor invocation; changing a setting requires `sm scan` to re-emit (per ROADMAP.md decision D4)."
|
|
87
87
|
},
|
|
88
88
|
"author": { "type": "string" },
|
|
89
89
|
"license": { "type": "string", "description": "SPDX identifier." },
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://skill-map.dev/spec/v0/project-config.schema.json",
|
|
4
4
|
"title": "ProjectConfig",
|
|
5
|
-
"description": "Shape of `.skill-map/settings.json` (and its `.skill-map/settings.local.json` partner) inside a scope. Loaded by the layered config hierarchy (library defaults → user → user-local → project → project-local → env/flags) and deep-merged per key. All fields optional; defaults apply when absent. camelCase keys throughout
|
|
5
|
+
"description": "Shape of `.skill-map/settings.json` (and its `.skill-map/settings.local.json` partner) inside a scope. Loaded by the layered config hierarchy (library defaults → user → user-local → project → project-local → env/flags) and deep-merged per key. All fields optional; defaults apply when absent. camelCase keys throughout, consistent with the rest of the spec.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"properties": {
|
|
@@ -54,19 +54,19 @@
|
|
|
54
54
|
"debounceMs": {
|
|
55
55
|
"type": "integer",
|
|
56
56
|
"minimum": 0,
|
|
57
|
-
"description": "Milliseconds to wait after the last filesystem event before triggering an incremental scan. Groups bursts (editor saves, branch switches, package installs) into a single scan pass. Default 300. Set to 0 to disable debouncing
|
|
57
|
+
"description": "Milliseconds to wait after the last filesystem event before triggering an incremental scan. Groups bursts (editor saves, branch switches, package installs) into a single scan pass. Default 300. Set to 0 to disable debouncing, every filesystem event triggers a scan immediately."
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
},
|
|
61
61
|
"extraFolders": {
|
|
62
62
|
"type": "array",
|
|
63
63
|
"items": { "type": "string" },
|
|
64
|
-
"description": "**Privacy-sensitive, project-local only** (per `core/config/helper:PROJECT_LOCAL_ONLY_KEYS`) when entries point outside the project
|
|
64
|
+
"description": "**Privacy-sensitive, project-local only** (per `core/config/helper:PROJECT_LOCAL_ONLY_KEYS`) when entries point outside the project, opens disk access there. Default `[]`. Additional directories appended to the scan roots; same parsing / indexing as the project root. Paths starting with `~` resolve against the user home; relative paths resolve against the project root. Reference impl gates writes that introduce out-of-project paths behind `--yes` (CLI) and a confirm dialog (UI). **Stripped with a warning when found in the committed `project` layer**, paths are inherently per-machine and must not travel via the shared repo. This is the ONLY mechanism to extend the scan beyond the project root: skill-map does NOT auto-include the user's HOME based on Provider hints, every out-of-project path must be listed here explicitly."
|
|
65
65
|
},
|
|
66
66
|
"referencePaths": {
|
|
67
67
|
"type": "array",
|
|
68
68
|
"items": { "type": "string" },
|
|
69
|
-
"description": "**Privacy-sensitive, project-local only** (per `core/config/helper:PROJECT_LOCAL_ONLY_KEYS`) when entries point outside the project
|
|
69
|
+
"description": "**Privacy-sensitive, project-local only** (per `core/config/helper:PROJECT_LOCAL_ONLY_KEYS`) when entries point outside the project, opens read-only disk access for link validation only. Default `[]`. Directories walked in parallel by the scan to collect existing absolute paths into a side set; the kernel passes the set to analyzers via `IAnalyzerContext.referenceablePaths` so `core/broken-ref` can resolve a link against the filesystem when the in-graph lookup misses. Files under these paths are NOT parsed and NOT indexed as nodes, the only effect is suppressing `broken-ref` warnings for targets that exist on disk outside the scan. Same write-gate analyzers as `extraFolders`. **Stripped with a warning when found in the committed `project` layer**."
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
},
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"properties": {
|
|
89
89
|
"share": {
|
|
90
90
|
"type": "boolean",
|
|
91
|
-
"description": "When true, `./.skill-map/skill-map.db` is expected to be committed
|
|
91
|
+
"description": "When true, `./.skill-map/skill-map.db` is expected to be committed, teams remove it from `.gitignore` so the execution log becomes a shared artefact. Stability: experimental. Default false."
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
},
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"ttlSeconds": {
|
|
100
100
|
"type": "integer",
|
|
101
101
|
"minimum": 1,
|
|
102
|
-
"description": "Global fallback TTL (seconds) used when an action manifest omits `expectedDurationSeconds
|
|
102
|
+
"description": "Global fallback TTL (seconds) used when an action manifest omits `expectedDurationSeconds`, typically `mode: local` actions where the field is advisory. Default 3600. The submit-time resolution still applies `graceMultiplier` and `minimumTtlSeconds`."
|
|
103
103
|
},
|
|
104
104
|
"graceMultiplier": { "type": "number", "minimum": 1, "description": "Default grace multiplier applied to `expectedDurationSeconds`. Default 3." },
|
|
105
105
|
"minimumTtlSeconds": { "type": "integer", "minimum": 1, "description": "Floor for computed TTL. Default 60." },
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
"failed": {
|
|
127
127
|
"type": ["integer", "null"],
|
|
128
128
|
"minimum": 1,
|
|
129
|
-
"description": "Seconds after `finishedAt` before a `failed` job is eligible for pruning. `null` = never auto-prune. Default `null
|
|
129
|
+
"description": "Seconds after `finishedAt` before a `failed` job is eligible for pruning. `null` = never auto-prune. Default `null`, failed jobs are kept for post-mortem."
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
}
|
|
@@ -142,12 +142,12 @@
|
|
|
142
142
|
},
|
|
143
143
|
"allowEditSmFiles": {
|
|
144
144
|
"type": "boolean",
|
|
145
|
-
"description": "**Project-local only** (per `core/config/helper:PROJECT_LOCAL_ONLY_KEYS`). Grants this project permission to create / modify `.sm` annotation sidecars next to source files. Default `false`. The first time a verb or BFF route attempts a `.sm` write while this is `false`, the kernel raises `EConsentRequiredError`. The CLI surfaces it as an interactive `confirm()` prompt (or `--yes` bypass); the BFF returns 412 `confirm-required` so the UI can open a `ConfirmationService` dialog. On accept the flag is persisted to `<cwd>/.skill-map/settings.local.json` (gitignored, per-checkout) and never asked again. On decline the operation aborts WITHOUT persisting the rejection
|
|
145
|
+
"description": "**Project-local only** (per `core/config/helper:PROJECT_LOCAL_ONLY_KEYS`). Grants this project permission to create / modify `.sm` annotation sidecars next to source files. Default `false`. The first time a verb or BFF route attempts a `.sm` write while this is `false`, the kernel raises `EConsentRequiredError`. The CLI surfaces it as an interactive `confirm()` prompt (or `--yes` bypass); the BFF returns 412 `confirm-required` so the UI can open a `ConfirmationService` dialog. On accept the flag is persisted to `<cwd>/.skill-map/settings.local.json` (gitignored, per-checkout) and never asked again. On decline the operation aborts WITHOUT persisting the rejection, the next attempt re-asks. **Stripped with a warning when found in the committed `project` layer** (`<cwd>/.skill-map/settings.json`), each developer consents independently."
|
|
146
146
|
},
|
|
147
147
|
"updateCheck": {
|
|
148
148
|
"type": "object",
|
|
149
149
|
"additionalProperties": false,
|
|
150
|
-
"description": "Controls the once-per-day notification when a newer @skill-map/cli release is published on npm. Disabled in CI, when SM_NO_UPDATE_CHECK=1, when stderr is not a TTY, or when the project DB is missing. **User-scope only**: this key SHOULD live in `~/.skill-map/settings.json` and the reference implementation forces user-scope reads via `core/config/helper:USER_ONLY_KEYS
|
|
150
|
+
"description": "Controls the once-per-day notification when a newer @skill-map/cli release is published on npm. Disabled in CI, when SM_NO_UPDATE_CHECK=1, when stderr is not a TTY, or when the project DB is missing. **User-scope only**: this key SHOULD live in `~/.skill-map/settings.json` and the reference implementation forces user-scope reads via `core/config/helper:USER_ONLY_KEYS`, a project-layer entry from an older install continues to validate but is silently ignored at read time. `sm config set` rejects writes to the project layer for this key (rerun with `-g`); the Settings UI's General section persists toggles to the user layer.",
|
|
151
151
|
"properties": {
|
|
152
152
|
"enabled": {
|
|
153
153
|
"type": "boolean",
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://skill-map.dev/spec/v0/refresh-report.schema.json",
|
|
4
|
+
"title": "RefreshReport",
|
|
5
|
+
"description": "Machine-readable output of `sm refresh <node.path> --json` and `sm refresh --stale --json`. Reports the count of enrichment rows persisted across the targeted node set (universal enrichment layer per `architecture.md` §A.8). The `elapsedMs` top-level field is the command's own wall-clock (see `cli-contract.md` §Elapsed time).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["ok", "kind", "refreshed", "nodes", "elapsedMs"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"ok": {
|
|
11
|
+
"type": "boolean",
|
|
12
|
+
"const": true,
|
|
13
|
+
"description": "Always `true` on the happy path. Error envelopes use the shared `{ ok: false, error: { code, message } }` shape from `cli-contract.md` §Error envelope and do NOT carry this schema's other fields."
|
|
14
|
+
},
|
|
15
|
+
"kind": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"const": "refresh.report",
|
|
18
|
+
"description": "Discriminator pinning this envelope to the refresh verb. Consumers can branch on this when handling multiple `--json` shapes."
|
|
19
|
+
},
|
|
20
|
+
"refreshed": {
|
|
21
|
+
"type": "integer",
|
|
22
|
+
"minimum": 0,
|
|
23
|
+
"description": "Total count of enrichment rows persisted across all targeted nodes. Equal to the sum of every `nodes[i].enrichments`. Zero on `--stale` with an empty stale set or when no extractor matched any targeted node's kind."
|
|
24
|
+
},
|
|
25
|
+
"nodes": {
|
|
26
|
+
"type": "array",
|
|
27
|
+
"description": "Per-node breakdown of the refresh, in the iteration order the verb processed targets. Single-node mode emits exactly one entry; `--stale` emits one entry per node whose stale row drove inclusion. Empty array when nothing was refreshed.",
|
|
28
|
+
"items": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"required": ["path", "enrichments"],
|
|
31
|
+
"additionalProperties": false,
|
|
32
|
+
"properties": {
|
|
33
|
+
"path": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"minLength": 1,
|
|
36
|
+
"description": "Scope-root-relative `node.path` of the refreshed node."
|
|
37
|
+
},
|
|
38
|
+
"enrichments": {
|
|
39
|
+
"type": "integer",
|
|
40
|
+
"minimum": 0,
|
|
41
|
+
"description": "Count of enrichment rows the extractor pipeline produced and persisted for this node."
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"elapsedMs": {
|
|
47
|
+
"type": "integer",
|
|
48
|
+
"minimum": 0,
|
|
49
|
+
"description": "Command's own wall-clock duration in milliseconds (see `cli-contract.md` §Elapsed time)."
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://skill-map.dev/spec/v0/report-base-deterministic.schema.json",
|
|
4
4
|
"title": "ReportBaseDeterministic",
|
|
5
|
-
"description": "Universal base for deterministic Action reports. Every deterministic Action's report MUST extend this base via `allOf` + `$ref`. Symmetric with `report-base.schema.json` (the probabilistic / LLM base, which carries `confidence` + `safety`); deterministic vs probabilistic is the orthogonal axis declared by the Action manifest's `mode` field. Fields: `ok` (boolean
|
|
5
|
+
"description": "Universal base for deterministic Action reports. Every deterministic Action's report MUST extend this base via `allOf` + `$ref`. Symmetric with `report-base.schema.json` (the probabilistic / LLM base, which carries `confidence` + `safety`); deterministic vs probabilistic is the orthogonal axis declared by the Action manifest's `mode` field. Fields: `ok` (boolean, did the Action complete its logical work?), plus action-specific keys via `additionalProperties: true`. Action-specific shapes (e.g. bump's `version` / `noop` / `reason`) ride on the open extension.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": ["ok"],
|
|
8
8
|
"additionalProperties": true,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://skill-map.dev/spec/v0/sidecar.schema.json",
|
|
4
4
|
"title": "Sidecar",
|
|
5
|
-
"description": "Root shape of a co-located YAML sidecar (`<basename>.sm` next to `<basename>.md`). The `.sm` file IS the annotations file
|
|
5
|
+
"description": "Root shape of a co-located YAML sidecar (`<basename>.sm` next to `<basename>.md`). The `.sm` file IS the annotations file, every key under it is, conceptually, an annotation on the node. The YAML root organizes those annotations into structural blocks: `identity` (anchor + drift-detection hashes), `annotations` (the curated catalog of conventional fields), `audit` (timestamps), `settings` (reserved), and arbitrary `<plugin-id>:` namespaces for plugin-contributed data. Vendor file (`<basename>.md`) stays untouched. Schema is `additionalProperties: true` so plugins can add namespaces without coordination; the built-in `unknown-field` analyzer warns on truly unrecognized root keys (typo guard). Format is YAML, comments via `#`, multiline strings via `|` / `>`, permissive types per the YAML 1.2 spec. See `architecture.md` §Annotation system and ROADMAP §Step 9.6 for the design rationale.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": ["identity"],
|
|
8
8
|
"additionalProperties": true,
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"audit": {
|
|
24
24
|
"$ref": "#/$defs/audit",
|
|
25
|
-
"description": "Skill-map's audit trail. Populated by the built-in `bump` Action starting in Step 9.6.3 (Decision #125). All fields are optional at the property level
|
|
25
|
+
"description": "Skill-map's audit trail. Populated by the built-in `bump` Action starting in Step 9.6.3 (Decision #125). All fields are optional at the property level, the Action atomically fills `lastBumpedAt` + `lastBumpedBy` on every bump and `createdAt` + `createdBy` on first creation. `additionalProperties: true` so future fields land additively."
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"$defs": {
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"path": {
|
|
61
61
|
"type": "string",
|
|
62
62
|
"minLength": 1,
|
|
63
|
-
"description": "Relative path from the scope root to the `.md` file this sidecar annotates. Matches the canonical Node identifier (see `node.schema.json` #/properties/path). Survives content edits; breaks on file moves
|
|
63
|
+
"description": "Relative path from the scope root to the `.md` file this sidecar annotates. Matches the canonical Node identifier (see `node.schema.json` #/properties/path). Survives content edits; breaks on file moves, `sm refresh` re-points the sidecar after a move."
|
|
64
64
|
},
|
|
65
65
|
"bodyHash": {
|
|
66
66
|
"type": "string",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://skill-map.dev/spec/v0/summaries/markdown.schema.json",
|
|
4
4
|
"title": "SummaryMarkdown",
|
|
5
|
-
"description": "Report produced by `markdown-summarizer` for a single `markdown` node (the format-named generic fallback owned by the built-in `core/markdown` Provider
|
|
5
|
+
"description": "Report produced by `markdown-summarizer` for a single `markdown` node (the format-named generic fallback owned by the built-in `core/markdown` Provider, see `architecture.md` §Provider · dispatch order). Extends `report-base.schema.json`. Stability: experimental.",
|
|
6
6
|
"allOf": [
|
|
7
7
|
{ "$ref": "../report-base.schema.json" }
|
|
8
8
|
],
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://skill-map.dev/spec/v0/summaries/skill.schema.json",
|
|
4
4
|
"title": "SummarySkill",
|
|
5
|
-
"description": "Report produced by `skill-summarizer` for a single `skill` node. Extends `report-base.schema.json`. Stability: experimental
|
|
5
|
+
"description": "Report produced by `skill-summarizer` for a single `skill` node. Extends `report-base.schema.json`. Stability: experimental, field set may tighten as real summarizer output stabilizes.",
|
|
6
6
|
"allOf": [
|
|
7
7
|
{ "$ref": "../report-base.schema.json" }
|
|
8
8
|
],
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://skill-map.dev/spec/v0/view-slots.schema.json",
|
|
4
4
|
"title": "ViewSlots",
|
|
5
|
-
"description": "Closed catalog of view slots. A view slot is a kernel-published handle that names a visual surface in the UI, fixes the renderer that draws there, and fixes the payload shape the plugin emits. The plugin author picks ONE slot per view contribution; the kernel validates `ctx.emitContribution(id, payload)` against that slot's payload schema in `$defs.payloads`. There is no separate notion of a 'contract'
|
|
5
|
+
"description": "Closed catalog of view slots. A view slot is a kernel-published handle that names a visual surface in the UI, fixes the renderer that draws there, and fixes the payload shape the plugin emits. The plugin author picks ONE slot per view contribution; the kernel validates `ctx.emitContribution(id, payload)` against that slot's payload schema in `$defs.payloads`. There is no separate notion of a 'contract', the slot IS the contract. Closed catalog by design: every new slot requires a spec change + UI renderer mount + scaffolder support + conformance fixtures + tests. Compounds catalog evolution cost; see ROADMAP.md §UI contribution system → 'Known limitations carried forward'. Slots are versioned via the manifest field `catalogCompat` (semver against the catalog as a whole), not per-slot.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"$defs": {
|
|
8
8
|
"SlotName": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"minLength": 1,
|
|
36
36
|
"maxLength": 64,
|
|
37
37
|
"pattern": "^(?:pi pi-[a-z0-9-]+|pi-[a-z0-9-]+|fa-(?:solid|regular|brands) fa-[a-z0-9-]+|fa-[a-z0-9-]+|[^a-zA-Z].*)$",
|
|
38
|
-
"description": "Single string, prefix-discriminated by the UI. Four valid shapes: (1) emoji
|
|
38
|
+
"description": "Single string, prefix-discriminated by the UI. Four valid shapes: (1) emoji, any value starting with a non-ASCII-letter codepoint renders as text; (2) PrimeIcons, `pi-foo` or `pi pi-foo` renders as `<i class=\"pi pi-foo\">`; (3) FontAwesome explicit family, `fa-solid fa-foo` / `fa-regular fa-foo` / `fa-brands fa-foo` passes through as-is; (4) FontAwesome shorthand, `fa-foo` (no family token) defaults to `fa-solid fa-foo`. Bare class names without a `pi-` / `fa-` prefix are rejected at manifest load (invalid-manifest). Unknown PrimeIcons / FontAwesome names render no icon (silent fallback) plus a console warning."
|
|
39
39
|
},
|
|
40
40
|
"IViewContribution": {
|
|
41
41
|
"type": "object",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"priority": {
|
|
68
68
|
"type": "number",
|
|
69
69
|
"default": 100,
|
|
70
|
-
"description": "Optional ordering hint. Slots configured with `order: 'priority'` sort contributions ASC by this value, with alphabetical tie-break by qualified id. Default 100
|
|
70
|
+
"description": "Optional ordering hint. Slots configured with `order: 'priority'` sort contributions ASC by this value, with alphabetical tie-break by qualified id. Default 100, plugins use it to suggest where their contribution belongs relative to others sharing the same slot. The slot has the final say on whether `priority` is honoured at all."
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
73
|
"allOf": [
|
|
@@ -101,12 +101,12 @@
|
|
|
101
101
|
"properties": {
|
|
102
102
|
"icon": {
|
|
103
103
|
"$ref": "#/$defs/IconString",
|
|
104
|
-
"description": "Optional payload-time icon override. When omitted the manifest-declared `icon` (which is required for this slot
|
|
104
|
+
"description": "Optional payload-time icon override. When omitted the manifest-declared `icon` (which is required for this slot, see `IViewContribution.allOf`) is rendered instead. Lets a plugin emit a different icon per node without redeclaring the manifest."
|
|
105
105
|
},
|
|
106
106
|
"severity": { "$ref": "#/$defs/Severity" },
|
|
107
107
|
"tooltip": { "type": "string", "maxLength": 256 }
|
|
108
108
|
},
|
|
109
|
-
"description": "Single icon per node
|
|
109
|
+
"description": "Single icon per node, small standalone marker rendered next to the card title. The manifest requires `icon`; the payload optionally overrides it per node and may add `severity` (color tint) and `tooltip`. No counts, no labels, for chip + number use a counter slot; for label + severity use a tag slot. 'Empty' for `emitWhenEmpty` is the absence of both payload `icon` and a manifest fallback (in practice never empty since the manifest icon is required)."
|
|
110
110
|
},
|
|
111
111
|
"card.subtitle.left": { "$ref": "#/$defs/payloads/_counter" },
|
|
112
112
|
"card.footer.left": { "$ref": "#/$defs/payloads/_counter" },
|
|
@@ -286,7 +286,7 @@
|
|
|
286
286
|
"type": "string",
|
|
287
287
|
"minLength": 1,
|
|
288
288
|
"maxLength": 512,
|
|
289
|
-
"description": "Node path within the scope. Resolved by the UI to a clickable link via `Router.navigate
|
|
289
|
+
"description": "Node path within the scope. Resolved by the UI to a clickable link via `Router.navigate`, never rendered as a raw `[href]` (per the renderer attr-sanitization analyzer)."
|
|
290
290
|
},
|
|
291
291
|
"label": { "type": "string", "minLength": 1, "maxLength": 128 },
|
|
292
292
|
"kind": {
|
|
@@ -329,7 +329,7 @@
|
|
|
329
329
|
"tooltip": { "type": "string", "maxLength": 256 },
|
|
330
330
|
"severity": { "$ref": "#/$defs/Severity" }
|
|
331
331
|
},
|
|
332
|
-
"description": "Single value summarizing the entire scope. Emitted ONCE per scan (not per node). Plugins use `ctx.emitScopeContribution(...)` (analyzer context)
|
|
332
|
+
"description": "Single value summarizing the entire scope. Emitted ONCE per scan (not per node). Plugins use `ctx.emitScopeContribution(...)` (analyzer context), extractors do not see `emitScopeContribution`."
|
|
333
333
|
}
|
|
334
334
|
}
|
|
335
335
|
}
|
package/versioning.md
CHANGED
|
@@ -13,7 +13,7 @@ A given CLI release declares the spec range it implements (e.g. `"specCompat": "
|
|
|
13
13
|
|
|
14
14
|
## Semver for the spec
|
|
15
15
|
|
|
16
|
-
Patch, minor, major have precise meaning for a specification
|
|
16
|
+
Patch, minor, major have precise meaning for a specification, different from code.
|
|
17
17
|
|
|
18
18
|
| Bump | Allowed changes | Examples |
|
|
19
19
|
|---|---|---|
|
|
@@ -30,13 +30,13 @@ 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
|
|
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
|
|
|
37
37
|
- Editorial prose, examples, diagrams.
|
|
38
38
|
- README layout, cross-link structure.
|
|
39
|
-
- Filenames inside `../src/` (reference impl)
|
|
39
|
+
- Filenames inside `../src/` (reference impl), never referenced from spec normatively.
|
|
40
40
|
- Internal commentary inside `../ROADMAP.md` and `../CLAUDE.md`.
|
|
41
41
|
|
|
42
42
|
## Stability tags
|
|
@@ -63,16 +63,16 @@ 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
|
|
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
|
|
74
|
-
- **Spec `1.2.0` + CLI `0.8.0
|
|
75
|
-
- **Spec `2.0.0` + CLI `1.4.0
|
|
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"`.
|
|
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
|
|