@haposoft/cafekit 0.16.0-rc.8 → 0.16.1

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 (323) hide show
  1. package/README.md +117 -28
  2. package/bin/__tests__/codex-hooks-ownership.test.js +239 -0
  3. package/bin/__tests__/codex-hooks.test.js +175 -0
  4. package/bin/__tests__/codex-native.test.js +1882 -70
  5. package/bin/__tests__/develop-contract.test.js +442 -23
  6. package/bin/__tests__/grok-envelope.test.js +271 -0
  7. package/bin/__tests__/grok-platform.test.js +133 -0
  8. package/bin/__tests__/hook-payload.test.js +194 -0
  9. package/bin/__tests__/installer-safety.test.js +3 -3
  10. package/bin/__tests__/omp-bridge.test.js +168 -0
  11. package/bin/__tests__/omp-hooks.test.js +178 -0
  12. package/bin/__tests__/omp-runtime.test.js +150 -0
  13. package/bin/__tests__/optional-skill-inventory.test.js +249 -0
  14. package/bin/__tests__/output-styles.test.js +214 -0
  15. package/bin/__tests__/package-inventory.test.js +1735 -98
  16. package/bin/__tests__/runtime-dir.test.js +158 -0
  17. package/bin/__tests__/runtime-schema.test.js +113 -0
  18. package/bin/__tests__/secret-output-guardrail.test.js +122 -0
  19. package/bin/__tests__/skill-routing-source.test.js +172 -0
  20. package/bin/__tests__/specs-v2-execution-closeout.test.js +28 -1
  21. package/bin/install.js +15 -5
  22. package/bin/lib/codex-install.js +170 -5
  23. package/bin/lib/context.js +64 -3
  24. package/bin/lib/i18n.js +15 -36
  25. package/bin/lib/skill-deps.js +5 -5
  26. package/bin/phases/claude-runtime.js +23 -0
  27. package/bin/phases/claude-settings.js +8 -0
  28. package/bin/phases/codex-hooks.js +156 -0
  29. package/bin/phases/codex-runtime.js +29 -38
  30. package/bin/phases/copy-payload.js +15 -2
  31. package/bin/phases/omp-runtime.js +140 -0
  32. package/bin/phases/root-config.js +3 -0
  33. package/bin/phases/select-platform.js +22 -1
  34. package/bin/phases/select-skill-bundles.js +96 -0
  35. package/bin/phases/skill-inventory.js +71 -0
  36. package/bin/phases/skills-setup.js +9 -2
  37. package/bin/phases/summary.js +0 -4
  38. package/bin/phases/write-metadata.js +8 -5
  39. package/package.json +8 -2
  40. package/src/claude/CLAUDE.md +3 -3
  41. package/src/claude/agents/brainstormer.md +52 -69
  42. package/src/claude/agents/code-auditor.md +32 -14
  43. package/src/claude/agents/debugger.md +35 -20
  44. package/src/claude/agents/deployer.md +12 -5
  45. package/src/claude/agents/docs-keeper.md +11 -1
  46. package/src/claude/agents/implementer.md +25 -13
  47. package/src/claude/agents/inspector.md +6 -3
  48. package/src/claude/agents/project-manager.md +1 -1
  49. package/src/claude/agents/researcher.md +26 -55
  50. package/src/claude/agents/spec-maker.md +12 -7
  51. package/src/claude/agents/strategist.md +52 -0
  52. package/src/claude/agents/test-runner.md +117 -132
  53. package/src/claude/agents/ui-ux-designer.md +1 -1
  54. package/src/claude/hooks/__tests__/fixtures/statusline-default.golden +1 -0
  55. package/src/claude/hooks/__tests__/precompact.test.js +158 -0
  56. package/src/claude/hooks/__tests__/semantic-review-authority.test.js +2 -0
  57. package/src/claude/hooks/__tests__/spec-gate.test.js +348 -3
  58. package/src/claude/hooks/__tests__/state.test.js +7 -0
  59. package/src/claude/hooks/__tests__/statusline.test.js +196 -0
  60. package/src/claude/hooks/agent.cjs +13 -8
  61. package/src/claude/hooks/completion-authority.cjs +10 -4
  62. package/src/claude/hooks/docs-sync.cjs +14 -11
  63. package/src/claude/hooks/inspect-block.cjs +28 -8
  64. package/src/claude/hooks/lib/config.cjs +8 -2
  65. package/src/claude/hooks/lib/counter.cjs +7 -5
  66. package/src/claude/hooks/lib/hook-payload.cjs +160 -0
  67. package/src/claude/hooks/lib/hook-state-dir.cjs +39 -0
  68. package/src/claude/hooks/lib/runtime-dir.cjs +49 -0
  69. package/src/claude/hooks/lib/secret-keywords.cjs +42 -0
  70. package/src/claude/hooks/precompact.cjs +106 -0
  71. package/src/claude/hooks/privacy-block.cjs +4 -2
  72. package/src/claude/hooks/rules.cjs +5 -3
  73. package/src/claude/hooks/secret-output-guardrail.cjs +44 -0
  74. package/src/claude/hooks/semantic-review-authority.cjs +6 -4
  75. package/src/claude/hooks/session.cjs +37 -9
  76. package/src/claude/hooks/spec-gate.cjs +21 -11
  77. package/src/claude/hooks/spec-state.cjs +11 -7
  78. package/src/claude/hooks/state.cjs +7 -5
  79. package/src/claude/hooks/task-scaffold-guard.cjs +30 -9
  80. package/src/claude/hooks/usage.cjs +6 -5
  81. package/src/claude/migration-manifest.json +31 -15
  82. package/src/claude/output-styles/coding-level-0-eli5.md +103 -0
  83. package/src/claude/output-styles/coding-level-1-junior.md +124 -0
  84. package/src/claude/output-styles/coding-level-2-mid.md +146 -0
  85. package/src/claude/output-styles/coding-level-3-senior.md +148 -0
  86. package/src/claude/output-styles/coding-level-4-lead.md +159 -0
  87. package/src/claude/output-styles/coding-level-5-god.md +91 -0
  88. package/src/claude/references/debugger/core-philosophy.md +5 -3
  89. package/src/claude/references/debugger/frontend-verification.md +2 -2
  90. package/src/claude/references/debugger/log-ci-analysis.md +13 -1
  91. package/src/claude/references/debugger/parallel-agent-hydration.md +19 -34
  92. package/src/claude/references/debugger/performance-diagnostics.md +2 -2
  93. package/src/claude/references/debugger/root-cause-tracing.md +37 -6
  94. package/src/claude/references/debugger/side-effect-gate.md +3 -1
  95. package/src/claude/references/debugger/verification-protocol.md +13 -12
  96. package/src/claude/rules/orchestrator.md +7 -1
  97. package/src/claude/rules/process-management.md +39 -0
  98. package/src/claude/rules/review-audit-self-decision.md +48 -0
  99. package/src/claude/rules/skill-domain-routing.md +26 -24
  100. package/src/claude/rules/skill-workflow-routing.md +22 -14
  101. package/src/claude/rules/state-sync.md +7 -1
  102. package/src/claude/rules/workflow.md +5 -3
  103. package/src/claude/runtime.json +1 -0
  104. package/src/claude/runtime.schema.json +205 -0
  105. package/src/claude/scripts/generate-skill-catalog.cjs +156 -122
  106. package/src/claude/scripts/provenance.cjs +29 -9
  107. package/src/claude/scripts/spec-receipt.cjs +65 -4
  108. package/src/claude/scripts/workflow-policy.cjs +1 -1
  109. package/src/claude/settings/settings.json +16 -0
  110. package/src/claude/skills/agent-browser/SKILL.md +1 -1
  111. package/src/claude/skills/ai-multimodal/SKILL.md +1 -1
  112. package/src/claude/skills/brainstorm/SKILL.md +193 -172
  113. package/src/claude/skills/brainstorm/references/question-framework.md +105 -167
  114. package/src/claude/skills/chrome-devtools/SKILL.md +1 -1
  115. package/src/claude/skills/code-review/SKILL.md +43 -25
  116. package/src/claude/skills/code-review/references/spec-compliance-review.md +3 -4
  117. package/src/claude/skills/code-review/references/verification-gate.md +1 -1
  118. package/src/claude/skills/debug/SKILL.md +48 -19
  119. package/src/claude/skills/delegate/SKILL.md +1 -1
  120. package/src/claude/skills/develop/SKILL.md +92 -65
  121. package/src/claude/skills/develop/references/parallel-waves.md +36 -63
  122. package/src/claude/skills/develop/references/quality-gate.md +31 -4
  123. package/src/claude/skills/develop/references/subagent-patterns.md +21 -7
  124. package/src/claude/skills/docs/SKILL.md +42 -23
  125. package/src/claude/skills/docs/references/init-workflow.md +4 -4
  126. package/src/claude/skills/docs/references/reconstruct-workflow.md +6 -6
  127. package/src/claude/skills/docs/references/standard-docs-workflow.md +6 -2
  128. package/src/claude/skills/docs/references/summarize-workflow.md +2 -2
  129. package/src/claude/skills/docs/references/update-workflow.md +9 -7
  130. package/src/claude/skills/docs/templates/requirements-as-is.md +1 -1
  131. package/src/claude/skills/docx/SKILL.md +1 -1
  132. package/src/claude/skills/git/SKILL.md +1 -1
  133. package/src/claude/skills/git/references/commit-protocols.md +1 -1
  134. package/src/claude/skills/git/references/finish-branch.md +1 -1
  135. package/src/claude/skills/hotfix/SKILL.md +159 -170
  136. package/src/claude/skills/hotfix/references/diagnosis-protocol.md +14 -9
  137. package/src/claude/skills/hotfix/references/parallel-patterns.md +27 -13
  138. package/src/claude/skills/hotfix/references/prevention-gate.md +5 -0
  139. package/src/claude/skills/hotfix/references/review-cycle.md +21 -15
  140. package/src/claude/skills/hotfix/references/workflow-specialized.md +53 -21
  141. package/src/claude/skills/inspect/SKILL.md +55 -46
  142. package/src/claude/skills/inspect/references/internal-inspection.md +21 -5
  143. package/src/claude/skills/loop/SKILL.md +114 -0
  144. package/src/claude/skills/loop/references/bounded-loop-protocol.md +104 -0
  145. package/src/claude/skills/loop/references/metric-and-guard-contract.md +76 -0
  146. package/src/claude/skills/pdf/SKILL.md +1 -1
  147. package/src/claude/skills/pptx/SKILL.md +1 -1
  148. package/src/claude/skills/question/SKILL.md +16 -13
  149. package/src/claude/skills/research/SKILL.md +70 -43
  150. package/src/claude/skills/route/SKILL.md +94 -0
  151. package/src/claude/skills/route/references/agent-timing.md +46 -0
  152. package/src/claude/skills/route/references/chaining-patterns.md +53 -0
  153. package/src/claude/skills/route/references/task-taxonomy.md +48 -0
  154. package/src/claude/skills/specs/SKILL.md +51 -71
  155. package/src/claude/skills/specs/references/review.md +11 -8
  156. package/src/claude/skills/specs/references/templates.md +34 -18
  157. package/src/claude/skills/specs/templates/requirements-init.md +1 -1
  158. package/src/claude/skills/specs/templates/research.md +2 -2
  159. package/src/claude/skills/sync/SKILL.md +6 -6
  160. package/src/claude/skills/sync/references/sync-protocols.md +1 -1
  161. package/src/claude/skills/test/SKILL.md +120 -120
  162. package/src/claude/skills/test/references/execution-strategy.md +208 -361
  163. package/src/claude/skills/test/references/failure-triage.md +55 -121
  164. package/src/claude/skills/test/references/test-memory.md +20 -34
  165. package/src/claude/skills/ui-ux-pro-max/SKILL.md +1 -1
  166. package/src/claude/skills/web-testing/SKILL.md +1 -1
  167. package/src/claude/skills/xlsx/SKILL.md +1 -1
  168. package/src/claude/status.cjs +135 -33
  169. package/src/codex/AGENTS.md +14 -4
  170. package/src/codex/hooks/docs-sync.cjs +8 -3
  171. package/src/codex/hooks/lib/hook-context.cjs +18 -4
  172. package/src/codex/hooks/rules.cjs +29 -1
  173. package/src/codex/hooks/secret-output-guardrail.cjs +30 -0
  174. package/src/codex/hooks/spec-gate.cjs +3 -8
  175. package/src/codex/hooks/spec-state.cjs +3 -2
  176. package/src/codex/hooks.json +5 -0
  177. package/src/codex/rules/state-sync.md +7 -1
  178. package/src/codex/runtime.json +2 -0
  179. package/src/common/.env.example +1 -1
  180. package/src/omp/extensions/cafekit-bridge.mjs +233 -0
  181. package/src/omp/hooks/privacy-block.cjs +250 -0
  182. package/src/omp/runtime.json +33 -0
  183. package/bin/lib/rtk-run.js +0 -28
  184. package/bin/phases/setup-rtk.js +0 -159
  185. package/src/claude/skills/backend-development/SKILL.md +0 -101
  186. package/src/claude/skills/backend-development/references/backend-api-design.md +0 -495
  187. package/src/claude/skills/backend-development/references/backend-architecture.md +0 -454
  188. package/src/claude/skills/backend-development/references/backend-authentication.md +0 -338
  189. package/src/claude/skills/backend-development/references/backend-code-quality.md +0 -659
  190. package/src/claude/skills/backend-development/references/backend-debugging.md +0 -904
  191. package/src/claude/skills/backend-development/references/backend-devops.md +0 -491
  192. package/src/claude/skills/backend-development/references/backend-mindset.md +0 -387
  193. package/src/claude/skills/backend-development/references/backend-performance.md +0 -397
  194. package/src/claude/skills/backend-development/references/backend-security.md +0 -342
  195. package/src/claude/skills/backend-development/references/backend-technologies.md +0 -256
  196. package/src/claude/skills/backend-development/references/backend-testing.md +0 -429
  197. package/src/claude/skills/devops/.env.example +0 -76
  198. package/src/claude/skills/devops/SKILL.md +0 -102
  199. package/src/claude/skills/devops/references/browser-rendering.md +0 -305
  200. package/src/claude/skills/devops/references/cloudflare-d1-kv.md +0 -123
  201. package/src/claude/skills/devops/references/cloudflare-platform.md +0 -271
  202. package/src/claude/skills/devops/references/cloudflare-r2-storage.md +0 -280
  203. package/src/claude/skills/devops/references/cloudflare-workers-advanced.md +0 -312
  204. package/src/claude/skills/devops/references/cloudflare-workers-apis.md +0 -309
  205. package/src/claude/skills/devops/references/cloudflare-workers-basics.md +0 -418
  206. package/src/claude/skills/devops/references/docker-basics.md +0 -297
  207. package/src/claude/skills/devops/references/docker-compose.md +0 -292
  208. package/src/claude/skills/devops/references/gcloud-platform.md +0 -297
  209. package/src/claude/skills/devops/references/gcloud-services.md +0 -304
  210. package/src/claude/skills/devops/references/kubernetes-basics.md +0 -95
  211. package/src/claude/skills/devops/references/kubernetes-helm-advanced.md +0 -70
  212. package/src/claude/skills/devops/references/kubernetes-helm.md +0 -81
  213. package/src/claude/skills/devops/references/kubernetes-kubectl.md +0 -74
  214. package/src/claude/skills/devops/references/kubernetes-security-advanced.md +0 -94
  215. package/src/claude/skills/devops/references/kubernetes-security.md +0 -89
  216. package/src/claude/skills/devops/references/kubernetes-troubleshooting-advanced.md +0 -74
  217. package/src/claude/skills/devops/references/kubernetes-troubleshooting.md +0 -49
  218. package/src/claude/skills/devops/references/kubernetes-workflows-advanced.md +0 -75
  219. package/src/claude/skills/devops/references/kubernetes-workflows.md +0 -76
  220. package/src/claude/skills/devops/scripts/cloudflare_deploy.py +0 -269
  221. package/src/claude/skills/devops/scripts/docker_optimize.py +0 -332
  222. package/src/claude/skills/devops/scripts/requirements.txt +0 -20
  223. package/src/claude/skills/devops/scripts/tests/requirements.txt +0 -3
  224. package/src/claude/skills/devops/scripts/tests/test_cloudflare_deploy.py +0 -285
  225. package/src/claude/skills/devops/scripts/tests/test_docker_optimize.py +0 -436
  226. package/src/claude/skills/frontend-design/SKILL.md +0 -135
  227. package/src/claude/skills/frontend-design/references/ai-multimodal-overview.md +0 -165
  228. package/src/claude/skills/frontend-design/references/analysis-best-practices.md +0 -80
  229. package/src/claude/skills/frontend-design/references/analysis-prompts.md +0 -141
  230. package/src/claude/skills/frontend-design/references/analysis-techniques.md +0 -118
  231. package/src/claude/skills/frontend-design/references/animejs.md +0 -396
  232. package/src/claude/skills/frontend-design/references/anti-slop-rules.md +0 -103
  233. package/src/claude/skills/frontend-design/references/asset-generation.md +0 -337
  234. package/src/claude/skills/frontend-design/references/bento-motion-engine.md +0 -142
  235. package/src/claude/skills/frontend-design/references/design-extraction-overview.md +0 -71
  236. package/src/claude/skills/frontend-design/references/extraction-best-practices.md +0 -141
  237. package/src/claude/skills/frontend-design/references/extraction-output-templates.md +0 -162
  238. package/src/claude/skills/frontend-design/references/extraction-prompts.md +0 -127
  239. package/src/claude/skills/frontend-design/references/magicui-components.md +0 -129
  240. package/src/claude/skills/frontend-design/references/performance-guardrails.md +0 -169
  241. package/src/claude/skills/frontend-design/references/premium-design-patterns.md +0 -93
  242. package/src/claude/skills/frontend-design/references/redesign-audit-checklist.md +0 -114
  243. package/src/claude/skills/frontend-design/references/technical-accessibility.md +0 -119
  244. package/src/claude/skills/frontend-design/references/technical-best-practices.md +0 -97
  245. package/src/claude/skills/frontend-design/references/technical-optimization.md +0 -44
  246. package/src/claude/skills/frontend-design/references/technical-overview.md +0 -90
  247. package/src/claude/skills/frontend-design/references/technical-workflows.md +0 -150
  248. package/src/claude/skills/frontend-design/references/visual-analysis-overview.md +0 -95
  249. package/src/claude/skills/frontend-design/references/workflow-3d.md +0 -102
  250. package/src/claude/skills/frontend-design/references/workflow-describe.md +0 -87
  251. package/src/claude/skills/frontend-design/references/workflow-immersive.md +0 -87
  252. package/src/claude/skills/frontend-design/references/workflow-quick.md +0 -57
  253. package/src/claude/skills/frontend-design/references/workflow-screenshot.md +0 -63
  254. package/src/claude/skills/frontend-design/references/workflow-video.md +0 -74
  255. package/src/claude/skills/frontend-development/SKILL.md +0 -406
  256. package/src/claude/skills/frontend-development/resources/common-patterns.md +0 -331
  257. package/src/claude/skills/frontend-development/resources/complete-examples.md +0 -872
  258. package/src/claude/skills/frontend-development/resources/component-patterns.md +0 -502
  259. package/src/claude/skills/frontend-development/resources/data-fetching.md +0 -767
  260. package/src/claude/skills/frontend-development/resources/file-organization.md +0 -502
  261. package/src/claude/skills/frontend-development/resources/loading-and-error-states.md +0 -501
  262. package/src/claude/skills/frontend-development/resources/performance.md +0 -406
  263. package/src/claude/skills/frontend-development/resources/routing-guide.md +0 -364
  264. package/src/claude/skills/frontend-development/resources/styling-guide.md +0 -428
  265. package/src/claude/skills/frontend-development/resources/typescript-standards.md +0 -418
  266. package/src/claude/skills/mobile-development/SKILL.md +0 -218
  267. package/src/claude/skills/mobile-development/references/mobile-android.md +0 -604
  268. package/src/claude/skills/mobile-development/references/mobile-best-practices.md +0 -544
  269. package/src/claude/skills/mobile-development/references/mobile-debugging.md +0 -1089
  270. package/src/claude/skills/mobile-development/references/mobile-frameworks.md +0 -465
  271. package/src/claude/skills/mobile-development/references/mobile-ios.md +0 -496
  272. package/src/claude/skills/mobile-development/references/mobile-mindset.md +0 -542
  273. package/src/claude/skills/react-best-practices/AGENTS.md +0 -2249
  274. package/src/claude/skills/react-best-practices/README.md +0 -123
  275. package/src/claude/skills/react-best-practices/SKILL.md +0 -128
  276. package/src/claude/skills/react-best-practices/metadata.json +0 -15
  277. package/src/claude/skills/react-best-practices/rules/_sections.md +0 -46
  278. package/src/claude/skills/react-best-practices/rules/_template.md +0 -28
  279. package/src/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +0 -55
  280. package/src/claude/skills/react-best-practices/rules/advanced-use-latest.md +0 -49
  281. package/src/claude/skills/react-best-practices/rules/async-api-routes.md +0 -38
  282. package/src/claude/skills/react-best-practices/rules/async-defer-await.md +0 -80
  283. package/src/claude/skills/react-best-practices/rules/async-dependencies.md +0 -36
  284. package/src/claude/skills/react-best-practices/rules/async-parallel.md +0 -28
  285. package/src/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +0 -99
  286. package/src/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +0 -59
  287. package/src/claude/skills/react-best-practices/rules/bundle-conditional.md +0 -31
  288. package/src/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +0 -49
  289. package/src/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +0 -35
  290. package/src/claude/skills/react-best-practices/rules/bundle-preload.md +0 -50
  291. package/src/claude/skills/react-best-practices/rules/client-event-listeners.md +0 -74
  292. package/src/claude/skills/react-best-practices/rules/client-swr-dedup.md +0 -56
  293. package/src/claude/skills/react-best-practices/rules/js-batch-dom-css.md +0 -82
  294. package/src/claude/skills/react-best-practices/rules/js-cache-function-results.md +0 -80
  295. package/src/claude/skills/react-best-practices/rules/js-cache-property-access.md +0 -28
  296. package/src/claude/skills/react-best-practices/rules/js-cache-storage.md +0 -70
  297. package/src/claude/skills/react-best-practices/rules/js-combine-iterations.md +0 -32
  298. package/src/claude/skills/react-best-practices/rules/js-early-exit.md +0 -50
  299. package/src/claude/skills/react-best-practices/rules/js-hoist-regexp.md +0 -45
  300. package/src/claude/skills/react-best-practices/rules/js-index-maps.md +0 -37
  301. package/src/claude/skills/react-best-practices/rules/js-length-check-first.md +0 -49
  302. package/src/claude/skills/react-best-practices/rules/js-min-max-loop.md +0 -82
  303. package/src/claude/skills/react-best-practices/rules/js-set-map-lookups.md +0 -24
  304. package/src/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +0 -57
  305. package/src/claude/skills/react-best-practices/rules/rendering-activity.md +0 -26
  306. package/src/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
  307. package/src/claude/skills/react-best-practices/rules/rendering-conditional-render.md +0 -40
  308. package/src/claude/skills/react-best-practices/rules/rendering-content-visibility.md +0 -38
  309. package/src/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +0 -46
  310. package/src/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
  311. package/src/claude/skills/react-best-practices/rules/rendering-svg-precision.md +0 -28
  312. package/src/claude/skills/react-best-practices/rules/rerender-defer-reads.md +0 -39
  313. package/src/claude/skills/react-best-practices/rules/rerender-dependencies.md +0 -45
  314. package/src/claude/skills/react-best-practices/rules/rerender-derived-state.md +0 -29
  315. package/src/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +0 -74
  316. package/src/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +0 -58
  317. package/src/claude/skills/react-best-practices/rules/rerender-memo.md +0 -44
  318. package/src/claude/skills/react-best-practices/rules/rerender-transitions.md +0 -40
  319. package/src/claude/skills/react-best-practices/rules/server-after-nonblocking.md +0 -73
  320. package/src/claude/skills/react-best-practices/rules/server-cache-lru.md +0 -41
  321. package/src/claude/skills/react-best-practices/rules/server-cache-react.md +0 -26
  322. package/src/claude/skills/react-best-practices/rules/server-parallel-fetching.md +0 -79
  323. package/src/claude/skills/react-best-practices/rules/server-serialization.md +0 -38
