mandrel 2.30.0 → 2.32.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 (253) hide show
  1. package/.agents/README.md +13 -17
  2. package/.agents/agents/acceptance-critic.md +1 -2
  3. package/.agents/docs/SDLC.md +4 -4
  4. package/.agents/docs/agentrc-reference.json +61 -57
  5. package/.agents/docs/configuration.md +274 -227
  6. package/.agents/docs/execution-reference.md +13 -14
  7. package/.agents/docs/quality-gates.md +195 -23
  8. package/.agents/instructions.md +2 -5
  9. package/.agents/rules/ci-remediation.md +44 -1
  10. package/.agents/rules/git-conventions-reference.md +27 -27
  11. package/.agents/rules/git-conventions.md +4 -2
  12. package/.agents/rules/known-tooling-behavior.md +66 -30
  13. package/.agents/rules/testing-standards.md +35 -71
  14. package/.agents/runtime-deps.json +0 -1
  15. package/.agents/schemas/agentrc.schema.json +1939 -1400
  16. package/.agents/schemas/lifecycle/README.md +21 -14
  17. package/.agents/schemas/lifecycle/ledger-record.schema.json +76 -22
  18. package/.agents/schemas/story-deliver-terminal.schema.json +2 -2
  19. package/.agents/scripts/README.md +7 -29
  20. package/.agents/scripts/apply-quality-bootstrap.js +27 -34
  21. package/.agents/scripts/bootstrap.js +28 -26
  22. package/.agents/scripts/check-baseline-drift.js +73 -13
  23. package/.agents/scripts/check-baseline-scope.js +362 -0
  24. package/.agents/scripts/check-dead-exports.js +9 -1
  25. package/.agents/scripts/check-gherkin-corpus.js +508 -0
  26. package/.agents/scripts/check-knip-entries.js +136 -0
  27. package/.agents/scripts/check-lifecycle-lint.js +36 -112
  28. package/.agents/scripts/check-schema-references.js +1 -1
  29. package/.agents/scripts/diagnose-friction.js +7 -4
  30. package/.agents/scripts/generate-config-docs.js +263 -171
  31. package/.agents/scripts/install-matrix-assert.js +0 -1
  32. package/.agents/scripts/lib/ITicketingProvider.js +0 -58
  33. package/.agents/scripts/lib/audit-baselines/staleness.js +6 -6
  34. package/.agents/scripts/lib/audit-baselines/trend.js +7 -8
  35. package/.agents/scripts/lib/audit-baselines/weights.js +4 -5
  36. package/.agents/scripts/lib/audit-suite/checklist-threading.js +1 -1
  37. package/.agents/scripts/lib/audit-to-stories/build-story-body.js +0 -1
  38. package/.agents/scripts/lib/baselines/envelope.js +41 -60
  39. package/.agents/scripts/lib/baselines/git-base.js +30 -37
  40. package/.agents/scripts/lib/baselines/kinds/_crap-new-method-gate.js +103 -0
  41. package/.agents/scripts/lib/baselines/kinds/_crap-read.js +150 -0
  42. package/.agents/scripts/lib/baselines/kinds/crap.js +25 -65
  43. package/.agents/scripts/lib/baselines/orphan-pruner.js +233 -0
  44. package/.agents/scripts/lib/baselines/refresh-service.js +6 -8
  45. package/.agents/scripts/lib/baselines/scope-assert.js +223 -0
  46. package/.agents/scripts/lib/baselines/scope-inventory.js +314 -0
  47. package/.agents/scripts/lib/bdd-step-index.js +326 -0
  48. package/.agents/scripts/lib/bootstrap/install-ledger.js +5 -3
  49. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +4 -6
  50. package/.agents/scripts/lib/bootstrap/manifest.js +17 -40
  51. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +12 -59
  52. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +62 -2
  53. package/.agents/scripts/lib/checks/loop-health.js +9 -37
  54. package/.agents/scripts/lib/child-exec.js +193 -0
  55. package/.agents/scripts/lib/cli/standard-args.js +1 -1
  56. package/.agents/scripts/lib/cli-args.js +64 -0
  57. package/.agents/scripts/lib/close-validation/gates.js +2 -2
  58. package/.agents/scripts/lib/close-validation/runner.js +3 -3
  59. package/.agents/scripts/lib/config/acceptance-eval.js +5 -52
  60. package/.agents/scripts/lib/config/commands.js +3 -5
  61. package/.agents/scripts/lib/config/explain.js +5 -7
  62. package/.agents/scripts/lib/config/gates/bundle-size.schema.js +32 -6
  63. package/.agents/scripts/lib/config/gates/coverage.schema.js +25 -5
  64. package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +12 -2
  65. package/.agents/scripts/lib/config/gates/crap.schema.js +68 -23
  66. package/.agents/scripts/lib/config/gates/duplication.schema.js +29 -17
  67. package/.agents/scripts/lib/config/gates/index.js +5 -2
  68. package/.agents/scripts/lib/config/gates/lighthouse.schema.js +34 -6
  69. package/.agents/scripts/lib/config/gates/lint.schema.js +11 -2
  70. package/.agents/scripts/lib/config/gates/maintainability.schema.js +37 -15
  71. package/.agents/scripts/lib/config/gates/mutation.schema.js +15 -3
  72. package/.agents/scripts/lib/config/gates/shared.js +58 -9
  73. package/.agents/scripts/lib/config/github.js +0 -1
  74. package/.agents/scripts/lib/config/limits.js +3 -48
  75. package/.agents/scripts/lib/config/qa.js +105 -0
  76. package/.agents/scripts/lib/config/temp-paths.js +6 -5
  77. package/.agents/scripts/lib/config-settings-schema-delivery.js +237 -56
  78. package/.agents/scripts/lib/config-settings-schema-quality.js +209 -29
  79. package/.agents/scripts/lib/config-settings-schema.js +386 -39
  80. package/.agents/scripts/lib/crap-baseline-join.js +126 -9
  81. package/.agents/scripts/lib/crap-utils.js +84 -520
  82. package/.agents/scripts/lib/dead-exports-knip.js +79 -10
  83. package/.agents/scripts/lib/degraded-mode.js +2 -2
  84. package/.agents/scripts/lib/doc-tiers.js +3 -3
  85. package/.agents/scripts/lib/feedback-loop/graduator-core.js +46 -104
  86. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +10 -8
  87. package/.agents/scripts/lib/fs-walk.js +52 -0
  88. package/.agents/scripts/lib/git-branch-lifecycle.js +2 -2
  89. package/.agents/scripts/lib/git-utils.js +16 -36
  90. package/.agents/scripts/lib/knip-entry-sync.js +469 -0
  91. package/.agents/scripts/lib/observability/metrics-ledger.js +1 -1
  92. package/.agents/scripts/lib/observability/runtime-friction.js +10 -0
  93. package/.agents/scripts/lib/observability/signal-validator.js +5 -85
  94. package/.agents/scripts/lib/observability/signals-writer.js +19 -62
  95. package/.agents/scripts/lib/observability/source-classifier.js +5 -7
  96. package/.agents/scripts/lib/observability/terse-result.js +3 -3
  97. package/.agents/scripts/lib/orchestration/behind-recovery.js +114 -0
  98. package/.agents/scripts/lib/orchestration/ceremony-routing.js +7 -8
  99. package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +34 -33
  100. package/.agents/scripts/lib/orchestration/code-review.js +2 -2
  101. package/.agents/scripts/lib/orchestration/complexity-gate.js +43 -161
  102. package/.agents/scripts/lib/orchestration/diff-magnitude.js +4 -4
  103. package/.agents/scripts/lib/orchestration/label-transitions.js +3 -2
  104. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +12 -38
  105. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +5 -6
  106. package/.agents/scripts/lib/orchestration/plan-metrics.js +2 -3
  107. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +6 -0
  108. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +0 -1
  109. package/.agents/scripts/lib/orchestration/{lifecycle/listeners/watcher.js → pr-watch.js} +58 -208
  110. package/.agents/scripts/lib/orchestration/resolve-stories.js +5 -15
  111. package/.agents/scripts/lib/orchestration/review-providers/codex.js +1 -1
  112. package/.agents/scripts/lib/orchestration/review-providers/mi-exemptions.js +130 -0
  113. package/.agents/scripts/lib/orchestration/review-providers/native.js +30 -16
  114. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +1 -1
  115. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +37 -26
  116. package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +376 -0
  117. package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +161 -151
  118. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +15 -3
  119. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +10 -15
  120. package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +17 -2
  121. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +5 -0
  122. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +157 -0
  123. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +0 -14
  124. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +65 -25
  125. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +20 -31
  126. package/.agents/scripts/lib/orchestration/spec-spill.js +17 -3
  127. package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +7 -6
  128. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +2 -1
  129. package/.agents/scripts/lib/orchestration/task-body-validator.js +4 -1
  130. package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -127
  131. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -1
  132. package/.agents/scripts/lib/orchestration/ticketing/reads.js +5 -5
  133. package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -4
  134. package/.agents/scripts/lib/orchestration/verify-tier-repair.js +107 -0
  135. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -87
  136. package/.agents/scripts/lib/signals/detectors/common.js +1 -1
  137. package/.agents/scripts/lib/signals/index.js +8 -6
  138. package/.agents/scripts/lib/signals/schema.js +20 -25
  139. package/.agents/scripts/lib/signals/write.js +8 -8
  140. package/.agents/scripts/lib/story-body/story-body.js +12 -59
  141. package/.agents/scripts/lib/temp-retention.js +1 -1
  142. package/.agents/scripts/lib/templates/decomposer-prompts.js +16 -14
  143. package/.agents/scripts/lib/ticket-body-sections.js +4 -5
  144. package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +13 -45
  145. package/.agents/scripts/lib/worktree/lifecycle/reap.js +4 -5
  146. package/.agents/scripts/lib/worktree-manager.js +2 -3
  147. package/.agents/scripts/lint-label-vocabulary.js +2 -24
  148. package/.agents/scripts/pr-watch-with-update.js +7 -5
  149. package/.agents/scripts/providers/github/cache.js +2 -2
  150. package/.agents/scripts/providers/github/comments.js +6 -28
  151. package/.agents/scripts/providers/github/compose.js +0 -15
  152. package/.agents/scripts/providers/github/errors.js +10 -27
  153. package/.agents/scripts/providers/github/request-helpers.js +1 -2
  154. package/.agents/scripts/providers/github/sub-issues.js +10 -218
  155. package/.agents/scripts/providers/github.js +4 -7
  156. package/.agents/scripts/prune-baseline-orphans.js +181 -0
  157. package/.agents/scripts/resolve-stories.js +0 -2
  158. package/.agents/scripts/run-lint.js +61 -61
  159. package/.agents/scripts/run-test-profile.js +6 -6
  160. package/.agents/scripts/run-verify.js +48 -30
  161. package/.agents/scripts/single-story-close.js +20 -0
  162. package/.agents/scripts/single-story-init.js +12 -35
  163. package/.agents/scripts/update-dead-exports-baseline.js +321 -0
  164. package/.agents/skills/core/gates-and-baselines/SKILL.md +2 -2
  165. package/.agents/skills/skills.index.json +2 -12
  166. package/.agents/skills/stack/qa/playwright/SKILL.md +48 -0
  167. package/.agents/workflows/audit-documentation.md +5 -6
  168. package/.agents/workflows/audit-to-stories.md +2 -2
  169. package/.agents/workflows/helpers/audit-lens-core.md +11 -12
  170. package/.agents/workflows/helpers/code-quality-guardrails.md +15 -14
  171. package/.agents/workflows/helpers/code-review.md +3 -8
  172. package/.agents/workflows/helpers/deliver-reference.md +2 -1
  173. package/.agents/workflows/helpers/deliver-story-reference.md +27 -16
  174. package/.agents/workflows/helpers/worktree-lifecycle.md +1 -2
  175. package/.agents/workflows/mandrel-update.md +10 -10
  176. package/.agents/workflows/qa-assist.md +15 -20
  177. package/.agents/workflows/qa-explore.md +9 -8
  178. package/README.md +1 -1
  179. package/docs/CHANGELOG.md +49 -0
  180. package/lib/migrations/index.js +2 -0
  181. package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +127 -0
  182. package/package.json +12 -3
  183. package/.agents/schemas/lifecycle/checkpoint.written.schema.json +0 -13
  184. package/.agents/schemas/lifecycle/close-validate.end.schema.json +0 -18
  185. package/.agents/schemas/lifecycle/close-validate.start.schema.json +0 -13
  186. package/.agents/schemas/lifecycle/code-review.end.schema.json +0 -30
  187. package/.agents/schemas/lifecycle/code-review.start.schema.json +0 -12
  188. package/.agents/schemas/lifecycle/intervention.recorded.schema.json +0 -15
  189. package/.agents/schemas/lifecycle/loop.tick.schema.json +0 -20
  190. package/.agents/schemas/lifecycle/notification.emitted.schema.json +0 -18
  191. package/.agents/schemas/lifecycle/pr.created.schema.json +0 -14
  192. package/.agents/schemas/lifecycle/retro.end.schema.json +0 -16
  193. package/.agents/schemas/lifecycle/retro.start.schema.json +0 -12
  194. package/.agents/schemas/lifecycle/story.blocked.schema.json +0 -13
  195. package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +0 -17
  196. package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +0 -15
  197. package/.agents/schemas/lifecycle/story.merged.schema.json +0 -13
  198. package/.agents/scripts/check-gherkin-placeholders.js +0 -663
  199. package/.agents/scripts/check-lifecycle-doc-drift.js +0 -411
  200. package/.agents/scripts/lib/audit-suite/cli.js +0 -64
  201. package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +0 -202
  202. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +0 -212
  203. package/.agents/scripts/lib/checks/baseline-drift-main-checkout.js +0 -104
  204. package/.agents/scripts/lib/checks/push-hook-parity.js +0 -106
  205. package/.agents/scripts/lib/checks/windows-coverage-noise-floor.js +0 -92
  206. package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +0 -81
  207. package/.agents/scripts/lib/checks/worktree-residue-biome.js +0 -55
  208. package/.agents/scripts/lib/crap-baseline-index.js +0 -46
  209. package/.agents/scripts/lib/crap-utils-incremental.js +0 -113
  210. package/.agents/scripts/lib/dynamic-workflow/capability.js +0 -396
  211. package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +0 -335
  212. package/.agents/scripts/lib/mutation/baseline-snapshot.js +0 -239
  213. package/.agents/scripts/lib/mutation/config-detector.js +0 -119
  214. package/.agents/scripts/lib/mutation/stryker-runner.js +0 -306
  215. package/.agents/scripts/lib/mutation/survivor-report.js +0 -160
  216. package/.agents/scripts/lib/observability/active-story-env.js +0 -170
  217. package/.agents/scripts/lib/observability/tool-trace-hook.js +0 -456
  218. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +0 -111
  219. package/.agents/scripts/lib/orchestration/context-envelope.js +0 -277
  220. package/.agents/scripts/lib/orchestration/detectors-phase.js +0 -194
  221. package/.agents/scripts/lib/orchestration/lifecycle/bus.js +0 -309
  222. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +0 -181
  223. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +0 -229
  224. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +0 -54
  225. package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +0 -344
  226. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +0 -114
  227. package/.agents/scripts/lib/orchestration/pr-base-guard.js +0 -37
  228. package/.agents/scripts/lib/orchestration/resolves-token.js +0 -127
  229. package/.agents/scripts/lib/orchestration/spec-section-validator.js +0 -130
  230. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +0 -55
  231. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +0 -211
  232. package/.agents/scripts/lib/planning-corpus.js +0 -37
  233. package/.agents/scripts/lib/qa/coverage-report.js +0 -181
  234. package/.agents/scripts/lib/qa/propose-missing-test.js +0 -95
  235. package/.agents/scripts/lib/qa/qa-context-hydrator.js +0 -217
  236. package/.agents/scripts/lib/signals/detectors/index.js +0 -14
  237. package/.agents/scripts/lib/signals/detectors/retry.js +0 -253
  238. package/.agents/scripts/lib/signals/detectors/rework.js +0 -167
  239. package/.agents/scripts/lib/signals/read.js +0 -268
  240. package/.agents/scripts/lib/signals/span-tree.js +0 -291
  241. package/.agents/scripts/lib/story-lifecycle.js +0 -194
  242. package/.agents/scripts/lib/story-plan.js +0 -379
  243. package/.agents/scripts/lib/util/phase-timer-state.js +0 -72
  244. package/.agents/scripts/lib/util/phase-timer.js +0 -163
  245. package/.agents/scripts/lib/workers/combined-mi-crap-worker.js +0 -169
  246. package/.agents/scripts/lint-baseline.js +0 -507
  247. package/.agents/scripts/providers/github/prs.js +0 -103
  248. package/.agents/scripts/signals-view.js +0 -309
  249. package/.agents/scripts/story-plan.js +0 -370
  250. package/.agents/scripts/sync-branch-from-base.js +0 -149
  251. package/.agents/scripts/validate-docs-freshness.js +0 -314
  252. package/.agents/skills/core/diagnose-friction/SKILL.md +0 -78
  253. package/.agents/workflows/helpers/signals.md +0 -112
