@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
@@ -66,10 +66,20 @@ const STACK = 'php-hexagonal';
66
66
  const PLATFORM = 'claude';
67
67
 
68
68
  function waitForMetricsLock() {
69
+ // OD-1: real advisory lock via create-exclusive (fs.openSync(_, 'wx')).
70
+ // The previous impl spun on Atomics.wait against an unrelated SharedArrayBuffer
71
+ // and never blocked on the lock file, so two concurrent subagentStop events
72
+ // could both acquire the lock and corrupt metrics.json. Retry EEXIST with a
73
+ // 10 ms sleep up to 200 attempts (2 s ceiling). On any other error, throw.
69
74
  for (let attempt = 0; attempt < 200; attempt += 1) {
70
- try { fs.mkdirSync(METRICS_LOCK); return; } catch (error) {
71
- if (error && error.code !== 'EEXIST') throw error;
72
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
75
+ try {
76
+ const fd = fs.openSync(METRICS_LOCK, 'wx');
77
+ try { fs.closeSync(fd); } catch {}
78
+ return;
79
+ } catch (error) {
80
+ if (!error || error.code !== 'EEXIST') throw error;
81
+ const until = Date.now() + 10;
82
+ while (Date.now() < until) { /* 10 ms busy-wait */ }
73
83
  }
74
84
  }
75
85
  throw new Error('metrics lock timeout');
@@ -78,7 +88,7 @@ let taskLockHeld = false;
78
88
  function releaseTaskLock() {
79
89
  if (!taskLockHeld) return;
80
90
  taskLockHeld = false;
81
- try { fs.rmdirSync(METRICS_LOCK); } catch {}
91
+ try { fs.unlinkSync(METRICS_LOCK); } catch {}
82
92
  }
83
93
  waitForMetricsLock();
84
94
  taskLockHeld = true;
@@ -106,7 +116,7 @@ function mutateMetrics(mutator) {
106
116
  fs.writeFileSync(temporary, JSON.stringify(metrics, null, 2) + '\n');
107
117
  fs.renameSync(temporary, METRICS_PATH);
108
118
  } finally {
109
- if (acquiredHere) { try { fs.rmdirSync(METRICS_LOCK); } catch {} }
119
+ if (acquiredHere) { try { fs.unlinkSync(METRICS_LOCK); } catch {} }
110
120
  }
111
121
  }
112
122
  function appendMetric(event) { mutateMetrics((metrics) => metrics.events.push({ eventId: `${metrics.task.runId}:${Date.now()}:${Math.random().toString(16).slice(2)}`, ...event })); }
@@ -481,7 +491,19 @@ function legacyFlow() {
481
491
 
482
492
 
483
493
  if (hookInput.action === 'end_human_gate') {
484
- status.awaitingHumanGate = false;
494
+ status.awaitingHumanGate = false;
495
+ // A2 (retro §8): pin a single runId per pipeline so subsequent events don't
496
+ // fragment the metrics ledger across multiple runId sequences. Prefer
497
+ // metrics.json.task.runId when metrics already exist, then status.metricsRunId,
498
+ // then mint a fresh seed only on first ever emit.
499
+ try {
500
+ if (fs.existsSync(METRICS_PATH)) {
501
+ const existing = JSON.parse(fs.readFileSync(METRICS_PATH, 'utf8'));
502
+ if (existing && existing.task && typeof existing.task.runId === 'string') {
503
+ status.metricsRunId = existing.task.runId;
504
+ }
505
+ }
506
+ } catch {}
485
507
  if (status.state === 'awaiting_approval') {
486
508
  status.state = 'in_progress';
487
509
  status.phase = status.phase === 'human_gate' ? 'executing' : status.phase;
@@ -520,11 +542,29 @@ if (hookInput.action === 'end_human_gate') {
520
542
  }
521
543
  }
522
544
  }
523
- writeStatus({
524
- awaitingHumanGate: false,
525
- state: status.state,
526
- phase: status.phase,
527
- });
545
+ // A2 (retro §8): if gate closes the final human gate of the pipeline
546
+ // (no resume happens, or current step's gate was the last), surface
547
+ // state="awaiting_approval" so a subsequent /task-continue doesn't misinterpret
548
+ // the post-gate idle state as "still in progress".
549
+ {
550
+ let gateWasTerminal = false;
551
+ if (!hookInput.activateNext && fs.existsSync(pipelinePath)) {
552
+ try {
553
+ const pipe = JSON.parse(fs.readFileSync(pipelinePath, 'utf8'));
554
+ const steps = pipe.steps || [];
555
+ const idx = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
556
+ const cur = steps[idx];
557
+ const gates = Array.isArray(pipe.humanGates) ? pipe.humanGates : [];
558
+ const hasGate = (step) => Array.isArray(gates) && gates.some((g) => typeof g === 'string' && getStepAgents(step).some((a) => g === `after:${a}`));
559
+ if (cur && hasGate(cur)) gateWasTerminal = true;
560
+ } catch {}
561
+ }
562
+ writeStatus({
563
+ awaitingHumanGate: false,
564
+ state: gateWasTerminal ? 'awaiting_approval' : status.state,
565
+ phase: status.phase,
566
+ });
567
+ }
528
568
  process.stdout.write(JSON.stringify({ ok: true, action: 'end_human_gate', activeHumanGateEventId: status.activeHumanGateEventId || null }));
529
569
  process.exit(0);
530
570
  }
@@ -18,6 +18,7 @@ Checklist for PRs that touch `packages/ai-rules/presets/**` (and dogfood `.curso
18
18
  - [ ] **SoT:** edits in `packages/…` (not only root `.cursor/`)
19
19
  - [ ] **Core:** if `_shared/core/**` changed — all `<!-- shared-core: … -->` consumers updated (or documented intentional fork)
20
20
  - [ ] **Twin:** Cursor change → Claude twin **same PR** (**FAIL** if missing on next, nuxt, svelte, ios-swift, android-kotlin, go, java, mcp-ts, php-hexagonal, and php-laravel — see `preset-twin-sync`)
21
+ - [ ] **Mavis derive:** mapped `cursor/<stack>/` change → `mavis/<stack>/` + `adapter-map.json` **same PR** when the stack is in `releasedStacks` (run `generate-mavis-next.mjs` + `check-mavis-adapter-map.mjs` for `next`; see `preset-twin-sync` § Mavis derivation)
21
22
  - [ ] **Mapping:** `claude/<stack>/rules/README.md` mapping table is current
22
23
  - [ ] **Token budget:** Cursor `alwaysApply` ≤ 4 (4th slot reserved for `ai-local-overlay`); Claude session-start exactly 3; meta rules not always-on
23
24
  - [ ] **Local overlay coverage:** every installable stack ships `ai-local-overlay.mdc` (Cursor) and `ai-local-overlay.md` (Claude) with `MANDATORY` + acknowledgement format + `docs/LOCAL-OVERLAY.md` pointer; Claude twin listed in `claude/<stack>/rules/README.md` mapping table. Run `packages/ai-rules/scripts/check-local-overlay.sh`.
@@ -28,19 +28,34 @@ Cursor `.mdc` — **SoT per stack**. Claude topic `.md` — derived twin.
28
28
  | `php-laravel` | `presets/cursor/php-laravel/rules/*.mdc` | `presets/claude/php-laravel/rules/<topic>/` | Keep in `claude/php-laravel/rules/README.md` |
29
29
  | `android-kotlin` | `presets/cursor/android-kotlin/rules/*.mdc` | `presets/claude/android-kotlin/rules/<topic>/` | Keep in `claude/android-kotlin/rules/README.md` |
30
30
 
31
+ ## Mavis derivation (not a Claude twin)
32
+
33
+ | Stack | Cursor SoT | Mavis derive | Manifest |
34
+ |-------|------------|--------------|----------|
35
+ | `next` | `presets/cursor/next/**` | `presets/mavis/next/**` via `generate-mavis-next.mjs` | **Required** `adapter-map.json` |
36
+
37
+ Mavis is a **third platform adapter**, not a Claude-style topic twin:
38
+
39
+ ```text
40
+ _shared/core → cursor/<stack> → mavis/<stack>
41
+ ```
42
+
43
+ Regenerate with `node packages/ai-rules/scripts/generate-mavis-next.mjs`; validate with `node packages/ai-rules/scripts/check-mavis-adapter-map.mjs`.
44
+
31
45
  ## Same-PR checklist
32
46
 
33
47
  1. Edit Cursor rule first (body + frontmatter).
34
48
  2. Update Claude twin: body + `paths:` (from Cursor `globs:` / requestable triggers); drop `alwaysApply`.
35
49
  3. Update mapping table row if stem/path changed.
36
50
  4. If body embeds core (`<!-- shared-core: … -->`), sync core consumers across stacks when core changed.
51
+ 5. When mapped `cursor/<stack>/` sources change, regenerate `mavis/<stack>/` and refresh `adapter-map.json` `sourceRevision` in the **same PR** (run `generate-mavis-next.mjs` + `check-mavis-adapter-map.mjs` for `next`).
37
52
 
38
53
  ## Twin depth
39
54
 
40
55
  - **Domain** rules: ≥15 body lines (FAIL if thin without documented alias reason).
41
56
  - **Thin aliases** (`agent-team-intake`, `technical-retro`): may be shorter (soft) if they only point to a command/skill.
42
57
  - **Slim-companion rules** (UI-edit bundle, slim app-cores, slim tooling): may be shorter than 15 lines **if and only if** the body is a pointer into shared-core / stack-prose (no standalone prose). Examples live in stack `rules/README.md` (e.g. `cursor/next/rules/README.md` "UI-edit bundle" intent).
43
- - **Mechanical enforcement is pending.** Today build-verifier excludes only `agent-team-intake` / `technical-retro` / `preset-layering`; slim-companion rules currently pass under the same human-judgement path as thin aliases. Follow-up: add `allow_slim_stems` to `packages/ai-rules/scripts/check-preset-token-budget.sh` analogous to `trio_stems_allowlist`.
58
+ - Mechanical enforcement lives in `packages/ai-rules/scripts/check-preset-token-budget.sh` see `SLIM_COMPANION_ALLOWLIST` (the 53 stems accepted as slim pointers) and `MIN_SLIM_BODY_LINES` (5-line floor). Slim-companion rules with a body of 6–14 lines whose stem is **not** in the allowlist are reported as slim violations by the script.
44
59
 
45
60
  ## Severity (build-verifier)
46
61
 
@@ -54,5 +69,6 @@ Cursor `.mdc` — **SoT per stack**. Claude topic `.md` — derived twin.
54
69
  - **php-hexagonal:** missing Claude twin for a changed Cursor rule → **FAIL** (same as next / ios-swift / go / java / mcp-ts; mapping table required).
55
70
  - **php-laravel:** missing Claude twin for a changed Cursor rule → **FAIL** (same as next / ios-swift / go / java / mcp-ts / php-hexagonal; mapping table required).
56
71
  - **android-kotlin:** missing Claude twin for a changed Cursor rule → **FAIL** (same as next / ios-swift / go / java / mcp-ts / php-*; mapping table required).
72
+ - **mavis/next:** `cursor/next` mapped source changed without mavis regeneration / `adapter-map.json` `sourceRevision` drift → **FAIL** (`check-mavis-adapter-map.mjs`).
57
73
 
58
74
  Dogfood: sync packages → root `.cursor/` after Cursor SoT changes (not the reverse). Use `packages/ai-rules/scripts/sync-dogfood-cursor.sh`.
@@ -65,3 +65,7 @@ Document PASS/FAIL in `validation-report.md`.
65
65
 
66
66
  13. **Product-specs assets (FAIL):** package must ship `presets/_shared/assets/docs-specs/INDEX.md`, `_templates/page.md`, `_templates/feature.md`. Cursor+Claude twins for `product-specs` and `product-specs-authoring` must embed matching shared-core markers; both stems **requestable** (`alwaysApply: false` / Claude `paths: docs/specs/**`) — never session-start.
67
67
  14. **Optional consumer check (WARN default):** `scripts/check-product-specs.sh` + schema stub may exist for opt-in consumer CI; **FAIL** only with `--strict` or AC that requires named specs — do not force consumer CI.
68
+
69
+ ## Receipt binding
70
+
71
+ > **Receipt binding.** When writing the terminal receipt to `artifact-manifest.json`, set `entry.receipt.attemptId = <attemptId supplied by the orchestrator>` (the orchestrator provides this via `attemptId` on invocation). The hook at `chain-team-phases.sh:readBoundReceiptOutcome` rejects entries whose `attemptId` does not match the current attempt, or whose `Date.parse(timestamps.completedAt) < Date.parse(attemptStartedAt)`. A missing or stale attemptId causes `awaiting_artifact_receipt` stalls. <!-- shared-core: receipt-binding -->
@@ -49,3 +49,7 @@ Write `.claude/team/tasks/<slug>/review.md` with verdict APPROVE | REQUEST_CHANG
49
49
  - **Owned outputs:** `review.md`; upsert only the manifest entry assigned by handoff (`artifactOutputId`; fallback `review`) and preserve all foreign entries.
50
50
  - **Terminal receipt:** cover applicable AC/task IDs, list authoritative evidence paths, and emit `completed` or `changes_requested`. Bind it to the supplied `attemptId` when present.
51
51
  - **Shared state:** never create or mutate `status.json` or `metrics.json`; the orchestration runtime/hook owns lifecycle, gates, retries, attempts, and timestamps.
52
+
53
+ ## Receipt binding
54
+
55
+ > **Receipt binding.** When writing the terminal receipt to `artifact-manifest.json`, set `entry.receipt.attemptId = <attemptId supplied by the orchestrator>` (the orchestrator provides this via `attemptId` on invocation). The hook at `chain-team-phases.sh:readBoundReceiptOutcome` rejects entries whose `attemptId` does not match the current attempt, or whose `Date.parse(timestamps.completedAt) < Date.parse(attemptStartedAt)`. A missing or stale attemptId causes `awaiting_artifact_receipt` stalls. <!-- shared-core: receipt-binding -->
@@ -42,3 +42,7 @@ If a Sentry/Datadog (or similar) MCP is ready and the task is a production error
42
42
 
43
43
  - When writing or changing code, load / follow rule `anti-sycophancy-discipline`.
44
44
  - When acting on review feedback (`changes_requested` / human comments): verify each item against the codebase before implementing; no performative agreement.
45
+
46
+ ## Receipt binding
47
+
48
+ > **Receipt binding.** When writing the terminal receipt to `artifact-manifest.json`, set `entry.receipt.attemptId = <attemptId supplied by the orchestrator>` (the orchestrator provides this via `attemptId` on invocation). The hook at `chain-team-phases.sh:readBoundReceiptOutcome` rejects entries whose `attemptId` does not match the current attempt, or whose `Date.parse(timestamps.completedAt) < Date.parse(attemptStartedAt)`. A missing or stale attemptId causes `awaiting_artifact_receipt` stalls. <!-- shared-core: receipt-binding -->
@@ -11,6 +11,7 @@ Implement Laravel features following Laravel-first layer boundaries.
11
11
  1. Routes → Form Request / Policy → Action/Service → Eloquent → Controller/Resource → optional Job/Event → tests.
12
12
  2. Read `feature-delivery-workflow.mdc`, `architecture-boundaries.mdc`, layer globs.
13
13
  3. After `*.php` edits: invoke `post-change-test.mdc` (+ `php-tooling.mdc`) → `php artisan test` / `composer test`.
14
+ 4. **Verify carry-over at HEAD before applying** (refactor intent only). When the brief comes from a prior review's carry-over findings list, for each item run `git show HEAD:<path>` or `grep` to confirm the finding still applies at the current HEAD; if the file already contains the fix, mark it as **NO-OP at HEAD** in `implementation.md` and skip the edit; if the file is unchanged, apply the fix and cite file:line. Target ≤2 NO-OP items per refactor pipeline.
14
15
 
15
16
  ## Do / Don't
16
17
 
@@ -89,3 +90,7 @@ Load requestable rule `mcp-usage` when verifying third-party library APIs (Conte
89
90
  - **Owned outputs:** `implementation.md` plus changed implementation/test paths; upsert only the manifest entry assigned by handoff (`artifactOutputId`; fallback `implementation`) and preserve all foreign entries.
90
91
  - **Terminal receipt:** cover applicable AC/task IDs, list authoritative evidence paths, and emit `completed` or `blocked`. Bind it to the supplied `attemptId` when present.
91
92
  - **Shared state:** never create or mutate `status.json` or `metrics.json`; the orchestration runtime/hook owns lifecycle, gates, retries, attempts, and timestamps.
93
+
94
+ ## Receipt binding
95
+
96
+ > **Receipt binding.** When writing the terminal receipt to `artifact-manifest.json`, set `entry.receipt.attemptId = <attemptId supplied by the orchestrator>` (the orchestrator provides this via `attemptId` on invocation). The hook at `chain-team-phases.sh:readBoundReceiptOutcome` rejects entries whose `attemptId` does not match the current attempt, or whose `Date.parse(timestamps.completedAt) < Date.parse(attemptStartedAt)`. A missing or stale attemptId causes `awaiting_artifact_receipt` stalls. <!-- shared-core: receipt-binding -->
@@ -28,3 +28,7 @@ Coordinate with existing plans under `.claude/team/tasks/<slug>/`.
28
28
  ## Design guidance
29
29
 
30
30
  - Load `design-guidance` (rule stem) when assessing structure, smells, or pattern fit.
31
+
32
+ ## Receipt binding
33
+
34
+ > **Receipt binding.** When writing the terminal receipt to `artifact-manifest.json`, set `entry.receipt.attemptId = <attemptId supplied by the orchestrator>` (the orchestrator provides this via `attemptId` on invocation). The hook at `chain-team-phases.sh:readBoundReceiptOutcome` rejects entries whose `attemptId` does not match the current attempt, or whose `Date.parse(timestamps.completedAt) < Date.parse(attemptStartedAt)`. A missing or stale attemptId causes `awaiting_artifact_receipt` stalls. <!-- shared-core: receipt-binding -->
@@ -93,6 +93,7 @@ Keep top-level `profile` and emit this top-level object in every new `pipeline.j
93
93
  - Use concrete flags: `public-contract`, `cross-layer`, `security-sensitive`, `migration`, `destructive`, `concurrency-state`, `external-dependency`, `unclear-acceptance-criteria`, `broad-test-surface`, `behavior-regression`.
94
94
  - `routingReasons` must be non-empty and explain profile, gates, specialists, skips, or checkpoints; never write “standard by default.”
95
95
  - `estimatedWorkPackages` is an integer ≥1; `openDecisionCount` is an integer ≥0. Do not lower complexity because implementation is familiar.
96
+ - Add **api-contract-reviewer** for new/changed backend contracts — before developer.
96
97
 
97
98
  ## Model tiers (`steps[].model`)
98
99
 
@@ -66,10 +66,20 @@ const STACK = 'php-laravel';
66
66
  const PLATFORM = 'claude';
67
67
 
68
68
  function waitForMetricsLock() {
69
+ // OD-1: real advisory lock via create-exclusive (fs.openSync(_, 'wx')).
70
+ // The previous impl spun on Atomics.wait against an unrelated SharedArrayBuffer
71
+ // and never blocked on the lock file, so two concurrent subagentStop events
72
+ // could both acquire the lock and corrupt metrics.json. Retry EEXIST with a
73
+ // 10 ms sleep up to 200 attempts (2 s ceiling). On any other error, throw.
69
74
  for (let attempt = 0; attempt < 200; attempt += 1) {
70
- try { fs.mkdirSync(METRICS_LOCK); return; } catch (error) {
71
- if (error && error.code !== 'EEXIST') throw error;
72
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
75
+ try {
76
+ const fd = fs.openSync(METRICS_LOCK, 'wx');
77
+ try { fs.closeSync(fd); } catch {}
78
+ return;
79
+ } catch (error) {
80
+ if (!error || error.code !== 'EEXIST') throw error;
81
+ const until = Date.now() + 10;
82
+ while (Date.now() < until) { /* 10 ms busy-wait */ }
73
83
  }
