@skill-map/spec 0.51.0 → 0.53.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 +20 -0
- package/README.md +1 -1
- package/architecture.md +5 -5
- package/cli-contract.md +1 -3
- package/conformance/cases/sidecar-end-to-end.json +3 -5
- package/conformance/coverage.md +2 -2
- package/db-schema.md +2 -2
- package/index.json +15 -15
- package/job-events.md +1 -1
- package/package.json +1 -1
- package/plugin-author-guide.md +4 -4
- package/schemas/annotations.schema.json +2 -12
- package/schemas/extensions/provider.schema.json +1 -1
- package/schemas/issue.schema.json +1 -1
- package/schemas/link.schema.json +2 -2
- package/schemas/signal.schema.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Spec changelog
|
|
2
2
|
|
|
3
|
+
## 0.53.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Ship the `core/node-bump` action and the `core/annotation-stale` analyzer as `experimental`, so the sidecar bump/drift surface is disabled by default (Decision #128). Gated as a unit: with the action disabled no Bump button projects, and with the drift analyzer disabled no stale finding fires. The `sidecar-end-to-end` conformance case drops its `annotation-stale` assertion accordingly (a default scan now surfaces only `annotation-orphan`; the node still carries the derived `sidecar.status`).
|
|
8
|
+
|
|
9
|
+
## User-facing
|
|
10
|
+
|
|
11
|
+
The Bump button and the sidecar drift ("stale") finding are off by default now. Staleness still shows on the node's status; re-enable with `sm plugins enable core/node-bump core/annotation-stale` or the Settings toggles.
|
|
12
|
+
|
|
13
|
+
## 0.52.0
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- Remove the `supersede` feature end to end. The `supersedes` link kind is dropped from the global link-kind enum, the `annotations.supersedes` and `supersededBy` sidecar fields are removed from the spec, and the three built-ins that powered it (the `core/annotations` extractor, the `core/node-supersede` action, the `core/node-superseded` analyzer) are deleted. Scans no longer produce supersede links, and the inspector drops the Supersede button and the superseded-by banner.
|
|
18
|
+
|
|
19
|
+
## User-facing
|
|
20
|
+
|
|
21
|
+
The Supersede inspector button, the "superseded by" banner, and supersede links on the map are gone. The `supersedes` and `supersededBy` keys in `.sm` sidecars are no longer recognized, remove them from any sidecar that still declares them.
|
|
22
|
+
|
|
3
23
|
## 0.51.0
|
|
4
24
|
|
|
5
25
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ These are implementation decisions. The reference impl picks them (see [`../AGEN
|
|
|
39
39
|
Two analyzers govern every identifier in the spec. They are **normative**.
|
|
40
40
|
|
|
41
41
|
- **Filesystem artefacts use kebab-case.** Every file and directory in `spec/` (and in any conforming implementation), `scan-result.schema.json`, `job-lifecycle.md`, `report-base.schema.json`, `auto-rename-medium` (as an `issue.analyzerId` value), `direct-override` (as a `safety.injectionType` enum value), and so on, is kebab-case lowercase. Enum values and issue analyzer ids follow the same convention so they can be echoed back into URLs, filenames, and log keys without escaping.
|
|
42
|
-
- **JSON content uses camelCase.** Every key inside a JSON Schema, frontmatter block, config file, plugin manifest, action manifest, job record, report, event payload, or API response is camelCase: `whatItDoes`, `injectionDetected`, `expectedTools`, `
|
|
42
|
+
- **JSON content uses camelCase.** Every key inside a JSON Schema, frontmatter block, config file, plugin manifest, action manifest, job record, report, event payload, or API response is camelCase: `whatItDoes`, `injectionDetected`, `expectedTools`, `sourceVersion`, `docsUrl`, `examplesUrl`, `ttlSeconds`, `runId`, `jobId`. This matches the JS/TS ecosystem the reference impl ships in and the Kysely `CamelCasePlugin` that bridges to the `snake_case` SQL layer, but the analyzer is spec-level, not implementation-level: an alternative implementation in any language still exposes camelCase JSON keys.
|
|
43
43
|
|
|
44
44
|
The SQL persistence layer is the sole exception: tables, columns, and migration filenames use `snake_case` (see `db-schema.md`). That boundary is crossed only inside a storage adapter; nothing that leaves the kernel should ever be `snake_case`.
|
|
45
45
|
|
package/architecture.md
CHANGED
|
@@ -398,7 +398,7 @@ Default `undefined` ≡ empty map ≡ no reserved names. Links to non-reserved t
|
|
|
398
398
|
|
|
399
399
|
The `Extractor` runtime contract is `extract(ctx) → void`. The extractor emits its work through three callbacks the kernel binds onto `ctx`:
|
|
400
400
|
|
|
401
|
-
- `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`, `
|
|
401
|
+
- `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`, `points`) 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.
|
|
402
402
|
- `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).
|
|
403
403
|
- `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).
|
|
404
404
|
|
|
@@ -406,7 +406,7 @@ Extractors are deterministic-only; `ctx.runner` is NOT exposed on the Extractor
|
|
|
406
406
|
|
|
407
407
|
### Extractor · Signal IR (opt-in)
|
|
408
408
|
|
|
409
|
-
In addition to the `emitLink` path, Extractors MAY emit **Signals** via `ctx.emitSignal(signal)`. A Signal is a candidate detection: one or many alternative interpretations of the same body or frontmatter location, each carrying its own kind, target, confidence, and rationale. See [`signal.schema.json`](./schemas/signal.schema.json) for the full contract. The Signal IR is opt-in; an extractor whose detection is unambiguous (
|
|
409
|
+
In addition to the `emitLink` path, Extractors MAY emit **Signals** via `ctx.emitSignal(signal)`. A Signal is a candidate detection: one or many alternative interpretations of the same body or frontmatter location, each carrying its own kind, target, confidence, and rationale. See [`signal.schema.json`](./schemas/signal.schema.json) for the full contract. The Signal IR is opt-in; an extractor whose detection is unambiguous (`[text](file.md)` markdown links, plain `https://…` URLs) is encouraged to keep emitting Links directly with `ctx.emitLink`. Signals exist for the cases the resolver actually helps: detections where a single body token can plausibly mean several things and the active provider's rules need to decide.
|
|
410
410
|
|
|
411
411
|
The kernel's **resolver phase** runs after extraction completes and before analysis starts. For each Signal, the resolver:
|
|
412
412
|
|
|
@@ -670,7 +670,7 @@ Skill-map's own metadata layer (versioning, supersession, provenance, taxonomy,
|
|
|
670
670
|
Two schemas describe the wire shape:
|
|
671
671
|
|
|
672
672
|
- [`schemas/sidecar.schema.json`](./schemas/sidecar.schema.json), root shape with reserved blocks `identity` (anchor + drift hashes), `annotations` (the conventional catalog), `settings` (reserved), `audit` (write trail), plus opt-in `<plugin-id>:` namespacing.
|
|
673
|
-
- [`schemas/annotations.schema.json`](./schemas/annotations.schema.json), curated
|
|
673
|
+
- [`schemas/annotations.schema.json`](./schemas/annotations.schema.json), curated 8-field catalog: versioning (`version`, `stability`), provenance (`authors`, `license`, `source`, `sourceVersion`), taxonomy (`tags`), docs (`docsUrl`). The activity timestamp lives in the reserved `audit:` block (`audit.lastBumpedAt`), not in `annotations:`. `additionalProperties: true` so plugins or users add custom keys without coordination; the built-in `unknown-field` analyzer warns on truly unrecognized keys (typo guard).
|
|
674
674
|
|
|
675
675
|
### Identity and drift
|
|
676
676
|
|
|
@@ -682,7 +682,7 @@ At scan time the kernel re-computes the live hashes and compares against the sto
|
|
|
682
682
|
|
|
683
683
|
The deterministic built-in `core/node-bump` Action produces a sidecar patch:
|
|
684
684
|
|
|
685
|
-
- Increments `annotations.version` by 1 (or sets to `1` if missing, single integer monotonic, orthogonal to `stability`; major bumps are not a concept, the convention for breaking changes is "create a new node
|
|
685
|
+
- Increments `annotations.version` by 1 (or sets to `1` if missing, single integer monotonic, orthogonal to `stability`; major bumps are not a concept, the convention for breaking changes is "create a new node and retire the old").
|
|
686
686
|
- Refreshes `identity.bodyHash` and `identity.frontmatterHash` to the live values.
|
|
687
687
|
- Stamps `audit.lastBumpedAt` (ISO 8601 datetime) and `audit.lastBumpedBy` (the Git author name from `git config user.name` when the project is a Git repo; otherwise the channel literal `'cli'`, `'ui'`, or `'plugin:<id>'`).
|
|
688
688
|
- On first-time creation also stamps `audit.createdAt` and `audit.createdBy` (set once, stable thereafter).
|
|
@@ -879,7 +879,7 @@ Endpoints under `/api/contributions/*`:
|
|
|
879
879
|
- `GET /api/contributions/registered`, runtime catalog. Mirror of `/api/annotations/registered`. Envelope variant `kind: 'contributions.registered'` (see [`schemas/api/rest-envelope.schema.json`](./schemas/api/rest-envelope.schema.json)).
|
|
880
880
|
- `GET /api/contributions/:pluginId/:extensionId/:contributionId?path=...`, lazy per-node fetch for inspector slots. **Three URL segments** mirror the qualified id `<pluginId>/<extensionId>/<contributionId>`. Filters by qualified id + node path; the BFF enforces `pluginId` ↔ namespace at the route level, no cross-plugin reads via this endpoint.
|
|
881
881
|
|
|
882
|
-
The `inspector.action.button` contribution is **self-projected by the dispatching Action's own `project(ctx)`** (scan-time, deterministic), not by a separate projector Analyzer. The Action computes the per-node `enabled` / `disabledReason` and the prompt `options` / `defaultValue` from the live graph it receives, emits the button, and is itself the dispatch target. (This reverses the earlier "an Analyzer projects the button" shape; the projector
|
|
882
|
+
The `inspector.action.button` contribution is **self-projected by the dispatching Action's own `project(ctx)`** (scan-time, deterministic), not by a separate projector Analyzer. The Action computes the per-node `enabled` / `disabledReason` and the prompt `options` / `defaultValue` from the live graph it receives, emits the button, and is itself the dispatch target. (This reverses the earlier "an Analyzer projects the button" shape; the projector Analyzer `core/tags` was removed and `core/annotation-stale` keeps only its badge + issue.) The slot dispatches to a generic Action endpoint, sibling of the single-node `POST /api/sidecar/bump`:
|
|
883
883
|
|
|
884
884
|
- `POST /api/actions/:id`, dispatch a kernel Action by qualified id (`:id` is the `<plugin>/<action>` from the button payload's `actionId`). Body carries the target `nodePath`, the optional reserved `input` object (Steps 2+), and the consent fields `confirm` / `always` (see §Annotation system → Write consent) for `.sm`-writing Actions. The kernel resolves the Action in its registry (unknown id → 404), runs it against the node, and answers the action-result envelope `kind: 'action.applied'` (`{ value: { actionId, nodePath, report }, elapsedMs }`, see [`schemas/api/rest-envelope.schema.json`](./schemas/api/rest-envelope.schema.json)). `POST /api/sidecar/bump` remains the dedicated single-purpose route for `core/node-bump` (`kind: 'sidecar.bumped'`); the generic dispatch route shares the same action-result envelope variant.
|
|
885
885
|
|
package/cli-contract.md
CHANGED
|
@@ -376,10 +376,9 @@ identity:
|
|
|
376
376
|
|
|
377
377
|
annotations:
|
|
378
378
|
version: 3
|
|
379
|
-
# Deprecated because v0.6 architecture
|
|
379
|
+
# Deprecated because the v0.6 architecture replaced this skill.
|
|
380
380
|
# See decision #142 in ROADMAP for context.
|
|
381
381
|
stability: deprecated
|
|
382
|
-
supersededBy: agents/reviewer-v2.md
|
|
383
382
|
tags:
|
|
384
383
|
- review
|
|
385
384
|
- typescript # only TS, not JS
|
|
@@ -390,7 +389,6 @@ annotations:
|
|
|
390
389
|
```yaml
|
|
391
390
|
annotations:
|
|
392
391
|
stability: deprecated
|
|
393
|
-
supersededBy: agents/reviewer-v2.md
|
|
394
392
|
tags:
|
|
395
393
|
- review
|
|
396
394
|
- typescript
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://skill-map.ai/spec/v0/conformance-case.schema.json",
|
|
3
3
|
"id": "sidecar-end-to-end",
|
|
4
|
-
"description": "Step 9.6.6 — co-located `.sm` sidecar end-to-end. Scanning a fixture that carries a stale sidecar (wrong `identity.{bodyHash,frontmatterHash}`) plus an orphan sidecar (no sibling `.md`) MUST surface `sidecar.status` on the node and emit
|
|
4
|
+
"description": "Step 9.6.6 — co-located `.sm` sidecar end-to-end. Scanning a fixture that carries a stale sidecar (wrong `identity.{bodyHash,frontmatterHash}`) plus an orphan sidecar (no sibling `.md`) MUST surface `sidecar.status` on the node and emit the `annotation-orphan` (per orphan `.sm`) issue from the built-in core analyzers. The companion `annotation-stale` analyzer (drift) ships experimental / disabled by default, so a default scan does not emit a stale issue; the derived `sidecar.status` on the node still reflects the drift.",
|
|
5
5
|
"fixture": "sidecar-end-to-end",
|
|
6
6
|
"invoke": {
|
|
7
7
|
"verb": "scan",
|
|
@@ -15,11 +15,9 @@
|
|
|
15
15
|
{ "type": "json-path", "path": "$.nodes[0].sidecar.present", "equals": true },
|
|
16
16
|
{ "type": "json-path", "path": "$.nodes[0].sidecar.status", "matches": "^stale-(body|frontmatter|both)$" },
|
|
17
17
|
{ "type": "json-path", "path": "$.nodes[0].sidecar.annotations.version", "equals": 7 },
|
|
18
|
-
{ "type": "json-path", "path": "$.stats.issuesCount", "equals":
|
|
18
|
+
{ "type": "json-path", "path": "$.stats.issuesCount", "equals": 1 },
|
|
19
19
|
{ "type": "json-path", "path": "$.issues[0].analyzerId", "equals": "annotation-orphan" },
|
|
20
20
|
{ "type": "json-path", "path": "$.issues[0].severity", "equals": "warn" },
|
|
21
|
-
{ "type": "json-path", "path": "$.issues[0].data.expectedMdPath", "equals": ".claude/agents/orphan.md" }
|
|
22
|
-
{ "type": "json-path", "path": "$.issues[1].analyzerId", "equals": "annotation-stale" },
|
|
23
|
-
{ "type": "json-path", "path": "$.issues[1].severity", "equals": "info" }
|
|
21
|
+
{ "type": "json-path", "path": "$.issues[0].data.expectedMdPath", "equals": ".claude/agents/orphan.md" }
|
|
24
22
|
]
|
|
25
23
|
}
|
package/conformance/coverage.md
CHANGED
|
@@ -10,7 +10,7 @@ This file is hand-maintained. A CI check before spec release compares the schema
|
|
|
10
10
|
|---|---|---|---|---|
|
|
11
11
|
| 1 | `node.schema.json` | `kernel-empty-boot` (indirect) | 🟡 partial | Empty-boot validates the zero-filled ScanResult shape end-to-end. Direct cases that exercise populated `Node` rows are Provider-specific and live in the Provider's own conformance suite (see `provider:claude` for `basic-scan`). |
|
|
12
12
|
| 2 | `link.schema.json` |, | 🔴 missing | Needs fixture with at least one `invokes` + `references` + `mentions` link, both `high`/`medium`/`low` confidence. |
|
|
13
|
-
| 3 | `issue.schema.json` |, | 🔴 missing | Needs fixture triggering `name-collision` + `broken-ref
|
|
13
|
+
| 3 | `issue.schema.json` |, | 🔴 missing | Needs fixture triggering `name-collision` + `broken-ref`. |
|
|
14
14
|
| 4 | `scan-result.schema.json` | `kernel-empty-boot`, `orphan-markdown-fallback` | 🟢 covered | Zero-filled case via empty-boot. `orphan-markdown-fallback` (spec 0.18.0) asserts a populated `ScanResult` over a multi-Provider corpus where one node lands via the universal `core/markdown` fallback and another via vendor-specific claude classification, locks the orchestrator's path-dedup contract. Populated rename / orphan cases live under `provider:claude` (`basic-scan` / `rename-high` / `orphan-detection`). |
|
|
15
15
|
| 5 | `execution-record.schema.json` |, | 🔴 missing | Blocked by Step 5 (history). Needs a case that runs a `deterministic` action and inspects `state_executions` via `sm history --json`. |
|
|
16
16
|
| 6 | `project-config.schema.json` |, | 🔴 missing | Case: init a scope, write a partial `.skill-map/settings.json` (optionally with a `.skill-map/settings.local.json` overlay), assert effective config after the layered merge. |
|
|
@@ -27,7 +27,7 @@ This file is hand-maintained. A CI check before spec release compares the schema
|
|
|
27
27
|
| 17 | `extensions/base.schema.json` |, | 🔴 missing | Meta-case: every manifest under `src/extensions/` validates against the appropriate kind schema (which extends base via `allOf`). |
|
|
28
28
|
| 18 | `extensions/provider.schema.json` | `plugin-missing-ui-rejected` | 🟡 partial | A Provider plugin whose `kinds/<kindName>/kind.json` omits the required `ui` block fails AJV validation with `invalid-manifest` while the rest of the pipeline keeps running (built-in Claude Provider, exit 0). Since the structure-as-truth refactor, Providers no longer carry a `kinds` map in the manifest; per-kind metadata lives under `kinds/<kindName>/kind.json` and frontmatter schemas under `kinds/<kindName>/schema.json`. The complementary positive case (canonical Claude Provider validates) lives in `provider:claude` conformance. Direct case for missing `kinds/` directory rejection still pending. |
|
|
29
29
|
| 19 | `extensions/extractor.schema.json` |, | 🔴 missing | Case: `frontmatter` + `slash` + `at-directive` extractor manifests validate; an extractor declaring a `precondition.kind` against an unknown qualified kind emits `precondition-kind-unknown` in `sm plugins doctor`. |
|
|
30
|
-
| 20 | `extensions/analyzer.schema.json` | `score-phase-confidence` | 🟡 partial | `score-phase-confidence` exercises the `phase` enum end-to-end: a drop-in analyzer declaring `phase: 'score'` loads from its manifest and composes a confidence op (`delta -0.4`, then a no-op `floor 0.5`) on top of the kernel's 1.0 baseline (a clean resolved link keeps that baseline, no built-in op), landing the folded `scan_links.confidence` at `0.6`. Direct manifest-validation cases for the `detect` / `aggregate` defaults and the `precondition` / `ui` blocks (`name-collision`, `broken-ref
|
|
30
|
+
| 20 | `extensions/analyzer.schema.json` | `score-phase-confidence` | 🟡 partial | `score-phase-confidence` exercises the `phase` enum end-to-end: a drop-in analyzer declaring `phase: 'score'` loads from its manifest and composes a confidence op (`delta -0.4`, then a no-op `floor 0.5`) on top of the kernel's 1.0 baseline (a clean resolved link keeps that baseline, no built-in op), landing the folded `scan_links.confidence` at `0.6`. Direct manifest-validation cases for the `detect` / `aggregate` defaults and the `precondition` / `ui` blocks (`name-collision`, `broken-ref`) still pending. |
|
|
31
31
|
| 21 | `extensions/action.schema.json` |, | 🔴 missing | Case: a `deterministic` action manifest validates; a `probabilistic` action without `<action-dir>/prompt.md` surfaces as `load-error` (structure-as-truth: prompt template lives on disk by convention, no `promptTemplateRef` field). |
|
|
32
32
|
| 22 | `extensions/formatter.schema.json` |, | 🔴 missing | Case: `ascii` formatter manifest validates. |
|
|
33
33
|
| 23 | `history-stats.schema.json` |, | 🔴 missing | Blocked by Step 5 (history). Case: seed `state_executions` with a deterministic fixture, run `sm history stats --json --since <T0> --until <T1> --period month --top 5`, assert the document validates and that `totals.executionsCount == sum(perAction.executionsCount)` and `errorRates.global == totals.failedCount / totals.executionsCount`. Percentiles (`p95`/`p99`) intentionally omitted in v1, add later as a minor bump without breaking consumers. |
|
package/db-schema.md
CHANGED
|
@@ -105,7 +105,7 @@ One row per detected link, matching [`schemas/link.schema.json`](./schemas/link.
|
|
|
105
105
|
| `id` | INTEGER | PRIMARY KEY AUTOINCREMENT | |
|
|
106
106
|
| `source_path` | TEXT | NOT NULL | FK semantically; MAY be unenforced for performance. |
|
|
107
107
|
| `target_path` | TEXT | NOT NULL | MAY point to a missing node (broken ref). |
|
|
108
|
-
| `kind` | TEXT | NOT NULL, CHECK in (`invokes`, `references`, `mentions`, `
|
|
108
|
+
| `kind` | TEXT | NOT NULL, CHECK in (`invokes`, `references`, `mentions`, `points`) | |
|
|
109
109
|
| `confidence` | REAL | NOT NULL, CHECK `>= 0.0 AND <= 1.0` | Numeric `[0,1]` (`link.schema.json#/properties/confidence`). The kernel's 1.0 baseline, then the folded result of every `score`-phase `ctx.adjustConfidence` op (the built-in score-phase detectors `core/name-reserved`, `core/reference-broken`, plus any third-party scorer); the per-op attribution lives in `scan_link_scores`. Migrated from the legacy `high`/`medium`/`low` TEXT enum. |
|
|
110
110
|
| `sources_json` | TEXT | NOT NULL | JSON array of extractor ids. |
|
|
111
111
|
| `original_trigger` | TEXT | NULL | |
|
|
@@ -257,7 +257,7 @@ Per-op confidence-attribution audit trail. One row per attributed `ctx.adjustCon
|
|
|
257
257
|
| `extension_id` | TEXT | NOT NULL | Scorer extension id within the plugin. |
|
|
258
258
|
| `source_path` | TEXT | NOT NULL | The link's `source` (originating node path). Part of the structural identity key, the same tuple `scan_links` dedups on. |
|
|
259
259
|
| `target` | TEXT | NOT NULL | The link's `target` (MAY be a missing node: broken refs get scored too). |
|
|
260
|
-
| `kind` | TEXT | NOT NULL | The link's `kind` (`invokes` / `references` / `mentions` / `
|
|
260
|
+
| `kind` | TEXT | NOT NULL | The link's `kind` (`invokes` / `references` / `mentions` / `points`). |
|
|
261
261
|
| `normalized_trigger` | TEXT | NULL | The link's `trigger.normalizedTrigger`; NULL for path-style links that carry no trigger. Completes the structural identity key. |
|
|
262
262
|
| `op_kind` | TEXT | NOT NULL | Confidence-algebra bucket: `set` / `delta` / `ceil` / `floor`. Kept open at the SQL layer (no CHECK) so the op catalog can evolve as a kernel + spec change without a DDL migration. |
|
|
263
263
|
| `op_value` | REAL | NOT NULL | The op's operand. |
|
package/index.json
CHANGED
|
@@ -174,14 +174,14 @@
|
|
|
174
174
|
}
|
|
175
175
|
]
|
|
176
176
|
},
|
|
177
|
-
"specPackageVersion": "0.
|
|
177
|
+
"specPackageVersion": "0.53.0",
|
|
178
178
|
"integrity": {
|
|
179
179
|
"algorithm": "sha256",
|
|
180
180
|
"files": {
|
|
181
|
-
"CHANGELOG.md": "
|
|
182
|
-
"README.md": "
|
|
183
|
-
"architecture.md": "
|
|
184
|
-
"cli-contract.md": "
|
|
181
|
+
"CHANGELOG.md": "6c4c565708584b07b3e7467a357f62a67fdbedafa80fd25af82258e154192759",
|
|
182
|
+
"README.md": "abb663d0c96c3158ffc501b9d5ef6f58e0db09bdea824cda7f9f70f465793925",
|
|
183
|
+
"architecture.md": "044f7216015fc0629b0e4665e35953f14ab657ca75aaf1cae9ba47d3cd68226c",
|
|
184
|
+
"cli-contract.md": "be13ffcb8c96065f1686443af82324c8175962b53e3777ef42158c1cdc3bb381",
|
|
185
185
|
"conformance/README.md": "33fef8cea919c1d7440c06c7eaa100e50014f52636490f550720e086cf8b651f",
|
|
186
186
|
"conformance/cases/backtick-path-extraction.json": "4620e7f8bc161fc57cb44001e9d99879c7e22b4865a0c27a20dc28969cd936d9",
|
|
187
187
|
"conformance/cases/extractor-collision-detection.json": "179a02c61892f0d26492de0c4e2c327fa6b4986d1265a8f119e871df6afe4658",
|
|
@@ -191,11 +191,11 @@
|
|
|
191
191
|
"conformance/cases/orphan-markdown-fallback.json": "506119323ddde85c1fb4c986c7f6f40a345d44adb06de8d84002591df0e479ee",
|
|
192
192
|
"conformance/cases/plugin-missing-ui-rejected.json": "2074fd71937feae136c999f76da81f334f2caf8b65bfe8dc9d7fb800699fb85c",
|
|
193
193
|
"conformance/cases/score-phase-confidence.json": "aa3a06149d78ff056dd1a47852baaedc200e47b0d5b1e778d3459ae62f65f390",
|
|
194
|
-
"conformance/cases/sidecar-end-to-end.json": "
|
|
194
|
+
"conformance/cases/sidecar-end-to-end.json": "06374e619df1691f1593b0847b3671299318525d4a7bf4ff9bfda3ab03032a5f",
|
|
195
195
|
"conformance/cases/view-action-button.json": "51331f725be1c3655351f8fca6fc9d3d301ae68ea1741ff6c79998332ba2dfeb",
|
|
196
196
|
"conformance/cases/view-contribution-payloads.json": "e8f54ed62e64a2a0f86729866e507abb1f4246683f0e60d538280536f7cd3ecc",
|
|
197
197
|
"conformance/cases/view-slots-all.json": "05284e0324dd2da72b6b21d397c11b355802229a68053e9dddc323f69b3a1eba",
|
|
198
|
-
"conformance/coverage.md": "
|
|
198
|
+
"conformance/coverage.md": "63fed07a979fcc1ce0b3142a5f060d8ad049bef3430996f159a389e0989f3233",
|
|
199
199
|
"conformance/fixtures/backtick-path/docs/target.md": "a09ae2cb4c96358a2e0692215f172b0f8c48028b6b123e4e83424b28302e644c",
|
|
200
200
|
"conformance/fixtures/backtick-path/source.md": "217f78b12b3ff47a938a5cc9c1ff7d6989d6a1db82bd1ddf3656787f31efb902",
|
|
201
201
|
"conformance/fixtures/orphan-markdown/.claude/agents/reviewer.md": "7f062731106f2d9811e4fffcf6ab44b8dfff4cfb16536a469514cc0664e832bf",
|
|
@@ -229,14 +229,14 @@
|
|
|
229
229
|
"conformance/fixtures/view-contribution-payloads/notes/example.md": "312b1919cd7fd0f233648b053acfb2975662ede3c65dd391cc508204b67ad6fb",
|
|
230
230
|
"conformance/fixtures/view-slots-all/.skill-map/plugins/all-slots/analyzers/everything/index.js": "ea0022fec7f0fd5a26ba12db1310335f434f2f820682206a3a9542d98db0d346",
|
|
231
231
|
"conformance/fixtures/view-slots-all/.skill-map/plugins/all-slots/plugin.json": "c48e8a0574947ade0b4eb189d6bc27a48e24f92f616aacdc177f2d22d472a599",
|
|
232
|
-
"db-schema.md": "
|
|
232
|
+
"db-schema.md": "d6af5d26626b692b05e96726d3bdaa77ec1b703bb30e83f752ffcca55e163fa4",
|
|
233
233
|
"interfaces/security-scanner.md": "e8049712b9cf7a07c786bf19f8f775f8ef9638f063f7fba5c7a8b1431b92f38e",
|
|
234
|
-
"job-events.md": "
|
|
234
|
+
"job-events.md": "d5dc11cda9e9302d0d9f17d467c14c80ad4011456f574dc87516470aff0323cb",
|
|
235
235
|
"job-lifecycle.md": "9c429121f98a07c8795f8979ed1abc5e5334e3f89db51585a8da55c527ef855b",
|
|
236
|
-
"plugin-author-guide.md": "
|
|
236
|
+
"plugin-author-guide.md": "a6bca100e8963e74e0299eeaa15819d0734e9aee57af89f7f56707eb3c99dbb9",
|
|
237
237
|
"plugin-kv-api.md": "1acc69ed82433a74e35ada61d63a6d7379fb61046ff83de1e0facbe884c64704",
|
|
238
238
|
"prompt-preamble.md": "9dd4f6d1bc6a425f8782fcee10cbe75909e8d64e28781fda56c2fae909b02f40",
|
|
239
|
-
"schemas/annotations.schema.json": "
|
|
239
|
+
"schemas/annotations.schema.json": "09fcebc86e3b793bf9f03a35b38e5ca2a08d79ac3504f6f03895ac2ae1c2aded",
|
|
240
240
|
"schemas/api/rest-envelope.schema.json": "8eeb1c2d79fb69eaef23737a2231d48d67e59b8b19aad816239ab4680e2c4752",
|
|
241
241
|
"schemas/bump-report.schema.json": "c763e1f89f2665c479d6a4985c1d324c65e5278331ebab82220287a07e4c4429",
|
|
242
242
|
"schemas/conformance-case.schema.json": "958b316d646d0c64a715a7a28cee66d2c2d2498a60dbfc5ae8970687c2a96954",
|
|
@@ -249,13 +249,13 @@
|
|
|
249
249
|
"schemas/extensions/formatter.schema.json": "880dc379ad545a62404403533a01eda5171edba0390561fc46ec6e986e0b9bd3",
|
|
250
250
|
"schemas/extensions/hook.schema.json": "f56aef59e9986ffdf7d86aa2e048dccccf217000a358b8c64737cbd911c48dad",
|
|
251
251
|
"schemas/extensions/provider-kind.schema.json": "499b2418bbe6d8a84a1608e26c56b52c2652a30ce314bc2989094418797dc1e6",
|
|
252
|
-
"schemas/extensions/provider.schema.json": "
|
|
252
|
+
"schemas/extensions/provider.schema.json": "30f1f001192b3ca9fc1e3aa383b23419f8d6c179d0239f54cb7f41910126a6bb",
|
|
253
253
|
"schemas/frontmatter/base.schema.json": "cff81510ed94824dfd12ab8b30ce9fbac65e42d61ae0edf3fbb6bbb6bb8bcb8c",
|
|
254
254
|
"schemas/history-stats.schema.json": "436aa0ffe744bdb699000447e86b45724fbd2cc4642781074eb1527522b9058c",
|
|
255
255
|
"schemas/input-types.schema.json": "93b27a1cbd1f131d42730eb9a89cf3af6889e9f17b20a48ce36133885503e01b",
|
|
256
|
-
"schemas/issue.schema.json": "
|
|
256
|
+
"schemas/issue.schema.json": "840198acc36ed17f30957733dfaf4463d07d72911b13fb7f58b037a7dcf2d5b1",
|
|
257
257
|
"schemas/job.schema.json": "dbcedf137de03fde38f74686f594e600c627bf808f2aad23511a26617a663a02",
|
|
258
|
-
"schemas/link.schema.json": "
|
|
258
|
+
"schemas/link.schema.json": "e2f615bff0e569be936acd6ec249795906dfb56084f3a1a9a2a37fd51c0b00e2",
|
|
259
259
|
"schemas/node.schema.json": "1ebba38e0c0ae022fccbc0cdf7c298da1720a68d4cb375f0baf9f0847998a0d8",
|
|
260
260
|
"schemas/plugins-doctor.schema.json": "03e2dc51c052a09bf0198c80e2c26e6129734ada4a748e483245de3dd8576c42",
|
|
261
261
|
"schemas/plugins-registry.schema.json": "211d081691fc83526e1593c79ed9741ad8a5dbd4db1a756f72141b0cced2ea15",
|
|
@@ -265,7 +265,7 @@
|
|
|
265
265
|
"schemas/report-base.schema.json": "e4d25f055e24f18ae0f77c24661c1bddc87ff2e43b001b6a827fcb14f9753f44",
|
|
266
266
|
"schemas/scan-result.schema.json": "9fb81f496d6f8bdcb82131d0b2eb532da1addb801e7d27bd192a0c286a28c2c0",
|
|
267
267
|
"schemas/sidecar.schema.json": "f9d914e61b2d04495b84dc90e55240aca959e6f16137e5bfa4c0e10ada33ecbe",
|
|
268
|
-
"schemas/signal.schema.json": "
|
|
268
|
+
"schemas/signal.schema.json": "c677f04964dcc15e00368c5cc4b0569fb4cf21889d34fa3c29dc21a5cb6b919c",
|
|
269
269
|
"schemas/summaries/agent.schema.json": "5b26b95fb082b73d302c8aa6489ab09488a155ccfbb8943dfc47079509d35122",
|
|
270
270
|
"schemas/summaries/command.schema.json": "7f522c682d0fdf5a40172c7fc8fcd23e60a0ab0253354146525bd3a3d417f1f8",
|
|
271
271
|
"schemas/summaries/hook.schema.json": "6a1ceecda7a7173dfcd8b5f705d84be1792c4bb5a2269ff666088128c02c888a",
|
package/job-events.md
CHANGED
package/package.json
CHANGED
package/plugin-author-guide.md
CHANGED
|
@@ -126,13 +126,13 @@ Built-ins split between two namespaces:
|
|
|
126
126
|
|
|
127
127
|
### Extension id shape
|
|
128
128
|
|
|
129
|
-
The convention applied to every built-in extension id is **`<domain>-<detail>`** (general to specific): the leftmost segment names the entity the extension reasons about (`node`, `link`, `annotation`, `reference`, `name`, ...), the rest narrows the behaviour. Examples: `annotation-orphan`, `link-counter`, `node-stability`, `name-reserved`, `reference-broken`. Even Actions live under their entity domain (`node-bump`, `node-
|
|
129
|
+
The convention applied to every built-in extension id is **`<domain>-<detail>`** (general to specific): the leftmost segment names the entity the extension reasons about (`node`, `link`, `annotation`, `reference`, `name`, ...), the rest narrows the behaviour. Examples: `annotation-orphan`, `link-counter`, `node-stability`, `name-reserved`, `reference-broken`. Even Actions live under their entity domain (`node-bump`, `node-set-tags`) rather than verb-style ids, so the catalog reads as a structured list.
|
|
130
130
|
|
|
131
131
|
Authors are not required to follow this, but it makes `sm plugins list` self-grouping. In the extension file, declare only the short id-bearing **folder name**, not a prefixed id; the loader composes `<plugin-id>/<short-id>` from `plugin.json` (the directory name) and the extension folder. Any other cross-extension reference (`precondition.analyzerIds`, ...) uses the qualified id of the target.
|
|
132
132
|
|
|
133
133
|
### Toggle model
|
|
134
134
|
|
|
135
|
-
Every extension is independently toggle-able by its qualified id `<plugin>/<ext-id>` (e.g. `claude/at-directive`, `core/
|
|
135
|
+
Every extension is independently toggle-able by its qualified id `<plugin>/<ext-id>` (e.g. `claude/at-directive`, `core/reference-broken`). The **plugin row is a presentational grouping**, not the granular toggle target: the user sees a row per plugin in `sm plugins list` and the Settings UI, with each extension listed underneath with its own enabled / disabled state.
|
|
136
136
|
|
|
137
137
|
Two id shapes resolve at the toggle surface:
|
|
138
138
|
|
|
@@ -250,7 +250,7 @@ Pure single-node analysis. **Never** read another node, the graph, or the databa
|
|
|
250
250
|
|
|
251
251
|
`extract(ctx) → void`. Output flows through callbacks the kernel binds onto `ctx`:
|
|
252
252
|
|
|
253
|
-
- **`ctx.emitLink(link)`**, append a `Link`. The kernel validates `link.kind` against the **global closed enum** (`invokes`, `references`, `mentions`, `
|
|
253
|
+
- **`ctx.emitLink(link)`**, append a `Link`. The kernel validates `link.kind` against the **global closed enum** (`invokes`, `references`, `mentions`, `points`); off-enum kinds drop as `extension.error`. Confidence is declared per emit (default `'medium'`). URL-shaped targets are partitioned into `node.externalRefsCount` and never persisted. (There is no per-extractor `emitsLinkKinds` allowlist anymore.)
|
|
254
254
|
- **`ctx.enrichNode(partial)`**, merge kernel-curated properties onto the node's enrichment layer (persisted into `node_enrichments`). **Strictly separate from the author frontmatter**, which is immutable from any Extractor. Use it for inferred facts (computed titles, summaries) the author did not write.
|
|
255
255
|
- **`ctx.emitContribution(id, payload)`**, view contributions (see [View contributions](#view-contributions)).
|
|
256
256
|
- **`ctx.store`**, plugin-scoped persistence, present only when `plugin.json` declares `storage.mode`. See [`plugin-kv-api.md`](./plugin-kv-api.md).
|
|
@@ -414,7 +414,7 @@ Operate on one or more nodes. Dual-mode (`mode` optional, default `'deterministi
|
|
|
414
414
|
An Action has two independent surfaces:
|
|
415
415
|
|
|
416
416
|
- **`invoke(input, ctx)`**, the on-demand executor the user triggers (deterministic in-process code, or a probabilistic rendered prompt the runner executes). Unit-test deterministic ones by calling `invoke(input, ctx)` with a fake context; probabilistic ones still need a live kernel until Step 10 lands the job subsystem.
|
|
417
|
-
- **`project(ctx)`** (optional), a deterministic, side-effect-free, scan-time method with read-only graph access (`ctx.nodes`, `ctx.links`) plus `ctx.emitContribution(nodePath, ref, payload)`. Use it to self-project the Action's own UI affordance, typically an `inspector.action.button` declared in the manifest `ui` map (see [View contributions](#view-contributions)), computing the per-node `enabled` / prompt `options` from the live graph. `project()` is always deterministic, even when `invoke` is probabilistic, and runs every scan (same cost as an analyzer's emit). This is how built-in buttons like
|
|
417
|
+
- **`project(ctx)`** (optional), a deterministic, side-effect-free, scan-time method with read-only graph access (`ctx.nodes`, `ctx.links`) plus `ctx.emitContribution(nodePath, ref, payload)`. Use it to self-project the Action's own UI affordance, typically an `inspector.action.button` declared in the manifest `ui` map (see [View contributions](#view-contributions)), computing the per-node `enabled` / prompt `options` from the live graph. `project()` is always deterministic, even when `invoke` is probabilistic, and runs every scan (same cost as an analyzer's emit). This is how built-in buttons like Edit tags / Bump are produced: the dispatching Action owns its button, there is no separate "projector" analyzer. Unit-test it by calling `project(ctx)` with a fake `{ nodes, links, emitContribution }` and asserting the captured payload.
|
|
418
418
|
|
|
419
419
|
---
|
|
420
420
|
|
|
@@ -2,30 +2,20 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://skill-map.ai/spec/v0/annotations.schema.json",
|
|
4
4
|
"title": "Annotations",
|
|
5
|
-
"description": "Catalog of conventional annotation fields skill-map ships out of the box, written into the `annotations:` block of a sidecar (`<basename>.sm`). Every field is OPTIONAL, a sidecar with an empty `annotations: {}` is valid. Schema is `additionalProperties: true` so users / plugins can add custom keys without coordination; the built-in `unknown-field` analyzer emits a warning on unrecognized keys (typo guard). The curated catalog is the load-bearing
|
|
5
|
+
"description": "Catalog of conventional annotation fields skill-map ships out of the box, written into the `annotations:` block of a sidecar (`<basename>.sm`). Every field is OPTIONAL, a sidecar with an empty `annotations: {}` is valid. Schema is `additionalProperties: true` so users / plugins can add custom keys without coordination; the built-in `unknown-field` analyzer emits a warning on unrecognized keys (typo guard). The curated catalog is the load-bearing fields below, versioning (`version`, `stability`), provenance (`authors`, `license`, `source`, `sourceVersion`), taxonomy (`tags`), docs (`docsUrl`). The activity timestamp lives in the reserved `audit:` block (`audit.lastBumpedAt`), not in `annotations:`. Plugins that want first-class custom keys with their own validation declare `annotationContributions` in their manifest (see Step 9.6.6).",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": true,
|
|
8
8
|
"properties": {
|
|
9
9
|
"version": {
|
|
10
10
|
"type": "integer",
|
|
11
11
|
"minimum": 1,
|
|
12
|
-
"description": "Monotonic counter. Bumped via the built-in `bump` Action when the underlying node changes meaningfully. Orthogonal to `stability`, `stability` carries the lifecycle stage; `version` is just a counter. There is no major: a change so big it would justify a major bump uses the convention `create a new node
|
|
12
|
+
"description": "Monotonic counter. Bumped via the built-in `bump` Action when the underlying node changes meaningfully. Orthogonal to `stability`, `stability` carries the lifecycle stage; `version` is just a counter. There is no major: a change so big it would justify a major bump uses the convention `create a new node and retire the old one` instead. Default: missing == unversioned."
|
|
13
13
|
},
|
|
14
14
|
"stability": {
|
|
15
15
|
"type": "string",
|
|
16
16
|
"enum": ["experimental", "stable", "deprecated"],
|
|
17
17
|
"description": "Lifecycle stage. Denormalized into `scan_nodes.stability` for fast queries (see `node.schema.json` #/properties/stability). Default: missing == unspecified."
|
|
18
18
|
},
|
|
19
|
-
"supersedes": {
|
|
20
|
-
"type": "array",
|
|
21
|
-
"items": { "type": "string", "minLength": 1 },
|
|
22
|
-
"description": "Paths (relative to scope root) of nodes this node replaces. Consumed by the built-in `superseded` analyzer and surfaces in `sm list --superseded`."
|
|
23
|
-
},
|
|
24
|
-
"supersededBy": {
|
|
25
|
-
"type": "string",
|
|
26
|
-
"minLength": 1,
|
|
27
|
-
"description": "Path (relative to scope root) of the node that replaces this one. When set, the current node is end-of-life and consumers should migrate."
|
|
28
|
-
},
|
|
29
19
|
"authors": {
|
|
30
20
|
"type": "array",
|
|
31
21
|
"items": { "type": "string", "minLength": 1 },
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"description": "When present, the resolver ranks candidates whose `kind` appears earlier in this array ABOVE candidates whose `kind` appears later. Candidates whose `kind` is absent from the array drop to the end (after every listed kind). Example: a Provider that wants `invokes` edges to win against `mentions` and `references` of the same range declares `['invokes', 'references', 'mentions']`. Ties inside the same `kindPriority` bucket fall through to the confidence -> range length -> declaration order tiebreaks.",
|
|
128
128
|
"items": {
|
|
129
129
|
"type": "string",
|
|
130
|
-
"enum": ["invokes", "references", "mentions", "
|
|
130
|
+
"enum": ["invokes", "references", "mentions", "points"]
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"analyzerId": {
|
|
11
11
|
"type": "string",
|
|
12
12
|
"pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$",
|
|
13
|
-
"description": "Kebab-case identifier of the analyzer that emitted this issue (e.g. `name-collision`, `broken-ref`, `
|
|
13
|
+
"description": "Kebab-case identifier of the analyzer that emitted this issue (e.g. `name-collision`, `broken-ref`, `node-stability`)."
|
|
14
14
|
},
|
|
15
15
|
"severity": {
|
|
16
16
|
"type": "string",
|
package/schemas/link.schema.json
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
},
|
|
18
18
|
"kind": {
|
|
19
19
|
"type": "string",
|
|
20
|
-
"enum": ["invokes", "references", "mentions", "
|
|
21
|
-
"description": "Nature of the relation. `invokes` = execution-level call (e.g. slash command). `references` = explicit link (e.g. wikilink, @-directive). `mentions` = informal textual mention. `
|
|
20
|
+
"enum": ["invokes", "references", "mentions", "points"],
|
|
21
|
+
"description": "Nature of the relation. `invokes` = execution-level call (e.g. slash command). `references` = explicit link (e.g. wikilink, @-directive). `mentions` = informal textual mention. `points` = relative file path written inside a code region (backtick span / fenced block); coexists with `references` on the same `(source, target)` pair as a separate Link row (no merge, and `core/link-kind-conflict` does not treat the pair as a conflict)."
|
|
22
22
|
},
|
|
23
23
|
"confidence": {
|
|
24
24
|
"type": "number",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"kind": {
|
|
58
58
|
"type": "string",
|
|
59
|
-
"enum": ["invokes", "references", "mentions", "
|
|
59
|
+
"enum": ["invokes", "references", "mentions", "points"],
|
|
60
60
|
"description": "Proposed link kind, matching `link.schema.json#/properties/kind/enum`. Closed enum in v1; provider-specific kinds wait until a concrete need emerges."
|
|
61
61
|
},
|
|
62
62
|
"target": {
|