@skill-map/spec 0.45.1 → 0.47.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.
Files changed (30) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/architecture.md +18 -11
  3. package/cli-contract.md +11 -14
  4. package/conformance/README.md +1 -0
  5. package/conformance/cases/plugin-missing-ui-rejected.json +1 -1
  6. package/conformance/cases/sidecar-end-to-end.json +1 -1
  7. package/conformance/cases/view-action-button.json +21 -0
  8. package/conformance/cases/view-contribution-payloads.json +19 -0
  9. package/conformance/cases/view-slots-all.json +15 -0
  10. package/conformance/coverage.md +1 -1
  11. package/conformance/fixtures/view-action-button/.skill-map/plugins/badge-actions/analyzers/good-badges/index.js +46 -0
  12. package/conformance/fixtures/view-action-button/.skill-map/plugins/badge-actions/plugin.json +6 -0
  13. package/conformance/fixtures/view-action-button/.skill-map/plugins/legacy-badge/analyzers/header-counter/index.js +28 -0
  14. package/conformance/fixtures/view-action-button/.skill-map/plugins/legacy-badge/plugin.json +6 -0
  15. package/conformance/fixtures/view-action-button/notes/example.md +6 -0
  16. package/conformance/fixtures/view-contribution-payloads/.skill-map/plugins/payloads-demo/analyzers/panels/index.js +37 -0
  17. package/conformance/fixtures/view-contribution-payloads/.skill-map/plugins/payloads-demo/plugin.json +6 -0
  18. package/conformance/fixtures/view-contribution-payloads/notes/example.md +5 -0
  19. package/conformance/fixtures/view-slots-all/.skill-map/plugins/all-slots/analyzers/everything/index.js +35 -0
  20. package/conformance/fixtures/view-slots-all/.skill-map/plugins/all-slots/plugin.json +6 -0
  21. package/index.json +29 -16
  22. package/package.json +1 -1
  23. package/plugin-author-guide.md +42 -6
  24. package/schemas/api/rest-envelope.schema.json +13 -4
  25. package/schemas/extensions/action.schema.json +32 -0
  26. package/schemas/extensions/base.schema.json +4 -0
  27. package/schemas/plugins-doctor.schema.json +45 -2
  28. package/schemas/plugins-registry.schema.json +4 -0
  29. package/schemas/sidecar.schema.json +1 -1
  30. package/schemas/view-slots.schema.json +112 -23
package/index.json CHANGED
@@ -174,23 +174,26 @@
174
174
  }
175
175
  ]
176
176
  },
