@mmerterden/multi-agent-pipeline 17.6.0 → 19.0.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 (272) hide show
  1. package/CHANGELOG.md +310 -0
  2. package/README.md +76 -18
  3. package/README.tr.md +55 -16
  4. package/docs/adr/0002-instruction-driven-flag.md +1 -0
  5. package/docs/adr/0005-lazy-phase-docs.md +11 -1
  6. package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +1 -0
  7. package/docs/adr/0010-own-code-graph.md +1 -0
  8. package/docs/adr/0011-dormant-ci.md +25 -1
  9. package/docs/adr/0014-six-phase-consolidation.md +134 -0
  10. package/docs/adr/README.md +2 -1
  11. package/docs/architecture.md +37 -38
  12. package/docs/best-practices.md +1 -1
  13. package/docs/ecosystem.md +37 -26
  14. package/docs/engineering.md +1 -1
  15. package/docs/facts.json +45 -0
  16. package/docs/features.md +54 -53
  17. package/docs/performance.md +5 -5
  18. package/docs/recovery-guide.md +9 -9
  19. package/docs/server-readiness.md +188 -0
  20. package/docs/token-budget-history.md +3 -1
  21. package/index.js +18 -3
  22. package/install/_codex-agents.mjs +1 -1
  23. package/install/_common.mjs +42 -17
  24. package/install/_dev-only-files.mjs +8 -0
  25. package/install/_unattended-profile.mjs +113 -0
  26. package/install/index.mjs +48 -0
  27. package/install/templates/claude-hooks.json +1 -1
  28. package/install/templates/codex-instructions.md +1 -1
  29. package/install/templates/copilot-instructions.md +28 -28
  30. package/manifest.json +1065 -0
  31. package/package.json +6 -3
  32. package/pipeline/agents/dev-critic.md +3 -3
  33. package/pipeline/commands/figma-to-swiftui.md +1 -1
  34. package/pipeline/commands/multi-agent/SKILL.md +8 -8
  35. package/pipeline/commands/multi-agent/analysis/SKILL.md +9 -9
  36. package/pipeline/commands/multi-agent/autopilot/SKILL.md +7 -7
  37. package/pipeline/commands/multi-agent/channels/SKILL.md +15 -15
  38. package/pipeline/commands/multi-agent/diff-explain/SKILL.md +6 -6
  39. package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
  40. package/pipeline/commands/multi-agent/graph/SKILL.md +1 -1
  41. package/pipeline/commands/multi-agent/help/SKILL.md +62 -62
  42. package/pipeline/commands/multi-agent/language/SKILL.md +2 -2
  43. package/pipeline/commands/multi-agent/local/SKILL.md +11 -11
  44. package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +13 -13
  45. package/pipeline/commands/multi-agent/log/SKILL.md +2 -2
  46. package/pipeline/commands/multi-agent/manual-test/SKILL.md +9 -9
  47. package/pipeline/commands/multi-agent/model/SKILL.md +69 -0
  48. package/pipeline/commands/multi-agent/refactor/SKILL.md +3 -3
  49. package/pipeline/commands/multi-agent/resume/SKILL.md +4 -4
  50. package/pipeline/commands/multi-agent/resume-local/SKILL.md +19 -17
  51. package/pipeline/commands/multi-agent/review/SKILL.md +1 -1
  52. package/pipeline/commands/multi-agent/route-off/SKILL.md +36 -0
  53. package/pipeline/commands/multi-agent/route-on/SKILL.md +74 -0
  54. package/pipeline/commands/multi-agent/route-status/SKILL.md +56 -0
  55. package/pipeline/commands/multi-agent/setup/SKILL.md +2 -2
  56. package/pipeline/commands/multi-agent/status/SKILL.md +54 -23
  57. package/pipeline/commands/multi-agent/steer/SKILL.md +2 -2
  58. package/pipeline/commands/multi-agent/sync/SKILL.md +12 -13
  59. package/pipeline/commands/multi-agent/test/SKILL.md +1 -1
  60. package/pipeline/lib/_jira-auth.sh +8 -0
  61. package/pipeline/lib/analysis-jira-write.sh +32 -0
  62. package/pipeline/lib/ask-choice.sh +13 -2
  63. package/pipeline/lib/autopilot-state.sh +8 -0
  64. package/pipeline/lib/credential-inventory.sh +1 -1
  65. package/pipeline/lib/fatal.mjs +129 -0
  66. package/pipeline/lib/fetch-fortify.sh +1 -1
  67. package/pipeline/lib/figma-mcp-refresh.sh +18 -0
  68. package/pipeline/lib/figma-screenshot.sh +18 -0
  69. package/pipeline/lib/invoked-directly.mjs +43 -0
  70. package/pipeline/lib/jira-publish.sh +42 -0
  71. package/pipeline/lib/md2confluence-v3.py +47 -0
  72. package/pipeline/lib/model-rung.sh +142 -0
  73. package/pipeline/lib/outbound-gate.mjs +175 -0
  74. package/pipeline/lib/phase-schema.mjs +88 -0
  75. package/pipeline/lib/plan-todos.sh +32 -11
  76. package/pipeline/lib/post-pr-review.sh +77 -8
  77. package/pipeline/lib/repo-hygiene.sh +8 -3
  78. package/pipeline/lib/require-jq.sh +40 -0
  79. package/pipeline/lib/route-state.sh +161 -0
  80. package/pipeline/lib/run-paths.sh +335 -0
  81. package/pipeline/multi-agent-refs/_account-picker.md +1 -1
  82. package/pipeline/multi-agent-refs/_dev-context.md +1 -1
  83. package/pipeline/multi-agent-refs/_input-parser.md +1 -1
  84. package/pipeline/multi-agent-refs/analysis/evidence.md +0 -9
  85. package/pipeline/multi-agent-refs/analysis/intake.md +1 -1
  86. package/pipeline/multi-agent-refs/analysis/locked.md +21 -22
  87. package/pipeline/multi-agent-refs/analysis/render.md +1 -1
  88. package/pipeline/multi-agent-refs/analysis/synthesis.md +12 -6
  89. package/pipeline/multi-agent-refs/android-guide.md +1 -1
  90. package/pipeline/multi-agent-refs/audit-guide.md +13 -13
  91. package/pipeline/multi-agent-refs/channels/issue-comment.md +2 -2
  92. package/pipeline/multi-agent-refs/channels/jira.md +3 -3
  93. package/pipeline/multi-agent-refs/channels/pr.md +4 -4
  94. package/pipeline/multi-agent-refs/channels/wiki.md +1 -1
  95. package/pipeline/multi-agent-refs/component-dispatch.md +3 -3
  96. package/pipeline/multi-agent-refs/cross-cli-contract.md +31 -6
  97. package/pipeline/multi-agent-refs/features/autopilot-circuit-breaker.md +74 -4
  98. package/pipeline/multi-agent-refs/features/code-graph.md +5 -5
  99. package/pipeline/multi-agent-refs/features/cost-analysis.md +93 -0
  100. package/pipeline/multi-agent-refs/features/design-conformance.md +1 -1
  101. package/pipeline/multi-agent-refs/features/dev-critic.md +3 -3
  102. package/pipeline/multi-agent-refs/features/doctor.md +47 -2
  103. package/pipeline/multi-agent-refs/features/external-context-injection.md +3 -3
  104. package/pipeline/multi-agent-refs/features/maturity-followup.md +3 -3
  105. package/pipeline/multi-agent-refs/features/model-fallback.md +5 -5
  106. package/pipeline/multi-agent-refs/features/plan-todos.md +1 -1
  107. package/pipeline/multi-agent-refs/features/repo-map.md +1 -1
  108. package/pipeline/multi-agent-refs/features/review-delta.md +3 -3
  109. package/pipeline/multi-agent-refs/features/review-multi-repo.md +1 -1
  110. package/pipeline/multi-agent-refs/features/scope-check.md +4 -4
  111. package/pipeline/multi-agent-refs/features/skill-conformance.md +2 -2
  112. package/pipeline/multi-agent-refs/features/stack-skill-routing.md +1 -1
  113. package/pipeline/multi-agent-refs/features/verify-by-test.md +4 -4
  114. package/pipeline/multi-agent-refs/features/verify.md +83 -0
  115. package/pipeline/multi-agent-refs/features/visual-evidence.md +19 -19
  116. package/pipeline/multi-agent-refs/features/worktree-finalize.md +6 -6
  117. package/pipeline/multi-agent-refs/issue-jira-triad.md +10 -10
  118. package/pipeline/multi-agent-refs/knowledge.md +11 -11
  119. package/pipeline/multi-agent-refs/multi-repo-integration-build.md +13 -13
  120. package/pipeline/multi-agent-refs/payload-contracts.md +8 -8
  121. package/pipeline/multi-agent-refs/phases/log-format.md +10 -10
  122. package/pipeline/multi-agent-refs/phases/modes.md +30 -30
  123. package/pipeline/multi-agent-refs/phases/operations.md +21 -10
  124. package/pipeline/multi-agent-refs/phases/phase-0-init.md +25 -25
  125. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +599 -0
  126. package/pipeline/multi-agent-refs/phases/{phase-3-dev.md → phase-2-dev.md} +129 -49
  127. package/pipeline/multi-agent-refs/phases/{phase-4-review.md → phase-3-review.md} +225 -107
  128. package/pipeline/multi-agent-refs/phases/{phase-6-commit.md → phase-4-commit.md} +23 -23
  129. package/pipeline/multi-agent-refs/phases/{phase-7-report.md → phase-5-report.md} +29 -29
  130. package/pipeline/multi-agent-refs/phases.md +44 -48
  131. package/pipeline/multi-agent-refs/picker-contract.md +1 -1
  132. package/pipeline/multi-agent-refs/progress-contract.md +6 -6
  133. package/pipeline/multi-agent-refs/readiness-review.md +1 -1
  134. package/pipeline/multi-agent-refs/rules.md +7 -7
  135. package/pipeline/multi-agent-refs/swiftui-guide.md +2 -2
  136. package/pipeline/multi-agent-refs/tracker-contract.md +31 -32
  137. package/pipeline/multi-agent-refs/unattended-contract.md +129 -0
  138. package/pipeline/multi-agent-refs/wiki-capture.md +14 -14
  139. package/pipeline/preferences-template.json +9 -1
  140. package/pipeline/rules/outside-the-pipeline.md +1 -1
  141. package/pipeline/schemas/agent-state.schema.json +50 -50
  142. package/pipeline/schemas/analysis-output.schema.json +2 -2
  143. package/pipeline/schemas/autopilot-config.schema.json +1 -1
  144. package/pipeline/schemas/code-graph.schema.json +1 -1
  145. package/pipeline/schemas/criteria-manifest.schema.json +1 -1
  146. package/pipeline/schemas/dev-critic-output.schema.json +1 -1
  147. package/pipeline/schemas/diff-risk.schema.json +1 -1
  148. package/pipeline/schemas/migrations/prefs-2.4.0-to-2.5.0.mjs +2 -2
  149. package/pipeline/schemas/migrations/prefs-2.6.0-to-2.7.0.mjs +31 -0
  150. package/pipeline/schemas/migrations/state-2.1.0-to-2.2.0.mjs +129 -0
  151. package/pipeline/schemas/phases.json +105 -0
  152. package/pipeline/schemas/plan-todos.schema.json +5 -5
  153. package/pipeline/schemas/planning-output.schema.json +1 -1
  154. package/pipeline/schemas/prefs.schema.json +100 -56
  155. package/pipeline/schemas/reviewer-output.schema.json +3 -3
  156. package/pipeline/schemas/route-config.schema.json +74 -0
  157. package/pipeline/schemas/scope-check.schema.json +1 -1
  158. package/pipeline/schemas/test-gap.schema.json +1 -1
  159. package/pipeline/schemas/token-budget.json +12 -18
  160. package/pipeline/schemas/triage-output.schema.json +6 -6
  161. package/pipeline/scripts/README.md +3 -3
  162. package/pipeline/scripts/_code-graph.mjs +2 -2
  163. package/pipeline/scripts/_run-paths.mjs +372 -0
  164. package/pipeline/scripts/_smoke-root.sh +1 -1
  165. package/pipeline/scripts/aggregate-metrics.mjs +65 -65
  166. package/pipeline/scripts/autopilot-arming.mjs +2 -1
  167. package/pipeline/scripts/autopilot-intake.mjs +2 -1
  168. package/pipeline/scripts/autopilot-runner.mjs +206 -2
  169. package/pipeline/scripts/build-references.mjs +2 -1
  170. package/pipeline/scripts/build-stack-plugins.mjs +10 -2
  171. package/pipeline/scripts/capture-evidence.sh +7 -2
  172. package/pipeline/scripts/capture-flush.sh +8 -8
  173. package/pipeline/scripts/capture-resume.sh +3 -3
  174. package/pipeline/scripts/classify-plan-safety.mjs +3 -2
  175. package/pipeline/scripts/cost-analyze.mjs +600 -0
  176. package/pipeline/scripts/cost-budget-check.mjs +4 -12
  177. package/pipeline/scripts/council-view.mjs +2 -1
  178. package/pipeline/scripts/crush-json.mjs +2 -1
  179. package/pipeline/scripts/diff-explain.mjs +7 -10
  180. package/pipeline/scripts/diff-risk-score.mjs +2 -1
  181. package/pipeline/scripts/doctor.mjs +140 -6
  182. package/pipeline/scripts/evidence-gate.mjs +9 -3
  183. package/pipeline/scripts/feedback-send.mjs +12 -2
  184. package/pipeline/scripts/gc-abandoned.sh +32 -16
  185. package/pipeline/scripts/gc-tmp.sh +1 -1
  186. package/pipeline/scripts/gc-worktrees.sh +12 -5
  187. package/pipeline/scripts/gen-facts.mjs +175 -0
  188. package/pipeline/scripts/gen-mode-dispatch.mjs +32 -37
  189. package/pipeline/scripts/gen-ref-toc.mjs +1 -1
  190. package/pipeline/scripts/github-ssh-setup.sh +64 -7
  191. package/pipeline/scripts/graph-mermaid.mjs +4 -2
  192. package/pipeline/scripts/graph-report.mjs +1 -1
  193. package/pipeline/scripts/jira-attach.sh +1 -1
  194. package/pipeline/scripts/keychain-save.sh +101 -30
  195. package/pipeline/scripts/learn-from-transcripts.mjs +3 -2
  196. package/pipeline/scripts/learning-curve.mjs +36 -31
  197. package/pipeline/scripts/log-metric.sh +17 -4
  198. package/pipeline/scripts/make-manifest.mjs +199 -0
  199. package/pipeline/scripts/memory-save.sh +1 -1
  200. package/pipeline/scripts/migrate-prefs.mjs +24 -6
  201. package/pipeline/scripts/migrate-state.mjs +94 -4
  202. package/pipeline/scripts/phase-banner.sh +26 -22
  203. package/pipeline/scripts/phase-tracker.sh +48 -10
  204. package/pipeline/scripts/plan-coverage-gate.mjs +8 -4
  205. package/pipeline/scripts/pre-commit-check.sh +7 -0
  206. package/pipeline/scripts/pre-push-check.sh +7 -0
  207. package/pipeline/scripts/purge.sh +23 -6
  208. package/pipeline/scripts/render-agent-log-cost.sh +10 -3
  209. package/pipeline/scripts/render-cost-summary.sh +9 -2
  210. package/pipeline/scripts/render-work-summary.sh +14 -7
  211. package/pipeline/scripts/review-file-filter.mjs +5 -3
  212. package/pipeline/scripts/review-scope.mjs +2 -1
  213. package/pipeline/scripts/routine-registry.mjs +2 -1
  214. package/pipeline/scripts/run-aggregator.mjs +26 -20
  215. package/pipeline/scripts/run-metrics.mjs +4 -2
  216. package/pipeline/scripts/runs-index.mjs +353 -0
  217. package/pipeline/scripts/scorecard-snapshot.mjs +178 -0
  218. package/pipeline/scripts/search-logs.sh +18 -0
  219. package/pipeline/scripts/smoke-cross-cli-behavior.sh +6 -6
  220. package/pipeline/scripts/smoke-schema-validation.sh +26 -7
  221. package/pipeline/scripts/test-gap-scan.mjs +2 -1
  222. package/pipeline/scripts/test-integrity-gate.mjs +2 -1
  223. package/pipeline/scripts/token-budget-report.mjs +13 -2
  224. package/pipeline/scripts/triage-memory.mjs +2 -2
  225. package/pipeline/scripts/update-issue-progress.sh +56 -7
  226. package/pipeline/scripts/usage-report.mjs +12 -1
  227. package/pipeline/scripts/validate-analysis-doc.mjs +75 -18
  228. package/pipeline/scripts/validate-code-graph.mjs +6 -3
  229. package/pipeline/scripts/validate-complaint-doc.mjs +2 -1
  230. package/pipeline/scripts/validate-diff-risk.mjs +6 -3
  231. package/pipeline/scripts/validate-planning.mjs +1 -1
  232. package/pipeline/scripts/validate-reviewer.mjs +1 -1
  233. package/pipeline/scripts/validate-state.mjs +45 -5
  234. package/pipeline/scripts/validate-test-gap.mjs +6 -3
  235. package/pipeline/scripts/validate-triage.mjs +6 -4
  236. package/pipeline/scripts/verify-citations.mjs +4 -2
  237. package/pipeline/scripts/verify.mjs +327 -0
  238. package/pipeline/scripts/worktree-finalize.sh +18 -9
  239. package/pipeline/scripts/write-state.mjs +154 -15
  240. package/pipeline/skills/.skill-manifest.json +37 -21
  241. package/pipeline/skills/.skills-index.json +104 -5
  242. package/pipeline/skills/shared/README.md +15 -6
  243. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +2 -2
  244. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +2 -2
  245. package/pipeline/skills/shared/core/multi-agent/SKILL.md +69 -71
  246. package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +3 -3
  247. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +14 -14
  248. package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +5 -5
  249. package/pipeline/skills/shared/core/multi-agent-graph/SKILL.md +1 -1
  250. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +25 -23
  251. package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +2 -2
  252. package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +2 -2
  253. package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +8 -8
  254. package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +6 -6
  255. package/pipeline/skills/shared/core/multi-agent-model/SKILL.md +71 -0
  256. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +3 -3
  257. package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +1 -1
  258. package/pipeline/skills/shared/core/multi-agent-resume-local/SKILL.md +7 -7
  259. package/pipeline/skills/shared/core/multi-agent-route-off/SKILL.md +39 -0
  260. package/pipeline/skills/shared/core/multi-agent-route-on/SKILL.md +76 -0
  261. package/pipeline/skills/shared/core/multi-agent-route-status/SKILL.md +59 -0
  262. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +1 -1
  263. package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +35 -11
  264. package/pipeline/skills/shared/core/multi-agent-steer/SKILL.md +2 -2
  265. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +6 -5
  266. package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/package_app.sh +4 -1
  267. package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/setup_dev_signing.sh +4 -1
  268. package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/sign-and-notarize.sh +2 -1
  269. package/pipeline/skills/skills-index.md +13 -4
  270. package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +0 -263
  271. package/pipeline/multi-agent-refs/phases/phase-2-planning.md +0 -344
  272. package/pipeline/multi-agent-refs/phases/phase-5-test.md +0 -182
