@mmerterden/multi-agent-pipeline 12.6.0 → 12.7.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 (271) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +18 -18
  3. package/docs/FIGMA_PIPELINE.md +34 -34
  4. package/docs/adr/0001-three-model-triage.md +12 -12
  5. package/docs/adr/0002-instruction-driven-flag.md +5 -5
  6. package/docs/adr/0003-unified-shared-skills.md +5 -5
  7. package/docs/adr/0004-zero-dependency-philosophy.md +5 -5
  8. package/docs/adr/0005-lazy-phase-docs.md +2 -2
  9. package/docs/adr/0006-skills-core-external-split.md +6 -6
  10. package/docs/adr/0007-multi-tool-adapter-framework.md +19 -19
  11. package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +19 -19
  12. package/docs/adr/README.md +1 -1
  13. package/docs/best-practices.md +3 -3
  14. package/docs/features.md +28 -28
  15. package/docs/performance.md +16 -16
  16. package/docs/recovery-guide.md +39 -39
  17. package/index.js +4 -4
  18. package/install/_common.mjs +5 -11
  19. package/install/_copilot-instructions.mjs +2 -2
  20. package/install/_dev-only-files.mjs +1 -1
  21. package/install/_platform-filter.mjs +1 -1
  22. package/install/_telemetry.mjs +1 -1
  23. package/install/claude.mjs +10 -9
  24. package/install/copilot.mjs +10 -19
  25. package/install/index.mjs +7 -15
  26. package/install/templates/copilot-instructions.md +54 -54
  27. package/install.js +1 -1
  28. package/package.json +13 -10
  29. package/pipeline/commands/multi-agent/SKILL.md +1 -1
  30. package/pipeline/commands/multi-agent/analysis/SKILL.md +1 -1
  31. package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +1 -1
  32. package/pipeline/commands/multi-agent/autopilot/SKILL.md +1 -1
  33. package/pipeline/commands/multi-agent/build-optimize/SKILL.md +1 -1
  34. package/pipeline/commands/multi-agent/create-jira/SKILL.md +1 -1
  35. package/pipeline/commands/multi-agent/design-check/SKILL.md +1 -1
  36. package/pipeline/commands/multi-agent/dev/SKILL.md +1 -1
  37. package/pipeline/commands/multi-agent/dev-autopilot/SKILL.md +1 -1
  38. package/pipeline/commands/multi-agent/dev-local/SKILL.md +1 -1
  39. package/pipeline/commands/multi-agent/dev-local-autopilot/SKILL.md +1 -1
  40. package/pipeline/commands/multi-agent/diff-explain/SKILL.md +1 -1
  41. package/pipeline/commands/multi-agent/finish/SKILL.md +6 -6
  42. package/pipeline/commands/multi-agent/forget/SKILL.md +1 -1
  43. package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
  44. package/pipeline/commands/multi-agent/help/SKILL.md +3 -3
  45. package/pipeline/commands/multi-agent/issue/SKILL.md +1 -1
  46. package/pipeline/commands/multi-agent/jira/SKILL.md +1 -1
  47. package/pipeline/commands/multi-agent/kill/SKILL.md +1 -1
  48. package/pipeline/commands/multi-agent/language/SKILL.md +1 -1
  49. package/pipeline/commands/multi-agent/local/SKILL.md +1 -1
  50. package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +1 -1
  51. package/pipeline/commands/multi-agent/log/SKILL.md +1 -1
  52. package/pipeline/commands/multi-agent/manual-test/SKILL.md +1 -1
  53. package/pipeline/commands/multi-agent/prune-logs/SKILL.md +1 -1
  54. package/pipeline/commands/multi-agent/purge/SKILL.md +1 -1
  55. package/pipeline/commands/multi-agent/refactor/SKILL.md +16 -8
  56. package/pipeline/commands/multi-agent/resume/SKILL.md +2 -2
  57. package/pipeline/commands/multi-agent/review/SKILL.md +1 -1
  58. package/pipeline/commands/multi-agent/review-issue/SKILL.md +1 -1
  59. package/pipeline/commands/multi-agent/review-jira/SKILL.md +1 -1
  60. package/pipeline/commands/multi-agent/routines/SKILL.md +1 -1
  61. package/pipeline/commands/multi-agent/save/SKILL.md +1 -1
  62. package/pipeline/commands/multi-agent/scan/SKILL.md +1 -1
  63. package/pipeline/commands/multi-agent/search/SKILL.md +1 -1
  64. package/pipeline/commands/multi-agent/setup/SKILL.md +2 -2
  65. package/pipeline/commands/multi-agent/stack/SKILL.md +3 -3
  66. package/pipeline/commands/multi-agent/status/SKILL.md +1 -1
  67. package/pipeline/commands/multi-agent/sync/SKILL.md +5 -5
  68. package/pipeline/commands/multi-agent/test/SKILL.md +1 -1
  69. package/pipeline/commands/multi-agent/uninstall/SKILL.md +1 -1
  70. package/pipeline/commands/multi-agent/update/SKILL.md +3 -3
  71. package/pipeline/eval/run-metrics-fixture.json +60 -13
  72. package/pipeline/lib/account-resolver.sh +1 -1
  73. package/pipeline/lib/channels-multi-repo.sh +1 -1
  74. package/pipeline/lib/context-link-extractor.sh +1 -1
  75. package/pipeline/lib/credential-store.sh +1 -1
  76. package/pipeline/lib/fetch-confluence.sh +1 -1
  77. package/pipeline/lib/fetch-crashlytics.sh +1 -1
  78. package/pipeline/lib/fetch-fortify.sh +1 -1
  79. package/pipeline/lib/fetch-graylog.sh +1 -1
  80. package/pipeline/lib/fetch-swagger.sh +1 -1
  81. package/pipeline/lib/issue-fetcher.sh +1 -1
  82. package/pipeline/lib/multi-repo-pipeline.sh +1 -1
  83. package/pipeline/lib/repo-cache.sh +1 -1
  84. package/pipeline/lib/submodule-detector.sh +1 -1
  85. package/pipeline/multi-agent-refs/component-dispatch.md +1 -1
  86. package/pipeline/multi-agent-refs/component-generation.md +121 -0
  87. package/pipeline/multi-agent-refs/cross-cli-contract.md +1 -1
  88. package/pipeline/multi-agent-refs/features/model-fallback.md +2 -2
  89. package/pipeline/multi-agent-refs/phases/phase-4-review.md +1 -1
  90. package/pipeline/preferences-template.json +5 -11
  91. package/pipeline/schemas/agent-state.schema.json +39 -9
  92. package/pipeline/schemas/analysis-output.schema.json +18 -4
  93. package/pipeline/schemas/analysis-spec.schema.json +120 -32
  94. package/pipeline/schemas/clarify-output.schema.json +15 -5
  95. package/pipeline/schemas/design-check-config.schema.json +32 -11
  96. package/pipeline/schemas/dev-critic-output.schema.json +20 -5
  97. package/pipeline/schemas/figma-project-config.schema.json +42 -10
  98. package/pipeline/schemas/learnings-ledger.schema.json +10 -2
  99. package/pipeline/schemas/migrations/figma-config-1.0.0-to-2.0.0.mjs +1 -4
  100. package/pipeline/schemas/migrations/prefs-2.0.0-to-2.1.0.mjs +24 -7
  101. package/pipeline/schemas/plan-todos.schema.json +6 -3
  102. package/pipeline/schemas/planning-output.schema.json +5 -1
  103. package/pipeline/schemas/prefs.schema.json +91 -229
  104. package/pipeline/schemas/test-gap.schema.json +5 -5
  105. package/pipeline/schemas/token-budget.json +8 -8
  106. package/pipeline/schemas/triage-corpus.schema.json +1 -1
  107. package/pipeline/scripts/aggregate-metrics.mjs +18 -6
  108. package/pipeline/scripts/build-skills-index.mjs +6 -2
  109. package/pipeline/scripts/build-stack-plugins.mjs +142 -39
  110. package/pipeline/scripts/check-derived-drift.mjs +196 -0
  111. package/pipeline/scripts/check-md-links.mjs +6 -2
  112. package/pipeline/scripts/classify-plan-safety.mjs +20 -7
  113. package/pipeline/scripts/cost-budget-check.mjs +2 -1
  114. package/pipeline/scripts/cost-table.json +1 -1
  115. package/pipeline/scripts/diff-explain.mjs +7 -3
  116. package/pipeline/scripts/diff-risk-score.mjs +13 -3
  117. package/pipeline/scripts/eval-golden-tasks-live.mjs +8 -3
  118. package/pipeline/scripts/eval-golden-tasks.mjs +21 -9
  119. package/pipeline/scripts/eval-intent.mjs +8 -4
  120. package/pipeline/scripts/eval-mine-corpus.mjs +14 -4
  121. package/pipeline/scripts/evidence-gate.mjs +7 -2
  122. package/pipeline/scripts/fixtures/install-layout.tsv +3 -3
  123. package/pipeline/scripts/gen-mode-dispatch.mjs +38 -21
  124. package/pipeline/scripts/gen-skills-index.mjs +18 -3
  125. package/pipeline/scripts/learning-curve.mjs +13 -3
  126. package/pipeline/scripts/learnings-ledger.mjs +103 -36
  127. package/pipeline/scripts/lint-mcp-refs.mjs +13 -2
  128. package/pipeline/scripts/lint-skills.mjs +20 -9
  129. package/pipeline/scripts/localize-commands.mjs +6 -1
  130. package/pipeline/scripts/match-skills.mjs +15 -4
  131. package/pipeline/scripts/migrate-prefs.mjs +33 -16
  132. package/pipeline/scripts/phase-tracker.sh +3 -1
  133. package/pipeline/scripts/repo-map.mjs +110 -64
  134. package/pipeline/scripts/review-scope.mjs +7 -1
  135. package/pipeline/scripts/routine-registry.mjs +4 -9
  136. package/pipeline/scripts/run-aggregator.mjs +11 -5
  137. package/pipeline/scripts/run-metrics.mjs +13 -8
  138. package/pipeline/scripts/run-smokes.mjs +57 -3
  139. package/pipeline/scripts/scorecard.mjs +258 -0
  140. package/pipeline/scripts/smoke-context-budget.sh +72 -0
  141. package/pipeline/scripts/smoke-model-fallback.sh +1 -1
  142. package/pipeline/scripts/smoke-no-mcp-in-dev-phases.sh +86 -7
  143. package/pipeline/scripts/smoke-own-punctuation.sh +103 -0
  144. package/pipeline/scripts/smoke-workflow-audit.sh +43 -11
  145. package/pipeline/scripts/smoke-write-state.sh +49 -5
  146. package/pipeline/scripts/test-gap-rules/android.json +11 -11
  147. package/pipeline/scripts/test-gap-rules/ios.json +16 -11
  148. package/pipeline/scripts/test-gap-rules/node.json +19 -7
  149. package/pipeline/scripts/test-gap-rules/python.json +10 -4
  150. package/pipeline/scripts/test-gap-scan.mjs +44 -12
  151. package/pipeline/scripts/test-integrity-gate.mjs +5 -1
  152. package/pipeline/scripts/token-budget-report.mjs +44 -21
  153. package/pipeline/scripts/triage-memory.mjs +126 -34
  154. package/pipeline/scripts/uninstall.mjs +74 -30
  155. package/pipeline/scripts/validate-analysis-doc.mjs +15 -5
  156. package/pipeline/scripts/validate-diff-risk.mjs +32 -18
  157. package/pipeline/scripts/validate-test-gap.mjs +17 -7
  158. package/pipeline/scripts/validate-triage.mjs +17 -5
  159. package/pipeline/scripts/write-state.mjs +32 -9
  160. package/pipeline/skills/.skills-index.json +91 -91
  161. package/pipeline/skills/shared/README.md +57 -57
  162. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +1 -1
  163. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +1 -1
  164. package/pipeline/skills/shared/core/multi-agent/SKILL.md +26 -279
  165. package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +1 -1
  166. package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +1 -1
  167. package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +1 -1
  168. package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
  169. package/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md +1 -1
  170. package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +1 -1
  171. package/pipeline/skills/shared/core/multi-agent-dev/SKILL.md +1 -1
  172. package/pipeline/skills/shared/core/multi-agent-dev-autopilot/SKILL.md +1 -1
  173. package/pipeline/skills/shared/core/multi-agent-dev-local/SKILL.md +1 -1
  174. package/pipeline/skills/shared/core/multi-agent-dev-local-autopilot/SKILL.md +1 -1
  175. package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +1 -1
  176. package/pipeline/skills/shared/core/multi-agent-finish/SKILL.md +1 -1
  177. package/pipeline/skills/shared/core/multi-agent-forget/SKILL.md +1 -1
  178. package/pipeline/skills/shared/core/multi-agent-garbage-collect/SKILL.md +1 -1
  179. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +1 -1
  180. package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +1 -1
  181. package/pipeline/skills/shared/core/multi-agent-jira/SKILL.md +1 -1
  182. package/pipeline/skills/shared/core/multi-agent-kill/SKILL.md +1 -1
  183. package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +1 -1
  184. package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +1 -1
  185. package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +1 -1
  186. package/pipeline/skills/shared/core/multi-agent-log/SKILL.md +1 -1
  187. package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +1 -1
  188. package/pipeline/skills/shared/core/multi-agent-prune-logs/SKILL.md +1 -1
  189. package/pipeline/skills/shared/core/multi-agent-purge/SKILL.md +1 -1
  190. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +16 -8
  191. package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +1 -1
  192. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +1 -1
  193. package/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md +1 -1
  194. package/pipeline/skills/shared/core/multi-agent-review-jira/SKILL.md +1 -1
  195. package/pipeline/skills/shared/core/multi-agent-routines/SKILL.md +1 -1
  196. package/pipeline/skills/shared/core/multi-agent-save/SKILL.md +1 -1
  197. package/pipeline/skills/shared/core/multi-agent-scan/SKILL.md +1 -1
  198. package/pipeline/skills/shared/core/multi-agent-search/SKILL.md +1 -1
  199. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +1 -1
  200. package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +3 -3
  201. package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +1 -1
  202. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +1 -1
  203. package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +1 -1
  204. package/pipeline/skills/shared/core/multi-agent-uninstall/SKILL.md +1 -1
  205. package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +1 -1
  206. package/pipeline/skills/shared/external/accessibility-compliance-accessibility-audit/SKILL.md +1 -1
  207. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +4 -4
  208. package/pipeline/skills/shared/external/agentflow/SKILL.md +1 -1
  209. package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +1 -1
  210. package/pipeline/skills/shared/external/android_ui_verification/SKILL.md +1 -1
  211. package/pipeline/skills/shared/external/api-patterns/SKILL.md +1 -1
  212. package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +1 -1
  213. package/pipeline/skills/shared/external/app-store-changelog/SKILL.md +1 -1
  214. package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
  215. package/pipeline/skills/shared/external/backlog/SKILL.md +12 -12
  216. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -1
  217. package/pipeline/skills/shared/external/context-compression/SKILL.md +1 -1
  218. package/pipeline/skills/shared/external/council/SKILL.md +3 -3
  219. package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -1
  220. package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -1
  221. package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +1 -1
  222. package/pipeline/skills/shared/external/docker-expert/SKILL.md +1 -1
  223. package/pipeline/skills/shared/external/fastapi-pro/SKILL.md +1 -1
  224. package/pipeline/skills/shared/external/firebase/SKILL.md +1 -1
  225. package/pipeline/skills/shared/external/github-actions-templates/SKILL.md +1 -1
  226. package/pipeline/skills/shared/external/help-skills/SKILL.md +1 -1
  227. package/pipeline/skills/shared/external/hig-components-content/SKILL.md +1 -1
  228. package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +1 -1
  229. package/pipeline/skills/shared/external/hig-components-status/SKILL.md +1 -1
  230. package/pipeline/skills/shared/external/hig-components-system/SKILL.md +1 -1
  231. package/pipeline/skills/shared/external/hig-foundations/SKILL.md +1 -1
  232. package/pipeline/skills/shared/external/hig-inputs/SKILL.md +1 -1
  233. package/pipeline/skills/shared/external/hig-patterns/SKILL.md +1 -1
  234. package/pipeline/skills/shared/external/hig-platforms/SKILL.md +1 -1
  235. package/pipeline/skills/shared/external/hig-technologies/SKILL.md +1 -1
  236. package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -1
  237. package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -1
  238. package/pipeline/skills/shared/external/ios-debugger-agent/SKILL.md +1 -1
  239. package/pipeline/skills/shared/external/ios-developer/SKILL.md +1 -1
  240. package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +1 -1
  241. package/pipeline/skills/shared/external/macos-menubar-tuist-app/SKILL.md +1 -1
  242. package/pipeline/skills/shared/external/macos-spm-app-packaging/SKILL.md +1 -1
  243. package/pipeline/skills/shared/external/monorepo-architect/SKILL.md +1 -1
  244. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -1
  245. package/pipeline/skills/shared/external/nodejs-backend-patterns/SKILL.md +1 -1
  246. package/pipeline/skills/shared/external/observability-engineer/SKILL.md +1 -1
  247. package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -1
  248. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -1
  249. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -1
  250. package/pipeline/skills/shared/external/search-first/SKILL.md +2 -2
  251. package/pipeline/skills/shared/external/skill-creator/SKILL.md +12 -12
  252. package/pipeline/skills/shared/external/skill-creator/audit.md +21 -21
  253. package/pipeline/skills/shared/external/skill-creator/checklist.md +3 -3
  254. package/pipeline/skills/shared/external/skill-creator/examples.md +10 -10
  255. package/pipeline/skills/shared/external/skill-creator/label-check.md +17 -17
  256. package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +86 -50
  257. package/pipeline/skills/shared/external/skill-creator/template.md +9 -9
  258. package/pipeline/skills/shared/external/swift-concurrency-expert/SKILL.md +1 -1
  259. package/pipeline/skills/shared/external/swiftui-performance-audit/SKILL.md +1 -1
  260. package/pipeline/skills/shared/external/swiftui-ui-patterns/SKILL.md +1 -1
  261. package/pipeline/skills/shared/external/swiftui-view-refactor/SKILL.md +1 -1
  262. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -1
  263. package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -1
  264. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -1
  265. package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -1
  266. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -1
  267. package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -1
  268. package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -1
  269. package/pipeline/skills/shared/external/xcode-build-benchmark/schemas/build-benchmark.schema.json +9 -49
  270. package/pipeline/skills/skills-index.md +57 -57
  271. package/pipeline/scripts/smoke-plugin-validate.sh +0 -64