177
- "specPackageVersion": "0.45.1",
177
+ "specPackageVersion": "0.47.0",
178
178
  "integrity": {
179
179
  "algorithm": "sha256",
180
180
  "files": {
181
- "CHANGELOG.md": "72c9231484764c7ef75aebde2e5a3f35fa9bac6c4585071332c32ae23f917a0c",
181
+ "CHANGELOG.md": "456edc2e8eaab28524945c04f3678354b06f4cd4dfa793ff9b612e2b25aa7fc9",
182
182
  "README.md": "a7505a7b0672c39a8b011e3c5e7d41826306476ee63768249bba4bdb3c03d4d1",
183
- "architecture.md": "49644c727384f8e12061be834bc97e57d47459dae7bea096f94330b74f568a93",
184
- "cli-contract.md": "08b03016e89bd3ce48f32c1b31489f769c97fa1893fe1d13c99a70d8238783e1",
185
- "conformance/README.md": "0c69bd9becf511ada9175b1e428ba183e31d1c8a49ff09eedf4c950bb831ec4d",
183
+ "architecture.md": "159163aa0e2a225f5d701e4edd18ef45ce7ca22a75f6ce8e76e2a4d528561c3b",
184
+ "cli-contract.md": "71067c7ff7a845afaa3537f1dd0be5b34b20034d06b42fc29261486ff3f6021b",
185
+ "conformance/README.md": "4ec22ca3cc8e4282fe0bfd111f22b121e0781e2b525867cd092258b8f58ae1e1",
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
- "conformance/cases/plugin-missing-ui-rejected.json": "7c910b74e6f718ab5c1a590cd3544602f056559251d18995a26bca0a0648a2fa",
191
- "conformance/cases/sidecar-end-to-end.json": "2de448c52a93139143e5290df2842d898b3984c91fbee315d7a26157efd9b123",
190
+ "conformance/cases/plugin-missing-ui-rejected.json": "2074fd71937feae136c999f76da81f334f2caf8b65bfe8dc9d7fb800699fb85c",
191
+ "conformance/cases/sidecar-end-to-end.json": "0a0d941ab50bd7619e1021a6c6d6dc92918429c2efcf25236b42b5fac9eab901",
192
192
  "conformance/cases/signal-collision-detection.json": "c5e39a406ded6928a14c1a22b84f7b3cd49805bec56bd65de83130d9e419c09e",
193
- "conformance/coverage.md": "70cfa9a5736f1e12845da46c4c217b8a6061148f54548b67a30f1c74e3381bc5",
193
+ "conformance/cases/view-action-button.json": "51331f725be1c3655351f8fca6fc9d3d301ae68ea1741ff6c79998332ba2dfeb",
194
+ "conformance/cases/view-contribution-payloads.json": "e8f54ed62e64a2a0f86729866e507abb1f4246683f0e60d538280536f7cd3ecc",
195
+ "conformance/cases/view-slots-all.json": "05284e0324dd2da72b6b21d397c11b355802229a68053e9dddc323f69b3a1eba",
196
+ "conformance/coverage.md": "f93676ede774003c4d15ccf8d3bf2f65b5d032d75ae572df01dff892aeb1a8cf",
194
197
  "conformance/fixtures/orphan-markdown/.claude/agents/reviewer.md": "7f062731106f2d9811e4fffcf6ab44b8dfff4cfb16536a469514cc0664e832bf",
195
198
  "conformance/fixtures/orphan-markdown/ARCHITECTURE.md": "ec903666440bae65da3796b1158c92cfcdce22e0e09c3b20bb690176881a6ac4",
196
199
  "conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/kinds/markdown/kind.json": "6676a89bae5197e23cf50f1c11d596db558ac80f7334a7208fe57d8b92422251",
@@ -208,22 +211,32 @@
208
211
  "conformance/fixtures/signal-ir-collision/.claude/agents/architect.md": "acc46b5b2dff73d98a354e4d53b5041164595deae466a4e2ce41d7c5a72f28fb",
209
212
  "conformance/fixtures/signal-ir-single-signal/source.md": "1eda417b4c6eed372b66870e385c8d8cd631372b77cab7e996bb711e22218f89",
210
213
  "conformance/fixtures/signal-ir-single-signal/target.md": "527137f2b4f46c0034b0edc8932cf8613d2bf22ffaaf78f01085c82a3baaebe3",
214
+ "conformance/fixtures/view-action-button/.skill-map/plugins/badge-actions/analyzers/good-badges/index.js": "943fc3f11c328d2ccc4f8474106f4ae92077d353d02bd0207153efd1d0a1cf42",
215
+ "conformance/fixtures/view-action-button/.skill-map/plugins/badge-actions/plugin.json": "ed7b048c140d3d5acdf4456678acba8d9d55fd63511013c8621122b7062f40be",
216
+ "conformance/fixtures/view-action-button/.skill-map/plugins/legacy-badge/analyzers/header-counter/index.js": "7e097f3f2efcf1175dd02c926a8872f9d2de584c1e6a09fcceb56d603a4386ce",
217
+ "conformance/fixtures/view-action-button/.skill-map/plugins/legacy-badge/plugin.json": "1ed0decf76d195da2caa5949d6b11fc8fea097416e263d77ed294e5d158304dc",
218
+ "conformance/fixtures/view-action-button/notes/example.md": "c8fad69ee251b25080869c36d84c1f6b697773526a1cd8bda5a577e2027ebb55",
219
+ "conformance/fixtures/view-contribution-payloads/.skill-map/plugins/payloads-demo/analyzers/panels/index.js": "b313ec830ab48bd72e9f347e9d161469a56b8a805cf0861061d0012a452a2706",
220
+ "conformance/fixtures/view-contribution-payloads/.skill-map/plugins/payloads-demo/plugin.json": "18b7d246f004829ed3e86fa40654b34ac2e1ab416aa083fa17ae2e4f13ac3c0d",
221
+ "conformance/fixtures/view-contribution-payloads/notes/example.md": "312b1919cd7fd0f233648b053acfb2975662ede3c65dd391cc508204b67ad6fb",
222
+ "conformance/fixtures/view-slots-all/.skill-map/plugins/all-slots/analyzers/everything/index.js": "ea0022fec7f0fd5a26ba12db1310335f434f2f820682206a3a9542d98db0d346",
223
+ "conformance/fixtures/view-slots-all/.skill-map/plugins/all-slots/plugin.json": "c48e8a0574947ade0b4eb189d6bc27a48e24f92f616aacdc177f2d22d472a599",
211
224
  "db-schema.md": "f74ce6766bf7f2dcda187a49f82e1768bc1c091d9492846e718903a379610e2e",
212
225
  "interfaces/security-scanner.md": "e8049712b9cf7a07c786bf19f8f775f8ef9638f063f7fba5c7a8b1431b92f38e",
213
226
  "job-events.md": "9d5b35d4c451a7f8eef9915d85316d924ac52f1c026a316cdda5f1099d496854",
214
227
  "job-lifecycle.md": "9c429121f98a07c8795f8979ed1abc5e5334e3f89db51585a8da55c527ef855b",
215
- "plugin-author-guide.md": "b0c510529eb660c753cedfa6397fbf0520135f83f6c8e3cb3816fbfa1c9537a9",
228
+ "plugin-author-guide.md": "8ba0906346300f0d10f0d4a1646b8b27f82cc867c9bfe4d495ebf0f146d6ad0f",
216
229
  "plugin-kv-api.md": "1acc69ed82433a74e35ada61d63a6d7379fb61046ff83de1e0facbe884c64704",
217
230
  "prompt-preamble.md": "9dd4f6d1bc6a425f8782fcee10cbe75909e8d64e28781fda56c2fae909b02f40",
218
231
  "schemas/annotations.schema.json": "8c639b149cad675fdd4e7d6be2b47e920cfdd24087b41361d6e1b8280f646322",
219
- "schemas/api/rest-envelope.schema.json": "19cd02c96faa12b59768cd9e2e74b8830a07fbcba104de797345b6a61c0f44c3",
232
+ "schemas/api/rest-envelope.schema.json": "8eeb1c2d79fb69eaef23737a2231d48d67e59b8b19aad816239ab4680e2c4752",
220
233
  "schemas/bump-report.schema.json": "c763e1f89f2665c479d6a4985c1d324c65e5278331ebab82220287a07e4c4429",
221
234
  "schemas/conformance-case.schema.json": "958b316d646d0c64a715a7a28cee66d2c2d2498a60dbfc5ae8970687c2a96954",
222
235
  "schemas/conformance-result.schema.json": "14f983a8f4e62cd4ff964688c9b2b026a3bee3a0b762b64091c8c34db5b75777",
223
236
  "schemas/execution-record.schema.json": "db0eb16153493ad9f13ea0ecede44191e4a8536979adffd17ca278ddf8786c77",
224
- "schemas/extensions/action.schema.json": "dc4f52d23c163c6239a487fa1c1ad9c09685cf38833d3962c604d5872716cff9",
237
+ "schemas/extensions/action.schema.json": "8b300532c0217c0f65c454edd6df86d1fe4245590fb5e0974944ce9e593f7f28",
225
238
  "schemas/extensions/analyzer.schema.json": "8def4a5ca4934197c34abde97da70704b2751041a443c859eddd4b783e2fe1db",
226
- "schemas/extensions/base.schema.json": "49baa06a4ce8a6ce75fec52b650d9bf3566e5de0b1053b06f73a71ce103e4fdf",
239
+ "schemas/extensions/base.schema.json": "525226313fd8886a934f944218d34178a3a4e234ee3ca3ddb189ec4f60caec85",
227
240
  "schemas/extensions/extractor.schema.json": "ee44bf562b19318c93116c574a811857cdef1f4119326a9a604fa408889dd230",
228
241
  "schemas/extensions/formatter.schema.json": "880dc379ad545a62404403533a01eda5171edba0390561fc46ec6e986e0b9bd3",
229
242
  "schemas/extensions/hook.schema.json": "f56aef59e9986ffdf7d86aa2e048dccccf217000a358b8c64737cbd911c48dad",
@@ -236,14 +249,14 @@
236
249
  "schemas/job.schema.json": "dbcedf137de03fde38f74686f594e600c627bf808f2aad23511a26617a663a02",
237
250
  "schemas/link.schema.json": "10f700feb3e23d89453d4d11cbe559bcc0b31f6edf08fffbe9e6773e58120512",
238
251
  "schemas/node.schema.json": "14ed2e4c44d01e3f662e240219819895cca06dead374a5cadccfd423c520ed69",
239
- "schemas/plugins-doctor.schema.json": "2238266f31402a446b313af16f933e395a02eca70128e39ab99a11de90a4735f",
240
- "schemas/plugins-registry.schema.json": "6d850d06cdf70e233f20d0d7968bb0c34306f11f30ce2505cec173cd9fa784e5",
252
+ "schemas/plugins-doctor.schema.json": "03e2dc51c052a09bf0198c80e2c26e6129734ada4a748e483245de3dd8576c42",
253
+ "schemas/plugins-registry.schema.json": "211d081691fc83526e1593c79ed9741ad8a5dbd4db1a756f72141b0cced2ea15",
241
254
  "schemas/project-config.schema.json": "0a4a12a3409f900bd19b47c34588c77ac894b944d21a9beebb91ae1e9c0f3d01",
242
255
  "schemas/refresh-report.schema.json": "47184d4f6b15e9b7671dc178b3b3886a64422da198898508ecdb2cb27876db04",
243
256
  "schemas/report-base-deterministic.schema.json": "59785fe6f3ceb34814bbbd03d10fa7336a32835ce598946f2923d469b32aa32a",
244
257
  "schemas/report-base.schema.json": "e4d25f055e24f18ae0f77c24661c1bddc87ff2e43b001b6a827fcb14f9753f44",
245
258
  "schemas/scan-result.schema.json": "9fb81f496d6f8bdcb82131d0b2eb532da1addb801e7d27bd192a0c286a28c2c0",
246
- "schemas/sidecar.schema.json": "f23dfe3ba7f71a1af2cc5cd26b57d5e057e56438655f750c1895d35061efe80a",
259
+ "schemas/sidecar.schema.json": "f9d914e61b2d04495b84dc90e55240aca959e6f16137e5bfa4c0e10ada33ecbe",
247
260
  "schemas/signal.schema.json": "57baf52e55fc9a6f122fb9b33395b5a2790e7f5b7d461cf576099b68a8a17159",
248
261
  "schemas/summaries/agent.schema.json": "5b26b95fb082b73d302c8aa6489ab09488a155ccfbb8943dfc47079509d35122",
249
262
  "schemas/summaries/command.schema.json": "7f522c682d0fdf5a40172c7fc8fcd23e60a0ab0253354146525bd3a3d417f1f8",
@@ -251,7 +264,7 @@
251
264
  "schemas/summaries/markdown.schema.json": "369d75a18710eb6c7ee4220b2899767ddbc07dada24f81b611827fe2e3a2977a",
252
265
  "schemas/summaries/skill.schema.json": "85d68056054bade62391948cc038fcfa70cdcf465e2b295f69cd520bbdba0134",
253
266
  "schemas/user-settings.schema.json": "d155552ffca9c7dd4c6e31398aff4950dd9721d2a1f4b308cf0fe33000ca31b5",
254
- "schemas/view-slots.schema.json": "4623cf8d774f44435f960b1dd3ad4c8b241e37b234ce8eab5b390b9ae8a2acb1",
267
+ "schemas/view-slots.schema.json": "886487a1f38fd7e4270fa6213653664c0cf906043e8aa9e832017149932bf6a2",
255
268
  "telemetry.md": "fa659b47c59e692f50c7a091470888d5e7c98dcf858978fa549af25b2562803f",
256
269
  "versioning.md": "28a13f165f837921fe5066f4bfce61012cd9f1b7c451f88eeb67252e39a0981a"
257
270
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skill-map/spec",
3
- "version": "0.45.1",
3
+ "version": "0.47.0",
4
4
  "description": "JSON Schemas, prose contracts, and conformance suite for the skill-map specification.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -557,6 +557,27 @@ Inside an extractor or analyzer manifest, declare a `ui` map (sibling of `annota
557
557
  }
558
558
  ```
559
559
 
560
+ In TypeScript, declare each contribution as a module-level const with `satisfies IViewContribution` and build `ui` by shorthand. You then emit by passing the SAME object by reference (see [Emit path](#emit-path)) and get a typed payload for free:
561
+
562
+ ```ts
563
+ import type { IViewContribution } from '@skill-map/cli';
564
+
565
+ const breakdown = {
566
+ slot: 'inspector.body.panel.breakdown', label: 'Keyword hits', emptyText: 'No matches.',
567
+ } satisfies IViewContribution;
568
+ const total = {
569
+ slot: 'card.footer.left', icon: '🔍', label: 'kw', emitWhenEmpty: false,
570
+ } satisfies IViewContribution;
571
+
572
+ export default {
573
+ // ...
574
+ ui: { breakdown, total },
575
+ // ...
576
+ };
577
+ ```
578
+
579
+ The `ui` **key** (kebab-case per the manifest schema) is the contribution id; the const's variable name is incidental, because the kernel matches an emission to its declaration by object identity, not by name. Plain `.js` plugins use the same shape without `satisfies` (they get the runtime check, not the compile-time one).
580
+
560
581
  Field reference (full schema in [`schemas/view-slots.schema.json`](./schemas/view-slots.schema.json) at `$defs/IViewContribution`):
561
582
 
562
583
  | Field | Required | Notes |
@@ -596,8 +617,8 @@ The kernel ships exactly these 14 slots. Each fixes a renderer + a payload shape
596
617
  | `card.footer.left` | counter chip (icon required) |
597
618
  | `card.footer.right` | counter chip (icon required) |
598
619
  | `graph.node.alert` | graph corner badge (reserved, see `view-slots.md`) |
599
- | `inspector.header.badge.counter` | counter chip (icon required) |
600
- | `inspector.header.badge.tag` | tag chip |
620
+ | `inspector.header.badge` | unified header badge (icon and/or label and/or count) |
621
+ | `inspector.action.button` | action button (dispatches an Action, see `view-slots.md`) |
601
622
  | `inspector.body.panel.breakdown` | bar chart panel |
602
623
  | `inspector.body.panel.records` | table panel |
603
624
  | `inspector.body.panel.tree` | tree panel |
@@ -606,6 +627,19 @@ The kernel ships exactly these 14 slots. Each fixes a renderer + a payload shape
606
627
  | `inspector.body.panel.markdown` | sanitized markdown panel |
607
628
  | `topbar.nav.start` | scope chip |
608
629
 
630
+ ### Inspector grouping and `order`
631
+
632
+ The six `inspector.body.panel.*` contributions are not rendered in a shared drawer. The inspector groups them **one collapsible section per plugin**, titled by the plugin id (host-applied from the trusted contribution `pluginId`, never the payload) and **collapsed by default**. A plugin's bricks only ever land in its own section: a plugin cannot contribute into another plugin's space.
633
+
634
+ Two optional, inspector-only `order` hints (both `number`, default `100`) control layout:
635
+
636
+ | Field | Where | Effect |
637
+ |---|---|---|
638
+ | `order` | `plugin.json` (plugin level) | Sorts the plugin sections, ASC, tie-break by plugin id. |
639
+ | `order` | extension manifest (extension level) | Sorts the bricks inside a plugin's section, ASC, tie-break by the contribution `priority` then qualified id. |
640
+
641
+ `order` is purely presentational and never affects execution order (analyzer `phase` + registration order govern that). It only applies to the inspector body sections; `priority` still governs ordering within the card / header / action slots.
642
+
609
643
  ### Chip vs Issue
610
644
 
611
645
  For analyzers, a per-node card surfaces a finding through two independent channels: the `Issue` returned by `evaluate(ctx)` feeds the aggregated stats and the scan / check exit code; a view contribution to a card slot is **purely presentational** (its `severity` controls only the chip's own colour, never the count, never the exit code). The colour rule, when a chip may paint `warn` / `danger`, and the reserved status of `graph.node.alert` are documented in [`view-slots.md` §Chip vs Issue](./view-slots.md). Breaking the colour rule produces visually misleading cards and is caught in code review, not by the schema.
@@ -614,14 +648,14 @@ For analyzers, a per-node card surfaces a finding through two independent channe
614
648
 
615
649
  ```ts
616
650
  // Extractor (per-node walk): nodePath is implicit (ctx.node.path)
617
- ctx.emitContribution('breakdown', { entries: [...] });
618
- ctx.emitContribution('total', { value: total });
651
+ ctx.emitContribution(breakdown, { bars: [...] });
652
+ ctx.emitContribution(total, { value });
619
653
 
620
654
  // Analyzer (post-merge graph): explicit nodePath, the analyzer sees every node at once
621
- ctx.emitContribution(nodePath, 'breakdown', { ... });
655
+ ctx.emitContribution(nodePath, breakdown, { bars: [...] });
622
656
  ```
623
657
 
624
- The first id argument is the **manifest `ui` key**, NOT the slot name; the kernel composes the qualified id from your plugin id, extension id, and the key, and looks up the declared slot to validate the payload against `view-slots.schema.json#/$defs/payloads/<slot>`. Off-shape payloads emit an `extension.error` and drop silently, same posture as `emitLink`. For `topbar.nav.start`, analyzers use `ctx.emitScopeContribution(id, payload)` (reserved in the spec; the runtime callback lands when the first scope-level adopter arrives).
658
+ Pass the contribution **object you declared in `ui`, by reference** (the `const` above), not a string id. The kernel recovers the contribution id (the `ui` key) by object identity and looks up the declared slot to validate the payload against `view-slots.schema.json#/$defs/payloads/<slot>`. The payload argument is typed from `ref.slot` (`SlotPayload<C['slot']>`), so a wrong-shape payload is a **compile error** in TypeScript. At runtime, a ref that is not one of your declared `ui` objects (a spread copy, an inline literal) or an off-shape payload emits an `extension.error` and drops, same posture as `emitLink`. For `topbar.nav.start`, analyzers use `ctx.emitScopeContribution(ref, payload)` (reserved in the spec; the runtime callback lands when the first scope-level adopter arrives).
625
659
 
626
660
  To surface the same data in two surfaces, declare two contributions (one per slot) and emit twice, there is no broadcast.
627
661
 
@@ -705,6 +739,8 @@ Analyzers take a `ctx` with `nodes`, `links`, and (if you assert on view contrib
705
739
 
706
740
  `sm plugins doctor` runs the full load pass and exits `1` if any plugin is in a non-`loaded` / non-`disabled` state. Wire it into CI.
707
741
 
742
+ Beyond load status, `sm plugins doctor` also reports **runtime contribution errors from the last scan**: view contributions rejected at emit time (an undeclared ref, or a payload that fails the slot's schema) are persisted per scan and surfaced in a "Runtime contribution errors (last scan)" section grouped by plugin, and any present also promote the exit code to `1`. A plugin can be `loaded` (clean manifest) yet still have runtime rejections, a healthy `list` status does not mean your chips actually rendered. The same errors appear per-plugin in the Settings plugin panel (a warning badge plus a collapsible diagnostics list). Re-run `sm scan` after a fix to clear them.
743
+
708
744
  ---
709
745
 
710
746
  ## Scaffolder
@@ -24,6 +24,7 @@
24
24
  "health",
25
25
  "scan",
26
26
  "sidecar.bumped",
27
+ "action.applied",
27
28
  "annotations.registered",
28
29
  "contributions.registered"
29
30
  ],
@@ -39,7 +40,7 @@
39
40
  },
40
41
  "value": {
41
42
  "type": "object",
42
- "description": "Present when `kind` is `'config'` or `'sidecar.bumped'`. For `'config'`, carries the merged effective config object. For `'sidecar.bumped'`, carries `{ nodePath, version, status }` (the Action-result payload from `POST /api/sidecar/bump`)."
43
+ "description": "Present when `kind` is `'config'`, `'sidecar.bumped'`, or `'action.applied'`. For `'config'`, carries the merged effective config object. For `'sidecar.bumped'`, carries `{ nodePath, version, status }` (the bump report from `POST /api/sidecar/bump`). For `'action.applied'`, carries `{ actionId, nodePath, report }` (the generic Action-result payload from `POST /api/actions/:id`)."
43
44
  },
44
45
  "elapsedMs": {
45
46
  "type": "integer",
@@ -124,13 +125,21 @@
124
125
  "priority": {
125
126
  "type": "number",
126
127
  "description": "Optional ordering hint (default 100 when omitted). Slots whose `order` is `'priority'` sort contributions ASC by this value with alphabetical tie-break by qualified id. Mirror of `IViewContribution.priority` in `view-slots.schema.json#/$defs/ViewContribution`; propagated so the UI can apply the manifest-declared order without a second round-trip."
128
+ },
129
+ "pluginOrder": {
130
+ "type": "number",
131
+ "description": "Optional inspector-only ordering hint, denormalised from the owning plugin's `plugin.json` `order` field (default 100). The inspector groups `inspector.body.panel.*` contributions into one collapsible section per plugin and sorts those sections ASC by this value, tie-break by plugin id. Every contribution of a given plugin carries the same value."
132
+ },
133
+ "extensionOrder": {
134
+ "type": "number",
135
+ "description": "Optional inspector-only ordering hint, denormalised from the owning extension's `order` manifest field (default 100). Inside a plugin's inspector section, bricks are sorted ASC by this value, tie-break by `priority` then qualified id. Every contribution of a given extension carries the same value."
127
136
  }
128
137
  }
129
138
  }
