@mmerterden/multi-agent-pipeline 17.4.0 → 17.5.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 +136 -0
- package/README.md +23 -5
- package/README.tr.md +23 -5
- package/docs/adr/0013-lsp-code-intelligence.md +102 -0
- package/docs/adr/README.md +1 -0
- package/docs/token-budget-history.md +1 -1
- package/install/templates/copilot-instructions.md +9 -3
- package/package.json +1 -1
- package/pipeline/commands/multi-agent/analysis/SKILL.md +3 -3
- package/pipeline/commands/multi-agent/autopilot/SKILL.md +3 -3
- package/pipeline/commands/multi-agent/autopilot-off/SKILL.md +5 -3
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/local/SKILL.md +17 -6
- package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +3 -3
- package/pipeline/lib/multi-repo-pipeline.sh +26 -0
- package/pipeline/multi-agent-refs/features/base-branch-evidence.md +222 -0
- package/pipeline/multi-agent-refs/features/code-intelligence.md +80 -0
- package/pipeline/multi-agent-refs/phases/modes.md +23 -3
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +96 -71
- package/pipeline/multi-agent-refs/phases/phase-7-report.md +1 -1
- package/pipeline/multi-agent-refs/phases.md +7 -2
- package/pipeline/multi-agent-refs/picker-contract.md +37 -5
- package/pipeline/multi-agent-refs/tracker-contract.md +25 -14
- package/pipeline/schemas/agent-state.schema.json +88 -4
- package/pipeline/schemas/prefs.schema.json +22 -0
- package/pipeline/schemas/token-budget.json +2 -2
- package/pipeline/scripts/autopilot-runner.mjs +292 -45
- package/pipeline/scripts/base-branch-candidates.mjs +599 -0
- package/pipeline/scripts/gc-abandoned.sh +5 -3
- package/pipeline/scripts/gen-mode-dispatch.mjs +39 -16
- package/pipeline/scripts/phase-tracker.sh +39 -2
- package/pipeline/scripts/phase0-exit-gate.mjs +128 -0
- package/pipeline/scripts/verify-citations.mjs +84 -2
- package/pipeline/skills/.skill-manifest.json +2 -2
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
### Phase 0: Init
|
|
2
2
|
|
|
3
|
-
> **TLDR** -
|
|
3
|
+
> **TLDR** - every input type flows through the same numbered steps below, in order, and ends at the exit gate.
|
|
4
4
|
|
|
5
5
|
#### Step −1 - Bootstrap the cross-CLI tracker (FIRST thing in every run)
|
|
6
6
|
|
|
@@ -9,13 +9,17 @@ Before anything else - initialize the visual tracker so the user sees the pipe
|
|
|
9
9
|
```bash
|
|
10
10
|
TASK_ID="${INPUT_TASK_ID:-pipeline-$(date +%Y%m%d-%H%M%S)}"
|
|
11
11
|
$HOME/.claude/scripts/phase-tracker.sh init "$TASK_ID"
|
|
12
|
-
|
|
13
|
-
$HOME/.claude/scripts/phase-tracker.sh add "${p%%:*}" "${p#*:}"
|
|
14
|
-
done
|
|
12
|
+
$HOME/.claude/scripts/phase-tracker.sh add 0 Init
|
|
15
13
|
$HOME/.claude/scripts/phase-tracker.sh tiles
|
|
16
14
|
$HOME/.claude/scripts/phase-tracker.sh update 0 in_progress
|
|
17
15
|
```
|
|
18
16
|
|
|
17
|
+
**Phase 0 only.** `/multi-agent` and `:local` do not know their phase set yet -
|
|
18
|
+
depth decides it, and depth is Step 7.5 - so they register the rest there rather
|
|
19
|
+
than drawing eight tiles the user has not chosen. Every other mode registers its
|
|
20
|
+
whole set here, in phase-number order. Contract: `tracker-contract.md`,
|
|
21
|
+
"Deferred registration".
|
|
22
|
+
|
|
19
23
|
`tiles` prints this host's widget-registration calls: **make them before continuing.** The card alone lands in collapsed tool output, so a run that skips them runs in silence. Contract: `tracker-contract.md`, "The card is not the widget".
|
|
20
24
|
|
|
21
25
|
If `INPUT_TASK_ID` isn't known yet (free-text, project not selected), use a placeholder; rename later via `mv` once parsed in Step 1.
|
|
@@ -26,7 +30,7 @@ Every subsequent phase (1-7) MUST call `phase-tracker.sh update <N> in_progress`
|
|
|
26
30
|
|
|
27
31
|
##### TaskCreate ordering on Claude Code (strict)
|
|
28
32
|
|
|
29
|
-
On Claude Code, fire
|
|
33
|
+
On Claude Code, fire every `TaskCreate` in a registration batch in strict phase-number order BEFORE any `TaskUpdate` in that batch - which is the order `tiles` prints them in - and never register a phase whose number is below one already registered. A deferred batch (Step 7.5) therefore appends, it does not interleave. Full contract: `$HOME/.claude/multi-agent-refs/tracker-contract.md` section "TaskCreate ordering (strict)".
|
|
30
34
|
|
|
31
35
|
---
|
|
32
36
|
|
|
@@ -261,58 +265,76 @@ Scan `$HOME` (maxdepth 2) for project markers (`.xcodeproj`, `Package.swift`, `b
|
|
|
261
265
|
|
|
262
266
|
**Test policy (once per project).** Resolve `prefs.projects[{slug}].testPolicy` → `state.testPolicy`; missing → native picker "Should development write tests here?" (`header`: "Tests"): `tdd` (recommended) / `tests-after` / `none`; persist. Autopilot without a record: `tdd`, noted.
|
|
263
267
|
|
|
268
|
+
#### Step 2b - Dev context (extra repos)
|
|
269
|
+
|
|
270
|
+
Run `$HOME/.claude/multi-agent-refs/_dev-context.md`: `.gitmodules` submodules
|
|
271
|
+
plus `prefs.projects[{project}].webRepos[]`, editable ones pre-selected,
|
|
272
|
+
read-only siblings listed. It runs on every input type, direct-ID included, and
|
|
273
|
+
an empty submit is a valid answer meaning primary repo only. Here rather than
|
|
274
|
+
later because a base branch is a property of a repo set (`picker-contract.md`,
|
|
275
|
+
"Order: project, then repo, then branch").
|
|
276
|
+
|
|
277
|
+
Persist `state.siblings[]` even when empty: the empty array is the record that
|
|
278
|
+
the step ran. Absent, Phase 4's parity cross-check cannot tell "no siblings"
|
|
279
|
+
from "never asked", and the exit gate below fails.
|
|
280
|
+
|
|
264
281
|
#### Step 3 - Remote Detection + Branch Selection
|
|
265
282
|
|
|
266
283
|
1. **Check preferences first**: If `prefs.projects[{project}].remoteType` exists, use cached value.
|
|
267
284
|
2. Read remote: `git -C $PROJECT_ROOT remote get-url origin`
|
|
268
285
|
3. Detect type: `github.com` → github (`gh` CLI), `{BITBUCKET_HOST}` → bitbucket (API + `keychainMapping.bitbucket_token`), other → generic-git. Save to `prefs.projects[{project}].remoteType`.
|
|
269
286
|
4. **Skip if baseBranch already set** (from Step 1). Otherwise:
|
|
270
|
-
5. Fetch + list PR-targetable branches
|
|
287
|
+
5. Fetch + list PR-targetable branches. **Capture the exit code** - it decides
|
|
288
|
+
whether the list may be called the remote's answer:
|
|
271
289
|
```bash
|
|
272
|
-
git -C $PROJECT_ROOT fetch origin
|
|
290
|
+
git -C $PROJECT_ROOT fetch origin; FETCH_RC=$?
|
|
273
291
|
git -C $PROJECT_ROOT branch -r --sort=-committerdate \
|
|
274
|
-
| grep -E '(
|
|
275
|
-
| grep -
|
|
292
|
+
| grep -v -E '(feature/|bugfix/|fix/|hotfix/|chore/)' \
|
|
293
|
+
| grep -E '([0-9]+[._][0-9]+|develop|release|main|master)'
|
|
276
294
|
```
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
`
|
|
295
|
+
**Keep the version alternative.** A branch carrying a version token is a
|
|
296
|
+
candidate whatever it is called; the family words only RANK. Why that
|
|
297
|
+
distinction matters: `features/base-branch-evidence.md`, "A filter is not a
|
|
298
|
+
ranking".
|
|
299
|
+
5b. With `prefs.global.baseBranchEvidence` on, pipe that list plus the issue's
|
|
300
|
+
version fields and links through `$HOME/.claude/scripts/base-branch-candidates.mjs`,
|
|
301
|
+
which ranks the candidates and records the evidence behind each. It is pure, so
|
|
302
|
+
Step 3 owns every git and network call. Contract - autopilot resolution order and
|
|
303
|
+
the fenced ask-on-the-issue path included:
|
|
304
|
+
`$HOME/.claude/multi-agent-refs/features/base-branch-evidence.md`. Off: rule 6 alone.
|
|
305
|
+
6. Sort: `develop*` first, then `release/*`, then `main`/`master` (the collector's
|
|
306
|
+
order when it ran). Surface through the **native picker** per `picker-contract.md`,
|
|
307
|
+
recent branch first and marked `(Recommended)`, each row carrying its evidence:
|
|
280
308
|
|
|
281
309
|
```
|
|
282
310
|
header: "Base branch"
|
|
283
311
|
options: origin/develop (Recommended, reused from last run) | origin/main | release/8.4.0 | Other
|
|
284
312
|
```
|
|
285
|
-
7. User picks → store as `baseBranch`, and append `{branch, lastUsed, count?}` to
|
|
313
|
+
7. User picks → store as `baseBranch` with `baseBranchSource: "asked"`, and append `{branch, lastUsed, count?}` to
|
|
286
314
|
`prefs.global.recentBranches[{projectKey}]` (dedup by `branch`, cap 10) - what the TTL
|
|
287
315
|
filter below reads. Key is `branch`, not `name`. Never the legacy
|
|
288
316
|
`projects[{project}].branches`.
|
|
289
317
|
|
|
290
318
|
**MUST: this step is not skippable (BLOCKING).** The only legitimate skip is rule 4
|
|
291
|
-
above
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
nothing surfaced it. `phase0-exit-gate.mjs` now refuses to close Phase 0 unless
|
|
295
|
-
`agent-state.json` carries `baseBranch` and `baseFetchStatus`, so a skipped picker is a
|
|
296
|
-
gate failure rather than a silent default.
|
|
319
|
+
above, recorded as `baseBranchSource: "input"`. Everything else asks, and
|
|
320
|
+
`phase0-exit-gate.mjs` refuses to close Phase 0 without `baseBranch`,
|
|
321
|
+
`baseFetchStatus` and `baseBranchSource` - a skipped picker fails the gate.
|
|
297
322
|
|
|
298
|
-
One row is a normal outcome of the rule-5 filter and is **still asked
|
|
299
|
-
`picker-contract.md
|
|
323
|
+
One row is a normal outcome of the rule-5 filter and is **still asked**, with a
|
|
324
|
+
real second option - `picker-contract.md`, "Two options or it is not a question".
|
|
300
325
|
|
|
301
326
|
This holds in every mode. A Short run skips the *LLM* phases (Analysis, Planning); it
|
|
302
327
|
does not skip Phase 0's pickers. Autopilot resolves them without prompting, which still
|
|
303
|
-
writes the fields
|
|
304
|
-
recent inside the TTL, still on the remote) before the rule-6 order, recording which
|
|
305
|
-
fired in `state.baseBranchSource`.
|
|
328
|
+
writes the fields; its base-branch resolution order is in `features/base-branch-evidence.md`.
|
|
306
329
|
|
|
307
330
|
**TTL filter for recent branches**:
|
|
308
331
|
|
|
309
|
-
- `prefs.global.recentBranches[{projectKey}][]` carries `{branch, lastUsed, count?}`.
|
|
310
|
-
- Stale entries (>TTL) are pruned in-place during the read - keeps the picker uncluttered without a separate cleanup pass.
|
|
332
|
+
- `prefs.global.recentBranches[{projectKey}][]` carries `{branch, lastUsed, count?}`. Keep those whose `lastUsed` is within `settings.branchTtlDays` (default 15); prune the rest in place during the read.
|
|
311
333
|
- The filtered "Recent" list precedes the fresh `git branch -r` list; cap at 5 visible recent entries.
|
|
312
334
|
|
|
313
335
|
**Fetch-fail handling** (replaces silent `git fetch origin` failure):
|
|
314
336
|
|
|
315
|
-
|
|
337
|
+
`FETCH_RC` non-zero MUST not silently fall back to a stale cached ref. Surface the **native picker** (per `picker-contract.md`) with 4 options:
|
|
316
338
|
|
|
317
339
|
```
|
|
318
340
|
question: "git fetch failed for {project} - the base ref may be stale. How should I proceed?"
|
|
@@ -325,19 +347,13 @@ options:
|
|
|
325
347
|
Abort no worktree, no branch, no state file
|
|
326
348
|
```
|
|
327
349
|
|
|
328
|
-
**Say which is which.**
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
month-old ref. "Connect VPN and retry" re-runs the fetch and re-enters this picker if it
|
|
332
|
-
fails again; it is a real retry, not a label.
|
|
350
|
+
**Say which is which.** Name the host when the remote points at one - an unresolvable
|
|
351
|
+
`{BITBUCKET_HOST}` is almost always the VPN - and make the retry real: it re-runs the
|
|
352
|
+
fetch and re-enters this picker on a second failure.
|
|
333
353
|
|
|
334
|
-
Persist
|
|
335
|
-
```
|
|
336
|
-
⚠️ Base ref stale (fetch fail @ {ts}, choice: {cached-stale|local-branch})
|
|
337
|
-
```
|
|
338
|
-
Phase 6 (commit/push) MUST re-attempt `git fetch origin` before push; if successful, prompt to rebase before pushing.
|
|
354
|
+
Persist the choice in `agent-state.json.baseFetchStatus` ∈ `"fresh" | "cached-stale" | "local-branch" | "aborted"`, and on any non-fresh one log `⚠️ Base ref stale (fetch fail @ {ts}, choice: {status})`. Phase 6 re-attempts `git fetch origin` before push and prompts to rebase if it succeeds.
|
|
339
355
|
|
|
340
|
-
In multi-repo mode
|
|
356
|
+
In multi-repo mode the prompt fires per repo, and Abort on any one aborts the whole task (atomic - no partial worktrees).
|
|
341
357
|
|
|
342
358
|
#### Step 4 - Branch Naming (automatic)
|
|
343
359
|
|
|
@@ -365,10 +381,8 @@ Branch name is deterministic - no user confirmation needed.
|
|
|
365
381
|
|
|
366
382
|
**Collision handling** (automatic - no prompt):
|
|
367
383
|
- Probe local + remote for existing branch. **Distinguish "no such ref" from "the
|
|
368
|
-
probe failed"**: with `2>/dev/null` and an empty-output test
|
|
369
|
-
|
|
370
|
-
branch that already exists on the remote - surfacing as a rejected push at
|
|
371
|
-
Phase 6, far from its cause.
|
|
384
|
+
probe failed"**: with `2>/dev/null` and an empty-output test a failed probe reads
|
|
385
|
+
as "no collision", and the duplicate branch surfaces as a rejected push at Phase 6.
|
|
372
386
|
```bash
|
|
373
387
|
LOCAL_HIT=$(git -C "$root" rev-parse --verify --quiet "refs/heads/$branch")
|
|
374
388
|
REMOTE_ERR=$(git -C "$root" ls-remote --exit-code --heads origin "$branch" 2>&1 >/dev/null)
|
|
@@ -395,6 +409,21 @@ Branch name is deterministic - no user confirmation needed.
|
|
|
395
409
|
3. Map instruction files to `"instructionFiles": { "start": "...", "validate": "...", "dev": "...", "commit": "..." }`
|
|
396
410
|
4. Instruction-driven → later phases read SKILL.md; no instructions → standard phases
|
|
397
411
|
|
|
412
|
+
#### Step 5b - Workspace (worktree or local)
|
|
413
|
+
|
|
414
|
+
Ask where the branch lives - the wording, the two options and what local costs
|
|
415
|
+
are in `modes.md`, "Local Mode". Here because Step 4 named the branch, Step 6b
|
|
416
|
+
acts on the answer, and Step 7.5 needs the phase set it implies.
|
|
417
|
+
|
|
418
|
+
**Who is asked.** `/multi-agent` only (`workspaceSource: "asked"`). `:local` and
|
|
419
|
+
`--local` state it up front (`command`).
|
|
420
|
+
Every autopilot entry resolves it to a worktree and never asks (`autopilot`).
|
|
421
|
+
|
|
422
|
+
**Persist** `state.localMode` (semantics unchanged) and `state.workspaceSource`,
|
|
423
|
+
which is what separates a worktree the user chose from one nothing asked about.
|
|
424
|
+
|
|
425
|
+
Log: `Phase 0 Step 5b: workspace = {worktree|local} (source {asked|command|autopilot})`
|
|
426
|
+
|
|
398
427
|
#### Step 6 - Branch + Workspace Setup
|
|
399
428
|
|
|
400
429
|
**6a. Resolve git identity** (automatic, no prompt):
|
|
@@ -419,7 +448,7 @@ Log: `Identity: {identity.name} <{identity.email}>`
|
|
|
419
448
|
|
|
420
449
|
1. `git -C $PROJECT_ROOT fetch origin`
|
|
421
450
|
|
|
422
|
-
**If
|
|
451
|
+
**If local** (Step 5b answered local, by question, by `:local` / `--local`):
|
|
423
452
|
|
|
424
453
|
```bash
|
|
425
454
|
if [ -n "$(git -C $PROJECT_ROOT status --porcelain)" ]; then
|
|
@@ -430,9 +459,10 @@ git -C $PROJECT_ROOT config user.name "{identity.name}"
|
|
|
430
459
|
git -C $PROJECT_ROOT config user.email "{identity.email}"
|
|
431
460
|
```
|
|
432
461
|
|
|
433
|
-
`worktreePath` = `$PROJECT_ROOT`, `localMode` = `true`.
|
|
462
|
+
`worktreePath` = `$PROJECT_ROOT`, `localMode` = `true`. Step 5b already recorded
|
|
463
|
+
`workspaceSource`; this step does not decide, it applies.
|
|
434
464
|
|
|
435
|
-
**If
|
|
465
|
+
**If worktree** (Step 5b answered worktree, or autopilot resolved it): 2. Worktree path: Jira → `.worktrees/{jiraId}/`, GitHub → `.worktrees/GH{issueNo}/`, free-text → `.worktrees/task-{shortId}/` 3. **Heal stale admin state first** (see "Worktree stale-lock heal" below) and **apply the residue guard** (see "Worktree residue guard" below), then `git -C $PROJECT_ROOT worktree add {path} -b {branch} origin/{baseBranch}` (if exists: enter, pull) 4. Set identity: `git -C {worktree-path} config user.name/email` 5. Create log dir + `agent-log.md` + `agent-state.json` at `$HOME/.claude/logs/multi-agent/{project}/{task-id}/`, never inside the worktree:
|
|
436
466
|
|
|
437
467
|
**Worktree location convention (cited by every other command):** always `{projectRoot}/.worktrees/{taskId}`, inside the repo, never under `$HOME`. `{taskId}` is the directory name from the rule above (`DC-<shortId>` for `/multi-agent:design-check`). `.worktrees` is fixed, not a preference: no `worktreeBasePath` key exists, and `gc-worktrees.sh`, `purge.sh`, the cost renderers and `usage-report.mjs` resolve `<repo>/.worktrees/` by name. Multi-repo tasks get one worktree per repo (the loop below); `--local` creates none and `worktreePath` is `$PROJECT_ROOT`.
|
|
438
468
|
|
|
@@ -484,7 +514,7 @@ done
|
|
|
484
514
|
|
|
485
515
|
State file in multi-repo mode:
|
|
486
516
|
- Single shared `agent-state.json` lives at `$HOME/.claude/logs/multi-agent/{first-project}/{task-id}/agent-state.json` (anchored on the first repo for back-compat with `multi-agent log`/`status` commands)
|
|
487
|
-
-
|
|
517
|
+
- Every update to it, here and in every later phase, goes through `node $HOME/.claude/scripts/write-state.mjs` - the required mechanism in `operations.md` "Writing `agent-state.json`", and the race a per-repo read-modify-write loses `projects[]` entries to.
|
|
488
518
|
- `state.projects[]` holds per-repo `{name, root, worktreePath, branch, baseBranch, identity, platform, baseFetchStatus, commit, pr, pushAttempts, buildStatus}` - see `agent-state.schema.json`
|
|
489
519
|
- Scalar fields (`project`, `projectRoot`, `worktreePath`, `branch`, `baseBranch`, `identity`) mirror `projects[0]` so legacy phases that read scalars keep working
|
|
490
520
|
- Atomicity: if any repo's worktree creation fails (collision aborted, fetch aborted, disk full), roll back already-created worktrees: `git -C $proj worktree remove --force $WT_PATH; git -C $proj branch -D $BRANCH`. Never leave a partial multi-repo state.
|
|
@@ -562,7 +592,7 @@ Ask the depth question from `$HOME/.claude/multi-agent-refs/phases/modes.md` "Pi
|
|
|
562
592
|
|
|
563
593
|
When the intake carried an analysis document or a Figma reference, say so **inside** the question: Short skips the only two phases that would turn that document into a task breakdown, and the user should learn that before choosing, not after.
|
|
564
594
|
|
|
565
|
-
**Pass the default
|
|
595
|
+
**Pass the default as a 1-based index, never a label.** `ask-choice.sh` takes the first option on a non-TTY, and a label-valued default matches nothing once the options render in `outputLanguage`. Reasoning: `modes.md`, "Pipeline depth".
|
|
566
596
|
|
|
567
597
|
```bash
|
|
568
598
|
# Full is option 1, Short is option 2 (modes.md "Pipeline depth")
|
|
@@ -572,7 +602,9 @@ ASK_CHOICE_DEFAULT="$DEPTH_DEFAULT_INDEX" \
|
|
|
572
602
|
"<localized: 'Full'>" "<localized: 'Short'>"
|
|
573
603
|
```
|
|
574
604
|
|
|
575
|
-
**Persist.** Short sets `state.onlyDevelop = true`; Full leaves it `false`. The key is unchanged - only who sets it changed - so every downstream reader keeps working.
|
|
605
|
+
**Persist.** Short sets `state.onlyDevelop = true`; Full leaves it `false`. The key is unchanged - only who sets it changed - so every downstream reader keeps working.
|
|
606
|
+
|
|
607
|
+
**Now register the rest of the widget.** This answer is the first moment the phase set is known, so the remaining tiles are created here and not before - Full `1 2 3 4 5 6 7`, Short `3 4 5 6 7`, `:local` dropping 5 from either. `add` each, then `phase-tracker.sh tiles --new`, which emits TaskCreate only for phases that carry no tile yet. Contract: `tracker-contract.md`, "Deferred registration".
|
|
576
608
|
|
|
577
609
|
Log: `Phase 0 Step 7.5: depth = {full|short} (recommended {full|short}, source {user|autopilot|default})`
|
|
578
610
|
|
|
@@ -585,7 +617,7 @@ BASELINE_LOG="$WORKTREE/.baseline-test.log"
|
|
|
585
617
|
timeout "${prefs_testBaseline_timeoutSeconds:-600}" <same-test-command-as-Phase-4-Gate-3> 2>&1 | tee "$BASELINE_LOG"
|
|
586
618
|
```
|
|
587
619
|
|
|
588
|
-
Persist `state.baseline.tests` with `command`, `capturedAt`, `logPath` and exactly one status: `green` (passed), `red` + `failing[]` (failed, names parsed), `red` + empty `failing[]` (failed, names unparseable), `unknown` (no test command, `timeout` fired, or flag off).
|
|
620
|
+
Persist `state.baseline.tests` with `command`, `capturedAt`, `logPath` and exactly one status: `green` (passed), `red` + `failing[]` (failed, names parsed), `red` + empty `failing[]` (failed, names unparseable), `unknown` (no test command, `timeout` fired, or flag off). Never fold `unknown` into `green`: a skipped baseline would read as a clean tree.
|
|
589
621
|
|
|
590
622
|
Log: `Phase 0 Step 7.6: test baseline = {green|red|unknown} ({N} pre-existing failures)`
|
|
591
623
|
|
|
@@ -606,9 +638,7 @@ if [ -n "$EVIDENCE_PLATFORM" ]; then
|
|
|
606
638
|
fi
|
|
607
639
|
```
|
|
608
640
|
|
|
609
|
-
Empty is an outcome, not a failure: backend has no device, so the probe does not run and `evidenceCapability.skippedReason` says so. Web does
|
|
610
|
-
|
|
611
|
-
One run, both forms: stdout is `EVIDENCE_*` (shell-quoted, so the eval is safe), and the same measurement lands as JSON.
|
|
641
|
+
Empty is an outcome, not a failure: backend has no device, so the probe does not run and `evidenceCapability.skippedReason` says so. Web does - the browser the runner drives is the device (4.6). No `--changed` yet. One run, both forms: stdout is `EVIDENCE_*` (shell-quoted, so the eval is safe), and the same measurement lands as JSON.
|
|
612
642
|
|
|
613
643
|
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).
|
|
614
644
|
|
|
@@ -647,11 +677,7 @@ Log: `Phase 0 Step 7.7: testDepth = {unit|unit+ui|unit+mcp} (source {user|autopi
|
|
|
647
677
|
|
|
648
678
|
5. Phase 1 Analysis reads `state.clarification.userAnswers` (when present) as additional context - fold answers into the Explore prompt so downstream phases inherit the resolution.
|
|
649
679
|
|
|
650
|
-
**
|
|
651
|
-
|
|
652
|
-
**Reference:** see `$HOME/.claude/agents/task-clarifier.md` for the full scoring rubric and question-quality rules.
|
|
653
|
-
|
|
654
|
-
**Why this fits Phase 0 (not a new phase):** clarification doesn't change what code gets written - it changes what gets understood before code is written. Phase 0 already collects identity / project / branch / maturity; ambiguity scoring fits naturally as the last contextual gate.
|
|
680
|
+
**Reference:** `$HOME/.claude/agents/task-clarifier.md` - scoring rubric, question-quality rules and the ~$0.0025-per-call cost note.
|
|
655
681
|
|
|
656
682
|
#### Telemetry
|
|
657
683
|
|
|
@@ -686,20 +712,19 @@ node "$HOME/.claude/scripts/usage-report.mjs" --task-id "$TASK_ID" >/dev/null 2>
|
|
|
686
712
|
The second line reports the run as started: reporting only from Phase 7 reported
|
|
687
713
|
only runs that finish, and few do. Phase 7 upserts the same key over it.
|
|
688
714
|
|
|
689
|
-
It asserts
|
|
715
|
+
It asserts five things, each of which has failed silently in a real run:
|
|
690
716
|
|
|
691
|
-
1. **`agent-state.json` exists.**
|
|
692
|
-
|
|
693
|
-
not there.
|
|
694
|
-
2. **`taskType` is set.** Phase 3 branches on it (Step 7). Absent, a Figma-driven
|
|
695
|
-
screen is dispatched as generic development, skipping the stack plugin's
|
|
696
|
-
token-compliance check, Code Connect publish and component review. That run
|
|
697
|
-
guessed `16` where the frame said `Spacing/12`, and half its commits were rework.
|
|
717
|
+
1. **`agent-state.json` exists.** Every later phase reasons from it.
|
|
718
|
+
2. **`taskType` is set.** Phase 3 branches on it (Step 7).
|
|
698
719
|
3. **A Figma reference forces `taskType: "component"`, and `figmaAccess.tier` is
|
|
699
|
-
recorded
|
|
700
|
-
|
|
720
|
+
recorded**, so a later phase can tell a confirmed design from an unfetched one.
|
|
721
|
+
4. **`baseBranchSource` is recorded**, and an interactive run recorded `asked` or
|
|
722
|
+
`input` - the only field that separates a branch that was chosen from one that
|
|
723
|
+
was announced.
|
|
724
|
+
5. **`siblings` is an array**, empty included: Step 2b's record that it ran.
|
|
725
|
+
|
|
726
|
+
Each failed silently in a real run; the script header names which.
|
|
701
727
|
|
|
702
|
-
A failure is a halt, not a warning
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
consumes.
|
|
728
|
+
A failure is a halt, not a warning: fix the state, re-run the gate, and leave the phase
|
|
729
|
+
`in_progress` until it passes. Never close Phase 0 on the grounds that its steps ran -
|
|
730
|
+
the gate checks the output, which is what Phase 3 consumes.
|
|
@@ -323,7 +323,7 @@ If user does not respond at the channels multi-select menu within 30 minutes (wa
|
|
|
323
323
|
1. Channels command aborts its interactive prompt, returns `{status: "timeout"}`.
|
|
324
324
|
2. Phase 7 records `phase-tracker.sh sub 7 1 "Channels dispatch" timeout`.
|
|
325
325
|
3. **Internal capture still runs** - Steps 2 + 3 write `agent-log.md` (with `channels: timeout` in summary), emit telemetry, update knowledge base.
|
|
326
|
-
4. Session exits cleanly. State persisted: `state.
|
|
326
|
+
4. Session exits cleanly. State persisted: `state.currentPhase=7, state.waitingFor="user-channels-choice", state.channelsTimeout=true`.
|
|
327
327
|
5. Resume contract: `/multi-agent:resume <task-id>` re-opens the channels menu with the original state bundle (pipeline log, PR metadata, prefs pre-ticks).
|
|
328
328
|
|
|
329
329
|
Rationale: silently posting defaults to Jira / Confluence after a timeout would leak wrong-tone content to external systems. Hard-stop + resume is the safer policy.
|
|
@@ -90,7 +90,7 @@ Two channels run in parallel at every phase boundary. Both are required in their
|
|
|
90
90
|
|
|
91
91
|
### Tracker bootstrap (Phase 0, mandatory)
|
|
92
92
|
|
|
93
|
-
Phase 0 MUST initialize the tracker and register
|
|
93
|
+
Phase 0 MUST initialize the tracker and register the active mode's phase set:
|
|
94
94
|
|
|
95
95
|
```bash
|
|
96
96
|
$HOME/.claude/scripts/phase-tracker.sh init "$TASK_ID"
|
|
@@ -101,6 +101,11 @@ done
|
|
|
101
101
|
|
|
102
102
|
This produces an initial card stack printed by both CLIs.
|
|
103
103
|
|
|
104
|
+
`/multi-agent` and `/multi-agent:local` are the exception: they do not know their
|
|
105
|
+
set here, because depth decides it at Step 7.5. They register `0:Init` alone, then
|
|
106
|
+
the rest once the answer lands, and call `tiles --new` for the second batch. Full
|
|
107
|
+
contract: `tracker-contract.md`, "Deferred registration".
|
|
108
|
+
|
|
104
109
|
### Tracker updates (every phase boundary)
|
|
105
110
|
|
|
106
111
|
As each phase enters/exits:
|
|
@@ -162,7 +167,7 @@ TaskUpdate({ taskId: <saved>, status: "completed" })
|
|
|
162
167
|
bash phase-tracker.sh update <N> completed
|
|
163
168
|
```
|
|
164
169
|
|
|
165
|
-
A phase outside the command's set gets no TaskCreate at all. Depth is different: it is not known at registration time, because the tracker boots at Step -1 and the depth question runs at Step 7.5,
|
|
170
|
+
A phase outside the command's set gets no TaskCreate at all. Depth is different: it is not known at registration time, because the tracker boots at Step -1 and the depth question runs at Step 7.5. So registration splits - Phase 0 alone at Step -1, the rest at Step 7.5 once the answer says which phases the run has. A Short run never draws an Analysis tile it will not use. Registering all eight and flipping 1 and 2 to `skipped` is what this replaced, in v17.5.0: it put an eight-tile widget on screen beside the question asking whether to run two of them - see the ordering rule below.
|
|
166
171
|
|
|
167
172
|
**(strict) TaskCreate ordering**: All TaskCreate calls MUST fire in strict phase-number order BEFORE any TaskUpdate is applied. The native widget renders by creation order, not by phase number - out-of-order calls produce visually scrambled tile stacks (e.g. `1 ✓ · 2 ✓ · 4 ✓ · 0 ▶ · 3 ☐`) even when the underlying state is correct. Pre-marking phases as completed/skipped before Phase 0 starts is FORBIDDEN - register the tile in order with default `pending` status, then flip status via TaskUpdate when the phase actually short-circuits. Full contract in `$HOME/.claude/multi-agent-refs/tracker-contract.md` section "TaskCreate ordering (strict)".
|
|
168
173
|
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
- [Localized labels: what the caller owns](#localized-labels-what-the-caller-owns)
|
|
9
9
|
- [Order: project, then repo, then branch](#order-project-then-repo-then-branch)
|
|
10
10
|
- [A single candidate is still a question](#a-single-candidate-is-still-a-question)
|
|
11
|
+
- [Two options or it is not a question](#two-options-or-it-is-not-a-question)
|
|
11
12
|
- [Autopilot / non-interactive contract](#autopilot-non-interactive-contract)
|
|
12
13
|
- [Deterministic gates note](#deterministic-gates-note)
|
|
13
14
|
<!-- /toc -->
|
|
@@ -102,15 +103,46 @@ input with a second question.
|
|
|
102
103
|
## A single candidate is still a question
|
|
103
104
|
|
|
104
105
|
The number of options never authorises a skip. A filter that leaves one row has
|
|
105
|
-
narrowed the world; it has not decided anything
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
picker, and announcing the pick in prose first is the same skip with a sentence in front
|
|
109
|
-
of it.
|
|
106
|
+
narrowed the world; it has not decided anything. "There was only one option, so I
|
|
107
|
+
picked it" is a skipped picker, and announcing the pick in prose first is the same
|
|
108
|
+
skip with a sentence in front of it.
|
|
110
109
|
|
|
111
110
|
This is the failure that is hardest to see afterwards, because the transcript reads like
|
|
112
111
|
a decision was made. Only the picker's absence records that the user was never asked.
|
|
113
112
|
|
|
113
|
+
The one row is asked by giving the question a real second option, not by sending a
|
|
114
|
+
one-row picker - see the next section for why that is not the same thing.
|
|
115
|
+
|
|
116
|
+
## Two options or it is not a question
|
|
117
|
+
|
|
118
|
+
Claude Code's `AskUserQuestion` refuses a question with fewer than two declared
|
|
119
|
+
options, and it refuses the **whole call**: every other question batched with it is
|
|
120
|
+
discarded unasked, and the host's reply says not to retry and not to invent a filler
|
|
121
|
+
option. The host's **Other** row does not rescue it - the schema counts declared
|
|
122
|
+
options, and Other is injected afterwards.
|
|
123
|
+
|
|
124
|
+
This has already cost a run. A base-branch question with one remote candidate was
|
|
125
|
+
batched with the maturity, depth and workspace questions; the call was rejected, the
|
|
126
|
+
branch was announced in prose instead ("only candidate, continuing with it"), and the
|
|
127
|
+
three surviving questions had to be re-asked. The section above was followed to the
|
|
128
|
+
letter and produced the skip it exists to prevent.
|
|
129
|
+
|
|
130
|
+
So a one-candidate picker is asked with a genuine escape as its second option:
|
|
131
|
+
|
|
132
|
+
| One candidate | Second option that makes it a question |
|
|
133
|
+
|---|---|
|
|
134
|
+
| base branch | "Pick another branch" - re-opens with the unfiltered `git branch -r` list |
|
|
135
|
+
| account, repo, module | "Show all" - re-opens with the filter dropped |
|
|
136
|
+
| a destructive step | "Abort" |
|
|
137
|
+
|
|
138
|
+
A second option that is not a choice ("OK", "Continue") is worse than not asking: it
|
|
139
|
+
manufactures consent. When nothing genuine can be offered, do not ask - say which
|
|
140
|
+
single path is being taken and continue.
|
|
141
|
+
|
|
142
|
+
`ask-choice.sh` accepts one option and always has, so this floor is a Claude Code
|
|
143
|
+
fact the shell path does not share. Write the picker to the floor anyway: one spec
|
|
144
|
+
text drives both hosts.
|
|
145
|
+
|
|
114
146
|
## Autopilot / non-interactive contract
|
|
115
147
|
|
|
116
148
|
In autopilot, `ask_choice` resolves to `default` (or the safe first option) without prompting - identical to how the native gates auto-proceed today. A picker is only surfaced for genuinely ambiguous or destructive decisions, matching the maturity-check model.
|
|
@@ -94,7 +94,7 @@ Phases by mode:
|
|
|
94
94
|
| `/multi-agent:autopilot`, `/multi-agent:local-autopilot` | 0,1,2,3,4,6,7 (always Full; autopilot drops the interactive Phase 5 gate) |
|
|
95
95
|
| `/multi-agent:analysis` | 0,1,2,4,6,7 (no code is written, so no Dev and no Test) |
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
The two picker entries (`/multi-agent`, `:local`) register in two batches, because at Step -1 they do not yet know which set is theirs: see "Deferred registration" below. Every other mode registers its whole set at Step -1.
|
|
98
98
|
|
|
99
99
|
Register each phase:
|
|
100
100
|
|
|
@@ -199,29 +199,40 @@ Mode-specific phase sets:
|
|
|
199
199
|
|
|
200
200
|
| Mode | TaskCreate set (in order) |
|
|
201
201
|
|---|---|
|
|
202
|
-
| `/multi-agent` | 0
|
|
203
|
-
| `:local` |
|
|
202
|
+
| `/multi-agent` | 0 at Step -1; then at Step 7.5 either 1 → 2 → 3 → 4 → 5 → 6 → 7 (Full) or 3 → 4 → 5 → 6 → 7 (Short) |
|
|
203
|
+
| `:local` | same two batches, with Phase 5 in neither |
|
|
204
204
|
| `:autopilot`, `:local-autopilot` | 0 → 1 → 2 → 3 → 4 → 6 → 7 (7 phases - always Full, and the interactive Phase 5 gate is dropped) |
|
|
205
205
|
| `:analysis` | 0 → 1 → 2 → 4 → 6 → 7 (6 phases - no code is written, so 3 and 5 are not in the set) |
|
|
206
206
|
|
|
207
207
|
A phase outside the mode's set gets no TaskCreate at all; the `[SKIPPED]` pattern applies only to a phase that IS in the set and short-circuits at runtime. Phase 4 is in every mode's set as of v14.0.0. The authoritative per-mode set is the `for p in ...` init block in each mode's own entry doc, generated by `gen-mode-dispatch.mjs`; this table mirrors those blocks.
|
|
208
208
|
|
|
209
|
-
####
|
|
209
|
+
#### Deferred registration - the depth picker
|
|
210
210
|
|
|
211
|
-
|
|
211
|
+
`/multi-agent` and `:local` cannot know their phase set at Step -1. Depth decides it, and the depth picker cannot run before Step 7.5: its recommendation needs `taskType`, which needs the fetched issue and the branch.
|
|
212
|
+
|
|
213
|
+
Until v17.5.0 they registered all eight anyway and flipped 1 and 2 to `skipped` at 7.5. That put a widget reading "8 tasks, 7 open - Phase 1 Analysis, Phase 2 Planning, ..." on screen *beside* the question asking whether to run Analysis and Planning at all, and a Short answer then contradicted a list the user had just been shown. The widget was asserting a shape the run had not chosen.
|
|
214
|
+
|
|
215
|
+
So registration splits at the moment the shape is known:
|
|
212
216
|
|
|
213
217
|
```text
|
|
214
|
-
# Step -1,
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
218
|
+
# Step -1, first thing in the run: Phase 0 only. It is the one phase that is
|
|
219
|
+
# certain, and the run is never silent while Phase 0 does its work.
|
|
220
|
+
bash $HOME/.claude/scripts/phase-tracker.sh add 0 Init
|
|
221
|
+
bash $HOME/.claude/scripts/phase-tracker.sh tiles # -> TaskCreate(Phase 0)
|
|
222
|
+
bash $HOME/.claude/scripts/phase-tracker.sh update 0 in_progress
|
|
223
|
+
|
|
224
|
+
# Step 7.5, immediately after the depth answer:
|
|
225
|
+
# Full -> 1 2 3 4 5 6 7 Short -> 3 4 5 6 7
|
|
226
|
+
# :local drops 5 from either (no worktree to check out from)
|
|
227
|
+
for p in "3:Dev" "4:Review" "5:Test" "6:Commit" "7:Report"; do
|
|
228
|
+
bash $HOME/.claude/scripts/phase-tracker.sh add "${p%%:*}" "${p#*:}"
|
|
229
|
+
done
|
|
230
|
+
bash $HOME/.claude/scripts/phase-tracker.sh tiles --new # -> TaskCreate for the new tiles only
|
|
222
231
|
```
|
|
223
232
|
|
|
224
|
-
|
|
233
|
+
`tiles --new` emits `TaskCreate` only for phases that carry no `tasklist_id` yet, so the Phase 0 tile is not created twice. It is the same ordering rule, applied per batch: every tile in a batch is created in ascending phase order, and a deferred batch only ever appends phases numbered above everything already registered. Nothing is pre-marked, and a phase the run will not execute never gets a tile at all.
|
|
234
|
+
|
|
235
|
+
A phase that IS registered and short-circuits later still flips with `[SKIPPED]` - autopilot suppressing Phase 5, for instance. That is a runtime outcome, not an unknown set.
|
|
225
236
|
|
|
226
237
|
**Enforcement**: `smoke-tasklist-ordering.sh` scans the dispatcher (`commands/multi-agent/SKILL.md`) and every mode entry point doc (`commands/multi-agent/{autopilot,local,local-autopilot,analysis,resume-local}/SKILL.md` + the Copilot full-inline orchestrator mirror) for the explicit "in phase-number order" rule. Inventory drift fails the smoke.
|
|
227
238
|
|
|
@@ -76,10 +76,94 @@
|
|
|
76
76
|
"type": "string",
|
|
77
77
|
"description": "PR target branch (e.g. develop, main)."
|
|
78
78
|
},
|
|
79
|
+
"baseFetchStatus": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"enum": ["fresh", "cached-stale", "local-branch", "aborted"],
|
|
82
|
+
"description": "What the base ref is worth. fresh = git fetch origin succeeded; cached-stale = the fetch failed and the user chose the remote-tracking cache; local-branch = the fetch failed and the user chose the local branch; aborted = the user stopped the run at the fetch-fail picker. phase0-exit-gate.mjs has required this field since v17.0, while this schema forbade it under additionalProperties: false - so a state that satisfied the gate failed validation and vice versa. Declared here as of v17.5.0."
|
|
83
|
+
},
|
|
79
84
|
"baseBranchSource": {
|
|
80
85
|
"type": "string",
|
|
81
|
-
"enum": ["asked", "input", "remembered", "default"],
|
|
82
|
-
"description": "How baseBranch was decided. asked = the user answered the Step 3 picker; input = it arrived with the task reference; remembered = autopilot took the most recent entry in prefs.global.recentBranches still inside the TTL and still on the remote; default = autopilot fell back to the develop/release/main sort order.
|
|
86
|
+
"enum": ["asked", "input", "remembered", "default", "derived"],
|
|
87
|
+
"description": "How baseBranch was decided. asked = the user answered the Step 3 picker; input = it arrived with the task reference; remembered = autopilot took the most recent entry in prefs.global.recentBranches still inside the TTL and still on the remote; default = autopilot fell back to the develop/release/main sort order; derived = autopilot took the top base-branch-candidates.mjs candidate, which carried issue-version or linked-release evidence and tied with nothing. The last three are autopilot resolutions: an autopilot run cannot be asked anything, so recording which rule fired is what keeps it readable afterwards, and an interactive run that records one has skipped its picker. A derived branch that a human then confirmed is still asked - the derivation is recorded in baseBranchEvidence, not in this field."
|
|
88
|
+
},
|
|
89
|
+
"baseBranchEvidence": {
|
|
90
|
+
"type": "object",
|
|
91
|
+
"additionalProperties": false,
|
|
92
|
+
"description": "v17.5.0+ - what Step 3 knew when it chose the base branch (refs/features/base-branch-evidence.md). Required when baseBranchSource is derived, and whenever baseFetchStatus is cached-stale or local-branch: a run may degrade to local refs, it may not report a local-only list as the remote's answer.",
|
|
93
|
+
"required": ["refProvenance"],
|
|
94
|
+
"properties": {
|
|
95
|
+
"refProvenance": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"enum": ["remote", "local"],
|
|
98
|
+
"description": "Where the candidate ref list came from. local means the fetch failed and the list is the local cache plus local heads - possibly stale, possibly incomplete."
|
|
99
|
+
},
|
|
100
|
+
"chosen": { "type": "string" },
|
|
101
|
+
"ambiguous": {
|
|
102
|
+
"type": "boolean",
|
|
103
|
+
"description": "Two or more candidates tied at the top score. Autopilot may not record derived when this is true."
|
|
104
|
+
},
|
|
105
|
+
"convention": {
|
|
106
|
+
"type": ["object", "null"],
|
|
107
|
+
"additionalProperties": false,
|
|
108
|
+
"description": "The release-branch template inferred from the refs that exist, never from a built-in table.",
|
|
109
|
+
"properties": {
|
|
110
|
+
"template": { "type": "string" },
|
|
111
|
+
"members": { "type": "integer", "minimum": 0 }
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"candidates": {
|
|
115
|
+
"type": "array",
|
|
116
|
+
"items": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"additionalProperties": false,
|
|
119
|
+
"required": ["branch"],
|
|
120
|
+
"properties": {
|
|
121
|
+
"branch": { "type": "string" },
|
|
122
|
+
"score": { "type": "number" },
|
|
123
|
+
"refs": { "type": "array", "items": { "type": "string" } },
|
|
124
|
+
"evidence": {
|
|
125
|
+
"type": "array",
|
|
126
|
+
"items": {
|
|
127
|
+
"type": "object",
|
|
128
|
+
"additionalProperties": false,
|
|
129
|
+
"required": ["kind"],
|
|
130
|
+
"properties": {
|
|
131
|
+
"kind": {
|
|
132
|
+
"type": "string",
|
|
133
|
+
"enum": [
|
|
134
|
+
"issue-version",
|
|
135
|
+
"linked-release",
|
|
136
|
+
"version-convention",
|
|
137
|
+
"recent",
|
|
138
|
+
"repo-default",
|
|
139
|
+
"sort-order",
|
|
140
|
+
"ref-provenance"
|
|
141
|
+
]
|
|
142
|
+
},
|
|
143
|
+
"detail": { "type": "string" }
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
"notes": { "type": "array", "items": { "type": "string" } },
|
|
151
|
+
"askedOnIssue": {
|
|
152
|
+
"type": ["object", "null"],
|
|
153
|
+
"additionalProperties": false,
|
|
154
|
+
"description": "The one comment autopilot is allowed to post when the derivation is ambiguous, gated by prefs.global.baseBranchEvidence.autopilotAsksOnIssue (default false). A question, never a state change: no transition, no close, no assignee. Posting it trips circuit-breaker trigger 6 and the run waits for resume.",
|
|
155
|
+
"properties": {
|
|
156
|
+
"target": { "type": "string" },
|
|
157
|
+
"url": { "type": "string" },
|
|
158
|
+
"at": { "type": "string", "format": "date-time" }
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
"workspaceSource": {
|
|
164
|
+
"type": "string",
|
|
165
|
+
"enum": ["asked", "command", "autopilot"],
|
|
166
|
+
"description": "Who decided where the branch lives. asked = the user answered the Step 5b workspace picker; command = :local / --local / :local-autopilot stated it up front, or a flow that only ever builds worktrees; autopilot = resolved to a worktree without asking, because an unattended commit in the user's own checkout is what worktrees prevent. localMode alone cannot say: false is both a chosen worktree and one nothing asked about."
|
|
83
167
|
},
|
|
84
168
|
"remoteType": {
|
|
85
169
|
"type": "string",
|
|
@@ -883,7 +967,7 @@
|
|
|
883
967
|
"circuitBreaker": {
|
|
884
968
|
"type": "object",
|
|
885
969
|
"additionalProperties": false,
|
|
886
|
-
"description": "Autopilot circuit-breaker record (refs/features/autopilot-circuit-breaker.md). Written only when a trigger trips: trigger 2 by Phase 4 Step 3.8 (a mandate finding survived identicalFindingCycles rework cycles), trigger 3 by the Phase 3 re-entry hard-kill. /multi-agent:resume clears tripped and keeps counters.",
|
|
970
|
+
"description": "Autopilot circuit-breaker record (refs/features/autopilot-circuit-breaker.md). Written only when a trigger trips: trigger 2 by Phase 4 Step 3.8 (a mandate finding survived identicalFindingCycles rework cycles), trigger 3 by the Phase 3 re-entry hard-kill, trigger 6 by Phase 0 Step 3 when autopilot posted the base-branch question on the issue and must not answer it itself. /multi-agent:resume clears tripped and keeps counters.",
|
|
887
971
|
"required": ["tripped"],
|
|
888
972
|
"properties": {
|
|
889
973
|
"tripped": {
|
|
@@ -892,7 +976,7 @@
|
|
|
892
976
|
"trigger": {
|
|
893
977
|
"type": ["integer", "null"],
|
|
894
978
|
"minimum": 1,
|
|
895
|
-
"maximum":
|
|
979
|
+
"maximum": 6
|
|
896
980
|
},
|
|
897
981
|
"detail": {
|
|
898
982
|
"type": "string"
|