@mmerterden/multi-agent-pipeline 17.0.0 → 17.3.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 (88) hide show
  1. package/CHANGELOG.md +159 -0
  2. package/README.md +56 -4
  3. package/README.tr.md +57 -4
  4. package/docs/architecture.md +3 -3
  5. package/docs/ecosystem.md +5 -5
  6. package/docs/token-budget-history.md +22 -0
  7. package/install/_dev-only-files.mjs +1 -0
  8. package/install/codex.mjs +18 -1
  9. package/install/copilot.mjs +17 -1
  10. package/install/templates/multi-agent-autopilot.plist.template +79 -0
  11. package/package.json +1 -1
  12. package/pipeline/commands/multi-agent/autopilot-off/SKILL.md +64 -0
  13. package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +181 -0
  14. package/pipeline/commands/multi-agent/autopilot-status/SKILL.md +74 -0
  15. package/pipeline/commands/multi-agent/channels/SKILL.md +41 -12
  16. package/pipeline/commands/multi-agent/help/SKILL.md +41 -35
  17. package/pipeline/commands/multi-agent/manual-test/SKILL.md +1 -1
  18. package/pipeline/commands/multi-agent/sync/SKILL.md +10 -9
  19. package/pipeline/commands/multi-agent/update/SKILL.md +1 -1
  20. package/pipeline/lib/autopilot-activation.sh +117 -0
  21. package/pipeline/lib/autopilot-state.sh +184 -0
  22. package/pipeline/lib/issue-fetcher.sh +18 -1
  23. package/pipeline/lib/plan-todos.sh +18 -0
  24. package/pipeline/multi-agent-refs/_dev-context.md +10 -0
  25. package/pipeline/multi-agent-refs/analysis/redesign.md +8 -0
  26. package/pipeline/multi-agent-refs/analysis/review.md +9 -0
  27. package/pipeline/multi-agent-refs/android-guide.md +14 -0
  28. package/pipeline/multi-agent-refs/audit-guide.md +12 -0
  29. package/pipeline/multi-agent-refs/backend-guide.md +10 -0
  30. package/pipeline/multi-agent-refs/channels/confluence.md +11 -0
  31. package/pipeline/multi-agent-refs/channels/issue-comment.md +12 -0
  32. package/pipeline/multi-agent-refs/channels/jira.md +90 -20
  33. package/pipeline/multi-agent-refs/channels/pr-review-actions.md +13 -0
  34. package/pipeline/multi-agent-refs/channels/pr.md +76 -19
  35. package/pipeline/multi-agent-refs/component-dispatch.md +11 -0
  36. package/pipeline/multi-agent-refs/component-generation.md +11 -0
  37. package/pipeline/multi-agent-refs/conventions-defaults.md +15 -0
  38. package/pipeline/multi-agent-refs/cross-cli-contract.md +49 -5
  39. package/pipeline/multi-agent-refs/features/analysis-jira.md +11 -0
  40. package/pipeline/multi-agent-refs/features/design-conformance.md +10 -0
  41. package/pipeline/multi-agent-refs/features/doctor.md +10 -0
  42. package/pipeline/multi-agent-refs/features/external-context-injection.md +7 -0
  43. package/pipeline/multi-agent-refs/features/jira-context.md +9 -0
  44. package/pipeline/multi-agent-refs/features/model-fallback.md +10 -0
  45. package/pipeline/multi-agent-refs/features/skill-conformance.md +13 -0
  46. package/pipeline/multi-agent-refs/features/url-enrichment.md +9 -0
  47. package/pipeline/multi-agent-refs/features/visual-evidence.md +61 -7
  48. package/pipeline/multi-agent-refs/generate-issue.md +7 -0
  49. package/pipeline/multi-agent-refs/issue-jira-triad.md +9 -0
  50. package/pipeline/multi-agent-refs/knowledge.md +6 -0
  51. package/pipeline/multi-agent-refs/multi-repo-integration-build.md +13 -0
  52. package/pipeline/multi-agent-refs/phases/modes.md +7 -0
  53. package/pipeline/multi-agent-refs/phases/operations.md +9 -0
  54. package/pipeline/multi-agent-refs/phases/phase-0-init.md +2 -2
  55. package/pipeline/multi-agent-refs/phases/phase-2-planning.md +17 -15
  56. package/pipeline/multi-agent-refs/phases/phase-3-dev.md +1 -1
  57. package/pipeline/multi-agent-refs/phases/phase-6-commit.md +1 -1
  58. package/pipeline/multi-agent-refs/phases.md +11 -0
  59. package/pipeline/multi-agent-refs/picker-contract.md +12 -0
  60. package/pipeline/multi-agent-refs/platform-parity.md +10 -0
  61. package/pipeline/multi-agent-refs/progress-contract.md +10 -0
  62. package/pipeline/multi-agent-refs/readiness-review.md +7 -1
  63. package/pipeline/multi-agent-refs/rules.md +3 -11
  64. package/pipeline/multi-agent-refs/setup/firebase.md +9 -0
  65. package/pipeline/multi-agent-refs/swiftui-guide.md +17 -0
  66. package/pipeline/multi-agent-refs/tracker-contract.md +44 -0
  67. package/pipeline/multi-agent-refs/web-guide.md +10 -0
  68. package/pipeline/multi-agent-refs/wiki-capture.md +11 -0
  69. package/pipeline/schemas/autopilot-config.schema.json +149 -0
  70. package/pipeline/schemas/prefs.schema.json +4 -0
  71. package/pipeline/schemas/token-budget.json +10 -19
  72. package/pipeline/scripts/autopilot-arming.mjs +147 -0
  73. package/pipeline/scripts/autopilot-intake.mjs +387 -0
  74. package/pipeline/scripts/autopilot-menubar.swift +361 -0
  75. package/pipeline/scripts/autopilot-runner.mjs +354 -0
  76. package/pipeline/scripts/autopilot-status.sh +213 -0
  77. package/pipeline/scripts/capture-evidence.sh +79 -11
  78. package/pipeline/scripts/gen-ref-toc.mjs +279 -0
  79. package/pipeline/scripts/jira-search.sh +70 -0
  80. package/pipeline/scripts/phase-tracker.sh +134 -12
  81. package/pipeline/scripts/probe-evidence-capability.sh +27 -3
  82. package/pipeline/scripts/run-ui-tests.sh +113 -4
  83. package/pipeline/skills/.skill-manifest.json +16 -4
  84. package/pipeline/skills/shared/core/multi-agent-autopilot-off/SKILL.md +67 -0
  85. package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +146 -0
  86. package/pipeline/skills/shared/core/multi-agent-autopilot-status/SKILL.md +64 -0
  87. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +62 -11
  88. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +9 -8
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env bash
2
+ # autopilot-state.sh - where continuous mode keeps its state, and who may read it.
3
+ #
4
+ # Everything lives under ~/.claude/autopilot/ and NOT in
5
+ # multi-agent-preferences.json. That file has `additionalProperties: false` and a
6
+ # migration chain, so a block there would push keys into every local user's
7
+ # preferences forever - including everyone who never turns this on. Separation
8
+ # also gives the off state a definition that cannot be got wrong: the directory
9
+ # is absent.
10
+ #
11
+ # config.json the repo selection. Survives autopilot-off on purpose, so
12
+ # turning the mode back on does not re-ask which repos.
13
+ # queue.json the current ordered list plus whatever is in flight
14
+ # status.json what the menu bar renders. Written by the runner, read-only
15
+ # to everything else
16
+ # attempted.jsonl append-only: {source,id,taskId,outcome,prUrl,at}
17
+ # runner.pid pid + the in-flight sessionId + kern.boottime
18
+ # runner.log
19
+ # bin/menubar built on demand from scripts/autopilot-menubar.swift
20
+ #
21
+ # 0700 throughout. The queue names real tickets and real repos, and on a shared
22
+ # machine that is somebody's roadmap.
23
+ #
24
+ # Usage:
25
+ # . autopilot-state.sh
26
+ # ma_ap_root # prints the dir, creating it 0700 only when asked
27
+ # ma_ap_is_on # exit 0 when configured, 1 otherwise. NO side effects
28
+ # ma_ap_write <file> <- # atomic write from stdin, 0600
29
+ # ma_ap_asset <rel> # resolve a script/lib/template across the 3 host roots
30
+
31
+ set -uo pipefail
32
+
33
+ MA_AP_ROOT="${MA_AUTOPILOT_ROOT:-$HOME/.claude/autopilot}"
34
+
35
+ ma_ap_root() { printf '%s\n' "$MA_AP_ROOT"; }
36
+
37
+ # Where the scripts, libs and templates this mode needs actually live.
38
+ #
39
+ # This file ships into whichever host tree installed it, and a Copilot-only or
40
+ # Codex-only machine has no ~/.claude/scripts, no ~/.claude/lib and - the one
41
+ # that bites hardest - no ~/.claude/templates, which only install/claude.mjs
42
+ # creates. phase-tracker.sh already carries this resolver and its comment names
43
+ # what happens without it: "a hard-coded path silently disabled every live ping
44
+ # on those hosts". Autopilot reproduced the class; this is the same answer.
45
+ #
46
+ # The STATE root above is deliberately NOT resolved this way. ~/.claude/autopilot/
47
+ # is shared across hosts on purpose, exactly like logs/, knowledge/ and
48
+ # multi-agent-preferences.json, because two CLIs on one machine must see ONE
49
+ # queue. Resolving it per host would hand a Copilot session a second, invisible
50
+ # queue and the same item would be taken twice.
51
+ MA_AP_HOST_ROOT="${MA_AP_HOST_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." 2>/dev/null && pwd)}"
52
+
53
+ ma_ap_asset() { # $1 = path relative to a host root, e.g. scripts/autopilot-runner.mjs
54
+ local rel="$1" root
55
+ # The tree this file was sourced from wins: on a machine with all three
56
+ # installed, the caller's own tree is the one that is current.
57
+ if [ -n "$MA_AP_HOST_ROOT" ] && [ -e "$MA_AP_HOST_ROOT/$rel" ]; then
58
+ printf '%s\n' "$MA_AP_HOST_ROOT/$rel"
59
+ return 0
60
+ fi
61
+ for root in "$HOME/.claude" "$HOME/.copilot" "$HOME/.codex"; do
62
+ if [ -e "$root/$rel" ]; then
63
+ printf '%s\n' "$root/$rel"
64
+ return 0
65
+ fi
66
+ done
67
+ return 1
68
+ }
69
+
70
+ MA_AP_LABEL="${MA_AUTOPILOT_LABEL:-com.multi-agent.autopilot}"
71
+ MA_AP_PLIST="${MA_AUTOPILOT_PLIST:-$HOME/Library/LaunchAgents/$MA_AP_LABEL.plist}"
72
+
73
+ # Two different questions, and conflating them was a real bug in the first draft
74
+ # of this file: `autopilot-off` deliberately KEEPS config.json so turning the
75
+ # mode back on does not re-ask which repos, so a predicate reading the config
76
+ # would still answer "on" after you turned it off.
77
+ #
78
+ # configured = a repo selection exists
79
+ # on = launchd holds the job
80
+ #
81
+ # On is the launchd job because that is the thing that actually makes work
82
+ # happen; anything else is a claim about a file.
83
+ ma_ap_is_configured() { [ -f "$MA_AP_ROOT/config.json" ]; }
84
+
85
+ ma_ap_is_on() { [ -f "$MA_AP_PLIST" ] && launchctl list 2>/dev/null | grep -q "$MA_AP_LABEL"; }
86
+
87
+ # The failure mode a `resume` command would have papered over: configured and
88
+ # meant to be running, but launchd does not hold the job - an OS update dropped
89
+ # the plist, or it was booted out by hand. doctor reports this; there is no
90
+ # command to remember.
91
+ ma_ap_is_orphaned() { ma_ap_is_configured && ! ma_ap_is_on && [ -f "$MA_AP_PLIST" ]; }
92
+
93
+ # Deliberately no side effects in any of the three. A predicate that creates its
94
+ # own directory turns "is autopilot on?" into "autopilot is now half on", and
95
+ # every status command, hook and doctor check calls these.
96
+
97
+ ma_ap_ensure_root() {
98
+ [ -d "$MA_AP_ROOT" ] || mkdir -p "$MA_AP_ROOT" || return 1
99
+ chmod 700 "$MA_AP_ROOT" 2>/dev/null
100
+ [ -d "$MA_AP_ROOT/bin" ] || mkdir -p "$MA_AP_ROOT/bin" 2>/dev/null
101
+ chmod 700 "$MA_AP_ROOT/bin" 2>/dev/null
102
+ return 0
103
+ }
104
+
105
+ # Write via a temp file in the SAME directory then rename. The menu bar polls
106
+ # status.json every few seconds, and a reader that catches a half-written file
107
+ # shows an empty menu; rename is atomic on the same filesystem, so it never sees
108
+ # a partial one.
109
+ ma_ap_write() { # $1 = filename under the root; content on stdin
110
+ ma_ap_ensure_root || return 1
111
+ local dst="$MA_AP_ROOT/$1" tmp="$MA_AP_ROOT/.$1.$$"
112
+ cat > "$tmp" || {
113
+ rm -f "$tmp"
114
+ return 1
115
+ }
116
+ chmod 600 "$tmp" 2>/dev/null
117
+ mv -f "$tmp" "$dst"
118
+ }
119
+
120
+ ma_ap_append() { # $1 = filename, content on stdin - for the jsonl
121
+ ma_ap_ensure_root || return 1
122
+ local dst="$MA_AP_ROOT/$1"
123
+ cat >> "$dst" || return 1
124
+ chmod 600 "$dst" 2>/dev/null
125
+ }
126
+
127
+ ma_ap_read() { # $1 = filename; empty and exit 1 when absent
128
+ local src="$MA_AP_ROOT/$1"
129
+ [ -f "$src" ] || return 1
130
+ cat "$src"
131
+ }
132
+
133
+ # jq with a default, so a caller never has to distinguish "key absent" from
134
+ # "file absent" from "file unparseable" - all three mean "use the default".
135
+ ma_ap_cfg() { # $1 = jq path, $2 = default
136
+ local v
137
+ v=$(ma_ap_read config.json 2>/dev/null | jq -r "$1 // empty" 2>/dev/null)
138
+ [ -n "$v" ] && printf '%s\n' "$v" || printf '%s\n' "$2"
139
+ }
140
+
141
+ # AC or battery. The mode holds a sleep assertion only on AC: a queue that keeps
142
+ # a laptop awake on battery is a bug, and `StartInterval` does not wake a
143
+ # sleeping Mac anyway, so on battery the work resumes when you plug in.
144
+ ma_ap_power() {
145
+ case "$(pmset -g ps 2>/dev/null | head -1)" in
146
+ *"AC Power"*) printf 'ac\n' ;;
147
+ *) printf 'battery\n' ;;
148
+ esac
149
+ }
150
+
151
+ # Boot time, so a stale runner.pid cannot be mistaken for a live runner. After a
152
+ # restart pids start low and the recorded 4711 may belong to something unrelated;
153
+ # a pid recorded BEFORE the current boot is stale by definition, with no probing.
154
+ # The pattern is ANCHORED on purpose. `kern.boottime` prints
155
+ # `{ sec = 1788181644, usec = 618574 } Mon Aug 31 ...`, and `.*sec = ` is greedy:
156
+ # it walks past `sec` to `usec` and captures 618574, the microseconds. The
157
+ # original form here did exactly that, so this returned a six-digit number that
158
+ # looked plausible and never matched the same fact read anywhere else - which is
159
+ # how the runner's staleness check compared two different numbers and called a
160
+ # live runner dead.
161
+ ma_ap_boottime() {
162
+ sysctl -n kern.boottime 2>/dev/null | sed -n 's/^{ *sec = \([0-9]*\).*/\1/p'
163
+ }
164
+
165
+ # The machine's honest ceiling, so raising `slots` is a measured decision. RAM
166
+ # and cores bound the concurrent Claude sessions; disk bounds the worktrees
167
+ # (0.75 GB each, measured at 11 GB across 15). The hard cap is 4 because beyond
168
+ # that the bottleneck stops being this machine.
169
+ ma_ap_slot_ceiling() {
170
+ local ram_gb cores free_gb a b c
171
+ ram_gb=$(( $(sysctl -n hw.memsize 2>/dev/null || echo 0) / 1073741824 ))
172
+ cores=$(sysctl -n hw.ncpu 2>/dev/null || echo 2)
173
+ free_gb=$(df -g "$HOME" 2>/dev/null | awk 'NR==2{print $4}')
174
+ [ -n "$free_gb" ] || free_gb=0
175
+ a=$(((ram_gb - 4) * 10 / 25))
176
+ b=$((cores / 2))
177
+ c=$(((free_gb - 20) * 100 / 75))
178
+ local m=$a
179
+ [ "$b" -lt "$m" ] && m=$b
180
+ [ "$c" -lt "$m" ] && m=$c
181
+ [ "$m" -gt 4 ] && m=4
182
+ [ "$m" -lt 1 ] && m=1
183
+ printf '%s\n' "$m"
184
+ }
@@ -49,8 +49,23 @@
49
49
 
