@gobing-ai/spur 0.4.18 → 0.4.19

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 (64) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/workflows/task-pipeline.yaml +6 -1
  3. package/package.json +1 -1
  4. package/plugins/sp/README.md +10 -6
  5. package/plugins/sp/commands/dev-changelog.md +1 -1
  6. package/plugins/sp/plugin.json +1 -1
  7. package/plugins/sp/skills/spur-cli/SKILL.md +2 -2
  8. package/plugins/sp/skills/spur-cli/references/message.md +7 -5
  9. package/plugins/sp/skills/spur-dev/references/dev-operations.md +3 -2
  10. package/spur.js +1683 -1155
  11. package/web/_astro/{BoardApp.mWyrWCAR.js → BoardApp.CNF3sCnm.js} +1 -1
  12. package/web/_astro/BoardApp.DY8fwPfw.js +322 -0
  13. package/web/_astro/{TaskDetail.CtAXuiv8.js → TaskDetail.8z3DgYux.js} +1 -1
  14. package/web/_astro/{arc.Duf4u60i.js → arc.CFEYbpFT.js} +1 -1
  15. package/web/_astro/{architectureDiagram-3BPJPVTR.JoymtqxN.js → architectureDiagram-3BPJPVTR.3eDvG_X8.js} +1 -1
  16. package/web/_astro/{blockDiagram-GPEHLZMM.BvmhcioC.js → blockDiagram-GPEHLZMM.CMbLzMpk.js} +1 -1
  17. package/web/_astro/{c4Diagram-AAUBKEIU.CfaWkgoQ.js → c4Diagram-AAUBKEIU.2lZW8nZ-.js} +1 -1
  18. package/web/_astro/channel.CdQ8ZO1m.js +1 -0
  19. package/web/_astro/{chunk-2J33WTMH.B7uR02cD.js → chunk-2J33WTMH.BtM1eI3D.js} +1 -1
  20. package/web/_astro/{chunk-4BX2VUAB.SwiEauYR.js → chunk-4BX2VUAB.CKeckzAf.js} +1 -1
  21. package/web/_astro/{chunk-55IACEB6.6ZRCK8PO.js → chunk-55IACEB6.BBmHet8U.js} +1 -1
  22. package/web/_astro/{chunk-727SXJPM.Cxra_G3N.js → chunk-727SXJPM.Bi8v7M_x.js} +1 -1
  23. package/web/_astro/{chunk-AQP2D5EJ.Cxd7gZeX.js → chunk-AQP2D5EJ.DOcBJyb6.js} +1 -1
  24. package/web/_astro/{chunk-FMBD7UC4.DCLhFkVc.js → chunk-FMBD7UC4.C-EdUBD7.js} +1 -1
  25. package/web/_astro/{chunk-ND2GUHAM.B9AmKcv6.js → chunk-ND2GUHAM.IqTyWQhk.js} +1 -1
  26. package/web/_astro/{chunk-QZHKN3VN.DOjCvl6S.js → chunk-QZHKN3VN.a2DShvIL.js} +1 -1
  27. package/web/_astro/{classDiagram-4FO5ZUOK.BMiT_mNf.js → classDiagram-4FO5ZUOK.CQBnvIjk.js} +1 -1
  28. package/web/_astro/{classDiagram-v2-Q7XG4LA2.BMiT_mNf.js → classDiagram-v2-Q7XG4LA2.CQBnvIjk.js} +1 -1
  29. package/web/_astro/{cose-bilkent-S5V4N54A.mLBCEGjI.js → cose-bilkent-S5V4N54A.BW9Ryz7c.js} +1 -1
  30. package/web/_astro/{cynefin-OW5HDTMX.CjDAY0qn.js → cynefin-OW5HDTMX.BPhLeSep.js} +1 -1
  31. package/web/_astro/{dagre-BM42HDAG._G_sP8fP.js → dagre-BM42HDAG._oD9MsDB.js} +1 -1
  32. package/web/_astro/{diagram-2AECGRRQ.39OcfSuz.js → diagram-2AECGRRQ.iciSOlxY.js} +1 -1
  33. package/web/_astro/{diagram-5GNKFQAL.D7v_eNC4.js → diagram-5GNKFQAL.ZzqeC45Q.js} +1 -1
  34. package/web/_astro/{diagram-KO2AKTUF.Da1x-lhg.js → diagram-KO2AKTUF.CMqWGOYB.js} +1 -1
  35. package/web/_astro/{diagram-LMA3HP47.C8EnzbEb.js → diagram-LMA3HP47.B5MPN99W.js} +1 -1
  36. package/web/_astro/{diagram-OG6HWLK6.DPnBj69F.js → diagram-OG6HWLK6.Zgi-ANAF.js} +1 -1
  37. package/web/_astro/{erDiagram-TEJ5UH35.CXMu2TIh.js → erDiagram-TEJ5UH35.Bd5aeIyR.js} +1 -1
  38. package/web/_astro/{flowDiagram-I6XJVG4X.ZklePVtQ.js → flowDiagram-I6XJVG4X.B8LBo_PH.js} +1 -1
  39. package/web/_astro/{ganttDiagram-6RSMTGT7.BhYOkdqk.js → ganttDiagram-6RSMTGT7.Di2HLHk4.js} +1 -1
  40. package/web/_astro/{gitGraphDiagram-PVQCEYII.CADyX8a1.js → gitGraphDiagram-PVQCEYII.BqDuU6fc.js} +1 -1
  41. package/web/_astro/index.DVDW9-eD.css +1 -0
  42. package/web/_astro/{infoDiagram-5YYISTIA.BsdmwjHt.js → infoDiagram-5YYISTIA.C_hw98uF.js} +1 -1
  43. package/web/_astro/{ishikawaDiagram-YF4QCWOH.CzjZ3Crd.js → ishikawaDiagram-YF4QCWOH.uK7JK9Kk.js} +1 -1
  44. package/web/_astro/{journeyDiagram-JHISSGLW.Bxj-ZH_t.js → journeyDiagram-JHISSGLW.D03MNAmn.js} +1 -1
  45. package/web/_astro/{kanban-definition-UN3LZRKU.Cbg3wVvX.js → kanban-definition-UN3LZRKU.DuiDtKhq.js} +1 -1
  46. package/web/_astro/{linear.DJNUrBI9.js → linear.Der6_z5m.js} +1 -1
  47. package/web/_astro/{mermaid.core.DVHQV3SF.js → mermaid.core.CaxkEgwh.js} +4 -4
  48. package/web/_astro/{mindmap-definition-RKZ34NQL.DyomosRU.js → mindmap-definition-RKZ34NQL.DecT082u.js} +1 -1
  49. package/web/_astro/{pieDiagram-4H26LBE5.DajtFknN.js → pieDiagram-4H26LBE5.DuAEfZXh.js} +1 -1
  50. package/web/_astro/{quadrantDiagram-W4KKPZXB.Bjq6SjGe.js → quadrantDiagram-W4KKPZXB.CWXczU1k.js} +1 -1
  51. package/web/_astro/{requirementDiagram-4Y6WPE33.ubBVqGLP.js → requirementDiagram-4Y6WPE33.BsAHUsR6.js} +1 -1
  52. package/web/_astro/{sankeyDiagram-5OEKKPKP.I4q46xd-.js → sankeyDiagram-5OEKKPKP.ghBqvXN8.js} +1 -1
  53. package/web/_astro/{sequenceDiagram-3UESZ5HK.DsCgZIa8.js → sequenceDiagram-3UESZ5HK.BKbwb7ad.js} +1 -1
  54. package/web/_astro/{stateDiagram-AJRCARHV.DcYjYSqT.js → stateDiagram-AJRCARHV.D5geOKAc.js} +1 -1
  55. package/web/_astro/{stateDiagram-v2-BHNVJYJU.Dt8yPqiS.js → stateDiagram-v2-BHNVJYJU.Dc4Sjs4R.js} +1 -1
  56. package/web/_astro/{timeline-definition-PNZ67QCA.0YTkoa5F.js → timeline-definition-PNZ67QCA.ve2dW2tR.js} +1 -1
  57. package/web/_astro/{vennDiagram-CIIHVFJN.BNl0l1hs.js → vennDiagram-CIIHVFJN.BdJumy3f.js} +1 -1
  58. package/web/_astro/{wardleyDiagram-YWT4CUSO.BYSX6cfn.js → wardleyDiagram-YWT4CUSO.X8HgtN8V.js} +1 -1
  59. package/web/_astro/{xychartDiagram-2RQKCTM6.DzxxpixK.js → xychartDiagram-2RQKCTM6.C30VVwQ6.js} +1 -1
  60. package/web/board-runtime.json +0 -6
  61. package/web/index.html +2 -2
  62. package/web/_astro/BoardApp.CC7Mwswv.js +0 -324
  63. package/web/_astro/channel.DtEPXXcH.js +0 -1
  64. package/web/_astro/index.DiC1a6lh.css +0 -1
