@mmerterden/multi-agent-pipeline 16.15.0 → 16.16.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +5 -4
  3. package/README.tr.md +5 -4
  4. package/docs/adr/0011-dormant-ci.md +87 -0
  5. package/docs/adr/README.md +1 -0
  6. package/docs/architecture.md +2 -2
  7. package/docs/ecosystem.md +5 -5
  8. package/install/_common.mjs +0 -1
  9. package/install/_dev-only-files.mjs +1 -0
  10. package/install/claude.mjs +14 -4
  11. package/install/index.mjs +8 -1
  12. package/package.json +6 -5
  13. package/pipeline/commands/multi-agent/help/SKILL.md +2 -0
  14. package/pipeline/commands/multi-agent/steer/SKILL.md +109 -0
  15. package/pipeline/commands/multi-agent/sync/SKILL.md +5 -7
  16. package/pipeline/multi-agent-refs/cross-cli-contract.md +4 -4
  17. package/pipeline/multi-agent-refs/phases/operations.md +23 -0
  18. package/pipeline/multi-agent-refs/phases.md +38 -0
  19. package/pipeline/preferences-template.json +2 -10
  20. package/pipeline/schemas/agent-state.schema.json +35 -0
  21. package/pipeline/schemas/analysis-spec.schema.json +8 -2
  22. package/pipeline/schemas/conventions-output.schema.json +2 -13
  23. package/pipeline/scripts/build-references.mjs +22 -6
  24. package/pipeline/scripts/code-graph-rules/android.json +4 -21
  25. package/pipeline/scripts/code-graph-rules/go.json +4 -19
  26. package/pipeline/scripts/code-graph-rules/node.json +4 -21
  27. package/pipeline/scripts/feedback-send.mjs +3 -1
  28. package/pipeline/scripts/gate-linux.sh +62 -0
  29. package/pipeline/scripts/localize-commands.mjs +1 -2
  30. package/pipeline/scripts/pre-push-check.sh +6 -4
  31. package/pipeline/scripts/usage-report.mjs +16 -14
  32. package/pipeline/scripts/validate-analysis-doc.mjs +46 -27
  33. package/pipeline/scripts/validate-analysis.mjs +7 -1
  34. package/pipeline/scripts/validate-complaint-doc.mjs +24 -8
  35. package/pipeline/scripts/write-state.mjs +71 -12
  36. package/pipeline/skills/shared/core/multi-agent-steer/SKILL.md +111 -0
  37. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +4 -4
package/CHANGELOG.md CHANGED
@@ -16,6 +16,25 @@ Internal file-layout changes that don't affect the slash-command surface are sti
16
16
 
17
17
  ## [Unreleased]
18
18
 
19
+ ## [16.16.0] - 2026-08-31
20
+
21
+ ### Added
22
+
23
+ - **`/multi-agent:steer #N "<instruction>"` corrects a run without stopping it.** `kill` deletes a task and `resume` picks a stopped one back up; neither helps while a phase is in flight, so a correction that arrived mid-run had nowhere to go and the run finished in the wrong direction. The command queues one instruction into `state.pendingSteer`, written through `write-state.mjs` rather than by editing a file the running task is also writing to, and the next phase reads it at entry, applies it, and marks it consumed with `appliedAt`/`appliedPhase` while keeping the record as a trace. Entry rather than mid-phase on purpose: a phase that changes target halfway discards the work it already did, which is the outcome the command exists to avoid. An instruction that contradicts an approved plan halts for the user instead of silently rewriting it. `smoke-steer.sh` walks the whole path - schema, writer, phase-entry contract, help and both READMEs - because a command whose only value is a phase reading the field back is exactly the shape that ships green and dead.
24
+ - **`state.rev`, a monotonic revision counter.** The advisory lock makes a single write atomic and nothing more; it does not cover the gap between a phase reading state and writing it back, and `steer` above puts a deliberate second writer inside that gap. A reader that keeps the `rev` it read can tell the record moved underneath it instead of silently dropping whatever landed in between. Every successful write bumps `rev`; the writer takes the higher of the on-disk value and the incoming patch, so a stale patch cannot walk the counter backwards. A record written before this has none - treat a missing `rev` as 0.
25
+ - **`npm run gate:linux`** runs the ci-lite job on a real Ubuntu image through `act`, covering the Linux leg and a clean `npm ci` that the macOS pre-push gate cannot answer. Deliberately not part of `npm run gate`: `act` is not installed everywhere, and a required gate that cannot run is the failure ADR-0011 exists to avoid.
26
+
27
+ ### Fixed
28
+
29
+ - **A live lock holder could lose its lock to the age rule.** `write-state.mjs` judged a lock stale when its holder PID was dead **or** the file was older than 30 seconds - age checked first, and returning on its own. So a writer that was demonstrably alive had its lock deleted the moment the file aged out, and the next writer overwrote the update behind it. Liveness now outranks age: an alive holder keeps its lock, `WRITE_STATE_LOCK_STALE_MS` applies only to a lock with no readable PID, and a new `WRITE_STATE_LOCK_ABANDON_MS` (default ten times that) is the last-resort ceiling for a recycled PID. No heartbeat: the writer holds the lock across one synchronous write, so there is no window for a timer to refresh anything, and a refreshed timestamp is weaker evidence than the liveness probe already gives.
30
+ - **Prettier was enforced nowhere, and `main` was failing it.** `format:check` was declared in `package.json`, wired into ci-lite, and ci-lite has not executed since 2026-07-02; `eslint-config-prettier` only disables conflicting rules, it does not check formatting. Twenty-five files were failing the project's own declared style. `format:check` now runs inside `npm test`, so the pre-push gate carries it, and a `.prettierignore` exempts two trees with reasons: git worktrees checked out inside the repo, and `pipeline/skills/shared/external/`, which stays byte-identical to upstream so a re-derivation diff remains readable.
31
+ - **The installed-tree leak sweep was running against a private copy of the pattern list, drifted to under half of it.** `smoke-install-leak-gate.sh` carried its own patterns under a comment saying it mirrored `smoke-personal-data.sh`. It did not: personal domains, the maintainer's name, one spelling of the machine username, corporate symbol prefixes and product literals were all missing, so the sweep that runs on every `npm test` was weaker than the one in ci-lite - which has not executed since 2026-07-02. The step now delegates to the file that owns the list, and ci-lite's inline copy of the same dance was replaced by a call to that smoke. Three shapes the private copy caught and the owner did not (a trailing-underscore username compound, the bare organisation name, an address with no local part) were added to the owner, so the union is preserved rather than traded away - and the first of them immediately caught a real leak that had been sitting in `docs/internal/`, which the narrower pattern could not match. That file is not in the published tarball, so nothing reached a user.
32
+
33
+ ### Changed
34
+
35
+ - **CI is dormant in the repository, not only in platform state.** `test.yml` and `ci-lite.yml` still declared `on: push` and `on: pull_request` while both had already been disabled from the Actions UI - true only for a fork, or for whoever re-enables one without reading further. Both trigger blocks are commented out with the reason beside them, `workflow_dispatch` stays live, and [ADR-0011](./docs/adr/0011-dormant-ci.md) records why, what stays unverified while they sleep (the `windows-test` job, a clean Linux runner, a clean `npm ci` from the lockfile) and the two-line wake-up. `CONTRIBUTING.md` now tells an outside contributor that no workflow will run on their PR and to paste the `npm run gate` output instead of waiting for a green tick.
36
+ - **The scorecard covers moderate advisories and registry signatures.** It measured high and critical only; `--audit-level=moderate` and `npm audit signatures` lived in ci-lite and stopped running with it. Both are scorecard metrics now, the moderate ceiling locked at the measured value of 0. An unreachable registry fails the signature metric rather than skipping it: a supply-chain check that reports green when it could not reach the registry is worse than no check.
37
+
19
38
  ## [16.15.0] - 2026-08-30
