@mmerterden/multi-agent-pipeline 12.7.0 → 12.9.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 (247) hide show
  1. package/CHANGELOG.md +207 -0
  2. package/install/_common.mjs +48 -0
  3. package/install/_dev-only-files.mjs +125 -5
  4. package/install/claude.mjs +14 -8
  5. package/install/copilot.mjs +5 -8
  6. package/package.json +17 -2
  7. package/pipeline/commands/multi-agent/analysis/SKILL.md +1 -1
  8. package/pipeline/lib/credential-store.sh +20 -0
  9. package/pipeline/multi-agent-refs/_account-picker.md +1 -1
  10. package/pipeline/multi-agent-refs/_dev-context.md +1 -1
  11. package/pipeline/multi-agent-refs/_repo-picker.md +1 -1
  12. package/pipeline/multi-agent-refs/features/review-multi-repo.md +65 -0
  13. package/pipeline/multi-agent-refs/features/url-enrichment.md +93 -0
  14. package/pipeline/multi-agent-refs/phases/operations.md +28 -0
  15. package/pipeline/multi-agent-refs/phases/phase-0-init.md +3 -83
  16. package/pipeline/multi-agent-refs/phases/phase-2-planning.md +1 -1
  17. package/pipeline/multi-agent-refs/phases/phase-3-dev.md +1 -2
  18. package/pipeline/multi-agent-refs/phases/phase-4-review.md +50 -62
  19. package/pipeline/schemas/prefs.schema.json +6 -0
  20. package/pipeline/scripts/_smoke-root.sh +61 -0
  21. package/pipeline/scripts/agent-guard.py +57 -3
  22. package/pipeline/scripts/audit-log.sh +25 -0
  23. package/pipeline/scripts/smoke-cross-cli-behavior.sh +21 -7
  24. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-1-analysis.json +0 -25
  25. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-2-plan.json +0 -30
  26. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-review.json +0 -20
  27. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-triage.json +0 -15
  28. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/metadata.json +0 -14
  29. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/task.json +0 -12
  30. package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-1-analysis.json +0 -29
  31. package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-2-plan.json +0 -43
  32. package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-review.json +0 -35
  33. package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-triage.json +0 -35
  34. package/pipeline/eval/golden-tasks/02-android-feature-compose/metadata.json +0 -14
  35. package/pipeline/eval/golden-tasks/02-android-feature-compose/task.json +0 -12
  36. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-1-analysis.json +0 -29
  37. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-2-plan.json +0 -42
  38. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-review.json +0 -20
  39. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-triage.json +0 -15
  40. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/metadata.json +0 -14
  41. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/task.json +0 -12
  42. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-1-analysis.json +0 -29
  43. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-2-plan.json +0 -40
  44. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-review.json +0 -20
  45. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-triage.json +0 -15
  46. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/metadata.json +0 -14
  47. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/task.json +0 -12
  48. package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-1-analysis.json +0 -29
  49. package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-2-plan.json +0 -42
  50. package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-review.json +0 -28
  51. package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-triage.json +0 -27
  52. package/pipeline/eval/golden-tasks/05-ios-security-keychain/metadata.json +0 -14
  53. package/pipeline/eval/golden-tasks/05-ios-security-keychain/task.json +0 -12
  54. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-1-analysis.json +0 -29
  55. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-2-plan.json +0 -41
  56. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-review.json +0 -12
  57. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-triage.json +0 -6
  58. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/metadata.json +0 -14
  59. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/task.json +0 -12
  60. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-1-analysis.json +0 -29
  61. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-2-plan.json +0 -42
  62. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-review.json +0 -28
  63. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-triage.json +0 -27
  64. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/metadata.json +0 -14
  65. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/task.json +0 -12
  66. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-1-analysis.json +0 -25
  67. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-2-plan.json +0 -31
  68. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-review.json +0 -12
  69. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-triage.json +0 -18
  70. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/metadata.json +0 -14
  71. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/task.json +0 -12
  72. package/pipeline/eval/golden-tasks/README.md +0 -65
  73. package/pipeline/eval/intent-cases.json +0 -40
  74. package/pipeline/eval/run-metrics-fixture.json +0 -93
  75. package/pipeline/eval/triage/01-empty-findings/expected.json +0 -6
  76. package/pipeline/eval/triage/01-empty-findings/input.json +0 -5
  77. package/pipeline/eval/triage/01-empty-findings/notes.md +0 -7
  78. package/pipeline/eval/triage/02-real-blocker/expected.json +0 -15
  79. package/pipeline/eval/triage/02-real-blocker/input.json +0 -14
  80. package/pipeline/eval/triage/02-real-blocker/notes.md +0 -7
  81. package/pipeline/eval/triage/03-out-of-scope-defer/expected.json +0 -18
  82. package/pipeline/eval/triage/03-out-of-scope-defer/input.json +0 -14
  83. package/pipeline/eval/triage/03-out-of-scope-defer/notes.md +0 -10
  84. package/pipeline/eval/triage/04-false-positive-reject/expected.json +0 -18
  85. package/pipeline/eval/triage/04-false-positive-reject/input.json +0 -14
  86. package/pipeline/eval/triage/04-false-positive-reject/notes.md +0 -10
  87. package/pipeline/eval/triage/05-mixed-classification/expected.json +0 -43
  88. package/pipeline/eval/triage/05-mixed-classification/input.json +0 -38
  89. package/pipeline/eval/triage/05-mixed-classification/notes.md +0 -17
  90. package/pipeline/eval/triage/06-severity-mismatch/expected.json +0 -15
  91. package/pipeline/eval/triage/06-severity-mismatch/input.json +0 -14
  92. package/pipeline/eval/triage/06-severity-mismatch/notes.md +0 -9
  93. package/pipeline/eval/triage/07-duplicate-reviewers/expected.json +0 -27
  94. package/pipeline/eval/triage/07-duplicate-reviewers/input.json +0 -22
  95. package/pipeline/eval/triage/07-duplicate-reviewers/notes.md +0 -9
  96. package/pipeline/eval/triage/08-style-misclassified/expected.json +0 -18
  97. package/pipeline/eval/triage/08-style-misclassified/input.json +0 -14
  98. package/pipeline/eval/triage/08-style-misclassified/notes.md +0 -9
  99. package/pipeline/eval/triage/09-cascading-finding/expected.json +0 -23
  100. package/pipeline/eval/triage/09-cascading-finding/input.json +0 -22
  101. package/pipeline/eval/triage/09-cascading-finding/notes.md +0 -9
  102. package/pipeline/eval/triage/10-deferred-crossref/expected.json +0 -18
  103. package/pipeline/eval/triage/10-deferred-crossref/input.json +0 -14
  104. package/pipeline/eval/triage/10-deferred-crossref/notes.md +0 -9
  105. package/pipeline/eval/triage/11-vercel-token-leak-blocker/expected.json +0 -27
  106. package/pipeline/eval/triage/11-vercel-token-leak-blocker/input.json +0 -22
  107. package/pipeline/eval/triage/11-vercel-token-leak-blocker/notes.md +0 -14
  108. package/pipeline/eval/triage/README.md +0 -54
  109. package/pipeline/scripts/benchmark-phase-0.sh +0 -128
  110. package/pipeline/scripts/check-md-links.mjs +0 -88
  111. package/pipeline/scripts/eval-golden-tasks-live.mjs +0 -302
  112. package/pipeline/scripts/eval-golden-tasks.mjs +0 -224
  113. package/pipeline/scripts/eval-intent.mjs +0 -107
  114. package/pipeline/scripts/eval-mine-corpus.mjs +0 -211
  115. package/pipeline/scripts/eval-triage.mjs +0 -171
  116. package/pipeline/scripts/fixtures/diff-risk-android.diff +0 -40
  117. package/pipeline/scripts/fixtures/diff-risk-ios.diff +0 -48
  118. package/pipeline/scripts/fixtures/diff-risk-test-removal.diff +0 -40
  119. package/pipeline/scripts/fixtures/install-layout.tsv +0 -19
  120. package/pipeline/scripts/fixtures/pack-expected-count.txt +0 -1
  121. package/pipeline/scripts/fixtures/test-gap-node.diff +0 -30
  122. package/pipeline/scripts/fixtures/test-gap-python.diff +0 -32
  123. package/pipeline/scripts/lint-mcp-refs.mjs +0 -218
  124. package/pipeline/scripts/lint-skills.mjs +0 -154
  125. package/pipeline/scripts/run-smokes.mjs +0 -130
  126. package/pipeline/scripts/scorecard.mjs +0 -258
  127. package/pipeline/scripts/smoke-add-detail.sh +0 -137
  128. package/pipeline/scripts/smoke-agent-guard.sh +0 -74
  129. package/pipeline/scripts/smoke-agent-log-cost.sh +0 -262
  130. package/pipeline/scripts/smoke-agent-model-routing.sh +0 -87
  131. package/pipeline/scripts/smoke-ask-choice.sh +0 -42
  132. package/pipeline/scripts/smoke-autopilot-circuit-breaker.sh +0 -36
  133. package/pipeline/scripts/smoke-bitbucket-contract.sh +0 -255
  134. package/pipeline/scripts/smoke-changelog-version.sh +0 -47
  135. package/pipeline/scripts/smoke-channels-approval-gate.sh +0 -60
  136. package/pipeline/scripts/smoke-channels-flow.sh +0 -130
  137. package/pipeline/scripts/smoke-ci-workflows.sh +0 -88
  138. package/pipeline/scripts/smoke-clarify.sh +0 -148
  139. package/pipeline/scripts/smoke-command-inventory.sh +0 -81
  140. package/pipeline/scripts/smoke-commands-skills-parity.sh +0 -87
  141. package/pipeline/scripts/smoke-community-gates.sh +0 -75
  142. package/pipeline/scripts/smoke-compliance-skills.sh +0 -119
  143. package/pipeline/scripts/smoke-config-hygiene.sh +0 -58
  144. package/pipeline/scripts/smoke-context-budget.sh +0 -72
  145. package/pipeline/scripts/smoke-cost-budget.sh +0 -70
  146. package/pipeline/scripts/smoke-cost-summary.sh +0 -139
  147. package/pipeline/scripts/smoke-cross-phase-cohesion.sh +0 -128
  148. package/pipeline/scripts/smoke-description-tr.sh +0 -82
  149. package/pipeline/scripts/smoke-dev-critic.sh +0 -144
  150. package/pipeline/scripts/smoke-diff-explain.sh +0 -147
  151. package/pipeline/scripts/smoke-diff-risk.sh +0 -190
  152. package/pipeline/scripts/smoke-dynamic-skill-loading.sh +0 -160
  153. package/pipeline/scripts/smoke-eval-live.sh +0 -136
  154. package/pipeline/scripts/smoke-evidence-gate.sh +0 -93
  155. package/pipeline/scripts/smoke-extract-conventions.sh +0 -163
  156. package/pipeline/scripts/smoke-fetchers-offline.sh +0 -448
  157. package/pipeline/scripts/smoke-figma-dispatch.sh +0 -112
  158. package/pipeline/scripts/smoke-gate-hooks.sh +0 -74
  159. package/pipeline/scripts/smoke-gc-tmp.sh +0 -130
  160. package/pipeline/scripts/smoke-gc-worktrees.sh +0 -125
  161. package/pipeline/scripts/smoke-generate-issue.sh +0 -120
  162. package/pipeline/scripts/smoke-handoff-contract.sh +0 -92
  163. package/pipeline/scripts/smoke-identity-isolation.sh +0 -70
  164. package/pipeline/scripts/smoke-install-layout.sh +0 -248
  165. package/pipeline/scripts/smoke-intent-guard.sh +0 -86
  166. package/pipeline/scripts/smoke-issue-comment-template.sh +0 -86
  167. package/pipeline/scripts/smoke-issue-jira-triad.sh +0 -120
  168. package/pipeline/scripts/smoke-keychain.sh +0 -158
  169. package/pipeline/scripts/smoke-language-axis.sh +0 -109
  170. package/pipeline/scripts/smoke-learning-curve.sh +0 -61
  171. package/pipeline/scripts/smoke-learnings-ledger.sh +0 -86
  172. package/pipeline/scripts/smoke-lib-scripts.sh +0 -448
  173. package/pipeline/scripts/smoke-mcp-gate.sh +0 -68
  174. package/pipeline/scripts/smoke-md-links.sh +0 -8
  175. package/pipeline/scripts/smoke-md2confluence.sh +0 -126
  176. package/pipeline/scripts/smoke-metrics-cache-ratio.sh +0 -72
  177. package/pipeline/scripts/smoke-migrate-state.sh +0 -102
  178. package/pipeline/scripts/smoke-mode-dispatch-drift.sh +0 -161
  179. package/pipeline/scripts/smoke-model-fallback.sh +0 -89
  180. package/pipeline/scripts/smoke-multi-repo-integration.sh +0 -116
  181. package/pipeline/scripts/smoke-multi-repo-worktree.sh +0 -61
  182. package/pipeline/scripts/smoke-no-mcp-in-dev-phases.sh +0 -194
  183. package/pipeline/scripts/smoke-no-token-prompt.sh +0 -85
  184. package/pipeline/scripts/smoke-own-punctuation.sh +0 -103
  185. package/pipeline/scripts/smoke-pack-contents.sh +0 -140
  186. package/pipeline/scripts/smoke-pat-audit.sh +0 -128
  187. package/pipeline/scripts/smoke-per-repo-memory.sh +0 -156
  188. package/pipeline/scripts/smoke-phase-0-multi-repo.sh +0 -170
  189. package/pipeline/scripts/smoke-phase-6-multi.sh +0 -79
  190. package/pipeline/scripts/smoke-phase-banner.sh +0 -101
  191. package/pipeline/scripts/smoke-phase-tracker.sh +0 -324
  192. package/pipeline/scripts/smoke-phase0-bridge-contract.sh +0 -241
  193. package/pipeline/scripts/smoke-phase4-gates.sh +0 -45
  194. package/pipeline/scripts/smoke-phase4-triage.sh +0 -229
  195. package/pipeline/scripts/smoke-plan-approval-gate.sh +0 -71
  196. package/pipeline/scripts/smoke-plan-safety.sh +0 -139
  197. package/pipeline/scripts/smoke-plan-todos.sh +0 -196
  198. package/pipeline/scripts/smoke-pr-review-actions.sh +0 -152
  199. package/pipeline/scripts/smoke-pre-commit.sh +0 -170
  200. package/pipeline/scripts/smoke-pref-migration.sh +0 -226
  201. package/pipeline/scripts/smoke-prefs-language.sh +0 -134
  202. package/pipeline/scripts/smoke-progress-contract.sh +0 -127
  203. package/pipeline/scripts/smoke-prune-logs.sh +0 -137
  204. package/pipeline/scripts/smoke-purge.sh +0 -138
  205. package/pipeline/scripts/smoke-push-retry.sh +0 -75
  206. package/pipeline/scripts/smoke-repo-map.sh +0 -300
  207. package/pipeline/scripts/smoke-review-readiness.sh +0 -92
  208. package/pipeline/scripts/smoke-review-watch.sh +0 -146
  209. package/pipeline/scripts/smoke-routines.sh +0 -84
  210. package/pipeline/scripts/smoke-run-aggregator.sh +0 -216
  211. package/pipeline/scripts/smoke-run-metrics.sh +0 -50
  212. package/pipeline/scripts/smoke-search.sh +0 -187
  213. package/pipeline/scripts/smoke-shadow-git.sh +0 -224
  214. package/pipeline/scripts/smoke-skill-authoring.sh +0 -137
  215. package/pipeline/scripts/smoke-skill-language.sh +0 -83
  216. package/pipeline/scripts/smoke-skill-manifest.sh +0 -138
  217. package/pipeline/scripts/smoke-skill-scan.sh +0 -198
  218. package/pipeline/scripts/smoke-source-parity.sh +0 -85
  219. package/pipeline/scripts/smoke-subagent-validators.sh +0 -108
  220. package/pipeline/scripts/smoke-sync-parity.sh +0 -92
  221. package/pipeline/scripts/smoke-tasklist-ordering.sh +0 -112
  222. package/pipeline/scripts/smoke-telemetry.sh +0 -147
  223. package/pipeline/scripts/smoke-test-gap.sh +0 -183
  224. package/pipeline/scripts/smoke-token-budget.sh +0 -67
  225. package/pipeline/scripts/smoke-token-preflight.sh +0 -82
  226. package/pipeline/scripts/smoke-tracker-contract.sh +0 -191
  227. package/pipeline/scripts/smoke-tracker-tokens-invocation.sh +0 -73
  228. package/pipeline/scripts/smoke-triage-memory.sh +0 -174
  229. package/pipeline/scripts/smoke-update-check.sh +0 -135
  230. package/pipeline/scripts/smoke-url-enrichment.sh +0 -70
  231. package/pipeline/scripts/smoke-validate-analysis-doc.sh +0 -161
  232. package/pipeline/scripts/smoke-validator-contradiction.sh +0 -67
  233. package/pipeline/scripts/smoke-validator-gates.sh +0 -164
  234. package/pipeline/scripts/smoke-vercel-deploy-redact.sh +0 -129
  235. package/pipeline/scripts/smoke-verify-by-test.sh +0 -148
  236. package/pipeline/scripts/smoke-wiki-integration.sh +0 -122
  237. package/pipeline/scripts/smoke-work-summary.sh +0 -163
  238. package/pipeline/scripts/smoke-workflow-audit.sh +0 -101
  239. package/pipeline/scripts/smoke-worktree-path-convention.sh +0 -86
  240. package/pipeline/scripts/smoke-wrapper-preservation.sh +0 -68
  241. package/pipeline/scripts/smoke-write-state.sh +0 -159
  242. package/pipeline/scripts/sync-parity-check.sh +0 -135
  243. package/pipeline/scripts/test-gap-rules/android.json +0 -25
  244. package/pipeline/scripts/test-gap-rules/ios.json +0 -34
  245. package/pipeline/scripts/test-gap-rules/node.json +0 -29
  246. package/pipeline/scripts/test-gap-rules/python.json +0 -25
  247. package/pipeline/scripts/validate-schemas.mjs +0 -88