@@ -7,7 +7,7 @@
7
7
  "plugins": [
8
8
  {
9
9
  "name": "sp",
10
- "version": "0.4.18",
10
+ "version": "0.4.19",
11
11
  "source": "./plugins/sp"
12
12
  }
13
13
  ]
@@ -701,10 +701,15 @@ states:
701
701
  # PASS, and a clean review gate (open P1-P3 finding or an uncited claim blocks it).
702
702
  # Fail-closed: any missing/corrupt input leaves FAIL, so the transitions can route a
703
703
  # non-PASS review into remediation instead of dead-ending the run.
704
+ # Idempotent re-entry: the answer file is consumed as a stage input, so a second visit to
705
+ # this state has no answer to re-judge. An ABSENT answer is not new evidence against a
706
+ # PASS — the durable `<wbs>-verdict.json` and the residual gate still have to pass — so the
707
+ # run-scoped verdict status is consulted only when an answer is actually present (without
708
+ # this, re-entry overwrote a PASS with FAIL and closed the task's run as `failed`).
704
709
  - kind: shell
705
710
  options:
706
711
  command: >-
707
- mkdir -p .spur/run; S=FAIL; if [ "$(cat ".spur/run/$__runId-review-verdict.status" 2>/dev/null)" = PASS ] && [ "$(jq -r '.verdict // empty' ".spur/run/$wbs-verdict.json" 2>/dev/null)" = PASS ] && $spurBin task residual "$wbs" review-gate --no-logo >&2; then S=PASS; fi; printf '%s\n' "$S" > ".spur/run/$wbs-review.status"; exit 0
712
+ mkdir -p .spur/run; S=FAIL; if [ ! -f ".spur/run/$__runId-review-answer.txt" -o "$(cat ".spur/run/$__runId-review-verdict.status" 2>/dev/null)" = PASS ]; then [ "$(jq -r '.verdict // empty' ".spur/run/$wbs-verdict.json" 2>/dev/null)" = PASS ] && $spurBin task residual "$wbs" review-gate --no-logo >&2 && S=PASS; fi; printf '%s\n' "$S" > ".spur/run/$wbs-review.status"; exit 0
708
713
  # (d) record the review severity as the lane decision's outcome (1263 R3b): the