130
139
  },
131
140
  "providerRegistry": {
132
141
  "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.",
142
+ "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
143
  "additionalProperties": {
135
144
  "type": "object",
136
145
  "required": ["label", "color"],
@@ -251,10 +260,10 @@
251
260
  }
252
261
  },
253
262
  {
254
- "description": "Action-result envelope, `value` + `elapsedMs` siblings, no `filters` / `counts` / `kindRegistry` / `providerRegistry` / `contributionsRegistry`. Used by `POST /api/sidecar/bump` (Step 9.6.5, BFF half) where the response carries the bump report (`{ nodePath, version, status }`) plus the wall-clock duration. The registries are intentionally absent, the action result is orthogonal to every catalog and the SPA already has them cached from a prior list call.",
263
+ "description": "Action-result envelope, `value` + `elapsedMs` siblings, no `filters` / `counts` / `kindRegistry` / `providerRegistry` / `contributionsRegistry`. Used by `POST /api/actions/:id` (`action.applied`, carries `{ actionId, nodePath, report }`) and the legacy `POST /api/sidecar/bump` (`sidecar.bumped`, Step 9.6.5, carries `{ nodePath, version, status }`), in both cases plus the wall-clock duration. The registries are intentionally absent, the action result is orthogonal to every catalog and the SPA already has them cached from a prior list call.",
255
264
  "required": ["value", "elapsedMs"],
256
265
  "properties": {
257
- "kind": { "const": "sidecar.bumped" }
266
+ "kind": { "enum": ["sidecar.bumped", "action.applied"] }
258
267
  },
259
268
  "not": {
260
269
  "anyOf": [
@@ -51,6 +51,38 @@
51
51
  "description": "Qualified analyzer ids whose findings this action is intended to resolve (Modelo B, replaces the deprecated `Analyzer.recommendedActions`). The UI surfaces matching actions in the node inspector under 'Resolve this issue' when the analyzer's id matches an entry here. Format `<plugin>/<analyzer>` or `<plugin>/<analyzer>:<sub-id>` when the analyzer emits sub-typed issues. Dangling references warn via `recommended-action-missing` in `sm plugins doctor` but do NOT block load."
52
52
  }
53
53
  }
54
+ },
55
+ "prompt": {
56
+ "type": "object",
57
+ "additionalProperties": false,
58
+ "required": ["inputType", "paramKey", "label"],
59
+ "properties": {
60
+ "inputType": {
61
+ "$ref": "../input-types.schema.json#/$defs/InputTypeName",
62
+ "description": "Input-type id from the closed catalog. The UI renders the matching control before dispatch."
63
+ },
64
+ "paramKey": {
65
+ "type": "string",
66
+ "minLength": 1,
67
+ "maxLength": 48,
68
+ "description": "Key under which the UI-collected value is placed in the dispatch `input` body."
69
+ },
70
+ "label": { "type": "string", "minLength": 1, "maxLength": 64 },
71
+ "options": {
72
+ "type": "array",
73
+ "items": {
74
+ "type": "object",
75
+ "additionalProperties": false,
76
+ "required": ["value", "label"],
77
+ "properties": {
78
+ "value": { "type": "string" },
79
+ "label": { "type": "string" }
80
+ }
81
+ },
82
+ "description": "Choice list for `enum-pick` / `enum-multipick` input types."
83
+ }
84
+ },
85
+ "description": "Reserved (Steps 3+). When set, a parametrized Action declares the single user input it needs; the UI renders the matching input-type control before dispatch and places the value under `paramKey` in the dispatch body. Deterministic no-prompt actions (e.g. `node-bump`) omit it. Mirrors `view-slots.schema.json#/$defs/payloads/_ActionPrompt`."
54
86
  }