20
39
 
21
40
  ### Fixed
package/README.md CHANGED
@@ -89,7 +89,7 @@ Depth, autopilot and `--local` are the only knobs on the run itself; everything
89
89
 
90
90
  ## Commands
91
91
 
92
- `/multi-agent` plus 54 sub-commands. `/multi-agent:help` renders the same catalog in your terminal, in your `outputLanguage`.
92
+ `/multi-agent` plus 55 sub-commands. `/multi-agent:help` renders the same catalog in your terminal, in your `outputLanguage`.
93
93
 
94
94
  ### Pipeline entries
95
95
 
@@ -109,6 +109,7 @@ Depth, autopilot and `--local` are the only knobs on the run itself; everything
109
109
  | `/multi-agent:log [#N]` | Show a task's `agent-log.md` (most recent by default) |
110
110
  | `/multi-agent:resume [#N]` | Carry a stopped or failed task on from its last phase |
111
111
  | `/multi-agent:kill [#N]` | Stop a task, remove its worktree and branch |
112
+ | `/multi-agent:steer #N "<instruction>"` | Correct a running task without stopping it; applied at the next phase boundary |
112
113
  | `/multi-agent:search` | Ranked search across every task log; `--semantic` queries the triage corpus |
113
114
  | `/multi-agent:garbage-collect` | Sweep leftover scratch, orphan worktrees and offloaded payloads. Dry-run first |
114
115
  | `/multi-agent:prune-logs` | Delete per-task logs by age / project / task. Audit trail and metrics kept |
@@ -208,13 +209,13 @@ This enables the matching plugin (+ the shared `ai-common` plugin) in the repo's
208
209
 
209
210
  ## Tool support
210
211
 
211
- The pipeline runs natively on **Claude Code**, **Copilot CLI** and **Codex CLI** - all three install from the same `pipeline/` source and get the same 54 commands.
212
+ The pipeline runs natively on **Claude Code**, **Copilot CLI** and **Codex CLI** - all three install from the same `pipeline/` source and get the same 55 commands.
212
213
 
213
214
  | Tool | Flag | What it installs |
214
215
  |---|---|---|
215
216
  | Claude Code | `--claude` (default) | slash commands + skills + agents + `PreToolUse` secret-scan hook |
216
- | Copilot CLI | `--copilot` | instructions + 54 sub-command skills + scripts |
217
- | Codex CLI | `--codex` | one router skill + 54 specs as refs + 8 agent TOML + `AGENTS.md` block + `codex mcp add` |
217
+ | Copilot CLI | `--copilot` | instructions + 55 sub-command skills + scripts |
218
+ | Codex CLI | `--codex` | one router skill + 55 specs as refs + 8 agent TOML + `AGENTS.md` block + `codex mcp add` |
218
219
 
219
220
  Filter skills by stack with `--platform=ios\|android\|all`.
220
221
 
package/README.tr.md CHANGED
@@ -89,7 +89,7 @@ Koşunun kendisinde ayarlanabilen tek şey derinlik, autopilot ve `--local`; ger
89
89
 
90
90
  ## Komutlar
91
91
 
92
- `/multi-agent` ve 54 alt komut. `/multi-agent:help` aynı katalogu terminalde, `outputLanguage` ayarına göre gösterir.
92
+ `/multi-agent` ve 55 alt komut. `/multi-agent:help` aynı katalogu terminalde, `outputLanguage` ayarına göre gösterir.
93
93
 
94
94
  ### Pipeline girişleri
95
95
 
@@ -109,6 +109,7 @@ Koşunun kendisinde ayarlanabilen tek şey derinlik, autopilot ve `--local`; ger
109
109
  | `/multi-agent:log [#N]` | Görevin `agent-log.md` dosyası (varsayılan: en son görev) |
110
110
  | `/multi-agent:resume [#N]` | Durmuş ya da hata almış görevi kaldığı fazdan sürdürür |
111
111
  | `/multi-agent:kill [#N]` | Görevi durdurur, worktree'sini ve branch'ini siler |
112
+ | `/multi-agent:steer #N "<talimat>"` | Koşan bir görevi durdurmadan düzeltir; talimat bir sonraki faz sınırında uygulanır |
112
113
  | `/multi-agent:search` | Tüm görev loglarında sıralamalı arama; `--semantic` triyaj corpus'unu sorgular |
113
114
  | `/multi-agent:garbage-collect` | Artık scratch, sahipsiz worktree ve offload edilmiş payload'ları süpürür. Önce dry-run |
114
115
  | `/multi-agent:prune-logs` | Görev loglarını yaş / proje / görev filtresiyle siler. Denetim izi ve metrikler kalır |
@@ -208,13 +209,13 @@ Bu, ilgili plugin'i (+ ortak `ai-common` plugin'ini) repo'nun `.claude/settings.
208
209
 
209
210
  ## Araç desteği
210
211
 