709
714
  # question `task-semantic-risk` answered is "is this risky?", and the Review table's
710
715
  # worst finding is the answer as observed. Resolved rows count (a fixed P2 was still
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/spur",
3
- "version": "0.4.18",
3
+ "version": "0.4.19",
4
4
  "description": "Spur CLI — local-first harness for mainstream coding agents: constraint checking, workflow orchestration, agent health, and history analytics. Bun-native; exposes the `spur` command.",
5
5
  "keywords": [
6
6
  "spur",
@@ -536,14 +536,18 @@ block/ask prints one JSON object that superskill renders per host:
536
536
  targets:
537
537
 
538
538
  - Codex, OpenCode, Antigravity and Hermes get only `scripts/`, staged as
539
- `.agents/scripts/sp/`, with `lib/` beside it as `.agents/scripts/lib/`.
539
+ `.agents/scripts/sp/` — together with `lib/`, which lands **inside** the plugin root
540
+ (`.agents/scripts/sp/lib/`). Install drops the `scripts/` level, so it retargets an entry's
541
+ `../../lib/…` to `../lib/…` for the staged tree; older superskill builds stage the same `lib/`
542
+ at the shared `.agents/scripts/lib/` instead and leave the import depth alone. Both resolve.
540
543
  - Native targets (Claude Code, omp, Grok) get the whole plugin.
541
544
 
542
- So `scripts/hooks/<id>.ts`, importing `../../lib/…`, is the only path that resolves the same way on
543
- every target. `hooks/` keeps the registry only. `script-contract-check`
544
- rejects a hook entry there: on native installs it would shadow the staged one, and staged targets
545
- would never receive it. Hook logic stays in the plugin (Revision 2026-10-15); the files are entries
546
- and lib cores, not `spur` commands.
545
+ So `scripts/hooks/<id>.ts`, importing `../../lib/…` relative to `plugins/sp/`, is the only path that
546
+ resolves the same way on every target. Keep that import depth as written — it is correct for the
547
+ plugin tree, and install fixes it up for the collapsed staged tree. `hooks/` keeps the registry
548
+ only; `script-contract-check` rejects a hook entry there, because on native installs it would shadow
549
+ the staged one and staged targets would never receive it. Hook logic stays in the plugin (Revision
550
+ 2026-10-15); the files are entries and lib cores, not `spur` commands.
547
551
 
548
552
  **Escape hatch:** `SPUR_WRITE_GUARD=off` short-circuits the write guard before any subprocess.
549
553
 
@@ -26,5 +26,5 @@ For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag
26
26
 
27
27
  ## Implementation
28
28
 
29
- Follow the inline procedure in [dev-operations.md](../skills/spur-dev/references/dev-operations.md#8-changelog) (changelog). This is a docs-only generator: run no quality gates (`spur self exec autofix|check|lint`, typecheck, tests, rules) — a generic "verify before finishing" rule does not apply here. The generator's own claim-verification pass is part of the procedure, not a gate, and carries one required check per claim bullet (dev-operations §8, step 7): a **source citation** — an anchor naming a URL or a repo `path:line` — held in the bullet's `<!-- src: … -->` comment and re-verified by step 8's self-check. No confidence level is recorded (ADR-138).
29
+ Follow the inline procedure in [dev-operations.md](../skills/spur-dev/references/dev-operations.md#8-changelog) (changelog). This is a docs-only generator: run no quality gates (`spur self exec autofix|check|lint`, typecheck, tests, rules) — a generic "verify before finishing" rule does not apply here. The generator's own claim-verification pass is part of the procedure, not a gate, and carries one required check per claim bullet (dev-operations §8, step 7): a **source citation** — an anchor naming a URL or a repo `path:line` — held in the bullet's `<!-- src: … -->` comment and re-verified by step 8's self-check. The comment also carries `· confidence: <level>`, derived from the anchors — `HIGH` all `path:line` beyond line 1 and resolving, `MEDIUM` all resolving but not all line-precise, `LOW` otherwise — and cross-checked by `repo-wide-tests/changelog-confidence.test.ts` (ADR-144): it is a function of the anchors, not a self-assessment, and a level the anchors do not support is rejected.
30
30
 
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "sp",
3
- "version": "0.4.18",
3
+ "version": "0.4.19",
4
4
  "description": "Spur — a local-first harness engineering toolkit that wraps mainstream coding agents with constraint checking, workflow orchestration, and history analytics."
5
5
  }
@@ -46,7 +46,7 @@ Pick the noun, read its reference. Each Tier A and Tier B reference owns that no
46
46
  | **Tier A** | **workflow** | Dual-mode workflow runtime: author state-machine / transition-flow workflows, validate, run, read traces | [references/workflows.md](references/workflows.md) |
47
47
  | **Tier A** | **builder** | Release/PR plumbing: `bump-ver` (including read-only `--check`), `drop-tags`, `publish`, the four flat PR verbs (`pr-status`, `pr-check`, `pr-open`, `pr-review`), and the read-only `show-branches` topology report | [references/builder.md](references/builder.md) |
48
48
  | **Tier B** | **agent** | Coding-agent execution surface: run prompts via detected/named agents, list agent specs (incl. `agent.fleet` members), start/stop supervised processes, fleet `report`/`join`/`leave`/`wait`/`trace`, readiness check | [references/agent.md](references/agent.md) |
49
- | **Tier B** | **message** | Durable inter-agent messaging: send, inbox, reply, watch | [references/message.md](references/message.md) |
49
+ | **Tier B** | **message** | Durable inter-agent messaging: send, inbox, show, read, reply, watch | [references/message.md](references/message.md) |
50
50
  | **Tier B** | **self** | Self-management verbs: scaffold (`init`), project SDLC actions (`exec`), database maintenance (`maintain`), schema migrations (`migrate`), local web server (`serve`), status overview (`status`); `self init` runs post-scaffold validation probes & layout classification | [references/self.md](references/self.md) |
51
51
  | **Tier B** | **history** | Import agent histories, aggregate forensic artifacts, render reports, and run the checkpoint-resumed daily pipeline | [references/history.md](references/history.md) |
52
52
  | **Tier B** | **projects** | Manage the local multi-project registry and start/stop project servers | [references/projects.md](references/projects.md) |
@@ -162,7 +162,7 @@ and spreading it; full contract in `docs/04_DESIGN.md` §1.0.1.
162
162
  `list --specs`, `start`/`stop`). Cross-references the
163
163
  [dispatch-surface rule](../parallel-execution/references/dispatch-surface.md).
164
164
  - **[references/message.md](references/message.md)** - durable inter-agent messaging (`send`,
165
- `inbox`, `reply`, `watch`).
165
+ `inbox`, `show`, `read`, `reply`, `watch`).
166
166
  - **[references/decision.md](references/decision.md)** - decision catalog (`list`, `show`, `run`,
167
167
  `status`); with no reachable backend each decision serves its declared fallback. Authoring and