55
87
  },
56
88
  "allOf": [
@@ -15,6 +15,10 @@
15
15
  "minLength": 1,
16
16
  "description": "Required short description (1-3 sentences) shown by `sm <kind>s list` and the UI inspector. English-only per AGENTS.md."
17
17
  },
18
+ "order": {
19
+ "type": "number",
20
+ "description": "Optional visual ordering hint, inspector-only. Inside a plugin's inspector section (which groups the plugin's `inspector.body.panel.*` contributions), the bricks contributed by each extension are sorted ASC by this value (default 100), tie-break by the contribution's `priority` then qualified id. Does NOT affect execution order, which is governed by `phase` (analyzers) and registration order."
21
+ },
18
22
  "annotation": {
19
23
  "type": "object",
20
24
  "required": ["schema"],
@@ -2,9 +2,9 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://skill-map.ai/spec/v0/plugins-doctor.schema.json",
4
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).",
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, and the runtime contribution rejections persisted by the last scan. The `elapsedMs` top-level field is the command's own wall-clock (see `cli-contract.md` §Elapsed time).",
6
6
  "type": "object",
7
- "required": ["ok", "kind", "counts", "issues", "warnings", "elapsedMs"],
7
+ "required": ["ok", "kind", "counts", "issues", "warnings", "contributionErrors", "elapsedMs"],
8
8
  "additionalProperties": false,