@@ -12,6 +12,8 @@ const PACKAGE_ROOT = path.join(__dirname, '../..');
12
12
  const POLICY_PATH = path.join(PACKAGE_ROOT, 'src/claude/scripts/workflow-policy.cjs');
13
13
  const POLICY = require(POLICY_PATH);
14
14
  const PROVENANCE_HELPER = require(path.join(PACKAGE_ROOT, 'src/claude/scripts/provenance.cjs'));
15
+ const RESOLVER = require(path.join(PACKAGE_ROOT, 'src/claude/scripts/spec-resolver.cjs'));
16
+ const RECEIPTS = require(path.join(PACKAGE_ROOT, 'src/claude/scripts/spec-receipt.cjs'));
15
17
  const FINAL_STATE = require(path.join(PACKAGE_ROOT, 'src/claude/scripts/spec-final-state.cjs'));
16
18
  const VALIDATOR = require(path.join(PACKAGE_ROOT, 'src/claude/scripts/validate-spec-output.cjs'));
17
19
  const GROUNDER = require(path.join(PACKAGE_ROOT, 'src/claude/scripts/spec-ground.cjs'));
@@ -24,11 +26,24 @@ const SPECS = path.join(PACKAGE_ROOT, 'src/claude/skills/specs/SKILL.md');
24
26
  const SCAFFOLD = path.join(PACKAGE_ROOT, 'src/claude/scripts/spec-scaffold.cjs');
