@mmerterden/multi-agent-pipeline 16.9.0 → 16.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -26,6 +26,31 @@ Internal file-layout changes that don't affect the slash-command surface are sti
26
26
 
27
27
  - **`smoke-website-deploy-identity.sh` (20 assertions).** It runs the script against throwaway repos with real remotes rather than grepping the doc: the happy path lands and pushes, an exported `GIT_AUTHOR_EMAIL` halts with the commit still local, an unchanged tree makes no empty commit, a matching config is preserved while a wrong name is corrected, and missing or non-repository arguments exit 2. Wiring is asserted separately, since a correct script nothing calls is its own failure mode.
28
28
 
29
+ ## [16.10.1] - 2026-08-26
30
+
31
+ ### Fixed
32
+
33
+ - **`write-state.mjs` could delete a live lock and lose a writer's update.** The stale-lock reclaim deleted by path: between judging a lock stale and unlinking it, the holder can release and a third writer can acquire a fresh one, so the unlink removed a *live* lock and two writers then held it. It is the same failure the PID-window comment in that file already describes, at a different point in the acquire loop, and it survived because it only reproduces under load - `smoke-write-state.sh` failed inside a full gate run and passed 12/12 when run alone. Reclaim is now by identity: the inode and mtime judged stale must still be the file at that path, otherwise it belongs to somebody else and is left alone. Twelve runs under artificial load are clean, which is evidence and not proof - a race cannot be proven absent.
34
+
35
+ - **Counts that had drifted from the tree.** `skills-index.md` and `skills/shared/README.md` still said 208 skills against 210 on disk; both are generated, so they were regenerated rather than hand-edited. `/multi-agent:update` quoted "245 scripts, 208 skills" for what an install lays down; it is 263 and 210. A comment in `smoke-command-inventory.sh` used "51 commands" as its example, which is the kind of number that goes stale the moment a command lands - it now says what it means without pinning a figure.
36
+
37
+ ## [16.10.0] - 2026-08-26
38
+
39
+ Three debts the last few releases kept naming, closed.
40
+
41
+ ### Added
42
+
43
+ - **`smoke-help-sync.sh` pairs each command's own description with the line `/multi-agent:help` shows for it.** Three releases running shipped the same defect: 16.7.1 (two commands absent from help entirely), 16.8.1 (the analysis entry describing behaviour from two releases earlier), 16.9.0 (a telemetry block still saying "optional, opt-in" after the default flipped). Every other gate was green through all three, because none of them asked whether the sentence a user reads still matches what the command does. The gate hashes both sides and fails when one moved without the other. Its limit is written into the file: it cannot tell whether either text is *correct* - a maintainer who edits both to say something equally wrong still passes. What it forces is that the pair gets looked at together, which is exactly what did not happen those three times.
44
+
45
+ ### Changed
46
+
47
+ - **The telemetry self-registration path no longer needs `jq`.** It read preferences through `jq` behind a `command -v jq` guard, so a machine without jq silently never registered and never reported - which in the panel is indistinguishable from nobody using the pipeline. Reads now go through `node`, a declared engine (>=20.11), so the path cannot be skipped for a missing tool. Thirteen other shell helpers still use jq; the install now prints one note when it is absent, naming what degrades (cost summaries, the tracker's JSON reads, convention extraction, skill signing) instead of leaving each to fail quietly. Both READMEs gained a prerequisites section.
48
+ - **The phase-doc token budget has room again.** The token-telemetry instructions were repeated verbatim in four phase docs while the canonical text already lived in `progress-contract.md`; the phases now point at it. Total goes 57599 -> 57059 against a 57600 ceiling, so headroom moves from 1 token to 541. The ceiling was not raised - the space came back from text that had more than its share, the same rule 16.7.1 followed for `help`.
49
+
50
+ ### Fixed
51
+
52
+ - Six phase docs cited `progress-contract.md#token-telemetry-forwarding-v83`; the heading anchors as `#token-telemetry-forwarding`, so none of those links resolved.
53
+
29
54
  ## [16.9.0] - 2026-08-25
30
55
 
31
56
  ### Added
package/README.md CHANGED
@@ -14,6 +14,12 @@ Runs natively on Claude Code, Copilot CLI and Codex CLI. macOS / Linux / Windows
14
14
 
15
15
  📐 **[Architecture diagrams](./docs/architecture.md)** - the 8-phase flow, operating modes, review/triage, Figma subphases, component layout. **[Ecosystem diagram](./docs/ecosystem.md)** - how this repo, the `multi-agent-plugins` marketplace and `multi-agent-toolkit-mcp` compose.
16
16
 
17
+ ### Prerequisites
18
+
19
+ - **Node.js >= 20.11** - required; the pipeline's own tooling runs on it.
20
+ - **`jq`** - optional but recommended. Thirteen shell helpers reach for it and skip in silence without it: cost summaries, the phase tracker's JSON reads, convention extraction, skill signing. The install prints a note when it is missing.
21
+ - **`gh`** - for GitHub issue and PR work. Its built-in `--jq` is independent of the `jq` binary.
22
+
17
23
  ## Quick Start
18
24
 
19
25
  ```bash
package/README.tr.md CHANGED
@@ -14,6 +14,12 @@ Claude Code, Copilot CLI ve Codex CLI üzerinde native çalışır. macOS / Linu
14
14
 
15
15
  📐 **[Mimari diyagramları](./docs/architecture.md)** - 8 faz akışı, çalışma modları, review/triage, Figma subphase'leri, component yapısı. **[Ekosistem diyagramı](./docs/ecosystem.md)** - bu repo, `multi-agent-plugins` marketplace'i ve `multi-agent-toolkit-mcp`'nin nasıl bir araya geldiği.
16
16
 
17
+ ### Önkoşullar
18
+
19
+ - **Node.js >= 20.11** - zorunlu; pipeline'ın kendi araçları bunun üzerinde koşuyor.
20
+ - **`jq`** - opsiyonel ama önerilir. On üç kabuk yardımcısı ona başvuruyor ve yokken sessizce atlıyor: maliyet özetleri, faz izleyicisinin JSON okumaları, konvansiyon çıkarımı, skill imzalama. Kurulum, eksikse bir not basıyor.
21
+ - **`gh`** - GitHub issue ve PR işleri için. Kendi dahili `--jq`'su, `jq` ikilisinden bağımsız.
22
+
17
23
  ## Hızlı Başlangıç
18
24
 
19
25
  ```bash
package/install/index.mjs CHANGED
@@ -9,6 +9,7 @@
9
9
  */
10
10
 
11
11
  import { dirname } from "path";
12
+ import { execFileSync } from "node:child_process";
12
13
  import { fileURLToPath } from "url";
13
14
 
14
15
  import { readFileSync } from "fs";
@@ -144,6 +145,17 @@ export function writeVersionMarkers({ home, forClaude, forCopilot, forCodex }) {
144
145
  * @param {string[]} flags
145
146
  * @returns {"ios"|"android"|"all"}
146
147
  */
148
+ // Presence check only - never a hard requirement. The pipeline's own engine is
149
+ // node; jq is a convenience several shell helpers reached for.
150
+ function hasJq() {
151
+ try {
152
+ execFileSync("jq", ["--version"], { stdio: "ignore", timeout: 3000 });
153
+ return true;
154
+ } catch {
155
+ return false;
156
+ }
157
+ }
158
+
147
159
  function parsePlatformFlag(flags) {
148
160
  const raw = flags.find((f) => f.startsWith("--platform="));
149
161
  if (!raw) return "all";
@@ -203,6 +215,19 @@ function printSummary(opts) {
203
215
  " Skipped only when the host CLI is not on PATH - the command to run is printed above.",
204
216
  );
205
217
  console.log("");
218
+ // jq is not a declared engine and macOS did not always ship it, yet thirteen
219
+ // runtime paths guard on it and skip in silence when it is missing. Silence is
220
+ // the problem: a cost summary that never renders and a tracker that never
221
+ // updates look like bugs, not like an absent tool. Say it once, here, where
222
+ // somebody can act on it.
223
+ if (!hasJq()) {
224
+ console.log(" Note: jq was not found on PATH.");
225
+ console.log(" Everything installs and the pipeline runs, but a few shell");
226
+ console.log(" helpers degrade quietly without it: cost summaries, the phase");
227
+ console.log(" tracker's JSON reads, convention extraction and skill signing.");
228
+ console.log(" Install it (brew install jq / apt install jq) to get them back.");
229
+ console.log("");
230
+ }
206
231
  console.log(" Uninstall everything: npx @mmerterden/multi-agent-pipeline uninstall");
207
232
  console.log(" (Personal access tokens in keychain are preserved.)");
208
233
  console.log("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "16.9.0",
3
+ "version": "16.10.1",
4
4
  "description": "8-phase AI development pipeline with full orchestration on Claude Code, Copilot CLI and Codex CLI. Analysis, planning, TDD, CLI-aware parallel review with consensus surfacing + Fable triage, default-FAIL evidence gates, secret + intent guards, per-phase cost ledger, persistent learnings memory, wiki generation, commit automation. Token-preserving uninstall.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -113,29 +113,34 @@ A git clone of the pipeline repo is a maintainer workspace, kept in sync by `/mu
113
113
  self-registration permanently; print the opt-out hint on first auto-enable.
114
114
  ```bash
115
115
  PREFS="$HOME/.claude/multi-agent-preferences.json"
116
- if [ -f "$PREFS" ] && command -v jq >/dev/null 2>&1; then
117
- ENABLED=$(jq -r '.global.usageLog.enabled // false' "$PREFS" 2>/dev/null)
118
- OPTOUT=$(jq -r '.global.usageLog.optOut // false' "$PREFS" 2>/dev/null)
116
+ # Reads go through node, not jq. node is a declared engine (>=20.11) so it
117
+ # is always there; jq is not, and gating this block on it meant a machine
118
+ # without jq silently never registered and never reported - which reads in
119
+ # the panel exactly like nobody using the pipeline.
120
+ if [ -f "$PREFS" ]; then
121
+ pref() { node -e 'const fs=require("fs");let v;try{v=process.argv[2].split(".").reduce((a,k)=>a?.[k],JSON.parse(fs.readFileSync(process.argv[1],"utf8")))}catch{};process.stdout.write(v==null?"":String(v))' "$PREFS" "$1" 2>/dev/null; }
122
+ ENABLED=$(pref global.usageLog.enabled)
123
+ OPTOUT=$(pref global.usageLog.optOut)
119
124
  if [ "$ENABLED" != "true" ] && [ "$OPTOUT" != "true" ]; then
120
125
  TOK="${MULTI_AGENT_USAGE_TOKEN:-}"
121
- [ -z "$TOK" ] && TOK=$(jq -r '.global.usageLog.token // empty' "$PREFS" 2>/dev/null)
126
+ [ -z "$TOK" ] && TOK=$(pref global.usageLog.token)
122
127
  if [ -z "$TOK" ]; then
123
- KNAME=$(jq -r '.global.keychainMapping.usage_ingest // empty' "$PREFS" 2>/dev/null)
128
+ KNAME=$(pref global.keychainMapping.usage_ingest)
124
129
  [ -n "$KNAME" ] && TOK=$(bash "$HOME/.claude/lib/credential-store.sh" get "$KNAME" 2>/dev/null)
125
130
  fi
126
131
  if [ -z "$TOK" ]; then
127
- EP=$(jq -r '.global.usageLog.endpoint // "https://mmerterden.vercel.app/api/usage/ingest"' "$PREFS" 2>/dev/null)
132
+ EP=$(pref global.usageLog.endpoint); [ -z "$EP" ] && EP="https://mmerterden.vercel.app/api/usage/ingest"
128
133
  REG_EP="${EP%/ingest}/register"
129
134
  # Telemetry identity is the GitHub account name, never the git
130
135
  # identity.name (which can carry a corporate title). username -> live
131
136
  # gh login -> OS user.
132
- RUSER=$(jq -r '.global.identities[0].username // empty' "$PREFS" 2>/dev/null)
137
+ RUSER=$(pref global.identities.0.username)
133
138
  [ -z "$RUSER" ] && RUSER=$(gh api user --jq .login 2>/dev/null || echo "")
134
139
  [ -z "$RUSER" ] && RUSER="$USER"
135
140
  RESP=$(curl -sSL -m 10 -X POST -H "Content-Type: application/json" \
136
141
  --data "{\"u\":\"$RUSER\",\"c\":\"$(hostname -s 2>/dev/null || echo unknown)\"}" \
137
142
  "$REG_EP" 2>/dev/null)
138
- TOK=$(printf '%s' "$RESP" | jq -r '.token // empty' 2>/dev/null)
143
+ TOK=$(printf '%s' "$RESP" | node -e 'let b="";process.stdin.on("data",d=>b+=d).on("end",()=>{try{process.stdout.write(String(JSON.parse(b).token||""))}catch{}})' 2>/dev/null)
139
144
  if [ -n "$TOK" ]; then
140
145
  printf '%s' "$TOK" | bash "$HOME/.claude/lib/credential-store.sh" set "${USER}_Usage_Ingest_Token" "$(cat)"
141
146
  node -e 'const fs=require("fs"),p=process.argv[1];const j=JSON.parse(fs.readFileSync(p,"utf8"));j.global=j.global||{};j.global.keychainMapping=j.global.keychainMapping||{};j.global.keychainMapping.usage_ingest=process.argv[2];fs.writeFileSync(p,JSON.stringify(j,null,2)+"\n");' "$PREFS" "${USER}_Usage_Ingest_Token"
@@ -179,7 +184,7 @@ A git clone of the pipeline repo is a maintainer workspace, kept in sync by `/mu
179
184
  ```
180
185
  Current: v15.6.0 Latest: v15.6.1
181
186
  -> npm pack @{npm-scope}/multi-agent-pipeline@15.6.1
182
- -> node install.js --all (53 commands, 245 scripts, 208 skills)
187
+ -> node install.js --all (53 commands, 263 scripts, 210 skills)
183
188
  -> migrate-prefs.mjs (0 changes - already v2.6.0)
184
189
 
185
190
  ✓ Updated: v15.6.0 → v15.6.1
@@ -215,7 +215,7 @@ LOG_METRIC_FORWARD_TO_TRACKER=1 $HOME/.claude/scripts/log-metric.sh "$TASK_ID" 1
215
215
  model=opus tokens_in=$IN tokens_out=$OUT duration_ms=$DUR
216
216
  ```
217
217
 
218
- Best-effort. See `$HOME/.claude/multi-agent-refs/progress-contract.md#token-telemetry-forwarding-v83` for the canonical contract.
218
+ Best-effort. See `$HOME/.claude/multi-agent-refs/progress-contract.md#token-telemetry-forwarding` for the canonical contract.
219
219
 
220
220
  #### Prior-Art Enrichment (advisory)
221
221
 
@@ -234,12 +234,9 @@ Treat hits as **context only** - they are past Phase 4 verdicts, not prescript
234
234
 
235
235
  ## Token telemetry - invoke after every LLM call
236
236
 
237
- After every LLM call in this phase (analysis, plan generation, code/diff write, review verdict, triage), invoke:
238
-
239
237
  ```bash
240
238
  bash $HOME/.claude/scripts/phase-tracker.sh tokens 1 <input_count> <output_count>
241
239
  ```
242
240
 
243
- The tracker accumulates the totals additively, so multiple calls in the same phase compound. The render output then shows live cost on the active phase tile (e.g. `Phase 1 Dev 2m 14s · 12.4k tok`). This satisfies the contract in `$HOME/.claude/multi-agent-refs/tracker-contract.md` and the `smoke-tracker-tokens-invocation.sh` enforcement gate. Skipping this call is the #1 cause of "I can't see how much it cost" complaints.
241
+ Contract and rationale: `progress-contract.md` -> Token telemetry forwarding.
244
242
 
245
- If you do not have access to the model's reported token counts, pass best-effort estimates derived from input length / output length - partial cost data is better than none.
@@ -327,18 +327,15 @@ LOG_METRIC_FORWARD_TO_TRACKER=1 $HOME/.claude/scripts/log-metric.sh "$TASK_ID" 2
327
327
  model=opus tokens_in=$IN tokens_out=$OUT duration_ms=$DUR iteration=$N
328
328
  ```
329
329
 
330
- Best-effort. See `$HOME/.claude/multi-agent-refs/progress-contract.md#token-telemetry-forwarding-v83`.
330
+ Best-effort. See `$HOME/.claude/multi-agent-refs/progress-contract.md#token-telemetry-forwarding`.
331
331
 
332
332
  ---
333
333
 
334
334
  ## Token telemetry - invoke after every LLM call
335
335
 
336
- After every LLM call in this phase (analysis, plan generation, code/diff write, review verdict, triage), invoke:
337
-
338
336
  ```bash
339
337
  bash $HOME/.claude/scripts/phase-tracker.sh tokens 2 <input_count> <output_count>
340
338
  ```
341
339
 
342
- The tracker accumulates the totals additively, so multiple calls in the same phase compound. The render output then shows live cost on the active phase tile (e.g. `Phase 2 Dev 2m 14s · 12.4k tok`). This satisfies the contract in `$HOME/.claude/multi-agent-refs/tracker-contract.md` and the `smoke-tracker-tokens-invocation.sh` enforcement gate. Skipping this call is the #1 cause of "I can't see how much it cost" complaints.
340
+ Contract and rationale: `progress-contract.md` -> Token telemetry forwarding.
343
341
 
344
- If you do not have access to the model's reported token counts, pass best-effort estimates derived from input length / output length - partial cost data is better than none.
@@ -345,21 +345,17 @@ LOG_METRIC_FORWARD_TO_TRACKER=1 $HOME/.claude/scripts/log-metric.sh "$TASK_ID" 3
345
345
  tokens_in=$IN tokens_out=$OUT duration_ms=$DUR
346
346
  ```
347
347
 
348
- Model resolves from the active mode: a Full run uses Sonnet, a Short run uses Opus. Best-effort. See `$HOME/.claude/multi-agent-refs/progress-contract.md#token-telemetry-forwarding-v83`.
348
+ Model resolves from the active mode: a Full run uses Sonnet, a Short run uses Opus. Best-effort. See `$HOME/.claude/multi-agent-refs/progress-contract.md#token-telemetry-forwarding`.
349
349
 
350
350
  ---
351
351
 
352
352
  ## Token telemetry - invoke after every LLM call
353
353
 
354
- After every LLM call in this phase (analysis, plan generation, code/diff write, review verdict, triage), invoke:
355
-
356
354
  ```bash
357
355
  bash $HOME/.claude/scripts/phase-tracker.sh tokens 3 <input_count> <output_count>
358
356
  ```
359
357
 
360
- The tracker accumulates the totals additively, so multiple calls in the same phase compound. The render output then shows live cost on the active phase tile (e.g. `Phase 3 Dev 2m 14s · 12.4k tok`). This satisfies the contract in `$HOME/.claude/multi-agent-refs/tracker-contract.md` and the `smoke-tracker-tokens-invocation.sh` enforcement gate. Skipping this call is the #1 cause of "I can't see how much it cost" complaints.
361
-
362
- If you do not have access to the model's reported token counts, pass best-effort estimates derived from input length / output length - partial cost data is better than none.
358
+ Contract and rationale: `progress-contract.md` -> Token telemetry forwarding.
363
359
 
364
360
 
365
361
  #### Generated trees are not yours to edit
@@ -565,7 +565,7 @@ bash "$M" "$TASK_ID" 4 review.completed raw_count=$RAW accepted=$ACC \
565
565
  deferred=$DEF rejected=$REJ approved=$APPROVED duration_ms=$DURATION
566
566
  ```
567
567
 
568
- `LOG_METRIC_FORWARD_TO_TRACKER=1` mirrors `tokens_in`/`tokens_out`/`model` into `phase-tracker.sh` (see `$HOME/.claude/multi-agent-refs/progress-contract.md#token-telemetry-forwarding-v83`). On non-zero validator exit (1/2/3), also emit `triage.edge_case` with cause. Omit `tokens_in`/`tokens_out` if unavailable. Best-effort - never fails the pipeline.
568
+ `LOG_METRIC_FORWARD_TO_TRACKER=1` mirrors `tokens_in`/`tokens_out`/`model` into `phase-tracker.sh` (see `$HOME/.claude/multi-agent-refs/progress-contract.md#token-telemetry-forwarding`). On non-zero validator exit (1/2/3), also emit `triage.edge_case` with cause. Omit `tokens_in`/`tokens_out` if unavailable. Best-effort - never fails the pipeline.
569
569
 
570
570
  ##### 3.5 Optional cross-check (single-point-of-failure mitigation)
571
571
 
@@ -630,12 +630,11 @@ Log: "Phase 4: Review - raw={N1+N2} accepted={Na} deferred={Nd} rejected={Nr}
630
630
 
631
631
  ## Token telemetry - invoke after every LLM call
632
632
 
633
- After every LLM call in this phase (analysis, plan generation, code/diff write, review verdict, triage), invoke:
634
-
635
633
  ```bash
636
634
  bash $HOME/.claude/scripts/phase-tracker.sh tokens 4 <input_count> <output_count> [cached_count]
637
635
  ```
638
636
 
639
637
  The optional 4th `cached_count` is the prompt-cache-read token count when the host reports it (Anthropic `cache_read_input_tokens`); it defaults to 0 and is priced at the cheaper `cacheReadPerMtok` rate in the Phase 7 cost ledger. The tracker accumulates the totals additively, so multiple calls in the same phase compound. The render output then shows live cost on the active phase tile (e.g. `Phase 4 Dev 2m 14s · 12.4k tok`). This satisfies the contract in `$HOME/.claude/multi-agent-refs/tracker-contract.md` and the `smoke-tracker-tokens-invocation.sh` enforcement gate. Skipping this call is the #1 cause of "I can't see how much it cost" complaints.
640
638
 
641
- If you do not have access to the model's reported token counts, pass best-effort estimates derived from input length / output length - partial cost data is better than none.
639
+ Contract and rationale: `progress-contract.md` -> Token telemetry forwarding.
640
+
@@ -158,4 +158,4 @@ LOG_METRIC_FORWARD_TO_TRACKER=1 $HOME/.claude/scripts/log-metric.sh "$TASK_ID" 5
158
158
  model=opus tokens_in=$IN tokens_out=$OUT duration_ms=$DUR
159
159
  ```
160
160
 
161
- If Phase 5 is purely user-driven (no sub-agent ran), no token forwarding is required and the cost block stays empty for this phase. Best-effort. See `$HOME/.claude/multi-agent-refs/progress-contract.md#token-telemetry-forwarding-v83`.
161
+ If Phase 5 is purely user-driven (no sub-agent ran), no token forwarding is required and the cost block stays empty for this phase. Best-effort. See `$HOME/.claude/multi-agent-refs/progress-contract.md#token-telemetry-forwarding`.
@@ -345,4 +345,4 @@ LOG_METRIC_FORWARD_TO_TRACKER=1 $HOME/.claude/scripts/log-metric.sh "$TASK_ID" 6
345
345
  model=<sonnet|opus> tokens_in=$IN tokens_out=$OUT duration_ms=$DUR
346
346
  ```
347
347
 
348
- Best-effort. See `$HOME/.claude/multi-agent-refs/progress-contract.md#token-telemetry-forwarding-v83`.
348
+ Best-effort. See `$HOME/.claude/multi-agent-refs/progress-contract.md#token-telemetry-forwarding`.
@@ -100,12 +100,26 @@ async function acquireLock(
100
100
  /* already gone */
101
101
  }
102
102
  if (err.code !== "EEXIST") throw err;
103
- if (isLockStale(lockPath, staleMs)) {
104
- // Holder crashed or the lock outlived any plausible writer - reclaim it.
103
+ // Reclaim by identity, never by path. Between judging a lock stale and
104
+ // deleting it, the holder can release and a THIRD writer can acquire a
105
+ // fresh one - and an unlink by path deletes that live lock, after which
106
+ // two writers hold it and one update is lost. Same failure the PID window
107
+ // above once caused, at a different point in the loop; it survived because
108
+ // it only reproduces under load, where the gap is wide enough to lose.
109
+ //
110
+ // The inode is the identity: a newly acquired lock is a different file
111
+ // even at the same path. Delete only if the file we judged is still the
112
+ // file that is there.
113
+ const stale = lockIdentityIfStale(lockPath, staleMs);
114
+ if (stale) {
105
115
  try {
106
- unlinkSync(lockPath);
116
+ const now = statSync(lockPath);
117
+ if (now.ino === stale.ino && now.mtimeMs === stale.mtimeMs) {
118
+ unlinkSync(lockPath);
119
+ }
120
+ // Changed under us: somebody else's live lock. Leave it and retry.
107
121
  } catch {
108
- /* another writer won the race - fall through and retry */
122
+ /* vanished on its own - fall through and retry */
109
123
  }
110
124
  continue;
111
125
  }
@@ -128,28 +142,32 @@ async function acquireLock(
128
142
  * @param {number} staleMs
129
143
  * @returns {boolean}
130
144
  */
131
- function isLockStale(lockPath, staleMs) {
145
+ function lockIdentityIfStale(lockPath, staleMs) {
132
146
  let pid;
133
147
  let mtimeMs;
148
+ let ino;
134
149
  try {
135
150
  pid = parseInt(readFileSync(lockPath, "utf-8").trim(), 10);
136
- mtimeMs = statSync(lockPath).mtimeMs;
151
+ const st = statSync(lockPath);
152
+ mtimeMs = st.mtimeMs;
153
+ ino = st.ino;
137
154
  } catch {
138
155
  // Lock vanished between EEXIST and our read - let the retry re-open it.
139
- return false;
156
+ return null;
140
157
  }
141
- if (Date.now() - mtimeMs > staleMs) return true;
158
+ const identity = { ino, mtimeMs };
159
+ if (Date.now() - mtimeMs > staleMs) return identity;
142
160
  // An unreadable PID is NOT proof of staleness. Treating it as such is what
143
161
  // let a writer delete a live lock and lose another writer's update. With the
144
162
  // link-based acquire above a lock is never observable without its PID, so
145
163
  // this can only be genuine corruption - which the staleMs check reclaims
146
164
  // anyway, without racing a writer that is merely mid-flight.
147
- if (!Number.isInteger(pid) || pid <= 0) return false;
165
+ if (!Number.isInteger(pid) || pid <= 0) return null;
148
166
  try {
149
167
  process.kill(pid, 0); // probe liveness without signalling
150
- return false; // holder alive
168
+ return null; // holder alive
151
169
  } catch (err) {
152
- return err.code === "ESRCH"; // no such process - stale
170
+ return err.code === "ESRCH" ? identity : null; // no such process - stale
153
171
  }
154
172
  }
155
173
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "skillCount": 208,
3
+ "skillCount": 210,
4
4
  "entries": [
5
5
  {
6
6
  "name": "accessibility-compliance-accessibility-audit",
@@ -961,7 +961,7 @@
961
961
  },
962
962
  {
963
963
  "name": "multi-agent-analysis",
964
- "description": "Standalone feature-spec analysis (v3 template). Platform-agnostic concept layer with repo-driven convention extraction and per-platform Pass B render. 23 sections in Full mode; 8 of them in Lite mode (auto for small features). Collects Figma / Swagger / Confluence / Jira / Standards / Firebase / rep",
964
+ "description": "Standalone feature-spec analysis. Two profiles picked at intake: global (23-section development handoff, 8 of them in Lite mode) or corporate (IG/UC/FG requirements document with traceability matrices). Platform-agnostic concept layer with repo-driven convention extraction and per-platform Pass B re",
965
965
  "platform": null,
966
966
  "group": "core",
967
967
  "plugin": null,
@@ -1102,6 +1102,17 @@
1102
1102
  "triggerPaths": [],
1103
1103
  "relativePath": "shared/core/multi-agent-diff-explain/SKILL.md"
1104
1104
  },
1105
+ {
1106
+ "name": "multi-agent-feedback",
1107
+ "description": "Send one message to the maintainer: a bug, an idea or a question. Only the text you type is sent - no logs, no repo names, no paths. Shows the payload and asks before sending. Use when a run went wrong and the maintainer should know.",
1108
+ "platform": null,
1109
+ "group": "core",
1110
+ "plugin": null,
1111
+ "invokeAs": "multi-agent-feedback",
1112
+ "triggerKeywords": [],
1113
+ "triggerPaths": [],
1114
+ "relativePath": "shared/core/multi-agent-feedback/SKILL.md"
1115
+ },
1105
1116
  {
1106
1117
  "name": "multi-agent-forget",
1107
1118
  "description": "Remove a saved /multi-agent routine (created by /multi-agent:save): deletes its local-only command and its registry entry. Asks which one and confirms. Use when a saved routine is no longer wanted and should be removed.",
@@ -1311,6 +1322,17 @@
1311
1322
  "triggerPaths": [],
1312
1323
  "relativePath": "shared/core/multi-agent-review/SKILL.md"
1313
1324
  },
1325
+ {
1326
+ "name": "multi-agent-review-analysis",
1327
+ "description": "Review a written analysis document instead of a diff: resolve it from a path, a Confluence page or a Jira issue, run the deterministic gates first, then a parallel model review. Findings cite the Locked rule they break. Never edits the document. Use when an analysis needs judging before development ",
1328
+ "platform": null,
1329
+ "group": "core",
1330
+ "plugin": null,
1331
+ "invokeAs": "multi-agent-review-analysis",
1332
+ "triggerKeywords": [],
1333
+ "triggerPaths": [],
1334
+ "relativePath": "shared/core/multi-agent-review-analysis/SKILL.md"
1335
+ },
1314
1336
  {
1315
1337
  "name": "multi-agent-review-issue",
1316
1338
  "description": "Assess whether a GitHub issue is ready for multi-agent development: fetch it, grade scope / acceptance criteria / repro / design / API / stack readiness, then (after confirm) post the gaps as an issue comment. Read-only on code. Use when deciding whether a GitHub issue is specified well enough to ha",
@@ -2,11 +2,11 @@
2
2
 
3
3
  Single source of truth for skills delivered to both Claude Code (`~/.claude/skills/`) and Copilot CLI (`~/.copilot/skills/`) by the installer.
4
4
 
5
- **Total:** 208 skills (54 core + 154 external). Auto-generated by `scripts/gen-skills-index.mjs` - do not edit by hand.
5
+ **Total:** 210 skills (56 core + 154 external). Auto-generated by `scripts/gen-skills-index.mjs` - do not edit by hand.
6
6
 
7
7
  ## Directory layout
8
8
 
9
- - **`core/`** - 54 `multi-agent*` orchestration skills that are pipeline-critical. Edits here are core-code changes.
9
+ - **`core/`** - 56 `multi-agent*` orchestration skills that are pipeline-critical. Edits here are core-code changes.
10
10
  - **`external/`** - 154 iOS / Android / generic skills imported from the upstream skill library. Mirrors of third-party guidance.
11
11
  - Install destinations (ADR-0009): Claude Code gets NO local copy of `external/` - it loads those skills from the `multi-agent-plugins` marketplace, namespaced (`ai-<stack>-toolkit:<name>`); only the two compliance catalogs from `core/` land in `~/.claude/skills/`. Copilot CLI and Codex CLI receive a flat copy filtered to the enabled stacks. `external/` remains the single authoring source that `build-stack-plugins.mjs` publishes from.
12
12
 
@@ -14,7 +14,7 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
14
14
 
15
15
  ## Categories
16
16
 
17
- - [Pipeline Orchestration](#pipeline-orchestration) - 54
17
+ - [Pipeline Orchestration](#pipeline-orchestration) - 56
18
18
  - [iOS / Apple Ecosystem](#ios-apple-ecosystem) - 90
19
19
  - [Android / Kotlin](#android-kotlin) - 13
20
20
  - [Web / Frontend](#web-frontend) - 10
@@ -28,7 +28,7 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
28
28
  | [`apple-archive-compliance`](./core/apple-archive-compliance/) | `core` | Apple App Store Review compliance - wraps the multi-agent-toolkit-mcp `ios_app_store_audit` tool (18-rule deep scan) with ITMS error code |
29
29
  | [`google-play-compliance`](./core/google-play-compliance/) | `core` | Google Play Store publication compliance - bundletool + aapt2 + apksigner orchestration + 21-rule policy catalog with Play Console error c |
30
30
  | [`multi-agent`](./core/multi-agent/) | `core` | Task orchestrator: runs the full pipeline from a Jira ID or GitHub Issue URL - analysis → plan → TDD development → parallel review (Fable |
31
- | [`multi-agent-analysis`](./core/multi-agent-analysis/) | `core` | Standalone feature-spec analysis (v3 template). Platform-agnostic concept layer with repo-driven convention extraction and per-platform Pass |
31
+ | [`multi-agent-analysis`](./core/multi-agent-analysis/) | `core` | Standalone feature-spec analysis. Two profiles picked at intake: global (23-section development handoff, 8 of them in Lite mode) or corporat |
32
32
  | [`multi-agent-analysis-resolve`](./core/multi-agent-analysis-resolve/) | `core` | Resolve the Section 20 Risks and Open Questions of an analysis v3 document one row at a time: up to 3 source-labeled answer candidates per r |
33
33
  | [`multi-agent-autopilot`](./core/multi-agent-autopilot/) | `core` | Launch any task in autopilot mode: skips every confirmation, runs end-to-end autonomously. Use when a task should run end to end with no con |
34
34
  | [`multi-agent-build-optimize`](./core/multi-agent-build-optimize/) | `core` | Wrapper that dispatches to xcode-build-orchestrator on iOS repos. Benchmarks the current Xcode build, runs compilation / project / SPM analy |
@@ -41,6 +41,7 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
41
41
  | [`multi-agent-dev-local`](./core/multi-agent-dev-local/) | `core` | Removed in v16.0.0. Its worktree-free twin is /multi-agent:local, which asks the same depth question; answer Short there. Invoke only to see |
42
42
  | [`multi-agent-dev-local-autopilot`](./core/multi-agent-dev-local-autopilot/) | `core` | Retired alongside its worktree twin in v16.0.0, with nothing standing in for it. Invoke only to be pointed at /multi-agent:local-autopilot o |
43
43
  | [`multi-agent-diff-explain`](./core/multi-agent-diff-explain/) | `core` | Map Phase 4 triage findings to branch diff lines. Read-only post-hoc command, used after review to answer 'which finding lines up with which |
44
+ | [`multi-agent-feedback`](./core/multi-agent-feedback/) | `core` | Send one message to the maintainer: a bug, an idea or a question. Only the text you type is sent - no logs, no repo names, no paths. Shows t |
44
45
  | [`multi-agent-forget`](./core/multi-agent-forget/) | `core` | Remove a saved /multi-agent routine (created by /multi-agent:save): deletes its local-only command and its registry entry. Asks which one an |
45
46
  | [`multi-agent-garbage-collect`](./core/multi-agent-garbage-collect/) | `core` | Sweep leftover /tmp scratch (picker state, review diffs, channel payloads, analysis drafts) from past runs. Dry-run first; confirms before d |
46
47
  | [`multi-agent-help`](./core/multi-agent-help/) | `core` | Multi-agent pipeline usage guide - renders in EN or TR per prefs.global.outputLanguage (falls back to promptLanguage for backward compatib |
@@ -60,6 +61,7 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
60
61
  | [`multi-agent-resume`](./core/multi-agent-resume/) | `core` | Resume a stopped or failed task from the phase where it left off. Use when a task stopped or failed and should carry on from where it left o |
61
62
  | [`multi-agent-resume-local`](./core/multi-agent-resume-local/) | `core` | Continue already-done LOCAL work through the pipeline tail: Review → Build+Test → Commit/PR → Report (technical analysis + Jira test-scenari |
62
63
  | [`multi-agent-review`](./core/multi-agent-review/) | `core` | Run parallel review on a branch diff or a Pull Request: 2 models on Claude Code (Fable + Sonnet), 3 models on Copilot CLI (GPT + Opus + Sonn |
64
+ | [`multi-agent-review-analysis`](./core/multi-agent-review-analysis/) | `core` | Review a written analysis document instead of a diff: resolve it from a path, a Confluence page or a Jira issue, run the deterministic gates |
63
65
  | [`multi-agent-review-issue`](./core/multi-agent-review-issue/) | `core` | Assess whether a GitHub issue is ready for multi-agent development: fetch it, grade scope / acceptance criteria / repro / design / API / sta |
64
66
  | [`multi-agent-review-jira`](./core/multi-agent-review-jira/) | `core` | Assess whether a Jira issue is ready for multi-agent development: fetch it, grade scope / acceptance criteria / repro / design / API / stack |
65
67
  | [`multi-agent-routines`](./core/multi-agent-routines/) | `core` | List your saved /multi-agent routines (from /multi-agent:save) with what each one does, rendered in outputLanguage. Use when asked which sav |
@@ -3,7 +3,7 @@
3
3
  > Auto-generated by `pipeline/scripts/build-skills-index.mjs` - do not hand-edit.
4
4
  > Regenerate with `node pipeline/scripts/build-skills-index.mjs`.
5
5
 
6
- **208 skills** across 2 groups.
6
+ **210 skills** across 2 groups.
7
7
 
8
8
  | Group | Name | Platform | Description |
9
9
  |-------|------|----------|-------------|
@@ -94,7 +94,7 @@
94
94
  | external | `metrickit-diagnostics` | - | Collect and analyze on-device performance metrics and crash diagnostics using MetricKit. Use when setting up MXMetricManager, handling MXMet |
95
95
  | external | `monorepo-architect` | - | Expert in monorepo architecture, build systems, and dependency management at scale. Masters Nx, Turborepo, Bazel, and Lerna for efficient mu |
96
96
  | core | `multi-agent` | - | Task orchestrator: runs the full pipeline from a Jira ID or GitHub Issue URL - analysis → plan → TDD development → parallel review (Fable |
97
- | core | `multi-agent-analysis` | - | Standalone feature-spec analysis (v3 template). Platform-agnostic concept layer with repo-driven convention extraction and per-platform Pass |
97
+ | core | `multi-agent-analysis` | - | Standalone feature-spec analysis. Two profiles picked at intake: global (23-section development handoff, 8 of them in Lite mode) or corporat |
98
98
  | core | `multi-agent-analysis-resolve` | - | Resolve the Section 20 Risks and Open Questions of an analysis v3 document one row at a time: up to 3 source-labeled answer candidates per r |
99
99
  | core | `multi-agent-autopilot` | - | Launch any task in autopilot mode: skips every confirmation, runs end-to-end autonomously. Use when a task should run end to end with no con |
100
100
  | core | `multi-agent-build-optimize` | - | Wrapper that dispatches to xcode-build-orchestrator on iOS repos. Benchmarks the current Xcode build, runs compilation / project / SPM analy |
@@ -107,6 +107,7 @@
107
107
  | core | `multi-agent-dev-local` | - | Removed in v16.0.0. Its worktree-free twin is /multi-agent:local, which asks the same depth question; answer Short there. Invoke only to see |
108
108
  | core | `multi-agent-dev-local-autopilot` | - | Retired alongside its worktree twin in v16.0.0, with nothing standing in for it. Invoke only to be pointed at /multi-agent:local-autopilot o |
109
109
  | core | `multi-agent-diff-explain` | - | Map Phase 4 triage findings to branch diff lines. Read-only post-hoc command, used after review to answer 'which finding lines up with which |
110
+ | core | `multi-agent-feedback` | - | Send one message to the maintainer: a bug, an idea or a question. Only the text you type is sent - no logs, no repo names, no paths. Shows t |
110
111
  | core | `multi-agent-forget` | - | Remove a saved /multi-agent routine (created by /multi-agent:save): deletes its local-only command and its registry entry. Asks which one an |
111
112
  | core | `multi-agent-garbage-collect` | - | Sweep leftover /tmp scratch (picker state, review diffs, channel payloads, analysis drafts) from past runs. Dry-run first; confirms before d |
112
113
  | core | `multi-agent-help` | - | Multi-agent pipeline usage guide - renders in EN or TR per prefs.global.outputLanguage (falls back to promptLanguage for backward compatib |
@@ -126,6 +127,7 @@
126
127
  | core | `multi-agent-resume` | - | Resume a stopped or failed task from the phase where it left off. Use when a task stopped or failed and should carry on from where it left o |
127
128
  | core | `multi-agent-resume-local` | - | Continue already-done LOCAL work through the pipeline tail: Review → Build+Test → Commit/PR → Report (technical analysis + Jira test-scenari |
128
129
  | core | `multi-agent-review` | - | Run parallel review on a branch diff or a Pull Request: 2 models on Claude Code (Fable + Sonnet), 3 models on Copilot CLI (GPT + Opus + Sonn |
130
+ | core | `multi-agent-review-analysis` | - | Review a written analysis document instead of a diff: resolve it from a path, a Confluence page or a Jira issue, run the deterministic gates |
129
131
  | core | `multi-agent-review-issue` | - | Assess whether a GitHub issue is ready for multi-agent development: fetch it, grade scope / acceptance criteria / repro / design / API / sta |
130
132
  | core | `multi-agent-review-jira` | - | Assess whether a Jira issue is ready for multi-agent development: fetch it, grade scope / acceptance criteria / repro / design / API / stack |
131
133
  | core | `multi-agent-routines` | - | List your saved /multi-agent routines (from /multi-agent:save) with what each one does, rendered in outputLanguage. Use when asked which sav |