@@ -1,57 +1,38 @@
1
1
  /**
2
2
  * ticket-lease.js — assignee-as-lease primitive (Story #3480, Epic #3457).
3
3
  *
4
- * The workflow-guards Feature (#3478) needs a way for one operator to take
5
- * an exclusive, time-bounded claim on a ticket so two concurrent runs do not
6
- * both drive the same Story. Rather than invent a new state column, the lease
7
- * rides the ticket's existing **assignees** surface: the single assignee *is*
8
- * the lease owner. Liveness is decided by the owner's last-heartbeat epoch-ms
9
- * compared against a configured TTL.
4
+ * The workflow-guards Feature (#3478) needs a way for one operator to take an
5
+ * exclusive claim on a ticket so two concurrent runs do not both drive the
6
+ * same Story. Rather than invent a new state column, the lease rides the
7
+ * ticket's existing **assignees** surface: the single assignee *is* the lease
8
+ * owner.
10
9
  *
11
- * **There is no live heartbeat source.** The `story.heartbeat` emitter this
12
- * module was designed against was structurally inert (it demanded an
13
- * `epicId >= 1` that v2, which has no Epics, never supplies) and has been
14
- * deleted. Every caller now reaches `acquireLease` through
15
- * `lease-guard-shared.acquireLeaseFailClosed` with `anchorHeartbeatToNow`,
16
- * which pins `heartbeatAt` to `now`: the lease **fails closed**, so any
17
- * foreign claim reads as live and refuses. A stranded claim is cleared with
18
- * `--steal`, never by TTL expiry. The TTL and the stale-reclaim branch below
19
- * are therefore reachable only via an explicit caller-supplied `heartbeatAt`
20
- * the seam is kept, the automatic expiry is not real.
10
+ * **A foreign claim is a refusal, full stop (Story #5006).** The lease shipped
11
+ * with a TTL: a claim whose owner's last heartbeat was older than
12
+ * `delivery.lease.ttlMs` counted as stale and was silently reclaimed. The
13
+ * `story.heartbeat` emitter that would have fed it was structurally inert (it
14
+ * demanded an `epicId >= 1` that v2, which has no Epics, never supplies) and
15
+ * was deleted, after which every guard pinned `heartbeatAt` to `now` so the
16
+ * liveness test always answered "live" the TTL, the heartbeat parameter and
17
+ * the reclaim branch were an elaborate way of writing `true`. They are gone.
18
+ * A stranded claim is cleared with `--steal`, which is now the only way past
19
+ * a foreign owner.
21
20
  *
22
- * The three exported operations are deliberately thin and provider-agnostic:
21
+ * The two exported operations are deliberately thin and provider-agnostic:
23
22
  *
24
23
  * - `acquireLease` — claim an unassigned ticket, re-affirm a self-held
25
- * claim, reclaim a ticket whose foreign claim has gone
26
- * stale (heartbeat older than TTL), or — with
27
- * `steal: true` — forcibly transfer a *live* foreign
28
- * claim.
24
+ * claim, or with `steal: true` forcibly transfer a
25
+ * foreign claim.
29
26
  * - `releaseLease` — clear the assignment, but only when the operator
30
27
  * still holds it (a no-op once the ticket was
31
28
  * reassigned elsewhere, so a late release never steals
32
29
  * a claim back from whoever took over).
33
- * - `describeLease` — a read-only snapshot of the current claim and its
34
- * liveness, used by callers (and tests) to reason about
35
- * a ticket without mutating it.
36
30
  *
37
31
  * Provider contract (a subset of `ITicketingProvider`):
38
32
  * - `getTicket(id)` → `{ assignees: string[], ... }`
39
33
  * - `updateTicket(id, { assignees })` writes the assignee list.
40
- *
41
- * Liveness seam: callers supply the owner's last-heartbeat epoch-ms via the
42
- * `heartbeatAt` option (a number, or `null`/`undefined` when no heartbeat has
43
- * ever been recorded for the current owner). Threading the timestamp in keeps
44
- * this module pure and trivially unit-testable — it does not read any ledger
45
- * itself. A claim with no heartbeat is treated as stale (reclaimable) by
46
- * `isClaimLive`; note the live guards never take that branch, per the
47
- * fail-closed anchoring described above.
48
- *
49
- * `now` is injectable (epoch ms) for deterministic tests; it defaults to
50
- * `Date.now()`.
51
34
  */