74
84
  }
75
85
  throw new Error('metrics lock timeout');
@@ -78,7 +88,7 @@ let taskLockHeld = false;
78
88
  function releaseTaskLock() {
79
89
  if (!taskLockHeld) return;
80
90
  taskLockHeld = false;
81
- try { fs.rmdirSync(METRICS_LOCK); } catch {}
91
+ try { fs.unlinkSync(METRICS_LOCK); } catch {}
82
92
  }
83
93
  waitForMetricsLock();
84
94
  taskLockHeld = true;
@@ -106,7 +116,7 @@ function mutateMetrics(mutator) {
106
116
  fs.writeFileSync(temporary, JSON.stringify(metrics, null, 2) + '\n');
107
117
  fs.renameSync(temporary, METRICS_PATH);
108
118
  } finally {
109
- if (acquiredHere) { try { fs.rmdirSync(METRICS_LOCK); } catch {} }
119
+ if (acquiredHere) { try { fs.unlinkSync(METRICS_LOCK); } catch {} }
110
120
  }
111
121
  }
112
122
  function appendMetric(event) { mutateMetrics((metrics) => metrics.events.push({ eventId: `${metrics.task.runId}:${Date.now()}:${Math.random().toString(16).slice(2)}`, ...event })); }
@@ -481,7 +491,19 @@ function legacyFlow() {
481
491
 
482
492
 
483
493
  if (hookInput.action === 'end_human_gate') {
484
- status.awaitingHumanGate = false;
494
+ status.awaitingHumanGate = false;
495
+ // A2 (retro §8): pin a single runId per pipeline so subsequent events don't
496
+ // fragment the metrics ledger across multiple runId sequences. Prefer
497
+ // metrics.json.task.runId when metrics already exist, then status.metricsRunId,
498
+ // then mint a fresh seed only on first ever emit.
499
+ try {
500
+ if (fs.existsSync(METRICS_PATH)) {
501
+ const existing = JSON.parse(fs.readFileSync(METRICS_PATH, 'utf8'));
502
+ if (existing && existing.task && typeof existing.task.runId === 'string') {
503
+ status.metricsRunId = existing.task.runId;
504
+ }
505
+ }
506
+ } catch {}
485
507
  if (status.state === 'awaiting_approval') {
486
508
  status.state = 'in_progress';
487
509
  status.phase = status.phase === 'human_gate' ? 'executing' : status.phase;
@@ -520,11 +542,29 @@ if (hookInput.action === 'end_human_gate') {
520
542
  }
521
543
  }
522
544
  }
523
- writeStatus({
524
- awaitingHumanGate: false,
525
- state: status.state,
526
- phase: status.phase,
527
- });
545
+ // A2 (retro §8): if gate closes the final human gate of the pipeline
546
+ // (no resume happens, or current step's gate was the last), surface
547
+ // state="awaiting_approval" so a subsequent /task-continue doesn't misinterpret
548
+ // the post-gate idle state as "still in progress".
549
+ {
550
+ let gateWasTerminal = false;
551
+ if (!hookInput.activateNext && fs.existsSync(pipelinePath)) {
552
+ try {
553
+ const pipe = JSON.parse(fs.readFileSync(pipelinePath, 'utf8'));
554
+ const steps = pipe.steps || [];
555
+ const idx = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
556
+ const cur = steps[idx];
557
+ const gates = Array.isArray(pipe.humanGates) ? pipe.humanGates : [];
558
+ const hasGate = (step) => Array.isArray(gates) && gates.some((g) => typeof g === 'string' && getStepAgents(step).some((a) => g === `after:${a}`));
559
+ if (cur && hasGate(cur)) gateWasTerminal = true;
560
+ } catch {}
561
+ }
562
+ writeStatus({
563
+ awaitingHumanGate: false,
564
+ state: gateWasTerminal ? 'awaiting_approval' : status.state,
565
+ phase: status.phase,
566
+ });
567
+ }
528
568
  process.stdout.write(JSON.stringify({ ok: true, action: 'end_human_gate', activeHumanGateEventId: status.activeHumanGateEventId || null }));
529
569
  process.exit(0);
530
570
  }