package/CHANGELOG.md CHANGED
@@ -16,6 +16,213 @@ Internal file-layout changes that don't affect the slash-command surface are sti
16
16
 
17
17
  ## [Unreleased]
18
18
 
19
+ ## [12.9.0] - 2026-07-26
20
+
21
+ Context engineering follow-through, and a guard that was blocking safe commands.
22
+
23
+ ### Phase docs load their conditional sections on demand
24
+
25
+ The two biggest phase docs carried the UNION of every branch, so a run paid for
26
+ paths it never took. Both were over their own warn thresholds.
27
+
28
+ - `phase-0-init.md` Step 1b (URL Enrichment, 9.6 kB) applies only when the task
29
+ input carries URLs. A bare Jira ID, a GitHub issue number or a free-text task
30
+ never needs it. Moved to `multi-agent-refs/features/url-enrichment.md` behind a
31
+ 761-byte stub that states the run condition. Phase 0: 11701 -> 9469 tokens.
32
+ - `phase-4-review.md` Multi-Repo Mode (3.0 kB) applies only when
33
+ `state.projects[].length > 1`. Moved to `features/review-multi-repo.md`.
34
+ Phase 4: 12070 -> 11048 tokens.
35
+
36
+ Total phase-doc budget 50955 -> 48047 tokens, and both docs are now under warn
37
+ rather than over. `smoke-url-enrichment.sh` follows the content to the ref and
38
+ additionally asserts the phase doc still POINTS at it: a JIT reference nothing
39
+ loads is worse than the inline copy it replaced.
40
+
41
+ ### The always-on description surface is budgeted
42
+
43
+ Progressive disclosure defers reference BODIES, but a skill or command
44
+ `description` sits in the host's listing for the whole session whether or not the
45
+ skill is ever invoked. That is a second fixed load, it was LARGER than the
46
+ budgeted one (76918 bytes across 236 descriptions), and nothing capped it.
47
+ `smoke-context-budget.sh` now gates the total and the per-description average
48
+ (the total can stay under budget while every description bloats), reports the
49
+ whole pre-task cost as one number, and `test/context-budget-gate.test.mjs` pins
50
+ all three ceilings as literals.
51
+
52
+ ### No prose block loads twice
53
+
54
+ `smoke-context-duplication.sh` hashes normalised prose blocks across the files
55
+ that can co-load in one run. 12.7.0 removed three sections the orchestrator had
56
+ inlined from `multi-agent-refs/` - one already stale, naming a path the code no
57
+ longer used - found by hand with nothing to prevent a recurrence.
58
+
59
+ Scope mattered more than the algorithm. A first draft scanned the command and
60
+ skill trees too and reported 10+ duplicates, every one legitimate: the
61
+ `commands/multi-agent/X` vs `skills/shared/core/multi-agent-X` pairs are the
62
+ cross-CLI parity contract, which `cross-cli-contract.md` REQUIRES byte-identical,
63
+ and the mode commands repeat prose but only one loads per run. A gate that fires
64
+ on deliberate design trains people to ignore it. Narrowed to the ref tree plus the
65
+ orchestrator, it found one real case: three pickers each restating the Language
66
+ matrix that `rules.md` already carries on every run - and the copies had already
67
+ drifted, two saying "Commit/PR/Jira/Wiki" and one "Commit/PR/Jira". Replaced with
68
+ a one-line pointer, 678 bytes reclaimed.
69
+
70
+ ### agent-guard stopped blocking safe pushes
71
+
72
+ The force-push detector ended in `-\S*f\S*`, matching any flag containing an `f`:
73
+
74
+ ```
75
+ git pull --ff-only && git push origin main -> BLOCKED
76
+ git push --follow-tags origin main -> BLOCKED
77
+ ```
78
+
79
+ Neither rewrites history, and `--follow-tags` is the normal way to push a release
80
+ tag with its commit, so the guard blocked a routine release step. False positives
81
+ are not free: people learn to route around the hook, which loses more safety than
82
+ the guard buys. The short-flag arm is now `-[A-Za-z]*f[A-Za-z]*` (a single dash
83
+ followed only by letters, so `-f`/`-fq` still match and no `--long-flag` can), and
84
+ force detection is scoped to the `git push` segment so a neighbouring command's
85
+ `-f` no longer implicates it (`rm -f stale.log && git push origin main`).
86
+
87
+ Fail-closed behaviour is unchanged and now asserted: an untokenizable command, and
88
+ a bare force-push whose target branch cannot be determined, still block.
89
+ `test/agent-guard.test.mjs` covers 30 cases across both directions.
90
+
91
+ ### Also
92
+
93
+ - Trees an older installer created and no current one manages are pruned on
94
+ install (`pruneAbandonedTrees`). `~/.claude/eval/` held 31 files that no
95
+ installer wrote and no uninstall removed - wipe-before-copy only protects trees
96
+ still being written, so an abandoned one persists forever.
97
+ - shellcheck in CI scans `git ls-files '*.sh'` instead of `find pipeline`, so a
98
+ shell script added outside `pipeline/` is no longer silently unchecked.
99
+
100
+ ## [12.8.0] - 2026-07-26
101
+
102
+ 12.7.0 fixed nine gates that reported success without checking anything. This
103
+ release is the level above that: gates that check correctly and never run, and a
104
+ shipped surface that could not work where it was shipped to.
105
+
106
+ ### 117 maintainer smokes stopped shipping to users
107
+
108
+ `pipeline/scripts/` was copied wholesale into `~/.claude/scripts/`, so every
109
+ repository CI gate landed on every user's machine. Measured from a real install:
110
+ of 117 installed smoke scripts, **100 failed** and 17 passed only by accident of
111
+ not needing the repo. They resolve the repo root as `dirname($0)/../..`, which in
112
+ an install layout is `$HOME`, and then read `package.json` / `install.js` /
113
+ `node_modules`. What a user saw:
114
+
115
+ ```
116
+ $ bash ~/.claude/scripts/smoke-install-layout.sh
117
+ FAIL: install.js missing
118
+ ```
119
+
120
+ A green repo gate, run in the wrong place, indistinguishable from a broken
121
+ installation. Worse, `/multi-agent:update` actively instructed users to run
122
+ `smoke-cross-cli-behavior.sh`, which reported "3 failed" for the same reason.
123
+
124
+ The default for `smoke-*.sh` is now EXCLUDE, in both the install and the tarball.
125
+ A smoke ships only when a command actually invokes it - `CONSUMER_SMOKES` in
126
+ `install/_dev-only-files.mjs`, currently the two that `/multi-agent:update` runs.
127
+ Both now resolve paths through the new `pipeline/scripts/_smoke-root.sh`, which
128
+ handles the repo and install layouts, so `smoke-cross-cli-behavior.sh` reports
129
+ 20/20 from an install instead of 17/20.
130
+
131
+ Also withheld: `run-smokes.mjs`, `eval-*.mjs`, `pipeline/eval/**`, `scorecard.mjs`,
132
+ the linters, `validate-schemas.mjs`, and the fixture corpora - all of them need
133
+ devDependencies or the repo tree. Install drops from 201 files to 73; the tarball
134
+ from 977 to 753.
135
+
136
+ `smoke-consumer-smoke-surface.sh` holds four invariants: the allowlist equals what
137
+ commands invoke (drift either way fails), every consumer smoke uses the shared
138
+ resolver, no maintainer smoke survives the install filter, and none survives into
139
+ the tarball. `smoke-pack-contents.sh` now evaluates `files[]` the way npm does
140
+ (last matching pattern wins, so a broad negation may be followed by a narrower
141
+ re-inclusion - verified against npm directly) and reports an inert negation that
142
+ guards nothing. `smoke-install-leak-gate.sh` step 4 previously asserted the
143
+ OPPOSITE contract, naming three "essential" smokes that had to install; it now
144
+ derives the expected set from `CONSUMER_SMOKES`.
145
+
146
+ Fixed along the way: both installers computed the excluded-file count by summing
147
+ `countFiles()` over the exclusion array, which returns 0 for every non-literal
148
+ entry, so the count would have under-reported by 100+ files.
149
+
150
+ ### Four gates that were tested, green, and never ran
151
+
152
+ Every gate in this repo checks its own behaviour. Nothing checked that a gate is
153
+ *reachable*. Four were not - each with a passing unit test and a passing smoke,
154
+ and no reference from `pipeline/commands/`, `multi-agent-refs/`, `agents/`,
155
+ `skills/` or `install/`:
156
+
157
+ - **`test-integrity-gate.mjs`** - the anti-reward-hacking control for a suite made
158
+ green by deleting tests. Now Phase 4 Step 1.76, emitting blocking findings that
159
+ merge into the reviewer findings at Step 3.0 (before the no-findings
160
+ short-circuit, which would otherwise skip triage entirely on a diff whose only
161
+ finding is a deleted test). No opt-out: a run that can switch off its own
162
+ anti-reward-hacking control cannot be trusted to report a pass.
163
+ - **`review-scope.mjs`** - the reviewer-count cost gate. Now Phase 4 Step 1.77,
164
+ behind `prefs.global.reviewScopeGate` (default true). Every diff had been paying
165
+ for the full reviewer set.
166
+ - **`audit-log.sh`** - the PAT-lookup audit trail. `.gitignore` carried defensive
167
+ globs for it and `/multi-agent:prune-logs` documented managing it, but
168
+ `credential-store.sh` never called it, so the trail was always empty. Now
169
+ written on every `get`, on both the success and the miss path, through the
170
+ Python delegate and the raw-shell path. Since the trail now actually grows,
171
+ `audit-log.sh` self-rotates past `AUDIT_ROTATE_AT_BYTES` (1 MB) rather than
172
+ depending on a launchd/cron install nothing ever told a user to set up.
173
+ - **`write-state.mjs`** - the atomic `agent-state.json` writer with an advisory
174
+ lock, written for exactly the multi-repo race the phase docs create by sharing
175
+ one state file across worktrees. `operations.md` now documents it as the
176
+ required mechanism, with the exit codes a caller must handle, and
177
+ `phase-0-init.md` points at it where the shared file is introduced.
178
+
179
+ Phase 4 Step 1.75 now scores the diff **once without `--top`**: both new gates
180
+ need every scored file (a shrinking test file ranked 20th; whether any file is
181
+ high-stakes), and the top-5 prompt hint is derived from that report instead of a
182
+ second git walk.
183
+
184
+ `smoke-gate-wiring.sh` sweeps every runtime script for a reference from the
185
+ instruction surface, with maintainer tooling listed explicitly and by reason
186
+ rather than being the silent default for anything unreferenced. It also pins the
187
+ four wirings individually, and asserts the test-integrity gate is fed the full
188
+ report rather than the truncated hint. It found two more scripts on the first run
189
+ (`review-watch.sh`, `audit-log-rotate.sh`), both correctly user-launched.
190
+
191
+ ### Removed: the v9.1.0 legacy-v2 analysis escape hatch
192
+
193
+ Triple-dead and still documented in three files. Its own text said "escape hatch
194
+ removed in v9.2.0" - this is 12.8.0. The `prefs.global.legacyV2AnalysisAllowed` it
195
+ depended on was never added to `prefs.schema.json`, and `global` is
196
+ `additionalProperties: false`, so a user who configured it as documented produced
197
+ a preferences file that fails validation. `template_version < v3` now simply
198
+ aborts. The reclaimed budget paid for the two new phase-4 steps, so the fixed
199
+ per-run context ceiling did not move.
200
+
201
+ ### Gate honesty
202
+
203
+ - **The context ceiling can no longer be bypassed.** It was
204
+ `${CONTEXT_BUDGET_CEILING:-60000}` - the only env-overridable threshold in the
205
+ suite, so `CONTEXT_BUDGET_CEILING=999999` passed while measuring nothing. It is
206
+ a literal now, pinned by `test/context-budget-gate.test.mjs` (the companion to
207
+ `test/coverage-gate.test.mjs`, which already protects the coverage floors from
208
+ the same move). Headroom was 3.5% when this was written - precisely when raising
209
+ the number looks attractive. The test also prints current headroom so it shrinks
210
+ visibly instead of silently.
211
+ - **The coverage claim names its denominator.** "coverage floors held" reads as a
212
+ repo-wide figure; c8 instruments JavaScript, and this repo carries ~30k lines of
213
+ shell and Python that no coverage number covers at all. The metric is now
214
+ "coverage floors held (JavaScript only)" and states the excluded size, matching
215
+ what the scorecard's UNMEASURED section already does for whole categories.
216
+
217
+ ### Also
218
+
219
+ - `smoke-consumer-smoke-surface.sh` and `smoke-gate-wiring.sh` added to `ci-lite`;
220
+ the consumer-surface gate also runs in `release`, since a publish is the only
221
+ moment tarball contents become permanent.
222
+ - `test/dev-only-files.test.mjs` (20 tests) covers the exclusion predicate,
223
+ including that a hypothetical new smoke is withheld by default and that runtime
224
+ scripts the phases invoke are not.
225
+
19
226
  ## [12.7.0] - 2026-07-26