@@ -0,0 +1,335 @@
1
+ #!/bin/bash
2
+ # run-paths.sh - the single resolver for pipeline run-state paths (shell side).
3
+ #
4
+ # Shell twin of pipeline/scripts/_run-paths.mjs. The two must agree; the
5
+ # contract is asserted by pipeline/scripts/smoke-run-path-canonical.sh, which
6
+ # runs both over the same fixture tree and diffs their answers.
7
+ #
8
+ # A run's files live under the log root in ONE of two layouts:
9
+ #
10
+ # nested <root>/<project>/<task_id>/ documented canonical
11
+ # flat <root>/<task_id>/ what phase-tracker.sh writes
12
+ #
13
+ # Both are populated on real machines, so every reader accepts both. This file
14
+ # does NOT change where anything is written: measured on a real install, 90 of
15
+ # 103 runs are flat and every tracker-state.json is. A task id present in both
16
+ # layouts is ONE run; the most recently written directory wins. Relocation is
17
+ # opt-in and explicit: `migrate-state.mjs --relocate`.
18
+ #
19
+ # No `set -e` on purpose: this is a sourced library and changing the caller's
20
+ # shell options is not this file's business.
21
+ #
22
+ # Usage:
23
+ # . "$(dirname "$0")/../lib/run-paths.sh"
24
+ # root=$(ma_logs_root)
25
+ # dir=$(ma_resolve_run_dir "{JIRA_KEY}-123") || echo "unknown run"
26
+ # f=$(ma_resolve_run_file "{JIRA_KEY}-123" tracker-state.json)
27
+ # ma_list_runs # task_id<TAB>project<TAB>dir<TAB>layout, one per run
28
+ #
29
+ # Bash 3.2 compatible: no associative arrays, no `mapfile`, no `grep -P`.
30
+
31
+ # Marker files and reserved names are spelled out at each test rather than held
32
+ # in a space-separated variable iterated with `for x in $VAR`. bash word-splits
33
+ # an unquoted variable; zsh does not, so there the loop would run ONCE with the
34
+ # whole string as a single word, every test would fail, and the library would
35
+ # answer "no runs" instead of erroring. This file is sourced, so the caller's
36
+ # shell decides - and answering zero is the failure mode that looks like data.
37
+
38
+ ma_logs_root() {
39
+ printf '%s\n' "${LOGS_ROOT:-$HOME/.claude/logs/multi-agent}"
40
+ }
41
+
42
+ # ma_is_run_dir <dir> -> 0 when the directory holds at least one run marker
43
+ # Phase 4 removes the worktree and salvages the run's files into `artifacts/`
44
+ # inside the same run directory, so a shipped run keeps its state one level
45
+ # deeper. A reader that only looks at the top level reports it as stateless.
46
+ MA_ARTIFACTS_SUBDIR=artifacts
47
+
48
+ ma_is_run_dir() {
49
+ local d="$1"
50
+ [ -f "$d/agent-state.json" ] && return 0
51
+ [ -f "$d/tracker-state.json" ] && return 0
52
+ [ -f "$d/agent-log.md" ] && return 0
53
+ [ -f "$d/$MA_ARTIFACTS_SUBDIR/agent-state.json" ] && return 0
54
+ [ -f "$d/$MA_ARTIFACTS_SUBDIR/tracker-state.json" ] && return 0
55
+ [ -f "$d/$MA_ARTIFACTS_SUBDIR/agent-log.md" ] && return 0
56
+ return 1
57
+ }
58
+
59
+ ma_is_reserved() {
60
+ case "$1" in
61
+ review-watch | jira-backups | shadow-git | _analysis-jira | prompts) return 0 ;;
62
+ esac
63
+ return 1
64
+ }
65
+
66
+ # Newest mtime across a run directory's markers, as epoch seconds. GNU-first
67
+ # `stat -c` ordering is required: `stat -f` is a valid GNU flag
68
+ # (--file-system) that SUCCEEDS, so BSD-first would silently win on Linux.
69
+ ma_run_mtime() {
70
+ local d="$1" m t newest=0 b
71
+ for b in "$d" "$d/$MA_ARTIFACTS_SUBDIR"; do
72
+ for m in agent-state.json tracker-state.json agent-log.md; do
73
+ [ -f "$b/$m" ] || continue
74
+ # -L follows symlinks. Some nested run directories are symlink bridges to
75
+ # the flat copy of the same run (3 such pairs on the install this was
76
+ # measured on); without -L the shell reads the LINK's mtime while the
77
+ # .mjs twin's fs.statSync reads the TARGET's, and the two picked
78
+ # different winners for one record. Both must read the same clock.
79
+ t=$(stat -L -c %Y "$b/$m" 2>/dev/null || stat -L -f %m "$b/$m" 2>/dev/null || echo 0)
80
+ [ -n "$t" ] || t=0
81
+ [ "$t" -gt "$newest" ] && newest="$t"
82
+ done
83
+ done
84
+ printf '%s\n' "$newest"
85
+ }
86
+
87
+ # ma_canonical_run_dir <task_id> [project] -> where a NEW run should be written
88
+ ma_canonical_run_dir() {
89
+ local task_id="$1" project="${2:-}" root
90
+ root=$(ma_logs_root)
91
+ if [ -n "$project" ]; then
92
+ printf '%s\n' "$root/$project/$task_id"
93
+ else
94
+ printf '%s\n' "$root/$task_id"
95
+ fi
96
+ }
97
+
98
+ # ma_run_dir_candidates <task_id> [project] -> candidate dirs, most specific
99
+ # first. Existence is not checked here.
100
+ ma_run_dir_candidates() {
101
+ local task_id="$1" project="${2:-}" root d n
102
+ root=$(ma_logs_root)
103
+ [ -n "$project" ] && printf '%s\n' "$root/$project/$task_id"
104
+ printf '%s\n' "$root/$task_id"
105
+ [ -n "$project" ] && return 0
106
+ # No project given: the run may still be nested under one.
107
+ #
108
+ # `find` rather than a `*/` glob on purpose: an unmatched glob is a literal
109
+ # string in bash and a hard error in zsh, and this file is sourced, so the
110
+ # caller's shell decides. find behaves identically in both.
111
+ while IFS= read -r d; do
112
+ [ -n "$d" ] || continue
113
+ n=$(basename "$d")
114
+ ma_is_reserved "$n" && continue
115
+ [ "$n" = "$task_id" ] && continue
116
+ ma_is_run_dir "$root/$n/$task_id" && printf '%s\n' "$root/$n/$task_id"
117
+ done <<EOF
118
+ $(find "$root" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
119
+ EOF
120
+ return 0
121
+ }
122
+
123
+ # ma_realpath <path> -> the path with every symlink resolved, or the input when
124
+ # it cannot be resolved. BSD realpath first, python3 as the fallback (both are
125
+ # already hard requirements of this tree).
126
+ ma_realpath() {
127
+ realpath "$1" 2>/dev/null ||
128
+ python3 -c 'import os,sys; print(os.path.realpath(sys.argv[1]))' "$1" 2>/dev/null ||
129
+ printf '%s\n' "$1"
130
+ }
131
+
132
+ # ma_is_bridge_dir <dir> -> 0 when any marker in it is a symlink elsewhere.
133
+ # Some nested run directories are symlink bridges into the flat copy of the
134
+ # same run. They are one record seen twice, not two copies.
135
+ ma_is_bridge_dir() {
136
+ local d="$1" m
137
+ for m in agent-state.json tracker-state.json agent-log.md; do
138
+ [ -L "$d/$m" ] && return 0
139
+ done
140
+ return 1
141
+ }
142
+
143
+ # ma_same_record <dir_a> <dir_b> -> 0 when both are views of ONE record.
144
+ ma_same_record() {
145
+ local a="$1" b="$2" m ra rb
146
+ for m in agent-state.json tracker-state.json agent-log.md; do
147
+ [ -e "$a/$m" ] && [ -e "$b/$m" ] || continue
148
+ ra=$(ma_realpath "$a/$m")
149
+ rb=$(ma_realpath "$b/$m")
150
+ [ "$ra" = "$rb" ] && return 0
151
+ done
152
+ return 1
153
+ }
154
+
155
+ # ma_rank_fields <dir> -> "<mtime>\t<has_state>\t<depth>", the three ranking
156
+ # columns used to pick between candidate directories for one task id.
157
+ #
158
+ # ma_sort_key packs the same three into one space-separated key for callers
159
+ # that sort a single column; both must match compareCandidates in
160
+ # _run-paths.mjs exactly.
161
+ #
162
+ # mtime alone is not an order: two directories written in the same second tie,
163
+ # and each implementation then fell back to its own traversal order - which is
164
+ # how the two came to disagree about a run present in both layouts. The tail of
165
+ # the key defines the answer: richer record first (agent-state.json is what
166
+ # every reader wants), then the documented nested layout, then the path.
167
+ ma_rank_fields() {
168
+ local d="$1" t has_state depth real
169
+ t=$(ma_run_mtime "$d")
170
+ has_state=0
171
+ [ -f "$d/agent-state.json" ] && has_state=1
172
+ depth=$(printf '%s' "$d" | awk -F/ '{print NF}')
173
+ real=1
174
+ ma_is_bridge_dir "$d" && real=0
175
+ printf '%s\t%s\t%s\t%s\n' "$real" "$t" "$has_state" "$depth"
176
+ }
177
+
178
+ ma_sort_key() {
179
+ local d="$1" t has_state depth real
180
+ t=$(ma_run_mtime "$d")
181
+ has_state=0
182
+ [ -f "$d/agent-state.json" ] && has_state=1
183
+ depth=$(printf '%s' "$d" | awk -F/ '{print NF}')
184
+ real=1
185
+ ma_is_bridge_dir "$d" && real=0
186
+ printf '%d %012d %d %04d %s\n' "$real" "$t" "$has_state" "$depth" "$d"
187
+ }
188
+
189
+ # ma_resolve_run_dir <task_id> [project] -> the directory, or rc 1 when unknown.
190
+ ma_resolve_run_dir() {
191
+ local task_id="$1" project="${2:-}" c best
192
+ best=$(
193
+ while IFS= read -r c; do
194
+ [ -n "$c" ] || continue
195
+ ma_is_run_dir "$c" || continue
196
+ ma_sort_key "$c"
197
+ done <<EOF
198
+ $(ma_run_dir_candidates "$task_id" "$project")
199
+ EOF
200
+ )
201
+ [ -n "$best" ] || return 1
202
+ # Descending on mtime, then has-state, then depth; ascending on path.
203
+ printf '%s\n' "$best" | sort -k1,1nr -k2,2nr -k3,3nr -k4,4nr -k5,5 | head -1 | cut -d' ' -f5-
204
+ }
205
+
206
+ # ma_task_id_variants <id> -> the spellings one task id has been written under,
207
+ # in preference order. `#316` arrives from a GitHub issue reference, `316` from
208
+ # the bare-number input class, `task-316` from an older directory convention.
209
+ # Callers used to inline this list; dropping a spelling silently stops old runs
210
+ # from resolving.
211
+ ma_task_id_variants() {
212
+ local raw="$1" bare
213
+ bare="${raw#\#}"
214
+ printf '%s\n' "$raw"
215
+ [ "$bare" != "$raw" ] && printf '%s\n' "$bare"
216
+ printf '%s\n' "task-$bare"
217
+ }
218
+
219
+ # ma_resolve_run_dir_any <task_id> [project] -> dir for any spelling, or rc 1
220
+ ma_resolve_run_dir_any() {
221
+ local task_id="$1" project="${2:-}" v dir
222
+ while IFS= read -r v; do
223
+ [ -n "$v" ] || continue
224
+ dir=$(ma_resolve_run_dir "$v" "$project") && {
225
+ printf '%s\n' "$dir"
226
+ return 0
227
+ }
228
+ done <<EOF
229
+ $(ma_task_id_variants "$task_id")
230
+ EOF
231
+ return 1
232
+ }
233
+
234
+ # ma_resolve_run_file <task_id> <filename> [project] -> path, or rc 1.
235
+ # Every spelling of the id is tried.
236
+ ma_resolve_run_file() {
237
+ local task_id="$1" filename="$2" project="${3:-}" v dir
238
+ while IFS= read -r v; do
239
+ [ -n "$v" ] || continue
240
+ dir=$(ma_resolve_run_dir "$v" "$project") || continue
241
+ if [ -f "$dir/$filename" ]; then
242
+ printf '%s\n' "$dir/$filename"
243
+ return 0
244
+ fi
245
+ # The salvaged copy Phase 4 leaves behind.
246
+ if [ -f "$dir/$MA_ARTIFACTS_SUBDIR/$filename" ]; then
247
+ printf '%s\n' "$dir/$MA_ARTIFACTS_SUBDIR/$filename"
248
+ return 0
249
+ fi
250
+ done <<EOF
251
+ $(ma_task_id_variants "$task_id")
252
+ EOF
253
+ return 1
254
+ }
255
+
256
+ # ma_list_runs -> one TAB-separated row per run, deduplicated by task id:
257
+ # task_id<TAB>project<TAB>dir<TAB>layout
258
+ # `project` is "-" when the layout does not name one.
259
+ ma_list_runs() {
260
+ local root d n kd kn rows row id
261
+ root=$(ma_logs_root)
262
+ [ -d "$root" ] || return 0
263
+ rows=""
264
+ while IFS= read -r d; do
265
+ [ -n "$d" ] || continue
266
+ n=$(basename "$d")
267
+ ma_is_reserved "$n" && continue
268
+ if ma_is_run_dir "$root/$n"; then
269
+ rows="$rows$n - $root/$n flat $(ma_rank_fields "$root/$n")
270
+ "
271
+ continue
272
+ fi
273
+ while IFS= read -r kd; do
274
+ [ -n "$kd" ] || continue
275
+ kn=$(basename "$kd")
276
+ ma_is_run_dir "$root/$n/$kn" || continue
277
+ rows="$rows$kn $n $root/$n/$kn nested $(ma_rank_fields "$root/$n/$kn")
278
+ "
279
+ done <<EOF
280
+ $(find "$root/$n" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
281
+ EOF
282
+ done <<EOF
283
+ $(find "$root" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
284
+ EOF
285
+ # Dedup by task id using the same total order as resolveRunDir and as
286
+ # compareCandidates in the .mjs twin: mtime desc, has-state desc, depth desc,
287
+ # path ASC. The sub-keys are separate columns on purpose - a single reverse
288
+ # sort over a composite key also reverses the path, which is the one
289
+ # component that has to ascend.
290
+ printf '%s' "$rows" | grep -v '^$' |
291
+ sort -t' ' -k1,1 -k5,5nr -k6,6nr -k7,7nr -k8,8nr -k3,3 | awk -F' ' '
292
+ !seen[$1]++ { print $1 "\t" $2 "\t" $3 "\t" $4 }
293
+ ' | sort -t' ' -k1,1
294
+ }
295
+
296
+ # ma_duplicate_run_ids -> task ids that exist as two SEPARATE records in the two
297
+ # layouts, one per line. A symlink bridge is excluded: it is one record seen
298
+ # twice, and reporting it overstates the drift.
299
+ ma_duplicate_run_ids() {
300
+ local root d n kd kn pairs id dirs a b
301
+ root=$(ma_logs_root)
302
+ [ -d "$root" ] || return 0
303
+ pairs=$(
304
+ while IFS= read -r d; do
305
+ [ -n "$d" ] || continue
306
+ n=$(basename "$d")
307
+ ma_is_reserved "$n" && continue
308
+ if ma_is_run_dir "$root/$n"; then
309
+ printf '%s\t%s\n' "$n" "$root/$n"
310
+ continue
311
+ fi
312
+ while IFS= read -r kd; do
313
+ [ -n "$kd" ] || continue
314
+ kn=$(basename "$kd")
315
+ ma_is_run_dir "$root/$n/$kn" && printf '%s\t%s\n' "$kn" "$root/$n/$kn"
316
+ done <<EOF
317
+ $(find "$root/$n" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
318
+ EOF
319
+ done <<EOF
320
+ $(find "$root" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
321
+ EOF
322
+ )
323
+ # Ids seen more than once, minus the ones whose two directories resolve to the
324
+ # same underlying files (a symlink bridge is one record, not drift).
325
+ while IFS= read -r id; do
326
+ [ -n "$id" ] || continue
327
+ dirs=$(printf '%s\n' "$pairs" | awk -F'\t' -v k="$id" '$1==k {print $2}')
328
+ a=$(printf '%s\n' "$dirs" | sed -n 1p)
329
+ b=$(printf '%s\n' "$dirs" | sed -n 2p)
330
+ [ -n "$b" ] || continue
331
+ ma_same_record "$a" "$b" || printf '%s\n' "$id"
332
+ done <<EOF
333
+ $(printf '%s\n' "$pairs" | awk -F'\t' '{print $1}' | sort | uniq -d)
334
+ EOF
335
+ }
@@ -12,7 +12,7 @@ First step of every `multi-agent` flow **that touches a remote provider**.
12
12
  > question, no token lookup. Detection: `freetext` flow first fetches local
13
13
  > repos (`repo-cache.sh local "$HOME"`); if the picker resolves to local-only
14
14
  > repos, account-picker is bypassed and the state file's `accountId` is left
15
- > `null` with `tokens={}`. Phases 6/7 read these as "local-only" signals.
15
+ > `null` with `tokens={}`. Phases 4/5 read these as "local-only" signals.
16
16
 
17
17
  > **Language**: see `picker-contract.md` + `rules.md` Language Application matrix.
18
18
 
@@ -70,7 +70,7 @@ Selects extra repos the pipeline may touch beyond the primary repo(s) - typica
70
70
  is not written here is a candidate the check can never see.
71
71
 
72
72
  Resolve each entry's `stack` from its local checkout, with the marker table
73
- in `phases/phase-1-analysis.md` Step 2 (`.xcodeproj` / `Package.swift` →
73
+ in `phases/phase-1-plan.md` Step 2 (`.xcodeproj` / `Package.swift` →
74
74
  `ios`, `build.gradle(.kts)` → `android`, and so on). No checkout, or no
75
75
  marker matched → `stack: "unknown"` and `root: null`. Never infer a stack
76
76
  from the repo NAME: `my-app-android` is a naming convention, not a marker,
@@ -35,7 +35,7 @@ The top-level `multi-agent` command classifies user arguments per the rules belo
35
35
 
36
36
  ¹ Account picker is **skipped** when the resolved primary repo is local-only
37
37
  (`provider="local"`). No token lookup, no provider auth - the pipeline
38
- proceeds with `accountId=null` and Phases 6/7 honor the local-only mode.
38
+ proceeds with `accountId=null` and Phases 4/5 honor the local-only mode.
39
39
 
40
40
  ² Free-text flow merges `github`+`bitbucket`+`local` repo caches into a
41
41
  single picker list; `(local)` rows come from `repo-cache.sh local "$HOME"`.
@@ -225,12 +225,3 @@ The tier boundary is not advisory. A signal row that reaches Section 4 (business
225
225
 
226
226
  Per source: reachable and answered, reachable and empty, or unreachable. All three are recorded; only the third produces a `fetchErrors[]` entry, and none of them halts.
227
227
 
228
- **Lite mode auto-detection (Locked 25, v9.1.0 scoring)**: at the end of Phase 1c, evaluate three signals and score each:
229
-
230
- | Signal | True condition | Score |
231
- |---|---|---|
232
- | Confluence spec body lines | < 100 | 1 |
233
- | Figma frames count | <= 1 | 1 |
234
- | Repo evidence direct-match count | >= 8 | 1 |
235
-
236
- `liteModeAuto = (totalScore >= 2)`. Two of three signals true is enough; the v8.12.0..v9.0.x AND-threshold (all three) forced too many small features into Full mode when one signal was marginal (e.g. a tiny spec with 2 Figma frames). User overrides via `--full` or `--lite` always win over scoring.
@@ -1,6 +1,6 @@
1
1
  # Intake (analysis Phase 0)
2
2
 
3
- > The picker chain that fills `state.analysisSpec.*` before any fetch runs. Loaded by `/multi-agent:analysis`. Pipeline Phase 1 does NOT load this file: in a pipeline run the account, project, repos and task already came from the orchestrator's own Phase 0, and only the source and coverage batches below are asked (see `phase-1-analysis.md` Step 4).
3
+ > The picker chain that fills `state.analysisSpec.*` before any fetch runs. Loaded by `/multi-agent:analysis`. Pipeline Phase 1 does NOT load this file: in a pipeline run the account, project, repos and task already came from the orchestrator's own Phase 0, and only the source and coverage batches below are asked (see `phase-1-plan.md` Step 4).
4
4
 
5
5
  ### Phase 0 - Intake
6
6
 
@@ -1,25 +1,25 @@
1
- # Locked decisions (37)
1
+ # Locked decisions (36)
2
2
 
3
- > The 37 Locked decisions of the analysis flow. Loaded by `/multi-agent:analysis`, by `/multi-agent:analysis-resolve` (which inherits them) and by pipeline Phase 1 when it runs the analysis engine. Numbering is canonical: cite as `Locked <n> (<short label>)`.
3
+ > The 36 Locked decisions of the analysis flow. Loaded by `/multi-agent:analysis`, by `/multi-agent:analysis-resolve` (which inherits them) and by pipeline Phase 1 when it runs the analysis engine. Numbering is canonical: cite as `Locked <n> (<short label>)`.
4
4
 
5
5
  ### Index by category (v9.1.0+)
6
6
 
7
- Browse-friendly grouping of the 37 Locked decisions. Numbering stays canonical (matches the list below); the index is read-only navigation.
7
+ Browse-friendly grouping of the 36 Locked decisions. Numbering stays canonical (matches the list below); the index is read-only navigation.
8
8
 
9
9
  | Category | Decisions | Concern |
10
10
  |---|---|---|
11
- | **A. Governance** | 1, 5, 6, 7, 10, 26, 27, 32, 36 | Run-level process rules: one feature per run, default output, auto-commit ban, punctuation policy, output picker timing, Pass B preview, evidence digest cache, analysis profile, document reviewed before publish |
12
- | **B. Citation and Evidence** | 3, 4, 8, 11, 24, 30, 34 | Every fact in the doc traces back to a source: citation discipline, forward-looking spec, standards binding, repo-evidence reuse-first, Pass B footnote mandatory, analysis self-contained (pipeline-wide), references built from the evidence record |
13
- | **C. Output Format and Structure** | 2, 9, 13, 14, 16, 17, 20, 21, 25, 33, 35, 37 | How the document is laid out: section omission rule, per-platform output split, Gherkin user stories, Goals + Non-Goals paired, Files-to-Add tag, API response variants exhaustive, localization mode (ownership-aware), References at the bottom, Lite mode, corporate backbone always renders, stack-optional render, redesign records v1 before planning v2 |
11
+ | **A. Governance** | 1, 5, 6, 7, 10, 25, 26, 31, 35 | Run-level process rules: one feature per run, default output, auto-commit ban, punctuation policy, output picker timing, Pass B preview, evidence digest cache, analysis profile, document reviewed before publish |
12
+ | **B. Citation and Evidence** | 3, 4, 8, 11, 24, 29, 33 | Every fact in the doc traces back to a source: citation discipline, forward-looking spec, standards binding, repo-evidence reuse-first, Pass B footnote mandatory, analysis self-contained (pipeline-wide), references built from the evidence record |
13
+ | **C. Output Format and Structure** | 2, 9, 13, 14, 16, 17, 20, 21, 32, 34, 36 | How the document is laid out: section omission rule, per-platform output split, Gherkin user stories, Goals + Non-Goals paired, Files-to-Add tag, API response variants exhaustive, localization mode (ownership-aware), References at the bottom, corporate backbone always renders, stack-optional render, redesign records v1 before planning v2 |
14
14
  | **D. Design Source and Pipeline Architecture** | 12, 22, 23 | Where design comes from and how the pipeline renders: Figma 3-tier access (BLOCKING), platform-agnostic template + Pass B render, convention extraction (Phase 1c) |
15
- | **E. UI, Variant, and Test Coverage** | 15, 18, 19, 28, 29, 31 | UI artefact rules: SVG default for new assets, screenshots embedded, all Figma variants drilled, SwiftUI Preview block (iOS), variant usage explicit, business-rule to acceptance-criterion to test traceability |
15
+ | **E. UI, Variant, and Test Coverage** | 15, 18, 19, 27, 28, 30 | UI artefact rules: SVG default for new assets, screenshots embedded, all Figma variants drilled, SwiftUI Preview block (iOS), variant usage explicit, business-rule to acceptance-criterion to test traceability |
16
16
 
17
17
  When citing a Locked decision in code or docs, prefer `Locked <n> (<short label>)` form so the category is inferable (e.g. `Locked 30 (analysis self-contained, category B)`).
18
18
 
19
19
  ### Full list
20
20
 
21
21
  1. **One feature per run.** Every Figma URL, Confluence page, Jira ID, and Standards source the user supplies belongs to the **same feature**. The command never asks "which feature is this for?" or "which URL is primary?". Mixed inputs covering multiple features are treated as user error: surface the conflict, stop, and ask the user to split into separate runs.
22
- 2. **Section omission rule.** Sections with zero evidence are dropped entirely; no `TBD` placeholder section. Numbering remains sequential `1..N` over the rendered set.
22
+ 2. **Section omission rule.** Sections with zero evidence are dropped entirely; no `TBD` placeholder section. **A rendered section keeps its canonical template number**, so the set has gaps and that is correct: `1, 2, 4, 9, 13, 14, 21` is a valid rendered document. This clause used to say numbering re-flows sequentially `1..N`, which could not hold alongside Locked 30 - that decision threads ids across sections BY NUMBER (`Section 15.1 scenario`, `Section 4.4`, `Section 3/5 layout cells`), and a re-flowed document sends every one of those cross-references to the wrong section or to nothing. No emitted document ever re-flowed; the rule was the part that was wrong. What IS checked: every rendered number is a real template number, and the numbers ascend without repeating.
23
23
  3. **Citation discipline.** Every quoted UI string, endpoint path, error code, or analytics event name in Sections 2-4 must carry an inline citation: `[Figma annotation <nodeId>]` for copy taken from a Dev Mode annotation, `[Figma <nodeId>]` (MCP) or `[figma-export: <project-slug>/<screen-slug>:<nodeId>]` (local source) for design strings; `file:line` for repo evidence; `Confluence:<pageId>:<heading-slug>` for spec text. **Annotation-as-copy precedence:** when the project's `figma-config` has `annotations.enabled` and a node carries a Dev Mode annotation, that annotation is the authoritative copy for the node and the visible text layer is treated as a placeholder; cite the annotation, not the layer text. Never invent copy - blank beats a guess; a node whose annotation has the base language but is missing a target language emits a Section 20 (Risks) row rather than a fabricated value. Uncited quotes are downgraded to `[label TBD - see Open Questions]` and a row is added to Section 7 Risks. Subagent prose and Code Connect snippets are not citations.
24
24
  4. **The spec is forward-looking.** Section bodies describe the new feature as drawn / specified. Findings that exist only in legacy code or the existing branch appear as `> Legacy reference: <text> (file:line)` blockquotes inside the relevant section, never as the lead sentence or a primary table row. A legacy-only finding with no forward counterpart goes to Section 7 Risks as a decision item: "current code does X; should the new feature keep, change, or drop this?".
25
25
  5. **Output default = Local file.** The Phase 3.5 output picker keeps `Local file` pre-selected. Confluence and Jira outputs are never default-selected (see `analysis-output-confluence-on-request` memory).
@@ -44,17 +44,16 @@ When citing a Locked decision in code or docs, prefer `Locked <n> (<short label>
44
44
  22. **Platform-agnostic template + Pass B render.** The template (`$HOME/.claude/multi-agent-refs/analysis-template.md`) describes concepts (state holder, view, navigator, use case, repository, DTO, state model, DI register, localization key, accessibility identifier, test method) without platform-specific class names. Pass B (Phase 2b) projects each concept onto the selected platform using conventions extracted at Phase 1c.
45
45
  23. **Convention extraction mandatory (Phase 1c).** After Phase 1b repo evidence, Phase 1c extracts seven pattern groups (folder structure, class naming, UI state model, test method naming, accessibility identifier, localization key, DI registration) per selected repo via `~/.claude/lib/extract-conventions.sh`. Output lands in `state.analysisSpec.evidence.conventions[<repo>]` with confidence levels (high / medium / low / none) and evidence file citations.
46
46
  24. **Pass B cell footnote mandatory.** Every cell Pass B fills in Section 13 (Architecture Plan concept table) and any other per-platform projection carries a footnote of the form `^[<convention-key> <confidence>: <evidence-source>]` pointing back to Phase 1c output. Cells with `confidence: low | none` cite `conventions-defaults.md:C<n>-<platform>` and emit a row in Section 20 Risks ("convention fallback applied"). Footnote-less cells fail the dispatch gate.
47
- 25. **Lite mode for small features.** When Phase 1 signals indicate a small feature, Lite mode auto-activates and renders only Sections 1, 2, 4, 9, 13, 14, 21, plus optional 23 Changelog. The user can force Lite with `--lite` or force Full with `--full`. Full mode is the default for new feature analyses. **Scoring (v9.1.0+):** three independent signals - `confluenceSpecLines < 100`, `figmaFramesCount <= 1`, `repoDirectMatchCount >= 8`. Each true signal scores 1 point; Lite auto-activates at `score >= 2`. Previous AND-threshold (`v8.12.0..v9.0.x`) was too strict and forced small features into Full mode when one signal was just over the line. Explicit user flags (`--lite` / `--full`) always win over auto-scoring.
48
- 26. **Pass B preview before render.** Phase 2a presents the resolved convention table to the user before Phase 2b emits any platform file. The user can approve, override individual cells, or cancel. Empty answers do not imply consent (`feedback_no-inferred-defaults-from-empty-answer`); the picker re-asks on empty submit.
49
- 27. **Evidence digest caches Phase 1b and 1c.** `evidence_digest = sha256(featureName || sorted(platforms) || repoEvidence.summary || conventions.summary)`. When the same feature name is invoked again against the same set of repos and the digest matches, Phase 1b and 1c are skipped and the cached `evidence.repoEvidence` / `evidence.conventions` is reused. Cache TTL is 24 hours; manual invalidation via `--no-cache` flag.
50
- 28. **SwiftUI Preview block mandatory (iOS projection, SwiftUI only).** When the iOS file is produced AND the affected view is a SwiftUI view (detected via `import SwiftUI` + `: View` protocol conformance in `evidence.repoEvidence[<repo>].buckets.uiComponents`), Section 13.6 renders a Preview block table covering at minimum: canonical default (LTR Light), Dark, RTL, Dynamic Type accessibilityLarge, and one error variant. Loading state and edge-case variants are added when distinct from canonical. UIKit-only features (no SwiftUI view artefact) drop Section 13.6 with note `(N/A: UIKit-only feature)`. Preview macro convention (`#Preview` for Swift 5.9+ vs legacy `PreviewProvider`) is read from `evidence.conventions[<repo>].previewMacro`. Each Preview variant listed in Section 13.6 must have a matching row in Section 15.2 Snapshot Tests; a Preview without a snapshot row triggers a Section 20 Risk.
51
- 29. **Variant usage explicit and bounded.** Section 6 inventory rows list which variants this feature consumes per component (concrete enum case + bool value). New Section 6.X (Variant Usage Matrix) catalogues the full variant axis vs. used subset with a rationale per excluded variant. Sections 13.6 (Preview) and 15.2 (Snapshot) cover only the used subset; expanding the variant set requires updating Section 6.X first.
52
- 30. **Analysis as self-contained design bridge - no MCP outside analysis phase (BLOCKING, pipeline-wide).** The analysis document is the sole design source for every downstream phase. After Phase 1 of `/multi-agent:analysis` produces `analysis/<feature>-<platform>.md`, Phase 2 Planning, Phase 3 Dev, Phase 4 Review, Phase 5 Test, Phase 6 Commit, and Phase 7 Report consume only the analysis document plus repo Code Connect mappings (`*.figma.swift` / `*.figma.kt`). Calling `mcp__claude_ai_Figma__*`, hitting `api.figma.com`, or fetching a `figma.com/design/...` URL during Phase 2+ is a violation. Applies to every mode that runs Phase 2+: `/multi-agent`, `/multi-agent:autopilot`, `/multi-agent:local`, `/multi-agent:local-autopilot`, at either depth. Hard requirement (v9.0.0): Phase 2 Pre-item and Phase 3 Pre-item (BLOCKING) abort the run when the analysis document is missing. Memory: `[[mcp-only-in-analysis]]`. Generic rule rationale and access matrix: see `$HOME/.claude/rules/figma-pipeline.md` "MUST: No MCP outside analysis phase".
53
- 31. **Business-rule to acceptance-criterion to test traceability (AI + human spine).** The analysis is a development handoff that both an AI implementer and a human reviewer must act on, so it is bound by one shared-ID vocabulary. Every business rule carries a stable id `BR-<slug>-NN` (Section 4.4). Each rule maps to at least one acceptance criterion written Given / When / Then (binary - two readers must not be able to disagree on pass/fail). Each acceptance criterion maps to unit-test scenarios in Section 15.1, one row per case across happy / boundary / error / empty-nil (enumerate at least the failure modes; agents hallucinate error handling when it is omitted). The same ids thread onward: Section 15.6 UI-test flows reference the `BR-` ids and use stable selectors (accessibilityIdentifier / testTag), Section 16 accessibility items reuse those identifiers, Section 11 analytics events cite their triggering rule or story, and Section 5/7 layout cells carry token + Figma node refs. Never invent copy or values (blank beats a guess; a missing source becomes a Section 20 Open Question). **Mode-aware gate:** in Full mode a business rule with no acceptance criterion, or an acceptance criterion with no Section 15.1 scenario, fails the dispatch gate. In **Lite mode Section 15 is not rendered**, so the rule-to-test half does not apply - Section 4.4 still lists each rule with its Given/When/Then acceptance criterion (the acceptance criterion is itself the testable statement), and the 15.1 mapping is deferred to whenever the feature is later analyzed in Full or implemented by a dev run. The rule-to-acceptance-criterion half always holds, in both modes.
54
-
55
- 32. **Analysis profile selected at intake.** `state.analysisSpec.profile` is `global` (default) or `corporate`, asked once at Phase 0 Step 1b and never re-asked mid-run. `global` renders `$HOME/.claude/multi-agent-refs/analysis-template.md` (23 sections, development handoff). `corporate` renders `$HOME/.claude/multi-agent-refs/analysis-template-corporate.md` (requirements document: `IG -> UC -> FG` spine, three traceability matrices, current-to-target state with impact analysis, then Technical Analysis and Development Analysis). **Both profiles read the same `state.analysisSpec.evidence.*`** - intake, fetching, repo evidence and convention extraction are shared and profile-independent; only the projection differs. This is what keeps the two templates from drifting into two products. One run emits one profile: rendering both from a single run would produce two documents describing the same feature, and the next reader would have to decide which one is current. When only one profile is available (`prefs.global.analysisProfiles` lists one, or the corporate profile has no binding configuration), the step auto-resolves and prints its breadcrumb with the resolution noted, per the picker contract.
56
- 33. **Corporate backbone always renders.** In the `corporate` profile the Locked 2 omission rule is replaced for Part A and the footer: those sections render even with zero evidence, carrying `N/A` when the section is genuinely out of scope for the feature and `EKLENECEK` when evidence is expected but missing. This is the point of a requirements document - a reader has to be able to tell "we considered hardware needs and there are none" from "nobody looked". Every `EKLENECEK` emits a matching Section 20 Risks and Open Questions row naming what is missing and who can answer it; an `EKLENECEK` with no such row fails the dispatch gate, because an unanswered question nobody owns is how a placeholder reaches production. **Missing inputs never block the run**: the corporate source practice of halting until every input arrives is deliberately not adopted - the document is produced with `EKLENECEK` in the gaps and the gaps are raised in Section 20. Part B follows the global omission table unchanged. In the `global` profile Locked 2 applies as written, with no placeholder of any kind.
57
- 34. **References are built from the evidence record, not written.** Section 21 is emitted by `$HOME/.claude/scripts/build-references.mjs` from `state.analysisSpec.evidence.*` in both profiles. Each row carries a precision anchor in its `Sürüm / Ref` column - Figma node id, Confluence `pageId` plus page version, the commit SHA a repo was read at, the Swagger spec version - because a reference with no anchor points at a moving target. Each row carries an `Erişim / Access` cell: a declared source that could not be fetched still gets a row reading `erişilemedi (<reason>)`, since a silently dropped source reads to the next person as a source that never existed. User statements from the conversation that no fetched source contains are recorded as `Serbest metin` rows, quoted verbatim, with the decision they settled. **Coverage gate**: every entry in `evidence.figma[]`, `confluence[]`, `jira[]`, `swagger[]`, `repo[]`, `standards[]`, `firebase[]`, `documents[]`, `outside[]`, `freeText[]` and every entry in `evidence.fetchErrors[]` must appear as a row, and every row must map to an evidence entry. A source that shaped the document but is missing from References fails the dispatch gate; so does an invented row with no evidence behind it.
58
- 35. **Stack-optional render.** Platform and repo selection are optional. When `state.analysisSpec.platforms[]` is empty, the run still completes: the analysis layers that do not need a target repository render in full - Part A and Part B in the corporate profile, Sections 1-12 and 16-17 in the global profile - and only the development layer is dropped (corporate Part C; global Sections 13, 14, 15) along with the Pass B projection, since there are no conventions to project onto. A Section 20 row records that the development analysis awaits a repo selection. **The channel split survives the missing repo.** Channels are derived from the evidence instead of repo stack tags (`intake.md` Step 3 carries the signal table) and one document is emitted per derived channel - `mobile`, `web`, or both. A phone screen and a browser screen carry different requirements before anyone has picked a repository; the split (Locked 9) exists to carry that difference and only its *projection* half needs conventions. `mobile` stays one channel rather than iOS plus Android, since without conventions nothing tells the two apart. Evidence with no interface at all yields a single channel-agnostic `<feature>.md`. Files land under `~/Desktop/multiAgentAnalysis/<feature-name>/`, named `<feature>-<channel>.md` (or `<feature>.md` for the channel-agnostic case): the repo-relative `analysis/` path has nothing to be relative to without a repo, and the current working directory is never used, since for a repo-less run it is arbitrary. Desktop rather than a hidden directory because the document is a deliverable somebody is meant to open and hand over, and `multiAgentAnalysis` rather than a bare `Analysis` because a generic word collides with whatever else is on a desktop while the producer name groups every run this command ever writes. The Phase 3.5 picker shows the resolved path and takes an override through its Other input. A requirements document is useful before anyone has decided which repository will hold the code, and refusing to produce one until that decision exists inverts the order the work actually happens in.
59
- 36. **The document is reviewed before it is published.** An analysis run used to go from draft straight to dispatch behind a deterministic validator, so nothing read what it was about to publish: one run put a channel it never searched for, an open question about a frame it never opened, and twenty-three unowned `EKLENECEK` markers onto a live page. Every one is what a reader catches on the first pass. Phase 3.2 runs `phases/phase-4-review.md` Step 0 (strict validator, the host's three-reviewer set, triage) on the draft before the destination is chosen: a finding is cheap while nothing is written. Reviewers are subagents holding `analysis/review.md`, never the context that wrote the document, which cannot notice a search it never thought to run. A blocking finding returns to Phase 2b with dispatch closed and never becomes an open question, since "the document is wrong" is not something to ask the reader; capped at two returns. Phase 3.3 sorts every remaining gap into searched-and-closed, asked-and-answered, or `AS-NN`; an unstamped gap fails the dispatch gate. Autopilot runs both; only the asking degrades, into rows stamped `autopilot: could not ask`.
60
- 37. **A redesign records v1 before it plans v2.** `options.redesign` is an opt-in on the `options.uiTests` axis, never a third `mode` value: `mode` says how many sections, `redesign` says which content, and a `mode: redesign` would switch off the Full-mode traceability, Test Plan and rule-to-test gates in exactly the documents that need them. It adds three sub-sections and no top-level section: current behaviour with `CB-<slug>-NN` ids and `repo/file:line` citations, the v1 to v2 endpoint mapping, and a difference list over a closed status vocabulary. Every `Missing` and `Partial` owes a Section 20 row by `AS-NN`, and a `CB-` id in one table but not the other fails in both directions - a behaviour that is in the code and on nobody's difference list is what a redesign loses and production finds. Evidence and certainty are derived from `repoEvidence`, never graded by the writer (Locked 24, same reason), and `options.redesign` is an `evidence_digest` input. Contract and the eight checks: `analysis/redesign.md`, loaded only on a redesign run.
47
+ 25. **Pass B preview before render.** Phase 2a presents the resolved convention table to the user before Phase 2b emits any platform file. The user can approve, override individual cells, or cancel. Empty answers do not imply consent (`feedback_no-inferred-defaults-from-empty-answer`); the picker re-asks on empty submit.
48
+ 26. **Evidence digest caches Phase 1b and 1c.** `evidence_digest = sha256(featureName || sorted(platforms) || repoEvidence.summary || conventions.summary)`. When the same feature name is invoked again against the same set of repos and the digest matches, Phase 1b and 1c are skipped and the cached `evidence.repoEvidence` / `evidence.conventions` is reused. Cache TTL is 24 hours; manual invalidation via `--no-cache` flag.
49
+ 27. **SwiftUI Preview block mandatory (iOS projection, SwiftUI only).** When the iOS file is produced AND the affected view is a SwiftUI view (detected via `import SwiftUI` + `: View` protocol conformance in `evidence.repoEvidence[<repo>].buckets.uiComponents`), Section 13.6 renders a Preview block table covering at minimum: canonical default (LTR Light), Dark, RTL, Dynamic Type accessibilityLarge, and one error variant. Loading state and edge-case variants are added when distinct from canonical. UIKit-only features (no SwiftUI view artefact) drop Section 13.6 with note `(N/A: UIKit-only feature)`. Preview macro convention (`#Preview` for Swift 5.9+ vs legacy `PreviewProvider`) is read from `evidence.conventions[<repo>].previewMacro`. Each Preview variant listed in Section 13.6 must have a matching row in Section 15.2 Snapshot Tests; a Preview without a snapshot row triggers a Section 20 Risk.
50
+ 28. **Variant usage explicit and bounded.** Section 6 inventory rows list which variants this feature consumes per component (concrete enum case + bool value). New Section 6.X (Variant Usage Matrix) catalogues the full variant axis vs. used subset with a rationale per excluded variant. Sections 13.6 (Preview) and 15.2 (Snapshot) cover only the used subset; expanding the variant set requires updating Section 6.X first.
51
+ 29. **Analysis as self-contained design bridge - no MCP outside analysis phase (BLOCKING, pipeline-wide).** The analysis document is the sole design source for every downstream phase. After Phase 1 of `/multi-agent:analysis` produces `analysis/<feature>-<platform>.md`, Phase 1 Plan, Phase 2 Dev, Phase 3 Review, Phase 3 Review, Phase 4 Commit, and Phase 5 Report consume only the analysis document plus repo Code Connect mappings (`*.figma.swift` / `*.figma.kt`). Calling `mcp__claude_ai_Figma__*`, hitting `api.figma.com`, or fetching a `figma.com/design/...` URL during Phase 2+ is a violation. Applies to every mode that runs Phase 2+: `/multi-agent`, `/multi-agent:autopilot`, `/multi-agent:local`, `/multi-agent:local-autopilot`, at either depth. Hard requirement (v9.0.0): Phase 2 Pre-item and Phase 3 Pre-item (BLOCKING) abort the run when the analysis document is missing. Memory: `[[mcp-only-in-analysis]]`. Generic rule rationale and access matrix: see `$HOME/.claude/rules/figma-pipeline.md` "MUST: No MCP outside analysis phase".
52
+ 30. **Business-rule to acceptance-criterion to test traceability (AI + human spine).** The analysis is a development handoff that both an AI implementer and a human reviewer must act on, so it is bound by one shared-ID vocabulary. Every business rule carries a stable id `BR-<slug>-NN` (Section 4.4). Each rule maps to at least one acceptance criterion written Given / When / Then (binary - two readers must not be able to disagree on pass/fail). Each acceptance criterion maps to unit-test scenarios in Section 15.1, one row per case across happy / boundary / error / empty-nil (enumerate at least the failure modes; agents hallucinate error handling when it is omitted). The same ids thread onward: Section 15.6 UI-test flows reference the `BR-` ids and use stable selectors (accessibilityIdentifier / testTag), Section 16 accessibility items reuse those identifiers, Section 11 analytics events cite their triggering rule or story, and Section 3/5 layout cells carry token + Figma node refs. Never invent copy or values (blank beats a guess; a missing source becomes a Section 20 Open Question). **Gate:** a business rule with no acceptance criterion, or an acceptance criterion with no Section 15.1 scenario, fails the dispatch gate. There is no mode clause: the rule used to be suspended in Lite mode, which deferred the rule-to-test half to "whenever the feature is later analyzed in Full" - a debt nothing tracked and nothing ever paid. Section 15 renders when there is evidence for it and is dropped when there is not, like every other section, and the gate applies to whatever was rendered.
53
+
54
+ 31. **Analysis profile selected at intake.** `state.analysisSpec.profile` is `global` (default) or `corporate`, asked once at Phase 0 Step 1b and never re-asked mid-run. `global` renders `$HOME/.claude/multi-agent-refs/analysis-template.md` (23 sections, development handoff). `corporate` renders `$HOME/.claude/multi-agent-refs/analysis-template-corporate.md` (requirements document: `IG -> UC -> FG` spine, three traceability matrices, current-to-target state with impact analysis, then Technical Analysis and Development Analysis). **Both profiles read the same `state.analysisSpec.evidence.*`** - intake, fetching, repo evidence and convention extraction are shared and profile-independent; only the projection differs. This is what keeps the two templates from drifting into two products. One run emits one profile: rendering both from a single run would produce two documents describing the same feature, and the next reader would have to decide which one is current. When only one profile is available (`prefs.global.analysisProfiles` lists one, or the corporate profile has no binding configuration), the step auto-resolves and prints its breadcrumb with the resolution noted, per the picker contract.
55
+ 32. **Corporate backbone always renders.** In the `corporate` profile the Locked 2 omission rule is replaced for Part A and the footer: those sections render even with zero evidence, carrying `N/A` when the section is genuinely out of scope for the feature and `EKLENECEK` when evidence is expected but missing. This is the point of a requirements document - a reader has to be able to tell "we considered hardware needs and there are none" from "nobody looked". Every `EKLENECEK` emits a matching Section 20 Risks and Open Questions row naming what is missing and who can answer it; an `EKLENECEK` with no such row fails the dispatch gate, because an unanswered question nobody owns is how a placeholder reaches production. **Missing inputs never block the run**: the corporate source practice of halting until every input arrives is deliberately not adopted - the document is produced with `EKLENECEK` in the gaps and the gaps are raised in Section 20. Part B follows the global omission table unchanged. In the `global` profile Locked 2 applies as written, with no placeholder of any kind.
56
+ 33. **References are built from the evidence record, not written.** Section 21 is emitted by `$HOME/.claude/scripts/build-references.mjs` from `state.analysisSpec.evidence.*` in both profiles. Each row carries a precision anchor in its `Sürüm / Ref` column - Figma node id, Confluence `pageId` plus page version, the commit SHA a repo was read at, the Swagger spec version - because a reference with no anchor points at a moving target. Each row carries an `Erişim / Access` cell: a declared source that could not be fetched still gets a row reading `erişilemedi (<reason>)`, since a silently dropped source reads to the next person as a source that never existed. User statements from the conversation that no fetched source contains are recorded as `Serbest metin` rows, quoted verbatim, with the decision they settled. **Coverage gate**: every entry in `evidence.figma[]`, `confluence[]`, `jira[]`, `swagger[]`, `repo[]`, `standards[]`, `firebase[]`, `documents[]`, `outside[]`, `freeText[]` and every entry in `evidence.fetchErrors[]` must appear as a row, and every row must map to an evidence entry. A source that shaped the document but is missing from References fails the dispatch gate; so does an invented row with no evidence behind it.
57
+ 34. **Stack-optional render.** Platform and repo selection are optional. When `state.analysisSpec.platforms[]` is empty, the run still completes: the analysis layers that do not need a target repository render in full - Part A and Part B in the corporate profile, Sections 1-12 and 16-17 in the global profile - and only the development layer is dropped (corporate Part C; global Sections 13, 14, 15) along with the Pass B projection, since there are no conventions to project onto. A Section 20 row records that the development analysis awaits a repo selection. **The channel split survives the missing repo.** Channels are derived from the evidence instead of repo stack tags (`intake.md` Step 3 carries the signal table) and one document is emitted per derived channel - `mobile`, `web`, or both. A phone screen and a browser screen carry different requirements before anyone has picked a repository; the split (Locked 9) exists to carry that difference and only its *projection* half needs conventions. `mobile` stays one channel rather than iOS plus Android, since without conventions nothing tells the two apart. Evidence with no interface at all yields a single channel-agnostic `<feature>.md`. Files land under `~/Desktop/multiAgentAnalysis/<feature-name>/`, named `<feature>-<channel>.md` (or `<feature>.md` for the channel-agnostic case): the repo-relative `analysis/` path has nothing to be relative to without a repo, and the current working directory is never used, since for a repo-less run it is arbitrary. Desktop rather than a hidden directory because the document is a deliverable somebody is meant to open and hand over, and `multiAgentAnalysis` rather than a bare `Analysis` because a generic word collides with whatever else is on a desktop while the producer name groups every run this command ever writes. The Phase 3.5 picker shows the resolved path and takes an override through its Other input. A requirements document is useful before anyone has decided which repository will hold the code, and refusing to produce one until that decision exists inverts the order the work actually happens in.
58
+ 35. **The document is reviewed before it is published.** An analysis run used to go from draft straight to dispatch behind a deterministic validator, so nothing read what it was about to publish: one run put a channel it never searched for, an open question about a frame it never opened, and twenty-three unowned `EKLENECEK` markers onto a live page. Every one is what a reader catches on the first pass. Phase 3.2 runs `phases/phase-3-review.md` Step 0 (strict validator, the host's three-reviewer set, triage) on the draft before the destination is chosen: a finding is cheap while nothing is written. Reviewers are subagents holding `analysis/review.md`, never the context that wrote the document, which cannot notice a search it never thought to run. A blocking finding returns to Phase 2b with dispatch closed and never becomes an open question, since "the document is wrong" is not something to ask the reader; capped at two returns. Phase 3.3 sorts every remaining gap into searched-and-closed, asked-and-answered, or `AS-NN`; an unstamped gap fails the dispatch gate. Autopilot runs both; only the asking degrades, into rows stamped `autopilot: could not ask`.
59
+ 36. **A redesign records v1 before it plans v2.** `options.redesign` is an opt-in on the `options.uiTests` axis, never a third `mode` value: `mode` says how many sections, `redesign` says which content, and a `mode: redesign` would switch off the Full-mode traceability, Test Plan and rule-to-test gates in exactly the documents that need them. It adds three sub-sections and no top-level section: current behaviour with `CB-<slug>-NN` ids and `repo/file:line` citations, the v1 to v2 endpoint mapping, and a difference list over a closed status vocabulary. Every `Missing` and `Partial` owes a Section 20 row by `AS-NN`, and a `CB-` id in one table but not the other fails in both directions - a behaviour that is in the code and on nobody's difference list is what a redesign loses and production finds. Evidence and certainty are derived from `repoEvidence`, never graded by the writer (Locked 24, same reason), and `options.redesign` is an `evidence_digest` input. Contract and the eight checks: `analysis/redesign.md`, loaded only on a redesign run.
@@ -46,7 +46,7 @@
46
46
 
47
47
  Locked 36 carries the rule. Operationally:
48
48
 
49
- 1. Run `phases/phase-4-review.md` Step 0, the analysis-mode branch. It already defines
49
+ 1. Run `phases/phase-3-review.md` Step 0, the analysis-mode branch. It already defines
50
50
  the strict validator, the host's three-reviewer set with its model routing, and the
51
51
  triage. Do not restate it here; a second definition is the one that rots.
52
52
  2. Dispatch reviewers as subagents, each given the draft path, the state JSON and
@@ -52,8 +52,8 @@ Convention preview - Pass B will render with:
52
52
  | DI | UserProfileDependencyConfigurator ^[C7 high] | UserProfileModule (Hilt) ^[C7 fallback: defaults] |
53
53
 
54
54
  Confidence summary:
55
- iOS: 7/7 high, 0 medium, 0 low, 0 fallback
56
- Android: 5/7 high, 1 medium, 0 low, 1 fallback
55
+ iOS: 5/5 high, 0 medium, 0 low, 0 fallback
56
+ Android: 3/5 high, 1 medium, 0 low, 1 fallback
57
57
  ```
58
58
 
59
59
  AskUserQuestion shape:
@@ -86,11 +86,17 @@ For each `platform` in `state.analysisSpec.platforms[]`:
86
86
  - `android` → `~/.claude/rules/kotlin-android.md` first → `evidence.standards[]` entries whose path contains `android` or `kotlin`
87
87
  - `backend` → `evidence.standards[]` entries matching language hints (`python`, `go`, `node`, `fastapi`) → fall back to `~/.claude/rules/security.md` + `code-style.md`
88
88
  - `web` → `evidence.standards[]` entries matching `react`, `vue`, `next`, `sveltekit` → `~/.claude/rules/code-style.md`
89
- 2. **Apply per-platform omission rules.** Backend-only file drops Sections 5, 6, 7, 8, 16. Web with no UI inventory still keeps 5 (UI exists in code). Sections 1, 2, 4, 9, 13, 14, 20, 21 always present per Locked decision 2 + 13.
90
- 3. **Resolve mode.** If user passed `--lite` → Lite. If user passed `--full` → Full. Otherwise use `state.analysisSpec.liteModeAuto`. Lite mode renders only Sections 1, 2, 4, 9, 13, 14, 21 plus optional 23.
91
- 4. **Produce YAML front-matter header** (see `$HOME/.claude/multi-agent-refs/analysis-template.md`). Include `profile: <state.analysisSpec.profile | global>` and `platform: <platform | none>` so the validator applies the right contract per profile (Locked 32) and recognises the stack-optional render (Locked 35), `mode: full | lite`, plus `ui_tests: <state.analysisSpec.options.uiTests | false>`, `a11y_depth: <state.analysisSpec.options.a11yDepth | basic>` and `redesign: <state.analysisSpec.options.redesign | false>` so the pre-dispatch validator can enforce the opt-in coverage (15.6 present when ui_tests, 16.2 walkthrough present when a11y_depth is full, 4.5 / 4.6 / 9.5 present when redesign). `status` is written only by `/multi-agent:analysis-resolve`; a rendered document is a draft.
89
+ 2. **Apply per-platform omission rules.** Backend-only file drops Sections 5, 6, 7, 8, 16. Web with no UI inventory still keeps 5 (UI exists in code). Sections 1, 2, 4, 9, 13, 14, 20, 21 always present per Locked decision 2 + 13; their numbers are canonical and never re-flowed.
90
+ 3. **Resolve the section set from evidence, not from a mode.** There is one
91
+ pipeline. A section renders when it has evidence and is dropped when it does
92
+ not, per Locked 2. Lite mode used to answer this question with a fixed list
93
+ (1, 2, 4, 9, 13, 14, 21 plus optional 23) chosen by a three-signal score, and
94
+ that list fought Locked 2 in both directions: a small feature with rich
95
+ business rules lost Section 15 because it was not on the list, and a feature
96
+ with no API contract kept Section 9 because it was. Evidence decides now.
97
+ 4. **Produce YAML front-matter header** (see `$HOME/.claude/multi-agent-refs/analysis-template.md`). Include `profile: <state.analysisSpec.profile | global>` and `platform: <platform | none>` so the validator applies the right contract per profile (Locked 32) and recognises the stack-optional render (Locked 35), plus `ui_tests: <state.analysisSpec.options.uiTests | false>`, `a11y_depth: <state.analysisSpec.options.a11yDepth | basic>` and `redesign: <state.analysisSpec.options.redesign | false>` so the pre-dispatch validator can enforce the opt-in coverage (15.6 present when ui_tests, 16.2 walkthrough present when a11y_depth is full, 4.5 / 4.6 / 9.5 present when redesign). `status` is written only by `/multi-agent:analysis-resolve`; a rendered document is a draft.
92
98
  5. **Read conventions for this platform's repo.** For each cell Pass B fills in Section 13 and in any per-platform projection (Sections 5, 6, 7, 8, 10, 11, 13, 14, 15, 16, 17), read `state.analysisSpec.evidence.conventions[<repo>].<field>` and emit the value with a footnote (Locked 24). If `conventionOverrides` has an entry for that field, use the override and footnote with `^[user-override: <reason>]` instead of evidence path.
93
- 6. **Concatenate non-null sections in canonical order.** Numbering stays sequential `1..N` over the rendered set (omitted sections do not create gaps).
99
+ 6. **Concatenate non-null sections in canonical order.** Each rendered section keeps its canonical template number, so omitted sections DO leave gaps - `1, 2, 4, 9, 13, 14, 21` is a correct rendered document. Locked 30 cites sections by number across the whole document; re-flowing them would break every one of those references.
94
100
  7. **Schema validation** on the per-platform spec object:
95
101
  ```bash
96
102
  python3 -c "import json,jsonschema; jsonschema.validate(json.load(open('state/<feature>-<platform>.json')), json.load(open('$HOME/.claude/schemas/analysis-spec.schema.json')))"