@mmerterden/multi-agent-pipeline 14.0.0 → 14.1.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/CHANGELOG.md +85 -0
- package/package.json +1 -1
- package/pipeline/commands/multi-agent/log/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/resume/SKILL.md +1 -0
- package/pipeline/commands/multi-agent/status/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/sync/SKILL.md +2 -2
- package/pipeline/multi-agent-refs/features/stack-skill-routing.md +72 -0
- package/pipeline/multi-agent-refs/features/worktree-finalize.md +66 -0
- package/pipeline/multi-agent-refs/phases/phase-3-dev.md +4 -4
- package/pipeline/multi-agent-refs/phases/phase-6-commit.md +17 -4
- package/pipeline/multi-agent-refs/phases/phase-7-report.md +4 -1
- package/pipeline/preferences-template.json +2 -1
- package/pipeline/schemas/agent-state.schema.json +13 -0
- package/pipeline/schemas/criteria-manifest.schema.json +223 -53
- package/pipeline/schemas/prefs.schema.json +5 -0
- package/pipeline/schemas/token-budget.json +2 -2
- package/pipeline/scripts/gc-worktrees.sh +3 -2
- package/pipeline/scripts/migrate-prefs.mjs +1 -0
- package/pipeline/scripts/render-work-summary.sh +51 -3
- package/pipeline/scripts/skill-conformance.mjs +10 -0
- package/pipeline/scripts/test-integrity-gate.mjs +10 -2
- package/pipeline/scripts/worktree-finalize.sh +299 -0
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,91 @@ Internal file-layout changes that don't affect the slash-command surface are sti
|
|
|
16
16
|
|
|
17
17
|
## [Unreleased]
|
|
18
18
|
|
|
19
|
+
## [14.1.0] - 2026-07-29
|
|
20
|
+
|
|
21
|
+
Two things the pipeline was supposed to do and did not: use the skills a project's
|
|
22
|
+
own toolkit says apply, and clean up after itself.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **Phase 3 asks the stack toolkit which of its skills govern the task.** Each
|
|
27
|
+
`ai-<platform>-engineering-toolkit` already ships an `index` skill holding a
|
|
28
|
+
30-plus row intent-to-skill table, maintained beside the skills it points at.
|
|
29
|
+
Phase 3 dispatched to that plugin for exactly one case (`taskType == "component"`),
|
|
30
|
+
so `bugfix` / `feature` / `refactor` / `chore` had no skill dispatch at all:
|
|
31
|
+
whichever skills the host surfaced by description match were the ones used, and
|
|
32
|
+
nothing recorded or required any of them. That was the dev-side half of the gap
|
|
33
|
+
v14.0.0 closed on the review side - review asked "was this built to the rules it
|
|
34
|
+
was supposed to follow" while nobody had chosen any rules.
|
|
35
|
+
|
|
36
|
+
The routing table is NOT copied into this repo. A second copy would drift the
|
|
37
|
+
moment the plugin shipped a skill, and the copy here would be the stale one, so
|
|
38
|
+
the pipeline asks rather than knows. `smoke-stack-skill-routing.sh` check 5 fails
|
|
39
|
+
the build if a routing table appears in a shipped file (verified against a planted
|
|
40
|
+
6-row table). Routed skills land in `telemetry.skillCalls[]` with
|
|
41
|
+
`routedBy: "<toolkit>:index@<version>"`, so Phase 4 conformance can hold the run to
|
|
42
|
+
what its own toolkit chose. An absent or disabled toolkit is a recorded no-op, not
|
|
43
|
+
a halt - a backend repo has no toolkit and must still run.
|
|
44
|
+
Contract: `refs/features/stack-skill-routing.md`.
|
|
45
|
+
|
|
46
|
+
- **Phase 6 removes a task's worktree once its PR is open** (`worktree-finalize.sh`,
|
|
47
|
+
gated by `prefs.global.settings.worktreeAutoRemoveOnPr`, default **true**). It
|
|
48
|
+
salvages `agent-state.json`, `phase-tracker.json`, `triage-output.json`,
|
|
49
|
+
`.pipeline/`, `.build.log`, `.test.log` and `.review-diff.txt` into the log dir
|
|
50
|
+
first, because Phase 7's triage-memory ingest, the learnings-ledger distill,
|
|
51
|
+
`render-work-summary.sh`, `:resume`, `:status` and `:log` all read them - and the
|
|
52
|
+
first three are `[ -f ]`-guarded, so a removal without salvage would have degraded
|
|
53
|
+
silently rather than failing.
|
|
54
|
+
|
|
55
|
+
It keeps the branch and **does not check it out**. `git worktree remove` leaves the
|
|
56
|
+
branch as an ordinary local branch, so nothing is lost, while a checkout would move
|
|
57
|
+
the user's HEAD and could collide with their own uncommitted work on another
|
|
58
|
+
branch. Phase 5 removes-then-checks-out on purpose because it is a test handoff;
|
|
59
|
+
this is not. Verified end to end: HEAD stays put, the user's uncommitted file
|
|
60
|
+
survives, the branch is still checkoutable on demand.
|
|
61
|
+
|
|
62
|
+
Every destructive path is gated and each skips with a reason rather than failing:
|
|
63
|
+
real uncommitted changes, an unpushed HEAD, `--local` mode, a cwd inside the tree,
|
|
64
|
+
an unregistered path. `--force` appears nowhere. Contract:
|
|
65
|
+
`refs/features/worktree-finalize.md`. New state: `worktreeRemovedAt`,
|
|
66
|
+
`artifactsPath`.
|
|
67
|
+
|
|
68
|
+
- Gates: `smoke-stack-skill-routing.sh` (12 checks) and `smoke-worktree-finalize.sh`
|
|
69
|
+
(28 checks, exercising real git repos rather than grepping the doc).
|
|
70
|
+
|
|
71
|
+
### Changed
|
|
72
|
+
|
|
73
|
+
- `render-work-summary.sh` gained a log-dir fallback for state and tracker files.
|
|
74
|
+
It resolved them only from the worktree, so it exited 2 and the entire Work Summary
|
|
75
|
+
vanished from the PR body and the Jira comment. Its sibling
|
|
76
|
+
`render-agent-log-cost.sh` has had that fallback all along.
|
|
77
|
+
- `:resume`, `:status` and `:log` no longer treat a missing worktree as a broken run
|
|
78
|
+
when `worktreeRemovedAt` is set: state is read from `artifactsPath`, and resume asks
|
|
79
|
+
before moving the user's HEAD.
|
|
80
|
+
- `gc-worktrees.sh` and `/multi-agent:garbage-collect` both claimed the finishing
|
|
81
|
+
command owned worktree removal. That is now true rather than aspirational.
|
|
82
|
+
- Token budget 52200 -> 52700, after compressing 414 tokens out of Phase 3 and
|
|
83
|
+
Phase 6 first, per the discipline recorded in `token-budget.json`.
|
|
84
|
+
|
|
85
|
+
### Fixed
|
|
86
|
+
|
|
87
|
+
- **Every finding the test-integrity gate produced was unattributable.** It read
|
|
88
|
+
`f.file` from the diff-risk report, which declares and emits `path`, so each
|
|
89
|
+
finding carried `file: undefined` and read "Test file 'undefined' shrinks". That
|
|
90
|
+
is useless to the developer and rejected by `reviewer-output.schema.json`, which
|
|
91
|
+
requires `file` with `minLength: 1` - on the one gate that deliberately has no
|
|
92
|
+
opt-out. It stayed invisible because the smoke's own fixtures used `"file"`, the
|
|
93
|
+
key the bug read: the fixture matched the bug instead of the schema. Fixtures
|
|
94
|
+
corrected, and a new assertion checks the finding names a real path (verified by
|
|
95
|
+
reintroducing the bug).
|
|
96
|
+
|
|
97
|
+
- **The command inventories kept a renamed command alive.** `finish` survived the
|
|
98
|
+
v14.0.0 rename to `ship` in all three inventory lists, because
|
|
99
|
+
`smoke-command-inventory.sh` only proved nothing was MISSING and `ship` appears
|
|
100
|
+
elsewhere in those files' prose. Added check 4b: every name in an inventory must be
|
|
101
|
+
a command that exists in the tree (verified against a planted `ghostcmd`).
|
|
102
|
+
|
|
103
|
+
|
|
19
104
|
## [14.0.0] - 2026-07-29
|
|
20
105
|
|
|
21
106
|
The `--dev` family reviews its own work, and review now checks the code against the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mmerterden/multi-agent-pipeline",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.1.0",
|
|
4
4
|
"description": "8-phase AI development pipeline with full orchestration on Claude Code, Copilot CLI and Codex CLI. Analysis, planning, TDD, CLI-aware parallel review with consensus surfacing + Fable triage, default-FAIL evidence gates, secret + intent guards, per-phase cost ledger, persistent learnings memory, wiki generation, commit automation. Token-preserving uninstall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -15,7 +15,7 @@ Show the task's detailed agent-log.md report.
|
|
|
15
15
|
1. **Parse task ID** - extract the `#N` form from the argument.
|
|
16
16
|
- No argument → find the most recent (highest-ID) worktree.
|
|
17
17
|
|
|
18
|
-
2. **Find the worktree** - search the known repos:
|
|
18
|
+
2. **Find the worktree** - search the known repos. A task whose worktree was removed after its PR (Phase 6 finalize) is found under `$HOME/.claude/logs/multi-agent/<project>/<task-id>/` instead; the `agent-log.md` was always there, and `artifacts/agent-state.json` holds the state. Look there before reporting "task not found":
|
|
19
19
|
```bash
|
|
20
20
|
find ~/my-ios-app/.worktrees/ ~/my-figma-app/.worktrees/ ~/my-ui-components/.worktrees/ -name "agent-state.json" -maxdepth 2 2>/dev/null
|
|
21
21
|
```
|
|
@@ -19,6 +19,7 @@ Resume a paused or failed task from the last successful phase.
|
|
|
19
19
|
2. **Read + validate state** - parse `agent-state.json`:
|
|
20
20
|
- Validate first: `node $HOME/.claude/scripts/validate-state.mjs <state-file>` (resume-safety check, tolerant of legacy shapes). On non-zero exit, do NOT guess a phase - surface the errors and stop with `ERR: agent-state.json is unsafe to resume; inspect it or 'kill #N' and restart.`
|
|
21
21
|
- Confirm the worktree (`worktreePath` / `projects[].worktreePath`) exists and is usable; if missing or locked, run the Phase 0 "Worktree stale-lock heal" before continuing.
|
|
22
|
+
- **Unless `state.worktreeRemovedAt` is set.** Then the worktree was removed on purpose by Phase 6 once the PR opened, the branch is still local, and the artefacts live under `state.artifactsPath`. Do NOT heal or recreate it: read state from `artifactsPath`, and if the remaining work needs a checkout (a Phase 7 pause needs none), ask before moving the user's HEAD - they may be mid-work on another branch, which is exactly why the removal did not check the branch out.
|
|
22
23
|
- `currentPhase` - last completed phase
|
|
23
24
|
- `status` - `paused` | `failed` | `in_progress`
|
|
24
25
|
- `haltReason` - if set, show it so the user knows why the run stopped; clear it on successful re-entry
|
|
@@ -14,7 +14,7 @@ Show every active and completed task as a table.
|
|
|
14
14
|
- `~/my-figma-app/.worktrees/`
|
|
15
15
|
- `~/my-ui-components/.worktrees/`
|
|
16
16
|
|
|
17
|
-
2. **Scan worktrees** - read `agent-state.json` in each worktree dir:
|
|
17
|
+
2. **Scan worktrees** - read `agent-state.json` in each worktree dir. **Also scan the log dir**, because a task whose PR is open has no worktree any more (Phase 6 removes it and salvages its state): `find $HOME/.claude/logs/multi-agent -maxdepth 4 -name agent-state.json -path '*/artifacts/*'`. (`-maxdepth 4`, not 3: Phase 6 always passes `--project`, so the salvaged copy lands at `<project>/<task-id>/artifacts/agent-state.json`, which a depth-3 scan can never reach.) Merge both sets by `taskId`, preferring the worktree copy when both exist, and render a finalized task with its `worktreeRemovedAt` rather than omitting it - a task that shipped should not vanish from status.
|
|
18
18
|
```bash
|
|
19
19
|
find {repo}/.worktrees/ -name "agent-state.json" -maxdepth 2
|
|
20
20
|
```
|
|
@@ -490,10 +490,10 @@ same 43 specs as reference files rather than as peer skills, via Step 2b - see
|
|
|
490
490
|
|
|
491
491
|
```
|
|
492
492
|
analysis, analysis-resolve, autopilot, build-optimize, channels, create-jira, design-check, dev,
|
|
493
|
-
dev-autopilot, dev-local, dev-local-autopilot, diff-explain,
|
|
493
|
+
dev-autopilot, dev-local, dev-local-autopilot, diff-explain, forget, garbage-collect,
|
|
494
494
|
help, ios-coding-standard, issue, jira, kill, language, local,
|
|
495
495
|
local-autopilot, log, manual-test, prune-logs, purge, refactor, resume, review, review-issue, review-jira,
|
|
496
|
-
routines, save, scan, search, setup, stack, status, sync, test, testflight-validation, uninstall, update
|
|
496
|
+
routines, save, scan, search, setup, ship, stack, status, sync, test, testflight-validation, uninstall, update
|
|
497
497
|
```
|
|
498
498
|
|
|
499
499
|
**NOT synced**: `$HOME/.claude/multi-agent-refs/*` - lazy-load references, Claude Code specific
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Stack skill routing - letting the toolkit plugin choose its own skills
|
|
2
|
+
|
|
3
|
+
> **TLDR** - When a stack toolkit plugin is enabled, Phase 3 asks that plugin's own `index` skill which of its skills apply to this task, loads them before writing code, and records each into `state.telemetry.skillCalls[]`. The routing table lives in the plugin; the pipeline copies none of it.
|
|
4
|
+
|
|
5
|
+
## Why this exists
|
|
6
|
+
|
|
7
|
+
Phase 3 dispatched to the toolkit plugin for exactly one case, `taskType === "component"` (see `component-dispatch.md`). Every other task - `bugfix`, `feature`, `refactor`, `chore` - had no skill dispatch at all: whichever skills the host happened to surface by description match were the ones that got used, and nothing recorded or required any of them.
|
|
8
|
+
|
|
9
|
+
That is the dev-side half of the gap `features/skill-conformance.md` closes on the review side. Review now asks "was this built to the rules it was supposed to follow"; without this step, the answer for a non-component task was "there were no declared rules, because nobody chose any".
|
|
10
|
+
|
|
11
|
+
The fix is not a routing table in the pipeline. Each `ai-<platform>-engineering-toolkit` already ships one: an `index` skill whose description says *"Load this first when unsure which skill applies"*, holding a 30-plus row intent-to-skill map maintained alongside the skills it points at. A second copy in this repo would drift the moment the plugin shipped a new skill, and the pipeline's copy would be the stale one.
|
|
12
|
+
|
|
13
|
+
So the pipeline's job is to **ask**, not to know.
|
|
14
|
+
|
|
15
|
+
## When it runs
|
|
16
|
+
|
|
17
|
+
Phase 3 pre-flight, before any code is written, for **every** `taskType`. Component tasks keep their dedicated dispatch in `component-dispatch.md`; this step runs in addition, because the reference skills (architecture, naming, file placement, tokens) apply to a component build too.
|
|
18
|
+
|
|
19
|
+
## Resolution
|
|
20
|
+
|
|
21
|
+
Platform comes from the same mapping component dispatch uses, so the two cannot disagree:
|
|
22
|
+
|
|
23
|
+
| `state.platform` / detected stack | Toolkit |
|
|
24
|
+
|---|---|
|
|
25
|
+
| ios, swift | `ai-ios-engineering-toolkit` |
|
|
26
|
+
| android, kotlin | `ai-android-engineering-toolkit` |
|
|
27
|
+
| anything else | no toolkit - step is a recorded no-op |
|
|
28
|
+
|
|
29
|
+
The toolkit is enabled per repo (`.claude/settings.local.json` / `~/.claude/settings.json` `enabledPlugins`). **Not enabled is not an error here**, unlike component dispatch: a backend or web repo legitimately has no toolkit, and halting would make the pipeline unusable outside mobile. Record the no-op and continue.
|
|
30
|
+
|
|
31
|
+
Two marketplaces may ship the same toolkit name (a public one and a corporate one). Resolve whichever is enabled and record its **name and version** in the ledger entry, because the routing table and the skill set differ between versions - a finding that cites a skill has to be traceable to the version that defined it.
|
|
32
|
+
|
|
33
|
+
## The call
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
Skill(<toolkit>:index, args: "<task title + one-line intent>")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The index returns which `reference/` and `workflow/` skills apply. Load each via the Skill tool before writing code. A typical task pulls one workflow skill plus one or more reference skills.
|
|
40
|
+
|
|
41
|
+
Emit one progress line per loaded skill per `progress-contract.md`, so the user can see which standards the run bound itself to rather than inferring it afterwards.
|
|
42
|
+
|
|
43
|
+
## Recording - what makes this checkable
|
|
44
|
+
|
|
45
|
+
Append one `state.telemetry.skillCalls[]` entry per skill actually loaded:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{"skill": "ai-ios-engineering-toolkit:reference/architecture", "phase": 3,
|
|
49
|
+
"targetFiles": ["Domains/Checkin/Sources/CheckinScene.swift"],
|
|
50
|
+
"routedBy": "ai-ios-engineering-toolkit:index@0.13.0", "timestamp": "<ISO-8601>"}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`routedBy` names the index and version that chose it. That is the difference between "the model happened to read a skill" and "the toolkit said this skill governs this task".
|
|
54
|
+
|
|
55
|
+
What Phase 4 actually does with it, precisely: Step 1.78 lists these entries in the manifest under `ledger.routedByToolkit`, so a reviewer and the Phase 7 report can see which skills the project's own toolkit selected. It does **not** give them extra weight in the coverage maths. The deterministic resolver stays primary because an unrecorded load and no load are indistinguishable in state, and no `routedBy` tag changes that - the tag says who chose the skill, not that the code honoured it.
|
|
56
|
+
|
|
57
|
+
## Failure modes, and why none of them halt
|
|
58
|
+
|
|
59
|
+
| Situation | Behaviour |
|
|
60
|
+
|---|---|
|
|
61
|
+
| No toolkit for this stack | recorded no-op, continue |
|
|
62
|
+
| Toolkit not enabled in this repo | recorded no-op, continue (component dispatch still halts for its own case) |
|
|
63
|
+
| `index` resolves but routes to a skill that does not exist in this version | record the miss with the version, load the rest, continue. A stale row in a plugin's table must not stop a run |
|
|
64
|
+
| `index` itself does not resolve | record and fall back to the host's own description matching, which is the pre-v14.1.0 behaviour - no worse than before |
|
|
65
|
+
|
|
66
|
+
Nothing here blocks Phase 3. What is downstream is visibility, not enforcement: routed skills appear in the manifest's `ledger.routedByToolkit`, and a task that recorded nothing shows up as `ledgerSource: derived` with its coverage gap stated. Enforcement over rule IDs is the registry's job (`features/skill-conformance.md`), not this step's.
|
|
67
|
+
|
|
68
|
+
## What this deliberately does NOT do
|
|
69
|
+
|
|
70
|
+
- It does not decide which skills apply. Copying the plugin's routing into this repo would put the authoritative table in the wrong place and guarantee drift.
|
|
71
|
+
- It does not fail a run for a missing skill. The pipeline's contract is to ask and record, not to require that a third-party plugin be complete.
|
|
72
|
+
- It does not replace `component-dispatch.md`. That path owns the component build itself, including the `figma-validate` pre-check and the halt-on-incomplete-state rule.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Worktree finalize - removing a task's worktree once its PR is open
|
|
2
|
+
|
|
3
|
+
> **TLDR** - Phase 6 step 9. Once the PR exists the worktree is dead weight, so it is removed: artefacts are salvaged into the log dir first, the branch is kept and deliberately NOT checked out, and every destructive path is gated. Gated by `prefs.global.settings.worktreeAutoRemoveOnPr` (default **true**). Script: `worktree-finalize.sh`.
|
|
4
|
+
|
|
5
|
+
## Why it exists
|
|
6
|
+
|
|
7
|
+
A finished task's worktree is a full second checkout that nobody needs after the PR is open, and removing it is the step people forget. `.worktrees/` then accumulates copies of the repo until `/multi-agent:kill` or `:garbage-collect` is run by hand.
|
|
8
|
+
|
|
9
|
+
Removing it at PR-open is only safe because of the salvage, so the two are one step and not two.
|
|
10
|
+
|
|
11
|
+
## What it will not do
|
|
12
|
+
|
|
13
|
+
**No `git checkout` of the task branch.** `git worktree remove` leaves the branch as an ordinary local branch: the ref, its commits, and the ability to `git checkout <branch>` later all survive untouched. Checking it out here would move the user's HEAD out from under them and can collide with their own uncommitted work on another branch. Phase 5 removes-then-checks-out on purpose, because it is handing the branch over for manual testing; this step is not.
|
|
14
|
+
|
|
15
|
+
**No `git branch -D`.** The branch is the deliverable.
|
|
16
|
+
|
|
17
|
+
**No `--force`, ever.** `git worktree remove` refusing is a safety feature. The clean-tree check runs before it, so a refusal at that point means something unexpected (a lock, a submodule, permissions) and forcing past unexpected dirt is how work gets lost.
|
|
18
|
+
|
|
19
|
+
## Preconditions - each one skips with a reason, none is an error
|
|
20
|
+
|
|
21
|
+
| Condition | Why it blocks |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `worktreePath == projectRoot` (`--local` mode) | there is no worktree; removing it would delete the user's checkout |
|
|
24
|
+
| cwd is inside the worktree | a shell left on a deleted inode is worse than a leftover directory, and Phase 6 legitimately `cd`s into the worktree earlier |
|
|
25
|
+
| not a registered worktree of the project root | a mistyped path must not delete an unrelated directory |
|
|
26
|
+
| real uncommitted changes | never discarded; see the artefact carve-out below |
|
|
27
|
+
| HEAD not on the remote | removing a worktree whose commits exist nowhere else is data loss, not cleanup |
|
|
28
|
+
|
|
29
|
+
Exit codes: `0` removed, `3` skipped with a reason (report and continue to Phase 7), `1` usage error.
|
|
30
|
+
|
|
31
|
+
### The artefact carve-out, and why `--untracked-files=no` is wrong
|
|
32
|
+
|
|
33
|
+
The pipeline's own artefacts live inside the worktree and are untracked, so a raw `git status --porcelain` is never empty at PR-open. Left unhandled the removal would never fire and the feature would look implemented while doing nothing.
|
|
34
|
+
|
|
35
|
+
So exactly these paths are forgiven, and nothing else:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
agent-state.json phase-tracker.json triage-output.json
|
|
39
|
+
.review-diff.txt .build.log .test.log .pipeline/
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Suppressing all untracked files instead (`--untracked-files=no`) would have been shorter and wrong: a source file the developer created but never `git add`ed is invisible to it, and that file would be destroyed silently.
|
|
43
|
+
|
|
44
|
+
## Salvage
|
|
45
|
+
|
|
46
|
+
Copied into `$HOME/.claude/logs/multi-agent/<project>/<task-id>/artifacts/` before removal, each only if present - a task that never reached Phase 4 has no triage output and that is not an error.
|
|
47
|
+
|
|
48
|
+
This is why the removal is safe:
|
|
49
|
+
|
|
50
|
+
| Consumer | Reads | Without salvage |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| Phase 7 triage-memory ingest | `triage-output.json` | `[ -f ]`-guarded, so it degrades **silently**: the triage corpus and learnings ledger stop being fed and no error appears |
|
|
53
|
+
| Phase 7 learnings-ledger distill | same file | same silent degradation |
|
|
54
|
+
| `render-work-summary.sh` | `agent-state.json`, `phase-tracker.json` | exits 2, so the Work Summary vanishes from the PR body and the Jira comment |
|
|
55
|
+
| `:resume` | `agent-state.json` | cannot continue a Phase 7 pause |
|
|
56
|
+
| `:status`, `:log` | `agent-state.json` | the task becomes invisible |
|
|
57
|
+
|
|
58
|
+
`state.worktreeRemovedAt` and `state.artifactsPath` record the outcome. The timestamp is what tells a reader that a worktree-less task was finished-and-tidied rather than killed - without it, a missing worktree is indistinguishable from a broken run.
|
|
59
|
+
|
|
60
|
+
## Multi-repo
|
|
61
|
+
|
|
62
|
+
Run serially per repo, and only **after** `update_sibling_links`: that function issues an update per PR and the loop `cd`s per repo, so removing repo 1's worktree mid-loop breaks repos 2..N.
|
|
63
|
+
|
|
64
|
+
## Interaction with the existing removal sites
|
|
65
|
+
|
|
66
|
+
`gc-worktrees.sh` and `/multi-agent:garbage-collect` never touch a registered, healthy worktree - they sweep orphans. Both name this step as the owner of finishing-a-task removal, which is now true rather than aspirational.
|
|
@@ -8,7 +8,7 @@ Per Locked decision 30, Phase 3 Dev consumes the analysis document as the sole d
|
|
|
8
8
|
|
|
9
9
|
Pre-flight steps (run in order, abort on failure).
|
|
10
10
|
|
|
11
|
-
**Steps 1, 2, 3, 5 and 6 apply only when Phase 1 ran.** In the `--dev` family (`state.onlyDevelop === true`) there is no analysis
|
|
11
|
+
**Steps 1, 2, 3, 5 and 6 apply only when Phase 1 ran.** In the `--dev` family (`state.onlyDevelop === true`) there is no analysis doc by design, so they are recorded `not-applicable (no Phase 1 in this mode)` and skipped - an unconditional abort there would make every fast mode impossible. Steps 4, 7, 8 and 9 apply in every mode.
|
|
12
12
|
|
|
13
13
|
1. **Analysis document presence** (Phase 1 modes only): locate `analysis/<feature-slug>-<platform>.md` for the active platform.
|
|
14
14
|
- Path resolution: `state.run.repoPath` + `/analysis/<feature>-<platform>.md`
|
|
@@ -28,7 +28,7 @@ Pre-flight steps (run in order, abort on failure).
|
|
|
28
28
|
|
|
29
29
|
6. **Conventions handoff**: read `analysis Section 13.1 Concept Table` (Pass B output with footnotes). Persist concept-to-realization mapping into `state.dev.conventions[<concept>]`. Phase 3 implementation uses these names verbatim (e.g., if Section 13.1 says "State holder: PassengerFlightViewModel", Phase 3 names the class exactly `PassengerFlightViewModel`).
|
|
30
30
|
|
|
31
|
-
7. **MCP forbidden**:
|
|
31
|
+
7. **MCP forbidden**: calling `mcp__claude_ai_Figma__*` in Phase 3 is a violation. `smoke-no-mcp-in-dev-phases.sh` reads `state.telemetry.mcpCalls[]` after the run and fails if Phase 3 contributed an entry.
|
|
32
32
|
|
|
33
33
|
8. **Criteria ledger (required, every mode)**: the moment this phase consults a skill, a marketplace plugin skill, a stack guide or a module `CLAUDE.md` in order to write code, append an entry to `state.telemetry.skillCalls[]`:
|
|
34
34
|
|
|
@@ -36,9 +36,9 @@ Pre-flight steps (run in order, abort on failure).
|
|
|
36
36
|
{"skill": "ios-coding-standard", "phase": 3, "targetFiles": ["Sources/Login/LoginViewModel.swift"], "timestamp": "<ISO-8601>"}
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
`targetFiles` is required
|
|
39
|
+
`targetFiles` is required - without it a skill applied to the wrong files still reads as "applied". Append at the moment of consultation, not at the end of the phase. Phase 4 Step 1.78 treats this as self-report only and resolves criteria independently; it is the one signal separating "applied to the wrong files" from "never opened".
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
9. **Stack skill routing (every `taskType`, when a stack toolkit plugin is enabled)**: ask the enabled `ai-<platform>-engineering-toolkit`'s own `index` skill which skills govern this task, load them BEFORE writing code, and record each into `state.telemetry.skillCalls[]` with `routedBy: "<toolkit>:index@<version>"`. The routing table stays in the plugin - a copy here would be the stale one. No toolkit, or none enabled, is a recorded no-op, not a halt. Contract: [`features/stack-skill-routing.md`]($HOME/.claude/multi-agent-refs/features/stack-skill-routing.md).
|
|
42
42
|
|
|
43
43
|
The analysis document is the SOLE design source in Phase 3. Variant choices, padding values, color tokens, copy strings, accessibility identifiers, and test method names all come from the rendered Pass B cells. If something is missing in the analysis doc, the fix is to re-run `/multi-agent:analysis`, not to fetch from Figma.
|
|
44
44
|
|
|
@@ -89,9 +89,20 @@ Branch **deterministically**, no implicit fallback. Read `agent-state.json` and
|
|
|
89
89
|
- In mixed mode (some local, some remote), only prompt for the remote-backed repos; local ones auto-skip.
|
|
90
90
|
- No -> Phase 7
|
|
91
91
|
- Yes -> Create PR with technical description (see below)
|
|
92
|
-
9. **
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
9. **Worktree finalize (gated by `settings.worktreeAutoRemoveOnPr`, default true)**: run **from the project root** - step 3 leaves the shell inside the worktree and the script refuses there.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
cd "$PROJECT_ROOT"
|
|
96
|
+
FIN=$(bash $HOME/.claude/scripts/worktree-finalize.sh --json \
|
|
97
|
+
--worktree "$WT_PATH" --project-root "$PROJECT_ROOT" \
|
|
98
|
+
--task-id "$TASK_ID" --project "$PROJECT" --branch "$BRANCH")
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The script salvages before removing, keeps the branch, and does **not** check it out - the user's HEAD and uncommitted work stay put. On success it stamps `worktreeRemovedAt`, `artifactsPath` and `worktreePath: null` into the **salvaged** `agent-state.json` itself. Do NOT write those via `write-state.mjs "$STATE_FILE"`: in single-repo mode that path went away with the worktree, so the write fails, the fields land nowhere, and Phase 7 reads a dead path and silently skips the triage ingest. After a removal re-point `STATE_FILE` at `$(jq -r .artifactsPath <<< "$FIN")/agent-state.json`. Exit 3 is a safe skip (uncommitted changes, unpushed or detached HEAD, `--local`, cwd inside the tree, preference off): report and continue. Never `--force`. Contract: [`features/worktree-finalize.md`]($HOME/.claude/multi-agent-refs/features/worktree-finalize.md).
|
|
102
|
+
|
|
103
|
+
10. **Issue body update** (GitHub Issue only): if the issue body has a `### Pull Requests` section and/or a `### Progress` table, fill in the PR URL(s) (one row per submodule - e.g. `- **common:** {url}`, `- **uicomponents:** {url}`) and flip the Implementation / Testing / Code Connect flags from Pending to Done using whatever marker the template uses (match in place, do NOT introduce new markers). Apply with `gh issue edit {issueNo} --body "{updated body}"` and preserve every other section unchanged.
|
|
104
|
+
11. **NEVER close or resolve the issue** - neither GitHub Issue nor Jira. Issues require team review (4 approvals) before closing. Only post a comment with commit/PR URLs.
|
|
105
|
+
12. Log: "Phase 6: Commit {sha} - PR #{number}, worktree {removed|kept: <reason>}"
|
|
95
106
|
|
|
96
107
|
#### Step 3 - PR Description (technical detail for reviewers)
|
|
97
108
|
|
|
@@ -294,11 +305,13 @@ done
|
|
|
294
305
|
update_sibling_links "${PR_URLS[@]}"
|
|
295
306
|
```
|
|
296
307
|
|
|
308
|
+
**Then finalize each repo's worktree**, serially and only now - `update_sibling_links` updates one PR per repo and the loop `cd`s per repo, so removing repo 1's worktree mid-loop breaks 2..N. Run `worktree-finalize.sh` once per repo with that repo's `--worktree` / `--project-root` / `--project`; each skips or removes independently.
|
|
309
|
+
|
|
297
310
|
`update_sibling_links` does an UPDATE call per PR (Bitbucket: PUT with `version+1`; GitHub: `gh pr edit --body-file`). Idempotent: re-running with the same set is a no-op.
|
|
298
311
|
|
|
299
312
|
##### GitHub issue body - list all PRs
|
|
300
313
|
|
|
301
|
-
Step
|
|
314
|
+
Step 10's "Issue body update" extends to multi-repo: instead of a single `### Pull Requests` line, write one row per repo:
|
|
302
315
|
|
|
303
316
|
```
|
|
304
317
|
### Pull Requests
|
|
@@ -212,7 +212,10 @@ This is independent of the channels-side `reportContent.costSummary` (which gate
|
|
|
212
212
|
**Triage memory ingest (mandatory):** after Phase 4 produces a final triage output, persist the accepted/deferred/rejected rows into the per-repo triage corpus so Phase 1 enrichment and Phase 4 prior-art lookup can recall them on future tasks. Idempotent - re-running on the same task writes 0 rows.
|
|
213
213
|
|
|
214
214
|
```bash
|
|
215
|
-
|
|
215
|
+
# Salvaged copy first: Phase 6 removes the worktree once the PR is open, and this
|
|
216
|
+
# reader is `[ -f ]`-guarded, so a wrong path degrades SILENTLY.
|
|
217
|
+
TRIAGE_PATH="$(jq -r '.artifactsPath // empty' "$STATE_FILE" 2>/dev/null)/triage-output.json"
|
|
218
|
+
[ -f "$TRIAGE_PATH" ] || TRIAGE_PATH="$WORKTREE/triage-output.json"
|
|
216
219
|
if [ -f "$TRIAGE_PATH" ]; then
|
|
217
220
|
node $HOME/.claude/scripts/triage-memory.mjs ingest \
|
|
218
221
|
--triage "$TRIAGE_PATH" \
|
|
@@ -162,6 +162,10 @@
|
|
|
162
162
|
"timestamp": {
|
|
163
163
|
"type": "string",
|
|
164
164
|
"description": "ISO-8601 time of the consultation."
|
|
165
|
+
},
|
|
166
|
+
"routedBy": {
|
|
167
|
+
"type": "string",
|
|
168
|
+
"description": "Set when a stack toolkit's own index skill chose this skill, as '<toolkit>:index@<version>'. Recorded so a finding can be traced to the index version that selected it - the skill set differs between plugin versions. Phase 4 Step 1.78 surfaces these separately in the manifest ledger; it does NOT grant them extra trust, because the resolver stays primary either way."
|
|
165
169
|
}
|
|
166
170
|
}
|
|
167
171
|
}
|
|
@@ -685,6 +689,15 @@
|
|
|
685
689
|
}
|
|
686
690
|
}
|
|
687
691
|
}
|
|
692
|
+
},
|
|
693
|
+
"worktreeRemovedAt": {
|
|
694
|
+
"type": ["string", "null"],
|
|
695
|
+
"format": "date-time",
|
|
696
|
+
"description": "Set when Phase 6 removed the worktree after opening the PR. Its presence is what tells :resume, :status and :log that a worktree-less task is finished-and-tidied rather than broken - without it a missing worktree is indistinguishable from a killed run."
|
|
697
|
+
},
|
|
698
|
+
"artifactsPath": {
|
|
699
|
+
"type": ["string", "null"],
|
|
700
|
+
"description": "Directory the worktree's artefacts were salvaged into before removal (agent-state, phase-tracker, triage-output, .pipeline/, build+test logs, review diff). Phase 7 and :resume read from here when worktreePath is gone."
|
|
688
701
|
}
|
|
689
702
|
}
|
|
690
703
|
}
|