52
35
 
53
- import { resolveLeaseTtlMs } from '../config/limits.js';
54
-
55
36
  /**
56
37
  * The shipped, non-personal operator-identity placeholder (and its bare,
57
38
  * post-normalise form). The committed `.agentrc.json` and the distributed
@@ -94,35 +75,6 @@ export function normalizeOperatorHandle(raw) {
94
75
  return trimmed;
95
76
  }
96
77
 
97
- /**
98
- * Decide whether a foreign claim is still "live" given the owner's last
99
- * heartbeat and the configured TTL. A claim is live when a heartbeat exists
100
- * and is no older than `ttlMs`. A missing heartbeat (`null`/`undefined`) or a
101
- * heartbeat older than the TTL is stale and therefore reclaimable.
102
- *
103
- * The `heartbeatAt` seam is retained, but there is no longer any in-repo
104
- * heartbeat *source*: the `story.heartbeat` emitter was structurally inert
105
- * (it required an `epicId >= 1` that v2 never sets) and was deleted along
106
- * with the ledger reader that scanned for it. Every live caller reaches this
107
- * through `lease-guard-shared.acquireLeaseFailClosed` with
108
- * `anchorHeartbeatToNow`, which pins `heartbeatAt` to `now` so ANY foreign
109
- * claim reads live and the guard fails closed — a stranded claim is cleared
110
- * with `--steal`, not by TTL expiry. The parameter stays because that
111
- * anchoring is expressed through it.
112
- *
113
- * @param {object} args
114
- * @param {number|null|undefined} args.heartbeatAt Owner's last heartbeat (epoch ms).
115
- * @param {number} args.ttlMs Lease TTL in milliseconds.
116
- * @param {number} args.now Current time (epoch ms).
117
- * @returns {boolean}
118
- */
119
- export function isClaimLive({ heartbeatAt, ttlMs, now }) {
120
- if (typeof heartbeatAt !== 'number' || !Number.isFinite(heartbeatAt)) {
121
- return false;
122
- }
123
- return now - heartbeatAt <= ttlMs;
124
- }
125
-
126
78
  /**
127
79
  * Normalise the assignee list into a single current owner. The lease model is
128
80
  * single-holder: the first assignee is authoritative. Returns `null` for an
@@ -141,10 +93,10 @@ export function currentOwner(assignees) {
141
93
  *
142
94
  * @param {string} op
143
95
  * @param {object} opts
144
- * @returns {{ provider: object, ticketId: number, operator: string, ttlMs: number, now: number }}
96
+ * @returns {{ provider: object, ticketId: number, operator: string }}
145
97
  */