@@ -93,21 +93,65 @@ else
93
93
  fi
94
94
 
95
95
  # 10. Concurrent writers don't corrupt state
96
+ #
97
+ # Was 5 writers, which lost an update roughly one run in five on a loaded
98
+ # machine and never on an idle one, so it read as a flaky test rather than the
99
+ # data-loss bug it was. 20 writers puts enough writers in the acquire window to
100
+ # make a regression show up on an idle machine too.
96
101
  STATE2="$TMPDIR/concurrent.json"
102
+ WRITERS=20
97
103
  echo '{"counter":0}' | node "$WRITER" "$STATE2"
98
- for i in 1 2 3 4 5; do
104
+ for i in $(seq 1 "$WRITERS"); do
99
105
  echo "{\"w$i\":$i}" | node "$WRITER" "$STATE2" &
100
106
  done
101
107
  wait
102
- # All 5 keys should be present (deep merge)
103
108
  MISSING=0
104
- for i in 1 2 3 4 5; do
109
+ for i in $(seq 1 "$WRITERS"); do
105
110
  grep -q "\"w$i\": $i" "$STATE2" || MISSING=$((MISSING+1))
106
111
  done
107
112
  if [ "$MISSING" -eq 0 ]; then
108
- pass "5 concurrent writers all persisted"
113
+ pass "$WRITERS concurrent writers all persisted"
109
114
  else
