@mmerterden/multi-agent-pipeline 18.0.0 → 19.1.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 (234) hide show
  1. package/CHANGELOG.md +287 -0
  2. package/README.md +36 -20
  3. package/README.tr.md +14 -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/0014-six-phase-consolidation.md +134 -0
  9. package/docs/adr/README.md +2 -1
  10. package/docs/architecture.md +37 -38
  11. package/docs/best-practices.md +1 -1
  12. package/docs/ecosystem.md +46 -27
  13. package/docs/engineering.md +1 -1
  14. package/docs/facts.json +61 -0
  15. package/docs/features.md +55 -54
  16. package/docs/performance.md +5 -5
  17. package/docs/recovery-guide.md +17 -17
  18. package/docs/token-budget-history.md +3 -1
  19. package/index.js +2 -2
  20. package/install/_codex-agents.mjs +1 -1
  21. package/install/templates/claude-hooks.json +1 -1
  22. package/install/templates/codex-instructions.md +1 -1
  23. package/install/templates/copilot-instructions.md +28 -28
  24. package/manifest.json +234 -216
  25. package/package.json +2 -2
  26. package/pipeline/agents/dev-critic.md +7 -7
  27. package/pipeline/commands/figma-to-swiftui.md +1 -1
  28. package/pipeline/commands/multi-agent/SKILL.md +9 -9
  29. package/pipeline/commands/multi-agent/analysis/SKILL.md +15 -15
  30. package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +2 -2
  31. package/pipeline/commands/multi-agent/autopilot/SKILL.md +7 -7
  32. package/pipeline/commands/multi-agent/channels/SKILL.md +15 -15
  33. package/pipeline/commands/multi-agent/diff-explain/SKILL.md +6 -6
  34. package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
  35. package/pipeline/commands/multi-agent/graph/SKILL.md +1 -1
  36. package/pipeline/commands/multi-agent/help/SKILL.md +62 -62
  37. package/pipeline/commands/multi-agent/language/SKILL.md +2 -2
  38. package/pipeline/commands/multi-agent/local/SKILL.md +11 -11
  39. package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +13 -13
  40. package/pipeline/commands/multi-agent/log/SKILL.md +2 -2
  41. package/pipeline/commands/multi-agent/manual-test/SKILL.md +9 -9
  42. package/pipeline/commands/multi-agent/model/SKILL.md +69 -0
  43. package/pipeline/commands/multi-agent/refactor/SKILL.md +3 -3
  44. package/pipeline/commands/multi-agent/resume/SKILL.md +4 -4
  45. package/pipeline/commands/multi-agent/resume-local/SKILL.md +19 -17
  46. package/pipeline/commands/multi-agent/review/SKILL.md +2 -2
  47. package/pipeline/commands/multi-agent/review-analysis/SKILL.md +1 -1
  48. package/pipeline/commands/multi-agent/route-off/SKILL.md +36 -0
  49. package/pipeline/commands/multi-agent/route-on/SKILL.md +74 -0
  50. package/pipeline/commands/multi-agent/route-status/SKILL.md +56 -0
  51. package/pipeline/commands/multi-agent/setup/SKILL.md +2 -2
  52. package/pipeline/commands/multi-agent/status/SKILL.md +5 -5
  53. package/pipeline/commands/multi-agent/steer/SKILL.md +2 -2
  54. package/pipeline/commands/multi-agent/sync/SKILL.md +12 -13
  55. package/pipeline/commands/multi-agent/test/SKILL.md +1 -1
  56. package/pipeline/lib/credential-inventory.sh +1 -1
  57. package/pipeline/lib/fetch-fortify.sh +1 -1
  58. package/pipeline/lib/model-dispatch.sh +140 -0
  59. package/pipeline/lib/model-rung.sh +142 -0
  60. package/pipeline/lib/outbound-gate.mjs +14 -0
  61. package/pipeline/lib/phase-schema.mjs +88 -0
  62. package/pipeline/lib/plan-todos.sh +5 -5
  63. package/pipeline/lib/route-state.sh +161 -0
  64. package/pipeline/lib/run-paths.sh +2 -2
  65. package/pipeline/multi-agent-refs/_account-picker.md +1 -1
  66. package/pipeline/multi-agent-refs/_dev-context.md +6 -6
  67. package/pipeline/multi-agent-refs/_input-parser.md +1 -1
  68. package/pipeline/multi-agent-refs/analysis/evidence.md +2 -11
  69. package/pipeline/multi-agent-refs/analysis/intake.md +7 -7
  70. package/pipeline/multi-agent-refs/analysis/locked.md +48 -22
  71. package/pipeline/multi-agent-refs/analysis/redesign.md +1 -1
  72. package/pipeline/multi-agent-refs/analysis/render.md +10 -10
  73. package/pipeline/multi-agent-refs/analysis/resolve.md +1 -1
  74. package/pipeline/multi-agent-refs/analysis/review.md +2 -2
  75. package/pipeline/multi-agent-refs/analysis/synthesis.md +13 -7
  76. package/pipeline/multi-agent-refs/analysis-template-corporate.md +9 -9
  77. package/pipeline/multi-agent-refs/analysis-template.md +19 -19
  78. package/pipeline/multi-agent-refs/android-guide.md +1 -1
  79. package/pipeline/multi-agent-refs/audit-guide.md +13 -13
  80. package/pipeline/multi-agent-refs/channels/issue-comment.md +2 -2
  81. package/pipeline/multi-agent-refs/channels/jira.md +3 -3
  82. package/pipeline/multi-agent-refs/channels/pr.md +4 -4
  83. package/pipeline/multi-agent-refs/channels/wiki.md +1 -1
  84. package/pipeline/multi-agent-refs/component-dispatch.md +8 -8
  85. package/pipeline/multi-agent-refs/conventions-defaults.md +2 -2
  86. package/pipeline/multi-agent-refs/cross-cli-contract.md +31 -6
  87. package/pipeline/multi-agent-refs/features/analysis-jira.md +1 -1
  88. package/pipeline/multi-agent-refs/features/autopilot-circuit-breaker.md +4 -4
  89. package/pipeline/multi-agent-refs/features/code-graph.md +5 -5
  90. package/pipeline/multi-agent-refs/features/design-conformance.md +1 -1
  91. package/pipeline/multi-agent-refs/features/dev-critic.md +3 -3
  92. package/pipeline/multi-agent-refs/features/doctor.md +3 -3
  93. package/pipeline/multi-agent-refs/features/external-context-injection.md +3 -3
  94. package/pipeline/multi-agent-refs/features/maturity-followup.md +3 -3
  95. package/pipeline/multi-agent-refs/features/model-fallback.md +41 -5
  96. package/pipeline/multi-agent-refs/features/plan-todos.md +1 -1
  97. package/pipeline/multi-agent-refs/features/repo-map.md +1 -1
  98. package/pipeline/multi-agent-refs/features/review-delta.md +3 -3
  99. package/pipeline/multi-agent-refs/features/review-multi-repo.md +2 -2
  100. package/pipeline/multi-agent-refs/features/scope-check.md +4 -4
  101. package/pipeline/multi-agent-refs/features/skill-conformance.md +2 -2
  102. package/pipeline/multi-agent-refs/features/stack-skill-routing.md +1 -1
  103. package/pipeline/multi-agent-refs/features/url-enrichment.md +1 -1
  104. package/pipeline/multi-agent-refs/features/verify-by-test.md +4 -4
  105. package/pipeline/multi-agent-refs/features/visual-evidence.md +19 -19
  106. package/pipeline/multi-agent-refs/features/worktree-finalize.md +6 -6
  107. package/pipeline/multi-agent-refs/issue-jira-triad.md +10 -10
  108. package/pipeline/multi-agent-refs/knowledge.md +11 -11
  109. package/pipeline/multi-agent-refs/multi-repo-integration-build.md +13 -13
  110. package/pipeline/multi-agent-refs/payload-contracts.md +8 -8
  111. package/pipeline/multi-agent-refs/phases/log-format.md +10 -10
  112. package/pipeline/multi-agent-refs/phases/modes.md +30 -30
  113. package/pipeline/multi-agent-refs/phases/operations.md +8 -8
  114. package/pipeline/multi-agent-refs/phases/phase-0-init.md +24 -24
  115. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +599 -0
  116. package/pipeline/multi-agent-refs/phases/{phase-3-dev.md → phase-2-dev.md} +129 -49
  117. package/pipeline/multi-agent-refs/phases/{phase-4-review.md → phase-3-review.md} +225 -107
  118. package/pipeline/multi-agent-refs/phases/{phase-6-commit.md → phase-4-commit.md} +23 -23
  119. package/pipeline/multi-agent-refs/phases/{phase-7-report.md → phase-5-report.md} +29 -29
  120. package/pipeline/multi-agent-refs/phases.md +44 -48
  121. package/pipeline/multi-agent-refs/picker-contract.md +1 -1
  122. package/pipeline/multi-agent-refs/progress-contract.md +6 -6
  123. package/pipeline/multi-agent-refs/readiness-review.md +1 -1
  124. package/pipeline/multi-agent-refs/rules.md +7 -7
  125. package/pipeline/multi-agent-refs/swiftui-guide.md +2 -2
  126. package/pipeline/multi-agent-refs/tracker-contract.md +31 -32
  127. package/pipeline/multi-agent-refs/wiki-capture.md +14 -14
  128. package/pipeline/preferences-template.json +9 -1
  129. package/pipeline/rules/figma-pipeline.md +8 -8
  130. package/pipeline/rules/outside-the-pipeline.md +1 -1
  131. package/pipeline/schemas/agent-state.schema.json +50 -50
  132. package/pipeline/schemas/analysis-output.schema.json +3 -3
  133. package/pipeline/schemas/analysis-spec.schema.json +2 -2
  134. package/pipeline/schemas/autopilot-config.schema.json +1 -1
  135. package/pipeline/schemas/code-graph.schema.json +1 -1
  136. package/pipeline/schemas/criteria-manifest.schema.json +1 -1
  137. package/pipeline/schemas/dev-critic-output.schema.json +1 -1
  138. package/pipeline/schemas/diff-risk.schema.json +1 -1
  139. package/pipeline/schemas/figma-project-config.schema.json +1 -1
  140. package/pipeline/schemas/migrations/prefs-2.4.0-to-2.5.0.mjs +2 -2
  141. package/pipeline/schemas/migrations/prefs-2.6.0-to-2.7.0.mjs +31 -0
  142. package/pipeline/schemas/migrations/state-2.1.0-to-2.2.0.mjs +129 -0
  143. package/pipeline/schemas/phases.json +105 -0
  144. package/pipeline/schemas/plan-todos.schema.json +5 -5
  145. package/pipeline/schemas/planning-output.schema.json +1 -1
  146. package/pipeline/schemas/prefs.schema.json +102 -58
  147. package/pipeline/schemas/reviewer-output.schema.json +3 -3
  148. package/pipeline/schemas/route-config.schema.json +74 -0
  149. package/pipeline/schemas/scope-check.schema.json +1 -1
  150. package/pipeline/schemas/secret-patterns.json +124 -0
  151. package/pipeline/schemas/test-gap.schema.json +1 -1
  152. package/pipeline/schemas/token-budget.json +12 -18
  153. package/pipeline/schemas/triage-output.schema.json +6 -6
  154. package/pipeline/scripts/README.md +3 -3
  155. package/pipeline/scripts/_code-graph.mjs +2 -2
  156. package/pipeline/scripts/_run-paths.mjs +2 -2
  157. package/pipeline/scripts/_smoke-root.sh +1 -1
  158. package/pipeline/scripts/aggregate-metrics.mjs +1 -1
  159. package/pipeline/scripts/build-references.mjs +2 -2
  160. package/pipeline/scripts/bulk-read.sh +10 -1
  161. package/pipeline/scripts/capture-flush.sh +8 -8
  162. package/pipeline/scripts/capture-resume.sh +3 -3
  163. package/pipeline/scripts/classify-plan-safety.mjs +1 -1
  164. package/pipeline/scripts/cost-table.json +8 -1
  165. package/pipeline/scripts/diff-explain.mjs +1 -1
  166. package/pipeline/scripts/doctor.mjs +3 -3
  167. package/pipeline/scripts/gc-abandoned.sh +3 -3
  168. package/pipeline/scripts/gc-tmp.sh +1 -1
  169. package/pipeline/scripts/gc-worktrees.sh +1 -1
  170. package/pipeline/scripts/gen-facts.mjs +280 -0
  171. package/pipeline/scripts/gen-mode-dispatch.mjs +32 -37
  172. package/pipeline/scripts/gen-ref-toc.mjs +1 -1
  173. package/pipeline/scripts/graph-report.mjs +1 -1
  174. package/pipeline/scripts/jira-attach.sh +1 -1
  175. package/pipeline/scripts/learn-from-transcripts.mjs +1 -1
  176. package/pipeline/scripts/learning-curve.mjs +2 -2
  177. package/pipeline/scripts/log-metric.sh +17 -4
  178. package/pipeline/scripts/memory-save.sh +1 -1
  179. package/pipeline/scripts/migrate-prefs.mjs +22 -5
  180. package/pipeline/scripts/phase-banner.sh +20 -20
  181. package/pipeline/scripts/phase-tracker.sh +12 -12
  182. package/pipeline/scripts/plan-coverage-gate.mjs +2 -2
  183. package/pipeline/scripts/pre-commit-check.sh +30 -1
  184. package/pipeline/scripts/render-agent-log-cost.sh +1 -1
  185. package/pipeline/scripts/render-work-summary.sh +3 -3
  186. package/pipeline/scripts/review-file-filter.mjs +1 -1
  187. package/pipeline/scripts/run-aggregator.mjs +13 -6
  188. package/pipeline/scripts/run-metrics.mjs +1 -1
  189. package/pipeline/scripts/runs-index.mjs +11 -1
  190. package/pipeline/scripts/scan-skills.sh +26 -0
  191. package/pipeline/scripts/smoke-cross-cli-behavior.sh +6 -6
  192. package/pipeline/scripts/smoke-schema-validation.sh +26 -7
  193. package/pipeline/scripts/token-budget-report.mjs +13 -2
  194. package/pipeline/scripts/triage-memory.mjs +2 -2
  195. package/pipeline/scripts/validate-analysis-doc.mjs +274 -43
  196. package/pipeline/scripts/validate-planning.mjs +1 -1
  197. package/pipeline/scripts/validate-reviewer.mjs +1 -1
  198. package/pipeline/scripts/validate-state.mjs +45 -5
  199. package/pipeline/scripts/validate-triage.mjs +3 -3
  200. package/pipeline/scripts/verify-citations.mjs +1 -1
  201. package/pipeline/scripts/worktree-finalize.sh +5 -5
  202. package/pipeline/scripts/write-state.mjs +32 -0
  203. package/pipeline/skills/.skill-manifest.json +38 -22
  204. package/pipeline/skills/.skills-index.json +49 -5
  205. package/pipeline/skills/shared/README.md +10 -6
  206. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +8 -8
  207. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +8 -8
  208. package/pipeline/skills/shared/core/multi-agent/SKILL.md +81 -82
  209. package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +3 -3
  210. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +14 -14
  211. package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +5 -5
  212. package/pipeline/skills/shared/core/multi-agent-graph/SKILL.md +1 -1
  213. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +25 -23
  214. package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +2 -2
  215. package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +2 -2
  216. package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +8 -8
  217. package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +6 -6
  218. package/pipeline/skills/shared/core/multi-agent-model/SKILL.md +71 -0
  219. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +3 -3
  220. package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +1 -1
  221. package/pipeline/skills/shared/core/multi-agent-resume-local/SKILL.md +7 -7
  222. package/pipeline/skills/shared/core/multi-agent-route-off/SKILL.md +39 -0
  223. package/pipeline/skills/shared/core/multi-agent-route-on/SKILL.md +76 -0
  224. package/pipeline/skills/shared/core/multi-agent-route-status/SKILL.md +59 -0
  225. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +1 -1
  226. package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +5 -5
  227. package/pipeline/skills/shared/core/multi-agent-steer/SKILL.md +2 -2
  228. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +6 -5
  229. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +1 -1
  230. package/pipeline/skills/shared/external/signal-community/SKILL.md +8 -1
  231. package/pipeline/skills/skills-index.md +8 -4
  232. package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +0 -263
  233. package/pipeline/multi-agent-refs/phases/phase-2-planning.md +0 -344
  234. package/pipeline/multi-agent-refs/phases/phase-5-test.md +0 -182