50
50
  set -euo pipefail
51
51
 
52
+ # Sourcing this file hands a caller the provider helpers - `jira_search`,
53
+ # `fetch_jira`, `fetch_github` and the `jira_curl` underneath them - without
54
+ # resolving anything. Executing it resolves one input, exactly as before.
55
+ #
56
+ # The guard exists because the alternative is worse than it looks: a second
57
+ # consumer of `jira_search` with no way to source would have to copy
58
+ # `jira_curl`, and that function is not a URL - it is the credential resolution,
59
+ # the host lookup and the rule that keeps a token off argv. Three copies of that
60
+ # is three places for a token to leak.
61
+ MA_IF_EXECUTED=0
62
+ [ "${BASH_SOURCE[0]}" = "$0" ] && MA_IF_EXECUTED=1
63
+
52
64
  INPUT="${1:-}"
53
- [ -z "$INPUT" ] && { echo '{"error":"input required"}'; exit 1; }
65
+ if [ "$MA_IF_EXECUTED" = 1 ] && [ -z "$INPUT" ]; then
66
+ echo '{"error":"input required"}'
67
+ exit 1
68
+ fi
54
69
 
55
70
  JIRA_TOKEN_KEY="${ACCOUNT_JIRA_TOKEN_KEY:-}"
56
71
  JIRA_HOST="${ACCOUNT_JIRA_HOST:-}"