25
27
  const GATE = path.join(PACKAGE_ROOT, 'src/claude/skills/develop/references/quality-gate.md');
26
28
  const TEST_SKILL = path.join(PACKAGE_ROOT, 'src/claude/skills/test/SKILL.md');
29
+ const TEST_SOURCE_ROOT = path.dirname(TEST_SKILL);
30
+ const TEST_REFERENCES = ['execution-strategy.md', 'failure-triage.md', 'test-memory.md'];
31
+ const TEST_RUNNER = path.join(PACKAGE_ROOT, 'src/claude/agents/test-runner.md');
27
32
  const SYNC_SKILL = path.join(PACKAGE_ROOT, 'src/claude/skills/sync/SKILL.md');
28
33
  const CODE_REVIEW_SKILL = path.join(PACKAGE_ROOT, 'src/claude/skills/code-review/SKILL.md');
29
34
  const PARALLEL_WAVES = path.join(PACKAGE_ROOT, 'src/claude/skills/develop/references/parallel-waves.md');
35
+ const DEVELOP_REFERENCES = ['quality-gate.md', 'parallel-waves.md', 'subagent-patterns.md'];
36
+ const INSTALLER = path.join(PACKAGE_ROOT, 'bin/install.js');
30
37
  const SYNC_PROTOCOLS = path.join(PACKAGE_ROOT, 'src/claude/skills/sync/references/sync-protocols.md');