110
- fail "$MISSING concurrent writes lost"
115
+ fail "$MISSING of $WRITERS concurrent writes lost"
116
+ fi
117
+
118
+ # 11. A lock with no readable PID must not be treated as stale
119
+ #
120
+ # This is the exact shape of the bug above. The lock used to be created empty
121
+ # and filled on the next line, so a writer that arrived in between read an
122
+ # unparseable PID, called the lock stale, and deleted it while its owner was
123
+ # still working. Both then wrote and one update vanished.
124
+ #
125
+ # An empty lock now has to block until the staleness window elapses, so a
126
+ # writer given a 1s acquire budget must give up with exit 2 rather than help
127
+ # itself to the lock.
128
+ STATE3="$TMPDIR/emptylock.json"
129
+ echo '{"seed":1}' | node "$WRITER" "$STATE3"
130
+ : > "$STATE3.lock"
131
+ LOCK_RC=0
132
+ echo '{"stolen":true}' | WRITE_STATE_LOCK_TIMEOUT_MS=1000 WRITE_STATE_LOCK_STALE_MS=30000 \
133
+ node "$WRITER" "$STATE3" >/dev/null 2>&1 || LOCK_RC=$?
134
+ if [ "$LOCK_RC" -eq 2 ] && ! grep -q '"stolen"' "$STATE3"; then
135
+ pass "empty lock is not stolen (exit 2, state untouched)"
136
+ else
137
+ fail "empty lock was treated as stale (exit $LOCK_RC) - the live-lock race is back"
138
+ fi
139
+ rm -f "$STATE3.lock"
140
+
141
+ # 12. A lock older than the staleness window is still reclaimed
142
+ #
143
+ # The counterpart to 11: refusing to reclaim an unreadable PID must not mean a
144
+ # genuinely abandoned lock blocks forever.
145
+ STATE4="$TMPDIR/stalelock.json"
146
+ echo '{"seed":1}' | node "$WRITER" "$STATE4"
147
+ : > "$STATE4.lock"
148
+ STALE_RC=0
149
+ echo '{"recovered":true}' | WRITE_STATE_LOCK_TIMEOUT_MS=3000 WRITE_STATE_LOCK_STALE_MS=1 \
150
+ node "$WRITER" "$STATE4" >/dev/null 2>&1 || STALE_RC=$?
151
+ if [ "$STALE_RC" -eq 0 ] && grep -q '"recovered"' "$STATE4"; then
152
+ pass "abandoned lock past the staleness window is reclaimed"
153
+ else
154
+ fail "stale lock not reclaimed (exit $STALE_RC) - writers would wedge"
111
155
  fi
