mandrel 1.88.0 → 1.90.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 (145) hide show
  1. package/.agents/README.md +18 -13
  2. package/.agents/audit-checklists/architecture.md +24 -0
  3. package/.agents/audit-checklists/clean-code.md +24 -0
  4. package/.agents/audit-checklists/dependencies.md +14 -0
  5. package/.agents/audit-checklists/devops.md +17 -0
  6. package/.agents/audit-checklists/documentation.md +22 -0
  7. package/.agents/audit-checklists/lighthouse.md +15 -0
  8. package/.agents/audit-checklists/navigability.md +14 -0
  9. package/.agents/audit-checklists/performance.md +22 -0
  10. package/.agents/audit-checklists/privacy.md +21 -0
  11. package/.agents/audit-checklists/quality.md +18 -0
  12. package/.agents/audit-checklists/security.md +22 -0
  13. package/.agents/audit-checklists/seo.md +16 -0
  14. package/.agents/audit-checklists/sre.md +24 -0
  15. package/.agents/audit-checklists/ux-ui.md +21 -0
  16. package/.agents/docs/SDLC.md +62 -27
  17. package/.agents/docs/configuration.md +5 -4
  18. package/.agents/instructions.md +51 -21
  19. package/.agents/personas/architect.md +10 -7
  20. package/.agents/personas/engineer.md +4 -3
  21. package/.agents/personas/project-manager.md +5 -2
  22. package/.agents/personas/refactorer.md +5 -3
  23. package/.agents/rules/git-conventions.md +77 -0
  24. package/.agents/schemas/agentrc.schema.json +10 -6
  25. package/.agents/schemas/audit-rules.json +16 -2
  26. package/.agents/schemas/audit-rules.schema.json +7 -6
  27. package/.agents/schemas/lifecycle/epic.blocked.schema.json +1 -1
  28. package/.agents/schemas/lifecycle/merge.unlanded.schema.json +39 -0
  29. package/.agents/schemas/signal-event.schema.json +28 -13
  30. package/.agents/scripts/acceptance-spec-reconciler.js +6 -4
  31. package/.agents/scripts/check-context-budget.js +320 -0
  32. package/.agents/scripts/coverage-capture.js +17 -0
  33. package/.agents/scripts/diagnose-friction.js +4 -4
  34. package/.agents/scripts/epic-audit-prepare.js +30 -2
  35. package/.agents/scripts/epic-audit-recheck.js +46 -13
  36. package/.agents/scripts/epic-deliver-prepare.js +80 -8
  37. package/.agents/scripts/epic-plan-spec.js +4 -8
  38. package/.agents/scripts/generate-lens-checklists.js +180 -0
  39. package/.agents/scripts/lib/audit-suite/checklist-threading.js +300 -0
  40. package/.agents/scripts/lib/audit-suite/findings.js +27 -0
  41. package/.agents/scripts/lib/audit-suite/index.js +9 -0
  42. package/.agents/scripts/lib/audit-suite/lens-checklist.js +212 -0
  43. package/.agents/scripts/lib/audit-suite/selector.js +136 -5
  44. package/.agents/scripts/lib/checks/loop-health.js +340 -0
  45. package/.agents/scripts/lib/cli-args.js +8 -0
  46. package/.agents/scripts/lib/close-validation/gates.js +64 -24
  47. package/.agents/scripts/lib/config/ci.js +12 -1
  48. package/.agents/scripts/lib/config/runners.js +13 -5
  49. package/.agents/scripts/lib/config/temp-paths.js +24 -0
  50. package/.agents/scripts/lib/config-settings-schema-delivery.js +28 -8
  51. package/.agents/scripts/lib/doc-tiers.js +291 -0
  52. package/.agents/scripts/lib/epic-body-sections.js +5 -2
  53. package/.agents/scripts/lib/epic-merge-lock.js +83 -0
  54. package/.agents/scripts/lib/epic-plan-clarity.js +3 -1
  55. package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +47 -15
  56. package/.agents/scripts/lib/feedback-loop/graduator-core.js +395 -86
  57. package/.agents/scripts/lib/feedback-loop/memory-freshness.js +299 -72
  58. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +438 -0
  59. package/.agents/scripts/lib/gates/friction.js +15 -5
  60. package/.agents/scripts/lib/npm-scripts.js +55 -0
  61. package/.agents/scripts/lib/observability/perf-aggregator.js +30 -104
  62. package/.agents/scripts/lib/observability/perf-report-readers.js +1 -1
  63. package/.agents/scripts/lib/observability/signal-validator.js +204 -0
  64. package/.agents/scripts/lib/observability/signals-writer.js +157 -54
  65. package/.agents/scripts/lib/observability/tool-trace-hook.js +42 -4
  66. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +1 -1
  67. package/.agents/scripts/lib/orchestration/code-review.js +74 -4
  68. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +213 -0
  69. package/.agents/scripts/lib/orchestration/doc-reader.js +4 -96
  70. package/.agents/scripts/lib/orchestration/docs-digest.js +34 -0
  71. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +56 -19
  72. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +22 -0
  73. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +193 -0
  74. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +6 -0
  75. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +248 -13
  76. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +109 -12
  77. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +47 -61
  78. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +46 -4
  79. package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +144 -0
  80. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +258 -14
  81. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +6 -0
  82. package/.agents/scripts/lib/orchestration/merge-block-class.js +246 -0
  83. package/.agents/scripts/lib/orchestration/plan-review-routing.js +1 -1
  84. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +3 -3
  85. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +63 -34
  86. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +167 -52
  87. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +49 -2
  88. package/.agents/scripts/lib/orchestration/retro-proposals.js +12 -55
  89. package/.agents/scripts/lib/orchestration/retro-runner.js +9 -0
  90. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +5 -1
  91. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +8 -0
  92. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +419 -0
  93. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +35 -2
  94. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +353 -69
  95. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +66 -4
  96. package/.agents/scripts/lib/orchestration/spec-section-validator.js +60 -9
  97. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +7 -5
  98. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +24 -2
  99. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +167 -8
  100. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +8 -1
  101. package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +163 -0
  102. package/.agents/scripts/lib/orchestration/ticketing/reads.js +20 -9
  103. package/.agents/scripts/lib/planning-corpus.js +306 -0
  104. package/.agents/scripts/lib/signals/detectors/common.js +10 -10
  105. package/.agents/scripts/lib/signals/detectors/index.js +4 -4
  106. package/.agents/scripts/lib/signals/detectors/retry.js +19 -18
  107. package/.agents/scripts/lib/signals/detectors/rework.js +1 -1
  108. package/.agents/scripts/lib/signals/schema.js +56 -81
  109. package/.agents/scripts/lib/signals/span-tree.js +6 -5
  110. package/.agents/scripts/lib/story-plan.js +3 -0
  111. package/.agents/scripts/lib/wave-runner/tick.js +10 -2
  112. package/.agents/scripts/lifecycle-emit.js +39 -8
  113. package/.agents/scripts/providers/github/issues.js +12 -1
  114. package/.agents/scripts/resolve-doc-tiers.js +83 -0
  115. package/.agents/scripts/retro-run.js +51 -0
  116. package/.agents/scripts/signals-view.js +1 -1
  117. package/.agents/scripts/single-story-close.js +20 -1
  118. package/.agents/scripts/standalone-feedback-rollup.js +188 -0
  119. package/.agents/scripts/story-close.js +48 -0
  120. package/.agents/scripts/story-plan.js +51 -12
  121. package/.agents/scripts/validate-docs-freshness.js +69 -15
  122. package/.agents/skills/core/documentation-and-adrs/SKILL.md +58 -0
  123. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +5 -3
  124. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +20 -7
  125. package/.agents/skills/core/scope-triage/SKILL.md +61 -0
  126. package/.agents/skills/skills.index.json +3 -3
  127. package/.agents/workflows/audit-documentation.md +82 -2
  128. package/.agents/workflows/helpers/code-review.md +116 -43
  129. package/.agents/workflows/helpers/deliver-epic.md +123 -54
  130. package/.agents/workflows/helpers/deliver-stories.md +26 -0
  131. package/.agents/workflows/helpers/epic-audit.md +116 -366
  132. package/.agents/workflows/helpers/epic-deliver-story.md +14 -0
  133. package/.agents/workflows/helpers/epic-plan-decompose.md +18 -200
  134. package/.agents/workflows/helpers/epic-plan-spec.md +18 -180
  135. package/.agents/workflows/helpers/plan-epic.md +141 -105
  136. package/.agents/workflows/helpers/plan-story.md +32 -0
  137. package/.agents/workflows/helpers/single-story-deliver.md +43 -0
  138. package/.agents/workflows/loops/nightly-audit.md +9 -7
  139. package/docs/CHANGELOG.md +29 -0
  140. package/lib/cli/doctor.js +44 -0
  141. package/package.json +4 -3
  142. package/.agents/scripts/epic-plan-spec-validate.js +0 -111
  143. package/.agents/scripts/lib/feedback-loop/code-review-graduator.js +0 -224
  144. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +0 -58
  145. package/.agents/scripts/lib/signals/detectors/hotspot.js +0 -292