@@ -11,7 +11,7 @@
11
11
  # Three things keep this from being a foot-gun, and each is a rule rather than a
12
12
  # heuristic:
13
13
  #
14
- # 1. A run WAITING FOR YOU is never reaped. An open PR, Phase 6 or 7, or
14
+ # 1. A run WAITING FOR YOU is never reaped. An open PR, Phase 4 or 7, or
15
15
  # `status: awaiting_input` means the work landed and the pipeline is
16
16
  # holding for an answer by design. That is finished work, not residue.
17
17
  # 2. A path that is not strictly inside `<repo>/.worktrees/` is never removed.
@@ -49,7 +49,7 @@
49
49
  #
50
50
  # State is moved to `<run-dir>/artifacts/` before the worktree goes, so the log,
51
51
  # the findings and the phase history survive the sweep. The same recovery
52
- # contract Phase 6 uses when it removes a finished worktree.
52
+ # contract Phase 4 uses when it removes a finished worktree.
53
53
  #
54
54
  # SAFE BY DEFAULT: dry-run. Removes nothing until you pass --yes.
55
55
  #
@@ -271,7 +271,7 @@ if [ "$STATE_ONLY" -eq 0 ] && [ -d "$REPOS" ]; then
271
271
  ph=$(printf '%s' "$row" | cut -f5)
