@mmerterden/multi-agent-pipeline 14.0.0 → 14.1.1
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 +142 -1
- package/README.md +4 -4
- package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +1 -1
- package/package.json +1 -1
- package/pipeline/commands/multi-agent/dev/SKILL.md +5 -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 +9 -4
- 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-0-init.md +6 -3
- 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/build-stack-plugins.mjs +21 -0
- package/pipeline/scripts/gc-worktrees.sh +3 -2
- package/pipeline/scripts/migrate-prefs.mjs +31 -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/.skills-index.json +9 -9
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +2 -2
- package/pipeline/skills/skills-index.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,147 @@ Internal file-layout changes that don't affect the slash-command surface are sti
|
|
|
16
16
|
|
|
17
17
|
## [Unreleased]
|
|
18
18
|
|
|
19
|
+
## [14.1.1] - 2026-07-30
|
|
20
|
+
|
|
21
|
+
Three defects that all shared one shape: a name written in one place and read in
|
|
22
|
+
another, so the mechanism looked implemented, ran without error, and did nothing.
|
|
23
|
+
|
|
24
|
+
- **Phase 0 wrote the base-ref field its own exit gate does not read.** `/multi-agent:dev`
|
|
25
|
+
recorded `"baseRefFreshness"` while `phase0-exit-gate.mjs` requires `baseFetchStatus`
|
|
26
|
+
with one of `fresh | cached-stale | local-branch | aborted`. The value vocabulary was
|
|
27
|
+
already correct - only the field name differed - so every dev-mode run failed its own
|
|
28
|
+
exit gate with `baseFetchStatus="<unset>"` even when all four Phase 0 pickers had
|
|
29
|
+
actually run. A gate that always fails is as useless as one that never fails. The
|
|
30
|
+
canonical name appeared in five places and the wrong one in exactly one: the dev
|
|
31
|
+
command's own doc. `smoke-phase-0-multi-repo.sh` now asserts that the name the exit
|
|
32
|
+
gate reads equals the name the phase doc documents, and that no shipped file names an
|
|
33
|
+
alternative, with a planted-line probe proving the detector can fire.
|
|
34
|
+
- **Branch memory never populated.** Phase 0 Step 3 read
|
|
35
|
+
`prefs.global.recentBranches[{projectKey}]` while its own step 7 wrote the legacy
|
|
36
|
+
`prefs.projects[].branches`, which `prefs.schema.json` marks pre-v2.1.0. Both spots
|
|
37
|
+
also described a `{name, lastUsed}` entry the schema rejects (`branch` is required and
|
|
38
|
+
`additionalProperties` is false), so a literal implementation would have failed prefs
|
|
39
|
+
validation and the dedup - which keys on `branch` - would have accumulated a duplicate
|
|
40
|
+
every run. The "reused from last run" picker option could therefore never appear.
|
|
41
|
+
`migrate-prefs.mjs` carries stranded legacy entries into the canonical LRU, stamped
|
|
42
|
+
with the migration time because the legacy field never recorded a real one and an epoch
|
|
43
|
+
stamp would be pruned by the TTL on first read; `count: 0` marks them seeded rather
|
|
44
|
+
than observed.
|
|
45
|
+
- **The sync skill hardcoded the author's git identity.** `git config user.name`,
|
|
46
|
+
`user.email` and `gh auth switch --user` carried literal values in the plugin-publish
|
|
47
|
+
block, while the same file's other two publish blocks used `{identity.name}` and
|
|
48
|
+
`{owner}`. Since the file ships to every installation, a downstream user's
|
|
49
|
+
plugins-repo commits would have been attributed to someone else and their `gh` account
|
|
50
|
+
switched under them - and it contradicted the pipeline's own rule that the git author
|
|
51
|
+
is always the user's identity.
|
|
52
|
+
|
|
53
|
+
Leak-gate coverage, which is why the third defect had gone unnoticed:
|
|
54
|
+
|
|
55
|
+
- `smoke-personal-data.sh` only ever scanned `pipeline/`, but the package also publishes
|
|
56
|
+
`install/`, `docs/`, `index.js`, `install.js`, `README.md` and `CHANGELOG.md`, and every
|
|
57
|
+
tracked file is public regardless of what npm ships. `CHANGELOG.md` was additionally in
|
|
58
|
+
the `--exclude` list. A second pass now scans every tracked file outside `pipeline/`,
|
|
59
|
+
driven by `git ls-files` so the covered set stays exactly "what is public" with no
|
|
60
|
+
second list to maintain. `LICENSE`, `package.json` and `CODE_OF_CONDUCT.md` are exempt,
|
|
61
|
+
because a package must name its author and a code of conduct must give a real contact.
|
|
62
|
+
- Added patterns for the author's own name, personal email and `gh auth switch` account -
|
|
63
|
+
none were checked before, which is precisely how a literal identity survived in a
|
|
64
|
+
shipped command - plus the employer's abbreviation where it is used as a symbol or
|
|
65
|
+
workspace prefix, bounded so ordinary words that merely contain those letters
|
|
66
|
+
(`HEALTHY`, `RHYTHM`) do not match.
|
|
67
|
+
- `--exclude-dir` for `.git`, `node_modules`, `.worktrees`, `.next` and `DerivedData`.
|
|
68
|
+
Without it, `--root` mode scanned `.git/logs`, so auditing any checkout was guaranteed
|
|
69
|
+
to "fail" on commit metadata no consumer receives, burying the real findings.
|
|
70
|
+
- Five leaks removed from public files: a real corporate email in `CHANGELOG-archive.md`,
|
|
71
|
+
a corporate toolkit name in both changelogs, the author's website in `docs/adr/0008`
|
|
72
|
+
and `docs/internal/`, and corporate symbol/repo/task literals in `docs/internal/`.
|
|
73
|
+
These are gone from HEAD; git history still contains them.
|
|
74
|
+
|
|
75
|
+
## [14.1.0] - 2026-07-29
|
|
76
|
+
|
|
77
|
+
Two things the pipeline was supposed to do and did not: use the skills a project's
|
|
78
|
+
own toolkit says apply, and clean up after itself.
|
|
79
|
+
|
|
80
|
+
### Added
|
|
81
|
+
|
|
82
|
+
- **Phase 3 asks the stack toolkit which of its skills govern the task.** Each
|
|
83
|
+
`ai-<platform>-engineering-toolkit` already ships an `index` skill holding a
|
|
84
|
+
30-plus row intent-to-skill table, maintained beside the skills it points at.
|
|
85
|
+
Phase 3 dispatched to that plugin for exactly one case (`taskType == "component"`),
|
|
86
|
+
so `bugfix` / `feature` / `refactor` / `chore` had no skill dispatch at all:
|
|
87
|
+
whichever skills the host surfaced by description match were the ones used, and
|
|
88
|
+
nothing recorded or required any of them. That was the dev-side half of the gap
|
|
89
|
+
v14.0.0 closed on the review side - review asked "was this built to the rules it
|
|
90
|
+
was supposed to follow" while nobody had chosen any rules.
|
|
91
|
+
|
|
92
|
+
The routing table is NOT copied into this repo. A second copy would drift the
|
|
93
|
+
moment the plugin shipped a skill, and the copy here would be the stale one, so
|
|
94
|
+
the pipeline asks rather than knows. `smoke-stack-skill-routing.sh` check 5 fails
|
|
95
|
+
the build if a routing table appears in a shipped file (verified against a planted
|
|
96
|
+
6-row table). Routed skills land in `telemetry.skillCalls[]` with
|
|
97
|
+
`routedBy: "<toolkit>:index@<version>"`, so Phase 4 conformance can hold the run to
|
|
98
|
+
what its own toolkit chose. An absent or disabled toolkit is a recorded no-op, not
|
|
99
|
+
a halt - a backend repo has no toolkit and must still run.
|
|
100
|
+
Contract: `refs/features/stack-skill-routing.md`.
|
|
101
|
+
|
|
102
|
+
- **Phase 6 removes a task's worktree once its PR is open** (`worktree-finalize.sh`,
|
|
103
|
+
gated by `prefs.global.settings.worktreeAutoRemoveOnPr`, default **true**). It
|
|
104
|
+
salvages `agent-state.json`, `phase-tracker.json`, `triage-output.json`,
|
|
105
|
+
`.pipeline/`, `.build.log`, `.test.log` and `.review-diff.txt` into the log dir
|
|
106
|
+
first, because Phase 7's triage-memory ingest, the learnings-ledger distill,
|
|
107
|
+
`render-work-summary.sh`, `:resume`, `:status` and `:log` all read them - and the
|
|
108
|
+
first three are `[ -f ]`-guarded, so a removal without salvage would have degraded
|
|
109
|
+
silently rather than failing.
|
|
110
|
+
|
|
111
|
+
It keeps the branch and **does not check it out**. `git worktree remove` leaves the
|
|
112
|
+
branch as an ordinary local branch, so nothing is lost, while a checkout would move
|
|
113
|
+
the user's HEAD and could collide with their own uncommitted work on another
|
|
114
|
+
branch. Phase 5 removes-then-checks-out on purpose because it is a test handoff;
|
|
115
|
+
this is not. Verified end to end: HEAD stays put, the user's uncommitted file
|
|
116
|
+
survives, the branch is still checkoutable on demand.
|
|
117
|
+
|
|
118
|
+
Every destructive path is gated and each skips with a reason rather than failing:
|
|
119
|
+
real uncommitted changes, an unpushed HEAD, `--local` mode, a cwd inside the tree,
|
|
120
|
+
an unregistered path. `--force` appears nowhere. Contract:
|
|
121
|
+
`refs/features/worktree-finalize.md`. New state: `worktreeRemovedAt`,
|
|
122
|
+
`artifactsPath`.
|
|
123
|
+
|
|
124
|
+
- Gates: `smoke-stack-skill-routing.sh` (12 checks) and `smoke-worktree-finalize.sh`
|
|
125
|
+
(28 checks, exercising real git repos rather than grepping the doc).
|
|
126
|
+
|
|
127
|
+
### Changed
|
|
128
|
+
|
|
129
|
+
- `render-work-summary.sh` gained a log-dir fallback for state and tracker files.
|
|
130
|
+
It resolved them only from the worktree, so it exited 2 and the entire Work Summary
|
|
131
|
+
vanished from the PR body and the Jira comment. Its sibling
|
|
132
|
+
`render-agent-log-cost.sh` has had that fallback all along.
|
|
133
|
+
- `:resume`, `:status` and `:log` no longer treat a missing worktree as a broken run
|
|
134
|
+
when `worktreeRemovedAt` is set: state is read from `artifactsPath`, and resume asks
|
|
135
|
+
before moving the user's HEAD.
|
|
136
|
+
- `gc-worktrees.sh` and `/multi-agent:garbage-collect` both claimed the finishing
|
|
137
|
+
command owned worktree removal. That is now true rather than aspirational.
|
|
138
|
+
- Token budget 52200 -> 52700, after compressing 414 tokens out of Phase 3 and
|
|
139
|
+
Phase 6 first, per the discipline recorded in `token-budget.json`.
|
|
140
|
+
|
|
141
|
+
### Fixed
|
|
142
|
+
|
|
143
|
+
- **Every finding the test-integrity gate produced was unattributable.** It read
|
|
144
|
+
`f.file` from the diff-risk report, which declares and emits `path`, so each
|
|
145
|
+
finding carried `file: undefined` and read "Test file 'undefined' shrinks". That
|
|
146
|
+
is useless to the developer and rejected by `reviewer-output.schema.json`, which
|
|
147
|
+
requires `file` with `minLength: 1` - on the one gate that deliberately has no
|
|
148
|
+
opt-out. It stayed invisible because the smoke's own fixtures used `"file"`, the
|
|
149
|
+
key the bug read: the fixture matched the bug instead of the schema. Fixtures
|
|
150
|
+
corrected, and a new assertion checks the finding names a real path (verified by
|
|
151
|
+
reintroducing the bug).
|
|
152
|
+
|
|
153
|
+
- **The command inventories kept a renamed command alive.** `finish` survived the
|
|
154
|
+
v14.0.0 rename to `ship` in all three inventory lists, because
|
|
155
|
+
`smoke-command-inventory.sh` only proved nothing was MISSING and `ship` appears
|
|
156
|
+
elsewhere in those files' prose. Added check 4b: every name in an inventory must be
|
|
157
|
+
a command that exists in the tree (verified against a planted `ghostcmd`).
|
|
158
|
+
|
|
159
|
+
|
|
19
160
|
## [14.0.0] - 2026-07-29
|
|
20
161
|
|
|
21
162
|
The `--dev` family reviews its own work, and review now checks the code against the
|
|
@@ -2565,7 +2706,7 @@ Analysis open-question resolver + repo hygiene hardening.
|
|
|
2565
2706
|
blockquotes), humanizer punctuation policy, no Figma access (Locked 30 - design-gap
|
|
2566
2707
|
rows get only Defer + a re-run recommendation), no auto-commit. Command inventory
|
|
2567
2708
|
33 -> 34; `/multi-agent:analysis` Phase 5 report now suggests the resolver when
|
|
2568
|
-
Section 20 has open rows. Pattern ported from
|
|
2709
|
+
Section 20 has open rows. Pattern ported from a private stack toolkit's resolver skills.
|
|
2569
2710
|
- **Dead references removed.** `analysis.md` Reusable refs no longer points at a
|
|
2570
2711
|
non-existent `fetch-wiki.sh` (the wiki fetch chain is inline: clone -> gh api ->
|
|
2571
2712
|
WebFetch); `refs/features/external-context-injection.md` figma row routed to the real
|
package/README.md
CHANGED
|
@@ -62,11 +62,11 @@ The discipline behind all of this - bounded loops, evidence gates, token-budgete
|
|
|
62
62
|
| Autopilot | `/multi-agent:autopilot "task"` | All 8 phases, no confirmations |
|
|
63
63
|
| Dev | `/multi-agent:dev "task"` | Init → Dev → Review → Test → Commit → Report |
|
|
64
64
|
| Local | `/multi-agent:local "task"` | Full pipeline, current branch (no worktree) |
|
|
65
|
-
|
|
|
65
|
+
| Ship | `/multi-agent:ship` | Run the review→test→commit→report tail over local work |
|
|
66
66
|
| Audit | `/multi-agent:design-check` | Mock-mode vs Figma conformance, local-only |
|
|
67
67
|
| Audit | `/multi-agent:testflight-validation` | Pre-submission gates for a TestFlight build: static archive audit → Apple's `altool --validate-app` → Review-Guidelines check. Validates only, never uploads |
|
|
68
68
|
|
|
69
|
-
Helpers: `setup`, `status`, `resume #N`, `review`, `test`, `channels`, `stack`, `update`, `sync`, `refactor`, `jira`, `issue`, `analysis`, `create-jira`, `save`, `routines`, `forget`.
|
|
69
|
+
Helpers: `setup`, `status`, `resume #N`, `review`, `test`, `channels`, `stack`, `update`, `sync`, `refactor`, `jira`, `issue`, `analysis`, `create-jira`, `save`, `routines`, `forget`. 44 commands in all - full list: `/multi-agent:help`.
|
|
70
70
|
|
|
71
71
|
## Stacks
|
|
72
72
|
|
|
@@ -80,12 +80,12 @@ This enables the matching plugin (+ the shared `ai-common` plugin) in the repo's
|
|
|
80
80
|
|
|
81
81
|
## Tool support
|
|
82
82
|
|
|
83
|
-
The pipeline runs natively on **Claude Code**, **Copilot CLI** and **Codex CLI** - all three install from the same `pipeline/` source and get the same
|
|
83
|
+
The pipeline runs natively on **Claude Code**, **Copilot CLI** and **Codex CLI** - all three install from the same `pipeline/` source and get the same 44 commands.
|
|
84
84
|
|
|
85
85
|
| Tool | Flag | What it installs |
|
|
86
86
|
|---|---|---|
|
|
87
87
|
| Claude Code | `--claude` (default) | slash commands + skills + agents + `PreToolUse` secret-scan hook |
|
|
88
|
-
| Copilot CLI | `--copilot` | instructions +
|
|
88
|
+
| Copilot CLI | `--copilot` | instructions + 44 sub-command skills + scripts |
|
|
89
89
|
| Codex CLI | `--codex` | one router skill + 43 specs as refs + 8 agent TOML + `AGENTS.md` block + `codex mcp add` |
|
|
90
90
|
|
|
91
91
|
Filter skills by stack with `--platform=ios\|android\|all`.
|
|
@@ -10,7 +10,7 @@ Two pressures collided during the v7.x line:
|
|
|
10
10
|
|
|
11
11
|
1. **Installer monolith.** `install.js` reached 1246 LOC by v7.9.1. It mixed flag parsing, three platform installers (Claude / Copilot / six third-party adapters), telemetry, a dev-only file exclusion list, and a 200-line static generator for `copilot-instructions.md`. Reasoning about install behaviour required holding the whole file in your head; reviewers asked for documentation we could not produce because every change touched untyped, intertwined sections.
|
|
12
12
|
|
|
13
|
-
2. **Secret leaks at deploy time.** During the v7.9.1 production deploy of
|
|
13
|
+
2. **Secret leaks at deploy time.** During the v7.9.1 production deploy of the project website, a `vercel deploy --token=vcp_...` invocation failed. The Vercel CLI printed the failed argv verbatim in its retry hint. That leaked the deploy token into the conversation transcript and forced a token rotation. A second incident in the same release window - `git -c user.email=...` overriding the repo-local config - pushed seven commits with the wrong author identity, which Vercel's contributor gate then blocked. Both classes of failure share a root cause: privileged values flowed through argv when the codebase had no audited boundary between provider tools and the orchestration layer.
|
|
14
14
|
|
|
15
15
|
We needed an architectural answer that prevented both classes of failure from recurring without expanding scope into "rewrite everything as TypeScript" - the project's zero-dependency philosophy (ADR-4) is a hard constraint.
|
|
16
16
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mmerterden/multi-agent-pipeline",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.1.1",
|
|
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",
|
|
@@ -91,9 +91,13 @@ The agent CANNOT make these Phase 0 decisions automatically; it suggests, then w
|
|
|
91
91
|
3. Cancel
|
|
92
92
|
Confirm? [1/2/3]
|
|
93
93
|
```
|
|
94
|
-
- User picks `2` → log warning + record `"
|
|
94
|
+
- User picks `2` → log warning + record `"baseFetchStatus": "cached-stale"` in
|
|
95
95
|
`agent-state.json`, proceed from local ref. Phase 6 push needs network anyway,
|
|
96
96
|
so re-prompt there if still unreachable.
|
|
97
|
+
- Option `1` (fetch succeeded) records `"fresh"`; a local-branch base records
|
|
98
|
+
`"local-branch"`; option `3` records `"aborted"`. The field name and this
|
|
99
|
+
four-value vocabulary are what `phase0-exit-gate.mjs` reads, so a run that writes
|
|
100
|
+
anything else cannot close Phase 0.
|
|
97
101
|
|
|
98
102
|
Always show what was **observed** next to what was **classified**. The previous
|
|
99
103
|
wording asserted `Detected: <host> unreachable (VPN/DNS)` for every failure mode,
|
|
@@ -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
|
```
|
|
@@ -245,10 +245,15 @@ node "$HOME/multi-agent-pipeline/pipeline/scripts/build-stack-plugins.mjs"
|
|
|
245
245
|
```bash
|
|
246
246
|
cd "$PLUGINS_REPO"
|
|
247
247
|
if ! git diff --quiet; then
|
|
248
|
-
|
|
248
|
+
# Identity resolves from prefs.global.identities[] routed by platformIdentityRouting,
|
|
249
|
+
# exactly as Phase 6 does. Never hardcode a name, email or gh account here: this file
|
|
250
|
+
# ships to every installation, so a literal identity attributes a downstream user's
|
|
251
|
+
# plugins-repo commits to someone else and switches their gh account out from under them.
|
|
252
|
+
git config user.name "{identity.name}"
|
|
253
|
+
git config user.email "{identity.email}"
|
|
249
254
|
git add -A
|
|
250
255
|
git commit -m "chore: rebuild stack plugins from pipeline shared/external"
|
|
251
|
-
gh auth switch --user
|
|
256
|
+
gh auth switch --user {owner} 2>/dev/null || true
|
|
252
257
|
git push origin main
|
|
253
258
|
fi
|
|
254
259
|
```
|
|
@@ -490,10 +495,10 @@ same 43 specs as reference files rather than as peer skills, via Step 2b - see
|
|
|
490
495
|
|
|
491
496
|
```
|
|
492
497
|
analysis, analysis-resolve, autopilot, build-optimize, channels, create-jira, design-check, dev,
|
|
493
|
-
dev-autopilot, dev-local, dev-local-autopilot, diff-explain,
|
|
498
|
+
dev-autopilot, dev-local, dev-local-autopilot, diff-explain, forget, garbage-collect,
|
|
494
499
|
help, ios-coding-standard, issue, jira, kill, language, local,
|
|
495
500
|
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
|
|
501
|
+
routines, save, scan, search, setup, ship, stack, status, sync, test, testflight-validation, uninstall, update
|
|
497
502
|
```
|
|
498
503
|
|
|
499
504
|
**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.
|
|
@@ -73,7 +73,7 @@ Used for: input parsing, branch naming, commit messages.
|
|
|
73
73
|
|---|---|---|---|
|
|
74
74
|
| Project picked (Step 2) | `global.recentProjects` | `[{path, label, count, lastUsed}]` | 20 |
|
|
75
75
|
| Multi-repo group picked or saved (Step 2) | `global.recentGroups` | `[{label?, repos[], count, lastUsed}]` | 10 |
|
|
76
|
-
| Branch picked (Step 3) | `global.recentBranches[{projectKey}]` | `[{
|
|
76
|
+
| Branch picked (Step 3) | `global.recentBranches[{projectKey}]` | `[{branch, lastUsed, count?}]` (TTL `settings.branchTtlDays`, default 15d) | 10 (TTL also prunes) |
|
|
77
77
|
| Service ping (any external API call) | `global.serviceStatus[{service}]` | `{ok, checkedAt, reason?}` (TTL `settings.serviceStatusCacheSeconds`, default 300s) | n/a |
|
|
78
78
|
| Git identity routed (Step 6a) | `projects[{name}].lastIdentity` | int (index into `global.identities`) | n/a |
|
|
79
79
|
|
|
@@ -261,7 +261,10 @@ Scan `$HOME` (maxdepth 2) for project markers (`.xcodeproj`, `Package.swift`, `b
|
|
|
261
261
|
header: "Base branch"
|
|
262
262
|
options: origin/develop (Recommended, reused from last run) | origin/main | release/8.4.0 | Other
|
|
263
263
|
```
|
|
264
|
-
7. User picks → store as `baseBranch
|
|
264
|
+
7. User picks → store as `baseBranch`, and append `{branch, lastUsed, count?}` to
|
|
265
|
+
`prefs.global.recentBranches[{projectKey}]` (dedup by `branch`, cap 10) - what the TTL
|
|
266
|
+
filter below reads. Key is `branch`, not `name`. Never the legacy
|
|
267
|
+
`projects[{project}].branches`.
|
|
265
268
|
|
|
266
269
|
**MUST: this step is not skippable (BLOCKING).** The only legitimate skip is rule 4
|
|
267
270
|
above - `baseBranch` already supplied in the input. Everything else asks. A run once
|
|
@@ -277,7 +280,7 @@ which still writes the fields - it does not leave them unset.
|
|
|
277
280
|
|
|
278
281
|
**TTL filter for recent branches**:
|
|
279
282
|
|
|
280
|
-
- `prefs.global.recentBranches[{projectKey}][]` carries `{
|
|
283
|
+
- `prefs.global.recentBranches[{projectKey}][]` carries `{branch, lastUsed, count?}`. Filter to those whose `lastUsed` is within `settings.branchTtlDays` (default 15).
|
|
281
284
|
- Stale entries (>TTL) are pruned in-place during the read - keeps the picker uncluttered without a separate cleanup pass.
|
|
282
285
|
- The filtered "Recent" list precedes the fresh `git branch -r` list; cap at 5 visible recent entries.
|
|
283
286
|
|
|
@@ -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
|
}
|