@@ -17,10 +17,12 @@ Before permitting any code generation, you must enforce this workflow:
17
17
  1. **Interrogate Context:** Read the Epic body — including its `## User
18
18
  Stories` section and the folded Tech Spec sections (`## Delivery
19
19
  Slicing` onward; Story #4324 retired the separate Tech Spec ticket) —
20
- plus every file listed in
21
- `project.docsContextFiles` (typically `architecture.md` and
22
- `data-dictionary.md`). Ask clarifying questions about scale, budget, or
23
- edge cases.
20
+ plus the project's docs digest (digest-first with pull-on-demand for
21
+ `architecture.md`, `data-dictionary.md`, and the rest of
22
+ `project.docsContextFiles`; see
23
+ [`.agents/instructions.md` § 3](../instructions.md) — there is no
24
+ read-every-file mandate). Ask clarifying questions about scale, budget,
25
+ or edge cases.
24
26
  2. **Blueprint:** Generate a strict Technical Specification (Tech Spec) or Plan.
25
27
  3. **Validate:** Explicitly verify that your proposed changes do not violate
26
28
  existing database constraints or architectural boundaries.
@@ -54,9 +56,10 @@ Before permitting any code generation, you must enforce this workflow:
54
56
 
55
57
  - **Friction Analysis:** During the retro phase (Phase 5 of
56
58
  `/deliver`, driven by `lib/orchestration/retro-runner.js`), you
