@skill-map/spec 0.45.1 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Spec changelog
2
2
 
3
+ ## 0.46.0
4
+
5
+ ### Minor Changes
6
+
7
+ - The active-provider lens dropdown in Settings → Project now greys out (and refuses to select) any Provider the operator has disabled. `GET /api/active-provider` gained a `selectable` field listing the Provider ids that are enabled right now; the SPA renders Providers absent from it as disabled instead of offering a lens whose extractors would never run.
8
+
9
+ ## User-facing
10
+
11
+ Disabling a provider plugin now removes it as a choice in **Settings → Project → Active provider**. The provider stays listed but greyed out and labelled `(disabled)`, so you can no longer switch the lens to a provider whose extractors would not run.
12
+
13
+ - `sm bump` and the BFF bump route (`POST /api/sidecar/bump`) now stamp `audit.lastBumpedBy` / `audit.createdBy` with the project's Git author name (`git config user.name`) when the node lives in a Git repository, falling back to the channel literal (`'cli'` / `'ui'`) otherwise. This supersedes Decision A5, which kept the invoker a literal.
14
+
15
+ ## User-facing
16
+
17
+ Bumping a node now records **who** bumped it: the audit `by` fields show your Git author name (`git config user.name`) instead of `cli` / `ui`, when the project is a Git repo. It falls back to `cli` / `ui` outside a Git repo or when no `user.name` is configured.
18
+
19
+ ### Patch Changes
20
+
21
+ - The `core/annotation-stale` analyzer is now neutral instead of warning-tinted: drift is informational, not a warning. Its footer chip (`staleIcon`) carries no severity (the clock renders in the foreground colour instead of the warn tint), and the stale Findings issue is lowered from `warn` to `info`. As `info`, it no longer counts toward the card's warn chip (the issue-counter buckets error/warn only) and never affected `sm check`'s exit code (info and warn are both non-failing).
22
+
3
23
  ## 0.45.1
4
24
 
5
25
  ### Patch Changes
package/architecture.md CHANGED
@@ -629,7 +629,7 @@ Two schemas describe the wire shape:
629
629
 
630
630
  `identity` carries `path` (scope-root-relative, matches the canonical Node identifier in [`schemas/node.schema.json`](./schemas/node.schema.json)) plus `bodyHash` and `frontmatterHash`. Both hashes are sha256 over the kernel's canonical form of the markdown body (post-frontmatter bytes) and frontmatter (YAML re-emitted via `js-yaml dump` with `sortKeys: true`, `lineWidth: -1`, `noRefs: true`, `noCompatMode: true`); each sidecar captures the values the kernel saw at the moment it was last written.
631
631
 
632
- At scan time the kernel re-computes the live hashes and compares against the stored ones. Mismatch in either is **drift**, surfaced via the built-in `annotation-stale` analyzer (severity `warning`, never blocking, soft mode by design). A `.sm` whose `identity.path` no longer points at an existing `.md` is **orphan**, surfaced via the built-in `annotation-orphan` analyzer (also `warning`). Drift state is **derived**, never stored, pure function over existing data, no flag to drift between flag and reality.
632
+ At scan time the kernel re-computes the live hashes and compares against the stored ones. Mismatch in either is **drift**, surfaced via the built-in `annotation-stale` analyzer (severity `info`, never blocking, soft mode by design: drift is informational, the footer chip is a neutral clock). A `.sm` whose `identity.path` no longer points at an existing `.md` is **orphan**, surfaced via the built-in `annotation-orphan` analyzer (also `warning`). Drift state is **derived**, never stored, pure function over existing data, no flag to drift between flag and reality.
633
633
 
634
634
  ### Bump model
635
635
 
@@ -637,7 +637,7 @@ The deterministic built-in `core/node-bump` Action produces a sidecar patch:
637
637
 
638
638
  - 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, supersede the old").
639
639
  - Refreshes `identity.bodyHash` and `identity.frontmatterHash` to the live values.
