mandrel 1.86.0 → 1.88.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/docs/SDLC.md +15 -3
- package/.agents/docs/configuration.md +2 -0
- package/.agents/instructions.md +7 -0
- package/.agents/rules/git-conventions.md +13 -1
- package/.agents/schemas/agentrc.schema.json +12 -0
- package/.agents/scripts/boot-sweep.js +36 -4
- package/.agents/scripts/git-cleanup.js +8 -0
- package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +107 -30
- package/.agents/scripts/lib/config/explain.js +4 -0
- package/.agents/scripts/lib/config/runners.js +13 -2
- package/.agents/scripts/lib/config-settings-schema-delivery.js +7 -0
- package/.agents/scripts/lib/config-settings-schema-quality.js +7 -0
- package/.agents/scripts/lib/epic-plan-ideation.js +24 -3
- package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +19 -5
- package/.agents/scripts/lib/feedback-loop/code-review-graduator.js +17 -0
- package/.agents/scripts/lib/framework-version.js +210 -0
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +7 -22
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +41 -5
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +34 -3
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches.js +102 -7
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes.js +85 -1
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/phase-drivers.js +34 -3
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +71 -4
- package/.agents/scripts/lib/single-story-sweep.js +60 -5
- package/.agents/scripts/lib/story-body/story-body.js +81 -4
- package/.agents/scripts/providers/github/tickets.js +18 -1
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +7 -2
- package/.agents/skills/core/epic-plan-premortem/SKILL.md +8 -2
- package/.agents/skills/skills.index.json +3 -3
- package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +36 -8
- package/.agents/workflows/git-cleanup.md +72 -18
- package/.agents/workflows/helpers/acceptance-self-eval.md +23 -1
- package/.agents/workflows/helpers/code-review.md +83 -7
- package/.agents/workflows/helpers/deliver-epic.md +46 -7
- package/.agents/workflows/helpers/epic-audit.md +153 -12
- package/.agents/workflows/helpers/parallel-tooling.md +9 -2
- package/.agents/workflows/helpers/plan-epic.md +32 -14
- package/.agents/workflows/loops/nightly-audit.md +9 -1
- package/docs/CHANGELOG.md +22 -0
- package/package.json +1 -1
|
@@ -31,7 +31,12 @@ allowed_tools:
|
|
|
31
31
|
Senior Engineer + Architect, acting as a **fresh-context pre-mortem critic** —
|
|
32
32
|
deliberately *separate* from `epic-plan-decompose-author` (the generator) and
|
|
33
33
|
`epic-plan-consolidate` (the scope-preserving merge critic) so it is a
|
|
34
|
-
fresh-context, code-reading review, not a same-pass self-critique.
|
|
34
|
+
fresh-context, code-reading review, not a same-pass self-critique. The `/plan`
|
|
35
|
+
workflow delivers that fresh context by **dispatching this skill inside a
|
|
36
|
+
genuine sub-agent** (`Agent` tool, `subagent_type: general-purpose`) at Phase
|
|
37
|
+
8.5, rather than activating it inline in the authoring turn — the sub-agent does
|
|
38
|
+
not inherit the authoring conversation, so its code-reading review is
|
|
39
|
+
independent of the draft it grades.
|
|
35
40
|
|
|
36
41
|
> **Read [`examples.md`](./examples.md) on demand** for the extended rationale:
|
|
37
42
|
> why this critic opens the actual cited code, why it is additive-recommendation
|
|
@@ -52,7 +57,8 @@ surfaces reaches GitHub unreviewed.
|
|
|
52
57
|
|
|
53
58
|
## Inputs
|
|
54
59
|
|
|
55
|
-
The workflow
|
|
60
|
+
The `/plan` workflow dispatches this skill inside a fresh-context sub-agent,
|
|
61
|
+
passing the Epic ID as the Skill argument. The Skill itself reads:
|
|
56
62
|
|
|
57
63
|
- `temp/epic-<Epic_ID>/tickets.json` — the **draft** (or consolidated) Story
|
|
58
64
|
array. This is the pre-mortem subject.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-07-
|
|
2
|
+
"generatedAt": "2026-07-08T12:24:41.101Z",
|
|
3
3
|
"generator": "generate-skills-index.js@1",
|
|
4
4
|
"skills": [
|
|
5
5
|
{
|
|
@@ -377,8 +377,8 @@
|
|
|
377
377
|
"tier": "stack",
|
|
378
378
|
"category": "architecture",
|
|
379
379
|
"path": ".agents/skills/stack/architecture/subagent-orchestration/SKILL.md",
|
|
380
|
-
"description": "Coordinates complex tasks via task-isolated subagents. Use when one objective is too large for a single agent or when independent work streams should run concurrently with minimal context bleed. One objective per subagent; summarize before returning to keep the
|
|
381
|
-
"policyCapsuleBullets":
|
|
380
|
+
"description": "Coordinates complex tasks via task-isolated subagents. Use when one objective is too large for a single agent or when independent work streams should run concurrently with minimal context bleed. One objective per subagent; summarize before returning to keep the orchestrator's context window clean. Applies recursively — an orchestrator at any supported nesting depth applies the same policy to its own children.",
|
|
381
|
+
"policyCapsuleBullets": 8,
|
|
382
382
|
"allowedTools": null,
|
|
383
383
|
"vendor": null
|
|
384
384
|
},
|
|
@@ -4,11 +4,33 @@ description:
|
|
|
4
4
|
Coordinates complex tasks via task-isolated subagents. Use when one objective
|
|
5
5
|
is too large for a single agent or when independent work streams should run
|
|
6
6
|
concurrently with minimal context bleed. One objective per subagent;
|
|
7
|
-
summarize before returning to keep the
|
|
7
|
+
summarize before returning to keep the orchestrator's context window clean.
|
|
8
|
+
Applies recursively — an orchestrator at any supported nesting depth applies
|
|
9
|
+
the same policy to its own children.
|
|
8
10
|
---
|
|
9
11
|
|
|
10
12
|
# Skill: Subagent Orchestration
|
|
11
13
|
|
|
14
|
+
## Recursive orchestration model
|
|
15
|
+
|
|
16
|
+
This skill describes **recursive orchestration**, not a fixed two-tier
|
|
17
|
+
"main agent vs. subagents" split. An **orchestrator** is any agent that
|
|
18
|
+
dispatches sub-agents; a sub-agent is itself an orchestrator over its own
|
|
19
|
+
children. The Claude Code harness carries the `Agent` tool into sub-agents
|
|
20
|
+
(verified nesting depth 2, announced max depth 5; see
|
|
21
|
+
[#2870](https://github.com/dsj1984/mandrel/issues/2870)), so the same
|
|
22
|
+
one-objective / verify / parallelize policy applies **at every level** —
|
|
23
|
+
substitute "orchestrator" for "main agent" and "child" for "subagent"
|
|
24
|
+
throughout and the rules hold unchanged. Keeping a given dispatch level
|
|
25
|
+
flat remains a legitimate **design choice** (e.g. the `/deliver` wave
|
|
26
|
+
loop), but it is no longer forced by a harness limitation.
|
|
27
|
+
|
|
28
|
+
The cost caution compounds with depth: every nesting level re-pays the
|
|
29
|
+
full always-loaded context, so an orchestrator MUST weigh the depth it
|
|
30
|
+
opens against its budget (see
|
|
31
|
+
[`instructions.md` § 4](../../../../instructions.md)) and stay within the
|
|
32
|
+
supported depth envelope.
|
|
33
|
+
|
|
12
34
|
## Policy Capsule
|
|
13
35
|
|
|
14
36
|
- Dispatch one objective per subagent; never bundle unrelated goals into a single delegation.
|
|
@@ -16,11 +38,13 @@ description:
|
|
|
16
38
|
- Specify the expected return format explicitly (JSON summary, diff, bullet list) in every handoff.
|
|
17
39
|
- Verify the subagent's output before incorporating it; treat returned artifacts as untrusted until checked.
|
|
18
40
|
- Run non-dependent subagents in parallel; serialize only when one subagent's output is required input for another.
|
|
19
|
-
- Require a concise summary back from each subagent to keep the
|
|
41
|
+
- Require a concise summary back from each subagent to keep the orchestrator's context window clean.
|
|
20
42
|
- Investigate subagent failures rather than retrying blindly with the same prompt.
|
|
43
|
+
- Respect the nesting depth budget; each level opened re-pays the always-loaded context, so orchestrate deeper only when the isolation or parallelism gain justifies the cost.
|
|
21
44
|
|
|
22
45
|
Internal protocol for managing complex tasks through the creation and
|
|
23
|
-
coordination of subagents
|
|
46
|
+
coordination of subagents, applied recursively by the orchestrator at any
|
|
47
|
+
supported depth.
|
|
24
48
|
|
|
25
49
|
## 1. Core Principles
|
|
26
50
|
|
|
@@ -28,8 +52,11 @@ coordination of subagents.
|
|
|
28
52
|
with multiple unrelated tasks.
|
|
29
53
|
- **Minimal Context:** Provide only the necessary context (files, docs, specific
|
|
30
54
|
goal) to keep the subagent focused and token-efficient.
|
|
31
|
-
- **Verification:** The
|
|
32
|
-
before incorporating it into the
|
|
55
|
+
- **Verification:** The orchestrator must always verify each child's output
|
|
56
|
+
before incorporating it into its own result — at every level of the tree.
|
|
57
|
+
- **Depth Awareness:** Orchestration is recursive; before opening a deeper
|
|
58
|
+
level, confirm the work justifies re-paying the always-loaded context and
|
|
59
|
+
that the nesting stays within the supported depth envelope.
|
|
33
60
|
|
|
34
61
|
## 2. Operation Standards
|
|
35
62
|
|
|
@@ -38,11 +65,12 @@ coordination of subagents.
|
|
|
38
65
|
- **Error Handling:** If a subagent fails or returns an ambiguous result,
|
|
39
66
|
investigate the failure rather than retrying blindly.
|
|
40
67
|
- **Parallelism:** Use subagents to perform non-dependent tasks concurrently
|
|
41
|
-
(e.g., auditing three different modules simultaneously).
|
|
68
|
+
(e.g., auditing three different modules simultaneously). A child that is
|
|
69
|
+
itself an orchestrator may parallelize its own sub-units the same way.
|
|
42
70
|
|
|
43
71
|
## 3. Best Practices
|
|
44
72
|
|
|
45
|
-
- **State Sync:** Ensure the
|
|
73
|
+
- **State Sync:** Ensure the orchestrator's mental model remains the source of
|
|
46
74
|
truth if multiple subagents modify the codebase.
|
|
47
75
|
- **Summarization:** Require subagents to provide a concise summary of their
|
|
48
|
-
findings to prevent the
|
|
76
|
+
findings to prevent the orchestrator's context window from being flooded.
|
|
@@ -18,11 +18,17 @@ confirmation:
|
|
|
18
18
|
3. **reap merged local branches** — the existing squash-aware
|
|
19
19
|
`gh pr list --state merged` + `git branch --merged <base>` sweep,
|
|
20
20
|
with attached worktrees removed first. Optionally also deletes the
|
|
21
|
-
`origin/<branch>` ref when `--remote` is passed.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
`origin/<branch>` ref when `--remote` is passed. Every default run
|
|
22
|
+
also **enumerates** `refs/remotes/origin/*` and reports any
|
|
23
|
+
**remote-only** merged branches — branches whose local ref is
|
|
24
|
+
already gone (or never existed) but whose `origin/<branch>` still
|
|
25
|
+
points at a merged PR — even without `--remote`; `--remote` is still
|
|
26
|
+
required to *delete* them. A third branch, whose content already
|
|
27
|
+
landed in `<base>` by another route (a squash-merged Epic PR, a
|
|
28
|
+
cherry-pick, a manual `merge --squash`), is caught by a
|
|
29
|
+
**content-equivalence probe** (`git merge-tree --write-tree`,
|
|
30
|
+
git ≥ 2.38) even when it has no merged PR of its own and is not a
|
|
31
|
+
git ancestor of `<base>`.
|
|
26
32
|
4. **triage `git stash` entries** — list every stash and prompt for
|
|
27
33
|
`drop / keep / quit` per entry (or pass `--drop-stashes <ref>` for
|
|
28
34
|
non-interactive use).
|
|
@@ -150,6 +156,10 @@ programmatic consumption:
|
|
|
150
156
|
"detectedBy": "gh"
|
|
151
157
|
}
|
|
152
158
|
],
|
|
159
|
+
"skipped": [
|
|
160
|
+
{ "branch": "story-4200", "reason": "not-merged", "lastCommitAt": "2026-05-01T00:00:00Z" }
|
|
161
|
+
],
|
|
162
|
+
"ghDegraded": false,
|
|
153
163
|
"worktrees": [{ "path": "C:/repo/.worktrees/fix-foo", "ok": true, "dirty": false }],
|
|
154
164
|
"local": [{ "branch": "fix/foo", "ok": true, "alreadyGone": false }],
|
|
155
165
|
"remote": [{ "branch": "fix/foo", "ok": true, "alreadyGone": true }],
|
|
@@ -200,18 +210,51 @@ follow-up prune when `--remote` is set, so passing both is idempotent
|
|
|
200
210
|
|
|
201
211
|
### branches
|
|
202
212
|
|
|
203
|
-
The merged-branch sweep
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
213
|
+
The merged-branch sweep recognizes three detection signals, in order:
|
|
214
|
+
|
|
215
|
+
1. **`detectedBy: 'gh'`** — the branch has a merged PR
|
|
216
|
+
(`gh pr list --head <branch> --state all`, classified by the
|
|
217
|
+
**latest** PR's state).
|
|
218
|
+
2. **`detectedBy: 'git-merged'`** — the branch is a git ancestor of
|
|
219
|
+
`<base>` (`git branch --merged <base>`), or of `origin/<base>` when
|
|
220
|
+
that remote-tracking ref exists (unioned so a stale local `<base>` —
|
|
221
|
+
fast-forward phase skipped, or `--branches` run alone — no longer
|
|
222
|
+
hides a branch already merged on the remote).
|
|
223
|
+
3. **`detectedBy: 'content-merged'`** (Story #4395) — the branch has no
|
|
224
|
+
reapable PR verdict and is not an ancestor of `<base>` under either
|
|
225
|
+
anchor, but simulating the merge via
|
|
226
|
+
`git merge-tree --write-tree <base> <branch>` (git ≥ 2.38) produces a
|
|
227
|
+
tree identical to `<base>`'s own tree — i.e. applying the branch's
|
|
228
|
+
changes on top of `<base>` is a content no-op. This catches
|
|
229
|
+
`story-<id>` branches merged into `epic/<id>` whose Epic PR
|
|
230
|
+
**squash-merged** to `main` (the story commits are not ancestors of
|
|
231
|
+
`main` and the story branch usually has no PR of its own), and any
|
|
232
|
+
other branch whose content landed via a different route (a renamed
|
|
233
|
+
head, a cherry-pick, a manual `merge --squash`). When git rejects
|
|
234
|
+
`--write-tree` (git < 2.38) or the simulated merge conflicts, the
|
|
235
|
+
probe is inconclusive and the branch keeps its existing `not-merged`
|
|
236
|
+
skip — the signal never guesses. `content-merged` candidates render
|
|
237
|
+
with a "weaker signal — verify before deleting" annotation in the
|
|
238
|
+
dry-run list and are called out separately in the confirmation
|
|
239
|
+
prompt, since — unlike a merged PR or git ancestry — no CI or GitHub
|
|
240
|
+
merge check ever validated this branch's exact diff.
|
|
241
|
+
|
|
242
|
+
Other candidate semantics:
|
|
243
|
+
|
|
244
|
+
- A branch is a candidate iff it is not `<base>`, not the current HEAD,
|
|
245
|
+
not in `git config branch.protectedBranches`, and matches one of the
|
|
246
|
+
three signals above.
|
|
209
247
|
- When a candidate has an attached worktree, the worktree is removed
|
|
210
248
|
(force if dirty) **before** `git branch -D`, mirroring the pattern in
|
|
211
249
|
[`worktree-lifecycle.md`](helpers/worktree-lifecycle.md).
|
|
212
250
|
- `--remote` is required on top of `--execute` to touch `origin/`.
|
|
251
|
+
- A throwing `gh` runner (auth failure, rate limit, missing binary) no
|
|
252
|
+
longer aborts the run: the branches phase logs one warning and
|
|
253
|
+
continues with the git-only signals (ancestry + content-equivalence).
|
|
254
|
+
The JSON envelope's `ghDegraded: true` records that this happened for
|
|
255
|
+
the run, and the dry-run text carries a matching warning line.
|
|
213
256
|
|
|
214
|
-
The skip taxonomy
|
|
257
|
+
The skip taxonomy:
|
|
215
258
|
|
|
216
259
|
- `reason: 'protected'` — the base branch or a name in
|
|
217
260
|
`git config branch.protectedBranches`. Not reapable; ignore.
|
|
@@ -219,15 +262,26 @@ The skip taxonomy distinguishes two unreapable cases:
|
|
|
219
262
|
`git checkout <base>`. The dry-run output surfaces a remediation
|
|
220
263
|
hint so the operator sees the recovery path without having to look
|
|
221
264
|
in the JSON envelope.
|
|
222
|
-
|
|
223
|
-
|
|
265
|
+
- `reason: 'tip-diverged-from-merge'` — the latest PR merged, but the
|
|
266
|
+
branch's tip has since moved past the merged commit (a post-merge
|
|
267
|
+
force-push). The dry-run line names both SHAs and a remediation hint
|
|
268
|
+
(delete manually via `git branch -D <branch>`, or push the follow-up
|
|
269
|
+
commit).
|
|
270
|
+
- `reason: 'not-merged'` — none of the three detection signals matched.
|
|
271
|
+
Previously silent; the dry-run output now lists every surviving
|
|
272
|
+
`not-merged` branch as a one-line-per-branch summary with its
|
|
273
|
+
last-commit age, so the operator can see why a leftover branch isn't
|
|
274
|
+
reaped instead of hunting for it by hand.
|
|
275
|
+
|
|
276
|
+
Every default run also opts the planner into a **remote-only
|
|
224
277
|
enumeration pass**: in addition to walking `refs/heads/*`, the planner
|
|
225
278
|
also walks `refs/remotes/origin/*` and emits candidates for any branch
|
|
226
279
|
that exists on `origin` with a merged PR but has no local ref. These
|
|
227
|
-
candidates carry `detectedBy: 'remote-only'` and `localExists: false
|
|
228
|
-
and
|
|
229
|
-
path
|
|
230
|
-
local branch
|
|
280
|
+
candidates carry `detectedBy: 'remote-only'` and `localExists: false`
|
|
281
|
+
and are always shown in the dry-run list; **deleting** them (via the
|
|
282
|
+
`git push --delete origin/<branch>` path — no local `git branch -D` is
|
|
283
|
+
attempted, since there is no local branch) still requires `--remote` on
|
|
284
|
+
top of `--execute`, unchanged.
|
|
231
285
|
|
|
232
286
|
### stashes
|
|
233
287
|
|
|
@@ -35,7 +35,29 @@ per-criterion, mid-delivery, and evaluates the actual work product.
|
|
|
35
35
|
1. **Eval pass (fresh context, independent of the author).** Run a **separate
|
|
36
36
|
critic pass** — a fresh-context sub-agent (`Agent` tool,
|
|
37
37
|
`subagent_type: general-purpose`), *not* a continuation of your implementing
|
|
38
|
-
turn — so the evaluator does not grade its own homework.
|
|
38
|
+
turn — so the evaluator does not grade its own homework.
|
|
39
|
+
|
|
40
|
+
> **Inline-critic fallback (nesting-absent harness).** Dispatching the
|
|
41
|
+
> critic as a nested `Agent` is the preferred shape — it gives genuine
|
|
42
|
+
> fresh-context isolation — and works on any harness that carries `Agent`
|
|
43
|
+
> into sub-agents (Claude Code ≥ 2.1.202; see
|
|
44
|
+
> [#2870](https://github.com/dsj1984/mandrel/issues/2870)). This eval loop
|
|
45
|
+
> itself runs inside a Story delivery sub-agent, so the nested critic sits
|
|
46
|
+
> at nesting depth 2. If the host does **not** support nested `Agent`
|
|
47
|
+
> dispatch at that depth — the tool is absent, or a spawn attempt returns
|
|
48
|
+
> an unsupported-capability error — do **not** stall the Story. Fall back
|
|
49
|
+
> to authoring the verdict **inline**: in a deliberately scoped,
|
|
50
|
+
> self-critical pass (re-read only the diff, the `acceptance[]` /
|
|
51
|
+
> `verify[]` arrays, and the `verify[]` command output — treat the
|
|
52
|
+
> implementation reasoning as untrusted and score against the criteria
|
|
53
|
+
> afresh), write the same verdict file described below and hand it to the
|
|
54
|
+
> same `acceptance-eval.js` gate. The fresh-context isolation is weaker in
|
|
55
|
+
> the inline path, but the gate, the schema, the round cap, and the
|
|
56
|
+
> proceed / redraft / block decision are identical — a Story is **never**
|
|
57
|
+
> stranded on a nesting-absent harness. Note in the blocked/friction
|
|
58
|
+
> comment (if you block) that the inline fallback was used.
|
|
59
|
+
|
|
60
|
+
The critic:
|
|
39
61
|
- Inspects the working diff (`git diff origin/<baseBranch>...HEAD`) and the
|
|
40
62
|
Story's inline `acceptance[]` / `verify[]` arrays.
|
|
41
63
|
- **Runs the `verify[]` commands** and consumes their output as **required
|
|
@@ -293,12 +293,63 @@ For every finding, provide:
|
|
|
293
293
|
fix worked. Keep it tight (≤ 5 sentences); the sub-agent will read the
|
|
294
294
|
surrounding code itself.
|
|
295
295
|
|
|
296
|
+
### The `## Fixed on-branch` section (Story #4399)
|
|
297
|
+
|
|
298
|
+
Findings that Step 4.5 remediated on `[HEAD_REF]` MUST be rendered under a
|
|
299
|
+
dedicated **`## Fixed on-branch`** heading, **not** in the severity groups
|
|
300
|
+
above. This is the contract seam that keeps remediated findings from
|
|
301
|
+
spawning ghost follow-up issues: the
|
|
302
|
+
[`code-review` graduator](../../scripts/lib/feedback-loop/code-review-graduator.js)
|
|
303
|
+
skips every entry inside this section (both because a fixed entry is
|
|
304
|
+
rendered with a **✅ prefix** — so it carries no leading severity emoji the
|
|
305
|
+
parser would match — and because the parser has an explicit
|
|
306
|
+
Fixed-on-branch section guard).
|
|
307
|
+
|
|
308
|
+
Render each fixed finding as a `✅`-prefixed line naming its original
|
|
309
|
+
severity, the file path in backticks, and the remediating commit SHA, e.g.:
|
|
310
|
+
|
|
311
|
+
```markdown
|
|
312
|
+
## Fixed on-branch
|
|
313
|
+
|
|
314
|
+
- ✅ 🟡 Medium: `src/lib/foo.js` — missing edge-case guard added (a1b2c3d)
|
|
315
|
+
- ✅ 🟠 High: `src/api/users.js` — ownership check added (d4e5f6a)
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Open (escalated / unfixed) findings stay in their severity group with
|
|
319
|
+
their leading severity emoji so the graduator still files them.
|
|
320
|
+
|
|
296
321
|
## Step 4.5 — Focused-fix Routing (host LLM, no automated loop)
|
|
297
322
|
|
|
298
323
|
There is **no runtime auto-fix function** at this phase. The host LLM is
|
|
299
|
-
the executor:
|
|
300
|
-
|
|
301
|
-
|
|
324
|
+
the executor: it decides, per finding, between a focused fix on
|
|
325
|
+
`[HEAD_REF]` and leaving the finding on the `code-review` structured
|
|
326
|
+
comment for the operator.
|
|
327
|
+
|
|
328
|
+
### Resolve the remediation threshold (Story #4399)
|
|
329
|
+
|
|
330
|
+
Read `delivery.codeReview.autoFixSeverity` from the resolved `.agentrc.json`
|
|
331
|
+
(default **`medium`**; the resolver in
|
|
332
|
+
[`config/runners.js`](../../scripts/lib/config/runners.js) supplies the
|
|
333
|
+
default when the key is absent). The threshold governs **which severities
|
|
334
|
+
route into on-branch remediation** — it never changes the halting rule (a
|
|
335
|
+
surviving 🔴 still stops) or the escalation classes:
|
|
336
|
+
|
|
337
|
+
- **`medium`** (default) — route 🔴 Critical, 🟠 High, **and 🟡 Medium**
|
|
338
|
+
findings into remediation. 🟢 Suggestions stay on the comment (never
|
|
339
|
+
auto-fixed).
|
|
340
|
+
- **`high`** — route only 🔴 Critical and 🟠 High findings, reproducing
|
|
341
|
+
the pre-4399 behavior exactly. 🟡 Medium and 🟢 Suggestion findings stay
|
|
342
|
+
on the comment.
|
|
343
|
+
|
|
344
|
+
Hard cutover per
|
|
345
|
+
[`rules/git-conventions.md`](../../rules/git-conventions.md) § Contract
|
|
346
|
+
Cutovers — no back-compat flag; `high` is opt-in to the old routing.
|
|
347
|
+
|
|
348
|
+
### 🔴 / 🟠 findings — per-finding ceremony (unchanged)
|
|
349
|
+
|
|
350
|
+
For each 🔴 / 🟠 finding from Step 4, decide between two paths and keep the
|
|
351
|
+
`code-review` structured comment authoritative for anything not fixed
|
|
352
|
+
in-place.
|
|
302
353
|
|
|
303
354
|
1. **Apply a focused fix on `[HEAD_REF]`.** Permitted only when the
|
|
304
355
|
finding is unambiguously *fixable* (clean remediation, no scope
|
|
@@ -328,11 +379,36 @@ anything not fixed in-place.
|
|
|
328
379
|
attempt (the equivalent of the prior loop's
|
|
329
380
|
`validation-regression` / `thrash-detected` exits).
|
|
330
381
|
|
|
382
|
+
### 🟡 Medium findings — batched per-lens ceremony (only when `autoFixSeverity: medium`)
|
|
383
|
+
|
|
384
|
+
When the threshold is `medium`, remediate the fixable 🟡 Medium findings in
|
|
385
|
+
a **batch keyed by owning review lens/pillar** rather than the per-finding
|
|
386
|
+
ceremony above:
|
|
387
|
+
|
|
388
|
+
1. Group the fixable Mediums by owning lens (the pillar or audit family
|
|
389
|
+
that produced them). A Medium is fixable on the same terms as a 🟠; a
|
|
390
|
+
Medium in any escalation class stays on the comment exactly like a 🟠.
|
|
391
|
+
2. For each lens, call `assert-branch.js --expected [HEAD_REF]`, stage
|
|
392
|
+
explicit paths only, and make **one focused conventional commit per
|
|
393
|
+
lens** (`fix(<scope>): <description> (review findings batch)`).
|
|
394
|
+
3. Bounded-attempt semantics extend to the batch: each finding gets **at
|
|
395
|
+
most one** attempt, and a lens's batch commit that would exceed
|
|
396
|
+
`delivery.codeReview.maxFixScopeFiles` routes that lens's findings to
|
|
397
|
+
escalation (`scope-exceeded`) instead of committing.
|
|
398
|
+
4. After **all** lens batches are committed, run a **single** validation
|
|
399
|
+
pass (`npm run lint` plus the relevant `npm test` slice) and a
|
|
400
|
+
**single** targeted rescan over the touched files. Surviving batched
|
|
401
|
+
findings stay on the comment for Step 5.
|
|
402
|
+
|
|
403
|
+
Record every remediated finding (🟠 or 🟡) in the **"Fixed on-branch"**
|
|
404
|
+
section of the `code-review` comment (Step 4) so it does not graduate to a
|
|
405
|
+
follow-up issue.
|
|
406
|
+
|
|
331
407
|
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.
|
|
408
|
+
one* focused-fix attempt per finding (or per batched finding) before
|
|
409
|
+
escalating to the operator. Escalated findings remain on the `code-review`
|
|
410
|
+
structured comment with their reason recorded, so Step 5 (and downstream
|
|
411
|
+
consumers) see exactly why each one was not auto-remediated.
|
|
336
412
|
|
|
337
413
|
## Step 4.6 — Cross-phase re-check trigger
|
|
338
414
|
|
|
@@ -89,9 +89,17 @@ Every other runtime modifier is sourced from the Epic's labels or from
|
|
|
89
89
|
- **Single pause point.** Only `agent::blocked` halts execution. No
|
|
90
90
|
clarifying questions — if stuck, flip to `agent::blocked`, post a
|
|
91
91
|
friction comment, park.
|
|
92
|
-
- **
|
|
93
|
-
directly with `subagent_type: general-purpose`.
|
|
94
|
-
|
|
92
|
+
- **Flat Story dispatch by design.** Host LLM fans out per-Story Agent
|
|
93
|
+
calls directly with `subagent_type: general-purpose`. Keeping Story
|
|
94
|
+
dispatch flat — the host owns the single fan-out level — is a
|
|
95
|
+
**design choice**, not a harness constraint: the wave aggregator, idle
|
|
96
|
+
watchdog, and merge-lock all assume one host-owned dispatch level. As of
|
|
97
|
+
Claude Code 2.1.202 a level-1 sub-agent **does** carry the `Agent` tool
|
|
98
|
+
and can nest further (verified depth 2, announced max depth 5; see
|
|
99
|
+
[#2870](https://github.com/dsj1984/mandrel/issues/2870)), so a Story
|
|
100
|
+
worker may itself fan out for its own sub-work within that depth budget —
|
|
101
|
+
the Epic wave loop nonetheless stays flat by choice, not because nesting
|
|
102
|
+
is unavailable.
|
|
95
103
|
- **Operator-merges-PR exit.** Phase 7 opens the PR; the workflow
|
|
96
104
|
never merges to `main` itself. Phase 8.5 may fire auto-merge when
|
|
97
105
|
every signal is clean.
|
|
@@ -481,8 +489,29 @@ therefore auto-runs its mapped lenses (e.g. a `security`-axis Epic runs
|
|
|
481
489
|
low-risk Epic adds nothing. Findings are persisted as an `audit-results`
|
|
482
490
|
structured comment on the Epic.
|
|
483
491
|
|
|
484
|
-
|
|
485
|
-
|
|
492
|
+
The helper's Step 3 remediation is **threshold-aware** (Story #4399): it
|
|
493
|
+
reads `delivery.epicAudit.autoFixSeverity` (default **`medium`**) and, at
|
|
494
|
+
`medium`, routes 🔴/🟠/**🟡** findings into on-branch remediation (Mediums
|
|
495
|
+
batched per lens — one commit per lens, a single validation + overlapping-
|
|
496
|
+
lens rescan at the end) while 🟢 Suggestions still graduate; `high`
|
|
497
|
+
reproduces the pre-4399 Critical/High-only routing. Remediated findings are
|
|
498
|
+
rendered under the comment's `## Fixed on-branch` section so they never
|
|
499
|
+
graduate to follow-up issues. The severity gate below is **unchanged** —
|
|
500
|
+
it keys off the surviving (unfixed) findings.
|
|
501
|
+
|
|
502
|
+
The helper walks the selected roster **serially in-context by default**; when
|
|
503
|
+
the roster carries more than one lens it **may delegate the walk to a single
|
|
504
|
+
audit-orchestrator sub-agent** that fans the already-selected lenses out as
|
|
505
|
+
parallel level-2 agents and returns only the aggregated `audit-results` (see
|
|
506
|
+
[`epic-audit.md` § "Optional: delegate the roster walk to an audit-orchestrator
|
|
507
|
+
sub-agent"](epic-audit.md), within the sub-agent depth budget noted under
|
|
508
|
+
"Flat Story dispatch by design" above). The roster stays fixed upstream, every
|
|
509
|
+
per-lens cost gate is preserved, and the seven sequential-only lenses are **not**
|
|
510
|
+
batch-converted — the fan-out parallelizes across lenses only and never changes
|
|
511
|
+
how any single lens runs internally.
|
|
512
|
+
|
|
513
|
+
- **Any surviving 🔴 Critical Blocker** — STOP. Relay to the operator.
|
|
514
|
+
- **Only 🟠/🟡/🟢 surviving** — log as non-blocking and continue.
|
|
486
515
|
- **Selector reports `degraded: true`** — STOP. Propagate the
|
|
487
516
|
`reason`/`detail`, post a friction comment, do not fall back to a
|
|
488
517
|
full-roster audit.
|
|
@@ -514,8 +543,18 @@ emit so a high-risk Epic gets a deeper adversarial pass and a low-risk one a
|
|
|
514
543
|
lighter one. Depth is **input-only** — it never changes the findings envelope
|
|
515
544
|
or the posted comment shape.
|
|
516
545
|
|
|
517
|
-
|
|
518
|
-
|
|
546
|
+
The helper's Step 4.5 focused-fix routing is **threshold-aware**
|
|
547
|
+
(Story #4399): it reads `delivery.codeReview.autoFixSeverity` (default
|
|
548
|
+
**`medium`**) and, at `medium`, routes 🔴/🟠/**🟡** findings into on-branch
|
|
549
|
+
remediation (Mediums batched per lens — one commit per lens, a single
|
|
550
|
+
validation + rescan at the end) while 🟢 Suggestions stay on the comment;
|
|
551
|
+
`high` reproduces the pre-4399 Critical/High-only routing. Remediated
|
|
552
|
+
findings are rendered under the comment's `## Fixed on-branch` section so
|
|
553
|
+
they never graduate to follow-up issues. The severity gate below is
|
|
554
|
+
**unchanged** — it keys off the surviving (unfixed) findings.
|
|
555
|
+
|
|
556
|
+
- **Any surviving 🔴 Critical Blocker** — STOP. Relay to the operator.
|
|
557
|
+
- **Only 🟠/🟡/🟢 surviving** — log as non-blocking and continue.
|
|
519
558
|
|
|
520
559
|
---
|
|
521
560
|
|