@bonesofspring/ai-rules 0.2.21 → 0.2.23

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 (363) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +6 -2
  3. package/bin/cli.js +32 -8
  4. package/bin/mavis-install.js +441 -0
  5. package/package.json +9 -2
  6. package/presets/_shared/core/agent-team/agent-artifact-contracts.md +2 -0
  7. package/presets/_shared/core/meta/preset-pr-checklist.md +1 -0
  8. package/presets/_shared/core/meta/preset-twin-sync.md +17 -1
  9. package/presets/claude/android-kotlin/agents/build-verifier.md +2 -0
  10. package/presets/claude/android-kotlin/agents/code-reviewer.md +2 -0
  11. package/presets/claude/android-kotlin/agents/debugger.md +1 -0
  12. package/presets/claude/android-kotlin/agents/feature-developer.md +3 -0
  13. package/presets/claude/android-kotlin/agents/qa-tester.md +2 -0
  14. package/presets/claude/android-kotlin/agents/task-router.md +4 -0
  15. package/presets/claude/android-kotlin/hooks/chain-team-phases.sh +51 -11
  16. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-pr-checklist.md +1 -0
  17. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-twin-sync.md +17 -1
  18. package/presets/claude/go/agents/build-verifier.md +4 -0
  19. package/presets/claude/go/agents/code-reviewer.md +4 -0
  20. package/presets/claude/go/agents/debugger.md +4 -0
  21. package/presets/claude/go/agents/feature-developer.md +5 -0
  22. package/presets/claude/go/agents/qa-tester.md +4 -0
  23. package/presets/claude/go/agents/task-router.md +4 -0
  24. package/presets/claude/go/hooks/chain-team-phases.sh +51 -11
  25. package/presets/claude/go/rules/tooling-and-review/preset-pr-checklist.md +1 -0
  26. package/presets/claude/go/rules/tooling-and-review/preset-twin-sync.md +17 -1
  27. package/presets/claude/ios-swift/agents/build-verifier.md +2 -0
  28. package/presets/claude/ios-swift/agents/code-reviewer.md +2 -0
  29. package/presets/claude/ios-swift/agents/debugger.md +1 -0
  30. package/presets/claude/ios-swift/agents/feature-developer.md +3 -0
  31. package/presets/claude/ios-swift/agents/qa-tester.md +2 -0
  32. package/presets/claude/ios-swift/agents/task-router.md +4 -0
  33. package/presets/claude/ios-swift/hooks/chain-team-phases.sh +51 -11
  34. package/presets/claude/ios-swift/rules/tooling-and-review/preset-pr-checklist.md +1 -0
  35. package/presets/claude/ios-swift/rules/tooling-and-review/preset-twin-sync.md +17 -1
  36. package/presets/claude/java/agents/build-verifier.md +4 -0
  37. package/presets/claude/java/agents/code-reviewer.md +4 -0
  38. package/presets/claude/java/agents/debugger.md +4 -0
  39. package/presets/claude/java/agents/feature-developer.md +5 -0
  40. package/presets/claude/java/agents/qa-tester.md +4 -0
  41. package/presets/claude/java/agents/task-router.md +4 -0
  42. package/presets/claude/java/hooks/chain-team-phases.sh +51 -11
  43. package/presets/claude/java/rules/tooling-and-review/preset-pr-checklist.md +1 -0
  44. package/presets/claude/java/rules/tooling-and-review/preset-twin-sync.md +17 -1
  45. package/presets/claude/mcp-ts/agents/build-verifier.md +4 -0
  46. package/presets/claude/mcp-ts/agents/feature-developer.md +5 -0
  47. package/presets/claude/mcp-ts/agents/task-router.md +1 -1
  48. package/presets/claude/mcp-ts/hooks/chain-team-phases.sh +51 -11
  49. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-pr-checklist.md +1 -0
  50. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-twin-sync.md +17 -1
  51. package/presets/claude/next/agents/build-verifier.md +2 -0
  52. package/presets/claude/next/agents/code-reviewer.md +2 -0
  53. package/presets/claude/next/agents/debugger.md +4 -0
  54. package/presets/claude/next/agents/feature-developer.md +3 -0
  55. package/presets/claude/next/agents/qa-tester.md +4 -0
  56. package/presets/claude/next/agents/unit-test-generator.md +1 -1
  57. package/presets/claude/next/agents/unit-test-planner.md +1 -1
  58. package/presets/claude/next/hooks/chain-team-phases.sh +51 -11
  59. package/presets/claude/next/rules/api-and-data/http-client.md +1 -1
  60. package/presets/claude/next/rules/architecture/reference-features.md +1 -1
  61. package/presets/claude/next/rules/testing/README.md +1 -1
  62. package/presets/claude/next/rules/testing/tests-e2e-structure.md +2 -0
  63. package/presets/claude/next/rules/testing/tests-unit.md +3 -5
  64. package/presets/claude/next/rules/tooling-and-review/preset-pr-checklist.md +1 -0
  65. package/presets/claude/next/rules/tooling-and-review/preset-twin-sync.md +17 -1
  66. package/presets/claude/next/rules/ui-and-accessibility/react-ui.md +1 -1
  67. package/presets/claude/next/skills/playwright-e2e/SKILL.md +5 -3
  68. package/presets/claude/next/skills/unit-testing/SKILL.md +6 -4
  69. package/presets/claude/nuxt/agents/build-verifier.md +2 -0
  70. package/presets/claude/nuxt/agents/code-reviewer.md +2 -0
  71. package/presets/claude/nuxt/agents/debugger.md +4 -0
  72. package/presets/claude/nuxt/agents/feature-developer.md +3 -0
  73. package/presets/claude/nuxt/agents/qa-tester.md +4 -0
  74. package/presets/claude/nuxt/hooks/chain-team-phases.sh +51 -11
  75. package/presets/claude/nuxt/rules/tooling-and-review/preset-pr-checklist.md +1 -0
  76. package/presets/claude/nuxt/rules/tooling-and-review/preset-twin-sync.md +17 -1
  77. package/presets/claude/php-hexagonal/agents/build-verifier.md +4 -0
  78. package/presets/claude/php-hexagonal/agents/code-reviewer.md +4 -0
  79. package/presets/claude/php-hexagonal/agents/debugger.md +4 -0
  80. package/presets/claude/php-hexagonal/agents/feature-developer.md +5 -0
  81. package/presets/claude/php-hexagonal/agents/qa-tester.md +4 -0
  82. package/presets/claude/php-hexagonal/agents/task-router.md +4 -0
  83. package/presets/claude/php-hexagonal/hooks/chain-team-phases.sh +51 -11
  84. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-pr-checklist.md +1 -0
  85. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-twin-sync.md +17 -1
  86. package/presets/claude/php-laravel/agents/build-verifier.md +4 -0
  87. package/presets/claude/php-laravel/agents/code-reviewer.md +4 -0
  88. package/presets/claude/php-laravel/agents/debugger.md +4 -0
  89. package/presets/claude/php-laravel/agents/feature-developer.md +5 -0
  90. package/presets/claude/php-laravel/agents/qa-tester.md +4 -0
  91. package/presets/claude/php-laravel/agents/task-router.md +1 -0
  92. package/presets/claude/php-laravel/hooks/chain-team-phases.sh +51 -11
  93. package/presets/claude/php-laravel/rules/tooling-and-review/preset-pr-checklist.md +1 -0
  94. package/presets/claude/php-laravel/rules/tooling-and-review/preset-twin-sync.md +17 -1
  95. package/presets/claude/svelte/agents/build-verifier.md +2 -0
  96. package/presets/claude/svelte/agents/code-reviewer.md +2 -0
  97. package/presets/claude/svelte/agents/debugger.md +4 -0
  98. package/presets/claude/svelte/agents/feature-developer.md +3 -0
  99. package/presets/claude/svelte/agents/qa-tester.md +4 -0
  100. package/presets/claude/svelte/hooks/chain-team-phases.sh +51 -11
  101. package/presets/claude/svelte/rules/tooling-and-review/preset-pr-checklist.md +1 -0
  102. package/presets/claude/svelte/rules/tooling-and-review/preset-twin-sync.md +17 -1
  103. package/presets/cursor/android-kotlin/agents/build-verifier.md +2 -0
  104. package/presets/cursor/android-kotlin/agents/code-reviewer.md +2 -0
  105. package/presets/cursor/android-kotlin/agents/debugger.md +1 -0
  106. package/presets/cursor/android-kotlin/agents/feature-developer.md +3 -0
  107. package/presets/cursor/android-kotlin/agents/qa-tester.md +2 -0
  108. package/presets/cursor/android-kotlin/agents/task-router.md +4 -0
  109. package/presets/cursor/android-kotlin/hooks/chain-team-phases.sh +51 -11
  110. package/presets/cursor/android-kotlin/rules/preset-pr-checklist.mdc +1 -0
  111. package/presets/cursor/android-kotlin/rules/preset-twin-sync.mdc +17 -1
  112. package/presets/cursor/go/agents/build-verifier.md +4 -0
  113. package/presets/cursor/go/agents/code-reviewer.md +4 -0
  114. package/presets/cursor/go/agents/debugger.md +4 -0
  115. package/presets/cursor/go/agents/feature-developer.md +5 -0
  116. package/presets/cursor/go/agents/qa-tester.md +4 -0
  117. package/presets/cursor/go/agents/task-router.md +4 -0
  118. package/presets/cursor/go/hooks/chain-team-phases.sh +51 -11
  119. package/presets/cursor/go/rules/preset-pr-checklist.mdc +1 -0
  120. package/presets/cursor/go/rules/preset-twin-sync.mdc +17 -1
  121. package/presets/cursor/ios-swift/agents/build-verifier.md +2 -0
  122. package/presets/cursor/ios-swift/agents/code-reviewer.md +2 -0
  123. package/presets/cursor/ios-swift/agents/debugger.md +1 -0
  124. package/presets/cursor/ios-swift/agents/feature-developer.md +3 -0
  125. package/presets/cursor/ios-swift/agents/qa-tester.md +2 -0
  126. package/presets/cursor/ios-swift/agents/task-router.md +4 -0
  127. package/presets/cursor/ios-swift/hooks/chain-team-phases.sh +51 -11
  128. package/presets/cursor/ios-swift/rules/preset-pr-checklist.mdc +1 -0
  129. package/presets/cursor/ios-swift/rules/preset-twin-sync.mdc +17 -1
  130. package/presets/cursor/java/agents/build-verifier.md +4 -0
  131. package/presets/cursor/java/agents/code-reviewer.md +4 -0
  132. package/presets/cursor/java/agents/debugger.md +4 -0
  133. package/presets/cursor/java/agents/feature-developer.md +5 -0
  134. package/presets/cursor/java/agents/qa-tester.md +4 -0
  135. package/presets/cursor/java/agents/task-router.md +4 -0
  136. package/presets/cursor/java/hooks/chain-team-phases.sh +51 -11
  137. package/presets/cursor/java/rules/preset-pr-checklist.mdc +1 -0
  138. package/presets/cursor/java/rules/preset-twin-sync.mdc +17 -1
  139. package/presets/cursor/mcp-ts/agents/build-verifier.md +4 -0
  140. package/presets/cursor/mcp-ts/agents/feature-developer.md +5 -0
  141. package/presets/cursor/mcp-ts/agents/task-router.md +1 -1
  142. package/presets/cursor/mcp-ts/hooks/chain-team-phases.sh +51 -11
  143. package/presets/cursor/mcp-ts/rules/preset-pr-checklist.mdc +1 -0
  144. package/presets/cursor/mcp-ts/rules/preset-twin-sync.mdc +17 -1
  145. package/presets/cursor/next/agents/build-verifier.md +2 -0
  146. package/presets/cursor/next/agents/code-reviewer.md +2 -0
  147. package/presets/cursor/next/agents/debugger.md +4 -0
  148. package/presets/cursor/next/agents/feature-developer.md +3 -0
  149. package/presets/cursor/next/agents/qa-tester.md +4 -0
  150. package/presets/cursor/next/agents/unit-test-generator.md +1 -1
  151. package/presets/cursor/next/agents/unit-test-planner.md +1 -1
  152. package/presets/cursor/next/hooks/chain-team-phases.sh +51 -11
  153. package/presets/cursor/next/rules/http-client.mdc +1 -1
  154. package/presets/cursor/next/rules/preset-pr-checklist.mdc +1 -0
  155. package/presets/cursor/next/rules/preset-twin-sync.mdc +17 -1
  156. package/presets/cursor/next/rules/react-ui.mdc +1 -1
  157. package/presets/cursor/next/rules/reference-features.mdc +1 -1
  158. package/presets/cursor/next/rules/tests-e2e-structure.mdc +2 -0
  159. package/presets/cursor/next/rules/tests-unit.mdc +3 -5
  160. package/presets/cursor/next/skills/playwright-e2e/SKILL.md +5 -3
  161. package/presets/cursor/next/skills/unit-testing/SKILL.md +6 -4
  162. package/presets/cursor/nuxt/agents/build-verifier.md +2 -0
  163. package/presets/cursor/nuxt/agents/code-reviewer.md +2 -0
  164. package/presets/cursor/nuxt/agents/debugger.md +4 -0
  165. package/presets/cursor/nuxt/agents/feature-developer.md +3 -0
  166. package/presets/cursor/nuxt/agents/qa-tester.md +4 -0
  167. package/presets/cursor/nuxt/hooks/chain-team-phases.sh +51 -11
  168. package/presets/cursor/nuxt/rules/preset-pr-checklist.mdc +1 -0
  169. package/presets/cursor/nuxt/rules/preset-twin-sync.mdc +17 -1
  170. package/presets/cursor/php-hexagonal/agents/build-verifier.md +4 -0
  171. package/presets/cursor/php-hexagonal/agents/code-reviewer.md +4 -0
  172. package/presets/cursor/php-hexagonal/agents/debugger.md +4 -0
  173. package/presets/cursor/php-hexagonal/agents/feature-developer.md +5 -0
  174. package/presets/cursor/php-hexagonal/agents/qa-tester.md +4 -0
  175. package/presets/cursor/php-hexagonal/agents/task-router.md +4 -0
  176. package/presets/cursor/php-hexagonal/hooks/chain-team-phases.sh +51 -11
  177. package/presets/cursor/php-hexagonal/rules/preset-pr-checklist.mdc +1 -0
  178. package/presets/cursor/php-hexagonal/rules/preset-twin-sync.mdc +17 -1
  179. package/presets/cursor/php-laravel/agents/build-verifier.md +4 -0
  180. package/presets/cursor/php-laravel/agents/code-reviewer.md +4 -0
  181. package/presets/cursor/php-laravel/agents/debugger.md +4 -0
  182. package/presets/cursor/php-laravel/agents/feature-developer.md +5 -0
  183. package/presets/cursor/php-laravel/agents/qa-tester.md +4 -0
  184. package/presets/cursor/php-laravel/agents/task-router.md +1 -0
  185. package/presets/cursor/php-laravel/hooks/chain-team-phases.sh +51 -11
  186. package/presets/cursor/php-laravel/rules/preset-pr-checklist.mdc +1 -0
  187. package/presets/cursor/php-laravel/rules/preset-twin-sync.mdc +17 -1
  188. package/presets/cursor/svelte/agents/build-verifier.md +2 -0
  189. package/presets/cursor/svelte/agents/code-reviewer.md +2 -0
  190. package/presets/cursor/svelte/agents/debugger.md +4 -0
  191. package/presets/cursor/svelte/agents/feature-developer.md +3 -0
  192. package/presets/cursor/svelte/agents/qa-tester.md +4 -0
  193. package/presets/cursor/svelte/hooks/chain-team-phases.sh +51 -11
  194. package/presets/cursor/svelte/rules/preset-pr-checklist.mdc +1 -0
  195. package/presets/cursor/svelte/rules/preset-twin-sync.mdc +17 -1
  196. package/presets/mavis/README.md +60 -0
  197. package/presets/mavis/next/MAVIS.md +67 -0
  198. package/presets/mavis/next/MCP.md +36 -0
  199. package/presets/mavis/next/README.md +15 -0
  200. package/presets/mavis/next/REPO_AGENTS.md +94 -0
  201. package/presets/mavis/next/adapter-map.json +959 -0
  202. package/presets/mavis/next/agents/README.md +206 -0
  203. package/presets/mavis/next/agents/accessibility-reviewer.md +61 -0
  204. package/presets/mavis/next/agents/api-contract-reviewer.md +57 -0
  205. package/presets/mavis/next/agents/build-verifier.md +110 -0
  206. package/presets/mavis/next/agents/ci-investigator.md +63 -0
  207. package/presets/mavis/next/agents/code-reviewer.md +61 -0
  208. package/presets/mavis/next/agents/codebase-analyzer.md +21 -0
  209. package/presets/mavis/next/agents/debugger.md +80 -0
  210. package/presets/mavis/next/agents/feature-developer.md +101 -0
  211. package/presets/mavis/next/agents/migration-specialist.md +67 -0
  212. package/presets/mavis/next/agents/mockup-reviewer.md +53 -0
  213. package/presets/mavis/next/agents/openspec-author.md +157 -0
  214. package/presets/mavis/next/agents/performance-auditor.md +68 -0
  215. package/presets/mavis/next/agents/playwright-test-generator.md +28 -0
  216. package/presets/mavis/next/agents/playwright-test-healer.md +28 -0
  217. package/presets/mavis/next/agents/playwright-test-planner.md +21 -0
  218. package/presets/mavis/next/agents/qa-tester.md +52 -0
  219. package/presets/mavis/next/agents/security-reviewer.md +54 -0
  220. package/presets/mavis/next/agents/solution-architect.md +68 -0
  221. package/presets/mavis/next/agents/task-analyst.md +166 -0
  222. package/presets/mavis/next/agents/task-router.md +178 -0
  223. package/presets/mavis/next/agents/tech-writer.md +57 -0
  224. package/presets/mavis/next/agents/unit-test-generator.md +31 -0
  225. package/presets/mavis/next/agents/unit-test-healer.md +32 -0
  226. package/presets/mavis/next/agents/unit-test-planner.md +54 -0
  227. package/presets/mavis/next/commands/README.md +49 -0
  228. package/presets/mavis/next/commands/spec-start.md +24 -0
  229. package/presets/mavis/next/commands/task-continue.md +50 -0
  230. package/presets/mavis/next/commands/task.md +50 -0
  231. package/presets/mavis/next/commands/technical-retro.md +80 -0
  232. package/presets/mavis/next/hooks/README.md +22 -0
  233. package/presets/mavis/next/hooks/chain-team-phases.sh +845 -0
  234. package/presets/mavis/next/mcp.json +19 -0
  235. package/presets/mavis/next/rules/README.md +16 -0
  236. package/presets/mavis/next/rules/agent-team-intake.md +11 -0
  237. package/presets/mavis/next/rules/agent-team-orchestrator.md +210 -0
  238. package/presets/mavis/next/rules/ai-local-overlay.md +37 -0
  239. package/presets/mavis/next/rules/anti-sycophancy-discipline.md +23 -0
  240. package/presets/mavis/next/rules/api-services.md +72 -0
  241. package/presets/mavis/next/rules/architecture-boundaries-ui.md +9 -0
  242. package/presets/mavis/next/rules/architecture-boundaries.md +86 -0
  243. package/presets/mavis/next/rules/arrow-functions.md +25 -0
  244. package/presets/mavis/next/rules/code-quality-and-refactoring.md +40 -0
  245. package/presets/mavis/next/rules/code-review-mr.md +40 -0
  246. package/presets/mavis/next/rules/css-property-order-stylelint.md +5 -0
  247. package/presets/mavis/next/rules/design-guidance.md +99 -0
  248. package/presets/mavis/next/rules/feature-delivery-workflow.md +49 -0
  249. package/presets/mavis/next/rules/http-client.md +55 -0
  250. package/presets/mavis/next/rules/layer-barrel-exports.md +68 -0
  251. package/presets/mavis/next/rules/mcp-usage.md +86 -0
  252. package/presets/mavis/next/rules/mockup-review.md +107 -0
  253. package/presets/mavis/next/rules/navigation-router-stack.md +56 -0
  254. package/presets/mavis/next/rules/navigation-router-ui.md +10 -0
  255. package/presets/mavis/next/rules/next-app-core.md +29 -0
  256. package/presets/mavis/next/rules/next-app-router.md +44 -0
  257. package/presets/mavis/next/rules/no-props-spread.md +56 -0
  258. package/presets/mavis/next/rules/no-type-assertion-as-import-export.md +51 -0
  259. package/presets/mavis/next/rules/package-manager.md +11 -0
  260. package/presets/mavis/next/rules/playwright-agents.md +67 -0
  261. package/presets/mavis/next/rules/post-change-lint.md +35 -0
  262. package/presets/mavis/next/rules/preset-layering.md +27 -0
  263. package/presets/mavis/next/rules/preset-no-cross-stack-leakage.md +90 -0
  264. package/presets/mavis/next/rules/preset-pr-checklist.md +35 -0
  265. package/presets/mavis/next/rules/preset-token-budget.md +36 -0
  266. package/presets/mavis/next/rules/preset-twin-sync.md +65 -0
  267. package/presets/mavis/next/rules/product-specs-authoring.md +112 -0
  268. package/presets/mavis/next/rules/product-specs.md +56 -0
  269. package/presets/mavis/next/rules/public-imports.md +50 -0
  270. package/presets/mavis/next/rules/react-a11y-coding.md +31 -0
  271. package/presets/mavis/next/rules/react-next-anti-hallucination.md +60 -0
  272. package/presets/mavis/next/rules/react-ui.md +105 -0
  273. package/presets/mavis/next/rules/reference-features.md +45 -0
  274. package/presets/mavis/next/rules/security-next.md +45 -0
  275. package/presets/mavis/next/rules/store-rtk.md +78 -0
  276. package/presets/mavis/next/rules/technical-retro.md +11 -0
  277. package/presets/mavis/next/rules/tests-e2e-structure.md +72 -0
  278. package/presets/mavis/next/rules/tests-unit.md +72 -0
  279. package/presets/mavis/next/rules/types-jsdoc.md +43 -0
  280. package/presets/mavis/next/rules/ux-design.md +99 -0
  281. package/presets/mavis/next/skills/README.md +21 -0
  282. package/presets/mavis/next/skills/ci-investigation/SKILL.md +39 -0
  283. package/presets/mavis/next/skills/code-review/SKILL.md +39 -0
  284. package/presets/mavis/next/skills/debug-investigation/SKILL.md +26 -0
  285. package/presets/mavis/next/skills/feature-delivery/SKILL.md +34 -0
  286. package/presets/mavis/next/skills/mockup-review/SKILL.md +24 -0
  287. package/presets/mavis/next/skills/playwright-e2e/SKILL.md +28 -0
  288. package/presets/mavis/next/skills/react-performance/SKILL.md +39 -0
  289. package/presets/mavis/next/skills/spec-authoring/SKILL.md +21 -0
  290. package/presets/mavis/next/skills/spec-discovery/SKILL.md +53 -0
  291. package/presets/mavis/next/skills/technical-retro/SKILL.md +49 -0
  292. package/presets/mavis/next/skills/unit-testing/SKILL.md +29 -0
  293. package/presets/mavis/next/skills/ux-design/SKILL.md +31 -0
  294. package/presets/mavis/next/skills/write-adr/SKILL.md +36 -0
  295. package/presets/mavis/next/team/README.md +148 -0
  296. package/presets/mavis/next/team/conventions.md +21 -0
  297. package/presets/mavis/next/team/fixtures/artifact-manifest.example.json +79 -0
  298. package/presets/mavis/next/team/fixtures/bugfix-standard.json +25 -0
  299. package/presets/mavis/next/team/fixtures/feature-full.json +29 -0
  300. package/presets/mavis/next/team/fixtures/feature-light.json +27 -0
  301. package/presets/mavis/next/team/fixtures/feature-with-figma-url.json +17 -0
  302. package/presets/mavis/next/team/fixtures/feature-with-ux-no-figma.json +17 -0
  303. package/presets/mavis/next/team/fixtures/memlog.example.md +9 -0
  304. package/presets/mavis/next/team/fixtures/metrics-scenarios/tasks/human-gate/metrics.json +69 -0
  305. package/presets/mavis/next/team/fixtures/metrics-scenarios/tasks/missing-ledger/status.json +5 -0
  306. package/presets/mavis/next/team/fixtures/metrics-scenarios/tasks/parallel-receipts/metrics.json +79 -0
  307. package/presets/mavis/next/team/fixtures/metrics-scenarios/tasks/serial-retry/metrics.json +124 -0
  308. package/presets/mavis/next/team/fixtures/metrics-scenarios/tasks/validation-failed/metrics.json +124 -0
  309. package/presets/mavis/next/team/fixtures/metrics.example.json +54 -0
  310. package/presets/mavis/next/team/fixtures/mockup-review-fail-embedded-status.json +8 -0
  311. package/presets/mavis/next/team/fixtures/mockup-review-standalone.json +14 -0
  312. package/presets/mavis/next/team/fixtures/ux-design-standalone.json +15 -0
  313. package/presets/mavis/next/team/tasks/.gitkeep +0 -0
  314. package/presets/mavis/platform-contract.json +131 -0
  315. package/scripts/capture-cli-golden.mjs +44 -0
  316. package/scripts/check-chain-hook-coverage.sh +1 -0
  317. package/scripts/check-mavis-adapter-map.mjs +294 -0
  318. package/scripts/check-mavis-local-overlay.mjs +56 -0
  319. package/scripts/check-mavis-platform-contract.mjs +388 -0
  320. package/scripts/check-mavis-token-budget.mjs +48 -0
  321. package/scripts/check-preset-leakage.sh +35 -3
  322. package/scripts/check-preset-structure.sh +73 -0
  323. package/scripts/check-preset-token-budget.sh +100 -0
  324. package/scripts/check-shared-core-drift.sh +18 -0
  325. package/scripts/check-task-router-intents.sh +154 -0
  326. package/scripts/fixtures/cli-golden/claude/next.json +130 -0
  327. package/scripts/fixtures/cli-golden/cursor/next.json +132 -0
  328. package/scripts/fixtures/cli-golden/mavis/next.json +125 -0
  329. package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/cursor/AGENTS.md +1 -0
  330. package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/cursor/rules/extra.mdc +1 -0
  331. package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/cursor/rules/sample.mdc +2 -0
  332. package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/mavis/MAVIS.md +1 -0
  333. package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/mavis/adapter-map.json +26 -0
  334. package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/mavis/rules/sample.md +2 -0
  335. package/scripts/fixtures/mavis-adapter-map/valid/cursor/AGENTS.md +1 -0
  336. package/scripts/fixtures/mavis-adapter-map/valid/cursor/rules/sample.mdc +2 -0
  337. package/scripts/fixtures/mavis-adapter-map/valid/mavis/MAVIS.md +1 -0
  338. package/scripts/fixtures/mavis-adapter-map/valid/mavis/adapter-map.json +26 -0
  339. package/scripts/fixtures/mavis-adapter-map/valid/mavis/rules/sample.md +2 -0
  340. package/scripts/fixtures/mavis-platform-contract/invalid-minimax-destination.json +131 -0
  341. package/scripts/fixtures/mavis-platform-contract/invalid-missing-capability.json +126 -0
  342. package/scripts/fixtures/mavis-platform-contract/invalid-released-unknown-stack.json +133 -0
  343. package/scripts/fixtures/mavis-platform-contract/invalid-unknown-native-path.json +131 -0
  344. package/scripts/fixtures/mavis-platform-contract/invalid-version-pin.json +132 -0
  345. package/scripts/fixtures/mavis-platform-contract/valid.json +131 -0
  346. package/scripts/fixtures/mavis-probe/AGENTS.md +6 -0
  347. package/scripts/fixtures/mavis-probe/agents/probe-agent.md +6 -0
  348. package/scripts/fixtures/mavis-probe/fake-mcode.mjs +98 -0
  349. package/scripts/fixtures/mavis-probe/output.schema.json +28 -0
  350. package/scripts/fixtures/mavis-probe/rules/probe.md +5 -0
  351. package/scripts/fixtures/mavis-probe/skills/probe/SKILL.md +11 -0
  352. package/scripts/generate-mavis-next.mjs +704 -0
  353. package/scripts/lib/cli-golden.mjs +50 -0
  354. package/scripts/lib/discover-hooks.mjs +72 -0
  355. package/scripts/probe-mavis-capabilities.mjs +480 -0
  356. package/scripts/test-agent-task-metrics-hooks.mjs +120 -16
  357. package/scripts/test-chain-team-phases-coverage.mjs +110 -8
  358. package/scripts/test-cli-init-clean.mjs +256 -0
  359. package/scripts/test-cli-mcp-merge.mjs +23 -0
  360. package/scripts/test-mavis-context-budget.mjs +55 -0
  361. package/scripts/test-mavis-orchestration.mjs +649 -0
  362. package/scripts/test-probe-mavis-capabilities.mjs +205 -0
  363. package/scripts/test-task-router-intents-fixtures.mjs +109 -0
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * P1 context budget: root AGENTS + .mavis/AGENTS combined nonempty lines ≤ 170.
4
+ * Critical-policy sentinels must appear in the pair.
5
+ */
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+ import { loadReleasedMavisStacks } from './lib/discover-hooks.mjs';
10
+
11
+ const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
12
+ const presetsRoot = path.join(scriptDirectory, '..', 'presets');
13
+ const LINE_CAP = 170;
14
+ const SENTINELS = [
15
+ 'next-app-core',
16
+ 'package-manager',
17
+ 'code-quality-and-refactoring',
18
+ 'ai-local-overlay',
19
+ '.mavis/AGENTS.md',
20
+ 'status.json',
21
+ '~/.minimax',
22
+ ];
23
+
24
+ function nonemptyLines(text) {
25
+ return text.split(/\r?\n/).filter((line) => line.trim().length > 0).length;
26
+ }
27
+
28
+ const stacks = loadReleasedMavisStacks(presetsRoot);
29
+ if (stacks.length === 0) {
30
+ console.log('ok mavis context-budget: no released stacks');
31
+ process.exit(0);
32
+ }
33
+
34
+ let failed = 0;
35
+ for (const stack of stacks) {
36
+ const root = path.join(presetsRoot, 'mavis', stack);
37
+ const repoAgents = fs.readFileSync(path.join(root, 'REPO_AGENTS.md'), 'utf8');
38
+ const mavisAgents = fs.readFileSync(path.join(root, 'MAVIS.md'), 'utf8');
39
+ const combined = `${repoAgents}\n${mavisAgents}`;
40
+ const count = nonemptyLines(repoAgents) + nonemptyLines(mavisAgents);
41
+ if (count > LINE_CAP) {
42
+ console.error(`FAIL mavis/${stack}: entry nonempty lines ${count} > ${LINE_CAP}`);
43
+ failed += 1;
44
+ } else {
45
+ console.log(`ok mavis/${stack}: entry nonempty lines ${count} (cap ${LINE_CAP})`);
46
+ }
47
+ for (const sentinel of SENTINELS) {
48
+ if (!combined.includes(sentinel)) {
49
+ console.error(`FAIL mavis/${stack}: missing critical sentinel ${sentinel}`);
50
+ failed += 1;
51
+ }
52
+ }
53
+ }
54
+
55
+ process.exit(failed === 0 ? 0 : 1);
@@ -0,0 +1,649 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * P2 Mavis orchestration on `.mavis/team` + P3 semi-auto labeling.
4
+ * Native Team is not a lifecycle writer.
5
+ */
6
+ import assert from 'node:assert/strict';
7
+ import { execFileSync, spawn } from 'node:child_process';
8
+ import fs from 'node:fs';
9
+ import os from 'node:os';
10
+ import path from 'node:path';
11
+ import { fileURLToPath } from 'node:url';
12
+
13
+ const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
14
+ const packageDirectory = path.resolve(scriptDirectory, '..');
15
+ const hook = path.join(
16
+ packageDirectory,
17
+ 'presets',
18
+ 'mavis',
19
+ 'next',
20
+ 'hooks',
21
+ 'chain-team-phases.sh',
22
+ );
23
+ const contract = JSON.parse(
24
+ fs.readFileSync(
25
+ path.join(packageDirectory, 'presets', 'mavis', 'platform-contract.json'),
26
+ 'utf8',
27
+ ),
28
+ );
29
+
30
+ assert.equal(fs.existsSync(hook), true, 'mavis/next chain hook must exist');
31
+ assert.equal(
32
+ fs.existsSync(path.join(packageDirectory, 'presets', 'mavis', 'next', 'hooks.json')),
33
+ false,
34
+ 'Mavis must not ship hooks.json',
35
+ );
36
+ assert.equal(contract.nativeCapabilities.projectHooks.status, 'unknown');
37
+ assert.equal(contract.mcp.mode, 'fallback-manual-import');
38
+ console.log('ok P3 semi-auto: projectHooks unknown, no hooks.json');
39
+
40
+ function readJson(filePath) {
41
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
42
+ }
43
+
44
+ function writeJson(filePath, value) {
45
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
46
+ fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
47
+ }
48
+
49
+ function invokeHook(workingDirectory, payload) {
50
+ return execFileSync('bash', [hook], {
51
+ cwd: workingDirectory,
52
+ encoding: 'utf8',
53
+ input: JSON.stringify(payload),
54
+ });
55
+ }
56
+
57
+ function writeTask(root, slug, pipeline, status, metrics, artifacts = {}) {
58
+ const teamRoot = path.join(root, '.mavis', 'team');
59
+ const taskDirectory = path.join(teamRoot, 'tasks', slug);
60
+ fs.mkdirSync(taskDirectory, { recursive: true });
61
+ writeJson(path.join(teamRoot, 'active-task.json'), { slug });
62
+ writeJson(path.join(taskDirectory, 'pipeline.json'), pipeline);
63
+ writeJson(path.join(taskDirectory, 'status.json'), {
64
+ executionMode: 'manual-chain',
65
+ ...status,
66
+ });
67
+ writeJson(path.join(taskDirectory, 'metrics.json'), metrics);
68
+ for (const [name, content] of Object.entries(artifacts)) {
69
+ fs.writeFileSync(path.join(taskDirectory, name), content);
70
+ }
71
+ return taskDirectory;
72
+ }
73
+
74
+ function receipt(id, producer, attemptId, at) {
75
+ return {
76
+ id,
77
+ producer,
78
+ status: 'ready',
79
+ authoritativePaths: [`${id}.md`],
80
+ acceptanceCriteriaIds: [],
81
+ taskIds: [],
82
+ timestamps: { createdAt: at, updatedAt: at, completedAt: at },
83
+ receipt: {
84
+ outcome: 'completed',
85
+ attemptId,
86
+ summary: `${id} complete`,
87
+ evidencePaths: [`${id}.md`],
88
+ },
89
+ };
90
+ }
91
+
92
+ async function main() {
93
+ {
94
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mavis-p2-'));
95
+ try {
96
+ const runId = 'mavis-p2-run';
97
+ const analystId = `${runId}:step-0:task-analyst:attempt-1`;
98
+ const at = new Date().toISOString();
99
+ const taskDirectory = writeTask(
100
+ root,
101
+ 'mavis-p2',
102
+ {
103
+ autoChain: true,
104
+ humanGates: ['after:task-analyst'],
105
+ steps: [
106
+ { agent: 'task-analyst', artifactOutputId: 'analysis' },
107
+ { agent: 'feature-developer', artifactOutputId: 'implementation' },
108
+ { agent: 'build-verifier', artifactOutputId: 'validation' },
109
+ ],
110
+ },
111
+ {
112
+ slug: 'mavis-p2',
113
+ intent: 'feature',
114
+ pipelineIndex: 0,
115
+ currentAgent: 'task-analyst',
116
+ phase: 'executing',
117
+ state: 'completed',
118
+ activeAttempts: {
119
+ [analystId]: {
120
+ agent: 'task-analyst',
121
+ stepIndex: 0,
122
+ attempt: 1,
123
+ startedAt: at,
124
+ parallelGroupId: null,
125
+ },
126
+ },
127
+ metricsRunId: runId,
128
+ },
129
+ {
130
+ schemaVersion: 1,
131
+ task: {
132
+ runId,
133
+ slug: 'mavis-p2',
134
+ preset: 'ai-rules',
135
+ stack: 'next',
136
+ platform: 'mavis',
137
+ intent: 'feature',
138
+ profile: 'test',
139
+ },
140
+ events: [
141
+ { eventId: `${runId}:task_started`, type: 'task_started', at },
142
+ {
143
+ eventId: `${runId}:analyst-start`,
144
+ type: 'attempt_started',
145
+ attemptId: analystId,
146
+ agent: 'task-analyst',
147
+ stepIndex: 0,
148
+ attempt: 1,
149
+ at,
150
+ },
151
+ ],
152
+ },
153
+ {
154
+ 'brief.md': '# brief\n',
155
+ 'decomposition.md': '# decomp\n',
156
+ 'artifact-manifest.json': `${JSON.stringify(
157
+ {
158
+ schemaVersion: 1,
159
+ slug: 'mavis-p2',
160
+ updatedAt: at,
161
+ artifacts: [receipt('analysis', 'task-analyst', analystId, at)],
162
+ },
163
+ null,
164
+ 2,
165
+ )}\n`,
166
+ },
167
+ );
168
+
169
+ const afterAnalyst = JSON.parse(invokeHook(root, { status: 'completed', attemptId: analystId }));
170
+ const gated = readJson(path.join(taskDirectory, 'status.json'));
171
+ assert.equal(gated.executionMode, 'manual-chain');
172
+ assert.equal(gated.awaitingHumanGate, true, 'P2 must stop at after:task-analyst');
173
+ assert.ok(gated.activeHumanGateEventId, 'P2 must record human_gate_started');
174
+ assert.deepEqual(afterAnalyst, {}, 'human gate pause returns empty hook output');
175
+
176
+ const resume = JSON.parse(
177
+ invokeHook(root, {
178
+ status: 'completed',
179
+ action: 'end_human_gate',
180
+ activateNext: true,
181
+ }),
182
+ );
183
+ const afterGate = readJson(path.join(taskDirectory, 'status.json'));
184
+ assert.equal(afterGate.awaitingHumanGate, false);
185
+ assert.equal(afterGate.currentAgent, 'feature-developer');
186
+ assert.match(resume.followup_message || '', /feature-developer/);
187
+
188
+ const developerId = Object.keys(afterGate.activeAttempts).find((id) =>
189
+ id.includes('feature-developer'),
190
+ );
191
+ const later = new Date().toISOString();
192
+ writeJson(path.join(taskDirectory, 'artifact-manifest.json'), {
193
+ schemaVersion: 1,
194
+ slug: 'mavis-p2',
195
+ updatedAt: later,
196
+ artifacts: [
197
+ receipt('analysis', 'task-analyst', analystId, at),
198
+ receipt('implementation', 'feature-developer', developerId, later),
199
+ ],
200
+ });
201
+ fs.writeFileSync(path.join(taskDirectory, 'implementation.md'), '# impl\n');
202
+ invokeHook(root, { status: 'completed', attemptId: developerId });
203
+ const afterDev = readJson(path.join(taskDirectory, 'status.json'));
204
+ assert.equal(afterDev.currentAgent, 'build-verifier');
205
+ console.log('ok P2: route → receipt → human gate → continue → verifier');
206
+ } finally {
207
+ fs.rmSync(root, { recursive: true, force: true });
208
+ }
209
+ }
210
+
211
+ {
212
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mavis-stale-'));
213
+ try {
214
+ const runId = 'mavis-stale-run';
215
+ const attemptId = `${runId}:step-2:code-reviewer:attempt-1`;
216
+ const staleAt = '2026-01-01T00:00:00.000Z';
217
+ const startedAt = '2026-01-01T00:00:05.000Z';
218
+ const taskDirectory = writeTask(
219
+ root,
220
+ 'mavis-stale',
221
+ {
222
+ autoChain: true,
223
+ steps: [
224
+ { agent: 'feature-developer', artifactOutputId: 'implementation' },
225
+ { agent: 'build-verifier', artifactOutputId: 'validation' },
226
+ { agent: 'code-reviewer', artifactOutputId: 'review' },
227
+ ],
228
+ },
229
+ {
230
+ slug: 'mavis-stale',
231
+ intent: 'feature',
232
+ pipelineIndex: 2,
233
+ currentAgent: 'code-reviewer',
234
+ phase: 'executing',
235
+ state: 'awaiting_artifact_receipt',
236
+ activeAttempts: {
237
+ [attemptId]: {
238
+ agent: 'code-reviewer',
239
+ stepIndex: 2,
240
+ attempt: 1,
241
+ startedAt,
242
+ parallelGroupId: null,
243
+ },
244
+ },
245
+ metricsRunId: runId,
246
+ },
247
+ {
248
+ schemaVersion: 1,
249
+ task: {
250
+ runId,
251
+ slug: 'mavis-stale',
252
+ preset: 'ai-rules',
253
+ stack: 'next',
254
+ platform: 'mavis',
255
+ intent: 'feature',
256
+ profile: 'test',
257
+ },
258
+ events: [
259
+ { eventId: `${runId}:task_started`, type: 'task_started', at: startedAt },
260
+ {
261
+ eventId: `${runId}:review-start`,
262
+ type: 'attempt_started',
263
+ attemptId,
264
+ agent: 'code-reviewer',
265
+ stepIndex: 2,
266
+ attempt: 1,
267
+ at: startedAt,
268
+ },
269
+ ],
270
+ },
271
+ {
272
+ 'artifact-manifest.json': `${JSON.stringify(
273
+ {
274
+ schemaVersion: 1,
275
+ slug: 'mavis-stale',
276
+ updatedAt: staleAt,
277
+ artifacts: [receipt('review', 'code-reviewer', attemptId, staleAt)],
278
+ },
279
+ null,
280
+ 2,
281
+ )}\n`,
282
+ },
283
+ );
284
+ assert.deepEqual(JSON.parse(invokeHook(root, { status: 'completed', attemptId })), {});
285
+ const status = readJson(path.join(taskDirectory, 'status.json'));
286
+ assert.equal(status.pipelineIndex, 2);
287
+ assert.equal(status.currentAgent, 'code-reviewer');
288
+ console.log('ok stale receipt ignored');
289
+ } finally {
290
+ fs.rmSync(root, { recursive: true, force: true });
291
+ }
292
+ }
293
+
294
+ {
295
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mavis-retry-'));
296
+ try {
297
+ const runId = 'mavis-retry-run';
298
+ const reviewerId = `${runId}:step-1:code-reviewer:attempt-1`;
299
+ const at = new Date().toISOString();
300
+ const taskDirectory = writeTask(
301
+ root,
302
+ 'mavis-retry',
303
+ {
304
+ autoChain: true,
305
+ steps: [
306
+ { agent: 'feature-developer', artifactOutputId: 'implementation' },
307
+ { agent: 'code-reviewer', artifactInputIds: ['implementation'], artifactOutputId: 'review' },
308
+ ],
309
+ },
310
+ {
311
+ slug: 'mavis-retry',
312
+ intent: 'review-only',
313
+ pipelineIndex: 1,
314
+ currentAgent: 'code-reviewer',
315
+ phase: 'executing',
316
+ state: 'in_progress',
317
+ activeAttempts: {
318
+ [reviewerId]: {
319
+ agent: 'code-reviewer',
320
+ stepIndex: 1,
321
+ attempt: 1,
322
+ startedAt: at,
323
+ parallelGroupId: null,
324
+ },
325
+ },
326
+ metricsRunId: runId,
327
+ },
328
+ {
329
+ schemaVersion: 1,
330
+ task: {
331
+ runId,
332
+ slug: 'mavis-retry',
333
+ preset: 'ai-rules',
334
+ stack: 'next',
335
+ platform: 'mavis',
336
+ intent: 'review-only',
337
+ profile: 'test',
338
+ },
339
+ events: [
340
+ { eventId: `${runId}:task_started`, type: 'task_started', at },
341
+ {
342
+ eventId: `${runId}:review-start`,
343
+ type: 'attempt_started',
344
+ attemptId: reviewerId,
345
+ agent: 'code-reviewer',
346
+ stepIndex: 1,
347
+ attempt: 1,
348
+ at,
349
+ },
350
+ ],
351
+ },
352
+ {
353
+ 'review.md': '# review\nREQUEST_CHANGES\n',
354
+ 'artifact-manifest.json': `${JSON.stringify(
355
+ {
356
+ schemaVersion: 1,
357
+ slug: 'mavis-retry',
358
+ updatedAt: at,
359
+ artifacts: [
360
+ {
361
+ id: 'review',
362
+ producer: 'code-reviewer',
363
+ status: 'blocked',
364
+ authoritativePaths: ['review.md'],
365
+ acceptanceCriteriaIds: [],
366
+ taskIds: [],
367
+ timestamps: { createdAt: at, updatedAt: at, completedAt: at },
368
+ receipt: {
369
+ outcome: 'changes_requested',
370
+ attemptId: reviewerId,
371
+ summary: 'Review found blockers.',
372
+ evidencePaths: ['review.md'],
373
+ },
374
+ },
375
+ ],
376
+ },
377
+ null,
378
+ 2,
379
+ )}\n`,
380
+ },
381
+ );
382
+ const handoff = JSON.parse(invokeHook(root, { status: 'completed', attemptId: reviewerId }));
383
+ const status = readJson(path.join(taskDirectory, 'status.json'));
384
+ assert.equal(status.currentAgent, 'feature-developer');
385
+ assert.equal(status.retryAfterFix, 'code-reviewer');
386
+ assert.match(handoff.followup_message || '', /feature-developer/);
387
+ console.log('ok retry to nearest worker');
388
+ } finally {
389
+ fs.rmSync(root, { recursive: true, force: true });
390
+ }
391
+ }
392
+
393
+ {
394
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mavis-parallel-'));
395
+ try {
396
+ const runId = 'mavis-parallel-run';
397
+ const starterId = `${runId}:step-0:starter:attempt-1`;
398
+ const at = '2026-01-01T00:00:00.000Z';
399
+ const taskDirectory = writeTask(
400
+ root,
401
+ 'mavis-parallel',
402
+ {
403
+ autoChain: true,
404
+ steps: [
405
+ { agent: 'starter' },
406
+ { agent: ['alpha', 'beta'], parallel: true },
407
+ { agent: 'gamma' },
408
+ ],
409
+ },
410
+ {
411
+ slug: 'mavis-parallel',
412
+ intent: 'feature',
413
+ pipelineIndex: 0,
414
+ currentAgent: 'starter',
415
+ phase: 'executing',
416
+ state: 'completed',
417
+ activeAttempts: {
418
+ [starterId]: {
419
+ agent: 'starter',
420
+ stepIndex: 0,
421
+ attempt: 1,
422
+ startedAt: at,
423
+ parallelGroupId: null,
424
+ },
425
+ },
426
+ metricsRunId: runId,
427
+ },
428
+ {
429
+ schemaVersion: 1,
430
+ task: {
431
+ runId,
432
+ slug: 'mavis-parallel',
433
+ preset: 'ai-rules',
434
+ stack: 'next',
435
+ platform: 'mavis',
436
+ intent: 'feature',
437
+ profile: 'test',
438
+ },
439
+ events: [
440
+ { eventId: `${runId}:task_started`, type: 'task_started', at },
441
+ {
442
+ eventId: `${runId}:starter`,
443
+ type: 'attempt_started',
444
+ attemptId: starterId,
445
+ agent: 'starter',
446
+ stepIndex: 0,
447
+ attempt: 1,
448
+ at,
449
+ },
450
+ ],
451
+ },
452
+ );
453
+ invokeHook(root, { status: 'completed', attemptId: starterId });
454
+ const afterStart = readJson(path.join(taskDirectory, 'status.json'));
455
+ const alphaId = Object.keys(afterStart.activeAttempts).find((id) => id.includes('alpha'));
456
+ const betaId = Object.keys(afterStart.activeAttempts).find((id) => id.includes('beta'));
457
+ assert.ok(alphaId && betaId, 'parallel attempts must activate');
458
+ invokeHook(root, { status: 'completed', attemptId: betaId });
459
+ invokeHook(root, { status: 'completed', attemptId: alphaId });
460
+ const afterBoth = readJson(path.join(taskDirectory, 'status.json'));
461
+ assert.equal(afterBoth.currentAgent, 'gamma');
462
+ console.log('ok reverse-order parallel completion');
463
+ } finally {
464
+ fs.rmSync(root, { recursive: true, force: true });
465
+ }
466
+ }
467
+
468
+ {
469
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mavis-lock-'));
470
+ try {
471
+ const runId = 'mavis-lock-run';
472
+ const attemptId = `${runId}:step-0:feature-developer:attempt-1`;
473
+ const at = '2026-01-01T00:00:00.000Z';
474
+ const taskDirectory = writeTask(
475
+ root,
476
+ 'mavis-lock',
477
+ { autoChain: true, steps: [{ agent: 'feature-developer' }, { agent: 'build-verifier' }] },
478
+ {
479
+ slug: 'mavis-lock',
480
+ intent: 'feature',
481
+ pipelineIndex: 0,
482
+ currentAgent: 'feature-developer',
483
+ phase: 'executing',
484
+ state: 'in_progress',
485
+ activeAttempts: {
486
+ [attemptId]: {
487
+ agent: 'feature-developer',
488
+ stepIndex: 0,
489
+ attempt: 1,
490
+ startedAt: at,
491
+ parallelGroupId: null,
492
+ },
493
+ },
494
+ metricsRunId: runId,
495
+ },
496
+ {
497
+ schemaVersion: 1,
498
+ task: {
499
+ runId,
500
+ slug: 'mavis-lock',
501
+ preset: 'ai-rules',
502
+ stack: 'next',
503
+ platform: 'mavis',
504
+ intent: 'feature',
505
+ profile: 'test',
506
+ },
507
+ events: [
508
+ { eventId: `${runId}:task_started`, type: 'task_started', at },
509
+ {
510
+ eventId: `${runId}:dev-start`,
511
+ type: 'attempt_started',
512
+ attemptId,
513
+ agent: 'feature-developer',
514
+ stepIndex: 0,
515
+ attempt: 1,
516
+ at,
517
+ },
518
+ ],
519
+ },
520
+ );
521
+ const lockPath = path.join(taskDirectory, 'metrics.json.lock');
522
+ fs.writeFileSync(lockPath, 'held\n');
523
+ const first = new Promise((resolve, reject) => {
524
+ const child = spawn('bash', [hook], { cwd: root, stdio: ['pipe', 'pipe', 'pipe'] });
525
+ child.stdin.end(JSON.stringify({ status: 'completed', attemptId }));
526
+ child.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`lock wait ${code}`))));
527
+ });
528
+ setTimeout(() => {
529
+ if (fs.existsSync(lockPath)) {
530
+ fs.unlinkSync(lockPath);
531
+ }
532
+ }, 50);
533
+ await first;
534
+ const finished = readJson(path.join(taskDirectory, 'metrics.json')).events.find(
535
+ (event) => event.type === 'attempt_finished' && event.attemptId === attemptId,
536
+ );
537
+ assert.ok(finished, 'lock-race must emit one attempt_finished');
538
+ console.log('ok metrics lock race');
539
+ } finally {
540
+ fs.rmSync(root, { recursive: true, force: true });
541
+ }
542
+ }
543
+
544
+ {
545
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mavis-manual-'));
546
+ try {
547
+ fs.mkdirSync(path.join(root, '.mavis', 'hooks'), { recursive: true });
548
+ fs.copyFileSync(hook, path.join(root, '.mavis', 'hooks', 'chain-team-phases.sh'));
549
+ const runId = 'mavis-manual-run';
550
+ const analystId = `${runId}:step-0:task-analyst:attempt-1`;
551
+ const at = new Date().toISOString();
552
+ writeTask(
553
+ root,
554
+ 'mavis-manual',
555
+ { autoChain: true, steps: [{ agent: 'task-analyst' }, { agent: 'feature-developer' }] },
556
+ {
557
+ slug: 'mavis-manual',
558
+ intent: 'feature',
559
+ pipelineIndex: 0,
560
+ currentAgent: 'task-analyst',
561
+ phase: 'executing',
562
+ state: 'completed',
563
+ activeAttempts: {
564
+ [analystId]: {
565
+ agent: 'task-analyst',
566
+ stepIndex: 0,
567
+ attempt: 1,
568
+ startedAt: at,
569
+ parallelGroupId: null,
570
+ },
571
+ },
572
+ metricsRunId: runId,
573
+ },
574
+ {
575
+ schemaVersion: 1,
576
+ task: {
577
+ runId,
578
+ slug: 'mavis-manual',
579
+ preset: 'ai-rules',
580
+ stack: 'next',
581
+ platform: 'mavis',
582
+ intent: 'feature',
583
+ profile: 'test',
584
+ },
585
+ events: [
586
+ { eventId: `${runId}:task_started`, type: 'task_started', at },
587
+ {
588
+ eventId: `${runId}:analyst`,
589
+ type: 'attempt_started',
590
+ attemptId: analystId,
591
+ agent: 'task-analyst',
592
+ stepIndex: 0,
593
+ attempt: 1,
594
+ at,
595
+ },
596
+ ],
597
+ },
598
+ );
599
+ const installed = path.join(root, '.mavis', 'hooks', 'chain-team-phases.sh');
600
+ const out = execFileSync('bash', [installed], {
601
+ cwd: root,
602
+ encoding: 'utf8',
603
+ input: JSON.stringify({ status: 'completed', attemptId: analystId }),
604
+ });
605
+ assert.match(JSON.parse(out).followup_message || '', /feature-developer/);
606
+ assert.equal(fs.existsSync(path.join(root, '.mavis', 'hooks.json')), false);
607
+ console.log('ok manual fallback on fresh fixture (semi-auto)');
608
+ } finally {
609
+ fs.rmSync(root, { recursive: true, force: true });
610
+ }
611
+ }
612
+
613
+ {
614
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mavis-bad-pipeline-'));
615
+ try {
616
+ const taskDirectory = writeTask(
617
+ root,
618
+ 'mavis-bad',
619
+ null,
620
+ {
621
+ slug: 'mavis-bad',
622
+ intent: 'feature',
623
+ pipelineIndex: 0,
624
+ currentAgent: 'task-analyst',
625
+ phase: 'executing',
626
+ state: 'in_progress',
627
+ },
628
+ { schemaVersion: 1, task: { runId: 'bad', slug: 'mavis-bad' }, events: [] },
629
+ );
630
+ fs.writeFileSync(
631
+ path.join(taskDirectory, 'pipeline.json'),
632
+ '{ "intent": "feature", "steps": [\n { "agent": "task-analyst" }\n',
633
+ );
634
+ invokeHook(root, { status: 'completed', attemptId: 'x' });
635
+ const status = readJson(path.join(taskDirectory, 'status.json'));
636
+ assert.equal(status.state, 'blocked');
637
+ console.log('ok malformed pipeline blocked');
638
+ } finally {
639
+ fs.rmSync(root, { recursive: true, force: true });
640
+ }
641
+ }
642
+
643
+ console.log('test-mavis-orchestration: ok');
644
+ }
645
+
646
+ main().catch((error) => {
647
+ console.error(error);
648
+ process.exit(1);
649
+ });