57
- MUST analyze the
58
- `agent-friction-log.json` to identify systemic bottlenecks, repetitive tool
59
- failures, or prompt ambiguities.
59
+ MUST analyze the aggregated friction signals — the `kind: friction`
60
+ records on the per-Epic/per-Story `signals.ndjson` streams, rolled up by
61
+ the retro's perf-aggregator — to identify systemic bottlenecks, repetitive
62
+ tool failures, or prompt ambiguities.
60
63
  - **Actionable Optimization:** You are responsible for generating "agent-ready"
61
64
  recommendations. These must be formatted as specific markdown instructions or
62
65
  skill snippets that can be immediately reviewed and applied to
@@ -19,9 +19,10 @@ plan, stop and ask. Do not invent business logic.
19
19
  1. **Read Context:** Before writing a single line, read the parent Epic
20
20
  body — the single planning document, including its `## User Stories`
21
21
  section and the folded Tech Spec sections (`## Delivery Slicing`
22
- onward; Story #4324 retired the separate Tech Spec ticket) — plus
23
- every file listed in `project.docsContextFiles` (typically
24
- `architecture.md` and the project's architectural guidelines).
22
+ onward; Story #4324 retired the separate Tech Spec ticket) — plus the
23
+ docs digest (digest-first with pull-on-demand; see
24
+ [`.agents/instructions.md` § 3](../instructions.md) there is no
25
+ read-every-`project.docsContextFiles`-file mandate).
25
26
  2. **Workspace Awareness:** Identify if you are working in a monorepo or a
26
27
  standard repo. Ensure all commands (installing packages, running scripts) are
27
28
  executed in the correct workspace/directory. Check `package.json` or the
@@ -20,8 +20,11 @@ SQL, or UI components — stop immediately.
20
20
  1. **Gather Context:** Read the parent Epic body — including its
21
21
  `## User Stories` section and the folded Tech Spec sections
22
22
  (`## Delivery Slicing` onward; Story #4324 retired the separate Tech
23
- Spec ticket) — plus every file listed in `project.docsContextFiles`
24
- (typically `architecture.md` and the data dictionary).
23
+ Spec ticket) — plus the project's docs digest (digest-first with
24
+ pull-on-demand for `architecture.md`, the data dictionary, and the rest
25
+ of `project.docsContextFiles`; see
26
+ [`.agents/instructions.md` § 3](../instructions.md) — there is no
27
+ read-every-file mandate).
25
28
  2. **Decompose:** Break the Epic into **Stories** that carry their own
26
29
  inline acceptance criteria and verification steps. Aim for roughly
27
30
  five acceptance bullets per Story as a soft atomicity heuristic; if
@@ -24,9 +24,11 @@ red tests green by "refactoring".
24
24
 
25
25
  1. **Read Context:** Before touching anything, confirm the suite is green
26
26
  and the quality gates currently pass. Read the parent Epic body —