112
156
 
113
157
  echo ""
@@ -5,20 +5,20 @@
5
5
  "sourceExtensions": [".kt", ".java"],
6
6
  "testPathSuffixes": ["Test.kt", "Tests.kt", "Test.java"],
7
7
  "testPathContains": ["/src/test/", "/src/androidTest/", "/test/", "/tests/"],
8
- "excludePathGlobs": [
9
- "build/", ".gradle/", "generated/",
10
- "**/BuildConfig.java", "**/R.java"
11
- ],
8
+ "excludePathGlobs": ["build/", ".gradle/", "generated/", "**/BuildConfig.java", "**/R.java"],
12
9
  "publicApiPatterns": [
13
- {"id": "open_class", "regex": "\\bopen\\s+class\\s+([A-Z][A-Za-z0-9_]*)"},
14
- {"id": "open_fun", "regex": "\\bopen\\s+fun\\s+([a-z_][A-Za-z0-9_]*)"},
15
- {"id": "public_fun", "regex": "^\\s*(?:public\\s+)?fun\\s+([a-z_][A-Za-z0-9_]*)"},
16
- {"id": "public_class", "regex": "^\\s*(?:public\\s+)?class\\s+([A-Z][A-Za-z0-9_]*)"},
17
- {"id": "jvmstatic", "regex": "@JvmStatic\\b"},
18
- {"id": "interface", "regex": "^\\s*(?:public\\s+)?interface\\s+([A-Z][A-Za-z0-9_]*)"}
10
+ { "id": "open_class", "regex": "\\bopen\\s+class\\s+([A-Z][A-Za-z0-9_]*)" },
11
+ { "id": "open_fun", "regex": "\\bopen\\s+fun\\s+([a-z_][A-Za-z0-9_]*)" },
12
+ { "id": "public_fun", "regex": "^\\s*(?:public\\s+)?fun\\s+([a-z_][A-Za-z0-9_]*)" },
13
+ { "id": "public_class", "regex": "^\\s*(?:public\\s+)?class\\s+([A-Z][A-Za-z0-9_]*)" },
14
+ { "id": "jvmstatic", "regex": "@JvmStatic\\b" },
15
+ { "id": "interface", "regex": "^\\s*(?:public\\s+)?interface\\s+([A-Z][A-Za-z0-9_]*)" }
19
16
  ],