146
98
  function normaliseOpts(op, opts) {
147
- const { provider, ticketId, operator, config, now } = opts ?? {};
99
+ const { provider, ticketId, operator } = opts ?? {};
148
100
 
149
101
  if (!provider || typeof provider.getTicket !== 'function') {
150
102
  throw new Error(`${op}: provider with getTicket/updateTicket is required`);
@@ -156,45 +108,7 @@ function normaliseOpts(op, opts) {
156
108
  throw new Error(`${op}: operator must be a non-empty string`);
157
109
  }
158
110
 
159
- const ttlMs = resolveLeaseTtlMs(config, opts.ttlMs);
160
- const resolvedNow =
161
- typeof now === 'number' && Number.isFinite(now) ? now : Date.now();
162
-
163
- return { provider, ticketId, operator, ttlMs, now: resolvedNow };
164
- }
165
-
166
- /**
167
- * Read-only snapshot of the current lease on a ticket. Never mutates.
168
- *
169
- * @param {object} opts
170
- * @param {object} opts.provider Ticketing provider (getTicket).
171
- * @param {number} opts.ticketId Ticket to inspect.
172
- * @param {string} opts.operator The operator asking.
173
- * @param {number|null} [opts.heartbeatAt] Owner's last heartbeat (epoch ms).
174
- * @param {object} [opts.config] Resolved config (for TTL default).
175
- * @param {number} [opts.ttlMs] Explicit TTL override (epoch ms).
176
- * @param {number} [opts.now] Injectable clock (epoch ms).
177
- * @returns {Promise<{
178
- * ticketId: number,
179
- * owner: string|null,
180
- * heldByOperator: boolean,
181
- * live: boolean,
182
- * ttlMs: number,
183
- * }>}
184
- */
185
- export async function describeLease(opts) {
186
- const { provider, ticketId, operator, ttlMs, now } = normaliseOpts(
187
- 'describeLease',
188
- opts,
189
- );
190
- const ticket = await provider.getTicket(ticketId);
191
- const owner = currentOwner(ticket?.assignees);
192
- const heldByOperator = owner === operator;
193
- const live =
194
- owner === null
195
- ? false
196
- : isClaimLive({ heartbeatAt: opts.heartbeatAt, ttlMs, now });
197
- return { ticketId, owner, heldByOperator, live, ttlMs };
111
+ return { provider, ticketId, operator };
198
112
  }
199
113
 
200
114
  /**
@@ -205,10 +119,8 @@ export async function describeLease(opts) {
205
119
  * `reason: 'unclaimed'`.
206
120
  * - Operator already holds it → no write, `acquired: true`,
207
121
  * `reason: 'already-held'`.
208
- * - Live foreign claim, no steal → no write, `acquired: false`,
122
+ * - Foreign claim, no steal → no write, `acquired: false`,
209
123
  * `owner: <foreign>`, `reason: 'held'`.
210
- * - Stale foreign claim → reassign operator, `acquired: true`,
211
- * `reason: 'reclaimed'`.
212
124
  * - Foreign claim + `steal:true` → reassign operator, `acquired: true`,
213
125
  * `reason: 'stolen'`.
214
126
  * - Lost a write race → a foreign login co-assigned between our
@@ -226,23 +138,16 @@ export async function describeLease(opts) {
226
138
  * @param {object} opts.provider Ticketing provider.
227
139
  * @param {number} opts.ticketId Ticket to claim.
228
140
  * @param {string} opts.operator Operator acquiring the lease.
229
- * @param {number|null} [opts.heartbeatAt] Current owner's last heartbeat (epoch ms).
230
- * @param {boolean} [opts.steal=false] Transfer a live foreign claim.
231
- * @param {object} [opts.config] Resolved config (TTL default).
232
- * @param {number} [opts.ttlMs] Explicit TTL override.
233
- * @param {number} [opts.now] Injectable clock.
141
+ * @param {boolean} [opts.steal=false] Transfer a foreign claim.
234
142
  * @returns {Promise<{
235
143
  * acquired: boolean,
236
144
  * owner: string,
237
145
  * previousOwner: string|null,
238
- * reason: 'unclaimed'|'already-held'|'reclaimed'|'stolen'|'held'|'lost-race',
146
+ * reason: 'unclaimed'|'already-held'|'stolen'|'held'|'lost-race',
239
147
  * }>}
240
148
  */
241
149
  export async function acquireLease(opts) {
242
- const { provider, ticketId, operator, ttlMs, now } = normaliseOpts(
243
- 'acquireLease',
244
- opts,
245
- );
150
+ const { provider, ticketId, operator } = normaliseOpts('acquireLease', opts);
246
151
  const steal = opts.steal === true;
247
152
 
248
153
  const ticket = await provider.getTicket(ticketId);
@@ -269,9 +174,8 @@ export async function acquireLease(opts) {
269
174
  };
270
175
  }
271
176
 
272
- // Foreign claim — decide on liveness / steal.
273
- const live = isClaimLive({ heartbeatAt: opts.heartbeatAt, ttlMs, now });
274
- if (live && !steal) {
177
+ // Foreign claim — refuse unless the operator explicitly steals it.
178
+ if (!steal) {
275
179
  return {
276
180
  acquired: false,
277
181
  owner,
@@ -285,7 +189,7 @@ export async function acquireLease(opts) {
285
189
  ticketId,
286
190
  operator,
287
191
  previousOwner: owner,
288
- reason: steal && live ? 'stolen' : 'reclaimed',
192
+ reason: 'stolen',
289
193
  });
290
194
  }
291
195
 
@@ -359,9 +263,6 @@ async function claimAndVerify({
359
263
  * @param {object} opts.provider Ticketing provider.
360
264
  * @param {number} opts.ticketId Ticket to release.
361
265
  * @param {string} opts.operator Operator releasing the lease.
362
- * @param {object} [opts.config] Resolved config (TTL default).
363
- * @param {number} [opts.ttlMs] Explicit TTL override.
364
- * @param {number} [opts.now] Injectable clock.
365
266
  * @returns {Promise<{
366
267
  * released: boolean,
367
268
  * owner: string|null,
@@ -29,7 +29,7 @@
29
29
  */
30
30
 
31
31
  import { parse as parseStoryBody } from '../story-body/story-body.js';
32
- import { estimateTokens } from './context-envelope.js';
32
+ import { estimateTokens } from './spec-spill.js';
33
33
 
34
34
  /**
35
35
  * Normalize a Story's `body` to the structured object the sizing layers
@@ -146,14 +146,14 @@ export const STRUCTURED_COMMENT_TYPES = Object.freeze([
146
146
  // `--steal`, not by TTL expiry. One entry per ticket; re-acquires upsert
147
147
  // in place.
148
148
  'plan-lease',
149
- // Story #4415 (Epic #4406) — the feedback-loop graduators
150
- // (`audit-results-graduator.js` / `retro-proposals-graduator.js`) upsert a
151
- // `cross-repo-deferred` comment on the Epic listing findings that route
149
+ // Story #4415 (Epic #4406) — the feedback-loop graduator
150
+ // (`retro-proposals-graduator.js`) upserts a
151
+ // `cross-repo-deferred` comment listing findings that route
152
152
  // to a different repository and were therefore not filed here. Replaces
153
153
  // the prior log-line-only trace so the deferral survives the finalize
154
154
  // run as a durable, operator-visible record. Discriminated by a
155
- // `graduator="audit-results|code-review"` attr so the two graduators
156
- // upsert independent comments; re-runs upsert in place.
155
+ // `graduator="<name>"` attr so independent graduators do not clobber each
156
+ // other's comment; re-runs upsert in place.
157
157
  'cross-repo-deferred',
158
158
  // Epic #4474 (PR3) / v2 Stage 3 — `plan-persist.js` upserts a single
159
159
  // `plan-summary` comment on the primary Story at terminal persist
@@ -393,12 +393,13 @@ async function emitBlockedFriction(ticketId, fromState, newState, opts) {
393
393
  * `agent::done` at story-close (after the merge lands), not when the
394
394
  * last Task commit landed on the still-unmerged Story branch. The
395
395
  * parameter is preserved for callers that still suppress cascade
396
- * explicitly (e.g. batch-transition helpers).
396
+ * explicitly (`single-story-init.js`, and `bulk.js` where the upward
397
+ * recursion is driven by hand instead).
397
398
  *
398
399
  * `ticketSnapshot` (Story #1795 / Epic #1788) is an optional pre-fetched
399
- * ticket object. When the caller already holds the ticket (e.g.
400
- * `batchTransitionTickets`, which loops over a list it just hydrated),
401
- * passing the snapshot eliminates the two `getTicket` round-trips that
400
+ * ticket object. When the caller already holds the ticket e.g. it
401
+ * hydrated the ticket to inspect its labels first — passing the
402
+ * snapshot eliminates the two `getTicket` round-trips that
402
403
  * `transitionTicketState` would otherwise issue — one for the notify
403
404
  * `fromState` lookup and one inside `provider.updateTicket`'s label
404
405
  * merge path. Backwards compatible: when omitted, behaviour is unchanged.
@@ -0,0 +1,107 @@
1
+ /**
2
+ * verify-tier-repair.js — repair-before-judging for the `verify[]` tier suffix
3
+ * (Story #5005).
4
+ *
5
+ * The tier suffix (`… (unit)`) is a deterministic, mechanically-derivable
6
+ * formality. `task-body-validator.js` already computed the corrected entry in
7
+ * order to print it as a "Suggested fix" — then rejected the plan anyway and
8
+ * charged the author a full re-drafting round to paste that exact string back.
9
+ * This module applies the inference the validator already trusts, so the hard
10
+ * error is reserved for the entries only the author can resolve.
11
+ *
12
+ * It lives beside the validator rather than inside it because the validator's
13
+ * job is to *judge*: mixing a mutating repair pass into a module of pure
14
+ * collectors muddies both. `persist-helpers.js#validateTickets` calls this
15
+ * first, then `validateTaskBodies`.
16
+ *
17
+ * @module lib/orchestration/verify-tier-repair
18
+ */
19
+
20
+ import { suggestVerifyFix } from '../story-body/body-format-lints.js';
21
+ import {
22
+ parse as parseStoryBody,
23
+ serialize as serializeStoryBody,
24
+ } from '../story-body/story-body.js';
25
+ import { VERIFY_TIER_VALUES } from './task-body-validator.js';
26
+
27
+ /** A parenthesised tier drawn from the canonical vocabulary, at end of entry. */
28
+ const VERIFY_TIER_RE = new RegExp(
29
+ `\\((?:${VERIFY_TIER_VALUES.join('|')})\\)\\s*$`,
30
+ );
31
+
32
+ /**
33
+ * Rewrite one `verify[]` list, appending the inferable testing tier to any
34
+ * entry missing one. Returns `null` when nothing changed, so callers can keep
35
+ * an already-compliant list byte-identical (and skip re-serializing the body
36
+ * it came from).
37
+ *
38
+ * @param {unknown} rawVerify
39
+ * @returns {string[]|null} The corrected list, or `null` when no fix applied.
40
+ */
41
+ function repairVerifyList(rawVerify) {
42
+ if (!Array.isArray(rawVerify)) return null;
43
+ let changed = false;
44
+ const next = rawVerify.map((entry) => {
45
+ if (typeof entry !== 'string') return entry;
46
+ if (entry.startsWith('manual:') || VERIFY_TIER_RE.test(entry)) return entry;
47
+ const fix = suggestVerifyFix(entry);
48
+ if (fix === null) return entry;
49
+ changed = true;
50
+ return fix;
51
+ });
52
+ return changed ? next : null;
53
+ }
54
+
55
+ /**
56
+ * Repair the `## Verify` section of a serialized (string) body, in place on
57
+ * the ticket. Only re-serializes when a fix actually applied, so a compliant
58
+ * body is never round-tripped through `parse → serialize`.
59
+ *
60
+ * Leaving the body untouched while the top level was repaired would be worse
61
+ * than not repairing at all: `syncContractFieldFromTopLevel` fails closed on a
62
+ * body section that disagrees with its top-level array, so both sides must be
63
+ * repaired by the same rule or neither.
64
+ *
65
+ * @param {object} ticket
66
+ */
67
+ function repairStringBodyVerify(ticket) {
68
+ let parsed;
69
+ try {
70
+ parsed = parseStoryBody(ticket.body).body;
71
+ } catch {
72
+ // An unparseable body is already a hard error in `validateTaskBodyShape`;
73
+ // there is nothing to repair and nothing to report twice.
74
+ return;
75
+ }
76
+ const repaired = repairVerifyList(parsed.verify);
77
+ if (repaired === null) return;
78
+ ticket.body = serializeStoryBody({ ...parsed, verify: repaired });
79
+ }
80
+
81
+ /**
82
+ * Auto-append the testing tier to `verify[]` entries that omit one, wherever
83
+ * `suggestVerifyFix` can infer it from the command. An entry whose tier cannot
84
+ * be inferred is left untouched and still hard-errors downstream.
85
+ *
86
+ * Mutates `tickets` in place (the persist pipeline threads this same array on
87
+ * to assembly) and returns it. Already-compliant tickets are untouched, so
88
+ * their persisted output stays byte-identical. Total — a non-array argument
89
+ * and non-Story tickets are no-ops.
90
+ *
91
+ * @param {object[]} tickets
92
+ * @returns {object[]} `tickets`
93
+ */
94
+ export function normalizeVerifyTiers(tickets) {
95
+ for (const ticket of Array.isArray(tickets) ? tickets : []) {
96
+ if (!ticket || ticket.type !== 'story' || ticket.body == null) continue;
97
+ const topLevel = repairVerifyList(ticket.verify);
98
+ if (topLevel !== null) ticket.verify = topLevel;
99
+ if (typeof ticket.body === 'string') {
100
+ repairStringBodyVerify(ticket);
101
+ continue;
102
+ }
103
+ const bodyLevel = repairVerifyList(ticket.body.verify);
104
+ if (bodyLevel !== null) ticket.body.verify = bodyLevel;
105
+ }
106
+ return tickets;
107
+ }
@@ -34,17 +34,12 @@
34
34
  // operator-facing string explaining the verdict (always populated,
35
35
  // including for present tiers).
36
36
  //
37
- // acceptanceMatrix(criteria) -> {
38
- // tiers: TIERS,
39
- // rows: [{ id, label, verdict }, ...],
40
- // }
41
- //
42
- // Maps each acceptance criterion to its per-tier `coverageVerdict`, giving
43
- // the AC × test-tier matrix the markdown report (lib/qa/coverage-report.js)
44
- // renders.
37
+ // Story #5008 removed the `acceptanceMatrix` feeder and the markdown report it
38
+ // fed: the QA workflows read the per-tier verdict and author the missing-test
39
+ // prose directly, so the AC x tier matrix was a round-trip with no reader.
45
40
 
46
- /** The three test tiers, in pyramid order (base top). */
47
- export const TIERS = Object.freeze(['unit', 'contract', 'acceptance']);
41
+ /** The three test tiers, in pyramid order (base -> top). */
42
+ const TIERS = Object.freeze(['unit', 'contract', 'acceptance']);
48
43
 
49
44
  const PRESENT = 'present';
50
45
  const ABSENT = 'absent';
@@ -217,80 +212,3 @@ export function coverageVerdict(surface = {}) {
217
212
 
218
213
  return verdict;
219
214
  }
220
-
221
- /**
222
- * Normalize a single criterion descriptor into `{id, label, surface}`. A
223
- * descriptor may carry `id`, `label` (falls back to id), and either a nested
224
- * `surface` or a flat `{symbol, tests}` shape.
225
- */
226
- function normalizeOne(entry, index) {
227
- const e = entry && typeof entry === 'object' ? entry : {};
228
- const id =
229
- typeof e.id === 'string' && e.id.trim() !== ''
230
- ? e.id.trim()
231
- : `AC-${index + 1}`;
232
- const label =
233
- typeof e.label === 'string' && e.label.trim() !== '' ? e.label.trim() : id;
234
-
235
- let surface;
236
- if (e.surface && typeof e.surface === 'object') {
237
- surface = e.surface;
238
- } else if (Array.isArray(e.tests) || typeof e.symbol === 'string') {
239
- surface = { symbol: e.symbol, tests: e.tests };
240
- } else {
241
- surface = {};
242
- }
243
-
244
- return { id, label, surface };
245
- }
246
-
247
- /**
248
- * Normalize the criteria input for {@link acceptanceMatrix}. Accepts either an
249
- * array of criterion descriptors or a plain object keyed by criterion id whose
250
- * values are surfaces (or `{surface}` wrappers). Returns a normalized array of
251
- * `{id, label, surface}`.
252
- *
253
- * @param {Array<object>|object} criteria
254
- * @returns {Array<{id:string,label:string,surface:object}>}
255
- */
256
- function normalizeCriteria(criteria) {
257
- if (Array.isArray(criteria)) {
258
- return criteria.map((entry, index) => normalizeOne(entry, index));
259
- }
260
- if (criteria && typeof criteria === 'object') {
261
- return Object.entries(criteria).map(([id, value], index) =>
262
- normalizeOne(
263
- value && typeof value === 'object' && !Array.isArray(value)
264
- ? { id, ...value }
265
- : { id, surface: { tests: value } },
266
- index,
267
- ),
268
- );
269
- }
270
- throw new TypeError(
271
- 'acceptanceMatrix: criteria must be an array or an object',
272
- );
273
- }
274
-
275
- /**
276
- * Build the AC × test-tier matrix: for each acceptance criterion, the per-tier
277
- * {@link coverageVerdict}. This is the structured shape the markdown report
278
- * (`lib/qa/coverage-report.js`) renders into a table.
279
- *
280
- * @param {Array<{id?:string,label?:string,surface?:object,symbol?:string,tests?:Array}>|Record<string,object>} criteria
281
- * Acceptance criteria, either as an array of descriptors or an object keyed
282
- * by criterion id. Each descriptor names a surface (nested `surface`, or a
283
- * flat `{symbol, tests}`).
284
- * @returns {{tiers: ReadonlyArray<string>,
285
- * rows: Array<{id:string,label:string,
286
- * verdict:ReturnType<typeof coverageVerdict>}>}}
287
- */
288
- export function acceptanceMatrix(criteria) {
289
- const normalized = normalizeCriteria(criteria);
290
- const rows = normalized.map(({ id, label, surface }) => ({
291
- id,
292
- label,
293
- verdict: coverageVerdict(surface),
294
- }));
295
- return { tiers: TIERS, rows };
296
- }
@@ -2,7 +2,7 @@
2
2
  * detectors/common.js — shared helpers for the signals layer.
3
3
  *
4
4
  * Hoisted out of the detector modules (retry, rework) plus
5
- * `signals/read.js` and `signals/schema.js`, all of which shipped
5
+ * the since-deleted `signals/read.js`, and `signals/schema.js`, all of which shipped
6
6
  * byte-equivalent copies of these predicates. See Story #2464.
7
7
  */
8
8
 
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * `lib/signals/` barrel.
3
3
  *
4
- * Consumers do `import { read, schema, buildSpanTree, appendSignal } from './lib/signals/index.js'`
4
+ * Consumers do `import { schema, appendSignal } from './lib/signals/index.js'`
5
5
  * (or `import * as signals from './lib/signals/index.js'`) so a future
6
6
  * shape migration only has to touch this file.
7
7
  *
@@ -9,18 +9,20 @@
9
9
  * - Epic #1181 / Story #1438 / Task #1459 — initial barrel with `read`
10
10
  * + `schema`; `buildSpanTree` was a throwing placeholder.
11
11
  * - Epic #1181 / Story #1440 / Task #1461 — placeholder replaced by
12
- * the real export from `./span-tree.js`.
12
+ * the real span-tree builder.
13
13
  * - Story #1476 — `appendSignal` / `appendTrace` re-exported here
14
14
  * via `./write.js` so the gate scripts (and any
15
15
  * new code) converge on `lib/signals/` instead of importing the
16
16
  * writer directly from `lib/observability/`.
17
+ * - Story #5003 — the reader half (`read`, `buildSpanTree`) went with the
18
+ * debug viewer that consumed it: it walked a `run-<id>/` layout no v2
19
+ * writer populates. `appendTrace` went with the trace hook, its only
20
+ * producer. What remains is the live write surface.
17
21
  *
18
22
  * @module lib/signals
19
23
  */
20
24
 
21
- import { read } from './read.js';
22
25
  import * as schema from './schema.js';
23
- import { buildSpanTree } from './span-tree.js';
24
- import { appendSignal, appendTrace, forEachLine } from './write.js';
26
+ import { appendSignal, forEachLine } from './write.js';
25
27
 
26
- export { appendSignal, appendTrace, buildSpanTree, forEachLine, read, schema };
28
+ export { appendSignal, forEachLine, schema };
@@ -2,10 +2,10 @@
2
2
  * Signals schema — single source of truth for event-kind constants and
3
3
  * shape guards (Epic #1181 / Story #1438 / Task #1458).
4
4
  *
5
- * Both writers (`lib/observability/signals-writer.js` callers) and
6
- * readers (`lib/signals/read.js`, the retro's gather-signals phase)
7
- * import their field names from this module so the on-disk NDJSON shape
8
- * stays under one schema.
5
+ * Both writers (`lib/observability/signals-writer.js` callers) and readers
6
+ * (its `forEachLine` / `forEachSignalStreamLine` walkers, and the retro's
7
+ * gather-signals phase) import their field names from this module so the
8
+ * on-disk NDJSON shape stays under one schema.
9
9
  *
10
10
  * Pure module: importing has **no I/O side effects**. The exported
11
11
  * `EVENT_KINDS` object is `Object.freeze`'d so consumers can use it as a
@@ -14,30 +14,25 @@
14
14
  * ## Event-kind enumeration
15
15
  *
16
16
  * The set below is the audited union of `kind:` literals emitted via
17
- * `signals-writer.appendSignal` and `signals-writer.appendTrace` across
18
- * the codebase as of Epic #1181 (audit-snapshot 2026-05-11):
17
+ * `signals-writer.appendSignal` across the codebase as of Epic #1181
18
+ * (audit-snapshot 2026-05-11):
19
19
  *
20
20
  * Signals-writer `appendSignal` call sites:
21
21
  * - `friction` — quality-gate / runtime friction (diagnose-friction.js,
22
22
  * lib/gates/friction.js)
23
23
  * - `acceptance-eval` — acceptance-eval.js per-criterion terminus signal.
24
- * - `wave-start` / `wave-complete` — wave-window anchors; `wave-start` also
25
- * anchors span-tree Story spans
26
- * - `wave-end` — span-tree pairing anchor (retained for span-tree)
24
+ * - `wave-start` / `wave-complete` — wave-window anchors.
25
+ * - `wave-end` — wave-window pairing anchor.
27
26
  * - `state-transition` — notification-derived window anchor.
28
27
  *
29
28
  * The `dispatched` kind was deleted in the Epic #4406 signal-contract
30
29
  * cutover — it had no live emitter and no consumer.
31
30
  *
32
- * Signals-writer `appendTrace` call sites (traces.ndjson sibling, but
33
- * sharing the same envelope shape `tool-trace-hook.js`):
34
- * - `trace` per-tool-call timing record
35
- *
36
- * Detector-emitted / aggregator-consumed kinds (`rework`, `retry` are
37
- * emitted by `lib/signals/detectors/*`; `hotspot`, `churn`, `idle` remain
38
- * pinned in the enum for the aggregator's kind-count rollup even though no
39
- * live detector emits them):
40
- * - `hotspot`, `rework`, `churn`, `idle`, `retry`
31
+ * Producerless kinds, pinned in the enum so a re-introduction needs no schema
32
+ * bump. Story #5003 deleted the last emitters of `trace` (the tool-trace hook
33
+ * and its `appendTrace` writer limb) and of `rework` / `retry` (the detector
34
+ * pair the hook fed); `hotspot`, `churn`, `idle` never had one:
35
+ * - `trace`, `hotspot`, `rework`, `churn`, `idle`, `retry`
41
36
  *
42
37
  * ## Common envelope (canonical — Epic #4406 / Story #4413)
43
38
  *
@@ -68,18 +63,18 @@ import { isPositiveInt } from './detectors/common.js';
68
63
 
69
64
  /**
70
65
  * Frozen enumeration of every event kind currently emitted by
71
- * `signals-writer.appendSignal` / `appendTrace`, plus the
72
- * aggregator-consumed kinds whose detectors are future Stories.
66
+ * `signals-writer.appendSignal`, plus the producerless kinds a future Story
67
+ * may re-introduce an emitter for.
73
68
  *
74
69
  * Field names are kebab-cased to match the on-disk literals.
75
70
  */
76
71
  export const EVENT_KINDS = Object.freeze({
77
72
  FRICTION: 'friction',
78
73
  TRACE: 'trace',
79
- // Wave-window forensics signals: `wave-start` / `wave-end` anchor the
80
- // span-tree's Story spans. Story #4545 deleted the `waveParallelism`
81
- // consumer (perf-aggregator) with the execution-analysis surface; these
82
- // kinds stay as the span-tree's anchors.
74
+ // Wave-window forensics signals. Story #4545 deleted the `waveParallelism`
75
+ // consumer (perf-aggregator) with the execution-analysis surface, and
76
+ // Story #5003 deleted the span-tree that paired them; the kinds stay
77
+ // pinned so a re-introduced window reader needs no schema bump.
83
78
  WAVE_START: 'wave-start',
84
79
  WAVE_END: 'wave-end',
85
80
  WAVE_COMPLETE: 'wave-complete',
@@ -156,7 +151,7 @@ function isTimestamp(v) {
156
151
  * recognised `kind`. Canonical keys only — the legacy `timestamp` / `epic`
157
152
  * aliases were deleted in the Epic #4406 cutover.
158
153
  *
159
- * Returns true when the envelope is well-formed. Used by `lib/signals/read`
154
+ * Returns true when the envelope is well-formed. Used by the stream readers
160
155
  * to discard malformed lines before yielding them to the consumer.
161
156
  *
162
157
  * @param {unknown} evt