272
272
 
273
273
  # Rule 1 again: landed work is not residue, whichever pass finds it. Order
274
- # matters here - the phase-6/7 test is a proxy for "still waiting", and a
274
+ # matters here - the phase-4/5 test is a proxy for "still waiting", and a
275
275
  # run whose status already says it FINISHED is not waiting for anyone. A
276
276
  # complete run sitting at phase 7 was read as waiting and never reaped,
277
277
  # which is how three finished worktrees held 5.1 GB indefinitely.
@@ -107,7 +107,7 @@ NAME_ARGS=(
107
107
  # `complaint-analysis-*` needs its own entry: find matches the basename against
108
108
  # the whole glob, so `analysis-*` never matched it. Same for the other three,
109
109
  # which were writing into $ROOT with nothing sweeping them (phase-0-init.md,
110
- # phase-6-commit.md, generate-issue.md).
110
+ # phase-4-commit.md, generate-issue.md).
111
111
  #
112
112
  # `*-wiki` is the one loose pattern here, and a suffix glob over $ROOT could
113
113
  # name something a person created. It is narrowed twice: `-type d` above, and
@@ -21,7 +21,7 @@
21
21
  # `.pipeline/` left behind by a finished run stops reading as residue.
22
22
  #
23
23
  # Registered, healthy worktrees are NEVER touched. Finishing a task removes its
24
- # own worktree in Phase 6 (worktree-finalize, v14.1.0+); killing one is
24
+ # own worktree in Phase 4 (worktree-finalize, v14.1.0+); killing one is
25
25
  # /multi-agent:kill. This sweep only reaps orphans neither of those left behind.
26
26
  #
27
27
  # SAFE BY DEFAULT: dry-run. Deletes nothing until you pass --yes.
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @file gen-facts.mjs - the numbers the website is allowed to state, derived.
4
+ *
5
+ * A consumer that keeps its own copy of these numbers has nothing guarding it:
6
+ * a count typed into a page or a README is right on the day it is typed and
7
+ * silent afterwards. `smoke-phase-contract.sh` enforces the same shape inside
8
+ * the pipeline - one producer, everything else reads it.
9
+ *
10
+ * Source of truth per field:
11
+ * phases pipeline/schemas/phases.json - the phase contract itself
12
+ * commandCount a count of pipeline/commands/multi-agent/<name>/SKILL.md
13
+ * skillCount a count of pipeline/skills/shared/external/<name>/SKILL.md
14
+ * skillCountAll every SKILL.md under pipeline/skills, at any depth
15
+ * agentCount a count of pipeline/agents/*.md
16
+ * hosts the CLIs install/ has an adapter for
17
+ * toolCount the toolkit's own tools/list response, when reachable
18
+ * toolCategories that same response, grouped by the prefix in each tool name
19
+ * pluginSkillCount every SKILL.md in the plugin marketplace, when checked out
20
+ * version package.json
21
+ *
22
+ * Counts come from the filesystem at the moment of writing, never from a README
23
+ * or a previous version of this file.
24
+ *
25
+ * Usage:
26
+ * node gen-facts.mjs write docs/facts.json
27
+ * node gen-facts.mjs --check exit 1 if the file on disk is stale
28
+ * node gen-facts.mjs --stdout print, write nothing
29
+ *
30
+ * Exit codes:
31
+ * 0 - written, or up to date
32
+ * 1 - --check found drift
33
+ * 2 - a source could not be read (nothing is written)
34
+ */
35
+
36
+ import { readFileSync, writeFileSync, readdirSync, existsSync } from "node:fs";
37
+ import { join, dirname } from "node:path";
38
+ import { fileURLToPath } from "node:url";
39
+ import { spawnSync } from "node:child_process";
40
+
41
+ const HERE = dirname(fileURLToPath(import.meta.url));
42
+ const ROOT = join(HERE, "..", "..");
43
+ const OUT = join(ROOT, "docs", "facts.json");
44
+
45
+ function die(msg) {
46
+ console.error(`gen-facts: ${msg}`);
47
+ process.exit(2);
48
+ }
49
+
50
+ // Every SKILL.md under a tree, at any depth. `countSkillDirs` only sees the
51
+ // immediate children of one directory, which is the right shape for the
52
+ // external set and the wrong one for the whole tree: the shared/core and
53
+ // per-host mirrors sit a level deeper and were invisible to it.
54
+ function countSkillsDeep(rel) {
55
+ const dir = join(ROOT, rel);
56
+ if (!existsSync(dir)) die(`missing directory: ${rel}`);
57
+ let n = 0;
58
+ const walk = (d) => {
59
+ for (const e of readdirSync(d, { withFileTypes: true })) {
60
+ if (e.isDirectory()) walk(join(d, e.name));
61
+ else if (e.name === "SKILL.md") n += 1;
62
+ }
63
+ };
64
+ walk(dir);
65
+ return n;
66
+ }
67
+
68
+ function countSkillDirs(rel) {
69
+ const dir = join(ROOT, rel);
70
+ if (!existsSync(dir)) die(`missing directory: ${rel}`);
71
+ return readdirSync(dir, { withFileTypes: true }).filter(
72
+ (e) => e.isDirectory() && existsSync(join(dir, e.name, "SKILL.md")),
73
+ ).length;
74
+ }
75
+
76
+ let contract;
77
+ try {
78
+ contract = JSON.parse(readFileSync(join(ROOT, "pipeline", "schemas", "phases.json"), "utf8"));
79
+ } catch (err) {
80
+ die(`cannot read the phase contract - ${err.message}`);
81
+ }
82
+
83
+ let pkg;
84
+ try {
85
+ pkg = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8"));
86
+ } catch (err) {
87
+ die(`cannot read package.json - ${err.message}`);
88
+ }
89
+
90
+ // The toolkit is a separate repo and may not be checked out beside this one.
91
+ // Absent is reported as null rather than as a guess: a site that prints a stale
92
+ // tool count is the problem this file exists to remove, and a made-up one is
93
+ // worse than none.
94
+ let toolCount = null;
95
+ let toolCategories = null;
96
+ let toolkitVersion = null;
97
+ const toolkitDir = join(ROOT, "..", "multi-agent-toolkit-mcp");
98
+ if (existsSync(join(toolkitDir, "package.json"))) {
99
+ try {
100
+ toolkitVersion =
101
+ JSON.parse(readFileSync(join(toolkitDir, "package.json"), "utf8")).version ?? null;
102
+ } catch {
103
+ toolkitVersion = null;
104
+ }
105
+ // Asked of the server, not counted out of its source. Three tool families
106
+ // are defined in modules the main file only spreads in, so a regex over
107
+ // index.js returns a number that looks plausible and is wrong - the first
108
+ // version of this script said 2 when the answer was 99. tools/list is what
109
+ // a client actually receives, which is the number the site should print.
110
+ try {
111
+ const req =
112
+ [
113
+ JSON.stringify({
114
+ jsonrpc: "2.0",
115
+ id: 1,
116
+ method: "initialize",
117
+ params: {
118
+ protocolVersion: "2024-11-05",
119
+ capabilities: {},
120
+ clientInfo: { name: "gen-facts", version: "1" },
121
+ },
122
+ }),
123
+ JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }),
124
+ JSON.stringify({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} }),
125
+ ].join("\n") + "\n";
126
+ const res = spawnSync(process.execPath, [join(toolkitDir, "index.js")], {
127
+ input: req,
128
+ encoding: "utf8",
129
+ timeout: 30000,
130
+ });
131
+ for (const line of (res.stdout || "").split("\n")) {
132
+ if (!line.trim()) continue;
133
+ let msg;
134
+ try {
135
+ msg = JSON.parse(line);
136
+ } catch {
137
+ continue;
138
+ }
139
+ if (msg.id === 2 && Array.isArray(msg.result?.tools)) {
140
+ toolCount = msg.result.tools.length;
141
+ // Grouped by the prefix every tool name carries, from the same
142
+ // response the count comes from. Counting families out of the source
143
+ // gives a different and wrong answer for the reason above, and the
144
+ // site shows the per-family breakdown next to the total, so the two
145
+ // have to come from one place or they will disagree.
146
+ const byFamily = {};
147
+ for (const t of msg.result.tools) {
148
+ const family = String(t.name || "").split("_")[0];
149
+ if (family) byFamily[family] = (byFamily[family] || 0) + 1;
150
+ }
151
+ toolCategories = Object.fromEntries(
152
+ Object.entries(byFamily).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])),
153
+ );
154
+ }
155
+ }
156
+ } catch {
157
+ toolCount = null;
158
+ toolCategories = null;
159
+ }
160
+ }
161
+
162
+ // The plugin marketplace is a third repo, probed the same way and reported as
163
+ // null when it is not checked out beside this one. The site states this number
164
+ // next to the pipeline's own skill counts, and three different skill
165
+ // populations on one page is exactly the shape that drifts.
166
+ let pluginSkillCount = null;
167
+ const pluginsDir = join(ROOT, "..", "multi-agent-plugins", "plugins");
168
+ if (existsSync(pluginsDir)) {
169
+ let n = 0;
170
+ const walk = (d) => {
171
+ for (const e of readdirSync(d, { withFileTypes: true })) {
172
+ if (e.isDirectory()) walk(join(d, e.name));
173
+ else if (e.name === "SKILL.md") n += 1;
174
+ }
175
+ };
176
+ try {
177
+ walk(pluginsDir);
178
+ pluginSkillCount = n;
179
+ } catch {
180
+ pluginSkillCount = null;
181
+ }
182
+ }
183
+
184
+ const facts = {
185
+ $comment:
186
+ "Generated by pipeline/scripts/gen-facts.mjs. Do not hand-edit: the site reads this, and a number edited here instead of at its source is the drift this file removes.",
187
+ generatedAt: new Date().toISOString().slice(0, 10),
188
+ version: pkg.version,
189
+ phaseSchema: contract.phaseSchema,
190
+ phases: contract.phases.map((p) => ({ id: p.id, name: p.name })),
191
+ phaseCount: contract.phases.length,
192
+ modes: Object.fromEntries(Object.entries(contract.modes).map(([k, v]) => [k, v.phases])),
193
+ commandCount: countSkillDirs("pipeline/commands/multi-agent"),
194
+ // Two populations, and conflating them is how the site came to show a stale
195
+ // "212 Skills" chip next to a correct "216 skills" sentence. `skillCount` is
196
+ // the external set a user installs; `skillCountAll` is every SKILL.md the
197
+ // repo ships, mirrors included. Both are stated on the site, so both are
198
+ // generated here rather than counted by hand at the other end.
199
+ skillCount: countSkillDirs("pipeline/skills/shared/external"),
200
+ skillCountAll: countSkillsDeep("pipeline/skills"),
201
+ // The site shows both of these as headline figures and had typed both by
202
+ // hand: a "9 Sub-agents" that happened to be right and a host list that had
203
+ // gone stale, naming Claude Code and Copilot CLI while install/codex.mjs had
204
+ // been shipping a third target for releases.
205
+ agentCount: readdirSync(join(ROOT, "pipeline", "agents")).filter((f) => f.endsWith(".md")).length,
206
+ hosts: readdirSync(join(ROOT, "install"))
207
+ .filter((f) => f.endsWith(".mjs") && !f.startsWith("_") && f !== "index.mjs")
208
+ .map(
209
+ (f) =>
210
+ ({ claude: "Claude Code", copilot: "Copilot CLI", codex: "Codex CLI" })[
211
+ f.replace(".mjs", "")
212
+ ],
213
+ )
214
+ .filter(Boolean)
215
+ .sort(),
216
+ toolCount,
217
+ toolCategories,
218
+ pluginSkillCount,
219
+ toolkitVersion,
220
+ };
221
+
222
+ // JSON.stringify and prettier disagree about short arrays: stringify puts each
223
+ // phase id on its own line, prettier collapses a run that fits. That left this
224
+ // file failing `format:check` after every regeneration, held together only by
225
+ // someone remembering to run prettier by hand - and a generated file that needs
226
+ // a manual step to pass the gate is a generated file that will fail the gate.
227
+ //
228
+ // Importing prettier here is not the fix: this script ships in the published
229
+ // package, where a devDependency is not installed (`n/no-unpublished-import`
230
+ // says so). Collapsing the number runs directly costs nothing and leaves the
231
+ // script dependency-free.
232
+ const collapseShortArrays = (json) =>
233
+ json.replace(
234
+ /\[\n\s+((?:-?\d+|"[^"\n]*")(?:,\n\s+(?:-?\d+|"[^"\n]*"))*)\n\s+\]/g,
235
+ (whole, inner) => {
236
+ const one = `[${inner.split(/,\n\s*/).join(", ")}]`;
237
+ // Prettier only collapses what fits the print width; leave the rest alone
238
+ // rather than guess, so the two can never disagree in the other direction.
239
+ return one.length <= 72 ? one : whole;
240
+ },
241
+ );
242
+
243
+ const body = collapseShortArrays(`${JSON.stringify(facts, null, 2)}\n`);
244
+
245
+ if (process.argv.includes("--stdout")) {
246
+ process.stdout.write(body);
247
+ process.exit(0);
248
+ }
249
+
250
+ if (process.argv.includes("--check")) {
251
+ if (!existsSync(OUT)) {
252
+ console.error("gen-facts: docs/facts.json does not exist - run without --check");
253
+ process.exit(1);
254
+ }
255
+ const onDisk = JSON.parse(readFileSync(OUT, "utf8"));
256
+ const fresh = JSON.parse(body);
257
+ // generatedAt is a timestamp, not a fact: comparing it would fail every day.
258
+ delete onDisk.generatedAt;
259
+ delete fresh.generatedAt;
260
+ if (JSON.stringify(onDisk) !== JSON.stringify(fresh)) {
261
+ console.error(
262
+ "gen-facts: docs/facts.json is stale - run: node pipeline/scripts/gen-facts.mjs",
263
+ );
264
+ for (const k of Object.keys(fresh)) {
265
+ if (JSON.stringify(onDisk[k]) !== JSON.stringify(fresh[k])) {
266
+ console.error(
267
+ ` ${k}: on disk ${JSON.stringify(onDisk[k])}, derived ${JSON.stringify(fresh[k])}`,
268
+ );
269
+ }
270
+ }
271
+ process.exit(1);
272
+ }
273
+ console.log("gen-facts: docs/facts.json is current");
274
+ process.exit(0);
275
+ }
276
+
277
+ writeFileSync(OUT, body);
278
+ console.log(
279
+ `gen-facts: wrote docs/facts.json (${facts.phaseCount} phases, ${facts.commandCount} commands, ${facts.skillCount} skills, ${facts.toolCount ?? "?"} tools)`,
280
+ );
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
2
3
  /**
3
4
  * gen-mode-dispatch.mjs - emit the canonical Phase Tracker Contract block
4
5
  * for a given mode dispatch file (full / local / autopilot / analysis).
@@ -10,10 +11,10 @@
10
11
  * call, anti-pattern warning, and ref link are identical.
11
12
  *
12
13
  * Usage:
13
- * node pipeline/scripts/gen-mode-dispatch.mjs --mode=full # 0..7
14
- * node pipeline/scripts/gen-mode-dispatch.mjs --mode=local # 0/1/2/3/4/6/7 + local-mode caveat
15
- * node pipeline/scripts/gen-mode-dispatch.mjs --mode=autopilot # 0/1/2/3/4/6/7
16
- * node pipeline/scripts/gen-mode-dispatch.mjs --mode=analysis # 0/1/2/4/6/7 (no Dev, no Test)
14
+ * node pipeline/scripts/gen-mode-dispatch.mjs --mode=full # 0..5
15
+ * node pipeline/scripts/gen-mode-dispatch.mjs --mode=local # 0..5 + local-mode caveat
16
+ * node pipeline/scripts/gen-mode-dispatch.mjs --mode=autopilot # 0..5
17
+ * node pipeline/scripts/gen-mode-dispatch.mjs --mode=analysis # 0/1/3/4/5 (no Dev)
17
18
  *
18
19
  * v16.0.0 removed the four dev-* modes. Depth is no longer a command name: the
19
20
  * Phase 0 Step 7.5 picker asks Full or Short and sets `state.onlyDevelop`. That
@@ -35,22 +36,20 @@ const args = Object.fromEntries(
35
36
 
36
37
  const MODE = args.mode;
37
38
 
38
- const PHASE_NAMES = [
39
- "0:Init",
40
- "1:Analysis",
41
- "2:Planning",
42
- "3:Dev",
43
- "4:Review",
44
- "5:Test",
45
- "6:Commit",
46
- "7:Report",
47
- ];
48
-
49
- // Phase 5 (User Test) checks out the change from the git worktree and hands it
50
- // to the user interactively. It therefore only runs when the mode is BOTH
51
- // interactive (not autopilot) AND worktree-backed (not local), which since
52
- // v16.0.0 leaves exactly one command: `/multi-agent` itself.
53
- const PHASE_NAMES_NO_TEST = PHASE_NAMES.filter((p) => p !== "5:Test");
39
+ // The phase list has one owner: schemas/phases.json. This file used to restate
40
+ // it, which is how eight independent copies came to exist. smoke-phase-contract.sh
41
+ // holds every remaining copy to the same file.
42
+ const CONTRACT = JSON.parse(
43
+ readFileSync(new URL("../schemas/phases.json", import.meta.url), "utf8"),
44
+ );
45
+ const PHASES = CONTRACT.phases;
46
+ const modePhases = (name) =>
47
+ CONTRACT.modes[name].phases.map((id) => {
48
+ const p = PHASES.find((x) => x.id === id);
49
+ return `${p.id}:${p.name}`;
50
+ });
51
+
52
+ const PHASE_NAMES = PHASES.map((p) => `${p.id}:${p.name}`);
54
53
 
55
54
  /**
56
55
  * Mode → phases + flag descriptor. The descriptor controls the autopilot /
@@ -59,21 +58,17 @@ const PHASE_NAMES_NO_TEST = PHASE_NAMES.filter((p) => p !== "5:Test");
59
58
  * `smoke-mode-dispatch-drift.sh` can byte-equal-diff each on-disk section
60
59
  * against its generator output.
61
60
  */