20
17
  "composablePatterns": [
21
- {"id": "composable_fun", "regex": "@Composable\\s+(?:public\\s+|internal\\s+)?fun\\s+([A-Z][A-Za-z0-9_]*)"}
18
+ {
19
+ "id": "composable_fun",
20
+ "regex": "@Composable\\s+(?:public\\s+|internal\\s+)?fun\\s+([A-Z][A-Za-z0-9_]*)"
21
+ }
22
22
  ],
23
23
  "expectedTestKindsForComposable": ["compose-ui-test"],
24
24
  "hint": "Cover @Composable functions with createComposeRule() + onNodeWithText/Tag/ContentDescription assertions; ViewModels with runTest + Turbine."
@@ -6,22 +6,27 @@
6
6
  "testPathSuffixes": ["Tests.swift", "Test.swift"],
7
7
  "testPathContains": ["/Tests/", "/UnitTests/", "/SnapshotTests/"],
8
8
  "excludePathGlobs": [
9
- "Pods/", "Carthage/", ".build/", "DerivedData/",
10
- "Generated/", "Sourcery/", "_Generated.swift",
9
+ "Pods/",
10
+ "Carthage/",
11
+ ".build/",
12
+ "DerivedData/",
13
+ "Generated/",
14
+ "Sourcery/",
15
+ "_Generated.swift",
11
16
  "Tests/Fixtures/"
12
17
  ],
13
18
  "publicApiPatterns": [
14
- {"id": "public_func", "regex": "\\bpublic\\s+func\\s+([A-Za-z_][A-Za-z0-9_]*)"},
15
- {"id": "public_class", "regex": "\\bpublic\\s+(?:final\\s+)?class\\s+([A-Z][A-Za-z0-9_]*)"},
16
- {"id": "public_struct", "regex": "\\bpublic\\s+struct\\s+([A-Z][A-Za-z0-9_]*)"},
17
- {"id": "public_enum", "regex": "\\bpublic\\s+enum\\s+([A-Z][A-Za-z0-9_]*)"},
18
- {"id": "public_proto", "regex": "\\bpublic\\s+protocol\\s+([A-Z][A-Za-z0-9_]*)"},
19
- {"id": "public_init", "regex": "\\bpublic\\s+init\\b"},
20
- {"id": "objc_export", "regex": "@objc(?:Members)?\\s+(?:public\\s+)?(?:func|class|var)"}
19
+ { "id": "public_func", "regex": "\\bpublic\\s+func\\s+([A-Za-z_][A-Za-z0-9_]*)" },
20
+ { "id": "public_class", "regex": "\\bpublic\\s+(?:final\\s+)?class\\s+([A-Z][A-Za-z0-9_]*)" },
21
+ { "id": "public_struct", "regex": "\\bpublic\\s+struct\\s+([A-Z][A-Za-z0-9_]*)" },
22
+ { "id": "public_enum", "regex": "\\bpublic\\s+enum\\s+([A-Z][A-Za-z0-9_]*)" },
23
+ { "id": "public_proto", "regex": "\\bpublic\\s+protocol\\s+([A-Z][A-Za-z0-9_]*)" },
24
+ { "id": "public_init", "regex": "\\bpublic\\s+init\\b" },
25
+ { "id": "objc_export", "regex": "@objc(?:Members)?\\s+(?:public\\s+)?(?:func|class|var)" }
21
26
  ],
22
27
  "swiftUiViewPatterns": [
23
- {"id": "view_struct", "regex": "struct\\s+([A-Z][A-Za-z0-9_]*)\\s*:\\s*View\\b"},
24
- {"id": "config_struct", "regex": "struct\\s+([A-Z][A-Za-z0-9_]*Configuration)\\b"}
28
+ { "id": "view_struct", "regex": "struct\\s+([A-Z][A-Za-z0-9_]*)\\s*:\\s*View\\b" },
29
+ { "id": "config_struct", "regex": "struct\\s+([A-Z][A-Za-z0-9_]*Configuration)\\b" }
25
30
  ],