@@ -16,6 +16,7 @@ Checklist for PRs that touch `packages/ai-rules/presets/**` (and dogfood `.curso
16
16
  - [ ] **SoT:** edits in `packages/…` (not only root `.cursor/`)
17
17
  - [ ] **Core:** if `_shared/core/**` changed — all `<!-- shared-core: … -->` consumers updated (or documented intentional fork)
18
18
  - [ ] **Twin:** Cursor change → Claude twin **same PR** (**FAIL** if missing on next, nuxt, svelte, ios-swift, android-kotlin, go, java, mcp-ts, php-hexagonal, and php-laravel — see `preset-twin-sync`)
19
+ - [ ] **Mavis derive:** mapped `cursor/<stack>/` change → `mavis/<stack>/` + `adapter-map.json` **same PR** when the stack is in `releasedStacks` (run `generate-mavis-next.mjs` + `check-mavis-adapter-map.mjs` for `next`; see `preset-twin-sync` § Mavis derivation)
19
20
  - [ ] **Mapping:** `claude/<stack>/rules/README.md` mapping table is current
20
21
  - [ ] **Token budget:** Cursor `alwaysApply` ≤ 4 (4th slot reserved for `ai-local-overlay`); Claude session-start exactly 3; meta rules not always-on
21
22
  - [ ] **Local overlay coverage:** every installable stack ships `ai-local-overlay.mdc` (Cursor) and `ai-local-overlay.md` (Claude) with `MANDATORY` + acknowledgement format + `docs/LOCAL-OVERLAY.md` pointer; Claude twin listed in `claude/<stack>/rules/README.md` mapping table. Run `packages/ai-rules/scripts/check-local-overlay.sh`.
@@ -26,19 +26,34 @@ Cursor `.mdc` — **SoT per stack**. Claude topic `.md` — derived twin.
26
26
  | `php-laravel` | `presets/cursor/php-laravel/rules/*.mdc` | `presets/claude/php-laravel/rules/<topic>/` | Keep in `claude/php-laravel/rules/README.md` |
27
27
  | `android-kotlin` | `presets/cursor/android-kotlin/rules/*.mdc` | `presets/claude/android-kotlin/rules/<topic>/` | Keep in `claude/android-kotlin/rules/README.md` |
28
28
 
29
+ ## Mavis derivation (not a Claude twin)
30
+
31
+ | Stack | Cursor SoT | Mavis derive | Manifest |
32
+ |-------|------------|--------------|----------|
33
+ | `next` | `presets/cursor/next/**` | `presets/mavis/next/**` via `generate-mavis-next.mjs` | **Required** `adapter-map.json` |
34
+
35
+ Mavis is a **third platform adapter**, not a Claude-style topic twin:
36
+
37
+ ```text
38
+ _shared/core → cursor/<stack> → mavis/<stack>
39
+ ```
40
+
41
+ Regenerate with `node packages/ai-rules/scripts/generate-mavis-next.mjs`; validate with `node packages/ai-rules/scripts/check-mavis-adapter-map.mjs`.
42
+
29
43
  ## Same-PR checklist
30
44
 
31
45
  1. Edit Cursor rule first (body + frontmatter).
32
46
  2. Update Claude twin: body + `paths:` (from Cursor `globs:` / requestable triggers); drop `alwaysApply`.
33
47
  3. Update mapping table row if stem/path changed.
34
48
  4. If body embeds core (`<!-- shared-core: … -->`), sync core consumers across stacks when core changed.
49
+ 5. When mapped `cursor/<stack>/` sources change, regenerate `mavis/<stack>/` and refresh `adapter-map.json` `sourceRevision` in the **same PR** (run `generate-mavis-next.mjs` + `check-mavis-adapter-map.mjs` for `next`).
35
50
 
36
51
  ## Twin depth
37
52
 
38
53
  - **Domain** rules: ≥15 body lines (FAIL if thin without documented alias reason).
39
54
  - **Thin aliases** (`agent-team-intake`, `technical-retro`): may be shorter (soft) if they only point to a command/skill.
40
55
  - **Slim-companion rules** (UI-edit bundle, slim app-cores, slim tooling): may be shorter than 15 lines **if and only if** the body is a pointer into shared-core / stack-prose (no standalone prose). Examples live in stack `rules/README.md` (e.g. `cursor/next/rules/README.md` "UI-edit bundle" intent).
41
- - **Mechanical enforcement is pending.** Today build-verifier excludes only `agent-team-intake` / `technical-retro` / `preset-layering`; slim-companion rules currently pass under the same human-judgement path as thin aliases. Follow-up: add `allow_slim_stems` to `packages/ai-rules/scripts/check-preset-token-budget.sh` analogous to `trio_stems_allowlist`.
56
+ - Mechanical enforcement lives in `packages/ai-rules/scripts/check-preset-token-budget.sh` see `SLIM_COMPANION_ALLOWLIST` (the 53 stems accepted as slim pointers) and `MIN_SLIM_BODY_LINES` (5-line floor). Slim-companion rules with a body of 6–14 lines whose stem is **not** in the allowlist are reported as slim violations by the script.
42
57
 
43
58
  ## Severity (build-verifier)
44
59
 
@@ -52,5 +67,6 @@ Cursor `.mdc` — **SoT per stack**. Claude topic `.md` — derived twin.
52
67
  - **php-hexagonal:** missing Claude twin for a changed Cursor rule → **FAIL** (same as next / ios-swift / go / java / mcp-ts; mapping table required).
53
68
  - **php-laravel:** missing Claude twin for a changed Cursor rule → **FAIL** (same as next / ios-swift / go / java / mcp-ts / php-hexagonal; mapping table required).
54
69
  - **android-kotlin:** missing Claude twin for a changed Cursor rule → **FAIL** (same as next / ios-swift / go / java / mcp-ts / php-*; mapping table required).
70
+ - **mavis/next:** `cursor/next` mapped source changed without mavis regeneration / `adapter-map.json` `sourceRevision` drift → **FAIL** (`check-mavis-adapter-map.mjs`).
55
71
 
56
72
  Dogfood: sync packages → root `.cursor/` after Cursor SoT changes (not the reverse). Use `packages/ai-rules/scripts/sync-dogfood-cursor.sh`.
@@ -96,6 +96,8 @@ Write the owned terminal receipt described under **Artifact contract**. If **FAI
96
96
 
97
97
  Do not fix code — report only. Do not advance to code-reviewer until PASS.
98
98
 
99
+ **Receipt binding.** When writing the terminal receipt to `artifact-manifest.json`, set `entry.receipt.attemptId = <attemptId supplied by the orchestrator>` (the orchestrator provides this via `attemptId` on invocation). The hook at `chain-team-phases.sh:readBoundReceiptOutcome` rejects entries whose `attemptId` does not match the current attempt, or whose `Date.parse(timestamps.completedAt) < Date.parse(attemptStartedAt)`. A missing or stale attemptId causes `awaiting_artifact_receipt` stalls. <!-- shared-core: receipt-binding -->
100
+
99
101
  ## Artifact contract
100
102
 
101
103
  - **Authoritative inputs:** read `.claude/team/tasks/<slug>/artifact-manifest.json` first; consume handoff `artifactInputIds` when supplied, otherwise consume the manifest entries for brief/decomposition, implementation evidence, pipeline scope, and changed files.
@@ -54,6 +54,8 @@ Write the owned terminal receipt described under **Artifact contract**. If **REQ
54
54
 
55
55
  Do not mix this output with retrospective facilitation — keep review and retro separate.
56
56
 
57
+ **Receipt binding.** When writing the terminal receipt to `artifact-manifest.json`, set `entry.receipt.attemptId = <attemptId supplied by the orchestrator>` (the orchestrator provides this via `attemptId` on invocation). The hook at `chain-team-phases.sh:readBoundReceiptOutcome` rejects entries whose `attemptId` does not match the current attempt, or whose `Date.parse(timestamps.completedAt) < Date.parse(attemptStartedAt)`. A missing or stale attemptId causes `awaiting_artifact_receipt` stalls. <!-- shared-core: receipt-binding -->
58
+
57
59
  ## Design guidance
58
60
 
59
61
  - Load `design-guidance` (rule stem) when assessing structure, smells, or pattern fit.
@@ -80,3 +80,7 @@ Do not perform formal code review or write e2e plans — those are separate agen
80
80
 
81
81
  - When writing or changing code, load / follow rule `anti-sycophancy-discipline`.
82
82
  - When acting on review feedback (`changes_requested` / human comments): verify each item against the codebase before implementing; no performative agreement.
83
+
84
+ ## Receipt binding
85
+
86
+ > **Receipt binding.** When writing the terminal receipt to `artifact-manifest.json`, set `entry.receipt.attemptId = <attemptId supplied by the orchestrator>` (the orchestrator provides this via `attemptId` on invocation). The hook at `chain-team-phases.sh:readBoundReceiptOutcome` rejects entries whose `attemptId` does not match the current attempt, or whose `Date.parse(timestamps.completedAt) < Date.parse(attemptStartedAt)`. A missing or stale attemptId causes `awaiting_artifact_receipt` stalls. <!-- shared-core: receipt-binding -->
@@ -12,6 +12,7 @@ You are a senior frontend developer working in a SvelteKit monorepo with strict
12
12
  2. Read `status.json` — proceed if `in_progress`, `approved`, or `retryAfterFix`.
13
13
  3. Read `pipeline.json` for step context and scope.
14
14
  4. Follow the **`feature-delivery` skill** (layer order, reference features, validation handoff).
15
+ 5. **Verify carry-over at HEAD before applying** (refactor intent only). When the brief comes from a prior review's carry-over findings list, for each item run `git show HEAD:<path>` or `grep` to confirm the finding still applies at the current HEAD; if the file already contains the fix (carry-over was closed in a prior uncommitted batch), mark it as **NO-OP at HEAD** in `implementation.md` and skip the edit; if the file is unchanged, apply the fix and cite file:line. Target ≤2 NO-OP items per refactor pipeline.
15
16
 
16
17
  ## Task execution rules
17
18
 
@@ -39,6 +40,8 @@ Emit the owned implementation receipt with outcome `completed`. Handoff: tasks d
39
40
 
40
41
  Do not perform formal code review — that is the code-reviewer subagent's job.
41
42
 
43
+ **Receipt binding.** When writing the terminal receipt to `artifact-manifest.json`, set `entry.receipt.attemptId = <attemptId supplied by the orchestrator>` (the orchestrator provides this via `attemptId` on invocation). The hook at `chain-team-phases.sh:readBoundReceiptOutcome` rejects entries whose `attemptId` does not match the current attempt, or whose `Date.parse(timestamps.completedAt) < Date.parse(attemptStartedAt)`. A missing or stale attemptId causes `awaiting_artifact_receipt` stalls. <!-- shared-core: receipt-binding -->
44
+
42
45
  ## Design guidance
43
46
 
44
47
  - Load `design-guidance` (rule stem) when assessing structure, smells, or pattern fit.
@@ -52,3 +52,7 @@ Provide summary:
52
52
  ## Design guidance
53
53
 
54
54
  - Load `design-guidance` (rule stem) when assessing structure, smells, or pattern fit.
55
+
56
+ ## Receipt binding
57
+
58
+ > **Receipt binding.** When writing the terminal receipt to `artifact-manifest.json`, set `entry.receipt.attemptId = <attemptId supplied by the orchestrator>` (the orchestrator provides this via `attemptId` on invocation). The hook at `chain-team-phases.sh:readBoundReceiptOutcome` rejects entries whose `attemptId` does not match the current attempt, or whose `Date.parse(timestamps.completedAt) < Date.parse(attemptStartedAt)`. A missing or stale attemptId causes `awaiting_artifact_receipt` stalls. <!-- shared-core: receipt-binding -->
@@ -64,10 +64,20 @@ const STACK = 'svelte';
64
64
  const PLATFORM = 'claude';
65
65
 
66
66
  function waitForMetricsLock() {
67
+ // OD-1: real advisory lock via create-exclusive (fs.openSync(_, 'wx')).
68
+ // The previous impl spun on Atomics.wait against an unrelated SharedArrayBuffer
69
+ // and never blocked on the lock file, so two concurrent subagentStop events
70
+ // could both acquire the lock and corrupt metrics.json. Retry EEXIST with a
71
+ // 10 ms sleep up to 200 attempts (2 s ceiling). On any other error, throw.
67
72
  for (let attempt = 0; attempt < 200; attempt += 1) {
68
- try { fs.mkdirSync(METRICS_LOCK); return; } catch (error) {
69
- if (error && error.code !== 'EEXIST') throw error;
70
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
73
+ try {
74
+ const fd = fs.openSync(METRICS_LOCK, 'wx');
75
+ try { fs.closeSync(fd); } catch {}
76
+ return;
77
+ } catch (error) {
78
+ if (!error || error.code !== 'EEXIST') throw error;
79
+ const until = Date.now() + 10;
80
+ while (Date.now() < until) { /* 10 ms busy-wait */ }
71
81
  }