211
- Pipeline **Claude Code**, **Copilot CLI** ve **Codex CLI** üzerinde native çalışır - üçü de aynı `pipeline/` kaynağından kurulur ve aynı 54 komutu alır.
212
+ Pipeline **Claude Code**, **Copilot CLI** ve **Codex CLI** üzerinde native çalışır - üçü de aynı `pipeline/` kaynağından kurulur ve aynı 55 komutu alır.
212
213
 
213
214
  | Araç | Bayrak | Ne kurar |
214
215
  |---|---|---|
215
216
  | Claude Code | `--claude` (varsayılan) | slash komutları + skill'ler + agent'lar + `PreToolUse` secret-scan hook'u |
216
- | Copilot CLI | `--copilot` | talimatlar + 54 alt-komut skill'i + script'ler |
217
- | Codex CLI | `--codex` | bir router skill + ref olarak 54 spec + 8 agent TOML + `AGENTS.md` bloğu + `codex mcp add` |
217
+ | Copilot CLI | `--copilot` | talimatlar + 55 alt-komut skill'i + script'ler |
218
+ | Codex CLI | `--codex` | bir router skill + ref olarak 55 spec + 8 agent TOML + `AGENTS.md` bloğu + `codex mcp add` |
218
219
 
219
220
  Skill'leri stack'e göre filtrele: `--platform=ios\|android\|all`.
220
221
 
@@ -0,0 +1,87 @@
1
+ # 11. CI stays in the repository but dormant; the pre-push gate is primary
2
+
3
+ **Status:** Accepted · 2026-08-31
4
+
5
+ ## Context
6
+
7
+ The repository carries four workflows. `test.yml` runs the full chain across a
8
+ macOS/Ubuntu matrix plus a separate `windows-test` job, `ci-lite.yml` runs a
9
+ single cheap Ubuntu job, `release.yml`
10
+ publishes on a version tag and `live-eval.yml` is a manual, budgeted evaluation.
11
+
12
+ None of the first three has executed since 2026-07-02. That day's last run took
13
+ 318 seconds and passed; every attempt after it - five, the last on 2026-07-25 -
14
+ finished in about four seconds with zero steps recorded, which is the shape of a
15
+ quota block rather than a test failure. All three were then disabled from the
16
+ Actions UI. That stopped the noise but moved the fact out of the repository:
17
+ the files still declared `on: push` and `on: pull_request`, so anyone reading
18
+ them, forking them, or re-enabling the workflow inherited a promise the project
19
+ does not keep.
20
+
21
+ Meanwhile the gate did not disappear. `pipeline/scripts/pre-push-check.sh` runs
22
+ the canonical `npm test` chain, stamps the exact tree it verified, and refuses
23
+ any push whose tree carries no stamp. It is installed as `.git/hooks/pre-push`.
24
+ So the question was never "is this code verified" but "verified where, and what
25
+ does that leave uncovered".
26
+
27
+ There is no plan to run this on a server in the near term. There is a possible
28
+ later direction (an issue-triggered run that starts development automatically),
29
+ which argues for keeping the workflow files rather than deleting them.
30
+
31
+ ## Decision
32
+
33
+ Keep every workflow file. Comment out the `push` and `pull_request` triggers in
34
+ `test.yml` and `ci-lite.yml`, leaving `workflow_dispatch` live, and state the
35
+ dormancy in the file header next to the triggers.
36
+
37
+ Treat `npm run gate` as the primary gate, not as a fallback, and close the parts
38
+ of the ci-lite job that had no local equivalent: `format:check` joins the `npm
39
+ test` chain, the install dry-run plus personal-data audit moves onto the
40
+ existing `smoke-install-leak-gate.sh` (which was already doing the same install
41
+ into a temp HOME, against a private copy of the pattern list that had drifted to
42
+ less than half of it), the scorecard gains the two dependency checks that lived
43
+ only in ci-lite - moderate advisories and `npm audit signatures` - and the Linux
44
+ leg is reachable as `npm run gate:linux` through `act`.
45
+
46
+ Waking CI is a two-line edit: uncomment the trigger blocks and re-enable the
47
+ workflow in the Actions UI. Nothing else in the files changes.
48
+
49
+ ## Consequences
50
+
51
+ A reader of the repository now sees what actually runs. A fork does not
52
+ inherit surprise runs that fail for a reason belonging to this account.
53
+
54
+ The local gate runs the canonical chain rather than a hand-picked subset: the
55
+ hook calls `npm test`, so a step added to that script is a step the hook runs.
56
+ `ci-lite.yml` still enumerates its steps one by one and can therefore drift
57
+ behind the script - which is a further reason the local gate, not the workflow,
58
+ is the one being trusted.
59
+
60
+ What stays unverified while the workflows sleep, stated rather than implied:
61
+
62
+ - Windows. `lib/credential-store.sh` has a Windows branch exercised only by
63
+ `test.yml`'s separate `windows-test` job (the matrix itself is Ubuntu and
64
+ macOS).
65
+ - A clean `npm ci` from the lockfile into an empty tree. The local gate runs
66
+ against an installed `node_modules` that has accumulated across months.
67
+ - Linux, unless someone actually runs `npm run gate:linux`. It is deliberately
68
+ not a required step: `act` is not installed everywhere, and a required gate
69
+ that cannot run is the failure this ADR exists to avoid.
70
+
71
+ `release.yml` keeps its tag trigger and stays disabled in the UI. Publishing is
72
+ done locally from a clean tag clone, so the tag flow is what a future wake-up
73
+ would want unchanged.
74
+
75
+ ## Alternatives
76
+
77
+ **Pay for Actions minutes.** Buys the matrix and the clean room. Rejected for
78
+ now: there is no server-side plan to justify a recurring cost, and the local
79
+ gate already covers everything except the three items listed above.
80
+
81
+ **Make this machine a self-hosted runner.** Free and real. Rejected: it covers
82
+ only macOS, disappears when the laptop is closed, and a self-hosted runner on a
83
+ public repository executes code from any fork's pull request.
84
+
85
+ **Delete the workflows.** Honest but lossy. The files encode which steps matter
86
+ and in what order, and the possible issue-triggered direction would have to
87
+ rebuild them from nothing.
@@ -20,6 +20,7 @@ Format: lightly adapted from [Michael Nygard's ADR template](https://cognitect.c
20
20
  | [0008](./0008-installer-modularization-and-secret-leak-defense.md) | Installer modularization + secret-leak defense | Accepted (amended v10.7.0: adapter module removed) |
21
21
  | [0009](./0009-claude-stack-skills-plugin-only.md) | Claude Code stack skills are plugin-only; local copy retired | Accepted (supersedes ADR-0006 install-destination clause) |
22
22
  | [0010](./0010-own-code-graph.md) | Own code graph, referenced from graphify, not forked | Accepted |
23
+ | [0011](./0011-dormant-ci.md) | CI dormant in-repo; pre-push gate is primary | Accepted |
23
24
 
24
25
  ## Writing a New ADR
25
26
 
@@ -117,7 +117,7 @@ graph TB
117
117
  end
118
118
 
119
119
  subgraph "Pipeline Specs"
120
- CMD[commands/<br/>54 command files]
120
+ CMD[commands/<br/>55 command files]
121
121
  AGT[agents/<br/>8 agent personas]
122
122
  RUL[rules/<br/>12 domain rules]
123
123
  PHS[multi-agent-refs/phases/<br/>phase specs + contracts]
@@ -170,7 +170,7 @@ revisions of this diagram - Codex CLI and the two independently-shipped repos
170
170
  graph TD
171
171
  CC["Claude Code<br/>(source of truth)"]
172
172
  COP["Copilot CLI<br/>(instructions + 51 skills)"]
173
- COD["Codex CLI<br/>(1 router skill + 54 refs)"]
173
+ COD["Codex CLI<br/>(1 router skill + 55 refs)"]
174
174
  REPO["Pipeline Repo<br/>(npm package)"]
175
175
  WEB["Website"]
176
176
  PLUGREPO["multi-agent-plugins<br/>(5 stack plugins, own repo)"]
package/docs/ecosystem.md CHANGED
@@ -5,7 +5,7 @@ separately, wired together at install time and at run time:
5
5
 
6
6
  | Repo | What it owns | Ships as |
7
7
  |---|---|---|
8
- | **`multi-agent-pipeline`** (this repo) | Orchestration: the 8-phase flow, the 54 slash commands, quality gates, review/triage, cross-CLI parity | npm package (`@mmerterden/multi-agent-pipeline`), installs itself onto Claude Code / Copilot CLI / Codex CLI |
8
+ | **`multi-agent-pipeline`** (this repo) | Orchestration: the 8-phase flow, the 55 slash commands, quality gates, review/triage, cross-CLI parity | npm package (`@mmerterden/multi-agent-pipeline`), installs itself onto Claude Code / Copilot CLI / Codex CLI |
9
9
  | **`multi-agent-plugins`** | Stack knowledge: per-platform component/lifecycle skills (iOS, Android, Frontend, Backend) + shared knowledge | Claude Code marketplace, 5 independently-versioned plugins |
10
10
  | **`multi-agent-toolkit-mcp`** | The pipeline's hands on devices and browsers: 80 MCP tools across 6 categories (simulator/emulator control, accessibility audit, store compliance, web automation, Figma-vs-mock design audit, an agent-DSL batch runner) | npm package, registered as a standard stdio MCP server on every host |
11
11
 
@@ -18,7 +18,7 @@ Either can be swapped or removed without touching the other two's source.
18
18
  graph LR
19
19
  subgraph PIPE ["multi-agent-pipeline (orchestrator)"]
20
20
  direction TB
21
- PHASES["8 phases · 54 commands"]
21
+ PHASES["8 phases · 55 commands"]
22
22
  GATES["deterministic gates + review triage"]
23
23
  end
24
24
 
@@ -64,8 +64,8 @@ only those:
64
64
  graph TD
65
65
  CC["Claude Code<br/>~/.claude/commands/multi-agent/<br/>(source of truth)"]
66
66
 
67
- CC -->|"Step 2: copy + reformat<br/>54 sub-command skills"| COP["Copilot CLI<br/>~/.copilot/skills/"]
68
- CC -->|"Step 2b: transform<br/>(install.js --codex)"| COD["Codex CLI<br/>1 router skill + 54 refs<br/>+ 8 agent TOML"]
67
+ CC -->|"Step 2: copy + reformat<br/>55 sub-command skills"| COP["Copilot CLI<br/>~/.copilot/skills/"]
68
+ CC -->|"Step 2b: transform<br/>(install.js --codex)"| COD["Codex CLI<br/>1 router skill + 55 refs<br/>+ 8 agent TOML"]
69
69
  CC -->|"Step 3: genericize<br/>(strip personal data)"| REPO["multi-agent-pipeline repo<br/>pipeline/"]
70
70
  CC -->|"Step 4: version + feature sync"| WEB["Website<br/>projects.ts / i18n.tsx"]
71
71
 
@@ -153,7 +153,7 @@ measurements behind this table):
153
153
 