26
31
  "expectedTestKindsForView": ["snapshot", "viewinspector", "unit"],
27
32
  "snapshotPathHints": ["SnapshotTests", "__Snapshots__"],
@@ -3,15 +3,27 @@
3
3
  "stack": "node",
4
4
  "description": "Node.js / TypeScript test gap rules. Sources in *.ts/*.tsx/*.js/*.mjs; tests in *.test.* / *.spec.* / __tests__/.",
5
5
  "sourceExtensions": [".ts", ".tsx", ".js", ".mjs", ".cjs"],
6
- "testPathSuffixes": [".test.ts", ".test.tsx", ".test.js", ".test.mjs", ".spec.ts", ".spec.tsx", ".spec.js", ".spec.mjs"],
7
- "testPathContains": ["/__tests__/", "/test/", "/tests/"],
8
- "excludePathGlobs": [
9
- "node_modules/", "dist/", "build/", ".next/", "coverage/",
10
- "*.d.ts"
6
+ "testPathSuffixes": [
7
+ ".test.ts",
8
+ ".test.tsx",
9
+ ".test.js",
10
+ ".test.mjs",
11
+ ".spec.ts",
12
+ ".spec.tsx",
13
+ ".spec.js",
14
+ ".spec.mjs"
11
15
  ],
16
+ "testPathContains": ["/__tests__/", "/test/", "/tests/"],
17
+ "excludePathGlobs": ["node_modules/", "dist/", "build/", ".next/", "coverage/", "*.d.ts"],
12
18
  "publicApiPatterns": [
13
- {"id": "named_export", "regex": "^export\\s+(?:async\\s+)?(?:function|class|const|let|interface|type|enum)\\s+([A-Za-z_$][A-Za-z0-9_$]*)"},
14
- {"id": "default_export", "regex": "^export\\s+default\\s+(?:async\\s+)?(?:function|class)\\s+([A-Za-z_$][A-Za-z0-9_$]*)"}
19
+ {
20
+ "id": "named_export",
21
+ "regex": "^export\\s+(?:async\\s+)?(?:function|class|const|let|interface|type|enum)\\s+([A-Za-z_$][A-Za-z0-9_$]*)"
22
+ },
23
+ {
24
+ "id": "default_export",
25
+ "regex": "^export\\s+default\\s+(?:async\\s+)?(?:function|class)\\s+([A-Za-z_$][A-Za-z0-9_$]*)"
26
+ }
15
27
  ],
16
28
  "hint": "Cover named exports with vitest/jest test('<name>', ...). React components: React Testing Library (render + screen + userEvent)."
17
29
  }
@@ -7,13 +7,19 @@
7
7
  "testPathPrefixes": ["test_"],
8
8
  "testPathContains": ["/tests/", "/test/"],
9
9
  "excludePathGlobs": [
10
- "__pycache__/", ".venv/", "venv/", ".tox/", "build/", "dist/", "*.egg-info/",
10
+ "__pycache__/",
11
+ ".venv/",
12
+ "venv/",
13
+ ".tox/",
14
+ "build/",
15
+ "dist/",
16
+ "*.egg-info/",
11
17
  "**/migrations/**"
12
18
  ],
13
19
  "publicApiPatterns": [
14
- {"id": "top_def", "regex": "^def\\s+([a-z_][a-z0-9_]*)"},
15
- {"id": "top_class", "regex": "^class\\s+([A-Z][A-Za-z0-9_]*)"},
16
- {"id": "async_def", "regex": "^async\\s+def\\s+([a-z_][a-z0-9_]*)"}
20
+ { "id": "top_def", "regex": "^def\\s+([a-z_][a-z0-9_]*)" },
21
+ { "id": "top_class", "regex": "^class\\s+([A-Z][A-Za-z0-9_]*)" },
22
+ { "id": "async_def", "regex": "^async\\s+def\\s+([a-z_][a-z0-9_]*)" }
17
23
  ],
18
24
  "hint": "Cover with pytest test_<symbol> style. Async with pytest-asyncio. Avoid name collision with private helpers (prefix _)."
19
25
  }