72
82
  }
73
83
  throw new Error('metrics lock timeout');
@@ -76,7 +86,7 @@ let taskLockHeld = false;
76
86
  function releaseTaskLock() {
77
87
  if (!taskLockHeld) return;
78
88
  taskLockHeld = false;
79
- try { fs.rmdirSync(METRICS_LOCK); } catch {}
89
+ try { fs.unlinkSync(METRICS_LOCK); } catch {}
80
90
  }
81
91
  waitForMetricsLock();
82
92
  taskLockHeld = true;
@@ -104,7 +114,7 @@ function mutateMetrics(mutator) {
104
114
  fs.writeFileSync(temporary, JSON.stringify(metrics, null, 2) + '\n');
105
115
  fs.renameSync(temporary, METRICS_PATH);
106
116
  } finally {
107
- if (acquiredHere) { try { fs.rmdirSync(METRICS_LOCK); } catch {} }
117
+ if (acquiredHere) { try { fs.unlinkSync(METRICS_LOCK); } catch {} }
108
118
  }
109
119
  }
110
120
  function appendMetric(event) { mutateMetrics((metrics) => metrics.events.push({ eventId: `${metrics.task.runId}:${Date.now()}:${Math.random().toString(16).slice(2)}`, ...event })); }
@@ -483,7 +493,19 @@ function legacyFlow() {
483
493
 
484
494
 
485
495
  if (hookInput.action === 'end_human_gate') {
486
- status.awaitingHumanGate = false;
496
+ status.awaitingHumanGate = false;
497
+ // A2 (retro §8): pin a single runId per pipeline so subsequent events don't
498
+ // fragment the metrics ledger across multiple runId sequences. Prefer
499
+ // metrics.json.task.runId when metrics already exist, then status.metricsRunId,
500
+ // then mint a fresh seed only on first ever emit.
501
+ try {
502
+ if (fs.existsSync(METRICS_PATH)) {
503
+ const existing = JSON.parse(fs.readFileSync(METRICS_PATH, 'utf8'));
504
+ if (existing && existing.task && typeof existing.task.runId === 'string') {
505
+ status.metricsRunId = existing.task.runId;
506
+ }
507
+ }
508
+ } catch {}
487
509
  if (status.state === 'awaiting_approval') {
488
510
  status.state = 'in_progress';
489
511
  status.phase = status.phase === 'human_gate' ? 'executing' : status.phase;
@@ -522,11 +544,29 @@ if (hookInput.action === 'end_human_gate') {
522
544
  }
523
545
  }
524
546
  }
525
- writeStatus({
526
- awaitingHumanGate: false,
527
- state: status.state,
528
- phase: status.phase,
529
- });
547
+ // A2 (retro §8): if gate closes the final human gate of the pipeline
548
+ // (no resume happens, or current step's gate was the last), surface
549
+ // state="awaiting_approval" so a subsequent /task-continue doesn't misinterpret
550
+ // the post-gate idle state as "still in progress".
551
+ {
552
+ let gateWasTerminal = false;
553
+ if (!hookInput.activateNext && fs.existsSync(pipelinePath)) {
554
+ try {
555
+ const pipe = JSON.parse(fs.readFileSync(pipelinePath, 'utf8'));
556
+ const steps = pipe.steps || [];
557
+ const idx = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
558
+ const cur = steps[idx];
559
+ const gates = Array.isArray(pipe.humanGates) ? pipe.humanGates : [];
560
+ const hasGate = (step) => Array.isArray(gates) && gates.some((g) => typeof g === 'string' && getStepAgents(step).some((a) => g === `after:${a}`));
561
+ if (cur && hasGate(cur)) gateWasTerminal = true;
562
+ } catch {}
563
+ }
564
+ writeStatus({
565
+ awaitingHumanGate: false,
566
+ state: gateWasTerminal ? 'awaiting_approval' : status.state,
567
+ phase: status.phase,
568
+ });
569
+ }
530
570
  process.stdout.write(JSON.stringify({ ok: true, action: 'end_human_gate', activeHumanGateEventId: status.activeHumanGateEventId || null }));
531
571
  process.exit(0);
532
572
  }