20
227
 
21
228
  A refactor pass whose theme turned out to be one recurring defect: gates that
@@ -239,6 +239,54 @@ export function pruneLegacyMultiAgentSkills(skillsDir) {
239
239
  return removed;
240
240
  }
241
241
 
242
+ /**
243
+ * Trees an older installer created and no current one manages.
244
+ *
245
+ * A tree that stops being copied does NOT stop existing: the wipe-before-copy
246
+ * pattern only runs for trees the installer still writes, so dropping one from
247
+ * the installer leaves whatever it last wrote on every existing machine forever.
248
+ *
249
+ * `eval/` is the case that prompted this. An older version copied the eval
250
+ * corpora into `~/.claude/eval/`; the harnesses that read them are maintainer CI
251
+ * tooling and no longer ship, so neither installer references `eval` any more and
252
+ * neither did uninstall - 31 stale files sat there with nothing left that could
253
+ * ever read or remove them.
254
+ *
255
+ * @type {ReadonlyArray<{dir: string, reason: string}>}
256
+ */
257
+ export const ABANDONED_TREES = Object.freeze([
258
+ {
259
+ dir: "eval",
260
+ reason: "eval corpora; the harnesses that read them are maintainer-only and no longer ship",
261
+ },
262
+ ]);
263
+
264
+ /**
265
+ * Remove trees an older install left behind under `root`.
266
+ *
267
+ * @param {string} root e.g. `$HOME/.claude`
268
+ * @returns {number} directories removed
269
+ */
270
+ export function pruneAbandonedTrees(root) {
271
+ let removed = 0;
272
+ for (const { dir, reason } of ABANDONED_TREES) {
273
+ const target = join(root, dir);
274
+ if (!existsSync(target)) continue;
275
+ if (dryRun) {
276
+ console.log(` [dry-run] would remove abandoned tree ${target} (${reason})`);
277
+ removed++;
278
+ continue;
279
+ }
280
+ try {
281
+ rmSync(target, { recursive: true, force: true });
282
+ removed++;
283
+ } catch {
284
+ /* non-fatal - a leftover tree must never fail an install */
285
+ }
286
+ }
287
+ return removed;
288
+ }
289
+
242
290
  /**
243
291
  * Copy a single file, honouring dry-run mode.
244
292
  * @param {string} src
@@ -1,18 +1,138 @@
1
1
  /**
2
- * Dev-only files excluded from end-user installs.
2
+ * Dev-only files excluded from end-user installs and from the published tarball.
3
3
  *
4
- * These reference the maintainer's private upstream and contain personal data
5
- * (org names, hostnames, emails) on the `from:` side of substitution rules.
6
- * End users never run them, so we keep them out of `~/.claude/scripts/` and
7
- * `~/.copilot/scripts/`.
4
+ * Two separate reasons live here, and they must not be conflated:
5
+ *
6
+ * 1. LEAK: the file references the maintainer's private upstream or carries
7
+ * personal data (org names, hostnames, emails) on the `from:` side of a
8
+ * substitution rule. Shipping it is a disclosure, not just noise.
9
+ *
10
+ * 2. MAINTAINER CI: the file is a repository gate. It resolves the repo root
11
+ * as `dirname($0)/../..` and reads `package.json` / `install.js` /
12
+ * `node_modules` / the git tree. In an install layout that path is `$HOME`,
13
+ * so the script reports things like `FAIL: install.js missing` - a green
14
+ * repo check that reads as a broken installation to whoever ran it.
15
+ * Measured before this list existed: of 117 installed smoke scripts, 100
16
+ * failed that way and 17 passed only by accident of not needing the repo.
17
+ *
18
+ * Reason 2 is why the default for `smoke-*.sh` is EXCLUDE. A smoke ships only
19
+ * when a command actually tells a user to run it - that set is CONSUMER_SMOKES
20
+ * below, and `smoke-consumer-smoke-surface.sh` fails if the two drift apart.
8
21
  *
9
22
  * @module install/_dev-only-files
10
23
  */