640
- - Stamps `audit.lastBumpedAt` (ISO 8601 datetime) and `audit.lastBumpedBy` (`'cli'`, `'ui'`, or `'plugin:<id>'`).
640
+ - 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>'`).
641
641
  - On first-time creation also stamps `audit.createdAt` and `audit.createdBy` (set once, stable thereafter).
642
642
 
643
643
  The Action stays pure (no IO). The kernel materializes the patch through the `SidecarStore` port, a path-keyed read-modify-write critical section that deep-merges the patch into the on-disk file (arrays REPLACE, objects RECURSE, `null` DELETES) and writes atomically via `<path>.tmp` + POSIX rename. Concurrent bumps on the same path serialize through the lock; both patches' effects survive (no lost write).
@@ -831,7 +831,7 @@ Endpoints under `/api/contributions/*`:
831
831
 
832
832
  Plus catalog embedding into every payload-bearing envelope:
833
833
 
834
- - `kindRegistry`, `providerRegistry`, and `contributionsRegistry` are siblings on the envelope (see schema). Built once per server boot, embedded into list (`nodes` / `links` / `issues` / `plugins`), single (`node`), and value (`config`) envelopes. Sentinel envelopes (`health` / `scan` / `graph`) and action-result envelopes (`sidecar.bumped`) and the catalog envelopes themselves (`annotations.registered` / `contributions.registered`) carry none of them. `providerRegistry` is the static boot catalog of registered Providers' identity; the dynamic active lens (current value + filesystem-detected candidates) is served separately by `GET /api/active-provider`.
834
+ - `kindRegistry`, `providerRegistry`, and `contributionsRegistry` are siblings on the envelope (see schema). Built once per server boot, embedded into list (`nodes` / `links` / `issues` / `plugins`), single (`node`), and value (`config`) envelopes. Sentinel envelopes (`health` / `scan` / `graph`) and action-result envelopes (`sidecar.bumped`) and the catalog envelopes themselves (`annotations.registered` / `contributions.registered`) carry none of them. `providerRegistry` is the static boot catalog of registered Providers' identity; the dynamic active lens (current value + filesystem-detected candidates + the enabled `selectable` set) is served separately by `GET /api/active-provider`.
835
835
 
836
836
  Plus per-node embedding on node responses:
837
837
 
