mandrel 1.87.0 → 1.89.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/README.md +18 -13
- package/.agents/audit-checklists/architecture.md +24 -0
- package/.agents/audit-checklists/clean-code.md +24 -0
- package/.agents/audit-checklists/dependencies.md +14 -0
- package/.agents/audit-checklists/devops.md +17 -0
- package/.agents/audit-checklists/documentation.md +22 -0
- package/.agents/audit-checklists/lighthouse.md +15 -0
- package/.agents/audit-checklists/navigability.md +14 -0
- package/.agents/audit-checklists/performance.md +22 -0
- package/.agents/audit-checklists/privacy.md +21 -0
- package/.agents/audit-checklists/quality.md +18 -0
- package/.agents/audit-checklists/security.md +22 -0
- package/.agents/audit-checklists/seo.md +16 -0
- package/.agents/audit-checklists/sre.md +24 -0
- package/.agents/audit-checklists/ux-ui.md +21 -0
- package/.agents/docs/SDLC.md +63 -16
- package/.agents/docs/configuration.md +5 -3
- package/.agents/instructions.md +51 -21
- package/.agents/personas/architect.md +10 -7
- package/.agents/personas/engineer.md +4 -3
- package/.agents/personas/project-manager.md +5 -2
- package/.agents/personas/refactorer.md +5 -3
- package/.agents/rules/git-conventions.md +77 -0
- package/.agents/schemas/agentrc.schema.json +16 -4
- package/.agents/schemas/audit-rules.json +16 -2
- package/.agents/schemas/audit-rules.schema.json +7 -6
- package/.agents/schemas/lifecycle/merge.unlanded.schema.json +38 -0
- package/.agents/schemas/signal-event.schema.json +28 -13
- package/.agents/scripts/acceptance-spec-reconciler.js +6 -4
- package/.agents/scripts/check-context-budget.js +320 -0
- package/.agents/scripts/diagnose-friction.js +4 -4
- package/.agents/scripts/epic-audit-prepare.js +30 -2
- package/.agents/scripts/epic-audit-recheck.js +46 -13
- package/.agents/scripts/epic-deliver-prepare.js +80 -8
- package/.agents/scripts/epic-plan-spec.js +4 -8
- package/.agents/scripts/generate-lens-checklists.js +180 -0
- package/.agents/scripts/lib/audit-suite/checklist-threading.js +300 -0
- package/.agents/scripts/lib/audit-suite/findings.js +27 -0
- package/.agents/scripts/lib/audit-suite/index.js +9 -0
- package/.agents/scripts/lib/audit-suite/lens-checklist.js +212 -0
- package/.agents/scripts/lib/audit-suite/selector.js +136 -5
- package/.agents/scripts/lib/checks/loop-health.js +340 -0
- package/.agents/scripts/lib/cli-args.js +8 -0
- package/.agents/scripts/lib/config/explain.js +4 -0
- package/.agents/scripts/lib/config/runners.js +21 -2
- package/.agents/scripts/lib/config/temp-paths.js +24 -0
- package/.agents/scripts/lib/config-settings-schema-delivery.js +23 -3
- package/.agents/scripts/lib/config-settings-schema-quality.js +7 -0
- package/.agents/scripts/lib/doc-tiers.js +291 -0
- package/.agents/scripts/lib/epic-body-sections.js +5 -2
- package/.agents/scripts/lib/epic-merge-lock.js +83 -0
- package/.agents/scripts/lib/epic-plan-clarity.js +3 -1
- package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +66 -20
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +395 -86
- package/.agents/scripts/lib/feedback-loop/memory-freshness.js +299 -72
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +438 -0
- package/.agents/scripts/lib/gates/friction.js +15 -5
- package/.agents/scripts/lib/observability/perf-aggregator.js +30 -104
- package/.agents/scripts/lib/observability/perf-report-readers.js +1 -1
- package/.agents/scripts/lib/observability/signal-validator.js +204 -0
- package/.agents/scripts/lib/observability/signals-writer.js +157 -54
- package/.agents/scripts/lib/observability/tool-trace-hook.js +42 -4
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +1 -1
- package/.agents/scripts/lib/orchestration/code-review.js +74 -4
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +213 -0
- package/.agents/scripts/lib/orchestration/doc-reader.js +4 -96
- package/.agents/scripts/lib/orchestration/docs-digest.js +34 -0
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +56 -19
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +22 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +188 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +6 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +69 -8
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +11 -2
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +47 -61
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +39 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +144 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +258 -14
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +6 -0
- package/.agents/scripts/lib/orchestration/merge-block-class.js +218 -0
- package/.agents/scripts/lib/orchestration/plan-review-routing.js +1 -1
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +3 -3
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +63 -34
- package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +167 -52
- package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +49 -2
- package/.agents/scripts/lib/orchestration/retro-proposals.js +12 -55
- package/.agents/scripts/lib/orchestration/retro-runner.js +9 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +8 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +419 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +35 -2
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +353 -69
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +66 -4
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +60 -9
- package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +7 -5
- package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +24 -2
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +167 -8
- package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +163 -0
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +20 -9
- package/.agents/scripts/lib/planning-corpus.js +306 -0
- package/.agents/scripts/lib/signals/detectors/common.js +10 -10
- package/.agents/scripts/lib/signals/detectors/index.js +4 -4
- package/.agents/scripts/lib/signals/detectors/retry.js +19 -18
- package/.agents/scripts/lib/signals/detectors/rework.js +1 -1
- package/.agents/scripts/lib/signals/schema.js +56 -81
- package/.agents/scripts/lib/signals/span-tree.js +6 -5
- package/.agents/scripts/lib/story-plan.js +3 -0
- package/.agents/scripts/lib/wave-runner/tick.js +10 -2
- package/.agents/scripts/lifecycle-emit.js +39 -8
- package/.agents/scripts/providers/github/issues.js +12 -1
- package/.agents/scripts/resolve-doc-tiers.js +83 -0
- package/.agents/scripts/retro-run.js +51 -0
- package/.agents/scripts/signals-view.js +1 -1
- package/.agents/scripts/single-story-close.js +20 -1
- package/.agents/scripts/standalone-feedback-rollup.js +188 -0
- package/.agents/scripts/story-close.js +48 -0
- package/.agents/scripts/story-plan.js +51 -12
- package/.agents/scripts/validate-docs-freshness.js +69 -15
- package/.agents/skills/core/documentation-and-adrs/SKILL.md +58 -0
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +5 -3
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +20 -7
- package/.agents/skills/core/scope-triage/SKILL.md +61 -0
- package/.agents/skills/skills.index.json +3 -3
- package/.agents/workflows/audit-documentation.md +82 -2
- package/.agents/workflows/helpers/code-review.md +193 -44
- package/.agents/workflows/helpers/deliver-epic.md +128 -39
- package/.agents/workflows/helpers/deliver-stories.md +26 -0
- package/.agents/workflows/helpers/epic-audit.md +116 -283
- package/.agents/workflows/helpers/epic-deliver-story.md +14 -0
- package/.agents/workflows/helpers/epic-plan-decompose.md +18 -200
- package/.agents/workflows/helpers/epic-plan-spec.md +18 -180
- package/.agents/workflows/helpers/plan-epic.md +141 -105
- package/.agents/workflows/helpers/plan-story.md +32 -0
- package/.agents/workflows/helpers/single-story-deliver.md +43 -0
- package/.agents/workflows/loops/nightly-audit.md +9 -7
- package/docs/CHANGELOG.md +29 -0
- package/lib/cli/doctor.js +44 -0
- package/package.json +4 -3
- package/.agents/scripts/epic-plan-spec-validate.js +0 -111
- package/.agents/scripts/lib/feedback-loop/code-review-graduator.js +0 -207
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +0 -58
- package/.agents/scripts/lib/signals/detectors/hotspot.js +0 -292
|
@@ -64,7 +64,7 @@ envelope via
|
|
|
64
64
|
|
|
65
65
|
It is an **input-only** signal: it changes *how thorough* the review is, never
|
|
66
66
|
the findings envelope (`{ status, severity, posted, report, halted,
|
|
67
|
-
blockerReason }`) nor the posted `
|
|
67
|
+
blockerReason }`) nor the posted `verification-results` structured-comment body. An
|
|
68
68
|
absent or malformed `depth` is treated as `standard`, so an Epic that skipped
|
|
69
69
|
`/plan` still gets a passing review with no new failure mode.
|
|
70
70
|
|
|
@@ -117,13 +117,82 @@ The pipeline will:
|
|
|
117
117
|
- Run a focused lint check on the change set.
|
|
118
118
|
- Post a structured summary report to the `[TICKET_ID]` issue.
|
|
119
119
|
|
|
120
|
+
### Step 1a — Story-scope local-lens pass (`scope: story` only, Epic #4405)
|
|
121
|
+
|
|
122
|
+
When `scope === 'story'`, the shared review spine
|
|
123
|
+
[`runStoryReviewCore`](../../scripts/lib/orchestration/story-close/phases/code-review.js)
|
|
124
|
+
runs a **shift-left local-lens pass** in the same close subprocess, *before*
|
|
125
|
+
returning the review envelope. It:
|
|
126
|
+
|
|
127
|
+
1. Enumerates the actual Story diff (`baseRef...headRef` via
|
|
128
|
+
`git diff --name-only`).
|
|
129
|
+
2. Selects the **local-tier** lenses that own a concern decidable from a single
|
|
130
|
+
Story's diff — `resolveLensTier(lens) === 'local'` **plus** the pure
|
|
131
|
+
`matchesAnyFilePattern` matcher against the diff (the audit-suite SDK's
|
|
132
|
+
[`selectLocalLenses`](../../scripts/lib/audit-suite/selector.js)). This is
|
|
133
|
+
deliberately **not** `selectAudits`: `selectAudits` unions in keyword and
|
|
134
|
+
gate matches and has no per-tier gate, so it would widen the roster past the
|
|
135
|
+
footprint-matched local set this tier owns.
|
|
136
|
+
3. Materializes the matched roster at **`light`** depth
|
|
137
|
+
(`STORY_SCOPE_LENS_DEPTH`) via `runAuditSuite`, surfacing the outcome on the
|
|
138
|
+
review envelope's `localLensReview` field.
|
|
139
|
+
|
|
140
|
+
A diff that matches no local lens adds **no** lens work (the roster is empty and
|
|
141
|
+
`runAuditSuite` is never invoked). The pass is advisory and best-effort: a git
|
|
142
|
+
or materialization failure degrades to a skipped envelope and never blocks the
|
|
143
|
+
close.
|
|
144
|
+
|
|
145
|
+
Both close entry points —
|
|
146
|
+
[`runStoryCodeReview`](../../scripts/lib/orchestration/story-close/phases/code-review.js)
|
|
147
|
+
(Epic-attached) and
|
|
148
|
+
[`runStoryScopeReview`](../../scripts/lib/orchestration/single-story-close/phases/code-review.js)
|
|
149
|
+
(standalone) — reach this pass through the single `runStoryReviewCore` spine, so
|
|
150
|
+
standalone Stories gain local-lens coverage for the first time. Because the pass
|
|
151
|
+
lives inside the close subprocess (invoked after the delivering child exits), it
|
|
152
|
+
honors the maker-blind invariant above: a maker never runs its own local-lens
|
|
153
|
+
review. This step does not apply to `scope: epic`, whose lens roster is the
|
|
154
|
+
cumulative + global + risk-routed set resolved at Epic close (Step 1b).
|
|
155
|
+
|
|
156
|
+
### Step 1b — Epic-close lens roster (`scope: epic` only, Epic #4405)
|
|
157
|
+
|
|
158
|
+
When `scope === 'epic'`, the cumulative Epic diff is walked **once** at close:
|
|
159
|
+
the Epic-close lens roster is executed as **dimensions of this same review
|
|
160
|
+
pass**, not as a separate Phase 4 walk (Story #4412 folded the standalone
|
|
161
|
+
epic-audit lens walk into this pass). Resolve and walk the roster inline:
|
|
162
|
+
|
|
163
|
+
1. Resolve the roster via [`helpers/epic-audit.md`](epic-audit.md) Step 1 —
|
|
164
|
+
run `epic-audit-prepare.js --gate gate3` and take its **`epicCloseLenses`**
|
|
165
|
+
field: the slim roster of cumulative + global + risk-routed lenses, with
|
|
166
|
+
every local-tier change-set lens excluded (routed off `resolveLensTier` in
|
|
167
|
+
[`selectEpicCloseLenses`](../../scripts/lib/orchestration/code-review.js)).
|
|
168
|
+
Local-tier concerns are already verified shift-left (write-time checklists +
|
|
169
|
+
the Story-scope local-lens pass), so they are **not** re-run here.
|
|
170
|
+
2. Materialize each rostered lens via
|
|
171
|
+
[`runAuditSuite`](../../scripts/lib/audit-suite/index.js) at the envelope's
|
|
172
|
+
`depth`, applying the `{{changedFiles}}` substitution, and walk each lens's
|
|
173
|
+
`.agents/workflows/audit-<lens>.md` procedure over the cumulative
|
|
174
|
+
`main..epic/<epicId>` diff. Global lenses (`globalLenses`, e.g.
|
|
175
|
+
`audit-navigability`) run against the WHOLE route tree, exempt from the
|
|
176
|
+
change-set narrowing.
|
|
177
|
+
3. Fold the lens findings into this pass's severity aggregate **alongside** the
|
|
178
|
+
Step 2 review pillars — one walk of the cumulative diff, one aggregate, one
|
|
179
|
+
`verification-results` comment (Step 4). An empty `epicCloseLenses` roster
|
|
180
|
+
(docs-only, or every selected lens already covered shift-left, and no
|
|
181
|
+
risk-routed lens) adds no lens dimension — the pillars still run.
|
|
182
|
+
|
|
183
|
+
This step does not apply to `scope: story` (its roster is the local-tier set of
|
|
184
|
+
Step 1a) and is skipped when `epic-audit-prepare.js` returns `degraded: true`
|
|
185
|
+
(propagate the reason and STOP, per `epic-audit.md`).
|
|
186
|
+
|
|
120
187
|
## Step 2 — Review Pillars
|
|
121
188
|
|
|
122
189
|
For each changed file, execute a strict review against four pillars. The
|
|
123
190
|
second pillar (**Integration Review**) deliberately defers the security /
|
|
124
|
-
performance / quality / coverage sweeps to the change-set-scoped
|
|
125
|
-
|
|
126
|
-
|
|
191
|
+
performance / quality / coverage sweeps to the change-set-scoped lenses — at
|
|
192
|
+
`scope: story` those ran shift-left in the Story-scope local-lens pass (Step
|
|
193
|
+
1a); at `scope: epic` they run inline in this same pass as the Epic-close lens
|
|
194
|
+
roster (Step 1b). Re-walking those sweeps a second time in this pillar is
|
|
195
|
+
duplication, not defense-in-depth.
|
|
127
196
|
|
|
128
197
|
**Apply the `depth` lever** (see **Review depth** above) to how hard you walk
|
|
129
198
|
these pillars: at `light`, focus on Pillar 1 and reduce Pillars 2–3 to a quick
|
|
@@ -151,11 +220,11 @@ The integration view depends on `scope`. The diff under review is always
|
|
|
151
220
|
against differs:
|
|
152
221
|
|
|
153
222
|
- **`scope: story`** — the diff is `epic/<epicId>..story-<storyId>` (i.e.
|
|
154
|
-
one Story's contribution to the Epic).
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
cross-Task ripple within the Story and
|
|
158
|
-
branch tip. Look for:
|
|
223
|
+
one Story's contribution to the Epic). The Story-scope local-lens pass
|
|
224
|
+
(Step 1a) has already covered the local-tier concerns; the Epic-close lens
|
|
225
|
+
roster has not run for this change set (it runs once at Epic close). The
|
|
226
|
+
integration view here focuses on cross-Task ripple within the Story and
|
|
227
|
+
contract drift against the Epic branch tip. Look for:
|
|
159
228
|
- Cross-Task contract drift inside the Story (one Task's API change vs.
|
|
160
229
|
another Task's caller in the same branch).
|
|
161
230
|
- Shared-module ripple effects from this Story onto siblings already
|
|
@@ -163,30 +232,31 @@ against differs:
|
|
|
163
232
|
- Spec deviations that the per-Task commits papered over.
|
|
164
233
|
|
|
165
234
|
- **`scope: epic`** — the diff is `main..epic/<epicId>` (the cumulative
|
|
166
|
-
Epic change set).
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
235
|
+
Epic change set). The Epic-close lens roster (`epicCloseLenses`) is walked
|
|
236
|
+
**inline as part of this pass** (Step 1b) — the cumulative diff is read once,
|
|
237
|
+
and the security, privacy, performance, code-quality, and test-coverage
|
|
238
|
+
findings the rostered lenses produce feed this pass's aggregate directly.
|
|
239
|
+
There is no separate `audit-results` comment to read (Story #4412 retired it);
|
|
240
|
+
the lens findings and the pillar findings share the single
|
|
241
|
+
`verification-results` comment this pass posts.
|
|
173
242
|
|
|
174
243
|
The integration view at epic scope is what the per-lens audits cannot
|
|
175
244
|
produce because each lens runs in isolation:
|
|
176
245
|
|
|
177
|
-
- Cross-reference 🔴 / 🟠
|
|
246
|
+
- Cross-reference 🔴 / 🟠 lens findings against the spec deviations
|
|
178
247
|
flagged in Pillar 1 — a finding that traces back to a deliberate
|
|
179
248
|
Tech-Spec decision is different from one that traces back to an
|
|
180
249
|
oversight.
|
|
181
250
|
- Look for cross-cutting concerns no single lens owns: contract drift
|
|
182
251
|
between Stories, shared-module ripple effects, boundary changes that
|
|
183
252
|
thread security and performance implications together.
|
|
184
|
-
- Note any
|
|
253
|
+
- Note any lens finding that the operator's remediation flow should
|
|
185
254
|
bundle (e.g. one refactor closes findings from multiple lenses).
|
|
186
255
|
|
|
187
|
-
If the Epic
|
|
188
|
-
|
|
189
|
-
|
|
256
|
+
If the Epic-close roster is empty (docs-only Epic, every selected lens
|
|
257
|
+
already covered shift-left, or the pass was skipped via
|
|
258
|
+
`--skip-epic-audit` / `--skip-code-review`), record that explicitly in the
|
|
259
|
+
findings report and proceed — there is no lens dimension to integrate.
|
|
190
260
|
|
|
191
261
|
### Pillar 3: Documentation Integrity
|
|
192
262
|
|
|
@@ -261,9 +331,11 @@ prior baseline before merging.
|
|
|
261
331
|
|
|
262
332
|
## Step 4 — Produce Findings Report
|
|
263
333
|
|
|
264
|
-
Findings are **persisted as a `
|
|
265
|
-
`[TICKET_ID]` issue** by `runCodeReview
|
|
266
|
-
|
|
334
|
+
Findings are **persisted as a `verification-results` structured comment on
|
|
335
|
+
the `[TICKET_ID]` issue** by `runCodeReview` (the unified findings contract of
|
|
336
|
+
Story #4411; at `scope: epic` this single comment also carries the Step 1b
|
|
337
|
+
Epic-close lens findings). The target ticket is the Story when
|
|
338
|
+
`scope === 'story'` and the Epic when `scope === 'epic'`. The comment
|
|
267
339
|
is idempotent — re-runs replace the prior one — and its body includes
|
|
268
340
|
severity-tier counts plus the full findings list so downstream workflows
|
|
269
341
|
(notably the retro helper) can summarise blockers/high findings without
|
|
@@ -293,12 +365,64 @@ For every finding, provide:
|
|
|
293
365
|
fix worked. Keep it tight (≤ 5 sentences); the sub-agent will read the
|
|
294
366
|
surrounding code itself.
|
|
295
367
|
|
|
368
|
+
### The `## Fixed on-branch` section (Story #4399)
|
|
369
|
+
|
|
370
|
+
Findings that Step 4.5 remediated on `[HEAD_REF]` MUST be rendered under a
|
|
371
|
+
dedicated **`## Fixed on-branch`** heading, **not** in the severity groups
|
|
372
|
+
above. This is the contract seam that keeps remediated findings from
|
|
373
|
+
spawning ghost follow-up issues: the
|
|
374
|
+
[audit-results graduator](../../scripts/lib/feedback-loop/audit-results-graduator.js)
|
|
375
|
+
(the sole canonical reader of the unified comment)
|
|
376
|
+
skips every entry inside this section (both because a fixed entry is
|
|
377
|
+
rendered with a **✅ prefix** — so it carries no leading severity emoji the
|
|
378
|
+
parser would match — and because the parser has an explicit
|
|
379
|
+
Fixed-on-branch section guard).
|
|
380
|
+
|
|
381
|
+
Render each fixed finding as a `✅`-prefixed line naming its original
|
|
382
|
+
severity, the file path in backticks, and the remediating commit SHA, e.g.:
|
|
383
|
+
|
|
384
|
+
```markdown
|
|
385
|
+
## Fixed on-branch
|
|
386
|
+
|
|
387
|
+
- ✅ 🟡 Medium: `src/lib/foo.js` — missing edge-case guard added (a1b2c3d)
|
|
388
|
+
- ✅ 🟠 High: `src/api/users.js` — ownership check added (d4e5f6a)
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Open (escalated / unfixed) findings stay in their severity group with
|
|
392
|
+
their leading severity emoji so the graduator still files them.
|
|
393
|
+
|
|
296
394
|
## Step 4.5 — Focused-fix Routing (host LLM, no automated loop)
|
|
297
395
|
|
|
298
396
|
There is **no runtime auto-fix function** at this phase. The host LLM is
|
|
299
|
-
the executor:
|
|
300
|
-
|
|
301
|
-
|
|
397
|
+
the executor: it decides, per finding, between a focused fix on
|
|
398
|
+
`[HEAD_REF]` and leaving the finding on the `verification-results`
|
|
399
|
+
structured comment for the operator.
|
|
400
|
+
|
|
401
|
+
### Resolve the remediation threshold (Story #4399)
|
|
402
|
+
|
|
403
|
+
Read `delivery.codeReview.autoFixSeverity` from the resolved `.agentrc.json`
|
|
404
|
+
(default **`medium`**; the resolver in
|
|
405
|
+
[`config/runners.js`](../../scripts/lib/config/runners.js) supplies the
|
|
406
|
+
default when the key is absent). The threshold governs **which severities
|
|
407
|
+
route into on-branch remediation** — it never changes the halting rule (a
|
|
408
|
+
surviving 🔴 still stops) or the escalation classes:
|
|
409
|
+
|
|
410
|
+
- **`medium`** (default) — route 🔴 Critical, 🟠 High, **and 🟡 Medium**
|
|
411
|
+
findings into remediation. 🟢 Suggestions stay on the comment (never
|
|
412
|
+
auto-fixed).
|
|
413
|
+
- **`high`** — route only 🔴 Critical and 🟠 High findings, reproducing
|
|
414
|
+
the pre-4399 behavior exactly. 🟡 Medium and 🟢 Suggestion findings stay
|
|
415
|
+
on the comment.
|
|
416
|
+
|
|
417
|
+
Hard cutover per
|
|
418
|
+
[`rules/git-conventions.md`](../../rules/git-conventions.md) § Contract
|
|
419
|
+
Cutovers — no back-compat flag; `high` is opt-in to the old routing.
|
|
420
|
+
|
|
421
|
+
### 🔴 / 🟠 findings — per-finding ceremony (unchanged)
|
|
422
|
+
|
|
423
|
+
For each 🔴 / 🟠 finding from Step 4, decide between two paths and keep the
|
|
424
|
+
`verification-results` structured comment authoritative for anything not fixed
|
|
425
|
+
in-place.
|
|
302
426
|
|
|
303
427
|
1. **Apply a focused fix on `[HEAD_REF]`.** Permitted only when the
|
|
304
428
|
finding is unambiguously *fixable* (clean remediation, no scope
|
|
@@ -328,27 +452,52 @@ anything not fixed in-place.
|
|
|
328
452
|
attempt (the equivalent of the prior loop's
|
|
329
453
|
`validation-regression` / `thrash-detected` exits).
|
|
330
454
|
|
|
455
|
+
### 🟡 Medium findings — batched per-lens ceremony (only when `autoFixSeverity: medium`)
|
|
456
|
+
|
|
457
|
+
When the threshold is `medium`, remediate the fixable 🟡 Medium findings in
|
|
458
|
+
a **batch keyed by owning review lens/pillar** rather than the per-finding
|
|
459
|
+
ceremony above:
|
|
460
|
+
|
|
461
|
+
1. Group the fixable Mediums by owning lens (the pillar or audit family
|
|
462
|
+
that produced them). A Medium is fixable on the same terms as a 🟠; a
|
|
463
|
+
Medium in any escalation class stays on the comment exactly like a 🟠.
|
|
464
|
+
2. For each lens, call `assert-branch.js --expected [HEAD_REF]`, stage
|
|
465
|
+
explicit paths only, and make **one focused conventional commit per
|
|
466
|
+
lens** (`fix(<scope>): <description> (review findings batch)`).
|
|
467
|
+
3. Bounded-attempt semantics extend to the batch: each finding gets **at
|
|
468
|
+
most one** attempt, and a lens's batch commit that would exceed
|
|
469
|
+
`delivery.codeReview.maxFixScopeFiles` routes that lens's findings to
|
|
470
|
+
escalation (`scope-exceeded`) instead of committing.
|
|
471
|
+
4. After **all** lens batches are committed, run a **single** validation
|
|
472
|
+
pass (`npm run lint` plus the relevant `npm test` slice) and a
|
|
473
|
+
**single** targeted rescan over the touched files. Surviving batched
|
|
474
|
+
findings stay on the comment for Step 5.
|
|
475
|
+
|
|
476
|
+
Record every remediated finding (🟠 or 🟡) in the **"Fixed on-branch"**
|
|
477
|
+
section of the `verification-results` comment (Step 4) so it does not graduate
|
|
478
|
+
to a follow-up issue.
|
|
479
|
+
|
|
331
480
|
Do not invent a programmatic retry budget. The host LLM applies *at most
|
|
332
|
-
one* focused-fix attempt per finding
|
|
333
|
-
Escalated findings remain on the `code-review`
|
|
334
|
-
their reason recorded, so Step 5 (and downstream
|
|
335
|
-
why each one was not auto-remediated.
|
|
481
|
+
one* focused-fix attempt per finding (or per batched finding) before
|
|
482
|
+
escalating to the operator. Escalated findings remain on the `code-review`
|
|
483
|
+
structured comment with their reason recorded, so Step 5 (and downstream
|
|
484
|
+
consumers) see exactly why each one was not auto-remediated.
|
|
336
485
|
|
|
337
486
|
## Step 4.6 — Cross-phase re-check trigger
|
|
338
487
|
|
|
339
488
|
After the focused-fix routing in Step 4.5 completes, any host-LLM-applied
|
|
340
|
-
fix commits have modified files on `[HEAD_REF]` that the
|
|
341
|
-
|
|
342
|
-
of one or more lenses (e.g. a fix landing in `**/auth/*.js`
|
|
343
|
-
`audit-security` lens). When that happens, the
|
|
344
|
-
|
|
345
|
-
non-overlapping findings remain authoritative and MUST NOT be
|
|
489
|
+
fix commits have modified files on `[HEAD_REF]` that the Epic-close lens
|
|
490
|
+
roster already walked (Step 1b). Some of those edits may overlap the
|
|
491
|
+
`filePatterns` of one or more lenses (e.g. a fix landing in `**/auth/*.js`
|
|
492
|
+
overlaps the `audit-security` lens). When that happens, the lens findings in
|
|
493
|
+
the `verification-results` comment are **stale for the overlapping lenses
|
|
494
|
+
only** — the non-overlapping findings remain authoritative and MUST NOT be
|
|
346
495
|
re-derived.
|
|
347
496
|
|
|
348
497
|
> **Scope note.** This cross-phase re-check applies only when
|
|
349
|
-
> `scope === 'epic'`. Story-scope reviews
|
|
350
|
-
>
|
|
351
|
-
>
|
|
498
|
+
> `scope === 'epic'`. Story-scope reviews carry no Epic-close lens roster,
|
|
499
|
+
> so there is nothing to invalidate; skip this step entirely for
|
|
500
|
+
> `scope === 'story'`.
|
|
352
501
|
|
|
353
502
|
Invoke the re-check selector with the cumulative set of paths touched by
|
|
354
503
|
the focused-fix commits:
|
|
@@ -369,11 +518,11 @@ no-op.
|
|
|
369
518
|
When `selectedAudits` is non-empty:
|
|
370
519
|
|
|
371
520
|
1. Re-invoke each listed lens prompt under
|
|
372
|
-
[`../audit-*.md`](../) the same way
|
|
521
|
+
[`../audit-*.md`](../) the same way the Step 1b Epic-close walk does —
|
|
373
522
|
one lens at a time, against the current `[HEAD_REF]` tip.
|
|
374
523
|
2. **Append** a `## Cross-phase re-check` section to the **existing**
|
|
375
|
-
`
|
|
376
|
-
a new comment; the comment is idempotent and downstream consumers
|
|
524
|
+
`verification-results` structured comment on the Epic ticket. Do **not**
|
|
525
|
+
post a new comment; the comment is idempotent and downstream consumers
|
|
377
526
|
(the code-review trim, `/deliver` Pillar 2, the retro helper)
|
|
378
527
|
read it once. The append carries the re-checked lens names, the new
|
|
379
528
|
findings (if any), and the focused-fix commit SHAs that triggered the
|
|
@@ -383,7 +532,7 @@ When `selectedAudits` is non-empty:
|
|
|
383
532
|
through Step 4.5's focused-fix routing. Findings that already
|
|
384
533
|
received a focused-fix attempt in the first pass do not get a fresh
|
|
385
534
|
attempt when the cross-phase re-check resurfaces an adjacent one —
|
|
386
|
-
leave them on the `
|
|
535
|
+
leave them on the `verification-results` comment for the operator.
|
|
387
536
|
|
|
388
537
|
If `selectedAudits` is empty, skip silently and proceed to Step 5. The
|
|
389
538
|
re-check trigger is **read-only signal** — it never mutates the Epic
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: >-
|
|
3
3
|
Drive an Epic from `agent::ready` to a merged pull request against `main`.
|
|
4
|
-
The ten-phase flow runs the wave loop, close-validation,
|
|
5
|
-
|
|
4
|
+
The ten-phase flow runs the wave loop, close-validation, the Epic-close
|
|
5
|
+
lens-roster resolve, the code-review pass (which walks the cumulative diff
|
|
6
|
+
once with the lens roster folded in), retro, finalize, watch-and-iterate,
|
|
7
|
+
conditional auto-merge,
|
|
6
8
|
and local branch cleanup. When the run is end-to-end clean (zero manual
|
|
7
9
|
interventions, zero 🔴/🟠 review findings, compact retro) the PR
|
|
8
10
|
auto-merges via `gh pr merge --squash --delete-branch`; otherwise the
|
|
@@ -36,8 +38,8 @@ back to the operator-merges-button path.
|
|
|
36
38
|
→ Phase 1 — prepare (epic-deliver-prepare.js)
|
|
37
39
|
→ Phase 2 — ready-set loop (wave-tick.js → dispatch ready set → observe → re-tick)
|
|
38
40
|
→ Phase 3 — close-validation (lint + test + ratchets on epic/<id>)
|
|
39
|
-
→ Phase 4 — epic-
|
|
40
|
-
→ Phase 5 — code-review (helpers/code-review.md
|
|
41
|
+
→ Phase 4 — epic-close lens roster (helpers/epic-audit.md — resolve the slim cumulative+global+risk-routed roster)
|
|
42
|
+
→ Phase 5 — code-review (helpers/code-review.md scope: epic — walks the cumulative diff once, lens roster folded in)
|
|
41
43
|
→ Phase 6 — retro (.agents/scripts/lib/orchestration/retro-runner.js)
|
|
42
44
|
→ Phase 6.5 — integration gate (whole-product navigability + journey suite; @pending ≠ green for surface-adding Epics — blocks finalize)
|
|
43
45
|
→ Phase 7 — finalize (lifecycle-emit → epic.close.end → open PR to main)
|
|
@@ -81,6 +83,19 @@ spawned.
|
|
|
81
83
|
Every other runtime modifier is sourced from the Epic's labels or from
|
|
82
84
|
`delivery.deliverRunner` in `.agentrc.json`.
|
|
83
85
|
|
|
86
|
+
- `--yes` — the top-level `/deliver` unattended flag (suppresses the
|
|
87
|
+
operator confirmation gate; see [`deliver.md`](../deliver.md)). This
|
|
88
|
+
helper reads it as the **headless signal** for Phase 8.5's must-land
|
|
89
|
+
terminal step (Story #4427): when `/deliver` was invoked with `--yes`,
|
|
90
|
+
thread `--headless true` into the Phase 8.5
|
|
91
|
+
`epic.automerge.start` `lifecycle-emit.js` call (omit the flag, or
|
|
92
|
+
pass `--headless false`, for an attended run). The runtime reads this
|
|
93
|
+
as an explicit `headless` input threaded through
|
|
94
|
+
`runLifecycleEmit` → `buildDefaultListenerChain` →
|
|
95
|
+
`MergeWatcher({ headless })` — never an ambient/undefined global.
|
|
96
|
+
Attended-mode behavior (no `--headless` flag) is byte-for-byte
|
|
97
|
+
unchanged: `MergeWatcher` defaults `headless` to `false`.
|
|
98
|
+
|
|
84
99
|
---
|
|
85
100
|
|
|
86
101
|
## Contract
|
|
@@ -178,9 +193,14 @@ Validates `type::epic`, enumerates `type::story` descendants, parses
|
|
|
178
193
|
checkpoint in the per-Story-status shape (a flat `stories` map seeded at
|
|
179
194
|
`pending`, plus the global `concurrencyCap`). Treat the printed JSON as
|
|
180
195
|
`state`: `{ epicId, storyCount, concurrencyCap, stories, checkpointInitializedAt, docsDigestPath }`.
|
|
181
|
-
`stories` is the flat dispatch hint (`{ storyId, worktree, title
|
|
182
|
-
Story); the ready-set `tick` (Phase 2) decides
|
|
183
|
-
beat. `
|
|
196
|
+
`stories` is the flat dispatch hint (`{ storyId, worktree, title,
|
|
197
|
+
checklistPath }` per open Story); the ready-set `tick` (Phase 2) decides
|
|
198
|
+
which to dispatch on each beat. `checklistPath` is the repo-relative path to
|
|
199
|
+
that Story's footprint-matched local-lens authoring checklist
|
|
200
|
+
(`temp/epic-<epicId>/checklists/story-<storyId>.md`, Story #4410) — thread it
|
|
201
|
+
into that child's prompt (§ 2b, item 7); it is `null` when the Story's
|
|
202
|
+
predicted footprint matched no local lens. `docsDigestPath` is the
|
|
203
|
+
repo-relative path to the per-Epic docs digest
|
|
184
204
|
(`temp/epic-<epicId>/docs-digest.md`) that prepare writes from
|
|
185
205
|
`project.docsContextFiles` — thread it into every child prompt (§ 2b, item 6).
|
|
186
206
|
It is `null` when the project configured no `docsContextFiles` (no digest is
|
|
@@ -369,7 +389,14 @@ digest instead of re-reading the full `project.docsContextFiles` set,
|
|
|
369
389
|
and to pull individual docs files on demand (per
|
|
370
390
|
[`.agents/instructions.md` § 3](../../instructions.md)). When
|
|
371
391
|
`docsDigestPath` is null (the project configured no `docsContextFiles`),
|
|
372
|
-
say so — the child then has no per-Story docs mandate.
|
|
392
|
+
say so — the child then has no per-Story docs mandate. (7) pass the
|
|
393
|
+
**checklist path** — the `checklistPath` field from that Story's entry in
|
|
394
|
+
the `stories` dispatch hint (Story #4410), which points at
|
|
395
|
+
`temp/epic-<epicId>/checklists/story-<storyId>.md`. Instruct the child to
|
|
396
|
+
read that file (footprint-matched local-lens authoring checklists) and
|
|
397
|
+
self-check its change against those concerns while writing. When
|
|
398
|
+
`checklistPath` is null (the Story's predicted footprint matched no local
|
|
399
|
+
lens), there is nothing to read. The pairing of
|
|
373
400
|
`story.heartbeat` and `agent::blocked` is what lets the § 2d Idle
|
|
374
401
|
Watchdog distinguish a working child from a dead one; a silent child
|
|
375
402
|
with no recent heartbeat and no blocker label is the failure mode the
|
|
@@ -473,51 +500,61 @@ the Epic branch; if any drifts, refresh and commit
|
|
|
473
500
|
|
|
474
501
|
---
|
|
475
502
|
|
|
476
|
-
## Phase 4 — Epic
|
|
503
|
+
## Phase 4 — Epic-close lens roster
|
|
477
504
|
|
|
478
505
|
Skip when `--skip-epic-audit`. Otherwise auto-invoke
|
|
479
|
-
[`helpers/epic-audit.md`](epic-audit.md) inline
|
|
506
|
+
[`helpers/epic-audit.md`](epic-audit.md) inline to **resolve** (not walk) the
|
|
507
|
+
slim Epic-close lens roster. The helper runs
|
|
480
508
|
[`epic-audit-prepare.js`](../../scripts/epic-audit-prepare.js) to ask the
|
|
481
509
|
[`selectAudits`](../../scripts/lib/audit-suite/index.js) SDK which lenses fire
|
|
482
510
|
at the `gate3` close gate, **unions in the model-judged risk-routed lenses**
|
|
483
511
|
(Story #3889 — `epic-audit-prepare.js` reads the Epic's `planningRisk`
|
|
484
512
|
envelope off the `epic-plan-state` checkpoint and maps each high-risk axis to
|
|
485
|
-
its lens via `resolveAuditLenses`), then
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
-
|
|
513
|
+
its lens via `resolveAuditLenses`), then restricts that union to the tiers the
|
|
514
|
+
Epic-close tier owns via
|
|
515
|
+
[`selectEpicCloseLenses`](../../scripts/lib/orchestration/code-review.js): the
|
|
516
|
+
envelope's **`epicCloseLenses`** field is the slim roster of **cumulative +
|
|
517
|
+
global + risk-routed** lenses, with every **local-tier** change-set lens
|
|
518
|
+
excluded (its concern is already verified shift-left at the write-time and
|
|
519
|
+
Story-scope tiers). A high-risk Epic still auto-runs its risk-routed lenses
|
|
520
|
+
even when the change set alone did not select them; a docs-only or
|
|
521
|
+
already-shift-left-covered change set yields an empty roster.
|
|
522
|
+
|
|
523
|
+
**The lens roster is not walked here.** Story #4412 folded the standalone
|
|
524
|
+
Phase 4 lens walk into the Phase 5 code-review pass so the cumulative Epic diff
|
|
525
|
+
is walked **once**. Phase 4 resolves `epicCloseLenses` (plus `depth`,
|
|
526
|
+
`globalLenses`, `substitutionsPayload`) and hands it to Phase 5; there is no
|
|
527
|
+
separate `audit-results` comment. Remediation of the lens findings happens in
|
|
528
|
+
Phase 5 and is **tier-aware** (Story #4412): the Epic-close tier reads
|
|
529
|
+
`delivery.epicAudit.autoFixSeverity` (default **`high`**) and routes only
|
|
530
|
+
🔴 Critical + 🟠 High lens findings into on-branch remediation while 🟡 Medium
|
|
531
|
+
and 🟢 Suggestion findings graduate (🟡 Medium concerns are already remediated
|
|
532
|
+
shift-left); setting `medium` opts back into routing 🔴/🟠/🟡.
|
|
533
|
+
|
|
505
534
|
- **Selector reports `degraded: true`** — STOP. Propagate the
|
|
506
535
|
`reason`/`detail`, post a friction comment, do not fall back to a
|
|
507
536
|
full-roster audit.
|
|
508
|
-
- **`
|
|
509
|
-
|
|
537
|
+
- **`epicCloseLenses` is empty** (docs-only change set, or every selected lens
|
|
538
|
+
already covered shift-left, and no risk-routed lens) — there is no lens
|
|
539
|
+
dimension for Phase 5 to walk; continue to Phase 5, which still runs its
|
|
540
|
+
review pillars.
|
|
510
541
|
|
|
511
542
|
---
|
|
512
543
|
|
|
513
|
-
## Phase 5 — Code review
|
|
544
|
+
## Phase 5 — Code review (cumulative diff walked once)
|
|
514
545
|
|
|
515
546
|
Skip when `--skip-code-review`. Otherwise resolve the **risk-derived review
|
|
516
547
|
depth** for this Epic, then auto-invoke
|
|
517
548
|
[`helpers/code-review.md`](code-review.md) inline (read-only audit)
|
|
518
549
|
with the argument envelope `{ scope: 'epic', ticketId: <epicId>, baseRef:
|
|
519
|
-
'main', headRef: 'epic/<epicId>', depth: <reviewDepth> }
|
|
520
|
-
|
|
550
|
+
'main', headRef: 'epic/<epicId>', depth: <reviewDepth> }`, threading the
|
|
551
|
+
Phase 4 `epicCloseLenses` roster through. The pass walks the cumulative
|
|
552
|
+
`main..epic/<epicId>` diff **once**: it executes the Epic-close lens roster as
|
|
553
|
+
review dimensions (helper Step 1b) **and** the review pillars, folding both
|
|
554
|
+
into a single aggregate. Findings persist as the unified `verification-results`
|
|
555
|
+
structured comment on the Epic (the single findings surface — Story #4411
|
|
556
|
+
unified the former `code-review` and `audit-results` contracts, and Story #4412
|
|
557
|
+
folded the lens walk into this pass).
|
|
521
558
|
|
|
522
559
|
The `depth` is the live epic-scope producer for Story #3876's review-depth
|
|
523
560
|
lever (Story #3937). Resolve it from the Epic's judged risk envelope the same
|
|
@@ -533,8 +570,20 @@ emit so a high-risk Epic gets a deeper adversarial pass and a low-risk one a
|
|
|
533
570
|
lighter one. Depth is **input-only** — it never changes the findings envelope
|
|
534
571
|
or the posted comment shape.
|
|
535
572
|
|
|
536
|
-
|
|
537
|
-
|
|
573
|
+
Remediation in this pass is **tier-aware and split by finding class**
|
|
574
|
+
(Story #4412). The **review-pillar** findings (Pillars 1–4) route off
|
|
575
|
+
`delivery.codeReview.autoFixSeverity` (default **`medium`** — 🔴/🟠/🟡
|
|
576
|
+
on-branch, Mediums batched per lens, 🟢 stays on the comment). The **Epic-close
|
|
577
|
+
lens** findings (Step 1b) route off `delivery.epicAudit.autoFixSeverity`
|
|
578
|
+
(default **`high`** — only 🔴/🟠 on-branch; 🟡 Medium + 🟢 graduate, because
|
|
579
|
+
🟡 Medium concerns were already remediated shift-left). Setting either key to
|
|
580
|
+
its other value re-widens or narrows that class. Remediated findings are
|
|
581
|
+
rendered under the single `verification-results` comment's `## Fixed on-branch`
|
|
582
|
+
section so they never graduate to follow-up issues. The severity gate below is
|
|
583
|
+
**unchanged** — it keys off the surviving (unfixed) findings.
|
|
584
|
+
|
|
585
|
+
- **Any surviving 🔴 Critical Blocker** — STOP. Relay to the operator.
|
|
586
|
+
- **Only 🟠/🟡/🟢 surviving** — log as non-blocking and continue.
|
|
538
587
|
|
|
539
588
|
---
|
|
540
589
|
|
|
@@ -565,6 +614,23 @@ the GitHub upsert succeeds, the retro body is also **mirrored locally** to
|
|
|
565
614
|
`epicRetroMirrorPath`). GitHub remains the source of truth — a
|
|
566
615
|
mirror-write failure only logs a warn and never fails the phase.
|
|
567
616
|
|
|
617
|
+
**Auto-file routed proposals (Story #4418).** Between gathering the retro
|
|
618
|
+
signals and composing the body, the runner files the retro's **actionable
|
|
619
|
+
routed proposals** (the `framework` / `consumer` friction categories that
|
|
620
|
+
recurred ≥ 2× or were force-flagged by an unresolved `agent::blocked`) as
|
|
621
|
+
GitHub follow-up issues via the graduator pre-parsed-findings seam
|
|
622
|
+
([`retro-proposals-graduator.js`](../../scripts/lib/feedback-loop/retro-proposals-graduator.js)).
|
|
623
|
+
Each filed issue carries `meta::<framework-gap|consumer-improvement>` +
|
|
624
|
+
`friction::<category>` labels — the join key `/plan` Phase 0's
|
|
625
|
+
prior-feedback fetcher reads back — and the rendered retro sections then
|
|
626
|
+
list the **real filed issue numbers** instead of paste-ready `gh issue
|
|
627
|
+
create` command stanzas. Filing is idempotent (a content-hash marker probe
|
|
628
|
+
skips already-filed categories) and respects the graduator per-run filing
|
|
629
|
+
cap. The beat runs behind `delivery.feedbackLoop.retroProposals` (default
|
|
630
|
+
**ON**); set it to `false` to suppress auto-filing and fall back to the
|
|
631
|
+
command stanzas. Filing never fails the phase — a filing error degrades to
|
|
632
|
+
the command stanzas.
|
|
633
|
+
|
|
568
634
|
---
|
|
569
635
|
|
|
570
636
|
## Phase 6.5 — Post-wave integration gate (Epic #4131, F1/F4)
|
|
@@ -720,13 +786,36 @@ run. Passing `--epic <epicId>` scopes the red-path failure digest to
|
|
|
720
786
|
## Phase 8.5 — Auto-merge gate
|
|
721
787
|
|
|
722
788
|
After Phase 8 exits 0, evaluate the auto-merge predicate by emitting
|
|
723
|
-
`epic.automerge.start
|
|
789
|
+
`epic.automerge.start`. When this `/deliver` run was invoked with `--yes`,
|
|
790
|
+
add `--headless true` so the downstream `MergeWatcher` engages the
|
|
791
|
+
must-land terminal step (Story #4427, § Arguments above); omit the flag
|
|
792
|
+
(or pass `--headless false`) for an attended run:
|
|
724
793
|
|
|
725
794
|
```bash
|
|
726
795
|
node .agents/scripts/lifecycle-emit.js --epic <epicId> \
|
|
727
|
-
--event epic.automerge.start --pr-url <prUrl>
|
|
796
|
+
--event epic.automerge.start --pr-url <prUrl> [--headless true]
|
|
728
797
|
```
|
|
729
798
|
|
|
799
|
+
**Must-land terminal step (headless only).** `MergeWatcher` polls
|
|
800
|
+
`epic.merge.armed` to confirmation as usual. If its poll budget is
|
|
801
|
+
exhausted, a headless (`--headless true`) run does not exit silently —
|
|
802
|
+
it classifies the block (`classifyMergeBlock`, the shared classifier
|
|
803
|
+
from Story #4426) and applies one bounded retry before giving up:
|
|
804
|
+
|
|
805
|
+
- `checks-pending-timeout` (required checks still progressing) — extend
|
|
806
|
+
the watch budget once and keep polling.
|
|
807
|
+
- `api-race-other` (no definitive block signal) — re-arm once by
|
|
808
|
+
re-emitting `epic.merge.ready` on the bus (never a direct `gh pr merge`
|
|
809
|
+
call — `AutomergeArmer` remains the sole authorized call site).
|
|
810
|
+
- `branch-protection-human-required`, or both bounded retries already
|
|
811
|
+
spent — terminal: emit `merge.unlanded` (`scope: "epic"`, carrying the
|
|
812
|
+
block class) and fall through to the existing single `epic.blocked`
|
|
813
|
+
emit — one blocked path, never a duplicate `agent::blocked` transition.
|
|
814
|
+
|
|
815
|
+
Attended runs (no `--headless` flag) keep today's exact behavior: budget
|
|
816
|
+
exhaustion emits `epic.blocked` immediately, with no classification, no
|
|
817
|
+
retry, and no `merge.unlanded`.
|
|
818
|
+
|
|
730
819
|
`AutomergePredicate` first runs a **live `gh pr checks --required` probe**
|
|
731
820
|
(Story #4361): green required CI is the arming signal, so if any required
|
|
732
821
|
check is red, pending, or the probe is unreadable it emits
|
|
@@ -336,6 +336,32 @@ auto-merge. Report its one-line result in the summary. Merged Story branches
|
|
|
336
336
|
themselves are reaped by the boot sweep at the next `/plan` / `/deliver` boot —
|
|
337
337
|
see [`.agents/rules/git-conventions.md` § Local checkout hygiene](../../rules/git-conventions.md).
|
|
338
338
|
|
|
339
|
+
### Feedback rollup
|
|
340
|
+
|
|
341
|
+
Then run the **end-of-run feedback rollup** over the delivered Story set so
|
|
342
|
+
the write-only standalone signals tree finally has a consumer. Each per-Story
|
|
343
|
+
sub-agent appends `friction` records to its standalone stream
|
|
344
|
+
(`temp/standalone/stories/story-<sid>/signals.ndjson`); the rollup scans those
|
|
345
|
+
streams and aggregates friction by category:
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
node .agents/scripts/standalone-feedback-rollup.js --stories <all delivered story ids, comma-separated>
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
The CLI prints one JSON object
|
|
352
|
+
(`{ kind, stories, totalFriction, byCategory, perStory }`) and **always exits
|
|
353
|
+
0** — missing or empty streams degrade to an empty summary, so this beat never
|
|
354
|
+
fails the run. Surface `totalFriction` and the `byCategory` totals in the run
|
|
355
|
+
summary so the operator sees where friction clustered across the delivery:
|
|
356
|
+
|
|
357
|
+
```text
|
|
358
|
+
Feedback rollup — 4 friction signals across 3 Stories
|
|
359
|
+
Execution Error: 2, Tool Limitation: 1, Missing Skill: 1
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
When `totalFriction` is 0 (or every stream was missing), report a single
|
|
363
|
+
"no friction signals recorded" line and move on — it is not an error.
|
|
364
|
+
|
|
339
365
|
When some Stories are blocked or failed, list them explicitly with the
|
|
340
366
|
`blockerCommentId` or failure detail so the operator knows where to look.
|
|
341
367
|
|