27
- including its folded Tech Spec sections (Story #4324) — plus every file
28
- listed in
29
- `project.docsContextFiles` so you know the conventions the code must keep
27
+ including its folded Tech Spec sections (Story #4324) — plus the
28
+ project's docs digest (digest-first with pull-on-demand across
29
+ `project.docsContextFiles`; see
30
+ [`.agents/instructions.md` § 3](../instructions.md) — there is no
31
+ read-every-file mandate) so you know the conventions the code must keep
30
32
  matching.
31
33
  2. **Establish the baseline:** Capture the current CRAP and maintainability
32
34
  numbers (e.g. `node .agents/scripts/check-baselines.js`) and the set of
@@ -165,6 +165,83 @@ operator intervention:
165
165
  owning flow's hygiene step regressed — fix the flow, do not codify the manual
166
166
  sweep.
167
167
 
168
+ ### Shared-checkout contention (Story #4460)
169
+
170
+ `story-close.js`'s merge phase runs `git checkout <epic-branch>` directly in
171
+ the **shared main repo checkout** (`close-inputs.js` resolves `mainCwd` to
172
+ `PROJECT_ROOT`), not an isolated worktree. `lib/epic-merge-lock.js` guards
173
+ that checkout with a **per-Epic** filesystem lock
174
+ (`epic-<epicId>.merge.lock`) so two `story-close.js` runs for the **same**
175
+ Epic serialize against each other — but nothing stops a **different**
176
+ Epic's concurrently-running `story-close.js` from treating the same shared
177
+ checkout as scratch space at the same time.
178
+
179
+ - **Recognition signature**: a `git checkout`/`git switch` failure during
180
+ the merge phase whose message is `error: Your local changes ... would be
181
+ overwritten by checkout`, where the shared checkout is parked on a
182
+ **different** epic's branch (e.g. `epic/4405`) than the one the current
183
+ `story-close.js` run is trying to merge (e.g. `epic/4425`), with
184
+ uncommitted edits that belong to that other Epic's delivery. This was
185
+ observed live during Epic #4425 delivery (Stories #4427/#4428) colliding
186
+ with a concurrently-running Epic #4405 session, and had to be worked
187
+ around by hand via `git stash push -u`.
188
+ - **The fix — `assertSharedCheckoutAvailable`**
189
+ (`lib/orchestration/story-close/shared-checkout-guard.js`), called from
190
+ `runFinalizeMerge` in `lib/orchestration/story-close/merge-runner.js`
191
+ immediately before the merge-phase `git checkout <epicBranch>`. It
192
+ **composes with, not replaces,** the per-Epic lock:
193
+ - It first checks the shared common `.git/` dir for a **foreign**
194
+ (different-epic) `epic-*.merge.lock` file whose recorded PID is still
195
+ alive (`findForeignActiveEpicLock` in `lib/epic-merge-lock.js`). If
196
+ found, the merge phase fails fast with a diagnostic naming the holding
197
+ epic id, its lock-file path, and its PID/acquired-at timestamp —
198
+ instead of surfacing the raw git checkout error.
199
+ - It then checks whether the shared checkout is simply dirty (via `git
200
+ status --porcelain`), regardless of whose branch is checked out, and
201
+ reports the dirty file list plus the currently-checked-out branch in
202
+ the failure diagnostic.
203
+ - It never inspects the **caller's own** epic-id lock namespace, so
204
+ same-epic concurrent `story-close.js` runs continue to serialize
205
+ solely through `withEpicMergeLock` (the existing per-Epic lock) before
206
+ this guard ever executes — this guard only ever refuses on a truly
207
+ _foreign_ epic's live lock or unrelated dirt.
208
+ - **Not fixed by this guard**: the guard reports the contention early and
209
+ actionably; it does not redesign the merge phase to use an isolated
210
+ worktree, and it does not change `restoreStartingBranch`'s existing
211
+ dirty-tree refusal behavior (`phases/branch-restore.js`) — both remain
212
+ out of scope. Resolution of an actual collision is still manual: wait for
213
+ the other Epic's story-close run to finish, or — only once you have
214
+ independently confirmed that process is no longer running — remove the
215
+ stale lock file and resolve the dirty tree by hand (stash/commit/reset;
216
+ never `git reset --hard` or `git checkout --force`).
217
+
218
+ ## Documentation Freshness Gate
219
+
220
+ The `validate-docs-freshness.js` gate (run during `/deliver`) asks a
221
+ falsifiable question of every doc in `delivery.docsFreshness.paths` +
222
+ `project.docsContextFiles`: **was this doc actually updated for the Epic?**
223
+ A doc passes on either of two conditions, but they are not
224
+ interchangeable:
225
+
226
+ - **Living docs are satisfied by being rewritten, not annotated.** For
227
+ any non-changelog doc (architecture, decisions, README, guides, …) the
228
+ gate passes **only** when an Epic-referencing commit touched the file —
229
+ a commit whose message references `#<epicId>` and changes the doc.
230
+ Rewrite the doc as part of the Epic's work; do not sprinkle `#<epicId>`
231
+ into its prose to satisfy the check. An appended `#<epicId>` annotation
232
+ alone **fails** the gate for these files, and the failure message names
233
+ the file and the rewrite-not-append contract.
234
+ - **`#<epicId>` body annotations pass only for changelog files.** A
235
+ changelog-class file (basename matches `/changelog/i`, e.g.
236
+ `docs/CHANGELOG.md`) may pass on a body annotation, because an appended
237
+ release note keyed to the Epic is the legitimate, expected update there.
238
+ This is the single sanctioned annotation path; every other doc must use
239
+ the rewrite path above.
240
+
241
+ This restriction exists to remove the perverse incentive by which the
242
+ gate would otherwise reward manufacturing fake provenance — appending
243
+ Epic-ID history into living docs purely to clear the check.
244
+
168
245
  ## Meta Labels (Retrospective Signal Routing)
169
246
 
170
247
  Two `meta::*` labels route retrospective signals into durable substrates so
@@ -546,8 +546,8 @@
546
546
  "autoFixSeverity": {
547
547
  "type": "string",
548
548
  "enum": ["high", "medium"],
549
- "default": "medium",
550
- "description": "Severity threshold for on-branch remediation in /deliver Phase 4 (epic-audit). `medium` (default) routes 🔴/🟠/🟡 findings into the host-LLM remediation loop (Mediums batched per lens: one commit per lens, a single validation + overlapping-lens rescan at the end) while 🟢 suggestions still graduate to follow-up issues; `high` reproduces the pre-4399 Critical/High-only routing. Hard cutover — no back-compat flag."
549
+ "default": "high",
550
+ "description": "Severity threshold for on-branch remediation of the Epic-close lens findings walked inside the /deliver Phase 5 code-review pass (Story #4412 folded the former Phase 4 lens walk into that single cumulative-diff pass). `high` (default) routes only 🔴 Critical / 🟠 High lens findings into the host-LLM remediation loop while 🟡 Medium and 🟢 Suggestion findings graduate to follow-up issues (🟡 Medium concerns are already remediated shift-left at the write-time and Story-scope tiers); `medium` opts back into routing 🔴/🟠/🟡 (Mediums batched per lens). Hard cutover — no back-compat flag."
551
551
  }
552
552
  },