package/cli-contract.md CHANGED
@@ -114,6 +114,8 @@ CLI surfaces:
114
114
  - **Manual override**: `sm config set activeProvider <id>` switches the lens. The verb drops the `scan_*` zone atomically (see [`db-schema.md`](./db-schema.md#zones)) and triggers an immediate rescan under the new lens. `state_*` and `config_*` zones survive.
115
115
  - **No per-scan flag**: there is no `sm scan --provider=<id>` flag. The lens is a project-level decision, not a per-invocation parameter. The drop+rescan cost makes per-invocation switching the wrong default UX.
116
116
 
117
+ **UI lens-selection surface.** `GET /api/active-provider` returns `{ activeProvider, detected, source, selectable }`. `selectable` is the set of registered-Provider ids that are enabled right now, resolved against the live per-extension enabled resolver (`config_plugins` layered over `settings.json#/plugins`, the same resolution `GET /api/plugins` applies), so it is the subset of `providerRegistry` eligible to become the lens. A Provider the operator disabled is dropped from `selectable` but stays in `providerRegistry` (the static boot catalog keeps it so already-scanned nodes still render their chip / icon). The SPA's active-lens dropdown lists every `providerRegistry` Provider and renders those absent from `selectable` as disabled (greyed, not selectable), so a disabled Provider can never be picked as the lens. This mirrors the scan-time contract that a lens pointing at a disabled Provider runs none of its extractors (the runtime soft-warns on that drift); the dropdown closes the loop on the write side by refusing to create the drift in the first place.
118
+
117
119
  ---
118
120
 
119
121
  ## Targeted fan-out flags
@@ -360,7 +362,7 @@ The built-in deterministic `core/node-bump` Action is the canonical write channe
360
362
 
361
363
  | Command | Purpose |
362
364
  |---|---|
363
- | `sm bump <node.path> [--force] [--yes]` | Single-node bump. Wraps `core/node-bump`. Refuses on a fresh node (`{ ok: false, reason: 'fresh' }`, exit `2`) unless `--force` is passed; with `--force` on a fresh node the verb is a silent no-op (exit `0`, no stdout). On a stale node (or first-time creation) increments `annotations.version`, refreshes `for.{bodyHash, frontmatterHash}`, and stamps the audit block (`audit.lastBumpedAt` + `audit.lastBumpedBy: 'cli'`; on first creation also `audit.createdAt` + `audit.createdBy: 'cli'`). Exit `5` if the node is not in the persisted scan. `--json` emits the report shape declared by `bump-report.schema.json`. `--yes` confirms consent for `.sm` writes in this project, see §`.sm` write consent below. |
365
+ | `sm bump <node.path> [--force] [--yes]` | Single-node bump. Wraps `core/node-bump`. Refuses on a fresh node (`{ ok: false, reason: 'fresh' }`, exit `2`) unless `--force` is passed; with `--force` on a fresh node the verb is a silent no-op (exit `0`, no stdout). On a stale node (or first-time creation) increments `annotations.version`, refreshes `for.{bodyHash, frontmatterHash}`, and stamps the audit block (`audit.lastBumpedAt` + `audit.lastBumpedBy`; on first creation also `audit.createdAt` + `audit.createdBy`). The `by` fields carry the Git author name (`git config user.name`) when the project is a Git repository, otherwise the channel literal `'cli'`. Exit `5` if the node is not in the persisted scan. `--json` emits the report shape declared by `bump-report.schema.json`. `--yes` confirms consent for `.sm` writes in this project, see §`.sm` write consent below. |
364
366
  | `sm bump --pending [--staged] [--force] [--yes]` | Batch bump. Walks every node whose sidecar overlay reports drift in `node.path` ASC order and bumps each. `--staged` runs `git add <sidecar-path>` after each successful bump so the new content lands in the same commit; `git add` failure degrades to a stderr warning, the batch keeps running. Empty stale set → exit `0` with a "nothing to do" advisory. `--json` envelope: `{ bumped, refused, skipped, errors[], elapsedMs }`. Exit `0` on a clean run; `1` when at least one per-node error landed in `errors[]`. **Git error matrix for `--staged`**: not inside a git repo (no `.git/` parent of `cwd`) → exit `5`; `git` binary not on PATH (spawn ENOENT) → exit `2`. Both checks run BEFORE any sidecar write so a misconfigured environment never produces partial state. `--yes` confirms consent for `.sm` writes, see §`.sm` write consent below. |
365
367
  | `sm sidecar refresh <node.path> [--yes]` | Hash-only update on the sidecar. Refreshes `for.{bodyHash, frontmatterHash}` to match the live node WITHOUT bumping `annotations.version` and WITHOUT touching the audit block. Useful when the user knows a body change is editorial-only and doesn't want to spend a version increment. Distinct from the top-level `sm refresh` (which targets the enrichment layer at Step A.8), different storage, different concept; the sub-namespace prefix prevents the collision. Exit `5` if the node has no sidecar or is not in the persisted scan. No-op on a fresh node (informational stderr, exit `0`). `--yes` confirms consent for `.sm` writes, see §`.sm` write consent below. |
366
368
  | `sm sidecar prune [--dry-run] [--yes]` | Delete orphan `.sm` files (sidecars whose accompanying `<basename>.md` does not exist on disk). Destructive, without `--dry-run` prompts for interactive confirmation listing every file to be deleted (per the §Dry-run analyzer for destructive verbs). `--yes` (alias `--force`) bypasses the destructive-confirmation prompt for non-interactive callers (CI, the pre-commit hook, scripts), NOT to be confused with the `.sm` write-consent gate, which does not apply to prune (delete is not a write). With `--dry-run` reports what would be deleted without touching disk and never prompts. Different domain from `sm orphans`, that verb operates on the node graph (rename heuristic); this one operates on the filesystem layer. `--json` envelope: `{ deleted, wouldDelete, errors, items[], elapsedMs }`. Exit `1` when delete failures landed in `errors`. |
@@ -415,7 +417,7 @@ Tracked as **R6** in the §Step 9.6 review queue: open by design, defer the `js-
415
417
 
416
418
  ##### BFF endpoint, `POST /api/sidecar/bump` (Step 9.6.5, BFF half)
417
419
 
418
- The Hono BFF exposes the single-node bump flow over REST so the UI can drive the same Action / Store the CLI uses. Behaviour mirrors `sm bump <node.path> [--force]` 1:1: same `core/node-bump` Action, same `FilesystemSidecarStore`, same fresh-vs-stale refusal semantics. The only differences from the CLI verb are the invoker label (`'ui'` vs `'cli'`, Decision A5 of 9.6.4 left this as a literal) and the wire shape. Batch (`--pending`) is intentionally CLI-only at 9.6.5, surfacing it over REST needs a job-style progress channel and lands later.
420
+ The Hono BFF exposes the single-node bump flow over REST so the UI can drive the same Action / Store the CLI uses. Behaviour mirrors `sm bump <node.path> [--force]` 1:1: same `core/node-bump` Action, same `FilesystemSidecarStore`, same fresh-vs-stale refusal semantics. The only differences from the CLI verb are the invoker channel fallback (`'ui'` vs `'cli'`, used only when no Git author resolves) and the wire shape. This supersedes Decision A5 of 9.6.4 (which left the invoker a literal): both routes now stamp the Git `user.name` when the project is a Git repository, falling back to the channel literal otherwise. Batch (`--pending`) is intentionally CLI-only at 9.6.5, surfacing it over REST needs a job-style progress channel and lands later.
419
421
 
420
422
  | Field | Value |
421
423
  |---|---|
@@ -621,7 +623,7 @@ List endpoints conform to [`schemas/api/rest-envelope.schema.json`](schemas/api/
621
623
 
622
624
  **`kindRegistry` envelope field.** Every payload-bearing variant of the REST envelope (`nodes` / `links` / `issues` / `plugins` lists, the `node` single, the `config` value envelope) embeds a required `kindRegistry: { [kindName]: { providerId, label, color, colorDark?, emoji?, icon? } }` field. Sentinel envelopes (`health`, `scan`, `graph`) are exempt, they carry no payload at the wire level. The BFF assembles the registry once at boot from EVERY built-in Provider's `kinds[*].ui` block (regardless of the boot-time enabled verdict, their module code is statically imported by `built-ins.ts` and always in memory) PLUS every drop-in user Provider that loaded successfully at boot (see [`architecture.md` §Provider · `ui` presentation](architecture.md#provider--ui-presentation)). The registry is then attached to every applicable response. Built-ins are listed unconditionally because a user re-enabling one mid-session expects its kinds to render on the next scan; the runtime enabled/disabled axis is enforced at SCAN-TIME by `composeScanExtensions` reading the fresh resolver, not by hiding kinds from the registry. Drop-ins that loaded as `disabled` carry `startsAsDisabled: true` on `GET /api/plugins` and need `sm serve` restart to register, their module code was never imported. The UI consumes `kindRegistry` directly to render kind palettes, list rows, and inspector headers, built-in and user-plugin kinds render identically. A kind appearing in a response payload (e.g. `node.kind`) without a matching `kindRegistry` entry is a contract violation; the kernel rejects Providers without a `ui` block at load time so the registry is always complete for whatever kinds appear in the response.
623
625
 
624
- **`providerRegistry` envelope field.** The same payload-bearing envelopes also embed a required `providerRegistry: { [providerId]: { label, color, colorDark?, emoji?, icon?, hideChip? } }` field (sibling of `kindRegistry`). Sentinel envelopes (`health`, `scan`, `graph`), action-result envelopes (`sidecar.bumped`), and the catalog envelopes (`annotations.registered`, `contributions.registered`) are exempt. Same boot-time assembly discipline as `kindRegistry`: assembled once from EVERY built-in Provider's top-level `presentation` block (regardless of enabled verdict) PLUS every drop-in user Provider that loaded at boot. The UI consumes `providerRegistry` to render the active-lens dropdown, the topbar lens chip, and the per-node provider chip from the real registered-Provider set, never a hardcoded list; `hideChip: true` (the universal `markdown` fallback) suppresses only the per-card chip. This is the static boot catalog of Provider identity; the dynamic active lens (current value + filesystem-detected candidates) is served separately by `GET /api/active-provider`.
626
+ **`providerRegistry` envelope field.** The same payload-bearing envelopes also embed a required `providerRegistry: { [providerId]: { label, color, colorDark?, emoji?, icon?, hideChip? } }` field (sibling of `kindRegistry`). Sentinel envelopes (`health`, `scan`, `graph`), action-result envelopes (`sidecar.bumped`), and the catalog envelopes (`annotations.registered`, `contributions.registered`) are exempt. Same boot-time assembly discipline as `kindRegistry`: assembled once from EVERY built-in Provider's top-level `presentation` block (regardless of enabled verdict) PLUS every drop-in user Provider that loaded at boot. The UI consumes `providerRegistry` to render the active-lens dropdown, the topbar lens chip, and the per-node provider chip from the real registered-Provider set, never a hardcoded list; `hideChip: true` (the universal `markdown` fallback) suppresses only the per-card chip. This is the static boot catalog of Provider identity; the dynamic active lens (current value + filesystem-detected candidates + the enabled `selectable` set) is served separately by `GET /api/active-provider`.
625
627
 
626
628
  **`contributionsRegistry` envelope field.** Same payload-bearing envelopes also embed `contributionsRegistry: { "<pluginId>/<extensionId>/<contributionId>": { pluginId, extensionId, contributionId, slot, label?, tooltip?, icon?, emptyText?, emitWhenEmpty } }`. Same boot-time assembly discipline as `kindRegistry`: ALL built-in declarations are listed regardless of enabled state (so re-enabling a built-in mid-session renders correctly on the next scan), plus drop-in user plugins that loaded at boot. The `slot` value comes from the closed catalog in `spec/schemas/view-slots.schema.json`. A view contribution emitted by an extension whose qualified id is missing from the registry is dropped by the UI's slot host (mirrors the kindRegistry contract, `startsAsDisabled` drop-ins illustrate the absence path).
627
629
 
@@ -20,6 +20,6 @@
20
20
  { "type": "json-path", "path": "$.issues[0].severity", "equals": "warn" },
21
21
  { "type": "json-path", "path": "$.issues[0].data.expectedMdPath", "equals": ".claude/agents/orphan.md" },
22
22
  { "type": "json-path", "path": "$.issues[1].analyzerId", "equals": "annotation-stale" },
23
- { "type": "json-path", "path": "$.issues[1].severity", "equals": "warn" }
23
+ { "type": "json-path", "path": "$.issues[1].severity", "equals": "info" }
24
24
  ]
25
25
  }
package/index.json CHANGED
@@ -174,21 +174,21 @@
174
174
  }
175
175
  ]
176
176
  },
177
- "specPackageVersion": "0.45.1",
177
+ "specPackageVersion": "0.46.0",
178
178
  "integrity": {
179
179
  "algorithm": "sha256",
180
180
  "files": {
181
- "CHANGELOG.md": "72c9231484764c7ef75aebde2e5a3f35fa9bac6c4585071332c32ae23f917a0c",
181
+ "CHANGELOG.md": "f144432840afc98bb65fb0982865d1f7ed233cae2ec0b0eaea94c84d2299f771",
182
182
  "README.md": "a7505a7b0672c39a8b011e3c5e7d41826306476ee63768249bba4bdb3c03d4d1",
183
- "architecture.md": "49644c727384f8e12061be834bc97e57d47459dae7bea096f94330b74f568a93",
184
- "cli-contract.md": "08b03016e89bd3ce48f32c1b31489f769c97fa1893fe1d13c99a70d8238783e1",
183
+ "architecture.md": "b45089d52095177228c443c037ea699061f59ba4fb2b70231ebf8218ab49561d",
184
+ "cli-contract.md": "92259a45962f4c0374f707b33b814bce6430eca7936d2ff52e7095484c634279",
185
185
  "conformance/README.md": "0c69bd9becf511ada9175b1e428ba183e31d1c8a49ff09eedf4c950bb831ec4d",
186
186
  "conformance/cases/extractor-emits-signal.json": "0115c7bb62a7a705f72e9d8048b3f0396e5caaeb3d04dea204415e279e58479d",
187
187
  "conformance/cases/kernel-empty-boot.json": "9b51b85ff62479cd0eee37cad260245208d94f6d79644f7ee40945a934960913",
188
188
  "conformance/cases/no-global-scope.json": "1c83343422144be2ad9e3d27d2062e61af87c7c1c1f3b051b6b9f687d845ac7b",
189
189
  "conformance/cases/orphan-markdown-fallback.json": "506119323ddde85c1fb4c986c7f6f40a345d44adb06de8d84002591df0e479ee",
190
190
  "conformance/cases/plugin-missing-ui-rejected.json": "7c910b74e6f718ab5c1a590cd3544602f056559251d18995a26bca0a0648a2fa",
191
- "conformance/cases/sidecar-end-to-end.json": "2de448c52a93139143e5290df2842d898b3984c91fbee315d7a26157efd9b123",
191
+ "conformance/cases/sidecar-end-to-end.json": "0a0d941ab50bd7619e1021a6c6d6dc92918429c2efcf25236b42b5fac9eab901",
192
192
  "conformance/cases/signal-collision-detection.json": "c5e39a406ded6928a14c1a22b84f7b3cd49805bec56bd65de83130d9e419c09e",
193
193
  "conformance/coverage.md": "70cfa9a5736f1e12845da46c4c217b8a6061148f54548b67a30f1c74e3381bc5",
194
194
  "conformance/fixtures/orphan-markdown/.claude/agents/reviewer.md": "7f062731106f2d9811e4fffcf6ab44b8dfff4cfb16536a469514cc0664e832bf",
@@ -216,7 +216,7 @@
216
216
  "plugin-kv-api.md": "1acc69ed82433a74e35ada61d63a6d7379fb61046ff83de1e0facbe884c64704",
217
217
  "prompt-preamble.md": "9dd4f6d1bc6a425f8782fcee10cbe75909e8d64e28781fda56c2fae909b02f40",
218
218
  "schemas/annotations.schema.json": "8c639b149cad675fdd4e7d6be2b47e920cfdd24087b41361d6e1b8280f646322",
219
- "schemas/api/rest-envelope.schema.json": "19cd02c96faa12b59768cd9e2e74b8830a07fbcba104de797345b6a61c0f44c3",
219
+ "schemas/api/rest-envelope.schema.json": "cbab82381a49a0a7db796d61a11977abd51d62be423cc19af988a5301a55354f",
220
220
  "schemas/bump-report.schema.json": "c763e1f89f2665c479d6a4985c1d324c65e5278331ebab82220287a07e4c4429",
221
221
  "schemas/conformance-case.schema.json": "958b316d646d0c64a715a7a28cee66d2c2d2498a60dbfc5ae8970687c2a96954",
222
222
  "schemas/conformance-result.schema.json": "14f983a8f4e62cd4ff964688c9b2b026a3bee3a0b762b64091c8c34db5b75777",
@@ -243,7 +243,7 @@
243
243
  "schemas/report-base-deterministic.schema.json": "59785fe6f3ceb34814bbbd03d10fa7336a32835ce598946f2923d469b32aa32a",
244
244
  "schemas/report-base.schema.json": "e4d25f055e24f18ae0f77c24661c1bddc87ff2e43b001b6a827fcb14f9753f44",
245
245
  "schemas/scan-result.schema.json": "9fb81f496d6f8bdcb82131d0b2eb532da1addb801e7d27bd192a0c286a28c2c0",
246
- "schemas/sidecar.schema.json": "f23dfe3ba7f71a1af2cc5cd26b57d5e057e56438655f750c1895d35061efe80a",
246
+ "schemas/sidecar.schema.json": "f9d914e61b2d04495b84dc90e55240aca959e6f16137e5bfa4c0e10ada33ecbe",
247
247
  "schemas/signal.schema.json": "57baf52e55fc9a6f122fb9b33395b5a2790e7f5b7d461cf576099b68a8a17159",
248
248
  "schemas/summaries/agent.schema.json": "5b26b95fb082b73d302c8aa6489ab09488a155ccfbb8943dfc47079509d35122",
249
249
  "schemas/summaries/command.schema.json": "7f522c682d0fdf5a40172c7fc8fcd23e60a0ab0253354146525bd3a3d417f1f8",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skill-map/spec",
3
- "version": "0.45.1",
3
+ "version": "0.46.0",
4
4
  "description": "JSON Schemas, prose contracts, and conformance suite for the skill-map specification.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -130,7 +130,7 @@
130
130
  },
131
131
  "providerRegistry": {
132
132
  "type": "object",
133
- "description": "Catalog of Providers registered in the current scope, keyed by Provider id. Built once per server boot from every registered Provider's `ui` block and embedded into every payload-bearing envelope so the UI renders the active-lens dropdown, the topbar lens chip, and the per-node provider chip from the real Provider set instead of a hardcoded list. Sentinel envelopes (`health`, `scan`, `graph`), action-result envelopes (`sidecar.bumped`), and the catalog envelopes (`annotations.registered`, `contributions.registered`) are exempt. Mirror of `kindRegistry`, parallel surface. The active lens itself (current value + filesystem-detected candidates) is NOT here; it is served by `GET /api/active-provider`, a per-project dynamic surface orthogonal to this static boot catalog.",
133
+ "description": "Catalog of Providers registered in the current scope, keyed by Provider id. Built once per server boot from every registered Provider's `ui` block and embedded into every payload-bearing envelope so the UI renders the active-lens dropdown, the topbar lens chip, and the per-node provider chip from the real Provider set instead of a hardcoded list. Sentinel envelopes (`health`, `scan`, `graph`), action-result envelopes (`sidecar.bumped`), and the catalog envelopes (`annotations.registered`, `contributions.registered`) are exempt. Mirror of `kindRegistry`, parallel surface. The active lens itself (current value + filesystem-detected candidates + the enabled `selectable` set) is NOT here; it is served by `GET /api/active-provider`, a per-project dynamic surface orthogonal to this static boot catalog.",
134
134
  "additionalProperties": {
135
135
  "type": "object",
136
136
  "required": ["label", "color"],
@@ -38,7 +38,7 @@
38
38
  "lastBumpedBy": {
39
39
  "type": "string",
40
40
  "minLength": 1,
41
- "description": "Identity of the bump invoker. Literal `'cli'` for `sm bump` (we are not threading user identity yet); `'plugin:<plugin-id>'` when a plugin's deterministic Action triggered the bump. Tests / future invokers MAY pass any non-empty string."
41
+ "description": "Identity of the bump invoker. When the project is a Git repository with `user.name` configured, this is the resolved Git author name (`git config user.name`). Otherwise it falls back to the channel literal: `'cli'` for `sm bump`, `'ui'` for the BFF bump route, `'plugin:<plugin-id>'` when a plugin's deterministic Action triggered the bump. Tests / future invokers MAY pass any non-empty string."
42
42
  },
43
43
  "createdAt": {
44
44
  "type": "string",