11
24
 
25
+ import { readdirSync } from "node:fs";
26
+ import { join } from "node:path";
27
+
28
+ /**
29
+ * Smoke suites an end user is instructed to run, and which therefore must work
30
+ * from an installed layout (`~/.claude/scripts/`) as well as from the repo.
31
+ *
32
+ * `/multi-agent:update` runs both as its post-update verification. Anything
33
+ * added here MUST resolve its paths through `_smoke-root.sh`, never through a
34
+ * bare `dirname($0)/../..`.
35
+ */
36
+ export const CONSUMER_SMOKES = Object.freeze([
37
+ "smoke-schema-validation.sh",
38
+ "smoke-cross-cli-behavior.sh",
39
+ ]);
40
+
41
+ /**
42
+ * Every `smoke-*.sh` that is NOT in CONSUMER_SMOKES. Expressed as one regex so a
43
+ * newly added smoke is excluded by default rather than shipping until someone
44
+ * notices - the safe direction for reason 2 above.
45
+ */
46
+ export const MAINTAINER_SMOKES = new RegExp(
47
+ `^smoke-(?!${CONSUMER_SMOKES.map((n) => n.replace(/^smoke-/, "").replace(/\./g, "\\.")).join(
48
+ "|",
49
+ )}$)`,
50
+ );
51
+
52
+ /**
53
+ * Repository tooling that is not a smoke: the smoke runner itself, the eval
54
+ * harnesses, the scorecard, and the linters. All of them need devDependencies
55
+ * (`ajv`, `c8`, `eslint`) or the repo tree, neither of which exists in an
56
+ * install.
57
+ */
58
+ const DEV_ONLY_TOOLING = Object.freeze([
59
+ "run-smokes.mjs",
60
+ "scorecard.mjs",
61
+ "lint-skills.mjs",
62
+ "lint-mcp-refs.mjs",
63
+ "check-md-links.mjs",
64
+ "validate-schemas.mjs", // validates the repo's own schema files, needs ajv
65
+ "sync-parity-check.sh",
66
+ "benchmark-phase-0.sh",
67
+ "test-gap-rules", // rule corpus for the repo's own test-gap gate
68
+ ]);
69
+
70
+ /** Eval harnesses (`eval-*.mjs`) read `pipeline/eval/**`, which never ships. */
71
+ const EVAL_HARNESSES = /^eval-.*\.mjs$/;
72
+
12
73
  export const DEV_ONLY_SCRIPTS = Object.freeze([
74
+ // --- reason 1: leak ---
13
75
  "smoke-figma-config-schema.sh",
14
76
  "smoke-personal-data.sh", // scanner contains the patterns it detects
15
77
  "smoke-install-leak-gate.sh", // same - scanner shadows the install pipeline
16
78
  "validate-prefs.mjs", // needs ajv (devDependency); end users have no node_modules here
17
79
  "fixtures", // smoke test fixtures live in the repo only, never ship to users
80
+ // --- reason 2: maintainer CI ---
81
+ ...DEV_ONLY_TOOLING,
82
+ EVAL_HARNESSES,
83
+ MAINTAINER_SMOKES,
18
84
  ]);