553
553
  "additionalProperties": false
@@ -1456,6 +1456,10 @@
1456
1456
  "type": "string",
1457
1457
  "enum": ["trust-ci", "strict"],
1458
1458
  "description": "Story #4356 (Epic #4355). Merge posture. 'trust-ci' (default) merges once required checks pass; 'strict' additionally requires a clean review gate."
1459
+ },
1460
+ "requireChecks": {
1461
+ "type": "boolean",
1462
+ "description": "Story #4472. Fail-closed-without-checks policy. When true, the AutomergePredicate refuses to arm merge in a repo that reports zero required checks ('no checks reported'), treating the absent CI gate as a hard block. Defaults to false so a checks-less repo with green close-validation gates lands headlessly instead of parking on the operator-merges path."
1459
1463
  }
1460
1464
  },
1461
1465
  "additionalProperties": false
@@ -1493,15 +1497,15 @@
1493
1497
  "feedbackLoop": {
1494
1498
  "type": "object",
1495
1499
  "properties": {
1496
- "codeReviewAutoFile": {
1500
+ "auditResultsAutoFile": {
1497
1501
  "type": "boolean",
1498
1502
  "default": true,
1499
- "description": "When true (default), the Epic finalize listener auto-files non-blocking code-review findings as follow-up issues routed by source classification. Set to false to suppress auto-filing; findings remain accessible in the structured comments on the Epic."
1503
+ "description": "When true (default), the Epic finalize listener auto-files non-blocking audit-results findings as follow-up issues routed by source classification. Set to false to suppress auto-filing; findings remain accessible in the structured comments on the Epic."
1500
1504
  },
1501
- "auditResultsAutoFile": {
1505
+ "retroProposals": {
1502
1506
  "type": "boolean",
1503
1507
  "default": true,
1504
- "description": "When true (default), the Epic finalize listener auto-files non-blocking audit-results findings as follow-up issues routed by source classification. Set to false to suppress auto-filing; findings remain accessible in the structured comments on the Epic."
1508
+ "description": "When true (default), the retro auto-files its actionable routed proposals as meta::<framework-gap|consumer-improvement> + friction::<category> issues via the graduator pre-parsed-findings seam, and the rendered retro sections list the filed issue numbers instead of paste-ready gh command stanzas. Set to false to fall back to the command stanzas."
1505
1509
  }
1506
1510
  },
1507
1511
  "additionalProperties": false
@@ -2,11 +2,13 @@
2
2
  "version": 1,
3
3
  "audits": {
4
4
  "audit-clean-code": {
5
- "triggers": { "gates": ["gate1", "gate3"], "alwaysRun": true },
5
+ "triggers": { "gates": ["gate1", "gate3"], "filePatterns": ["**/*"] },
6
+ "scope": "local",
6
7
  "substitutionKeys": []
7
8
  },
8
9
  "audit-architecture": {
9
- "triggers": { "gates": ["gate1", "gate3"], "alwaysRun": true },
10
+ "triggers": { "gates": ["gate1", "gate3"] },
11
+ "scope": "cumulative",
10
12
  "substitutionKeys": []
11
13
  },
12
14
  "audit-security": {
@@ -22,6 +24,7 @@
22
24
  ],
23
25
  "filePatterns": ["**/*.lock", "**/auth/*.js", "**/crypto/*.js"]
24
26
  },
27
+ "scope": "local",
25
28
  "substitutionKeys": []
26
29
  },
27
30
  "audit-privacy": {
@@ -30,6 +33,7 @@
30
33
  "keywords": ["privacy", "pii", "gdpr", "cookie", "tracking"],
31
34
  "filePatterns": ["**/user-profile/**/*.js"]
32
35
  },
36
+ "scope": "local",
33
37
  "substitutionKeys": []
34
38
  },
35
39
  "audit-lighthouse": {
@@ -54,6 +58,7 @@
54
58
  "**/frontend/**/*.js"
55
59
  ]
56
60
  },