38
+ const PROCESS_FIRST_AGENTS = [
39
+ 'implementer.md',
40
+ 'inspector.md',
41
+ 'code-auditor.md',
42
+ 'docs-keeper.md',
43
+ 'deployer.md',
44
+ ].map((name) => path.join(PACKAGE_ROOT, 'src/claude/agents', name));
31
45
  const PROVENANCE = path.join(PACKAGE_ROOT, '../../docs/provenance.md');
46
+ const SPECS_USAGE_GUIDE = path.join(PACKAGE_ROOT, '../../docs/specs-usage-guide.md');
32
47
  const RUNTIME_ROOT = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-policy-runtime-'));
33
48
  const RUNTIME_SPEC = path.join(RUNTIME_ROOT, 'specs', 'demo', 'spec.json');
34
49
  fs.mkdirSync(path.dirname(RUNTIME_SPEC), { recursive: true });
@@ -73,9 +88,23 @@ function installClaudeRuntimeClosure(fixtureRoot, entry = 'hooks/spec-gate.cjs')
73
88
  fs.mkdirSync(path.dirname(target), { recursive: true });
74
89
  fs.copyFileSync(path.join(hooksRoot, hook), target);
75
90
  }
91
+ // Hook helpers the raw-copied hooks require at runtime.
92
+ for (const helper of ['hook-state-dir.cjs', 'runtime-dir.cjs', 'hook-payload.cjs']) {
93
+ const target = path.join(destinationRoot, 'hooks', 'lib', helper);
94
+ fs.mkdirSync(path.dirname(target), { recursive: true });
95
+ fs.copyFileSync(path.join(hooksRoot, 'lib', helper), target);
96
+ }
76
97
  return path.join(destinationRoot, entry);
77
98
  }
78
99
 