@@ -361,6 +376,7 @@ PY
361
376
  }
362
377
 
363
378
  # --- Dispatch -----------------------------------------------------------------
379
+ if [ "$MA_IF_EXECUTED" = 1 ]; then
364
380
  case "$KIND" in
365
381
  jira-id)
366
382
  KEY="$INPUT"
@@ -592,3 +608,4 @@ sys.stdout.write("\x1f".join(parts))
592
608
  "description=" "branchHint=$branch"
593
609
  ;;
594
610
  esac
611
+ fi
@@ -94,6 +94,24 @@ do_set() {
94
94
  if [ -z "$plan_blob" ] || [ "$plan_blob" = "-" ]; then
95
95
  plan_blob=$(cat)
96
96
  fi
97
+ # A planning-output document is accepted directly and converted here.
98
+ #
99
+ # The conversion used to live as a jq blob inside phase-2-planning.md, which
100
+ # made the mapping from `tasks[]` to `todos[]` a thing two files defined - and
101
+ # the phase doc was the copy nothing tested. Accepting both shapes costs four
102
+ # lines and removes the second definition.
103
+ if jq -e '.tasks and (.todos | not)' <<<"$plan_blob" >/dev/null 2>&1; then
104
+ plan_blob=$(jq '{
105
+ title: (.summary // .title // "plan"),
106
+ todos: [ .tasks[] | {
107
+ id: .id,
108
+ task: (.title // .subject // ""),
109
+ status: "pending",
110
+ deps: (.dependsOn // .blockedBy // [])
111
+ } ]
112
+ }' <<<"$plan_blob")
113
+ fi
114
+
97
115
  # Validate against schema (best-effort - jq syntax check, then required-field probe).
98
116
  if ! jq -e '.title and (.todos | type == "array")' <<<"$plan_blob" >/dev/null 2>&1; then
99
117
  echo "plan-todos: input must be an object with .title (string) and .todos (array)" >&2
@@ -4,6 +4,16 @@ description: "Internal - dev context (extra repos) picker for multi-agent."
4
4
 
5
5
  # _dev-context - Extra Dev Repo Selection
6
6
 
7
+ <!-- toc -->
8
+ - [Steps](#steps)
9
+ - [Web repos - `webRepos`](#web-repos---webrepos)
10
+ - [Pref override - `editableRelatedRepos`](#pref-override---editablerelatedrepos)
11
+ - [Output](#output)
12
+ - [Rule](#rule)
13
+ - [Autopilot Behavior](#autopilot-behavior)
14
+ - [Pipeline contract for read-only siblings](#pipeline-contract-for-read-only-siblings)
15
+ <!-- /toc -->
16
+
7
17
  Selects extra repos the pipeline may touch beyond the primary repo(s) - typically submodules (e.g. SDKs vendored inside an app repo) or sibling libraries. Each candidate is enriched with a `canPush` flag so the picker can pre-select repos that the active account is allowed to edit, and surface read-only ones as advisory context the pipeline will not modify.
8
18
 
9
19
  > **Language**: see `picker-contract.md` + `rules.md` Language Application matrix.
@@ -1,5 +1,13 @@
1
1
  # Redesign mode - the contract
2
2
 
3
+ <!-- toc -->
4
+ - [What the mode is for](#what-the-mode-is-for)
5
+ - [Why it is an option and not a mode](#why-it-is-an-option-and-not-a-mode)
6
+ - [The three artefacts](#the-three-artefacts)
7
+ - [The eight checks](#the-eight-checks)
8
+ - [The cache, and the silent failure to avoid](#the-cache-and-the-silent-failure-to-avoid)
9
+ <!-- /toc -->
10
+
3
11
  Loaded only when `state.analysisSpec.options.redesign` is true, the same way
4
12
  `analysis/review.md` is loaded only by a reviewer subagent. A run that is not a
5
13
  redesign never pays for this file.
@@ -1,5 +1,14 @@
1
1
  # Analysis document review (`/multi-agent:review-analysis`)
2
2
 
3
+ <!-- toc -->
4
+ - [Phase 0 - Resolve the document](#phase-0---resolve-the-document)
5
+ - [Phase 1 - Deterministic gates first](#phase-1---deterministic-gates-first)
6
+ - [Phase 1.5 - What did the run skip?](#phase-15---what-did-the-run-skip)
7
+ - [Phase 2 - Rubric](#phase-2---rubric)
8
+ - [Phase 3 - Triage and verdict](#phase-3---triage-and-verdict)
9
+ - [Phase 4 - Output](#phase-4---output)
10
+ <!-- /toc -->
11
+
3
12
  > Reviews a written analysis the way `/multi-agent:review` reviews a diff. Loaded on demand. Read-only: no branch, no worktree, no commit, and the source document is never edited in place.
4
13
 
5
14
  `/multi-agent:review` answers "is this code right". This answers "could an implementer build the thing from this document, and does the document keep the promises its own contract makes". The two are different questions with the same failure mode: a reviewer who has opinions instead of rules produces findings nobody can act on. So this flow cites `Locked <n>` the way a code review cites a rule ID.
@@ -1,5 +1,19 @@
1
1
  ## Android/Kotlin Component Generation Guide
2
2
 
3
+ <!-- toc -->
4
+ - [Component Architecture: State / Screen / Content](#component-architecture-state-screen-content)
5
+ - [Simple vs Complex Decision](#simple-vs-complex-decision)
6
+ - [State Pattern](#state-pattern)
7
+ - [Token Discipline](#token-discipline)
8
+ - [Stability for Performance](#stability-for-performance)
9
+ - [Accessibility](#accessibility)
10
+ - [Preview Best Practices](#preview-best-practices)
11
+ - [Testing](#testing)
12
+ - [Build Verification](#build-verification)
13
+ - [Component Quality Checklist](#component-quality-checklist)
14
+ - [Compliance Rules (maps to multi-agent-toolkit MCP audit tools)](#compliance-rules-maps-to-multi-agent-toolkit-mcp-audit-tools)
15
+ <!-- /toc -->
16
+
3
17
  > **MUST: Figma MCP-first (BLOCKING).** If the task references any Figma frame (URL, node ID, or "from the design"), the Dev phase MUST call `mcp__claude_ai_Figma__get_design_context` for every frame BEFORE writing a single Composable line. Use the `CodeConnectSnippet` component name verbatim - no sound-alike substitutions. Authentication failure is not a skip path. Full rule, trigger conditions, and gate failure modes: `$HOME/.claude/rules/figma-pipeline.md` "MUST: Figma MCP-first (BLOCKING)". Phase wiring: `$HOME/.claude/multi-agent-refs/phases/phase-3-dev.md` "MUST: Figma MCP-first (BLOCKING pre-step)".
4
18
 
5
19
  When the task involves creating an Android UI component (Jetpack Compose), follow this architecture.
@@ -1,5 +1,17 @@
1
1
  ## Audit & Quality Tools Guide
2
2
 
3
+ <!-- toc -->
4
+ - [Trigger Model](#trigger-model)
5
+ - [iOS Accessibility Audit](#ios-accessibility-audit)
6
+ - [Android Accessibility Audit](#android-accessibility-audit)
7
+ - [iOS Biometric Test](#ios-biometric-test)
8
+ - [Android Launch Time](#android-launch-time)
9
+ - [iOS Archive Audit (App Store Compliance)](#ios-archive-audit-app-store-compliance)
10
+ - [Android APK Audit (Play Store Compliance)](#android-apk-audit-play-store-compliance)
11
+ - [Integration with Pipeline Phases](#integration-with-pipeline-phases)
12
+ - [Graceful Degradation](#graceful-degradation)
13
+ <!-- /toc -->
14
+
3
15
  Standalone audit commands - runs directly via Bash, **no MCP server dependency**. These are the same checks that multi-agent-toolkit-mcp provides as MCP tools, but embedded here as pipeline skills.
4
16
 
5
17
  ### Trigger Model
@@ -1,5 +1,15 @@
1
1
  ## Backend API Development Guide
2
2
 
3
+ <!-- toc -->
4
+ - [API Architecture](#api-architecture)
5
+ - [Python/FastAPI Pattern](#pythonfastapi-pattern)
6
+ - [Node.js/Express Pattern](#nodejsexpress-pattern)
7
+ - [Error Handling](#error-handling)
8
+ - [Security Checklist](#security-checklist)
9
+ - [Testing](#testing)
10
+ - [Quality Checklist](#quality-checklist)
11
+ <!-- /toc -->
12
+
3
13
  When the task involves backend development (Python/FastAPI, Node.js/Express, Go), follow these patterns.
4
14
 
5
15
  ### API Architecture
@@ -1,5 +1,16 @@
1
1
  # Channel adapter - Confluence page
2
2
 
3
+ <!-- toc -->
4
+ - [Required body structure](#required-body-structure)
5
+ - [Token check](#token-check)
6
+ - [Parent page resolution](#parent-page-resolution)
7
+ - [Page title](#page-title)
8
+ - [Body conversion (markdown → storage format)](#body-conversion-markdown-storage-format)
9
+ - [POST contract](#post-contract)
10
+ - [Recents persistence](#recents-persistence)
11
+ - [Hard rules (must not regress)](#hard-rules-must-not-regress)
12
+ <!-- /toc -->
13
+
3
14
  > Detailed contract for the `confluence` channel of `/multi-agent:channels`. Split out of `channels.md` in v8.0.0; the parent doc keeps a one-line summary and a link here.
4
15
 
5
16
  The Confluence adapter creates or updates a Confluence page under a chosen parent. Like the Jira adapter, it runs **once** per invocation - the page lives at the primary repo's component slug in multi-repo mode.
@@ -1,5 +1,17 @@
1
1
  # Channel adapter - GitHub Issue comment
2
2
 
3
+ <!-- toc -->
4
+ - [When this fires](#when-this-fires)
5
+ - [The hard rule](#the-hard-rule)
6
+ - [Required body structure](#required-body-structure)
7
+ - [Hard prohibitions](#hard-prohibitions)
8
+ - [Compaction policy](#compaction-policy)
9
+ - [API contract](#api-contract)
10
+ - [Pairing with the Progress flag updater](#pairing-with-the-progress-flag-updater)
11
+ - [Drift detection](#drift-detection)
12
+ - [Hard rules (must not regress)](#hard-rules-must-not-regress)
13
+ <!-- /toc -->
14
+
3
15
  > Canonical template for the `issue` channel of `/multi-agent:channels`. Every successful run that touched a tracked GitHub issue MUST post one comment using this template - no exceptions, no "state-only" shortcuts.
4
16
 
5
17
  ## When this fires
@@ -1,5 +1,15 @@
1
1
  # Channel adapter - Jira comment
2
2
 
3
+ <!-- toc -->
4
+ - [Required body structure](#required-body-structure)
5
+ - [Wiki markup conversion](#wiki-markup-conversion)
6
+ - [Cross-link injection](#cross-link-injection)
7
+ - [Token resolution](#token-resolution)
8
+ - [POST contract](#post-contract)
9
+ - [Wiki → Jira auto-link triad](#wiki-jira-auto-link-triad)
10
+ - [Hard rules (must not regress)](#hard-rules-must-not-regress)
11
+ <!-- /toc -->
12
+
3
13
  > Detailed contract for the `jira` channel of `/multi-agent:channels`. Split out of `channels.md` in v8.0.0; the parent doc keeps a one-line summary and a link here.
4
14
 
5
15
  The Jira adapter posts a comment on the linked issue. The Jira ticket is shared by all repos in a multi-repo task, so the adapter runs **once** per channels invocation regardless of how many PR targets are dispatched in parallel.
@@ -10,35 +20,89 @@ Every Jira comment posted by this adapter follows the same section order. Sectio
10
20
 
11
21
  | # | Section key | Heading (`tr`) | Heading (`en`) | Required? |
12
22
  |---|---|---|---|---|
13
- | 1 | `summary` | `## Yapılan Çalışma Özeti` | `## Work Summary` | always |
23
+ | 1 | `summary` | `## Geliştirme Özeti` | `## Development Summary` | always |
14
24
  | 2 | `test_scenarios` | `## Test Senaryoları` | `## Test Scenarios` | always (use " - " placeholder line if truly N/A) |
15
- | 3 | `context_refs` | `## Bağlantılar` | `## References` | when any link exists |
25
+ | 3 | `impact` | `## Etki Analizi` | `## Impact Analysis` | always |
26
+ | 4 | `context_refs` | `## Bağlantılar` | `## References` | when any link exists |
16
27
 
17
28
  > Heading levels in the source markdown are `##`. The wiki-markup converter (next section) rewrites them to `h2.` for Jira rendering.
18
29
 
30
+ **Jira is not the PR, and this is the contract that keeps getting that wrong.**
31
+ The PR is read by a reviewer holding the diff. The Jira comment is read by the
32
+ person who filed the ticket and by the tester who has to verify it, and neither
33
+ of them has the diff open. So a Jira body carries no identifiers, no file paths,
34
+ no stack frames, no diff hunks and no framework names. Counts in prose are fine
35
+ and are often the most useful sentence in the comment ("two files, eight lines
36
+ removed, no additions"); a class name is not. A sentence that needs a symbol to
37
+ make its point is describing the change at the wrong level for this reader -
38
+ restate the behaviour, not the mechanism. The technical account has a home, and
39
+ it is the PR body (`channels/pr.md`).
40
+
19
41
  ### Section content rules
20
42
 
21
43
  **`summary`** - 2-5 sentences in `outputLanguage`. What changed, why, and the user-visible impact. No "we", no marketing tone. Past tense (the work is done at the time the comment goes up).
22
44
 
23
- **Visual evidence inside these two sections.** When `state.visualEvidence` carries artefacts, they render INSIDE `summary` and `test_scenarios` - never as a fourth section, which the fixed section order forbids. Upload first (`jira-attach.sh <issue> <file>...`), then reference by the returned filename:
45
+ **Visual evidence inside these sections.** When `state.visualEvidence` carries artefacts, they render INSIDE `summary` and `test_scenarios` - never as a section of their own, which the fixed section order forbids. Phase 6 Step 2.9 has already uploaded them and written the returned name to `visualEvidence.*[].jiraFilename`: reference that name, and call `jira-attach.sh <issue> <file>...` only for an artefact whose `jiraFilename` is absent. Uploading unconditionally here attaches every file a second time whenever the comment is re-rendered - which is exactly what a post-hoc `/multi-agent:channels` run does.
24
46
 
25
47
  - `summary`, after its sentences: one line naming the pair in `outputLanguage` (`Düzeltme öncesi / Düzeltme sonrası`), then the thumbnails on the next line - `!<file>-before.png|thumbnail! !<file>-after.png|thumbnail!`.
26
48
  - `test_scenarios`, under the scenario the recording demonstrates: `!<file>-flow.mp4!` plus one line stating the tier used.
27
49
 
28
50
  A `gaps[]` entry prints its reason on the line where the artefact would have been (`Düzeltme öncesi: ticket'ta görsel yok`). Never an empty thumbnail, never a silent omission. Contract: `$HOME/.claude/multi-agent-refs/features/visual-evidence.md`.
29
51
 
30
- **`test_scenarios`** - Given/When/Then numbered list. One scenario per acceptance criterion. The heading and scenario text are rendered in `outputLanguage` at write-time; the template itself (this file) shows the English skeleton:
52
+ **`test_scenarios`** - one titled scenario per acceptance criterion, each a
53
+ numbered list of steps ending in the expected result. Given/When/Then was the
54
+ shape here for several releases and it reads as translated English to the person
55
+ who actually runs these; a tester wants a list they can follow with the app open.
56
+ The heading and the text render in `outputLanguage`; this file shows the English
57
+ skeleton:
31
58
 
32
59
  ```markdown
33
60
  ## Test Scenarios
34
61
 
35
- 1. **Given** a signed-in user
36
- **When** they navigate to the Profile screen
37
- **Then** their saved preferences appear in the configured order
38
- 2. **Given** ...
62
+ **1. <what this scenario exercises>**
63
+ 1. <step the tester performs, in the app's own words>
64
+ 2. <step>
65
+ 3. **Expected:** <what they should see>
66
+
67
+ **2. <regression scenario>**
68
+ 1. <step>
69
+ 2. **Expected:** <what should still behave as before>
70
+ ```
71
+
72
+ Order is load-bearing. The scenarios that reproduce the reported behaviour come
73
+ first, then the regression scenarios for whatever the change could have disturbed -
74
+ anything sharing the component that was touched. A comment that lists only the fix
75
+ leaves the tester to guess the blast radius, and that guess is the one thing they
76
+ cannot make from the ticket.
77
+
78
+ Screens, fields and menu items are named the way the app shows them, in
79
+ `outputLanguage`. Function names and file paths do not appear at all, per the rule
80
+ above. When there are no executable scenarios (pure doc change, dependency bump),
81
+ write a single line: `- No manual test required - <short reason>`.
82
+
83
+ **`impact`** - four fixed numbered parts, each answered, never left as a
84
+ placeholder. This is what a test lead reads before deciding how wide to test and
85
+ what a release manager reads before deciding whether it ships this week, so it is
86
+ written in the same plain register as the rest of the comment:
87
+
88
+ ```markdown
89
+ ## Impact Analysis
90
+
91
+ **1 - The problem**
92
+ <what was wrong and since when, in the reader's words>
93
+
94
+ **2 - What was changed**
95
+ <what the change does, and why the behaviour around it is unchanged>
96
+
97
+ **3 - Affected areas**
98
+ <the screens and flows that must be tested, including anything sharing the changed component>
99
+
100
+ **4 - Effect on other systems**
101
+ <none, or which service, contract or channel is affected>
39
102
  ```
40
103
 
41
- Code identifiers (function names, file paths) stay verbatim - they are not translated. When there are no executable scenarios (pure doc change, dependency bump), write a single line: `- No manual test required - <short reason>`.
104
+ Part 4 is answered "none" far more often than it is answered at all, and "none" is
105
+ a real answer worth writing: it is what lets the reader stop looking.
42
106
 
43
107
  **`context_refs`** - flat bullet list of external links the work depends on or produced. PR URL is **not** repeated here; it lives on the first line (see *Cross-link injection*). Common entries (only when present in the task):
44
108
 
@@ -58,7 +122,7 @@ The links are pulled from `agent-state.json.contextLinks[]` (see Phase 0 link ex
58
122
 
59
123
  ```
60
124
  1. Read agent-state.json (taskId, contextLinks, prUrls, language).
61
- 2. Build section bodies in markdown - summary first, test_scenarios next, context_refs last.
125
+ 2. Build section bodies in markdown, in table order: summary, test_scenarios, impact, context_refs.
62
126
  3. Run the assembled body through the `humanizer` skill (see Hard rules below).
63
127
  4. Apply Cross-link injection (PR URL on line 1).
64
128
  5. Run Wiki markup conversion.
@@ -116,7 +180,7 @@ Do not hand-apply this table. It was hand-applied for several releases and a smi
116
180
 
117
181
  Lines outside these patterns pass through verbatim. Multi-paragraph blocks are joined with one blank line.
118
182
 
119
- Every row above is load-bearing, including the ones that look cosmetic. The table must cover each construct the section templates in this file actually emit - `##` for the three required headings, `**Given**` / `**When**` / `**Then**` in the test-scenario skeleton, and `1.`-numbered scenarios. A missing row does not degrade gracefully: the "pass through verbatim" fallback POSTs `## Test Senaryoları` and `**Given**` as literal text, so the comment renders with visible `##` and stray asterisks. Single `*bold*` in Markdown means *italic* in Jira wiki - never map `**bold**` to `*bold*` by dropping one asterisk mechanically without checking the source was bold, not italic.
183
+ Every row above is load-bearing, including the ones that look cosmetic. The table must cover each construct the section templates in this file actually emit - `##` for the four required headings, `**1. title**` on a scenario and `**Expected:**` inside it, the `**1 - ...**` part labels in the impact section, and `1.`-numbered steps. A missing row does not degrade gracefully: the "pass through verbatim" fallback POSTs `## Test Senaryoları` and `**Expected:**` as literal text, so the comment renders with visible `##` and stray asterisks. Single `*bold*` in Markdown means *italic* in Jira wiki - never map `**bold**` to `*bold*` by dropping one asterisk mechanically without checking the source was bold, not italic.
120
184
 
121
185
  There is no markdown→Jira-wiki converter program in the pipeline (`lib/` ships `md2confluence-v3.py` for Confluence only, and `scripts/jira-wiki-escape.mjs` covers the emoticon step alone). This conversion table is applied by the model, by hand, which is exactly why it has to be complete.
122
186
 
@@ -149,19 +213,25 @@ Authorization: Bearer $JIRA_TOKEN
149
213
  Content-Type: application/json
150
214
  ```
151
215
 
152
- Body assembled with `jq --rawfile` + `curl --data-binary @file`, from the escaped file that *Emoticon escaping* produced:
216
+ **Post through `jira-publish.sh`. Never hand-roll the `curl`.**
153
217
 
154
218
  ```bash
155
- node "$HOME/.claude/scripts/jira-wiki-escape.mjs" --check /tmp/channels-$TASK_ID-jira-escaped.txt \
156
- || { echo "unescaped Jira emoticon in the body - do not POST" >&2; exit 1; }
157
- jq -n --rawfile body /tmp/channels-$TASK_ID-jira-escaped.txt '{body: $body}' \
158
- > /tmp/channels-$TASK_ID-jira-payload.json
159
- curl -s -X POST -H "Authorization: Bearer $JIRA_TOKEN" \
160
- -H "Content-Type: application/json" \
161
- --data-binary @/tmp/channels-$TASK_ID-jira-payload.json \
162
- "$JIRA_BASE/rest/api/2/issue/$JIRA_ID/comment"
219
+ bash "$HOME/.claude/lib/jira-publish.sh" --issue "$JIRA_ID" \
220
+ --body-file /tmp/channels-$TASK_ID-jira.txt --target comment
163
221
  ```
164
222
 
223
+ That script escapes every body unconditionally (`lib/jira-publish.sh:109`) and
224
+ passes the token through a `-K` config rather than argv, so neither the escape
225
+ nor the token handling depends on an agent remembering a step.
226
+
227
+ This block used to be a hand-written `jq` + `curl` pair with the escape as a
228
+ separate `--check` line above it, and a shipped comment rendered the Swift
229
+ selector `hash(into:)` as `hash(into` plus a smiley - twice. The escaper was
230
+ present, correct, and simply not run on that path: `:)` reached Jira intact and
231
+ Jira's wiki renderer turned it into an emoticon, which is what section
232
+ *Emoticon escaping* below is about. A defence that has to be invoked by hand is
233
+ a defence that is eventually not invoked.
234
+
165
235
  The dispatch summary line includes the comment URL with `?focusedCommentId=...` so the user can paste it into Slack/Teams.
166
236
 
167
237
  ### Writing the issue description (not a comment)
@@ -1,5 +1,18 @@
1
1
  # Channel adapter - Pull Request review actions
2
2
 
3
+ <!-- toc -->
4
+ - [When this fires](#when-this-fires)
5
+ - [The decision rule](#the-decision-rule)
6
+ - [Inline comment template (per finding)](#inline-comment-template-per-finding)
7
+ - [Decision endpoints](#decision-endpoints)
8
+ - [Order of operations](#order-of-operations)
9
+ - [Hard prohibitions](#hard-prohibitions)
10
+ - [Idempotency](#idempotency)
11
+ - [Provider auto-detection](#provider-auto-detection)
12
+ - [Pairing with the chat summary](#pairing-with-the-chat-summary)
13
+ - [Drift detection](#drift-detection)
14
+ <!-- /toc -->
15
+
3
16
  > Canonical contract for the PR-actions branch of `/multi-agent:review`. The PR-side artifacts are **per-finding inline comments + an explicit approve / needs-work decision** - never a single monolithic description comment.
4
17
 
5
18
  ## When this fires