168
168
  tuning: [references/decision/lifecycle.md](references/decision/lifecycle.md).
@@ -19,8 +19,10 @@ use it well*.
19
19
 
20
20
  | Verb | Purpose | Key flags |
21
21
  | ---- | ------- | --------- |
22
- | `send <body>` | Enqueue a message for an agent | `--to <id>` `--role <name>` `--from <id>` `--request-key <key>` `--wait` `--until <state>` `--timeout <ms>` `--json` |
23
- | `inbox` | List messages addressed to an agent | `--agent <id>` `--unresolved` `--json` |
22
+ | `send <body>` | Send to a member inbox; `--to` defaults to the orchestrator | `--to <id>` `--role <name>` `--from <id>` `--request-key <key>` `--wait` `--until <state>` `--timeout <ms>` `--json` |
23
+ | `inbox` | List messages addressed to an agent (writes nothing) | `--agent <id>` `--unresolved` `--json` |
24
+ | `show <msg-id>` | Print one thread, root plus replies, oldest first, both directions | `--json` |
25
+ | `read <msg-id>` | Mark a message read (`--reader`, default `operator`); idempotent | `--reader <id>` `--json` |
24
26
  | `reply <msg-id> <body>` | Thread a reply to a message | `--json` |
25
27
  | `watch` | Follow an agent inbox - surface new messages as they arrive | `--agent <id>` `--interval <ms>` `--json` |