154
154
  | | Claude Code | Copilot CLI | Codex CLI |
155
155
  |---|---|---|---|
156
- | **Pipeline commands** | 54 slash-command skills, native | 54 skills, `multi-agent-{cmd}` naming, copied in | 1 router skill (`multi-agent`) + 54 command specs as reference files - Codex silently truncates its skills block past a few dozen entries, so sub-commands are not peer skills here |
156
+ | **Pipeline commands** | 55 slash-command skills, native | 55 skills, `multi-agent-{cmd}` naming, copied in | 1 router skill (`multi-agent`) + 55 command specs as reference files - Codex silently truncates its skills block past a few dozen entries, so sub-commands are not peer skills here |
157
157
  | **Stack plugins** | Marketplace plugin, loaded natively, resolved by `.claude/settings.json` enabled-list | Enabled plugin's authored skills copied flat into `~/.copilot/skills/`; `knowledge/` **not** re-copied (already delivered via `shared/external`) | Copied as reference files under `~/.codex/multi-agent-refs/skills/`, plugin-prefixed on name clash (e.g. `architecture` → `ai-ios-toolkit-architecture`) |
158
158
  | **Component dispatch (Phase 3)** | Marketplace plugin's `create-component`/`create-screen` skill via the Skill tool | No plugin loader - the enabled stack plugin's authored skills (incl. `create-component`) are copied flat into `~/.copilot/skills/` at install time (the old frozen `figma-*` copies are pruned, they were never a fallback) | Not part of the enforced parity axis; classification + state-shape must match, skill *inventory* does not |