9
9
  "properties": {
10
10
  "ok": {
@@ -88,6 +88,49 @@
88
88
  }
89
89
  }
90
90
  },
91
+ "contributionErrors": {
92
+ "type": "array",
93
+ "description": "View contributions the last persisted scan REJECTED at emit time (the \"off-shape visible\" follow-up): an `ctx.emitContribution(...)` call whose ref was not a declared contribution, or whose payload failed the target slot's payload schema. Read from `scan_contribution_errors`; empty when the last scan had no rejected emissions, or when no scan has run yet (fresh project / missing DB). Each entry gates the exit code (any contribution error → exit 1). Iteration order matches the human renderer (`pluginId`, `extensionId`, `nodePath`, `emittedAt` ASC).",
94
+ "items": {
95
+ "type": "object",
96
+ "required": ["pluginId", "extensionId", "nodePath", "reason", "message"],
97
+ "additionalProperties": false,
98
+ "properties": {
99
+ "pluginId": {
100
+ "type": "string",
101
+ "minLength": 1,
102
+ "description": "Plugin id of the extension whose emission was rejected."
103
+ },
104
+ "extensionId": {
105
+ "type": "string",
106
+ "minLength": 1,
107
+ "description": "Extension id (within the plugin) that emitted the rejected contribution."
108
+ },
109
+ "nodePath": {
110
+ "type": "string",
111
+ "minLength": 1,
112
+ "description": "Target node path the contribution was emitted against."
113
+ },
114
+ "reason": {
115
+ "type": "string",
116
+ "minLength": 1,
117
+ "description": "Rejection reason: the literal `undeclared-contribution-ref`, or the AJV error string when the payload failed the slot's payload schema."
118
+ },
119
+ "message": {
120
+ "type": "string",
121
+ "description": "Sanitised human-readable diagnostic (mirrors the ephemeral `extension.error` event of kind `contribution-rejected`)."
122
+ },
123
+ "contributionId": {
124
+ "type": "string",
125
+ "description": "Resolved contribution id. Absent for the `undeclared-contribution-ref` shape (no contribution was resolved)."
126
+ },
127
+ "slot": {
128
+ "type": "string",
129
+ "description": "Resolved target slot. Absent for the `undeclared-contribution-ref` shape."
130
+ }
131
+ }
132
+ }
133
+ },
91
134
  "elapsedMs": {
92
135
  "type": "integer",
93
136
  "minimum": 0,
@@ -32,6 +32,10 @@
32
32
  "minLength": 1,
33
33
  "description": "Required short description shown in `sm plugins list` and the UI. English-only per AGENTS.md."
34
34
  },
35
+ "order": {
36
+ "type": "number",
37
+ "description": "Optional visual ordering hint, inspector-only. The inspector renders one collapsible section per plugin (grouping the plugin's `inspector.body.panel.*` contributions); sections are sorted ASC by this value (default 100), tie-break by plugin id. Does NOT affect extension execution order, which is governed by `phase` (analyzers) and registration order."
38
+ },
35
39
  "storage": {
36
40
  "type": "object",
37
41
  "description": "Persistence mode for this plugin. Absent = plugin does not persist state.",
@@ -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",