26
28
 
@@ -49,14 +51,14 @@ wait; enqueue is **not** rolled back if the wait later fails.
49
51
 
50
52
  | Flag | Purpose |
51
53
  | ------ | --------- |
52
- | `--to <id>` | Recipient agent id. Mutually exclusive with `--role`; exactly one of the two is required. |
54
+ | `--to <id>` | Recipient address. Mutually exclusive with `--role`. Omitted = the project's orchestrator member (`FleetService.resolveOrchestrator`); with none bound the verb exits 2 and writes no row. |
53
55
  | `--role <name>` | Address by Layer-1 role or executor name. Must resolve to exactly one materialized instance; zero (`count=0`, candidates `none`) or multi (`count=N` + candidates) matches are hard errors (exit 1); unknown name exits 2 naming the accepted vocabulary (`AGENT_ROLE_NAMES` ∪ executor names). Resolution yields the same spec-id path as `--to`; `--wait` snapshots that occupant pin. (0685 R6 / ADR-075 amendment) |
54
56
  | `--from <id>` | Sender id (default: `operator`). |
55
57
  | `--request-key <key>` | Caller-minted idempotency key. The same key with the same body + recipient replays the original receipt (`replayed: true`, no second row/delivery); the same key with a different payload fails with a request-key-conflict error (0832). |