@@ -17,6 +17,7 @@ Checklist for PRs that touch `packages/ai-rules/presets/**` (and dogfood `.curso
17
17
  - [ ] **SoT:** edits in `packages/…` (not only root `.cursor/`)
18
18
  - [ ] **Core:** if `_shared/core/**` changed — all `<!-- shared-core: … -->` consumers updated (or documented intentional fork)
19
19
  - [ ] **Twin:** Cursor change → Claude twin **same PR** (**FAIL** if missing on next, nuxt, svelte, ios-swift, android-kotlin, go, java, mcp-ts, php-hexagonal, and php-laravel — see `preset-twin-sync`)
20
+ - [ ] **Mavis derive:** mapped `cursor/<stack>/` change → `mavis/<stack>/` + `adapter-map.json` **same PR** when the stack is in `releasedStacks` (run `generate-mavis-next.mjs` + `check-mavis-adapter-map.mjs` for `next`; see `preset-twin-sync` § Mavis derivation)
20
21
  - [ ] **Mapping:** `claude/<stack>/rules/README.md` mapping table is current
21
22
  - [ ] **Token budget:** Cursor `alwaysApply` ≤ 4 (4th slot reserved for `ai-local-overlay`); Claude session-start exactly 3; meta rules not always-on
22
23
  - [ ] **Local overlay coverage:** every installable stack ships `ai-local-overlay.mdc` (Cursor) and `ai-local-overlay.md` (Claude) with `MANDATORY` + acknowledgement format + `docs/LOCAL-OVERLAY.md` pointer; Claude twin listed in `claude/<stack>/rules/README.md` mapping table. Run `packages/ai-rules/scripts/check-local-overlay.sh`.
@@ -27,19 +27,34 @@ Cursor `.mdc` — **SoT per stack**. Claude topic `.md` — derived twin.
27
27
  | `php-laravel` | `presets/cursor/php-laravel/rules/*.mdc` | `presets/claude/php-laravel/rules/<topic>/` | Keep in `claude/php-laravel/rules/README.md` |
28
28
  | `android-kotlin` | `presets/cursor/android-kotlin/rules/*.mdc` | `presets/claude/android-kotlin/rules/<topic>/` | Keep in `claude/android-kotlin/rules/README.md` |
29
29
 
30
+ ## Mavis derivation (not a Claude twin)
31
+
32
+ | Stack | Cursor SoT | Mavis derive | Manifest |
33
+ |-------|------------|--------------|----------|
34
+ | `next` | `presets/cursor/next/**` | `presets/mavis/next/**` via `generate-mavis-next.mjs` | **Required** `adapter-map.json` |
35
+
36
+ Mavis is a **third platform adapter**, not a Claude-style topic twin:
37
+
38
+ ```text
39
+ _shared/core → cursor/<stack> → mavis/<stack>
40
+ ```
41
+
42
+ Regenerate with `node packages/ai-rules/scripts/generate-mavis-next.mjs`; validate with `node packages/ai-rules/scripts/check-mavis-adapter-map.mjs`.
43
+
30
44
  ## Same-PR checklist
31
45
 
32
46
  1. Edit Cursor rule first (body + frontmatter).
33
47
  2. Update Claude twin: body + `paths:` (from Cursor `globs:` / requestable triggers); drop `alwaysApply`.
34
48
  3. Update mapping table row if stem/path changed.
35
49
  4. If body embeds core (`<!-- shared-core: … -->`), sync core consumers across stacks when core changed.
50
+ 5. When mapped `cursor/<stack>/` sources change, regenerate `mavis/<stack>/` and refresh `adapter-map.json` `sourceRevision` in the **same PR** (run `generate-mavis-next.mjs` + `check-mavis-adapter-map.mjs` for `next`).
36
51
 
37
52
  ## Twin depth
38
53
 
39
54
  - **Domain** rules: ≥15 body lines (FAIL if thin without documented alias reason).
40
55
  - **Thin aliases** (`agent-team-intake`, `technical-retro`): may be shorter (soft) if they only point to a command/skill.
41
56
  - **Slim-companion rules** (UI-edit bundle, slim app-cores, slim tooling): may be shorter than 15 lines **if and only if** the body is a pointer into shared-core / stack-prose (no standalone prose). Examples live in stack `rules/README.md` (e.g. `cursor/next/rules/README.md` "UI-edit bundle" intent).
42
- - **Mechanical enforcement is pending.** Today build-verifier excludes only `agent-team-intake` / `technical-retro` / `preset-layering`; slim-companion rules currently pass under the same human-judgement path as thin aliases. Follow-up: add `allow_slim_stems` to `packages/ai-rules/scripts/check-preset-token-budget.sh` analogous to `trio_stems_allowlist`.
57
+ - Mechanical enforcement lives in `packages/ai-rules/scripts/check-preset-token-budget.sh` see `SLIM_COMPANION_ALLOWLIST` (the 53 stems accepted as slim pointers) and `MIN_SLIM_BODY_LINES` (5-line floor). Slim-companion rules with a body of 6–14 lines whose stem is **not** in the allowlist are reported as slim violations by the script.
43
58
 
44
59
  ## Severity (build-verifier)
45
60
 
@@ -53,5 +68,6 @@ Cursor `.mdc` — **SoT per stack**. Claude topic `.md` — derived twin.
53
68
  - **php-hexagonal:** missing Claude twin for a changed Cursor rule → **FAIL** (same as next / ios-swift / go / java / mcp-ts; mapping table required).
54
69
  - **php-laravel:** missing Claude twin for a changed Cursor rule → **FAIL** (same as next / ios-swift / go / java / mcp-ts / php-hexagonal; mapping table required).
55
70
  - **android-kotlin:** missing Claude twin for a changed Cursor rule → **FAIL** (same as next / ios-swift / go / java / mcp-ts / php-*; mapping table required).
71
+ - **mavis/next:** `cursor/next` mapped source changed without mavis regeneration / `adapter-map.json` `sourceRevision` drift → **FAIL** (`check-mavis-adapter-map.mjs`).
56
72
 
57
73
  Dogfood: sync packages → root `.cursor/` after Cursor SoT changes (not the reverse). Use `packages/ai-rules/scripts/sync-dogfood-cursor.sh`.
@@ -71,6 +71,8 @@ Write `.cursor/team/tasks/<slug>/validation-report.md` with verdict, commands, f
71
71
 
72
72
  Write the owned validation receipt (`completed` or `validation_failed`). Do not fix code — report only.
73
73
 
74
+ **Receipt binding.** When writing the terminal receipt to `artifact-manifest.json`, set `entry.receipt.attemptId = <attemptId supplied by the orchestrator>` (the orchestrator provides this via `attemptId` on invocation). The hook at `chain-team-phases.sh:readBoundReceiptOutcome` rejects entries whose `attemptId` does not match the current attempt, or whose `Date.parse(timestamps.completedAt) < Date.parse(attemptStartedAt)`. A missing or stale attemptId causes `awaiting_artifact_receipt` stalls. <!-- shared-core: receipt-binding -->
75
+
74
76
  ## Artifact contract
75
77
 
76
78
  - **Authoritative inputs:** read `.cursor/team/tasks/<slug>/artifact-manifest.json` first; consume handoff `artifactInputIds` when supplied.
@@ -66,6 +66,8 @@ Write the owned terminal receipt described under **Artifact contract**. If **REQ
66
66
 
67
67
  Do not mix this output with retrospective facilitation — keep review and retro separate.
68
68
 
69
+ **Receipt binding.** When writing the terminal receipt to `artifact-manifest.json`, set `entry.receipt.attemptId = <attemptId supplied by the orchestrator>` (the orchestrator provides this via `attemptId` on invocation). The hook at `chain-team-phases.sh:readBoundReceiptOutcome` rejects entries whose `attemptId` does not match the current attempt, or whose `Date.parse(timestamps.completedAt) < Date.parse(attemptStartedAt)`. A missing or stale attemptId causes `awaiting_artifact_receipt` stalls. <!-- shared-core: receipt-binding -->
70
+
69
71
  ## Design guidance
70
72
 
71
73
  - Load `design-guidance` (rule stem) when assessing structure, smells, or pattern fit.
@@ -57,6 +57,7 @@ fixApplied: false
57
57
 
58
58
  Write the owned terminal receipt described under **Artifact contract**.
59
59
 
60
+ **Receipt binding.** When writing the terminal receipt to `artifact-manifest.json`, set `entry.receipt.attemptId = <attemptId supplied by the orchestrator>` (the orchestrator provides this via `attemptId` on invocation). The hook at `chain-team-phases.sh:readBoundReceiptOutcome` rejects entries whose `attemptId` does not match the current attempt, or whose `Date.parse(timestamps.completedAt) < Date.parse(attemptStartedAt)`. A missing or stale attemptId causes `awaiting_artifact_receipt` stalls. <!-- shared-core: receipt-binding -->
60
61
 
61
62
  ## 3-fix architecture gate
62
63