62
- const MODES = {
63
- autopilot: { phases: PHASE_NAMES_NO_TEST, local: false, autopilot: true },
64
- full: { phases: PHASE_NAMES, local: false, autopilot: false, depth: true },
65
- local: { phases: PHASE_NAMES_NO_TEST, local: true, autopilot: false, depth: true },
66
- "local-autopilot": { phases: PHASE_NAMES_NO_TEST, local: true, autopilot: true },
67
- "full-local": { phases: PHASE_NAMES_NO_TEST, local: true, autopilot: false, depth: true },
68
- // Analysis produces a document, not code: no Dev, no Test, and Phase 6
69
- // publishes instead of committing. Same 8-phase contract, four of them
70
- // reinterpreted.
71
- analysis: {
72
- phases: ["0:Init", "1:Analysis", "2:Planning", "4:Review", "6:Commit", "7:Report"],
73
- local: false,
74
- autopilot: false,
75
- },
76
- };
61
+ const MODES = Object.fromEntries(
62
+ Object.entries(CONTRACT.modes).map(([name, m]) => [
63
+ name,
64
+ {
65
+ phases: modePhases(name),
66
+ local: m.local,
67
+ autopilot: m.autopilot,
68
+ ...(m.depth ? { depth: true } : {}),
69
+ },
70
+ ]),
71
+ );
77
72
 