61
+ "scope": "local",
57
62
  "substitutionKeys": []
58
63
  },
59
64
  "audit-devops": {
@@ -67,10 +72,12 @@
67
72
  "**/*.tf"
68
73
  ]
69
74
  },
75
+ "scope": "cumulative",
70
76
  "substitutionKeys": []
71
77
  },
72
78
  "audit-sre": {
73
79
  "triggers": { "gates": ["gate4"] },
80
+ "scope": "global",
74
81
  "substitutionKeys": []
75
82
  },
76
83
  "audit-documentation": {
@@ -91,6 +98,7 @@
91
98
  ".agents/workflows/**"
92
99
  ]
93
100
  },
101
+ "scope": "cumulative",
94
102
  "substitutionKeys": []
95
103
  },
96
104
  "audit-dependencies": {
@@ -112,6 +120,7 @@
112
120
  "**/yarn.lock"
113
121
  ]
114
122
  },
123
+ "scope": "cumulative",
115
124
  "substitutionKeys": []
116
125
  },
117
126
  "audit-performance": {
@@ -120,6 +129,7 @@
120
129
  "keywords": ["performance", "n+1", "hot path", "latency", "throughput"],
121
130
  "filePatterns": [".agents/scripts/**", "src/**/*.{ts,js}"]
122
131
  },
132
+ "scope": "local",
123
133
  "substitutionKeys": []
124
134
  },
125
135
  "audit-quality": {
@@ -128,6 +138,7 @@
128
138
  "keywords": ["test", "coverage", "flaky", "assertion"],
129
139
  "filePatterns": ["tests/**", "**/*.test.{ts,js}", "**/*.spec.{ts,js}"]
130
140
  },
141
+ "scope": "local",
131
142
  "substitutionKeys": []
132
143
  },
133
144
  "audit-seo": {
@@ -136,6 +147,7 @@
136
147
  "keywords": ["seo", "meta", "structured data", "schema.org", "sitemap"],
137
148
  "filePatterns": ["**/*.html", "**/sitemap*", "**/robots.txt"]
138
149
  },
150
+ "scope": "local",
139
151
  "substitutionKeys": []
140
152
  },
141
153
  "audit-ux-ui": {
@@ -148,6 +160,7 @@
148
160
  "**/*.css"
149
161
  ]
150
162
  },
163
+ "scope": "local",
151
164
  "substitutionKeys": []
152
165
  },
153
166
  "audit-navigability": {
@@ -160,6 +173,7 @@
160
173
  "route tree"
161
174
  ]
162
175
  },
176
+ "scope": "global",
163
177
  "substitutionKeys": []
164
178
  }
165
179
  }
