@mmerterden/multi-agent-pipeline 16.31.1 → 17.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 +110 -0
- package/README.md +148 -103
- package/README.tr.md +149 -103
- package/docs/adr/0011-dormant-ci.md +10 -1
- package/docs/adr/0012-macos-only.md +98 -0
- package/docs/adr/README.md +1 -0
- package/docs/architecture.md +3 -3
- package/docs/ecosystem.md +5 -5
- package/docs/engineering.md +1 -1
- package/index.js +26 -0
- package/install/_dev-only-files.mjs +0 -1
- package/install/index.mjs +10 -0
- package/install/templates/multi-agent-autopilot.plist.template +79 -0
- package/package.json +5 -3
- package/pipeline/commands/multi-agent/autopilot-off/SKILL.md +64 -0
- package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +173 -0
- package/pipeline/commands/multi-agent/autopilot-status/SKILL.md +74 -0
- package/pipeline/commands/multi-agent/channels/SKILL.md +41 -12
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +40 -3
- package/pipeline/commands/multi-agent/help/SKILL.md +43 -37
- package/pipeline/commands/multi-agent/manual-test/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/setup/SKILL.md +15 -7
- package/pipeline/commands/multi-agent/stack/SKILL.md +31 -32
- package/pipeline/commands/multi-agent/status/SKILL.md +17 -1
- package/pipeline/commands/multi-agent/sync/SKILL.md +34 -28
- package/pipeline/commands/multi-agent/update/SKILL.md +1 -1
- package/pipeline/lib/autopilot-activation.sh +117 -0
- package/pipeline/lib/autopilot-state.sh +150 -0
- package/pipeline/lib/issue-fetcher.sh +18 -1
- package/pipeline/lib/plan-todos.sh +18 -0
- package/pipeline/lib/stack-detect.sh +200 -0
- package/pipeline/multi-agent-refs/channels/jira.md +80 -20
- package/pipeline/multi-agent-refs/channels/pr.md +65 -19
- package/pipeline/multi-agent-refs/cross-cli-contract.md +35 -15
- package/pipeline/multi-agent-refs/features/doctor.md +15 -3
- package/pipeline/multi-agent-refs/features/visual-evidence.md +61 -1
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +14 -5
- package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +26 -12
- package/pipeline/multi-agent-refs/phases/phase-2-planning.md +17 -15
- package/pipeline/multi-agent-refs/phases/phase-3-dev.md +7 -5
- package/pipeline/multi-agent-refs/phases/phase-6-commit.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-7-report.md +2 -3
- package/pipeline/multi-agent-refs/readiness-review.md +7 -1
- package/pipeline/multi-agent-refs/rules.md +3 -11
- package/pipeline/multi-agent-refs/tracker-contract.md +32 -0
- package/pipeline/schemas/agent-state.schema.json +99 -25
- package/pipeline/schemas/autopilot-config.schema.json +149 -0
- package/pipeline/schemas/token-budget.json +4 -4
- package/pipeline/scripts/_stack-routing.mjs +91 -0
- package/pipeline/scripts/autopilot-arming.mjs +147 -0
- package/pipeline/scripts/autopilot-intake.mjs +383 -0
- package/pipeline/scripts/autopilot-menubar.swift +361 -0
- package/pipeline/scripts/autopilot-runner.mjs +349 -0
- package/pipeline/scripts/autopilot-status.sh +212 -0
- package/pipeline/scripts/capture-resume.sh +76 -14
- package/pipeline/scripts/doctor.mjs +26 -3
- package/pipeline/scripts/gc-abandoned.sh +352 -0
- package/pipeline/scripts/jira-search.sh +70 -0
- package/pipeline/scripts/phase-tracker.sh +134 -12
- package/pipeline/scripts/probe-evidence-capability.sh +27 -3
- package/pipeline/scripts/run-ui-tests.sh +113 -4
- package/pipeline/scripts/usage-report.mjs +5 -5
- package/pipeline/skills/.skill-manifest.json +16 -4
- package/pipeline/skills/shared/core/multi-agent-autopilot-off/SKILL.md +67 -0
- package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +146 -0
- package/pipeline/skills/shared/core/multi-agent-autopilot-status/SKILL.md +64 -0
- package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +62 -11
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +9 -8
- package/pipeline/scripts/gate-linux.sh +0 -62
|
@@ -10,35 +10,89 @@ Every Jira comment posted by this adapter follows the same section order. Sectio
|
|
|
10
10
|
|
|
11
11
|
| # | Section key | Heading (`tr`) | Heading (`en`) | Required? |
|
|
12
12
|
|---|---|---|---|---|
|
|
13
|
-
| 1 | `summary` | `##
|
|
13
|
+
| 1 | `summary` | `## Geliştirme Özeti` | `## Development Summary` | always |
|
|
14
14
|
| 2 | `test_scenarios` | `## Test Senaryoları` | `## Test Scenarios` | always (use " - " placeholder line if truly N/A) |
|
|
15
|
-
| 3 | `
|
|
15
|
+
| 3 | `impact` | `## Etki Analizi` | `## Impact Analysis` | always |
|
|
16
|
+
| 4 | `context_refs` | `## Bağlantılar` | `## References` | when any link exists |
|
|
16
17
|
|
|
17
18
|
> Heading levels in the source markdown are `##`. The wiki-markup converter (next section) rewrites them to `h2.` for Jira rendering.
|
|
18
19
|
|
|
20
|
+
**Jira is not the PR, and this is the contract that keeps getting that wrong.**
|
|
21
|
+
The PR is read by a reviewer holding the diff. The Jira comment is read by the
|
|
22
|
+
person who filed the ticket and by the tester who has to verify it, and neither
|
|
23
|
+
of them has the diff open. So a Jira body carries no identifiers, no file paths,
|
|
24
|
+
no stack frames, no diff hunks and no framework names. Counts in prose are fine
|
|
25
|
+
and are often the most useful sentence in the comment ("two files, eight lines
|
|
26
|
+
removed, no additions"); a class name is not. A sentence that needs a symbol to
|
|
27
|
+
make its point is describing the change at the wrong level for this reader -
|
|
28
|
+
restate the behaviour, not the mechanism. The technical account has a home, and
|
|
29
|
+
it is the PR body (`channels/pr.md`).
|
|
30
|
+
|
|
19
31
|
### Section content rules
|
|
20
32
|
|
|
21
33
|
**`summary`** - 2-5 sentences in `outputLanguage`. What changed, why, and the user-visible impact. No "we", no marketing tone. Past tense (the work is done at the time the comment goes up).
|
|
22
34
|
|
|
23
|
-
**Visual evidence inside these
|
|
35
|
+
**Visual evidence inside these sections.** When `state.visualEvidence` carries artefacts, they render INSIDE `summary` and `test_scenarios` - never as a section of their own, which the fixed section order forbids. Phase 6 Step 2.9 has already uploaded them and written the returned name to `visualEvidence.*[].jiraFilename`: reference that name, and call `jira-attach.sh <issue> <file>...` only for an artefact whose `jiraFilename` is absent. Uploading unconditionally here attaches every file a second time whenever the comment is re-rendered - which is exactly what a post-hoc `/multi-agent:channels` run does.
|
|
24
36
|
|
|
25
37
|
- `summary`, after its sentences: one line naming the pair in `outputLanguage` (`Düzeltme öncesi / Düzeltme sonrası`), then the thumbnails on the next line - `!<file>-before.png|thumbnail! !<file>-after.png|thumbnail!`.
|
|
26
38
|
- `test_scenarios`, under the scenario the recording demonstrates: `!<file>-flow.mp4!` plus one line stating the tier used.
|
|
27
39
|
|
|
28
40
|
A `gaps[]` entry prints its reason on the line where the artefact would have been (`Düzeltme öncesi: ticket'ta görsel yok`). Never an empty thumbnail, never a silent omission. Contract: `$HOME/.claude/multi-agent-refs/features/visual-evidence.md`.
|
|
29
41
|
|
|
30
|
-
**`test_scenarios`** -
|
|
42
|
+
**`test_scenarios`** - one titled scenario per acceptance criterion, each a
|
|
43
|
+
numbered list of steps ending in the expected result. Given/When/Then was the
|
|
44
|
+
shape here for several releases and it reads as translated English to the person
|
|
45
|
+
who actually runs these; a tester wants a list they can follow with the app open.
|
|
46
|
+
The heading and the text render in `outputLanguage`; this file shows the English
|
|
47
|
+
skeleton:
|
|
31
48
|
|
|
32
49
|
```markdown
|
|
33
50
|
## Test Scenarios
|
|
34
51
|
|
|
35
|
-
1.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
52
|
+
**1. <what this scenario exercises>**
|
|
53
|
+
1. <step the tester performs, in the app's own words>
|
|
54
|
+
2. <step>
|
|
55
|
+
3. **Expected:** <what they should see>
|
|
56
|
+
|
|
57
|
+
**2. <regression scenario>**
|
|
58
|
+
1. <step>
|
|
59
|
+
2. **Expected:** <what should still behave as before>
|
|
39
60
|
```
|
|
40
61
|
|
|
41
|
-
|
|
62
|
+
Order is load-bearing. The scenarios that reproduce the reported behaviour come
|
|
63
|
+
first, then the regression scenarios for whatever the change could have disturbed -
|
|
64
|
+
anything sharing the component that was touched. A comment that lists only the fix
|
|
65
|
+
leaves the tester to guess the blast radius, and that guess is the one thing they
|
|
66
|
+
cannot make from the ticket.
|
|
67
|
+
|
|
68
|
+
Screens, fields and menu items are named the way the app shows them, in
|
|
69
|
+
`outputLanguage`. Function names and file paths do not appear at all, per the rule
|
|
70
|
+
above. When there are no executable scenarios (pure doc change, dependency bump),
|
|
71
|
+
write a single line: `- No manual test required - <short reason>`.
|
|
72
|
+
|
|
73
|
+
**`impact`** - four fixed numbered parts, each answered, never left as a
|
|
74
|
+
placeholder. This is what a test lead reads before deciding how wide to test and
|
|
75
|
+
what a release manager reads before deciding whether it ships this week, so it is
|
|
76
|
+
written in the same plain register as the rest of the comment:
|
|
77
|
+
|
|
78
|
+
```markdown
|
|
79
|
+
## Impact Analysis
|
|
80
|
+
|
|
81
|
+
**1 - The problem**
|
|
82
|
+
<what was wrong and since when, in the reader's words>
|
|
83
|
+
|
|
84
|
+
**2 - What was changed**
|
|
85
|
+
<what the change does, and why the behaviour around it is unchanged>
|
|
86
|
+
|
|
87
|
+
**3 - Affected areas**
|
|
88
|
+
<the screens and flows that must be tested, including anything sharing the changed component>
|
|
89
|
+
|
|
90
|
+
**4 - Effect on other systems**
|
|
91
|
+
<none, or which service, contract or channel is affected>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Part 4 is answered "none" far more often than it is answered at all, and "none" is
|
|
95
|
+
a real answer worth writing: it is what lets the reader stop looking.
|
|
42
96
|
|
|
43
97
|
**`context_refs`** - flat bullet list of external links the work depends on or produced. PR URL is **not** repeated here; it lives on the first line (see *Cross-link injection*). Common entries (only when present in the task):
|
|
44
98
|
|
|
@@ -58,7 +112,7 @@ The links are pulled from `agent-state.json.contextLinks[]` (see Phase 0 link ex
|
|
|
58
112
|
|
|
59
113
|
```
|
|
60
114
|
1. Read agent-state.json (taskId, contextLinks, prUrls, language).
|
|
61
|
-
2. Build section bodies in markdown
|
|
115
|
+
2. Build section bodies in markdown, in table order: summary, test_scenarios, impact, context_refs.
|
|
62
116
|
3. Run the assembled body through the `humanizer` skill (see Hard rules below).
|
|
63
117
|
4. Apply Cross-link injection (PR URL on line 1).
|
|
64
118
|
5. Run Wiki markup conversion.
|
|
@@ -116,7 +170,7 @@ Do not hand-apply this table. It was hand-applied for several releases and a smi
|
|
|
116
170
|
|
|
117
171
|
Lines outside these patterns pass through verbatim. Multi-paragraph blocks are joined with one blank line.
|
|
118
172
|
|
|
119
|
-
Every row above is load-bearing, including the ones that look cosmetic. The table must cover each construct the section templates in this file actually emit - `##` for the
|
|
173
|
+
Every row above is load-bearing, including the ones that look cosmetic. The table must cover each construct the section templates in this file actually emit - `##` for the four required headings, `**1. title**` on a scenario and `**Expected:**` inside it, the `**1 - ...**` part labels in the impact section, and `1.`-numbered steps. A missing row does not degrade gracefully: the "pass through verbatim" fallback POSTs `## Test Senaryoları` and `**Expected:**` as literal text, so the comment renders with visible `##` and stray asterisks. Single `*bold*` in Markdown means *italic* in Jira wiki - never map `**bold**` to `*bold*` by dropping one asterisk mechanically without checking the source was bold, not italic.
|
|
120
174
|
|
|
121
175
|
There is no markdown→Jira-wiki converter program in the pipeline (`lib/` ships `md2confluence-v3.py` for Confluence only, and `scripts/jira-wiki-escape.mjs` covers the emoticon step alone). This conversion table is applied by the model, by hand, which is exactly why it has to be complete.
|
|
122
176
|
|
|
@@ -149,19 +203,25 @@ Authorization: Bearer $JIRA_TOKEN
|
|
|
149
203
|
Content-Type: application/json
|
|
150
204
|
```
|
|
151
205
|
|
|
152
|
-
|
|
206
|
+
**Post through `jira-publish.sh`. Never hand-roll the `curl`.**
|
|
153
207
|
|
|
154
208
|
```bash
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
jq -n --rawfile body /tmp/channels-$TASK_ID-jira-escaped.txt '{body: $body}' \
|
|
158
|
-
> /tmp/channels-$TASK_ID-jira-payload.json
|
|
159
|
-
curl -s -X POST -H "Authorization: Bearer $JIRA_TOKEN" \
|
|
160
|
-
-H "Content-Type: application/json" \
|
|
161
|
-
--data-binary @/tmp/channels-$TASK_ID-jira-payload.json \
|
|
162
|
-
"$JIRA_BASE/rest/api/2/issue/$JIRA_ID/comment"
|
|
209
|
+
bash "$HOME/.claude/lib/jira-publish.sh" --issue "$JIRA_ID" \
|
|
210
|
+
--body-file /tmp/channels-$TASK_ID-jira.txt --target comment
|
|
163
211
|
```
|
|
164
212
|
|
|
213
|
+
That script escapes every body unconditionally (`lib/jira-publish.sh:109`) and
|
|
214
|
+
passes the token through a `-K` config rather than argv, so neither the escape
|
|
215
|
+
nor the token handling depends on an agent remembering a step.
|
|
216
|
+
|
|
217
|
+
This block used to be a hand-written `jq` + `curl` pair with the escape as a
|
|
218
|
+
separate `--check` line above it, and a shipped comment rendered the Swift
|
|
219
|
+
selector `hash(into:)` as `hash(into` plus a smiley - twice. The escaper was
|
|
220
|
+
present, correct, and simply not run on that path: `:)` reached Jira intact and
|
|
221
|
+
Jira's wiki renderer turned it into an emoticon, which is what section
|
|
222
|
+
*Emoticon escaping* below is about. A defence that has to be invoked by hand is
|
|
223
|
+
a defence that is eventually not invoked.
|
|
224
|
+
|
|
165
225
|
The dispatch summary line includes the comment URL with `?focusedCommentId=...` so the user can paste it into Slack/Teams.
|
|
166
226
|
|
|
167
227
|
### Writing the issue description (not a comment)
|
|
@@ -10,20 +10,31 @@ The PR description targets code reviewers - it stays technical. Every adapter
|
|
|
10
10
|
|
|
11
11
|
| # | Section key | Heading (`tr`) | Heading (`en`) | Required? |
|
|
12
12
|
|---|---|---|---|---|
|
|
13
|
-
| 1 | `summary` | `##
|
|
14
|
-
| 2 | `
|
|
13
|
+
| 1 | `summary` | `## Geliştirme Özeti` | `## Development Summary` | always |
|
|
14
|
+
| 2 | `technical` | `## Teknik Açıklama` | `## Technical Explanation` | always |
|
|
15
15
|
| 3 | `architecture` | `## Mimari Kararlar` | `## Architecture Decisions` | when a non-trivial design choice was made |
|
|
16
|
-
| 4 | `
|
|
17
|
-
| 5 | `
|
|
18
|
-
| 6 | `
|
|
19
|
-
| 7 | `
|
|
20
|
-
| 8 | `
|
|
16
|
+
| 4 | `impact` | `## Etki Analizi` | `## Impact Analysis` | always |
|
|
17
|
+
| 5 | `test_scenarios` | `## Test Senaryoları` | `## Test Scenarios` | always |
|
|
18
|
+
| 6 | `visuals` | `## Görsel Kanıt` | `## Visual Evidence` | when `state.visualEvidence.required` |
|
|
19
|
+
| 7 | `risk` | `## Risk ve Güvenlik` | `## Risk and Security` | when `state.diffRisk.signals` carries a high-stakes signal (`security_path`, `migration`, `public_api`, `no_test_change`, `test_lines_removed`) |
|
|
20
|
+
| 8 | `dependencies` | `## Bağımlılıklar` | `## Dependencies` | when deps added/removed/bumped |
|
|
21
|
+
| 9 | `build` | `## Build` | `## Build` | always |
|
|
22
|
+
| 10 | `related` | `## İlgili` | `## Related` | always (Jira/issue ref; never `Closes/Fixes`) |
|
|
23
|
+
|
|
24
|
+
`summary`, `impact` and `test_scenarios` carry the same three section keys the
|
|
25
|
+
Jira adapter uses, and that pairing is deliberate: the same three questions get
|
|
26
|
+
answered on both surfaces, at the register each reader needs. The PR versions name
|
|
27
|
+
symbols, files, line counts and build shas. The Jira versions name screens and
|
|
28
|
+
behaviour and nothing else (`channels/jira.md`). `technical` has no Jira twin at
|
|
29
|
+
all - it is the section whose absence over there is the point.
|
|
21
30
|
|
|
22
31
|
### Section content rules
|
|
23
32
|
|
|
24
|
-
**`summary`** -
|
|
33
|
+
**`summary`** - 2-4 sentences in `outputLanguage`. What broke or what was added, where the user meets it, and the size of the effect when it is measurable (a crash count, a share of a known total, a version range). Past tense, no marketing voice. Code identifiers stay verbatim. This is the one section a reviewer reads before deciding whether to read the rest, so it names the user-visible behaviour before the mechanism.
|
|
25
34
|
|
|
26
|
-
**`
|
|
35
|
+
**`technical`** - the account for someone holding the diff: a short paragraph on the mechanism (what the code was actually doing wrong, or what the new code does), then the per-file bullets, then one line of diff stat (`2 files, 8 deletions, 0 insertions`). Where a change is safe for a reason that is not obvious from the diff - an equality relation preserved, an invariant kept, a call site left alone deliberately - that reason belongs here in a sentence, because it is the question the reviewer would otherwise ask in a comment. Tables are welcome when several symbols share a property worth listing side by side.
|
|
36
|
+
|
|
37
|
+
The bullet list is one item per logically distinct change. Each bullet starts with the touched component and ends with a one-line "what". The source is `$WORKTREE/.pipeline/scope-check.json` `files[].reason` (Phase 3 Step 3.7): a file the dev could not justify there is a file this list cannot describe either, so the bullet quotes the gate output instead of inventing a reason. Use the stack's native file extensions / module paths - the example below shows the **shape**, not a stack lock-in:
|
|
27
38
|
|
|
28
39
|
```markdown
|
|
29
40
|
## Changes
|
|
@@ -37,20 +48,55 @@ Across stacks the same shape produces, for example: `LoginView.swift - ...` (i
|
|
|
37
48
|
|
|
38
49
|
**`architecture`** - only when the change involves a non-trivial decision (new abstraction, pattern change, data flow shift, dependency direction). Format: short paragraph stating the decision and the alternative considered. Skip the section entirely for mechanical refactors / dependency bumps / formatting passes.
|
|
39
50
|
|
|
40
|
-
**`
|
|
51
|
+
**`impact`** - four fixed numbered parts, each answered, never a placeholder. Same four questions as the Jira `impact` section, answered here with the identifiers and numbers that section is not allowed to carry:
|
|
52
|
+
|
|
53
|
+
```markdown
|
|
54
|
+
## Impact Analysis
|
|
55
|
+
|
|
56
|
+
**1 - The problem**
|
|
57
|
+
<what was wrong, since which version, with the crash/report identifiers and counts>
|
|
58
|
+
|
|
59
|
+
**2 - What was changed**
|
|
60
|
+
<the change, and why behaviour around it is unchanged>
|
|
61
|
+
|
|
62
|
+
**3 - Affected functions**
|
|
63
|
+
<the screens, flows and symbols that must be tested, including shared components the change reaches>
|
|
64
|
+
|
|
65
|
+
**4 - Effect on other systems**
|
|
66
|
+
<none, or which service, contract or channel>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
When the change deliberately fixes part of a wider problem, a closing **Risk and remaining scope** paragraph names what is still open and why it was left - a reviewer who can see the rest of the pattern in the repo will ask otherwise, and the honest answer is cheaper written down than defended in a thread.
|
|
41
70
|
|
|
42
|
-
|
|
71
|
+
**`test_scenarios`** - the same titled-scenario shape the Jira adapter uses, so the tester reads one list on both surfaces, with symbols allowed here:
|
|
43
72
|
|
|
44
73
|
```markdown
|
|
45
|
-
##
|
|
74
|
+
## Test Scenarios
|
|
46
75
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
76
|
+
**1. <what this scenario exercises>**
|
|
77
|
+
1. <step>
|
|
78
|
+
2. **Expected:** <observable outcome>
|
|
79
|
+
|
|
80
|
+
**2. <regression scenario>**
|
|
81
|
+
1. <step>
|
|
82
|
+
2. **Expected:** <what should still behave as before>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Reproduction scenarios first, then regressions for whatever the change could have disturbed. When a UI test target covers a scenario, say so on the scenario line and let `## Build` carry the run result - a scenario a machine already ran is not the same request as one a human has to perform, and conflating them wastes the reviewer's time.
|
|
86
|
+
|
|
87
|
+
**`build`** - what was built, on what, and what came out. Not a promise that it builds; the recorded result of the run that happened:
|
|
88
|
+
|
|
89
|
+
```markdown
|
|
90
|
+
## Build
|
|
91
|
+
|
|
92
|
+
- <build command or scheme> on <base branch>@<sha>: BUILD SUCCEEDED, 0 errors
|
|
93
|
+
- Tests: <test command>: <N> passed, <M> failed
|
|
94
|
+
- UI tests: <target>: <status> | not run - <reason from state.uiTest.notRunReason>
|
|
51
95
|
```
|
|
52
96
|
|
|
53
|
-
|
|
97
|
+
The base sha matters because "it builds" is a claim about a merge base, and the reviewer's local tree is usually not that one. `state.uiTest` supplies the third line verbatim, including its `notRunReason` - "no UI test target in this repo" is a result, not a gap, and writing it stops the same question being asked on every PR.
|
|
98
|
+
|
|
99
|
+
Pick commands for the project's stack - the pipeline supports iOS (Swift/Xcode), Android (Gradle), web (npm/pnpm/yarn) and backend (pytest/jest/go test/etc). Multi-repo PRs (one PR per repo) emit the commands for that repo's stack only - never mix iOS + Android commands into a single PR body.
|
|
54
100
|
|
|
55
101
|
**`risk`** - only when `state.diffRisk.signals` (Phase 4 Step 1.75) contains a high-stakes signal. Four fixed lines, each answered, never left as a placeholder; the source is Phase 1 `touchedAreas` plus the signals themselves, and when a signal is present the absence of this section is a Phase 6 Step 3 blocker:
|
|
56
102
|
|
|
@@ -136,7 +182,7 @@ Never use `Closes #N`, `Fixes #N`, `Resolves PROJ-X`. Issues require 4-approval
|
|
|
136
182
|
|
|
137
183
|
```
|
|
138
184
|
1. Read agent-state.json (taskId, contextLinks, identity, language).
|
|
139
|
-
2. Build section bodies in markdown - summary first, then in the table order, skipping conditional sections that don't apply. Section order is fixed: `summary` → `
|
|
185
|
+
2. Build section bodies in markdown - summary first, then in the table order, skipping conditional sections that don't apply. Section order is fixed: `summary` → `technical` → `architecture` (cond.) → `impact` → `test_scenarios` → `visuals` (cond.) → `risk` (cond.) → `dependencies` (cond.) → `build` → `related`.
|
|
140
186
|
3. Run the assembled body through the `humanizer` skill.
|
|
141
187
|
4. Apply Multi-repo cross-links (## Related PRs prepend when projects.length > 1).
|
|
142
188
|
5. Dispatch per the Behaviour-by-remote table.
|
|
@@ -220,7 +266,7 @@ The Bitbucket REST API returns `409 Conflict` if `version` is stale. Adapter beh
|
|
|
220
266
|
## Hard rules (must not regress)
|
|
221
267
|
|
|
222
268
|
- Real newlines, no HTML entities - heredoc + `jq --rawfile` + `curl --data-binary @file`. Never embed `\n` literally; Bitbucket stores the literal `\n` characters.
|
|
223
|
-
- Section order is fixed: `summary` → `
|
|
269
|
+
- Section order is fixed: `summary` → `technical` → `architecture` (cond.) → `impact` → `test_scenarios` → `visuals` (cond.) → `risk` (cond.) → `dependencies` (cond.) → `build` → `related`. Conditional sections may be omitted but never reordered or inserted between fixed ones.
|
|
224
270
|
- Humanizer pass runs **after** body assembly and **before** dispatch - every body line carries technical, non-AI tone. References at least one symbol/file/line drawn from the diff or pipeline log.
|
|
225
271
|
- Body content language follows `prefs.global.outputLanguage`. Code identifiers, file paths, branch names, PR titles, commands, type names, and `Closes/Fixes`-style keywords stay verbatim English. The template file (this doc) is English because `promptLanguage="en"` is locked; the body is rendered in the user's language at write-time.
|
|
226
272
|
- No body markers (`<!-- channels:start -->`) - channels does a full replace each run.
|
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
## 1. Command Inventory (
|
|
9
|
+
## 1. Command Inventory (56 commands)
|
|
10
10
|
|
|
11
11
|
```
|
|
12
|
-
analysis, analysis-jira, analysis-resolve, autopilot,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
analysis, analysis-jira, analysis-resolve, autopilot, autopilot-off,
|
|
13
|
+
autopilot-on, autopilot-status, build-optimize, channels, complaint-analysis,
|
|
14
|
+
create-jira, design-check, diff-explain, doctor, feedback, forget,
|
|
15
|
+
garbage-collect, graph, help, ios-coding-standard, issue, jira, kill,
|
|
16
|
+
language, local, local-autopilot, log, manual-test, prune-logs,
|
|
16
17
|
prune-prompts, purge, refactor, resume, resume-local, review,
|
|
17
18
|
review-analysis, review-issue, review-jira, routines, save, scan, search,
|
|
18
19
|
setup, stack, status, steer, store-ready, sync, test, test-accessibility,
|
|
@@ -297,20 +298,39 @@ Modifier flags are orthogonal and compose:
|
|
|
297
298
|
|
|
298
299
|
---
|
|
299
300
|
|
|
300
|
-
## 7. Platform Guards (macOS
|
|
301
|
+
## 7. Platform Guards (macOS)
|
|
301
302
|
|
|
302
|
-
All three CLIs
|
|
303
|
+
All three CLIs are installed from this package, and `package.json` declares
|
|
304
|
+
`os: ["darwin"]` (ADR-0012), so every command executes on macOS. That is not a
|
|
305
|
+
licence to stop being careful about shell: the runtime is **BSD userland and
|
|
306
|
+
bash 3.2**, which is a narrower target than "portable", and three constructs
|
|
307
|
+
below exist because the BSD form silently differs rather than failing.
|
|
303
308
|
|
|
304
|
-
For Keychain I/O the canonical path is **`~/.claude/lib/credential-store.sh`**
|
|
309
|
+
For Keychain I/O the canonical path is **`~/.claude/lib/credential-store.sh`**
|
|
310
|
+
(or `~/.copilot/lib/credential-store.sh` / `~/.codex/lib/credential-store.sh` on
|
|
311
|
+
those installs). Call sites never invoke `security` themselves - the wrapper is
|
|
312
|
+
what keeps a secret off argv and what writes the audit entry.
|
|
305
313
|
|
|
306
|
-
| Purpose | Canonical
|
|
314
|
+
| Purpose | Canonical | Underlying backend (for debugging only) |
|
|
307
315
|
|---|---|---|
|
|
308
|
-
| Keychain read | `~/.claude/lib/credential-store.sh get <key>` |
|
|
309
|
-
| Keychain write | `~/.claude/lib/credential-store.sh set <key> <value>` (or `keychain.py set <key> -` to stream the secret over stdin) |
|
|
310
|
-
| Clipboard read |
|
|
311
|
-
| Clipboard clear |
|
|
312
|
-
|
|
313
|
-
|
|
316
|
+
| Keychain read | `~/.claude/lib/credential-store.sh get <key>` | `security find-generic-password -a "$USER" -s <key> -w` |
|
|
317
|
+
| Keychain write | `~/.claude/lib/credential-store.sh set <key> <value>` (or `keychain.py set <key> -` to stream the secret over stdin, keeping it off argv) | `security add-generic-password -a "$USER" -s <key> -w` |
|
|
318
|
+
| Clipboard read | `pbpaste` | - |
|
|
319
|
+
| Clipboard clear | `pbcopy < /dev/null` | - |
|
|
320
|
+
|
|
321
|
+
**The three that bite on BSD**, and why each is a rule rather than a preference:
|
|
322
|
+
|
|
323
|
+
- PCRE mode is absent from BSD grep, and is banned here. It exits 2, which under
|
|
324
|
+
`2>/dev/null` reads as "found nothing" - two gates shipped green for exactly
|
|
325
|
+
that reason. Use `-E`.
|
|
326
|
+
- `stat -c %Y` FIRST, then `stat -f %m`. GNU-first ordering is required because
|
|
327
|
+
`stat -f` is a valid GNU flag (`--file-system`) that succeeds and prints
|
|
328
|
+
something that is not a timestamp.
|
|
329
|
+
- `sed -E`, never `-r`; `sed -i ''` when editing in place.
|
|
330
|
+
|
|
331
|
+
Clipboard callers may use `pbpaste` / `pbcopy` directly; the three-way
|
|
332
|
+
`command -v` guard they used to carry was for hosts this package no longer
|
|
333
|
+
installs on.
|
|
314
334
|
|
|
315
335
|
---
|
|
316
336
|
|
|
@@ -25,9 +25,9 @@ resolver as a clean bill.
|
|
|
25
25
|
|
|
26
26
|
**A state in which a run will fail or leak. Not a state in which it will merely
|
|
27
27
|
be worse.** Without a closed definition "blocked" grows until nobody respects
|
|
28
|
-
exit 2, so only
|
|
29
|
-
`state-writable`, `prefs-valid` and `embedded-credentials`.
|
|
30
|
-
tops out at WARN however bad it looks.
|
|
28
|
+
exit 2, so only six checks can produce it: `host-platform`, `install-present`,
|
|
29
|
+
`script-surface`, `state-writable`, `prefs-valid` and `embedded-credentials`.
|
|
30
|
+
Every other check tops out at WARN however bad it looks.
|
|
31
31
|
|
|
32
32
|
## Who calls it, and what they do with the code
|
|
33
33
|
|
|
@@ -91,6 +91,18 @@ its host; everything after that is behind `--explain`.
|
|
|
91
91
|
|
|
92
92
|
## Checks
|
|
93
93
|
|
|
94
|
+
### host-platform
|
|
95
|
+
|
|
96
|
+
The host is macOS. BLOCK otherwise: every credential read shells `security`,
|
|
97
|
+
every iOS build shells `xcodebuild`, every piece of visual evidence shells
|
|
98
|
+
`simctl`, so a run on another platform does not degrade, it fails partway
|
|
99
|
+
through with a worktree and a branch already created (ADR-0012).
|
|
100
|
+
|
|
101
|
+
It runs **first**, before `install-present`, so an unsupported host reads one
|
|
102
|
+
line naming the cause instead of a cascade of consequences. The step names the
|
|
103
|
+
`MULTI_AGENT_ALLOW_NON_DARWIN=1` escape, which the entry points honour and
|
|
104
|
+
nothing tests.
|
|
105
|
+
|
|
94
106
|
### install-present
|
|
95
107
|
|
|
96
108
|
The installed tree exists and carries the subtrees a run reads: `commands/`,
|
|
@@ -33,6 +33,66 @@ model's reading of the task. A file counts as UI by stack:
|
|
|
33
33
|
Nothing about this section is optional-by-omission: when it is required and an
|
|
34
34
|
artefact is absent, the absence is written down with its reason (section 5).
|
|
35
35
|
|
|
36
|
+
### 1a. Who writes the verdict, and when
|
|
37
|
+
|
|
38
|
+
**Phase 0 Step 7.7 writes `required`, `requiredBy` and `platform`**, before it
|
|
39
|
+
probes. Naming the writer is not a formality: five phases read these three fields
|
|
40
|
+
and nothing set them, so `required` was never true, Step 7.7 never ran, Phase 3
|
|
41
|
+
never captured, and Phase 6's blocker never blocked. A contract with readers and
|
|
42
|
+
no writer reads exactly like a contract that is satisfied.
|
|
43
|
+
|
|
44
|
+
Phase 0 has no diff, so it decides on what it actually knows - `taskType` and
|
|
45
|
+
whether the intake carried a Figma reference - and that is enough for the whole
|
|
46
|
+
design-work row. The `bugfix` row needs a changed-file list, so Phase 0 records
|
|
47
|
+
it as provisional (`requiredBy: "bugfix (pending diff)"`) and **Phase 3 re-decides
|
|
48
|
+
from the real diff** before capturing. A provisional `false` never suppresses the
|
|
49
|
+
Phase 3 decision.
|
|
50
|
+
|
|
51
|
+
### 1b. The platform, and when there is not one
|
|
52
|
+
|
|
53
|
+
`platform` comes from the stack, not from the diff: it selects which device
|
|
54
|
+
tooling is probed, and that is a property of the repo.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
eval "$(bash $HOME/.claude/lib/stack-detect.sh "$PROJECT_ROOT")" # MA_STACKS
|
|
58
|
+
EVIDENCE_PLATFORM=""
|
|
59
|
+
case " $MA_STACKS " in
|
|
60
|
+
*" ios "*) EVIDENCE_PLATFORM=ios ;;
|
|
61
|
+
*" android "*) EVIDENCE_PLATFORM=android ;;
|
|
62
|
+
*" web "*) EVIDENCE_PLATFORM=web ;;
|
|
63
|
+
esac
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
iOS wins a tie, and the tie is recorded in `stackWhy`: `simctl` discovery is the
|
|
67
|
+
cheaper of the two, and a repo that is honestly both gets the same answer on
|
|
68
|
+
every run rather than one that depends on ordering. Web is last for the same
|
|
69
|
+
reason and not because it is lesser: a repo that is both iOS and web is a mobile
|
|
70
|
+
app with a marketing site, and the screen the ticket is about is the mobile one.
|
|
71
|
+
|
|
72
|
+
**Web is a device platform here.** It was not until v17.1.0, and the omission was
|
|
73
|
+
not a decision anyone made: `run-ui-tests.sh` rejected everything but
|
|
74
|
+
`ios|android`, so a repo with a full Playwright suite reported "no UI test
|
|
75
|
+
target". The PR body then said UI tests had not run, which was true of the runner
|
|
76
|
+
and false of the repo - the exact shape of claim this file exists to prevent. The
|
|
77
|
+
browser is the device, the runner is the recorder (both Playwright and Cypress
|
|
78
|
+
write their own video), and `npx --no-install` is what keeps a probe from
|
|
79
|
+
installing one behind the user's back.
|
|
80
|
+
|
|
81
|
+
**An empty value is still a real outcome, not an error.** A backend-only repo has
|
|
82
|
+
nothing to probe, and `probe-evidence-capability.sh` rejects anything but
|
|
83
|
+
`ios|android|web` with exit 2. So when `EVIDENCE_PLATFORM` is empty the probe
|
|
84
|
+
**does not run**, `platform` is written as `other` from `MA_STACKS`, and the
|
|
85
|
+
reason goes to `state.evidenceCapability.skippedReason` in the probe's own
|
|
86
|
+
`*_REASON` idiom - "no device platform in stacks [backend]". Passing an empty
|
|
87
|
+
string instead is what the probe's exit 2 exists to refuse, and swallowing that
|
|
88
|
+
exit would turn a missing value into a silent skip.
|
|
89
|
+
|
|
90
|
+
Every `--platform` argument in this pipeline is `$EVIDENCE_PLATFORM`, read from
|
|
91
|
+
`state.visualEvidence.platform` in later phases. `$PLATFORM` is not a pipeline
|
|
92
|
+
variable: nothing ever assigned it, in any phase document, which is why
|
|
93
|
+
`smoke-evidence-probe.sh` now fails a `--platform` argument whose variable is not
|
|
94
|
+
assigned earlier in the same file.
|
|
95
|
+
|
|
36
96
|
## 2. Before - the reporter's screenshot, or nothing
|
|
37
97
|
|
|
38
98
|
**The pipeline does not build the old state to photograph it.** Reproducing a
|
|
@@ -61,7 +121,7 @@ never the only source.
|
|
|
61
121
|
|
|
62
122
|
```bash
|
|
63
123
|
bash $HOME/.claude/scripts/capture-evidence.sh after \
|
|
64
|
-
--task "$TASK_ID" --platform "$
|
|
124
|
+
--task "$TASK_ID" --platform "$EVIDENCE_PLATFORM" --label "<screen-slug>"
|
|
65
125
|
```
|
|
66
126
|
|
|
67
127
|
What the capture is worth checking against - the per-component catalog, the
|
|
@@ -591,21 +591,30 @@ Log: `Phase 0 Step 7.6: test baseline = {green|red|unknown} ({N} pre-existing fa
|
|
|
591
591
|
|
|
592
592
|
#### Step 7.7 - Evidence capability, then test depth
|
|
593
593
|
|
|
594
|
-
|
|
594
|
+
Decide, then probe, then ask, then run. Skipped only when `visualEvidence.enabled` is `false`. Contract: `features/visual-evidence.md` sections 1a, 1b, 4.
|
|
595
|
+
|
|
596
|
+
**This step is the only writer of `visualEvidence.required`, `requiredBy` and `platform`** (section 1a). No diff exists yet, so the `bugfix` row is provisional and Phase 3 re-decides.
|
|
595
597
|
|
|
596
598
|
```bash
|
|
597
|
-
eval "$(bash $HOME/.claude/
|
|
598
|
-
|
|
599
|
-
|
|
599
|
+
eval "$(bash $HOME/.claude/lib/stack-detect.sh "$PROJECT_ROOT")"
|
|
600
|
+
EVIDENCE_PLATFORM=""
|
|
601
|
+
case " $MA_STACKS " in *" ios "*) EVIDENCE_PLATFORM=ios ;; *" android "*) EVIDENCE_PLATFORM=android ;; *" web "*) EVIDENCE_PLATFORM=web ;; esac
|
|
602
|
+
if [ -n "$EVIDENCE_PLATFORM" ]; then
|
|
603
|
+
eval "$(bash $HOME/.claude/scripts/probe-evidence-capability.sh \
|
|
604
|
+
--platform "$EVIDENCE_PLATFORM" --repo "$WORKTREE" \
|
|
605
|
+
--json-out "$WORKTREE/.pipeline/evidence-capability.json")"
|
|
606
|
+
fi
|
|
600
607
|
```
|
|
601
608
|
|
|
609
|
+
Empty is an outcome, not a failure: no simulator on web or backend, so the probe does not run and `evidenceCapability.skippedReason` says so. No `--changed` yet, by the same token.
|
|
610
|
+
|
|
602
611
|
One run, both forms: stdout is `EVIDENCE_*` (shell-quoted, so the eval is safe), and the same measurement lands as JSON.
|
|
603
612
|
|
|
604
613
|
Persist that file as `state.evidenceCapability`, then build the menu from it, never from a reading of the repo: `1. Sadece unit test` / `2. Unit + UI test, ekran kaydiyla` (tier 1) / `3. Unit + MCP ile akis kaydi` (tier 2).
|
|
605
614
|
|
|
606
615
|
A closed option keeps its row and prints the probe's reason verbatim. No `uiTestTargets` closes 2; `mcp` false closes 3; a missing `device` or `recorder` closes both. Targets present with no `matchingTests` leaves 2 open, warning that the whole UI suite will run. **When only option 1 is open, do not ask**: `testDepth = unit`, `testDepthSource = forced`, and the tier 3 gap is written.
|
|
607
616
|
|
|
608
|
-
|
|
617
|
+
Default as a 1-based index, never a label - same rule and same reason as Step 7.5:
|
|
609
618
|
|
|
610
619
|
```bash
|
|
611
620
|
DEPTH_DEFAULT_INDEX=1
|
|
@@ -119,19 +119,33 @@ Phase 0 Step 1b catalogued every typed external link from the task description i
|
|
|
119
119
|
|
|
120
120
|
#### Step 2 - Stack Detection
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
Two questions shared one answer here, and the shared answer was wrong on both.
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
| -------------- | ----------------------------------------------- | ------------------------------------------------------------ |
|
|
126
|
-
| iOS/Swift | `.xcodeproj`, `Package.swift` | `ai-ios-toolkit:*` skills, `ai-ios-toolkit:swift-testing` |
|
|
127
|
-
| Android/Kotlin | `build.gradle`, `build.gradle.kts` | `ai-android-toolkit:android-jetpack-compose-expert`, `ai-android-toolkit:kotlin-coroutines-expert` |
|
|
128
|
-
| Python | `requirements.txt`, `pyproject.toml`, `Pipfile` | `ai-backend-toolkit:fastapi-pro`, `ai-backend-toolkit:api-patterns` |
|
|
129
|
-
| Node.js | `package.json` | `ai-backend-toolkit:nodejs-backend-patterns`, `ai-backend-toolkit:api-patterns` |
|
|
130
|
-
| Go | `go.mod` | `ai-backend-toolkit:api-patterns`, `ai-backend-toolkit:clean-code` |
|
|
131
|
-
| Docker | `Dockerfile`, `docker-compose.yml` | `ai-backend-toolkit:docker-expert` |
|
|
132
|
-
| Monorepo | Multiple of above | `ai-backend-toolkit:monorepo-architect` |
|
|
124
|
+
**What the repo is BUILT WITH** - one owner, no marker list in this file:
|
|
133
125
|
|
|
134
|
-
|
|
126
|
+
```bash
|
|
127
|
+
eval "$(bash $HOME/.claude/lib/stack-detect.sh "$PROJECT_ROOT")"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Persist `MA_STACKS` as `state.stacks[]` (a subset of `ios android web backend`,
|
|
131
|
+
always in that order) and `MA_STACK_WHY` as `state.stackWhy`. Empty is a real
|
|
132
|
+
answer meaning no marker matched; `stackWhy` separates that from a directory that
|
|
133
|
+
could not be read, and a caller that cannot tell those apart treats an unreadable
|
|
134
|
+
repo as a language-free one. `state.stacks[]` routes toolkits, through
|
|
135
|
+
`pluginsForStacks()` in `$HOME/.claude/scripts/_stack-routing.mjs`. Do not derive
|
|
136
|
+
plugin names here.
|
|
137
|
+
|
|
138
|
+
The table that used to live here scanned at depth 2, and `AndroidManifest.xml`
|
|
139
|
+
sits at `<module>/src/main/` - depth 4 in every multi-module app - so **Android
|
|
140
|
+
was never detected**. The owner goes to depth 5 for that one file, prunes
|
|
141
|
+
submodules (a vendored checkout ships its own `Package.swift`, which reported a
|
|
142
|
+
Compose app as iOS) and checks the root first so traversal order cannot decide.
|
|
143
|
+
|
|
144
|
+
**What the code is WRITTEN IN** - a different axis with its own field.
|
|
145
|
+
`state.detectedStack[]` stays the language answer (`ios`, `python`, `node`, `go`,
|
|
146
|
+
`docker`, `monorepo`): `graph-build.mjs --stack` takes `ios|android|node|python|go`
|
|
147
|
+
and has no notion of `web` or `backend`. Folding the two together is what made a
|
|
148
|
+
Gradle-built JVM service read as an Android app.
|
|
135
149
|
|
|
136
150
|
This informs:
|
|
137
151
|
|
|
@@ -211,7 +225,7 @@ Progress line: ` → checking validator validate-analysis`
|
|
|
211
225
|
|
|
212
226
|
Non-zero exit fails CLOSED: emit the validator stderr + `errors[]` verbatim, attempt ONE self-correction rework (re-invoke the explorer with the errors quoted, overwrite `$ANALYSIS_FILE`), re-run the validator. If it fails again -> HALT the phase with recovery hint: `ERR: analysis output failed validate-analysis.mjs twice. Inspect $ANALYSIS_FILE against $HOME/.claude/schemas/analysis-output.schema.json, then resume with /multi-agent:resume #N.` Record `agent-state.phases["1"].validator` (`pass` | `pass-after-rework` | `halted`).
|
|
213
227
|
|
|
214
|
-
Log: "Phase 1: Analysis - stack:{detectedStack} | {N} files identified, {summary}"
|
|
228
|
+
Log: "Phase 1: Analysis - stack:{stacks} lang:{detectedStack} | {N} files identified, {summary}"
|
|
215
229
|
|
|
216
230
|
#### Telemetry - token forwarding
|
|
217
231
|
|
|
@@ -7,7 +7,7 @@ Progress emission per `$HOME/.claude/multi-agent-refs/progress-contract.md` -
|
|
|
7
7
|
|
|
8
8
|
## Phase 2 Pre-flight (BLOCKING, v9.0.0)
|
|
9
9
|
|
|
10
|
-
Phase 2 Planning consumes the analysis document. MCP forbidden.
|
|
10
|
+
Phase 2 Planning consumes the analysis document. **Figma** MCP / REST forbidden; the toolkit MCP is not.
|
|
11
11
|
|
|
12
12
|
1. **Analysis document presence**: read `state.analysis.docStatus`, which Phase 1 Step 4 set.
|
|
13
13
|
- `produced` | `reused` -> the file is at `state.analysis.docPath[]`; continue with steps 2-4.
|
|
@@ -137,27 +137,29 @@ Non-zero exit fails CLOSED: emit the validator stderr + `errors[]` verbatim, att
|
|
|
137
137
|
|
|
138
138
|
Log: "Phase 2: Plan - {N} tasks created, {M} with architecture review, validator:pass"
|
|
139
139
|
|
|
140
|
+
#### Step 4.45 - Put the plan on the widget (required)
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
printf '%s' "$PLAN_JSON" | bash "$HOME/.claude/scripts/phase-tracker.sh" plan 3
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Then do what its output asks - it prints a tile rebuild, because the widget
|
|
147
|
+
orders by creation. `dependsOn` becomes `addBlockedBy`, so the widget answers
|
|
148
|
+
"why has t3 not started". Required, unlike Step 4.5: the plan was always computed
|
|
149
|
+
and stored, only invisible.
|
|
150
|
+
|
|
140
151
|
#### Step 4.5 - Emit Plan Todo List (opt-in)
|
|
141
152
|
|
|
142
153
|
**Gated by `prefs.global.planTodos.enabled`** (default: `false`). When enabled, after the planning-output JSON validates and BEFORE the approval gate, transform `tasks[]` into a structured Todo list conforming to `$HOME/.claude/schemas/plan-todos.schema.json` and persist into `agent-state.plan`. The plan is rendered as a live, always-visible Todo list.
|
|
143
154
|
|
|
144
155
|
```bash
|
|
145
|
-
|
|
146
|
-
{
|
|
147
|
-
title: .summary,
|
|
148
|
-
todos: [ .tasks[] | {
|
|
149
|
-
id: .id,
|
|
150
|
-
task: .subject,
|
|
151
|
-
status: "pending",
|
|
152
|
-
deps: (.dependsOn // .blockedBy // []),
|
|
153
|
-
estimatedMinutes: (.estimatedMinutes // null)
|
|
154
|
-
} | with_entries(select(.value != null)) ]
|
|
155
|
-
}
|
|
156
|
-
' <<<"$PLAN_JSON")
|
|
157
|
-
|
|
158
|
-
bash "$HOME/.claude/lib/plan-todos.sh" set "$TASK_ID" "$TODO_BLOB"
|
|
156
|
+
printf '%s' "$PLAN_JSON" | bash "$HOME/.claude/lib/plan-todos.sh" set "$TASK_ID" -
|
|
159
157
|
```
|
|
160
158
|
|
|
159
|
+
`set` accepts a planning-output document and converts it itself. The conversion
|
|
160
|
+
used to be written out here, which made the `tasks[]`-to-`todos[]` mapping a
|
|
161
|
+
thing two files defined, and this was the copy nothing tested.
|
|
162
|
+
|
|
161
163
|
Phase 3 (Dev) then iterates with `plan-todos.sh next "$TASK_ID"` until empty, calling `start` before each step and `complete` (with notes) or `fail`/`skip` after. Phase 4 (Review) reads the Todo list to verify all `completed` items map to diff hunks. Phase 7 (Report) renders `list` into the agent-log + PR body.
|
|
162
164
|
|
|
163
165
|
**Why opt-in:** the existing `planning-output.schema.json` already drives Phase 3 dependency order - `plan.todos[]` is a richer surface (notes, durations, status transitions) but adds state writes per step. Off by default to keep the bare-bones flow unchanged; flip on for visibility into long features.
|