56
58
  | `replayed` receipt field | Present on keyed sends: `true` when this submission was a replay of an earlier accepted send. |
57
59
  | `requestKey` receipt field | Present on keyed sends, including replays; echoes the accepted key. Blank keys are rejected. |
58
60
  | `--wait` | Block until the recipient reaches `--until` (snapshots occupant before send). |
59
- | `--until <state>` | Wait target: `injected` \| `invoke-exit` (repeatable OR). Default `invoke-exit`. |
61
+ | `--until <state>` | Wait target: `injected` \| `invoke-exit` (repeatable OR). Default `invoke-exit`. `injected` is satisfied when THIS message leaves `queued` — claimed, delivered or failed — not when the recipient's inbox is empty. |
60
62
  | `--timeout <ms>` | Caller deadline in milliseconds. |
61
63
  | `--json` | Output machine-readable JSON (`{ msgId, toId, status, wait: { satisfied } }`). |
62
64
 
@@ -86,7 +88,7 @@ carry their persisted origin and run status; exhausted attempts keep the same re
86
88
  | `reason` | Meaning |
87
89
  | -------- | --------- |
88
90
  | `delivery-failed` | The drain marked the delivery failed (`injectError` carries why), or its run's receipt outcome is `errored`. |
89
- | `attempts-exhausted` | The message burned its bounded redelivery budget (`MAX_INJECT_ATTEMPTS`, 0831); the reconciler marks it `failed` — the reconciler's only write. |
91
+ | `attempts-exhausted` | The message burned its bounded redelivery budget (`MAX_DELIVERY_ATTEMPTS`, 0831; renamed by G11); the claim sweeper marks it `failed`. |
90
92
  | `outcome-unknown` | The drain consumed it and no completion receipt ever arrived: the agent may have edited files. **Never requeued, never auto-released** — a human decides. |
91
93
  | `run-exit-only` | Its run exited (receipt outcome `run-exit-only`) with no workflow verification result. |
92
94
 
@@ -590,10 +590,11 @@ host instructions does not add one. An opt-in gate is explicit: `gitmsg --check
590
590
  - `keepachangelog` (default): `## [<version>] - <date>` header, then category headings per the keepachangelog convention — `### Added` / `### Fixed` / `### Changed` / `### Removed` / `### Other` — mapped from conventional-commit types.