@@ -24,10 +24,15 @@
24
24
  "definitions": {
25
25
  "auditEntry": {
26
26
  "type": "object",
27
- "required": ["triggers"],
27
+ "required": ["triggers", "scope"],
28
28
  "additionalProperties": false,
29
29
  "properties": {
30
30
  "triggers": { "$ref": "#/definitions/triggers" },
31
+ "scope": {
32
+ "type": "string",
33
+ "enum": ["local", "cumulative", "global"],
34
+ "description": "Concern-ownership tier declaring the widest scope at which this lens's concern becomes decidable. `local` — decidable from a single Story's diff (verified at write-time + Story-scope review). `cumulative` — only decidable across the Epic's combined diff (verified at Epic close). `global` — evaluates a whole-product property regardless of the diff (verified at Epic close; exempt from change-set narrowing). All tier routing derives from this one field via resolveLensTier()."
35
+ },
31
36
  "substitutionKeys": {
32
37
  "type": "array",
33
38
  "description": "Substitution keys this audit accepts via run-audit-suite --substitution. Empty array means no caller-supplied keys are allowed.",
@@ -38,7 +43,7 @@
38
43
  },
39
44
  "triggers": {
40
45
  "type": "object",
41
- "description": "Selection criteria for whether this audit runs against a given ticket / changeset. At least one of `gates` or `alwaysRun: true` must be set for select-audits.js to ever pick the audit.",
46
+ "description": "Selection criteria for whether this audit runs against a given ticket / changeset. `gates` must be set for select-audits.js to ever pick the audit; keyword / file-pattern matches then narrow selection within a matched gate.",
42
47
  "additionalProperties": false,
43
48
  "properties": {
44
49
  "gates": {
@@ -47,10 +52,6 @@
47
52
  "uniqueItems": true,
48
53
  "description": "Quality-gate identifiers (gate1..gateN) that include this audit."
49
54
  },
50
- "alwaysRun": {
51
- "type": "boolean",
52
- "description": "If true, the audit is always selected regardless of keyword / file-pattern matches."
53
- },
54
55
  "keywords": {
55
56
  "type": "array",
56
57
  "items": { "type": "string", "minLength": 1 },
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://github.com/dsj1984/mandrel/blob/main/.agents/schemas/lifecycle/epic.blocked.schema.json",
4
4
  "title": "epic.blocked",
5
- "description": "Emitted by AcceptanceReconciler (on a failed acceptance reconciliation) and MergeWatcher (on a merge-watch timeout) when the Epic transitions to agent::blocked. Subscribed by NotifyDispatcher, which fans the blocker out to the curated webhook channel. The reason field carries either a typed marker (timeout:<event>, waiver, …) or a free-form summary; sourceStoryId scopes the blocker to a child Story when applicable.",
5
+ "description": "Emitted by AcceptanceReconciler (on a failed acceptance reconciliation), MergeWatcher (on a merge-watch timeout), and — in headless runs (Story #4472) — AutomergePredicate (on a predicate refusal that would otherwise silently park) and AutomergeArmer (on a genuine arm failure) when the Epic transitions to agent::blocked. Subscribed by NotifyDispatcher, which fans the blocker out to the curated webhook channel. The reason field carries either a typed marker (timeout:<event>, merge-predicate:refused, merge-arm:failed, waiver, …) or a free-form summary; sourceStoryId scopes the blocker to a child Story when applicable.",
6
6
  "type": "object",
7
7
  "required": ["reason"],
8
8
  "properties": {
@@ -0,0 +1,39 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/dsj1984/mandrel/blob/main/.agents/schemas/lifecycle/merge.unlanded.schema.json",
4
+ "title": "merge.unlanded",
5
+ "description": "Emitted whenever a headless delivery run (the epic-path finalize flow or the standalone single-story-close flow) finishes its work without a confirmed merge, so a work-complete-but-unmerged terminal state is precisely attributable from the lifecycle ledger instead of diffing origin/main after the fact (Epic #4425). scope distinguishes the epic-path (ticketId = epicId) from the standalone story-path (ticketId = storyId); blockClass is produced by the shared classifier in merge-block-class.js.",
6
+ "type": "object",
7
+ "required": [
8
+ "event",
9
+ "scope",
10
+ "ticketId",
11
+ "prNumber",
12
+ "blockClass",
13
+ "reason",
14
+ "elapsedSeconds"
15
+ ],
16
+ "properties": {
17
+ "event": { "type": "string", "const": "merge.unlanded" },
18
+ "scope": {
19
+ "type": "string",
20
+ "enum": ["epic", "story"]
21
+ },
22
+ "ticketId": { "type": "integer", "minimum": 1 },
23
+ "prNumber": { "type": "integer", "minimum": 1 },
24
+ "blockClass": {
25
+ "type": "string",
26
+ "enum": [
27
+ "checks-pending-timeout",
28
+ "branch-protection-human-required",
29
+ "arm-failure",
30
+ "api-race-other",
31
+ "predicate-refused"
32
+ ]
33
+ },
34
+ "reason": { "type": "string", "minLength": 1 },
35
+ "elapsedSeconds": { "type": "number", "minimum": 0 },
36
+ "timestamp": { "type": "string", "format": "date-time" }
37
+ },
38
+ "additionalProperties": false
39
+ }
@@ -2,15 +2,15 @@
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "https://github.com/dsj1984/mandrel/blob/main/.agents/schemas/signal-event.schema.json",
4
4
  "title": "SignalEvent",
5
- "description": "One NDJSON line emitted by the signals writer (Epic #1030). Runtime events live in append-only NDJSON on local disk under temp/epic-<eid>/stories/story-<sid>/signals.ndjson; tickets carry only summaries (story-perf-summary, epic-perf-report). `kind: trace` is the raw tool-call trace; the named kinds are derived signals.",
5
+ "description": "One NDJSON line emitted by the signals writer (Epic #1030; canonical envelope cutover Epic #4406 / Story #4413). Runtime events live in append-only NDJSON on local disk under temp/epic-<eid>/stories/story-<sid>/signals.ndjson (per-Story) and temp/epic-<eid>/signals.ndjson (Epic-level wave lifecycle); tickets carry only summaries (story-perf-summary, epic-perf-report). `kind: trace` is the raw tool-call trace; the named kinds are derived signals. This is the ONLY shape — the legacy `timestamp` / `epic` aliases and the `source: { tool }` provenance object were deleted in the same PR that moved every writer (no read-side compatibility branch).",
6
6
  "type": "object",
7
- "additionalProperties": false,
8
- "required": ["ts", "kind", "source", "epicId", "storyId"],
7
+ "additionalProperties": true,
8
+ "required": ["kind", "ts"],
9
9
  "properties": {
10
10
  "ts": {
11
11
  "type": "string",
12
12
  "format": "date-time",
13
- "description": "Event timestamp in ISO-8601 UTC."
13
+ "description": "Event timestamp in ISO-8601 UTC. The single canonical timestamp key — the legacy `timestamp` alias is gone."
14
14
  },
15
15
  "kind": {
16
16
  "type": "string",
@@ -22,28 +22,43 @@
22
22
  "idle",
23
23
  "retry",
24
24
  "acceptance-eval",
25
- "trace"
25
+ "trace",
26
+ "wave-start",
27
+ "wave-end",
28
+ "wave-complete",
29
+ "state-transition",
30
+ "notification.emitted"
26
31
  ]
27
32
  },
28
33
  "source": {
34
+ "type": "string",
35
+ "enum": ["framework", "consumer"],
36
+ "description": "Framework/consumer classification injected by `tagSignalSource` (signals-writer.js) for records appended via appendSignal/appendEpicSignal. Reserved exclusively for this classifier tag — the originating tool/surface lives in `emitter`."
37
+ },
38
+ "emitter": {
29
39
  "type": "object",
30
- "additionalProperties": false,
31
- "required": ["tool"],
40
+ "additionalProperties": true,
32
41
  "properties": {
33
42
  "tool": {
34
43
  "type": "string",
35
44
  "minLength": 1,
36
45
  "description": "Originating tool or surface (Bash, Edit, Write, Read, Grep, Glob, or a script name for derived signals)."
37
46
  },
38
- "script": {
47
+ "command": {
39
48
  "type": "string",
40
- "minLength": 1,
41
- "description": "Script that emitted the signal, when applicable (e.g. diagnose-friction.js)."
49
+ "description": "Command line the signal blames, when applicable (e.g. the failed command captured by diagnose-friction.js)."
42
50
  }
43
- }
51
+ },
52
+ "description": "Provenance of the record — the tool/script that produced it. Replaces the pre-cutover `source: { tool }` object; `source` now carries only the framework/consumer classification."
44
53
  },
45
54
  "epicId": { "type": ["integer", "null"], "minimum": 1 },
46
- "storyId": { "type": "integer", "minimum": 1 },
55
+ "storyId": { "type": ["integer", "null"], "minimum": 1 },
56
+ "taskId": { "type": ["integer", "null"], "minimum": 1 },
57
+ "category": {
58
+ "type": "string",
59
+ "minLength": 1,
60
+ "description": "Top-level friction category (e.g. Execution Error, baseline-refresh-regression). Read directly by perf-aggregator frictionByCategory and the retro routed-proposal extraction — never nested under details."
61
+ },
47
62
  "phase": {
48
63
  "type": ["string", "null"],
49
64
  "minLength": 1,
@@ -51,7 +66,7 @@
51
66
  },
52
67
  "details": {
53
68
  "type": "object",
54
- "description": "Kind-specific payload. Free-form for forward compatibility; common keys include category, command, elapsedMs, targetHash.",
69
+ "description": "Kind-specific payload — always an object, never a bare string. Free-form for forward compatibility; common keys include errorPreview, command, commandHash, targetHash, editCount, failureCount, exitCode, elapsedMs.",
55
70
  "additionalProperties": true
56
71
  }
57
72
  }
@@ -87,10 +87,12 @@ Options:
87
87
  /**
88
88
  * Pure: parse stable AC IDs (AC-<n>) out of an acceptance-table section.
89
89
  * AC authoring style is "Acceptance Table — Markdown table whose first
90
- * column is the AC ID" — see ACCEPTANCE_SPEC_SYSTEM_PROMPT in
91
- * epic-plan-spec.js. We scan the entire section with a permissive regex
92
- * because operators are free to format the content however they wish
93
- * around the canonical table.
90
+ * column is the AC ID" — see the Acceptance Spec system prompt in the
91
+ * `epic-plan-spec-author` skill body
92
+ * (`.agents/skills/core/epic-plan-spec-author/SKILL.md`), the sole home for
93
+ * that prompt. We scan the entire section with a permissive regex because
94
+ * operators are free to format the content however they wish around the
95
+ * canonical table.
94
96
  *
95
97
  * Returns IDs **in document order**, deduplicated, normalised to
96
98
  * upper-case (`AC-7`, not `ac-7`).