78
73
  const spec = MODE ? MODES[MODE] : null;
79
74
  if (!spec) {
@@ -101,7 +96,7 @@ const modeLabel = MODE_LABELS[MODE] ?? `\`${MODE}\``;
101
96
  const skipNote =
102
97
  skippedIds.length > 0
103
98
  ? `${modeLabel} mode does NOT TaskCreate phases ${skippedIds.join("/")} - those are not part of the ${modeLabel} phase set (\`${spec.phases.join(" ")}\`). Only register tiles for the active set.`
104
- : `${modeLabel} mode TaskCreates all 8 phases (no phase is skipped).`;
99
+ : `${modeLabel} mode TaskCreates all ${PHASES.length} phases (no phase is skipped).`;
105
100
 
106
101
  const orderingNote = `**All TaskCreate calls in a batch fire in strict phase-number order BEFORE any TaskUpdate is applied.** For ${modeLabel} that means: ${spec.depth ? `Phase 0 at Step -1, then the rest in ascending order at Step 7.5 (${phaseSequence} minus whatever the depth answer drops)` : phaseSequence}. The native widget renders by creation order, not by phase number - out-of-order calls produce visually scrambled tile stacks. Full ordering contract in \`$HOME/.claude/multi-agent-refs/tracker-contract.md\` section "TaskCreate ordering (strict)".`;
107
102
 
@@ -119,7 +114,7 @@ const banner = spec.autopilot
119
114
  // tiles beside the question that decides whether two of them run is the failure this
120
115
  // split exists to remove.
121
116
  const fullSet = spec.phases.filter((p) => p !== "0:Init");
122
- const shortSet = fullSet.filter((p) => Number(p.split(":")[0]) >= 3);
117
+ const shortSet = fullSet.filter((p) => Number(p.split(":")[0]) >= 2);
123
118
  const loopFor = (list) =>
124
119
  `for p in ${list.map((x) => `"${x}"`).join(" ")}; do\n bash $HOME/.claude/scripts/phase-tracker.sh add "\${p%%:*}" "\${p#*:}"\ndone`;
125
120
 
@@ -13,7 +13,7 @@
13
13
  //
14
14
  // Section level is DERIVED, not assumed. These files are not consistent: some
15
15
  // open at `##` and section at `###` (keychain.md), others open at `###` and
16
- // section at `##` (phases/phase-3-dev.md). The rule is the shallowest heading
16
+ // section at `##` (phases/phase-2-dev.md). The rule is the shallowest heading
17
17
  // depth that occurs more than once - a depth used exactly once is a title, not
18
18
  // a section level.
19
19
  //
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * @file graph-report.mjs - GRAPH_REPORT.md, the human-and-model readable summary.
5
5
  *
6
- * Phase 1 reads this before deciding how wide to explore, and Phase 7 ships it
6
+ * Phase 1 reads this before deciding how wide to explore, and Phase 5 ships it
7
7
  * next to the knowledge base. Sections are ordered by how much they narrow a
8
8
  * search: hubs first (the types everything touches), then the module map, then
9
9
  * the long tail that is usually safe to ignore.
@@ -5,7 +5,7 @@
5
5
  #
6
6
  # Two callers need this and they used to carry their own curl: the issue
7
7
  # generator (`generate-issue.md` step 12) and visual evidence (Phase 3 / Phase 5
8
- # captures rendered into the Phase 7 Jira comment). Two copies of an upload is
8
+ # captures rendered into the Phase 5 Jira comment). Two copies of an upload is
9
9
  # two places for the `X-Atlassian-Token: no-check` header to go missing, which
10
10
  # fails as a 403 that reads like an auth problem.
11
11
  #
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * WHY THIS EXISTS
6
6
  *
7
- * Phase 7 asks a subagent to read the run log and propose a few memory entries.
7
+ * Phase 5 asks a subagent to read the run log and propose a few memory entries.
8
8
  * That works for what the model noticed, and it is structurally blind to what it
9
9
  * did not: the same wrong path tried in three sessions, the command that fails
10
10
  * every time until its `--flag` variant is used, the directory that never has
@@ -87,9 +87,9 @@ for (const e of events) {
87
87
  }
88
88
  if (e.ts && (!t.firstTs || e.ts < t.firstTs)) t.firstTs = e.ts;
89
89
  const d = e.details || {};
90
- // phase-4-review.md's actual review.completed emission (raw_count=/
90
+ // phase-3-review.md's actual review.completed emission (raw_count=/
91
91
  // accepted=/deferred=/rejected=/approved=/duration_ms=) carries no
92
- // review_cycles field - that name only appears on Phase 7's task.completed
92
+ // review_cycles field - that name only appears on Phase 5's task.completed
93
93
  // summary event, which this never matched. Phase 4 re-runs (and logs its
94
94
  // own review.completed) once per rework cycle, so the cycle count for a
95
95
  // task is how many review.completed events share its task_id - the same
@@ -6,11 +6,19 @@
6
6
  # {
7
7
  # "ts": "<ISO 8601 timestamp>",
8
8
  # "task_id": "<jira id, github issue, or run id>",
9
- # "phase": "<0..7>",
9
+ # "phase": "<0..5>",
10
10
  # "event": "<event name>",
11
- # "details": { ...arbitrary key/value pairs... }
11
+ # "details": { ...arbitrary key/value pairs... },
12
+ # "phaseSchema": 2
12
13
  # }
13
14
  #
15
+ # phaseSchema names the phase vocabulary the line was written under, because
16
+ # this file is append-only and v19.0.0 renumbered the phases. A line with
17
+ # phase 3 means Dev under schema 1 and Review under schema 2. Lines with no
18
+ # phaseSchema field predate v19.0.0 and are schema 1. Aggregators pick their
19
+ # name table from this field; without it the two vocabularies are
20
+ # indistinguishable and every historical roll-up is quietly wrong.
21
+ #
14
22
  # Usage:
15
23
  # log-metric.sh <task_id> <phase> <event> [key=value ...]
16
24
  #
@@ -18,7 +26,7 @@
18
26
  # log-metric.sh PROJ-1234 4 review.completed raw_count=8 accepted=3 deferred=2 rejected=3 approved=true duration_ms=42000
19
27
  # log-metric.sh PROJ-1234 4 triage.edge_case case=high-rejection-rate
20
28
  # log-metric.sh PROJ-1234 3 rework.started iteration=2 accepted_blocking=2
21
- # log-metric.sh #316 7 task.completed phases=8 review_cycles=2 lang=tr
29
+ # log-metric.sh #316 5 task.completed phases=6 review_cycles=2 lang=tr
22
30
  #
23
31
  # Atomic append: each call uses `>> "$file"` which the kernel guarantees
24
32
  # atomic for writes ≤ PIPE_BUF (4 KB on macOS/Linux). One JSONL line is well
@@ -64,8 +72,13 @@ for kv in "$@"; do
64
72
  done
65
73
  DETAILS="${DETAILS}}"
66
74
 
75
+ # Read from the phase contract rather than hardcoding: the next renumbering
76
+ # bumps one JSON field instead of hunting for a literal in a shell script.
77
+ PHASE_SCHEMA="$(node -e 'const c=require("'"$(cd "$(dirname "$0")" && pwd)"'/../schemas/phases.json"); process.stdout.write(String(c.phaseSchema))' 2>/dev/null)"
78
+ [ -n "$PHASE_SCHEMA" ] || PHASE_SCHEMA=2
79
+
67
80
  TS="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
68
- LINE="{\"ts\":\"${TS}\",\"task_id\":\"${TASK_ID}\",\"phase\":\"${PHASE}\",\"event\":\"${EVENT}\",\"details\":${DETAILS}}"
81
+ LINE="{\"ts\":\"${TS}\",\"task_id\":\"${TASK_ID}\",\"phase\":\"${PHASE}\",\"event\":\"${EVENT}\",\"details\":${DETAILS},\"phaseSchema\":${PHASE_SCHEMA}}"
69
82
 
70
83
  # Atomic append (one line, well under PIPE_BUF).
71
84
  echo "$LINE" >> "$METRICS_FILE" 2>/dev/null || {
@@ -2,7 +2,7 @@
2
2
  # memory-save.sh - v6.2.E
3
3
  #
4
4
  # Writes a single per-repo memory file and updates the MEMORY.md index.
5
- # Called by Phase 7's synthesis step (or any user skill that wants to
5
+ # Called by Phase 5's synthesis step (or any user skill that wants to
6
6
  # persist a finding). Opt-in via prefs.global.perRepoMemory.
7
7
  #
8
8
  # Usage:
@@ -18,9 +18,26 @@ import { runMain } from "../lib/fatal.mjs";
18
18
 
19
19
  const DEFAULT_FILE = path.join(os.homedir(), ".claude", "multi-agent-preferences.json");
20
20
 
21
- // Single source of truth for the migration target version. Referenced by the
22
- // migrate() bump chain AND every user-facing message, so they cannot drift.
23
- const TARGET_VERSION = "2.6.0";
21
+ // The migration target, read from the schema rather than written here. It was
22
+ // a literal, and a literal is how it came to say 2.6.0 while the schema had
23
+ // moved to 2.7.0 - the runner then reported every current file as needing a
24
+ // migration it had already had. Referenced by the migrate() bump chain AND
25
+ // every user-facing message, so they cannot drift from each other either.
26
+ const TARGET_VERSION = (() => {
27
+ const schemaPath = path.join(
28
+ path.dirname(fileURLToPath(import.meta.url)),
29
+ "..",
30
+ "schemas",
31
+ "prefs.schema.json",
32
+ );
33
+ const sv = JSON.parse(fs.readFileSync(schemaPath, "utf-8"))?.properties?.schemaVersion;
34
+ const v = sv?.const ?? sv?.enum?.at(-1);
35
+ if (!v) {
36
+ console.error("migrate-prefs: prefs.schema.json declares no schemaVersion");
37
+ process.exit(1);
38
+ }
39
+ return v;
40
+ })();
24
41
 
25
42
  const USAGE = "Usage: migrate-prefs.mjs [--dry-run] [--file <path>]";
26
43
 
@@ -327,7 +344,7 @@ function migrate(prefs) {
327
344
  out.global.routines = [];
328
345
  changes.push("added empty routines (v2.4.0 user-defined /multi-agent:save routines)");
329
346
  }
330
- // v2.5.0 / v14.0.0: Phase 4 Review entered the --dev phase sets and Step 1.78
347
+ // v2.5.0 / v14.0.0: Phase 3 Review entered the --dev phase sets and Step 1.78
331
348
  // (criteria resolution) landed. Default false because only Swift plus the two
332
349
  // store-compliance catalogs ship a scoped registry today, so blocking on a
333
350
  // coverage gap would stop every Kotlin/Python/Node/ObjC run from day one. The
@@ -488,7 +505,7 @@ function migrateReportChannels(out, changes) {
488
505
  if (typeof g.autopilotReportTimeoutSeconds !== "number") {
489
506
  g.autopilotReportTimeoutSeconds = 1800;
490
507
  changes.push(
491
- "added autopilotReportTimeoutSeconds: 1800 (30 min) - Phase 7 always-pause timeout",
508
+ "added autopilotReportTimeoutSeconds: 1800 (30 min) - Phase 5 always-pause timeout",
492
509
  );
493
510
  }
494
511
 
@@ -12,9 +12,9 @@
12
12
  # phase-banner.sh label <phase> (print canonical name)
13
13
  #
14
14
  # Examples:
15
- # phase-banner.sh start 4 "Review" "deterministic gates + parallel + triage"
16
- # phase-banner.sh start 4 auto # resolves to "Review" (en) or "İnceleme" (tr)
17
- # PHASE_LANG=tr phase-banner.sh start 4 auto # "Faz 4: İnceleme"
15
+ # phase-banner.sh start 3 "Review" "parallel + triage + user test"
16
+ # phase-banner.sh start 3 auto # resolves to "Review" (en) or "İnceleme" (tr)
17
+ # PHASE_LANG=tr phase-banner.sh start 3 auto # "Faz 3: İnceleme"
18
18
  #
19
19
  # Status values for `end`: done | failed | skipped
20
20
  #
@@ -32,28 +32,28 @@ set -uo pipefail
32
32
 
33
33
  PHASE_LANG="${PHASE_LANG:-en}"
34
34
 
35
- # Canonical phase label table. Keep synced with
36
- # pipeline/multi-agent-refs/phases.md and the table in
37
- # pipeline/skills/shared/multi-agent/SKILL.md.
35
+ # Canonical phase label table. The English half is checked against
36
+ # pipeline/schemas/phases.json by smoke-phase-contract.sh, so it cannot drift
37
+ # from the contract in silence the way it did through the eight-phase era. It
38
+ # stays a case statement rather than a jq read because this runs on every
39
+ # banner and the pipeline's zero-dependency rule (ADR-0004) applies to the hot
40
+ # path. The Turkish half has no contract entry - labels are translations, not
41
+ # identifiers - and is kept beside it.
38
42
  phase_label() {
39
43
  local phase="$1" lang="${2:-en}"
40
44
  case "$lang:$phase" in
41
45
  en:0) echo "Init" ;;
42
- en:1) echo "Analysis" ;;
43
- en:2) echo "Planning" ;;
44
- en:3) echo "Dev" ;;
45
- en:4) echo "Review" ;;
46
- en:5) echo "Test" ;;
47
- en:6) echo "Commit & PR" ;;
48
- en:7) echo "Report" ;;
46
+ en:1) echo "Plan" ;;
47
+ en:2) echo "Dev" ;;
48
+ en:3) echo "Review" ;;
49
+ en:4) echo "Commit & PR" ;;
50
+ en:5) echo "Report" ;;
49
51
  tr:0) echo "Başlangıç" ;;
50
- tr:1) echo "Analiz" ;;
51
- tr:2) echo "Planlama" ;;
52
- tr:3) echo "Geliştirme" ;;
53
- tr:4) echo "İnceleme" ;;
54
- tr:5) echo "Kullanıcı Testi" ;;
55
- tr:6) echo "Commit & PR" ;;
56
- tr:7) echo "Rapor" ;;
52
+ tr:1) echo "Plan" ;;
53
+ tr:2) echo "Geliştirme" ;;
54
+ tr:3) echo "İnceleme" ;;
55
+ tr:4) echo "Commit & PR" ;;
56
+ tr:5) echo "Rapor" ;;
57
57
  *) echo "" ;;
58
58
  esac
59
59
  }