159
159
  | **multi-agent-toolkit-mcp** | `claude mcp add multi-agent-toolkit -- npx -y @mmerterden/multi-agent-toolkit-mcp` | `copilot mcp add multi-agent-toolkit -- npx -y @mmerterden/multi-agent-toolkit-mcp` | `codex mcp add multi-agent-toolkit -- npx -y @mmerterden/multi-agent-toolkit-mcp` (skipped with a warning if `codex` isn't on `PATH`) |
@@ -523,7 +523,6 @@ export function wipeDir(dir) {
523
523
  }
524
524
  }
525
525
 
526
-
527
526
  /**
528
527
  * @param {string} a
529
528
  * @param {string} b
@@ -64,6 +64,7 @@ const DEV_ONLY_TOOLING = Object.freeze([
64
64
  "validate-schemas.mjs", // validates the repo's own schema files, needs ajv
65
65
  "sync-parity-check.sh",
66
66
  "benchmark-phase-0.sh",
67
+ "gate-linux.sh", // drives .github/workflows/ci-lite.yml through act; no consumer has either
67
68
  ]);
68
69
 
69
70
  /** Eval harnesses (`eval-*.mjs`) read `pipeline/eval/**`, which never ships. */
@@ -97,7 +97,14 @@ export function installClaude(ctx) {
97
97
  installSchemas(pipelineSrc, CLAUDE_SCHEMAS, useSymlinks);
98
98
  installLib(pipelineSrc, CLAUDE_LIB, useSymlinks);
99
99
  runPreDeployScans(pipelineSrc);
100
- installSkills({ pipelineSrc, dest: CLAUDE_SKILLS, indexOnly, useSymlinks, platformFlag, pruneExternal });
100
+ installSkills({
101
+ pipelineSrc,
102
+ dest: CLAUDE_SKILLS,
103
+ indexOnly,
104
+ useSymlinks,
105
+ platformFlag,
106
+ pruneExternal,
107
+ });
101
108
  ensureClaudeMd(home, pipelineSrc);
102
109
  ensurePreferences(PREFS_PATH, pipelineSrc);
103
110
  configureSettings(home);
@@ -458,7 +465,9 @@ function installSkills(opts) {
458
465
  console.log(` -> pruned ${orphans} stale flat skill file(s) from an older layout`);
459
466
  }
460
467
 
461
- console.log(` -> ${claudeSkillCount} skill files installed to ${dest} (stack skills: marketplace plugins)`);
468
+ console.log(
469
+ ` -> ${claudeSkillCount} skill files installed to ${dest} (stack skills: marketplace plugins)`,
470
+ );
462
471
 
463
472
  // The shared skills README described the full local catalog; with the catalog
464
473
  // gone it would document skills that are not here. Drop it, including copies
@@ -604,7 +613,9 @@ function migratePreManifestExternalSkills(dest, externalSrc, opts) {
604
613
  );
605
614
  }
606
615
  if (flagged > 0) {
607
- console.log(` -> --prune-external: removed ${flagged} catalog-named skill dir(s) that had local differences`);
616
+ console.log(
617
+ ` -> --prune-external: removed ${flagged} catalog-named skill dir(s) that had local differences`,
618
+ );
608
619
  }