85
+
86
+ /**
87
+ * True when `name` (a path relative to `pipeline/scripts/`) must not be
88
+ * installed. Shared by both installers' excluded-file counters and by
89
+ * `smoke-consumer-smoke-surface.sh`, so the count and the gate can never
90
+ * disagree with what `copyDir` actually filtered.
91
+ *
92
+ * @param {string} name
93
+ * @returns {boolean}
94
+ */
95
+ export function isDevOnlyScript(name) {
96
+ const rel = String(name).split("\\").join("/").replace(/^\//, "");
97
+ return DEV_ONLY_SCRIPTS.some((pat) =>
98
+ typeof pat === "string" ? rel === pat || rel.startsWith(pat + "/") : pat.test(rel),
99
+ );
100
+ }
101
+
102
+ /**
103
+ * How many files under `scriptsDir` `copyDir` will filter out.
104
+ *
105
+ * Walks the tree and applies `isDevOnlyScript` to the same relative paths
106
+ * `copyDir`'s filter sees, so the number printed by an install is the number
107
+ * actually withheld. The previous form summed `countFiles()` over the exclusion
108
+ * array, which silently returned 0 for every non-literal (regex) entry.
109
+ *
110
+ * @param {string} scriptsDir absolute path to `pipeline/scripts`
111
+ * @returns {number}
112
+ */
113
+ export function countDevOnlyFiles(scriptsDir) {
114
+ let excluded = 0;
115
+ const walk = (abs, rel) => {
116
+ for (const entry of readdirSync(abs, { withFileTypes: true })) {
117
+ const childRel = rel ? `${rel}/${entry.name}` : entry.name;
118
+ const childAbs = join(abs, entry.name);
119
+ if (entry.isDirectory()) {
120
+ // An excluded directory takes its whole subtree with it, exactly as
121
+ // cpSync's filter does when it returns false for the directory itself.
122
+ if (isDevOnlyScript(childRel)) excluded += countTree(childAbs);
123
+ else walk(childAbs, childRel);
124
+ } else if (isDevOnlyScript(childRel)) {
125
+ excluded++;
126
+ }
127
+ }
128
+ };
129
+ const countTree = (abs) => {
130
+ let n = 0;
131
+ for (const entry of readdirSync(abs, { withFileTypes: true })) {
132
+ n += entry.isDirectory() ? countTree(join(abs, entry.name)) : 1;
133
+ }
134
+ return n;
135
+ };
136
+ walk(scriptsDir, "");
137
+ return excluded;
138
+ }
@@ -27,13 +27,14 @@ import {
27
27
  ensureDir,
28
28
  ensureRealDir,
29
29
  isDryRun,
30
+ pruneAbandonedTrees,
30
31
  pruneLegacyMultiAgentSkills,
31
32
  removePipelineAgentFiles,
32
33
  wipeDir,
33
34
  writeFile,
34
35
  } from "./_common.mjs";
35
36
  import { copyExternalSkillsFiltered } from "./_platform-filter.mjs";
36
- import { DEV_ONLY_SCRIPTS } from "./_dev-only-files.mjs";
37
+ import { DEV_ONLY_SCRIPTS, countDevOnlyFiles } from "./_dev-only-files.mjs";
37
38
 
38
39
  /**
39
40
  * Dead matcher written by pre-v11.4.1 installs. Hook matchers only ever see
@@ -64,6 +65,14 @@ export function installClaude(ctx) {
64
65
  const CLAUDE_LIB = join(home, ".claude", "lib");
65
66
  const CLAUDE_MA_REFS = join(home, ".claude", "multi-agent-refs");
66
67
 
68
+ // Before laying anything down: drop trees an older install created that no
69
+ // current installer manages. Wipe-before-copy only protects trees still being
70
+ // written, so an abandoned one persists on every existing machine forever.
71
+ const prunedTrees = pruneAbandonedTrees(join(home, ".claude"));
72
+ if (prunedTrees > 0) {
73
+ console.log(` [Claude Code] Removed ${prunedTrees} abandoned tree(s) from an older install`);
74
+ }
75
+
67
76
  installCommands(pipelineSrc, CLAUDE_COMMANDS, useSymlinks);
68
77
  installMultiAgentRefs(pipelineSrc, CLAUDE_MA_REFS, useSymlinks);
69
78
  installScripts(pipelineSrc, CLAUDE_SCRIPTS, useSymlinks);
@@ -201,13 +210,10 @@ function installScripts(pipelineSrc, dest, useSymlinks) {
201
210
  if (!useSymlinks) ensureRealDir(dest);
202
211
  wipeDir(dest);
203
212
  copyDir(scriptsSrc, dest, { exclude: DEV_ONLY_SCRIPTS, useSymlinks });
204
- // Count files actually excluded, not the array length: DEV_ONLY_SCRIPTS mixes
205
- // individual .sh files with the `fixtures` directory (many files), so the
206
- // array length under-counts the real exclusion.
207
- const excludedCount = DEV_ONLY_SCRIPTS.reduce(
208
- (n, name) => n + countFiles(join(scriptsSrc, name)),
209
- 0,
210
- );
213
+ // Count files actually excluded by walking the tree: DEV_ONLY_SCRIPTS mixes
214
+ // literal names, whole directories, and regexes (the maintainer-smoke rule),
215
+ // so summing countFiles() over the array reported 0 for every regex entry.
216
+ const excludedCount = countDevOnlyFiles(scriptsSrc);
211
217
  const scriptCount = countFiles(scriptsSrc) - excludedCount;
212
218
  console.log(` -> ${scriptCount} files copied to ${dest} (${excludedCount} dev-only excluded)`);
213
219
  }
@@ -23,7 +23,7 @@ import {
23
23
  writeFile,
24
24
  } from "./_common.mjs";
25
25
  import { copyExternalSkillsFiltered } from "./_platform-filter.mjs";
26
- import { DEV_ONLY_SCRIPTS } from "./_dev-only-files.mjs";
26
+ import { DEV_ONLY_SCRIPTS, countDevOnlyFiles } from "./_dev-only-files.mjs";
27
27
  import { generateCopilotInstructions } from "./_copilot-instructions.mjs";
28
28
 
29
29
  /**
@@ -166,13 +166,10 @@ function installScripts(pipelineSrc, dest, useSymlinks) {
166
166
  if (!useSymlinks) ensureRealDir(dest);
167
167
  wipeDir(dest);
168
168
  copyDir(scriptsSrc, dest, { exclude: DEV_ONLY_SCRIPTS, useSymlinks });
169
- // Count files actually excluded, not the array length: DEV_ONLY_SCRIPTS mixes
170
- // individual .sh files with the `fixtures` directory (many files), so the
171
- // array length under-counts the real exclusion.
172
- const excludedCount = DEV_ONLY_SCRIPTS.reduce(
173
- (n, name) => n + countFiles(join(scriptsSrc, name)),
174
- 0,
175
- );
169
+ // Count files actually excluded by walking the tree: DEV_ONLY_SCRIPTS mixes
170
+ // literal names, whole directories, and regexes (the maintainer-smoke rule),
171
+ // so summing countFiles() over the array reported 0 for every regex entry.
172
+ const excludedCount = countDevOnlyFiles(scriptsSrc);
176
173
  const scriptCount = countFiles(scriptsSrc) - excludedCount;
177
174
  console.log(` -> ${scriptCount} files copied to ${dest} (${excludedCount} dev-only excluded)`);
178
175
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "12.7.0",
3
+ "version": "12.9.0",
4
4
  "description": "8-phase AI development pipeline with full orchestration on Claude Code and Copilot CLI. Analysis, planning, TDD, CLI-aware parallel review with consensus surfacing + Fable triage, default-FAIL evidence gates, secret + intent guards, per-phase cost ledger, persistent learnings memory, wiki generation, commit automation. Token-preserving uninstall.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -75,7 +75,22 @@
75
75
  "!pipeline/scripts/smoke-personal-data.sh",
76
76
  "!pipeline/scripts/validate-prefs.mjs",
77
77
  "!pipeline/scripts/smoke-install-leak-gate.sh",
78
- "!docs/internal/**"
78
+ "!docs/internal/**",
79
+ "!pipeline/scripts/smoke-*.sh",
80
+ "!pipeline/scripts/run-smokes.mjs",
81
+ "!pipeline/scripts/eval-*.mjs",
82
+ "!pipeline/scripts/scorecard.mjs",
83
+ "!pipeline/scripts/lint-skills.mjs",
84
+ "!pipeline/scripts/lint-mcp-refs.mjs",
85
+ "!pipeline/scripts/check-md-links.mjs",
86
+ "!pipeline/scripts/validate-schemas.mjs",
87
+ "!pipeline/scripts/sync-parity-check.sh",
88
+ "!pipeline/scripts/benchmark-phase-0.sh",
89
+ "!pipeline/scripts/fixtures/**",
90
+ "!pipeline/scripts/test-gap-rules/**",
91
+ "!pipeline/eval/**",
92
+ "pipeline/scripts/smoke-schema-validation.sh",
93
+ "pipeline/scripts/smoke-cross-cli-behavior.sh"
79
94
  ],
80
95
  "devDependencies": {
81
96
  "@eslint/js": "^10.0.1",
@@ -65,7 +65,7 @@ When citing a Locked decision in code or docs, prefer `Locked <n> (<short label>
65
65
  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.
66
66
  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.
67
67
  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.
68
- 30. **Analysis as self-contained design bridge - no MCP outside analysis phase (BLOCKING, pipeline-wide, v9.0.0; legacy soft-skip v9.1.0).** 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`, `/multi-agent:dev`, `/multi-agent:dev-autopilot`, `/multi-agent:dev-local`, `/multi-agent:dev-local-autopilot`. Hard requirement (v9.0.0): Phase 2 Pre-item and Phase 3 Pre-item (BLOCKING) abort the run when the analysis document is missing. **Legacy soft-skip (v9.1.0+):** existing v2 analyses (`template_version: v2`) emit `WARN: legacy v2 analysis doc detected; v3 expected. Re-run /multi-agent:analysis to regenerate. Continuing with reduced binding strength.` and the run continues when `prefs.global.legacyV2AnalysisAllowed == true` OR env `MULTI_AGENT_LEGACY_V2_ANALYSIS=allow` is set. The soft-skip degrades Pass B footnote enforcement to warning level and skips Phase 1c convention extraction (no `conventions[]` data in v2 docs). The escape hatch is removed in v9.2.0; users must regenerate v3 docs by then. Smoke gate `pipeline/scripts/smoke-no-mcp-in-dev-phases.sh` reads `state.telemetry.mcpCalls[]` and fails on any entry with `phase >= 2`. Halt condition: if a downstream phase needs design information not present in the analysis document, the run halts and the user re-runs `/multi-agent:analysis` to regenerate. Memory: `[[mcp-only-in-analysis]]`. Generic rule rationale and access matrix: see `pipeline/rules/figma-pipeline.md` "MUST: No MCP outside analysis phase".
68
+ 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`, `/multi-agent:dev`, `/multi-agent:dev-autopilot`, `/multi-agent:dev-local`, `/multi-agent:dev-local-autopilot`. 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 `pipeline/rules/figma-pipeline.md` "MUST: No MCP outside analysis phase".
69
69
  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 via `/multi-agent:dev`. The rule-to-acceptance-criterion half always holds, in both modes.
70
70
 
71
71
  ## Input
@@ -113,18 +113,36 @@ if isinstance(name, str) and name.strip():
113
113
  }
114
114
 
115
115
  # --- Subcommands ------------------------------------------------------------
116
+ # Append one PAT-lookup audit event. Every `get` goes through here, which is the
117
+ # point of the control: the audit trail described in the .gitignore hardening and
118
+ # managed by `/multi-agent:prune-logs` was written by audit-log.sh, but nothing
119
+ # ever called it, so the trail was always empty and the control was inert.
120
+ #
121
+ # Never fails the caller and never echoes a secret: audit-log.sh hashes the repo
122
+ # URL, logs the logical key (not the value), and is silent on error by design.
123
+ audit_lookup() {
124
+ local logical="$1" success="$2"
125
+ local audit="$SCRIPT_DIR/../scripts/audit-log.sh"
126
+ [ -f "$audit" ] || return 0
127
+ bash "$audit" pat_lookup "$logical" "${USER:-unknown}" "${MULTI_AGENT_REPO_URL:-}" "$success" \
128
+ >/dev/null 2>&1 || true
129
+ }
130
+
116
131
  do_get() {
117
132
  local key="${1:-}"
118
133
  [ -z "$key" ] && { echo "usage: $0 get <key>" >&2; exit 3; }
134
+ local logical="$key"
119
135
  key=$(resolve_key "$key")
120
136
  if delegate_to_python; then
121
137
  local val rc
122
138
  val=$(python3 "$KEYCHAIN_PY" get "$key" 2>/dev/null) || rc=$?
123
139
  rc=${rc:-0}
124
140
  if [ "$rc" -eq 0 ] && [ -n "$val" ]; then
141
+ audit_lookup "$logical" true
125
142
  printf '%s' "$val"
126
143
  return 0
127
144
  fi
145
+ audit_lookup "$logical" false
128
146
  return 1
129
147
  fi
130
148
  local val=""
@@ -149,8 +167,10 @@ if (\$c) { \$c.GetNetworkCredential().Password }
149
167
  echo "ERR: unsupported platform" >&2; exit 2 ;;
150
168
  esac
151
169
  if [ -z "$val" ]; then
170
+ audit_lookup "$logical" false
152
171
  return 1
153
172
  fi
173
+ audit_lookup "$logical" true
154
174
  printf '%s' "$val"
155
175
  }
156
176
 
@@ -14,7 +14,7 @@ First step of every `multi-agent` flow **that touches a remote provider**.
14
14
  > repos, account-picker is bypassed and the state file's `accountId` is left
15
15
  > `null` with `tokens={}`. Phases 6/7 read these as "local-only" signals.
16
16
 
17
- > **Language**: Per `$HOME/.claude/multi-agent-refs/rules.md` Language Application matrix - `AskUserQuestion.label` and `header` stay English (UI button + chip contract); `question` and `description` follow `outputLanguage`. Commit/PR/Jira payloads: subject + branch names + code identifiers stay English; body text follows `outputLanguage`.
17
+ > **Language**: see `picker-contract.md` + `rules.md` Language Application matrix.
18
18
 
19
19
  ## Steps
20
20
 
@@ -6,7 +6,7 @@ description: "Internal - dev context (extra repos) picker for multi-agent."
6
6
 
7
7
  Selects extra repos the pipeline may touch beyond the primary repo(s) - typically submodules (e.g. SDKs vendored inside an app repo) or sibling libraries. Each candidate is enriched with a `canPush` flag so the picker can pre-select repos that the active account is allowed to edit, and surface read-only ones as advisory context the pipeline will not modify.
8
8
 
9
- > **Language**: Per `$HOME/.claude/multi-agent-refs/rules.md` Language Application matrix - `AskUserQuestion.label` and `header` stay English (UI button + chip contract); `question` and `description` follow `outputLanguage`. Commit/PR/Jira/Wiki payloads: subject + branch names + code identifiers stay English; body text follows `outputLanguage`.
9
+ > **Language**: see `picker-contract.md` + `rules.md` Language Application matrix.
10
10
 
11
11
  ## Steps
12
12
 
@@ -6,7 +6,7 @@ description: "Internal - multi-select repo picker for multi-agent."
6
6
 
7
7
  Used by `picker-issue`, `picker-jira`, `gh-num`, and `freetext` flows.
8
8
 
9
- > **Language**: Per `$HOME/.claude/multi-agent-refs/rules.md` Language Application matrix - `AskUserQuestion.label` and `header` stay English (UI button + chip contract); `question` and `description` follow `outputLanguage`. Commit/PR/Jira/Wiki payloads: subject + branch names + code identifiers stay English; body text follows `outputLanguage`.
9
+ > **Language**: see `picker-contract.md` + `rules.md` Language Application matrix.
10
10
 
11
11
  ## Steps
12
12