100
+ function installClaude(root) {
101
+ return spawnSync(process.execPath, [INSTALLER, '--platform', 'claude', '--yes'], {
102
+ cwd: root,
103
+ encoding: 'utf8',
104
+ env: { ...process.env, PATH: '/usr/bin:/bin' },
105
+ });
106
+ }
107
+
79
108
  function markdownLegacyRegions(content) {
80
109
  const headings = [];
81
110
  const primaryLines = [];
@@ -300,7 +329,7 @@ function canonicalFlashTask(overrides = {}) {
300
329
  return {
301
330
  status: 'in_progress',
302
331
  receipt: 'FLASH_UNVERIFIED',
303
- blocker: 'awaiting /hapo:test <feature>',
332
+ blocker: 'awaiting /cf:test <feature>',
304
333
  dependencyBlocked: true,
305
334
  unblocks: false,
306
335
  feature_name: 'demo',
@@ -348,7 +377,7 @@ test('develop flags retain their process-v3 contracts', () => {
348
377
  assert.match(develop, /implementation-notes-template\.html/);
349
378
  assert.match(develop, /Never create it by default/i);
350
379
  assert.match(develop, /--parallel \[N\][\s\S]*parallel-waves\.md[\s\S]*isolated worktrees[\s\S]*one controller writer/i);
351
- assert.match(develop, /--flash[\s\S]*Status: in_progress[\s\S]*FLASH_UNVERIFIED[\s\S]*awaiting \/hapo:test <feature>/i);
380
+ assert.match(develop, /--flash[\s\S]*Status: in_progress[\s\S]*FLASH_UNVERIFIED[\s\S]*awaiting \/cf:test <feature>/i);
352
381
  assert.match(develop, /FLASH_UNVERIFIED[\s\S]*do not unblock dependents[\s\S]*sync-finalize path/i);
353
382
  });
354
383
 
@@ -771,6 +800,35 @@ test('Specs primary output is a flat process-first packet with isolated legacy c
771
800
  assertVocabularyIsLegacyOnly(SPECS, regions);
772
801
  });
773
802
 
803
+ test('core execution agents default to process-first state and isolate legacy packets', () => {
804
+ for (const filePath of PROCESS_FIRST_AGENTS) {
805
+ const content = read(filePath);
806
+ const name = path.basename(filePath);
807
+ assert.match(content, /plan\.md/i, `${name} must read the process-first plan`);
808
+ assert.match(content, /task-NN-\*\.md|task-NN-<slug>\.md/i, `${name} must understand flat tasks`);
809
+ assert.match(content, /legacy/i, `${name} must isolate legacy compatibility`);
810
+ }
811
+
812
+ const implementer = read(PROCESS_FIRST_AGENTS[0]);
813
+ assert.match(implementer, /Under every dispatch mode[\s\S]{0,160}do NOT edit[\s\S]{0,80}`plan\.md`[\s\S]{0,80}`Status:`[\s\S]{0,80}`## Receipt`/i);
814
+ assert.match(implementer, /controller/i);
815
+ assert.match(implementer, /process-first work[\s\S]{0,100}Ownership[\s\S]{0,100}`Related Files` only for a valid legacy adapter/i);
816
+
817
+ const auditor = read(PROCESS_FIRST_AGENTS[2]);
818
+ assert.match(auditor, /accepted C1\/C2 decisions/i);
819
+ assert.match(auditor, /Only for a valid legacy adapter[\s\S]*`Related Files`[\s\S]*`## Evidence`/i);
820
+ assert.match(auditor, /Never require those legacy artifacts from a[\s\S]*process-first packet/i);
821
+ assert.match(auditor, /attestation belongs only to the valid legacy/i);
822
+
823
+ const docsKeeper = read(PROCESS_FIRST_AGENTS[3]);
824
+ assert.match(docsKeeper, /never[\s\S]{0,80}invent or write Status, Receipt, approval, or execution proof/i);
825
+
826
+ const deployer = read(PROCESS_FIRST_AGENTS[4]);
827
+ assert.match(deployer, /current final inline Receipt/i);
828
+ assert.match(deployer, /C3\/release authorization/i);
829
+ assert.match(deployer, /does not write process-first Status\/Receipt/i);
830
+ });
831
+
774
832
  test('R7 Develop and Sync surfaces teach process-v3 and isolate hierarchical Legacy sections', () => {
775
833
  const surfaces = [
776
834
  ['develop', DEVELOP],
@@ -795,10 +853,10 @@ test('R7 Develop and Sync surfaces teach process-v3 and isolate hierarchical Leg
795
853
 
796
854
  const develop = regionsByName.get('develop').primary;
797
855
  assert.match(develop, /`specs\/<feature>\/plan\.md` plus flat `task-NN-\*\.md`/i);
798
- assert.match(develop, /selects one unblocked task at a time/i);
856
+ assert.match(develop, /one unblocked task at a time/i);
799
857
  assert.match(develop, /Outcome and Acceptance/i);
800
858
  assert.match(develop, /Verification Plan/);
801
- assert.match(develop, /final `## Receipt`/i);
859
+ assert.match(develop, /final inline `## Receipt`/i);
802
860
 
803
861
  const sync = regionsByName.get('sync').primary;
804
862
  assert.match(sync, /<task-NN-slug\.md>/i);
@@ -807,10 +865,10 @@ test('R7 Develop and Sync surfaces teach process-v3 and isolate hierarchical Leg
807
865
  assert.match(sync, /current inline Receipt/i);
808
866
 
809
867
  const waves = regionsByName.get('parallel waves').primary;
810
- assert.match(waves, /plan task table[\s\S]*flat task's[\s\S]*`## Dependencies`/i);
811
- assert.match(waves, /acceptance, Verification Plan/i);
868
+ assert.match(waves, /dependencies from the plan table and each task/i);
869
+ assert.match(waves, /acceptance, proof command/i);
812
870
  assert.match(waves, /one file has one writer per wave/i);
813
- assert.match(waves, /writes inline Receipts and Status updates one task at a\s+time/i);
871
+ assert.match(waves, /writes inline Receipts and Status one task at a time/i);
814
872
 
815
873
  const protocols = regionsByName.get('sync protocols').primary;
816
874
  assert.match(protocols, /`plan\.md` and flat `task-\*\.md` files/i);
@@ -819,6 +877,267 @@ test('R7 Develop and Sync surfaces teach process-v3 and isolate hierarchical Leg
819
877
  assert.match(legacyCorpus, /sync-finalize/i);
820
878
  });
821
879
 
880
+ test('Develop process-first source contract preserves selection, recovery, final-Head, parallel, and Flash boundaries', () => {
881
+ const develop = markdownLegacyRegions(read(DEVELOP)).primary;
882
+ const quality = read(GATE);
883
+ const waves = markdownLegacyRegions(read(PARALLEL_WAVES)).primary;
884
+ const dispatch = read(path.join(path.dirname(PARALLEL_WAVES), 'subagent-patterns.md'));
885
+
886
+ for (const clause of [
887
+ /first dependency-valid `pending` row in `plan\.md` order/i,
888
+ /Specific-task\s+mode never touches a sibling/i,
889
+ /More than one `in_progress`/i,
890
+ /For an interrupted `in_progress` task/i,
891
+ /only the controller writes Status or Receipt/i,
892
+ /do not research, replan, or add a routine user gate/i,
893
+ ]) assert.match(develop, clause);
894
+ assert.match(quality, /consecutive Head captures are identical/i);
895
+ assert.match(quality, /skip, skipped, todo, cancel, canceled, cancelled/i);
896
+ assert.match(quality, /exact command, current Head, required proof level, oracle/i);
897
+ assert.match(waves, /every commit[\s\S]*complete changed tree/i);
898
+ assert.match(waves, /handoff metadata cannot substitute/i);
899
+ assert.match(develop, /later explicit non-Flash invocation may recover/i);
900
+ assert.match(dispatch, /controller alone writes Status and inline Receipt/i);
901
+
902
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-plan-native-'));
903
+ try {
904
+ const featureDir = path.join(root, 'specs', 'ordered');
905
+ fs.mkdirSync(featureDir, { recursive: true });
906
+ fs.writeFileSync(path.join(featureDir, 'plan.md'), '# Ordered\nSpecs-Contract: process-first-ready-v1\n');
907
+ const task = (status, dependencies, receipt = '<!-- Fill only after execution. -->') =>
908
+ `# Task\n\nStatus: ${status}\n\n## Dependencies\n\n${dependencies}\n\n## Verification Plan\n\n- Command: \`node --test\`\n\n## Receipt\n\n${receipt}\n`;
909
+ fs.writeFileSync(path.join(featureDir, 'task-01-first.md'), task('pending', '- none'));
910
+ fs.writeFileSync(path.join(featureDir, 'task-02-second.md'), task('pending', '- task-01-first.md'));
911
+ initFixtureGit(root);
912
+
913
+ let candidate = RESOLVER.resolveWorkflowCandidate({ projectRoot: root, explicitFeature: 'ordered' });
914
+ assert.equal(candidate.queueReady, true);
915
+ assert.deepEqual(Object.keys(candidate.taskRegistry), ['task-01-first.md', 'task-02-second.md']);
916
+ let runtime = PROVENANCE_HELPER.deriveRuntimeContext({
917
+ projectRoot: root, specsRoot: path.join(root, 'specs'), specFile: candidate.planFile,
918
+ featureName: 'ordered', runtimeSession: 'plan-native-test',
919
+ });
920
+ const preMutationHead = runtime.head;
921
+ fs.mkdirSync(path.join(root, 'src'), { recursive: true });
922
+ fs.writeFileSync(path.join(root, 'src', 'app.js'), 'later worktree mutation\n');
923
+ runtime = PROVENANCE_HELPER.deriveRuntimeContext({
924
+ projectRoot: root, specsRoot: path.join(root, 'specs'), specFile: candidate.planFile,
925
+ featureName: 'ordered', runtimeSession: 'plan-native-test',
926
+ });
927
+ assert.notEqual(runtime.head, preMutationHead, 'a later non-Spec mutation must stale the earlier Head');
928
+ const stableRuntime = PROVENANCE_HELPER.deriveRuntimeContext({
929
+ projectRoot: root, specsRoot: path.join(root, 'specs'), specFile: candidate.planFile,
930
+ featureName: 'ordered', runtimeSession: 'plan-native-test',
931
+ });
932
+ assert.equal(stableRuntime.head, runtime.head, 'unchanged consecutive captures must reach a fixed point');
933
+ let dependencies = RECEIPTS.workflowDependencyProofState(featureDir, candidate.taskRegistry, runtime, POLICY);
934
+ assert.equal(dependencies['task-01-first.md'].eligible, false);
935
+
936
+ const receipt = `Verification: PASS\nCommand: node --test\nExit: 0\nBase: ${runtime.base}\nHead: ${runtime.head}\n\n\`\`\`text\n1 test passed\n\`\`\``;
937
+ fs.writeFileSync(path.join(featureDir, 'task-01-first.md'), task('done', '- none', receipt));
938
+ candidate = RESOLVER.resolveWorkflowCandidate({ projectRoot: root, explicitFeature: 'ordered' });
939
+ runtime = PROVENANCE_HELPER.deriveRuntimeContext({
940
+ projectRoot: root, specsRoot: path.join(root, 'specs'), specFile: candidate.planFile,
941
+ featureName: 'ordered', runtimeSession: 'plan-native-test',
942
+ });
943
+ assert.deepEqual(RECEIPTS.checkWorkflowTaskReceipt(
944
+ featureDir, 'task-01-first.md', runtime, POLICY,
945
+ ).failures, []);
946
+ dependencies = RECEIPTS.workflowDependencyProofState(featureDir, candidate.taskRegistry, runtime, POLICY);
947
+ assert.equal(dependencies['task-01-first.md'].eligible, true);
948
+ assert.equal(candidate.taskRegistry['task-02-second.md'].dependencies[0], 'task-01-first.md');
949
+
950
+ fs.writeFileSync(path.join(featureDir, 'task-01-first.md'), task('in_progress', '- none'));
951
+ fs.writeFileSync(path.join(featureDir, 'task-02-second.md'), task('in_progress', '- task-01-first.md'));
952
+ candidate = RESOLVER.resolveWorkflowCandidate({ projectRoot: root, explicitFeature: 'ordered' });
953
+ assert.equal(Object.values(candidate.taskRegistry).filter(({ status }) => status === 'in_progress').length, 2);
954
+
955
+ fs.writeFileSync(path.join(featureDir, 'task-01-first.md'), task('paused', '- none'));
956
+ fs.writeFileSync(path.join(featureDir, 'task-02-second.md'), task('blocked', '- task-01-first.md'));
957
+ candidate = RESOLVER.resolveWorkflowCandidate({ projectRoot: root, explicitFeature: 'ordered' });
958
+ assert.equal(candidate.taskRegistry['task-01-first.md'].status, 'paused');
959
+ assert.equal(candidate.taskRegistry['task-02-second.md'].status, 'blocked');
960
+ } finally {
961
+ fs.rmSync(root, { recursive: true, force: true });
962
+ }
963
+ });
964
+
965
+ test('Claude installed Develop preserves plan-native execution and references', () => {
966
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-claude-develop-'));
967
+ try {
968
+ const result = installClaude(root);
969
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
970
+ const sourceRoot = path.dirname(DEVELOP);
971
+ const installedRoot = path.join(root, '.claude/skills/develop');
972
+ for (const relative of ['SKILL.md', ...DEVELOP_REFERENCES.map((name) => `references/${name}`)]) {
973
+ assert.equal(
974
+ fs.readFileSync(path.join(installedRoot, relative), 'utf8'),
975
+ fs.readFileSync(path.join(sourceRoot, relative), 'utf8'),
976
+ `Claude Develop projection drifted: ${relative}`,
977
+ );
978
+ }
979
+ assert.equal(fs.existsSync(path.join(root, '.agents/skills/develop')), false);
980
+ } finally {
981
+ fs.rmSync(root, { recursive: true, force: true });
982
+ }
983
+ });
984
+
985
+ test('Test process-first handoff preserves proof ownership and side-effect boundaries', () => {
986
+ const skill = read(TEST_SKILL);
987
+ const strategy = read(path.join(TEST_SOURCE_ROOT, 'references/execution-strategy.md'));
988
+ const runner = read(TEST_RUNNER);
989
+ const review = read(CODE_REVIEW_SKILL);
990
+ const developConsumers = [read(DEVELOP), read(GATE), read(path.join(
991
+ path.dirname(PARALLEL_WAVES), 'subagent-patterns.md',
992
+ ))].join('\n');
993
+
994
+ const exactKeys = [
995
+ 'schema_version', 'target', 'verdict', 'command', 'exit', 'counts',
996
+ 'provenance', 'proof_level', 'expected', 'observed', 'reachability',
997
+ 'artifacts', 'branches', 'raw_output', 'redactions', 'payload_sha256',
998
+ ];
999
+ for (const key of exactKeys) assert.match(strategy, new RegExp(`\\b${key}\\b`));
1000
+ const developHandoffFields = new Map([
1001
+ ['command', /Verification handoff separately contains the fresh command/i],
1002
+ ['exit', /fresh command, exit/i],
1003
+ ['branches', /named probes/i],
1004
+ ['counts', /counts, output/i],
1005
+ ['raw_output', /counts, output/i],
1006
+ ['provenance', /observed Head/i],
1007
+ ['reachability', /runtime reachability/i],
1008
+ ['proof_level', /required proof level/i],
1009
+ ]);
1010
+ for (const [field, sourceClause] of developHandoffFields) {
1011
+ assert.match(developConsumers, sourceClause, `Develop must currently consume ${field}`);
1012
+ assert.ok(exactKeys.includes(field), `test-proof-v1 must preserve Develop field ${field}`);
1013
+ }
1014
+ assert.match(runner, /Emit only `schema_version: "test-proof-v1"`/);
1015
+ assert.match(runner, /sole writer of[\s\S]*Status and inline Receipt/i);
1016
+ assert.match(review, /consume only the controller-validated[\s\S]*`test-proof-v1`[\s\S]*handoff/i);
1017
+ assert.match(review, /Never create or search for a separate[\s\S]*process-first receipt/i);
1018
+ assert.doesNotMatch([skill, strategy, runner].join('\n'), /npm\s+install|inject-auth\.js|--cookies\s|Bearer eyJ/);
1019
+
1020
+ const stable = (value) => {
1021
+ if (Array.isArray(value)) return `[${value.map(stable).join(',')}]`;
1022
+ if (value && typeof value === 'object') return `{${Object.keys(value).sort()
1023
+ .map((key) => `${JSON.stringify(key)}:${stable(value[key])}`).join(',')}}`;
1024
+ return JSON.stringify(value);
1025
+ };
1026
+ const proof = {
1027
+ schema_version: 'test-proof-v1',
1028
+ target: { feature: 'fixture', task_path: 'specs/fixture/task-01-proof.md' },
1029
+ verdict: 'PASS', command: 'node --test', exit: 0,
1030
+ counts: { executed: 1, passed: 1, failed: 0, skipped: 0 },
1031
+ provenance: { base: 'a'.repeat(40), head: 'b'.repeat(64) },
1032
+ proof_level: 'source', expected: 'named probe passes', observed: 'named probe passed',
1033
+ reachability: { status: 'PASS', evidence: ['test-runner -> controller'] },
1034
+ artifacts: [],
1035
+ branches: [{
1036
+ id: 'fixture named probe', required: true, verdict: 'PASS', command: 'node --test',
1037
+ exit: 0, counts: { executed: 1, passed: 1, failed: 0, skipped: 0 },
1038
+ proof_level: 'source',
1039
+ }],
1040
+ raw_output: '1 test passed', redactions: [],
1041
+ };
1042
+ proof.payload_sha256 = crypto.createHash('sha256').update(stable(proof)).digest('hex');
1043
+ assert.deepEqual(Object.keys(proof).sort(), exactKeys.slice().sort());
1044
+ assert.match(proof.payload_sha256, /^[a-f0-9]{64}$/);
1045
+ assert.equal(proof.counts.executed, proof.branches.reduce(
1046
+ (sum, branch) => sum + branch.counts.executed, 0,
1047
+ ));
1048
+
1049
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-test-proof-side-effects-'));
1050
+ const absentRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-test-proof-memory-absent-'));
1051
+ const externalTemp = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-test-owned-'));
1052
+ try {
1053
+ fs.mkdirSync(path.join(root, '.hapo'), { recursive: true });
1054
+ const memory = path.join(root, '.hapo/test-memory.json');
1055
+ fs.writeFileSync(memory, '{"known_issues":[]}\n');
1056
+ fs.writeFileSync(path.join(root, '.gitignore'), 'ignored-proof-state/\n');
1057
+ fs.writeFileSync(path.join(root, 'tracked.js'), 'before\n');
1058
+ initFixtureGit(root);
1059
+ const memoryBefore = crypto.createHash('sha256').update(fs.readFileSync(memory)).digest('hex');
1060
+ const command = spawnSync(process.execPath, ['-e', [
1061
+ "const fs=require('node:fs');",
1062
+ "fs.appendFileSync('tracked.js','after\\n');",
1063
+ "fs.writeFileSync('untracked-proof.txt','created by project command\\n');",
1064
+ "fs.mkdirSync('ignored-proof-state',{recursive:true});",
1065
+ "fs.writeFileSync('ignored-proof-state/output.txt','project output\\n');",
1066
+ "console.log('tests 1\\npass 1\\nfail 0\\nskipped 0');",
1067
+ ].join('')], { cwd: root, encoding: 'utf8' });
1068
+ assert.equal(command.status, 0, command.stderr);
1069
+ assert.match(command.stdout, /tests 1/);
1070
+ const drift = spawnSync('git', [
1071
+ '-C', root, 'status', '--porcelain=v1', '--ignored', '--untracked-files=all',
1072
+ ], { encoding: 'utf8' });
1073
+ assert.equal(drift.status, 0, drift.stderr);
1074
+ assert.match(drift.stdout, / M tracked\.js/);
1075
+ assert.match(drift.stdout, /\?\? untracked-proof\.txt/);
1076
+ assert.match(drift.stdout, /!! ignored-proof-state\//);
1077
+ const memoryAfter = crypto.createHash('sha256').update(fs.readFileSync(memory)).digest('hex');
1078
+ assert.equal(memoryAfter, memoryBefore, 'present Test memory must stay byte-identical');
1079
+ for (const relative of ['test-report.md', 'test-cache', 'test-auth.json']) {
1080
+ assert.equal(fs.existsSync(path.join(root, '.hapo', relative)), false, `${relative} must stay absent`);
1081
+ }
1082
+ assert.equal(fs.existsSync(path.join(root, 'node_modules')), false, 'project-local lazy install must stay absent');
1083
+
1084
+ const absentMemory = path.join(absentRoot, '.hapo/test-memory.json');
1085
+ assert.equal(fs.existsSync(absentMemory), false);
1086
+ const noop = spawnSync(process.execPath, ['-e', "console.log('tests 1; pass 1')"], {
1087
+ cwd: absentRoot, encoding: 'utf8',
1088
+ });
1089
+ assert.equal(noop.status, 0, noop.stderr);
1090
+ assert.equal(fs.existsSync(absentMemory), false, 'absent Test memory must stay absent');
1091
+
1092
+ fs.writeFileSync(path.join(externalTemp, 'ephemeral.txt'), 'proof temp\n');
1093
+ } finally {
1094
+ fs.rmSync(externalTemp, { recursive: true, force: true });
1095
+ fs.rmSync(absentRoot, { recursive: true, force: true });
1096
+ fs.rmSync(root, { recursive: true, force: true });
1097
+ }
1098
+ assert.equal(fs.existsSync(externalTemp), false, 'Test-owned external temp must be cleaned');
1099
+ });
1100
+
1101
+ test('Claude installed Test preserves plan-native proof and references', () => {
1102
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-claude-test-proof-'));
1103
+ try {
1104
+ const result = installClaude(root);
1105
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
1106
+ const installedRoot = path.join(root, '.claude/skills/test');
1107
+ for (const relative of ['SKILL.md', ...TEST_REFERENCES.map((name) => `references/${name}`)]) {
1108
+ assert.equal(
1109
+ fs.readFileSync(path.join(installedRoot, relative), 'utf8'),
1110
+ fs.readFileSync(path.join(TEST_SOURCE_ROOT, relative), 'utf8'),
1111
+ `Claude Test projection drifted: ${relative}`,
1112
+ );
1113
+ }
1114
+ const installedRunner = fs.readFileSync(path.join(root, '.claude/agents/test-runner.md'), 'utf8');
1115
+ assert.equal(installedRunner, fs.readFileSync(TEST_RUNNER, 'utf8'));
1116
+ assert.match(installedRunner, /test-proof-v1/);
1117
+ const installedReview = fs.readFileSync(path.join(root, '.claude/skills/code-review/SKILL.md'), 'utf8');
1118
+ assert.equal(installedReview, fs.readFileSync(CODE_REVIEW_SKILL, 'utf8'));
1119
+ assert.match(installedReview, /controller-validated `test-proof-v1`/);
1120
+ assert.match(installedReview, /Do not load or follow legacy separate-receipt paragraphs/i);
1121
+ assert.equal(fs.existsSync(path.join(root, '.agents/skills/test')), false);
1122
+ } finally {
1123
+ fs.rmSync(root, { recursive: true, force: true });
1124
+ }
1125
+ });
1126
+
1127
+ test('specs-usage-guide documents Test proof handoff without timing claims', () => {
1128
+ const guide = read(SPECS_USAGE_GUIDE);
1129
+ const section = guide.split('## Test proof handoff')[1]?.split('### Receipt canonical')[0] || '';
1130
+ assert.match(section, /`test-proof-v1`/);
1131
+ assert.match(section, /Test không ghi `Status:` hay inline[\s\S]*Develop controller là writer duy nhất/i);
1132
+ assert.match(section, /tracked\/untracked\/ignored drift/i);
1133
+ assert.match(section, /HTTPS\/localhost origin[\s\S]*fresh consent/i);
1134
+ assert.match(section, /separate-receipt adapter/i);
1135
+ assert.match(section, /không phải benchmark thời gian/i);
1136
+ assert.match(section, /không chứng minh live adherence/i);
1137
+ assert.doesNotMatch(section, /\b\d+(?:\.\d+)?\s*(?:ms|milliseconds?|seconds?|minutes?)\b/i);
1138
+ assert.doesNotMatch(section, /\[(?:LIVE_)?VERIFIED\]/i);
1139
+ });
1140
+
822
1141
  test('canonical verdict adapter handles completion and unfinished decisions', () => {
823
1142
  assert.deepEqual(POLICY.REVIEW_VERDICTS, ['PASS', 'PASS_WITH_WARNINGS', 'FAIL', 'BLOCKED']);
824
1143
  assert.doesNotThrow(() => POLICY.assertVerdict('PASS_WITH_WARNINGS'));
@@ -971,7 +1290,7 @@ test('auto authoring reaches readiness only after promotion, validation, groundi
971
1290
 
972
1291
  const specs = read(SPECS);
973
1292
  const executableBlocks = [...specs.matchAll(/```bash\n([\s\S]*?)```/g)].map((match) => match[1]);
974
- assert.ok(executableBlocks.every((block) => !/hapo:?develop/i.test(block)), 'Specs must never execute Develop');
1293
+ assert.ok(executableBlocks.every((block) => !/cf:?develop/i.test(block)), 'Specs must never execute Develop');
975
1294
  assert.match(specs, /An implementation workflow owns execution/i);
976
1295
  assert.match(specs, /selects one unblocked task[\s\S]*runs\s+the task's verification[\s\S]*inline `## Receipt`/i);
977
1296
  assert.match(specs, /The user decides\s+at C3 whether the feature is done/i);
@@ -1265,8 +1584,6 @@ test('first-run cache bypass is blocked - canonical receipt required even withou
1265
1584
  );
1266
1585
  // Clear cache to simulate fresh first-run with valid receipt
1267
1586
  try { fs.unlinkSync(path.join(claude, 'hooks', '.logs', 'spec-gate-last.json')); } catch {}
1268
- try { fs.unlinkSync(path.join(__dirname, '..', '.logs', 'spec-gate-last.json')); } catch {}
1269
- // Need to clear the cache that the hook uses (under claude hooks .logs) - we already cleared above
1270
1587
  // Re-run with valid receipt - should not block
1271
1588
  const result2 = spawnSync(process.execPath, [specGate], {
1272
1589
  cwd: root,
@@ -1277,7 +1594,6 @@ test('first-run cache bypass is blocked - canonical receipt required even withou
1277
1594
  assert.equal(result2.stdout, '', 'valid canonical receipt on first run must not block');
1278
1595
  } finally {
1279
1596
  fs.rmSync(root, { recursive: true, force: true });
1280
- try { fs.unlinkSync(path.join(PACKAGE_ROOT, 'src/claude/hooks/.logs/spec-gate-last.json')); } catch {}
1281
1597
  }
1282
1598
  });
1283
1599
 
@@ -1415,6 +1731,20 @@ test('runtime provenance derives exact Git evidence, CLI context, and stale/forg
1415
1731
  'valid-shaped arbitrary SHA values must not bind',
1416
1732
  );
1417
1733
 
1734
+ fs.writeFileSync(specFile, JSON.stringify({
1735
+ status: 'in_progress',
1736
+ feature_name: 'demo',
1737
+ task_registry: {},
1738
+ receipt_refresh: true,
1739
+ }));
1740
+ const specCommit = spawnSync('git', ['-C', root, 'add', 'specs/demo/spec.json'], { encoding: 'utf8' });
1741
+ assert.equal(specCommit.status, 0, specCommit.stderr);
1742
+ const commitReceipt = spawnSync('git', ['-C', root, 'commit', '-qm', 'refresh spec receipt'], { encoding: 'utf8' });
1743
+ assert.equal(commitReceipt.status, 0, commitReceipt.stderr);
1744
+ const afterSpecOnlyCommit = PROVENANCE_HELPER.deriveRuntimeContext(input);
1745
+ assert.equal(afterSpecOnlyCommit.base, initial.base, 'spec-only commits must not stale receipt Base');
1746
+ assert.equal(afterSpecOnlyCommit.head, initial.head, 'spec-only commits must not stale receipt Head');
1747
+
1418
1748
  const cli = spawnSync(process.execPath, [
1419
1749
  path.join(PACKAGE_ROOT, 'src/claude/scripts/provenance.cjs'), '--json',
1420
1750
  '--project-root', root, '--specs-root', specsRoot, '--spec-file', specFile,
@@ -1522,6 +1852,52 @@ test('runtime provenance derives exact Git evidence, CLI context, and stale/forg
1522
1852
  }
1523
1853
  });
1524
1854
 
1855
+ test('runtime provenance chooses a deterministic root when all history is Specs-only and multi-root', () => {
1856
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-provenance-multi-root-'));
1857
+ const specsRoot = path.join(root, 'specs');
1858
+ const featureRoot = path.join(specsRoot, 'demo');
1859
+ const specFile = path.join(featureRoot, 'spec.json');
1860
+ try {
1861
+ fs.mkdirSync(featureRoot, { recursive: true });
1862
+ fs.writeFileSync(specFile, JSON.stringify({ status: 'in_progress', feature_name: 'demo', task_registry: {} }));
1863
+ for (const args of [
1864
+ ['init', '-q', '-b', 'first-root'],
1865
+ ['config', 'user.email', 'cafekit@example.invalid'],
1866
+ ['config', 'user.name', 'CafeKit Test'],
1867
+ ['add', 'specs/demo/spec.json'],
1868
+ ['commit', '-qm', 'first specs root'],
1869
+ ['checkout', '--orphan', 'unrelated-specs'],
1870
+ ['rm', '-qrf', '.'],
1871
+ ]) {
1872
+ const result = spawnSync('git', ['-C', root, ...args], { encoding: 'utf8' });
1873
+ assert.equal(result.status, 0, result.stderr);
1874
+ }
1875
+ const unrelatedSpec = path.join(specsRoot, 'unrelated', 'spec.json');
1876
+ fs.mkdirSync(path.dirname(unrelatedSpec), { recursive: true });
1877
+ fs.writeFileSync(unrelatedSpec, JSON.stringify({ status: 'in_progress', feature_name: 'unrelated', task_registry: {} }));
1878
+ let result = spawnSync('git', ['-C', root, 'add', 'specs/unrelated/spec.json'], { encoding: 'utf8' });
1879
+ assert.equal(result.status, 0, result.stderr);
1880
+ result = spawnSync('git', ['-C', root, 'commit', '-qm', 'second specs root'], { encoding: 'utf8' });
1881
+ assert.equal(result.status, 0, result.stderr);
1882
+ result = spawnSync('git', ['-C', root, 'merge', '--allow-unrelated-histories', '-qm', 'merge specs roots', 'first-root'], { encoding: 'utf8' });
1883
+ assert.equal(result.status, 0, result.stderr);
1884
+
1885
+ const roots = spawnSync('git', ['-C', root, 'rev-list', '--max-parents=0', '--reverse', 'HEAD'], { encoding: 'utf8' });
1886
+ assert.equal(roots.status, 0, roots.stderr);
1887
+ const expectedBase = roots.stdout.trim().split('\n')[0].toLowerCase();
1888
+ const context = PROVENANCE_HELPER.deriveRuntimeContext({
1889
+ projectRoot: root,
1890
+ specsRoot,
1891
+ specFile,
1892
+ featureName: 'demo',
1893
+ runtimeSession: 'multi-root-session',
1894
+ });
1895
+ assert.equal(context.base, expectedBase);
1896
+ } finally {
1897
+ fs.rmSync(root, { recursive: true, force: true });
1898
+ }
1899
+ });
1900
+
1525
1901
  test('lane traces - Direct, Standard, explicit Strict classification, override, state mutation and completion', () => {
1526
1902
  // Direct: isolated reversible low-risk
1527
1903
  const direct = POLICY.classifyLane({ reversible: true, lowRisk: true, isolated: true, taskCount: 1 });
@@ -1595,26 +1971,27 @@ test('flash selective promotion - only specific task is promoted, not blanket',
1595
1971
  test('parallel waves require immutable provenance receipts and safe recovery', () => {
1596
1972
  const waves = markdownLegacyRegions(read(PARALLEL_WAVES)).primary;
1597
1973
  assert.match(waves, /only for explicit parallel execution/i);
1598
- assert.match(waves, /worktree isolation[\s\S]*fall back to the sequential loop/i);
1974
+ assert.match(waves, /isolated worktree support[\s\S]*fall back to the sequential loop/i);
1599
1975
  assert.match(waves, /Clamp `--parallel N` to 1\.\.5; default to 3/i);
1600
- assert.match(waves, /clean destination[\s\S]*base_sha[\s\S]*base compatibility/i);
1601
- assert.match(waves, /parallel consent[\s\S]*worker commits[\s\S]*controller cherry-picks/i);
1976
+ assert.match(waves, /clean destination[\s\S]*base_sha[\s\S]*incompatible bases/i);
1977
+ assert.match(waves, /consent for worker commits and controller cherry-picks/i);
1602
1978
  for (const field of ['base_sha', 'head_sha', 'branch', 'worktree_path', 'commit_range']) {
1603
1979
  assert.match(waves, new RegExp(`\\b${field}\\b`));
1604
1980
  }
1605
- assert.match(waves, /git diff(?: --stat)? [^\\n]*(?:BASE_SHA|base_sha)[^\\n]*(?:HEAD_SHA|head_sha)/i);
1606
- assert.match(waves, /new commit|new commits|Every fix is a new commit/i);
1607
- assert.match(waves, /plan task table[\s\S]*flat task's[\s\S]*Dependencies/i);
1981
+ assert.match(waves, /git rev-list --reverse <base_sha>\.\.<head_sha>/i);
1982
+ assert.match(waves, /git diff --name-status <base_sha>\.\.<head_sha>/i);
1983
+ assert.match(waves, /code\/spec repair[\s\S]*new commit/i);
1984
+ assert.match(waves, /dependencies from the plan table and each task/i);
1608
1985
  for (const sharedSurface of ['registry', 'lockfile', 'manifest', 'generated output', 'migration', 'export barrel']) {
1609
1986
  assert.match(waves, new RegExp(sharedSurface, 'i'));
1610
1987
  }
1611
1988
  assert.match(waves, /One file has one writer per wave/i);
1612
- assert.match(waves, /controller owns[\s\S]*state and evidence synchronization/i);
1613
- assert.match(waves, /authorization covers worker commits/i);
1989
+ assert.match(waves, /only the controller writes state\/proof/i);
1990
+ assert.match(waves, /commit only with user authorization/i);
1614
1991
  assert.match(waves, /blocked or failed worker keeps[\s\S]*worktree and branch/i);
1615
1992
  assert.match(waves, /Never[\s\S]*force-delete[\s\S]*merge success or explicit discard authorization/i);
1616
- assert.match(waves, /git cherry-pick <worker-commit>/i);
1617
- assert.match(waves, /On conflict, abort the cherry-pick[\s\S]*retain its[\s\S]*worktree\/branch/i);
1993
+ assert.match(waves, /git cherry-pick <next-worker-commit>/i);
1994
+ assert.match(waves, /On conflict, abort the pick[\s\S]*retain its branch\/worktree/i);
1618
1995
  assert.match(waves, /cleanup_authorization: merged-release/i);
1619
1996
  assert.match(waves, /affected integration|final.*integration/i);
1620
1997
  for (const category of ['baseline', 'environment', 'spec', 'code']) {
@@ -2305,7 +2682,7 @@ test('P0 receipt fail-closed: Exit 1/-1/abc/conflict/empty command rejected', ()
2305
2682
  });
2306
2683
 
2307
2684
  test('P0 flash marker-only must not promote or finalize', () => {
2308
- const flash = canonicalFlashTask({ blocker: 'awaiting /hapo:test' });
2685
+ const flash = canonicalFlashTask({ blocker: 'awaiting /cf:test' });
2309
2686
  const markerOnly = 'Verification: PASS';
2310
2687
  const promoted = POLICY.promoteFlashTask(flash, 'PASS', markerOnly);
2311
2688
  assert.equal(promoted.status, 'in_progress');
@@ -2388,7 +2765,7 @@ test('P0 sync-finalize derives promotion from current FLASH_UNVERIFIED plus expl
2388
2765
  assert.equal(forgedOpenResult.status, 'in_progress');
2389
2766
  assert.equal(forgedOpenResult.unblocks, false);
2390
2767
 
2391
- const current = canonicalFlashTask({ blocker: 'awaiting /hapo:test' });
2768
+ const current = canonicalFlashTask({ blocker: 'awaiting /cf:test' });
2392
2769
  const supported = spawnSync(process.execPath, [
2393
2770
  POLICY_PATH,
2394
2771
  '--sync-finalize',
@@ -2961,3 +3338,45 @@ test('P0 canonical phantom vectors - zero, cancellation, suite, FAIL, error, col
2961
3338
  assert.equal(POLICY.isTapMetadataHeading('# Notes'), false);
2962
3339
  assert.equal(POLICY.isTapMetadataHeading('# pass 1 explanation'), false);
2963
3340
  });
3341
+
3342
+ test('generated runtime state under every platform folder stays out of the worktree digest', () => {
3343
+ // The gate writes its cache and the installer writes runtime.json under the platform
3344
+ // folder on every run. If those writes moved Head, a project tracking its runtime
3345
+ // directory could never hold a stable receipt. The exclusion list must name each
3346
+ // shipped platform, and a real source change must still move Head.
3347
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-state-roots-'));
3348
+ try {
3349
+ for (const args of [['init', '-q'], ['config', 'user.email', 'cafekit@example.invalid'], ['config', 'user.name', 'CafeKit Test'], ['commit', '--allow-empty', '-qm', 'fixture']]) {
3350
+ const result = spawnSync('git', ['-C', root, ...args], { encoding: 'utf8' });
3351
+ assert.equal(result.status, 0, result.stderr);
3352
+ }
3353
+ fs.mkdirSync(path.join(root, 'specs', 'demo'), { recursive: true });
3354
+ fs.writeFileSync(path.join(root, 'specs', 'demo', 'plan.md'), '# Demo plan\n');
3355
+ const derive = () => PROVENANCE_HELPER.deriveRuntimeContext({
3356
+ projectRoot: root,
3357
+ specsRoot: path.join(root, 'specs'),
3358
+ specFile: path.join(root, 'specs', 'demo', 'plan.md'),
3359
+ featureName: 'demo',
3360
+ runtimeSession: 'state-roots',
3361
+ });
3362
+ const before = derive();
3363
+
3364
+ for (const folder of ['.claude', '.codex', '.omp']) {
3365
+ fs.mkdirSync(path.join(root, folder, 'hooks', '.logs'), { recursive: true });
3366
+ fs.mkdirSync(path.join(root, folder, '.logs'), { recursive: true });
3367
+ fs.writeFileSync(path.join(root, folder, 'hooks', '.logs', 'spec-gate-last.json'), '{"demo":{}}\n');
3368
+ fs.writeFileSync(path.join(root, folder, '.logs', 'hook-log.jsonl'), '{}\n');
3369
+ fs.writeFileSync(path.join(root, folder, 'runtime.json'), '{"spec":{"completion_gate":true}}\n');
3370
+ }
3371
+ const afterState = derive();
3372
+ assert.equal(afterState.head, before.head, 'runtime state under .claude, .codex, and .omp must not move Head');
3373
+ assert.equal(afterState.base, before.base);
3374
+
3375
+ // Anything else under the platform folder is source: a hook edit must move Head.
3376
+ fs.mkdirSync(path.join(root, '.omp', 'hooks'), { recursive: true });
3377
+ fs.writeFileSync(path.join(root, '.omp', 'hooks', 'rules.cjs'), '// edited\n');
3378
+ assert.notEqual(derive().head, before.head, 'a real file under the platform folder must still move Head');
3379
+ } finally {
3380
+ fs.rmSync(root, { recursive: true, force: true });
3381
+ }
3382
+ });