@@ -90,11 +90,20 @@ function git(args) {
90
90
 
91
91
  function resolveBase() {
92
92
  if (BASE) return BASE;
93
- for (const ref of ["origin/main", "main", "origin/master", "master", "develop", "origin/develop"]) {
93
+ for (const ref of [
94
+ "origin/main",
95
+ "main",
96
+ "origin/master",
97
+ "master",
98
+ "develop",
99
+ "origin/develop",
100
+ ]) {
94
101
  try {
95
102
  execFileSync("git", ["-C", ROOT, "rev-parse", "--verify", ref], { stdio: "ignore" });
96
103
  return ref;
97
- } catch { /* continue */ }
104
+ } catch {
105
+ /* continue */
106
+ }
98
107
  }
99
108
  throw new Error("no base ref found");
100
109
  }
@@ -133,9 +142,15 @@ function isExcluded(path) {
133
142
  }
134
143
 
135
144
  function isTestPath(path) {
136
- if (Array.isArray(rules.testPathSuffixes) && rules.testPathSuffixes.some((s) => path.endsWith(s))) return true;
137
- if (Array.isArray(rules.testPathPrefixes) && rules.testPathPrefixes.some((p) => basename(path).startsWith(p))) return true;
138
- if (Array.isArray(rules.testPathContains) && rules.testPathContains.some((c) => path.includes(c))) return true;
145
+ if (Array.isArray(rules.testPathSuffixes) && rules.testPathSuffixes.some((s) => path.endsWith(s)))
146
+ return true;
147
+ if (
148
+ Array.isArray(rules.testPathPrefixes) &&
149
+ rules.testPathPrefixes.some((p) => basename(path).startsWith(p))
150
+ )
151
+ return true;
152
+ if (Array.isArray(rules.testPathContains) && rules.testPathContains.some((c) => path.includes(c)))
153
+ return true;
139
154
  return false;
140
155
  }
141
156
 
@@ -227,8 +242,11 @@ function scanTreeForTests() {
227
242
  if (!SCAN_TREE) return hits;
228
243
  const visit = (dir) => {
229
244
  let entries;
230
- try { entries = readdirSync(dir, { withFileTypes: true }); }
231
- catch { return; }
245
+ try {
246
+ entries = readdirSync(dir, { withFileTypes: true });
247
+ } catch {
248
+ return;
249
+ }
232
250
  for (const ent of entries) {
233
251
  const full = join(dir, ent.name);
234
252
  const rel = full.replace(ROOT + "/", "");
@@ -238,7 +256,11 @@ function scanTreeForTests() {
238
256
  visit(full);
239
257
  } else if (ent.isFile() && isTestPath(rel)) {
240
258
  let body;
241
- try { body = readFileSync(full, "utf8"); } catch { continue; }
259
+ try {
260
+ body = readFileSync(full, "utf8");
261
+ } catch {
262
+ continue;
263
+ }
242
264
  // crude symbol mention: any Identifier-ish token. Cap body size.
243
265
  const slice = body.slice(0, 400_000);
244
266
  const tokens = slice.match(/[A-Za-z_][A-Za-z0-9_]{2,}/g) || [];
@@ -258,7 +280,14 @@ function severityFor(symbol, kind) {
258
280
  // Public API additions without tests are important; pure boolean exports
259
281
  // (e.g. const FLAG = true) tend to be suggestion-tier.
260
282
  if (kind === "objc_export" || kind === "interface" || kind === "public_proto") return "important";
261
- if (kind.startsWith("public_") || kind === "open_class" || kind === "open_fun" || kind === "named_export" || kind === "default_export") return "suggestion";
283
+ if (
284
+ kind.startsWith("public_") ||
285
+ kind === "open_class" ||
286
+ kind === "open_fun" ||
287
+ kind === "named_export" ||
288
+ kind === "default_export"
289
+ )
290
+ return "suggestion";
262
291
  if (kind === "view_struct" || kind === "config_struct") return "important";
263
292
  if (kind === "composable_fun") return "important";
264
293
  if (kind === "top_def" || kind === "top_class" || kind === "async_def") return "suggestion";
@@ -272,7 +301,10 @@ function symbolMentionedInTest(diffText, symbol) {
272
301
  let inTestFile = false;
273
302
  for (const line of lines) {
274
303
  const fm = line.match(/^diff --git a\/(.+?) b\/(.+)$/);
275
- if (fm) { inTestFile = isTestPath(fm[2]); continue; }
304
+ if (fm) {
305
+ inTestFile = isTestPath(fm[2]);
306
+ continue;
307
+ }
276
308
  if (!inTestFile) continue;
277
309
  if (!line.startsWith("+")) continue;
278
310
  if (re.test(line)) return true;
@@ -327,8 +359,8 @@ function main() {
327
359
  testsScanned: tests.length,
328
360
  gapCount: gaps.length,
329
361
  gapBySeverity: {
330
- blocking: gaps.filter((g) => g.severity === "blocking").length,
331
- important: gaps.filter((g) => g.severity === "important").length,
362
+ blocking: gaps.filter((g) => g.severity === "blocking").length,
363
+ important: gaps.filter((g) => g.severity === "important").length,
332
364
  suggestion: gaps.filter((g) => g.severity === "suggestion").length,
333
365
  },
334
366
  };
@@ -76,7 +76,11 @@ function main() {
76
76
  }
77
77
 
78
78
  process.stdout.write(
79
- JSON.stringify({ schemaVersion: "1.0.0", count: findings.length, findings }, null, PRETTY ? 2 : 0),
79
+ JSON.stringify(
80
+ { schemaVersion: "1.0.0", count: findings.length, findings },
81
+ null,
82
+ PRETTY ? 2 : 0,
83
+ ),
80
84
  );
81
85
  }
82
86
 
@@ -23,10 +23,10 @@ import os from "node:os";
23
23
  import path from "node:path";
24
24
 
25
25
  const args = parseArgs(process.argv.slice(2));
26
- const METRICS = process.env.METRICS_FILE
27
- || path.join(os.homedir(), ".claude/logs/multi-agent/metrics.jsonl");
28
- const BUDGET = process.env.BUDGET_FILE
29
- || path.join(os.homedir(), ".claude/configs/token-budget.json");
26
+ const METRICS =
27
+ process.env.METRICS_FILE || path.join(os.homedir(), ".claude/logs/multi-agent/metrics.jsonl");
28
+ const BUDGET =
29
+ process.env.BUDGET_FILE || path.join(os.homedir(), ".claude/configs/token-budget.json");
30
30
  const SUMMARY = path.join(os.homedir(), ".claude/logs/multi-agent/metrics-summary.md");
31
31
 
32
32
  const PERIOD_DAYS = { daily: 1, weekly: 7, monthly: 30 };
@@ -41,17 +41,28 @@ if (!fs.existsSync(METRICS)) {
41
41
 
42
42
  const lines = fs.readFileSync(METRICS, "utf8").split("\n").filter(Boolean);
43
43
  const events = lines
44
- .map(l => { try { return JSON.parse(l); } catch { return null; } })
45
- .filter(e => e && e.ts)
46
- .filter(e => Date.parse(e.ts) >= cutoffMs)
47
- .filter(e => !args.task || e.task_id === args.task);
44
+ .map((l) => {
45
+ try {
46
+ return JSON.parse(l);
47
+ } catch {
48
+ return null;
49
+ }
50
+ })
51
+ .filter((e) => e && e.ts)
52
+ .filter((e) => Date.parse(e.ts) >= cutoffMs)
53
+ .filter((e) => !args.task || e.task_id === args.task);
48
54
 
49
55
  // Per-task aggregation
50
56
  const byTask = new Map();
51
57
  for (const e of events) {
52
- if (!byTask.has(e.task_id)) byTask.set(e.task_id, {
53
- tokens_in: 0, tokens_out: 0, premium_calls: 0, phases: new Set(), models: new Set()
54
- });
58
+ if (!byTask.has(e.task_id))
59
+ byTask.set(e.task_id, {
60
+ tokens_in: 0,
61
+ tokens_out: 0,
62
+ premium_calls: 0,
63
+ phases: new Set(),
64
+ models: new Set(),
65
+ });
55
66
  const t = byTask.get(e.task_id);
56
67
  t.phases.add(e.phase);
57
68
  const d = e.details || {};
@@ -64,21 +75,26 @@ for (const e of events) {
64
75
  // Budget (best-effort)
65
76
  let budget = {};
66
77
  if (fs.existsSync(BUDGET)) {
67
- try { budget = JSON.parse(fs.readFileSync(BUDGET, "utf8")); }
68
- catch { /* tolerate malformed */ }
78
+ try {
79
+ budget = JSON.parse(fs.readFileSync(BUDGET, "utf8"));
80
+ } catch {
81
+ /* tolerate malformed */
82
+ }
69
83
  }
70
84
  const limits = {
71
85
  perTaskTokens: budget.perTaskTokens || 250000,
72
86
  perTaskTokensMultiRepo: budget.perTaskTokensMultiRepo || 500000,
73
- perTaskUsd: budget.perTaskUsd || 2.50,
74
- perTaskUsdMultiRepo: budget.perTaskUsdMultiRepo || 5.00,
87
+ perTaskUsd: budget.perTaskUsd || 2.5,
88
+ perTaskUsdMultiRepo: budget.perTaskUsdMultiRepo || 5.0,
75
89
  };
76
90
  // Crude USD estimate: opus ~$15/M in, $75/M out; sonnet ~$3/M, $15/M out.
77
91
  function estimateUsd(t) {
78
92
  const inK = t.tokens_in / 1e6;
79
93
  const outK = t.tokens_out / 1e6;
80
94
  const opusShare = t.models.has("opus") ? 0.5 : 0; // rough
81
- return (inK * (15 * opusShare + 3 * (1 - opusShare))) + (outK * (75 * opusShare + 15 * (1 - opusShare)));
95
+ return (
96
+ inK * (15 * opusShare + 3 * (1 - opusShare)) + outK * (75 * opusShare + 15 * (1 - opusShare))
97
+ );
82
98
  }
83
99
 
84
100
  const summary = {
@@ -100,14 +116,18 @@ for (const [taskId, t] of byTask) {
100
116
  const isMulti = t.phases.has("4") && t.premium_calls > 5; // heuristic
101
117
  const tokenLimit = isMulti ? limits.perTaskTokensMultiRepo : limits.perTaskTokens;
102
118
  const usdLimit = isMulti ? limits.perTaskUsdMultiRepo : limits.perTaskUsd;
103
- if (total > tokenLimit) summary.breaches.push({ taskId, kind: "tokens", actual: total, limit: tokenLimit });
104
- if (usd > usdLimit) summary.breaches.push({ taskId, kind: "usd", actual: usd.toFixed(2), limit: usdLimit });
119
+ if (total > tokenLimit)
120
+ summary.breaches.push({ taskId, kind: "tokens", actual: total, limit: tokenLimit });
121
+ if (usd > usdLimit)
122
+ summary.breaches.push({ taskId, kind: "usd", actual: usd.toFixed(2), limit: usdLimit });
105
123
  }
106
124
 
107
125
  if (args.json) {
108
126
  console.log(JSON.stringify(summary, null, 2));
109
127
  } else {
110
- console.log(`Token & cost report - ${summary.period} (${summary.windowStart} → ${summary.windowEnd})`);
128
+ console.log(
129
+ `Token & cost report - ${summary.period} (${summary.windowStart} → ${summary.windowEnd})`,
130
+ );
111
131
  console.log(` tasks: ${summary.taskCount}`);
112
132
  console.log(` tokens in: ${summary.totalTokensIn.toLocaleString()}`);
113
133
  console.log(` tokens out: ${summary.totalTokensOut.toLocaleString()}`);
@@ -123,10 +143,13 @@ if (args.json) {
123
143
  }
124
144
 
125
145
  if (args.append) {
126
- const row = `| ${summary.windowEnd.slice(0,10)} | ${summary.period} | ${summary.taskCount} | ${summary.totalTokensIn} | ${summary.totalTokensOut} | $${summary.totalUsd.toFixed(2)} | ${summary.breaches.length} |`;
146
+ const row = `| ${summary.windowEnd.slice(0, 10)} | ${summary.period} | ${summary.taskCount} | ${summary.totalTokensIn} | ${summary.totalTokensOut} | $${summary.totalUsd.toFixed(2)} | ${summary.breaches.length} |`;
127
147
  if (!fs.existsSync(SUMMARY)) {
128
148
  fs.mkdirSync(path.dirname(SUMMARY), { recursive: true });
129
- fs.writeFileSync(SUMMARY, "# Multi-Agent Pipeline Metrics Summary\n\n| date | period | tasks | tokens_in | tokens_out | est_usd | breaches |\n|---|---|---|---|---|---|---|\n");
149
+ fs.writeFileSync(
150
+ SUMMARY,
151
+ "# Multi-Agent Pipeline Metrics Summary\n\n| date | period | tasks | tokens_in | tokens_out | est_usd | breaches |\n|---|---|---|---|---|---|---|\n",
152
+ );
130
153
  }
131
154
  fs.appendFileSync(SUMMARY, row + "\n");
132
155
  }