609
620
  if (leftover.length > 0) {
610
621
  console.log(
@@ -614,7 +625,6 @@ function migratePreManifestExternalSkills(dest, externalSrc, opts) {
614
625
  }
615
626
  }
616
627
 
617
-
618
628
  function ensureClaudeMd(home, pipelineSrc) {
619
629
  console.log(" [Claude Code] Checking CLAUDE.md template...");
620
630
  const CLAUDE_MD = join(home, ".claude", "CLAUDE.md");
package/install/index.mjs CHANGED
@@ -46,7 +46,14 @@ export async function runInstall(argv) {
46
46
  // `multi-agent-pipeline install --all` (via bin, argv[2]="install")
47
47
  const flags = argv.slice(2).filter((a) => a !== "install");
48
48
 
49
- const KNOWN_FLAGS = [...TOOL_FLAGS, "--all", "--link", "--index-only", "--dry-run", "--prune-external"];
49
+ const KNOWN_FLAGS = [
50
+ ...TOOL_FLAGS,
51
+ "--all",
52
+ "--link",
53
+ "--index-only",
54
+ "--dry-run",
55
+ "--prune-external",
56
+ ];
50
57
  const KNOWN_PREFIXES = ["--target=", "--platform="];
51
58
  const unknown = flags.filter(
52
59
  (f) =>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "16.15.0",
3
+ "version": "16.16.0",
4
4
  "description": "8-phase AI development pipeline with full orchestration on Claude Code, Copilot CLI and Codex CLI. Analysis, planning, TDD, CLI-aware parallel review with consensus surfacing + Fable triage, default-FAIL evidence gates, secret + intent guards, per-phase cost ledger, persistent learnings memory, wiki generation, commit automation. Token-preserving uninstall.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "scripts": {
16
16
  "start": "node index.js",
17
- "test": "node --test test/*.test.mjs && node pipeline/scripts/run-smokes.mjs && node pipeline/scripts/lint-skills.mjs && node pipeline/scripts/lint-mcp-refs.mjs && node pipeline/scripts/eval-triage.mjs && node pipeline/scripts/eval-golden-tasks.mjs && node pipeline/scripts/eval-intent.mjs && node pipeline/scripts/eval-recall.mjs && node pipeline/scripts/validate-schemas.mjs && node pipeline/scripts/validate-prefs.mjs && node pipeline/scripts/scorecard.mjs",
17
+ "test": "npm run format:check && node --test test/*.test.mjs && node pipeline/scripts/run-smokes.mjs && node pipeline/scripts/lint-skills.mjs && node pipeline/scripts/lint-mcp-refs.mjs && node pipeline/scripts/eval-triage.mjs && node pipeline/scripts/eval-golden-tasks.mjs && node pipeline/scripts/eval-intent.mjs && node pipeline/scripts/eval-recall.mjs && node pipeline/scripts/validate-schemas.mjs && node pipeline/scripts/validate-prefs.mjs && node pipeline/scripts/scorecard.mjs",
18
18
  "test:unit": "node --test test/*.test.mjs",
19
19
  "test:smoke": "node pipeline/scripts/run-smokes.mjs",
20
20
  "lint:skills": "node pipeline/scripts/lint-skills.mjs",
@@ -22,10 +22,11 @@
22
22
  "test:coverage": "c8 --clean --reporter=none node --test test/*.test.mjs && c8 --clean=false --check-coverage --reporter=text --reporter=lcov node pipeline/scripts/run-smokes.mjs",
23
23
  "lint": "eslint .",
24
24
  "lint:fix": "eslint . --fix",
25
- "format": "prettier --write \"**/*.{js,mjs,json,yml}\" --ignore-path .gitignore",
26
- "format:check": "prettier --check \"**/*.{js,mjs,json,yml}\" --ignore-path .gitignore",
25
+ "format": "prettier --write \"**/*.{js,mjs,json,yml}\" --ignore-path .gitignore --ignore-path .prettierignore",
26
+ "format:check": "prettier --check \"**/*.{js,mjs,json,yml}\" --ignore-path .gitignore --ignore-path .prettierignore",
27
27
  "scorecard": "node pipeline/scripts/scorecard.mjs",
28
- "gate": "bash pipeline/scripts/pre-push-check.sh --run"
28
+ "gate": "bash pipeline/scripts/pre-push-check.sh --run",
29
+ "gate:linux": "bash pipeline/scripts/gate-linux.sh"
29
30
  },
30
31
  "keywords": [
31
32
  "multi-agent",
@@ -108,6 +108,7 @@ Status & Resume:
108
108
  /multi-agent:log [#id] Show task log (defaults to most recent)
109
109
  /multi-agent:resume [#id] Resume a paused or failed task from where it stopped
110
110
  /multi-agent:kill [#id] Stop task + delete worktree (logs preserved)
111
+ /multi-agent:steer #id "..." Correct a running task without stopping it (applied at the next phase)
111
112
  /multi-agent:prune-logs Delete per-task project logs (audit + metrics kept; age/project/task filters, dry-run first)
112
113
  /multi-agent:prune-prompts Zero-base prompt review: measure always-on instruction footprint, propose keep/trial/delete per rule (report first, apply on approval)
113
114
  /multi-agent:garbage-collect Sweep leftover /tmp scratch from past runs (dry-run first)
@@ -384,6 +385,7 @@ Status & Resume:
384
385
  /multi-agent:log [#id] Task log'unu göster (varsayılan: en son task)
385
386
  /multi-agent:resume [#id] Duraklamış / hata almış task'ı kaldığı yerden devam ettir
386
387
  /multi-agent:kill [#id] Task'ı durdur + worktree'yi sil (log'lar korunur)
388
+ /multi-agent:steer #id "..." Koşan task'ı durdurmadan düzelt (sonraki fazda uygulanır)
387
389
  /multi-agent:prune-logs Task bazlı proje log'larını sil (audit + metrics korunur; yaş/proje/task filtresi, önce dry-run)
388
390
  /multi-agent:prune-prompts Sıfır-tabanlı prompt incelemesi: sürekli yüklü talimat yükünü ölç, kural başına tut/dene/sil öner (önce rapor, onayla uygula)
389
391
  /multi-agent:garbage-collect Geçmiş koşumların /tmp artıklarını süpür (önce dry-run)
@@ -0,0 +1,109 @@
1
+ ---
2
+ description: "Queue an instruction for a task that is already running. It is applied at the next phase boundary, not mid-phase. Use when a run is going the wrong way and killing it would throw away good work."
3
+ description-tr: "Zaten koşmakta olan bir göreve talimat bırakır. Talimat faz ortasında değil, bir sonraki faz sınırında uygulanır. Koşu yanlış yöne gidiyorsa ve kill etmek iyi işi de çöpe atacaksa kullanılır."
4
+ argument-hint: "#id \"<instruction>\" - e.g. #3 \"the field is called web, not frontend\". With no instruction, you are asked for it."
5
+ ---
6
+
7
+ # multi-agent steer - correct a run without stopping it
8
+
9
+ **Input**: $ARGUMENTS
10
+
11
+ Leave one instruction for a running task. The next phase reads it before it
12
+ starts work, applies it, and marks it consumed.
13
+
14
+ This exists because the alternative was losing the run. `kill` stops a task and
15
+ deletes its worktree; `resume` picks a stopped one back up. Neither helps while
16
+ a phase is in flight, so a correction that arrived mid-run - "that field is
17
+ called `web`, not `frontend`", "keep the analysis, drop the rest" - had nowhere
18
+ to go, and the run carried on in the wrong direction until it finished.
19
+
20
+ **Not a second prompt.** One instruction is queued at a time. Steering a task
21
+ that already has an unconsumed instruction replaces it, after showing you what
22
+ is being replaced.
23
+
24
+ ## Steps
25
+
26
+ 1. **Find the task** - parse `#N` or `{JIRA-KEY}-XXXXX` from the argument,
27
+ the same way `kill` and `resume` do. Locate its `agent-state.json`:
28
+
29
+ ```bash
30
+ find {repo}/.worktrees/ -name "agent-state.json" -maxdepth 2
31
+ find $HOME/.claude/logs/multi-agent -maxdepth 4 -name agent-state.json -path '*/artifacts/*'
32
+ ```
33
+
34
+ Not found → `ERR: no task #N. '/multi-agent:status' lists what is running.`
35
+
36
+ 2. **Check it can still be steered** - read `status` and `currentPhase`:
37
+
38
+ | State | What to do |
39
+ |---|---|
40
+ | `in_progress` | Queue it. This is the case the command is for. |
41
+ | `paused` / `failed` | Say the task is not running, and that `/multi-agent:resume #N` will re-enter with the instruction applied at that phase's entry. Queue it. |
42
+ | `complete` | Refuse. Nothing will read it. Point at `/multi-agent` for a follow-up run. |
43
+
44
+ `currentPhase` is 7 and status is `in_progress` → warn that Phase 7 is the
45
+ last one, so an instruction queued now may never be consumed.
46
+
47
+ 3. **Read the instruction** - from the argument, or ask for it when the
48
+ argument carries only an id. Verbatim, up to 4000 characters. Do not
49
+ summarize or rewrite it: the phase that consumes it needs the user's own
50
+ words, and a paraphrase is where the meaning goes.
51
+
52
+ 4. **Show what will be queued, and ask**:
53
+
54
+ ```
55
+ Steer #3 ({JIRA-KEY}-12345, Phase 3 Dev, in_progress)
56
+
57
+ "the field is called web, not frontend"
58
+
59
+ Applied at the entry to Phase 4. The current phase finishes as it is.
60
+ ```
61
+
62
+ Already carrying an unconsumed `pendingSteer` → print the old text above the
63
+ new one and ask whether to replace it.
64
+
65
+ 5. **Write it** - through the state writer, never by editing the file, because
66
+ the running task is writing to it too. `$STATE_FILE` is the path from step 1
67
+ and `$INSTRUCTION` the text from step 3:
68
+
69
+ ```bash
70
+ printf '{"pendingSteer":{"text":%s,"at":"%s","appliedAt":null,"appliedPhase":null}}' \
71
+ "$(node -e 'process.stdout.write(JSON.stringify(process.argv[1]))' "$INSTRUCTION")" \
72
+ "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
73
+ | node $HOME/.claude/scripts/write-state.mjs "$STATE_FILE"
74
+ ```
75
+
76
+ The instruction is JSON-encoded by `node -e`, not by hand: it is arbitrary
77
+ user text, and a quote or newline in it would otherwise produce invalid JSON
78
+ or, worse, a payload that merges into fields nobody meant to touch.
79
+
80
+ Exit 2 (lock timeout) → the task is mid-write. Retry once, then report it
81
+ rather than forcing the write.
82
+
83
+ 6. **Confirm**: `🧭 Steer queued for #N - applies at the entry to Phase {N+1}`
84
+
85
+ ## What the phase does with it
86
+
87
+ Phase entry, before any work: read a `pendingSteer` that has no `appliedAt`.
88
+ When present, apply it to that phase's context, set `appliedAt` and
89
+ `appliedPhase` - which is what stops it being read a second time - and write a
90
+ `Steer applied` line into `agent-log.md`. The record itself stays, so the run
91
+ keeps the trace of what was asked and when. The contract lives in
92
+ `$HOME/.claude/multi-agent-refs/phases.md` under "Phase entry - pending
93
+ steer" (the file has a second, unrelated "Phase entry" line inside the tracker
94
+ block).
95
+
96
+ Applied at entry rather than the moment it arrives, on purpose: a phase that
97
+ changes target halfway through throws away the work it already did, which is
98
+ the outcome this command exists to avoid.
99
+
100
+ An instruction that contradicts the plan is not silently obeyed. The phase says
101
+ what it is changing, and a contradiction that would invalidate an approved plan
102
+ halts for the user instead of quietly rewriting it.
103
+
104
+ **One limit worth knowing.** The reader is the phase contract, so a task started
105
+ by an install that predates it will not consume the field: the instruction is
106
+ written and nothing picks it up. There is no version stamp on a state file to
107
+ detect this from, so the honest advice is that steer applies to runs started
108
+ after the install carrying it. A run already in flight from an older install is
109
+ still a `kill` or a wait.
@@ -59,7 +59,7 @@ Run every step automatically:
59
59
  ```
60
60
  Step 1: PLATFORM Detect macOS / Linux / Windows (Git Bash / WSL); export PLATFORM env
61
61
  Step 1.5: DETECT Compare timestamps, find stale targets
62
- Step 2: COPILOT Claude Code -> Copilot CLI (instructions + 54 sub-command skills)
62
+ Step 2: COPILOT Claude Code -> Copilot CLI (instructions + 55 sub-command skills)
63
63
  Step 2b: CODEX Claude Code -> Codex CLI (1 router skill + 51 specs as refs + 8 agent TOML)
64
64
  Step 3: REPO Claude Code -> pipeline repo (genericized, personal data scrub, bash -n on all sh)
65
65
  Step 3c: PLUGINS pipeline shared/external -> multi-agent-plugins marketplace (rebuild knowledge/,
@@ -166,7 +166,7 @@ If nothing is stale → report "All targets up to date" and stop.
166
166
  Unlike the Copilot step, this one does **not** hand-copy files. The Codex tree is a
167
167
  *transform* of the Claude tree, not a mirror of it, and the transform is real work:
168
168
 
169
- - the 54 sub-command specs become reference files, because Codex silently truncates
169
+ - the 55 sub-command specs become reference files, because Codex silently truncates
170
170
  its skills block (see `cross-cli-contract.md` 2.6 for the measurement)
171
171
  - every `$HOME/.claude/...` reference to a CLI-owned tree is retargeted, with
172
172
  `agents/<persona>.md` becoming `.toml` and the dispatcher becoming the router skill
@@ -479,17 +479,15 @@ When invoked with the `release` argument:
479
479
 
480
480
  ## Sub-Command Sync (Claude Code <-> Copilot CLI Skills)
481
481
 
482
- > Codex takes the Step 2b path instead; see that section.
483
-
484
482
  This runs on the Claude <-> Copilot axis. Codex is NOT synced here: it receives the
485
- same 51 specs as reference files rather than as peer skills, via Step 2b - see
483
+ same 55 specs as reference files rather than as peer skills, via Step 2b - see
486
484
  `cross-cli-contract.md` 2.6 for why the parity axis differs per host.
487
485
 
488
486
  | Claude Code | Copilot CLI |
489
487
  |-------------|-------------|
490
488
  | `~/.claude/commands/multi-agent/{cmd}/SKILL.md` | `~/.copilot/skills/multi-agent-{cmd}/SKILL.md` |
491
489
 
492
- **54 commands are synced** (canonical inventory - must match `cross-cli-contract.md` section 1; drift = contract violation):
490
+ **55 commands are synced** (canonical inventory - must match `cross-cli-contract.md` section 1; drift = contract violation):
493
491
 
494
492
  ```
495
493
  analysis, analysis-resolve, autopilot, build-optimize, channels,
@@ -498,7 +496,7 @@ dev-local-autopilot, diff-explain, feedback, forget, garbage-collect,
498
496
  graph, help, ios-coding-standard, issue, jira, kill, language, local,
499
497
  local-autopilot, log, manual-test, prune-logs, prune-prompts, purge,
500
498
  refactor, resume, resume-local, review, review-analysis, review-issue,
501
- review-jira, routines, save, scan, search, setup, stack, status,
499
+ review-jira, routines, save, scan, search, setup, stack, status, steer,
502
500
  store-ready, sync, test, test-accessibility, test-dark-mode,
503
501
  test-dynamic-type, test-screenshots, testflight-validation, uninstall, update
504
502
  ```
@@ -6,7 +6,7 @@
6
6
 
7
7
  ---
8
8
 
9
- ## 1. Command Inventory (54 files, 50 live commands)
9
+ ## 1. Command Inventory (55 files, 51 live commands)
10
10
 
11
11
  ```
12
12
  analysis, analysis-resolve, autopilot, build-optimize, channels,
@@ -15,7 +15,7 @@ dev-local-autopilot, diff-explain, feedback, forget, garbage-collect,
15
15
  graph, help, ios-coding-standard, issue, jira, kill, language, local,
16
16
  local-autopilot, log, manual-test, prune-logs, prune-prompts, purge,
17
17
  refactor, resume, resume-local, review, review-analysis, review-issue,
18
- review-jira, routines, save, scan, search, setup, stack, status,
18
+ review-jira, routines, save, scan, search, setup, stack, status, steer,
19
19
  store-ready, sync, test, test-accessibility, test-dark-mode,
20
20
  test-dynamic-type, test-screenshots, testflight-validation, uninstall, update
21
21
  ```
@@ -27,12 +27,12 @@ Categories:
27
27
  - **Pipeline entries**: `autopilot`, `local`, `local-autopilot` (plus the bare `/multi-agent` in the dispatcher). Depth is not a command: `/multi-agent` and `local` ask Full or Short at Phase 0 Step 7.5; the two autopilot entries never ask and always run Full.
28
28
  - **Retired stubs** (v16.0.0, deleted next minor - they print a redirect and run no phase): `dev`, `dev-local` redirect to the picker entries with Short; `dev-autopilot`, `dev-local-autopilot` have no equivalent, because fast-plus-unattended no longer exists
29
29
  - **Tail modes** (run the pipeline tail over already-done local work): `resume-local`
30
- - **Ops commands** (one-shot, no worktree): `status`, `log`, `kill`, `purge`, `uninstall`, `resume`, `review`, `review-jira`, `review-issue`, `analysis`, `analysis-resolve`, `complaint-analysis`, `build-optimize`, `channels`, `scan`, `search`, `diff-explain`, `garbage-collect`, `graph`, `prune-logs`, `prune-prompts`
30
+ - **Ops commands** (one-shot, no worktree): `status`, `log`, `kill`, `steer`, `purge`, `uninstall`, `resume`, `review`, `review-jira`, `review-issue`, `analysis`, `analysis-resolve`, `complaint-analysis`, `build-optimize`, `channels`, `scan`, `search`, `diff-explain`, `garbage-collect`, `graph`, `prune-logs`, `prune-prompts`
31
31
  - **Local audits** (worktree only to build; no commit, push, PR or channels): `design-check`, `testflight-validation`, `ios-coding-standard`. `testflight-validation` additionally never invokes `altool --upload-app` - a validation run must not be able to ship a build by accident.
32
32
  - **Meta-ops**: `setup`, `sync`, `update`, `help`, `refactor`, `test`, `stack`, `manual-test`, `language`
33
33
  - **Routines** (user-defined routine registry; the routines they create are local-only and never synced): `save`, `routines`, `forget`
34
34
 
35
- The count is 54 files and 50 live commands until the four stubs are deleted, at which point both numbers become 50. A stub is still installed and still invocable, so counting it as absent would be wrong; counting it as a command would be worse.
35
+ The count is 55 files and 51 live commands until the four stubs are deleted, at which point both numbers become 51. A stub is still installed and still invocable, so counting it as absent would be wrong; counting it as a command would be worse.
36
36
 
37
37
  > **Inventory drift is a contract violation.** Adding a slash command under `pipeline/commands/multi-agent/` without updating this list + its counterpart Copilot dir (`pipeline/skills/shared/core/multi-agent-<cmd>/`) is a merge blocker. `smoke-commands-skills-parity.sh` enforces command ↔ skill directory parity; `smoke-cross-cli-behavior.sh` enforces behavior parity. This doc is the authoritative command list - bump the count + table together.
38
38
 
@@ -95,6 +95,29 @@ halt per the halt-visibility rule), `3` I/O error.
95
95
  Reads need no wrapper; the rename makes any read see either the old or the new
96
96
  document, never a truncated one.
97
97
 
98
+ **`rev`, and reading before writing back.** Every successful write bumps
99
+ `state.rev`, a counter that only increases. It exists because a read and the
100
+ write that follows it are not one operation. The lock above makes a single write
101
+ atomic and nothing more; the concurrent worktrees named there, and
102
+ `/multi-agent:steer`, which writes into the state file of a task that is still
103
+ running, both land inside the gap between a phase's read and its write-back.
104
+
105
+ A phase that will write back what it read keeps the `rev` it read, re-reads
106
+ immediately before writing, and re-derives its patch when the number has moved.
107
+ `write-state.mjs` takes the higher of the on-disk `rev` and the one in the
108
+ incoming patch, so a stale patch can never walk the counter backwards - but that
109
+ only protects the counter, not the fields. Deciding what to do about a
110
+ concurrent change is the caller's job.
111
+
112
+ A record written before `rev` existed has none: treat a missing `rev` as 0.
113
+
114
+ **Stale locks: liveness outranks age.** A lock is reclaimed when its holder PID
115
+ is no longer alive. A holder that is alive keeps its lock however old the file
116
+ is - the reverse order (age first) is what let a writer delete a live lock and
117
+ lose the update behind it. `WRITE_STATE_LOCK_STALE_MS` now applies only to a
118
+ lock with no readable PID, and `WRITE_STATE_LOCK_ABANDON_MS` (default ten times
119
+ that) is the last-resort ceiling for a PID that has been recycled.
120
+
98
121
  **Halt visibility (required, autopilot included).** A halt is never silent. Whenever a phase halts on a hard error (validator failed twice, no subagent returned, dispatch error past fallback, lock irrecoverable), in addition to the `agent-log.md` line: (a) write `state.status = "paused"` and `state.haltReason = "<phase>:<cause>"`; (b) record the cause on the tracker via `phase-tracker.sh meta <phase> halt "<cause>"` and `phase-tracker.sh update <phase> failed`; (c) emit one `>&2` alert line `HALT phase <N>: <cause> - resume with /multi-agent:resume #<id>`; (d) if `prefs.global.usageLog.enabled` is true, emit the end-of-run report so a run that never reaches Phase 7 is still recorded with the phase it stopped at (`state.currentPhase` + `haltReason`) - the emitter no-ops when it is off or unconfigured:
99
122
 
100
123
  ```bash