591
591
  - `simple`: flat bulleted list grouped by type heading (`### feat`, `### fix`, …).
592
592
  6. Emit: with `<file>`, insert the section into that file above its newest `## [` release heading (or after its title when none exists) and touch nothing else in the file; without `<file>`, print it to stdout.
593
- 7. **Verify claims before emitting — one required check on every claim bullet.** Classify first: an *API/library claim* is a bullet asserting what code, an API, a library, a dependency or a version does — skip merge/bookkeeping bullets that assert no such fact (a status move, a filing, a doc relocation). Then run the check and append one HTML comment line directly under the bullet carrying its anchor: `<!-- src: <citation> -->`. No confidence level is recorded (ADR-138): the anchor is the evidence.
593
+ 7. **Verify claims before emitting — one required check on every claim bullet.** Classify first: an *API/library claim* is a bullet asserting what code, an API, a library, a dependency or a version does — skip merge/bookkeeping bullets that assert no such fact (a status move, a filing, a doc relocation). Then run the check and append one HTML comment line directly under the bullet carrying its anchor and the level that anchor supports: `<!-- src: <citation> · confidence: <level> -->`.
594
594
  - **Source citation — the anchor.** Internal claims (what a commit or the code does): confirm against the commit diff (`git show --stat <hash>`, reading hunks when the summary asserts behavior) or the artifact's presence in the tree; cite `path[:line]` — bare path for whole-file claims, `path:line` for a specific behavior or symbol. External claims (framework/library/version behavior): confirm against official docs for the installed version; cite the URL. An anchor you cannot produce fails the check — reword the bullet or drop the claim; a citation is not a citation until it names something a reader can open.
595
+ - **Confidence — derived, not judged.** The level is a function of the anchors in the comment (ADR-144), and the repo-wide check recomputes it: `HIGH` when every anchor is a `path:line` beyond line 1 and each resolves (file exists, line within it), `MEDIUM` when every anchor resolves but the set is not all line-precise (an external URL, a bare path, or a `:1` anchor), `LOW` when an anchor does not resolve or the comment lists none. To record `MEDIUM` over in-tree evidence, cite a URL or a whole file rather than asserting it — a `HIGH` the anchors do not support is exactly what `repo-wide-tests/changelog-confidence.test.ts` rejects.
595
596
  - **Defect precision decides what must be fixed.** The check is precise once a bullet is classified as a claim: an annotation missing its anchor is unambiguously wrong and blocks emitting. A summary broader than its evidence is reworded to what the anchor shows. Which bullets *are* claims is the heuristic half — resolve it by this step's rule, and for a genuinely borderline bullet say so in the bullet's body rather than annotating or skipping it silently.
596
- 8. Self-check before emitting: every short hash in `<since>..<until>` appears exactly once, and every claim bullet carries exactly one `<!-- src: … -->` comment holding exactly one anchor — no unannotated claim bullets, no duplicate annotations. Fix any miss or duplicate; do not report a verdict.
597
+ 8. Self-check before emitting: every short hash in `<since>..<until>` appears exactly once, and every claim bullet carries exactly one `<!-- src: … -->` comment holding exactly one anchor with its `· confidence: <level>` token matching what those anchors support (step 7's derivation: `HIGH` all `path:line` beyond line 1 and resolving, `MEDIUM` all resolving but not all line-precise, `LOW` otherwise) — no unannotated claim bullets, no duplicate annotations. Fix any miss or duplicate; do not report a verdict.
597
598
  9. Reply in at most three lines: the range, bullet counts per category plus the verified-claim count, and the output target.
598
599
  - **Invariants:** Bullets are derived from `git log` only; the claim-verification pass (step 7) adds evidence from commit diffs, the working tree, and official docs. No quality gates — no `spur self exec autofix|check|lint` and no typecheck, test or rule run, and no edits beyond the one inserted section (its verification comments included). The file is left uncommitted.
599
600