@haposoft/cafekit 0.16.0-rc.7 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/README.md +116 -27
  2. package/bin/__tests__/codex-hooks-ownership.test.js +239 -0
  3. package/bin/__tests__/codex-hooks.test.js +295 -0
  4. package/bin/__tests__/codex-native.test.js +2289 -34
  5. package/bin/__tests__/develop-contract.test.js +400 -23
  6. package/bin/__tests__/installer-safety.test.js +3 -3
  7. package/bin/__tests__/optional-skill-inventory.test.js +249 -0
  8. package/bin/__tests__/output-styles.test.js +214 -0
  9. package/bin/__tests__/package-inventory.test.js +2131 -16
  10. package/bin/__tests__/runtime-schema.test.js +111 -0
  11. package/bin/__tests__/secret-output-guardrail.test.js +122 -0
  12. package/bin/__tests__/skill-routing-source.test.js +172 -0
  13. package/bin/__tests__/specs-v2-execution-closeout.test.js +1 -1
  14. package/bin/install.js +10 -0
  15. package/bin/lib/codex-install.js +170 -5
  16. package/bin/lib/context.js +14 -0
  17. package/bin/lib/i18n.js +12 -6
  18. package/bin/lib/skill-deps.js +5 -5
  19. package/bin/phases/claude-runtime.js +23 -0
  20. package/bin/phases/claude-settings.js +8 -0
  21. package/bin/phases/codex-hooks.js +156 -0
  22. package/bin/phases/codex-runtime.js +29 -38
  23. package/bin/phases/copy-payload.js +8 -2
  24. package/bin/phases/select-skill-bundles.js +96 -0
  25. package/bin/phases/skill-inventory.js +71 -0
  26. package/bin/phases/skills-setup.js +9 -2
  27. package/bin/phases/write-metadata.js +8 -5
  28. package/package.json +8 -2
  29. package/src/claude/CLAUDE.md +3 -3
  30. package/src/claude/agents/brainstormer.md +52 -69
  31. package/src/claude/agents/code-auditor.md +32 -14
  32. package/src/claude/agents/debugger.md +35 -20
  33. package/src/claude/agents/deployer.md +12 -5
  34. package/src/claude/agents/docs-keeper.md +11 -1
  35. package/src/claude/agents/implementer.md +25 -13
  36. package/src/claude/agents/inspector.md +6 -3
  37. package/src/claude/agents/project-manager.md +1 -1
  38. package/src/claude/agents/researcher.md +26 -55
  39. package/src/claude/agents/spec-maker.md +24 -13
  40. package/src/claude/agents/strategist.md +52 -0
  41. package/src/claude/agents/test-runner.md +117 -132
  42. package/src/claude/agents/ui-ux-designer.md +1 -1
  43. package/src/claude/hooks/__tests__/fixtures/statusline-default.golden +1 -0
  44. package/src/claude/hooks/__tests__/precompact.test.js +158 -0
  45. package/src/claude/hooks/__tests__/spec-gate.test.js +87 -4
  46. package/src/claude/hooks/__tests__/state.test.js +254 -3
  47. package/src/claude/hooks/__tests__/statusline.test.js +196 -0
  48. package/src/claude/hooks/agent.cjs +1 -1
  49. package/src/claude/hooks/docs-sync.cjs +12 -10
  50. package/src/claude/hooks/inspect-block.cjs +1 -1
  51. package/src/claude/hooks/lib/config.cjs +3 -0
  52. package/src/claude/hooks/lib/hook-state-dir.cjs +39 -0
  53. package/src/claude/hooks/lib/secret-keywords.cjs +42 -0
  54. package/src/claude/hooks/precompact.cjs +105 -0
  55. package/src/claude/hooks/rules.cjs +1 -1
  56. package/src/claude/hooks/secret-output-guardrail.cjs +43 -0
  57. package/src/claude/hooks/session.cjs +27 -1
  58. package/src/claude/hooks/spec-gate.cjs +3 -3
  59. package/src/claude/hooks/spec-state.cjs +32 -5
  60. package/src/claude/hooks/state.cjs +1 -1
  61. package/src/claude/hooks/task-scaffold-guard.cjs +1 -1
  62. package/src/claude/hooks/usage.cjs +1 -1
  63. package/src/claude/migration-manifest.json +29 -15
  64. package/src/claude/output-styles/coding-level-0-eli5.md +103 -0
  65. package/src/claude/output-styles/coding-level-1-junior.md +124 -0
  66. package/src/claude/output-styles/coding-level-2-mid.md +146 -0
  67. package/src/claude/output-styles/coding-level-3-senior.md +148 -0
  68. package/src/claude/output-styles/coding-level-4-lead.md +159 -0
  69. package/src/claude/output-styles/coding-level-5-god.md +91 -0
  70. package/src/claude/references/debugger/core-philosophy.md +5 -3
  71. package/src/claude/references/debugger/frontend-verification.md +2 -2
  72. package/src/claude/references/debugger/log-ci-analysis.md +13 -1
  73. package/src/claude/references/debugger/parallel-agent-hydration.md +19 -34
  74. package/src/claude/references/debugger/performance-diagnostics.md +2 -2
  75. package/src/claude/references/debugger/root-cause-tracing.md +37 -6
  76. package/src/claude/references/debugger/side-effect-gate.md +3 -1
  77. package/src/claude/references/debugger/verification-protocol.md +13 -12
  78. package/src/claude/rules/orchestrator.md +7 -1
  79. package/src/claude/rules/process-management.md +39 -0
  80. package/src/claude/rules/review-audit-self-decision.md +48 -0
  81. package/src/claude/rules/skill-domain-routing.md +26 -24
  82. package/src/claude/rules/skill-workflow-routing.md +22 -14
  83. package/src/claude/rules/workflow.md +3 -3
  84. package/src/claude/runtime.json +1 -0
  85. package/src/claude/runtime.schema.json +205 -0
  86. package/src/claude/scripts/generate-skill-catalog.cjs +156 -122
  87. package/src/claude/scripts/provenance.cjs +18 -5
  88. package/src/claude/scripts/spec-receipt.cjs +76 -21
  89. package/src/claude/scripts/spec-resolver.cjs +144 -4
  90. package/src/claude/scripts/workflow-policy.cjs +1 -1
  91. package/src/claude/settings/settings.json +16 -0
  92. package/src/claude/skills/agent-browser/SKILL.md +1 -1
  93. package/src/claude/skills/ai-multimodal/SKILL.md +1 -1
  94. package/src/claude/skills/brainstorm/SKILL.md +193 -172
  95. package/src/claude/skills/brainstorm/references/question-framework.md +105 -167
  96. package/src/claude/skills/chrome-devtools/SKILL.md +1 -1
  97. package/src/claude/skills/code-review/SKILL.md +43 -25
  98. package/src/claude/skills/code-review/references/spec-compliance-review.md +3 -4
  99. package/src/claude/skills/code-review/references/verification-gate.md +1 -1
  100. package/src/claude/skills/debug/SKILL.md +48 -19
  101. package/src/claude/skills/delegate/SKILL.md +1 -1
  102. package/src/claude/skills/develop/SKILL.md +92 -65
  103. package/src/claude/skills/develop/references/parallel-waves.md +36 -63
  104. package/src/claude/skills/develop/references/quality-gate.md +31 -4
  105. package/src/claude/skills/develop/references/subagent-patterns.md +21 -7
  106. package/src/claude/skills/docs/SKILL.md +42 -23
  107. package/src/claude/skills/docs/references/init-workflow.md +4 -4
  108. package/src/claude/skills/docs/references/reconstruct-workflow.md +6 -6
  109. package/src/claude/skills/docs/references/standard-docs-workflow.md +6 -2
  110. package/src/claude/skills/docs/references/summarize-workflow.md +2 -2
  111. package/src/claude/skills/docs/references/update-workflow.md +9 -7
  112. package/src/claude/skills/docs/templates/requirements-as-is.md +1 -1
  113. package/src/claude/skills/docx/SKILL.md +1 -1
  114. package/src/claude/skills/git/SKILL.md +1 -1
  115. package/src/claude/skills/git/references/commit-protocols.md +1 -1
  116. package/src/claude/skills/git/references/finish-branch.md +1 -1
  117. package/src/claude/skills/hotfix/SKILL.md +159 -170
  118. package/src/claude/skills/hotfix/references/diagnosis-protocol.md +14 -9
  119. package/src/claude/skills/hotfix/references/parallel-patterns.md +27 -13
  120. package/src/claude/skills/hotfix/references/prevention-gate.md +5 -0
  121. package/src/claude/skills/hotfix/references/review-cycle.md +21 -15
  122. package/src/claude/skills/hotfix/references/workflow-specialized.md +53 -21
  123. package/src/claude/skills/inspect/SKILL.md +55 -46
  124. package/src/claude/skills/inspect/references/internal-inspection.md +21 -5
  125. package/src/claude/skills/loop/SKILL.md +114 -0
  126. package/src/claude/skills/loop/references/bounded-loop-protocol.md +104 -0
  127. package/src/claude/skills/loop/references/metric-and-guard-contract.md +76 -0
  128. package/src/claude/skills/pdf/SKILL.md +1 -1
  129. package/src/claude/skills/pptx/SKILL.md +1 -1
  130. package/src/claude/skills/question/SKILL.md +16 -13
  131. package/src/claude/skills/research/SKILL.md +70 -43
  132. package/src/claude/skills/route/SKILL.md +94 -0
  133. package/src/claude/skills/route/references/agent-timing.md +46 -0
  134. package/src/claude/skills/route/references/chaining-patterns.md +53 -0
  135. package/src/claude/skills/route/references/task-taxonomy.md +48 -0
  136. package/src/claude/skills/specs/SKILL.md +48 -57
  137. package/src/claude/skills/specs/references/review.md +26 -8
  138. package/src/claude/skills/specs/references/templates.md +76 -31
  139. package/src/claude/skills/specs/templates/requirements-init.md +1 -1
  140. package/src/claude/skills/specs/templates/research.md +2 -2
  141. package/src/claude/skills/sync/SKILL.md +6 -6
  142. package/src/claude/skills/sync/references/sync-protocols.md +1 -1
  143. package/src/claude/skills/test/SKILL.md +120 -120
  144. package/src/claude/skills/test/references/execution-strategy.md +208 -361
  145. package/src/claude/skills/test/references/failure-triage.md +55 -121
  146. package/src/claude/skills/test/references/test-memory.md +20 -34
  147. package/src/claude/skills/ui-ux-pro-max/SKILL.md +1 -1
  148. package/src/claude/skills/web-testing/SKILL.md +1 -1
  149. package/src/claude/skills/xlsx/SKILL.md +1 -1
  150. package/src/claude/status.cjs +135 -33
  151. package/src/codex/AGENTS.md +14 -4
  152. package/src/codex/hooks/docs-sync.cjs +8 -3
  153. package/src/codex/hooks/lib/hook-context.cjs +18 -4
  154. package/src/codex/hooks/rules.cjs +29 -1
  155. package/src/codex/hooks/secret-output-guardrail.cjs +30 -0
  156. package/src/codex/hooks/spec-gate.cjs +3 -8
  157. package/src/codex/hooks/spec-state.cjs +34 -5
  158. package/src/codex/hooks.json +5 -0
  159. package/src/codex/runtime.json +2 -0
  160. package/src/common/.env.example +1 -1
  161. package/src/claude/skills/backend-development/SKILL.md +0 -101
  162. package/src/claude/skills/backend-development/references/backend-api-design.md +0 -495
  163. package/src/claude/skills/backend-development/references/backend-architecture.md +0 -454
  164. package/src/claude/skills/backend-development/references/backend-authentication.md +0 -338
  165. package/src/claude/skills/backend-development/references/backend-code-quality.md +0 -659
  166. package/src/claude/skills/backend-development/references/backend-debugging.md +0 -904
  167. package/src/claude/skills/backend-development/references/backend-devops.md +0 -491
  168. package/src/claude/skills/backend-development/references/backend-mindset.md +0 -387
  169. package/src/claude/skills/backend-development/references/backend-performance.md +0 -397
  170. package/src/claude/skills/backend-development/references/backend-security.md +0 -342
  171. package/src/claude/skills/backend-development/references/backend-technologies.md +0 -256
  172. package/src/claude/skills/backend-development/references/backend-testing.md +0 -429
  173. package/src/claude/skills/devops/.env.example +0 -76
  174. package/src/claude/skills/devops/SKILL.md +0 -102
  175. package/src/claude/skills/devops/references/browser-rendering.md +0 -305
  176. package/src/claude/skills/devops/references/cloudflare-d1-kv.md +0 -123
  177. package/src/claude/skills/devops/references/cloudflare-platform.md +0 -271
  178. package/src/claude/skills/devops/references/cloudflare-r2-storage.md +0 -280
  179. package/src/claude/skills/devops/references/cloudflare-workers-advanced.md +0 -312
  180. package/src/claude/skills/devops/references/cloudflare-workers-apis.md +0 -309
  181. package/src/claude/skills/devops/references/cloudflare-workers-basics.md +0 -418
  182. package/src/claude/skills/devops/references/docker-basics.md +0 -297
  183. package/src/claude/skills/devops/references/docker-compose.md +0 -292
  184. package/src/claude/skills/devops/references/gcloud-platform.md +0 -297
  185. package/src/claude/skills/devops/references/gcloud-services.md +0 -304
  186. package/src/claude/skills/devops/references/kubernetes-basics.md +0 -95
  187. package/src/claude/skills/devops/references/kubernetes-helm-advanced.md +0 -70
  188. package/src/claude/skills/devops/references/kubernetes-helm.md +0 -81
  189. package/src/claude/skills/devops/references/kubernetes-kubectl.md +0 -74
  190. package/src/claude/skills/devops/references/kubernetes-security-advanced.md +0 -94
  191. package/src/claude/skills/devops/references/kubernetes-security.md +0 -89
  192. package/src/claude/skills/devops/references/kubernetes-troubleshooting-advanced.md +0 -74
  193. package/src/claude/skills/devops/references/kubernetes-troubleshooting.md +0 -49
  194. package/src/claude/skills/devops/references/kubernetes-workflows-advanced.md +0 -75
  195. package/src/claude/skills/devops/references/kubernetes-workflows.md +0 -76
  196. package/src/claude/skills/devops/scripts/cloudflare_deploy.py +0 -269
  197. package/src/claude/skills/devops/scripts/docker_optimize.py +0 -332
  198. package/src/claude/skills/devops/scripts/requirements.txt +0 -20
  199. package/src/claude/skills/devops/scripts/tests/requirements.txt +0 -3
  200. package/src/claude/skills/devops/scripts/tests/test_cloudflare_deploy.py +0 -285
  201. package/src/claude/skills/devops/scripts/tests/test_docker_optimize.py +0 -436
  202. package/src/claude/skills/frontend-design/SKILL.md +0 -135
  203. package/src/claude/skills/frontend-design/references/ai-multimodal-overview.md +0 -165
  204. package/src/claude/skills/frontend-design/references/analysis-best-practices.md +0 -80
  205. package/src/claude/skills/frontend-design/references/analysis-prompts.md +0 -141
  206. package/src/claude/skills/frontend-design/references/analysis-techniques.md +0 -118
  207. package/src/claude/skills/frontend-design/references/animejs.md +0 -396
  208. package/src/claude/skills/frontend-design/references/anti-slop-rules.md +0 -103
  209. package/src/claude/skills/frontend-design/references/asset-generation.md +0 -337
  210. package/src/claude/skills/frontend-design/references/bento-motion-engine.md +0 -142
  211. package/src/claude/skills/frontend-design/references/design-extraction-overview.md +0 -71
  212. package/src/claude/skills/frontend-design/references/extraction-best-practices.md +0 -141
  213. package/src/claude/skills/frontend-design/references/extraction-output-templates.md +0 -162
  214. package/src/claude/skills/frontend-design/references/extraction-prompts.md +0 -127
  215. package/src/claude/skills/frontend-design/references/magicui-components.md +0 -129
  216. package/src/claude/skills/frontend-design/references/performance-guardrails.md +0 -169
  217. package/src/claude/skills/frontend-design/references/premium-design-patterns.md +0 -93
  218. package/src/claude/skills/frontend-design/references/redesign-audit-checklist.md +0 -114
  219. package/src/claude/skills/frontend-design/references/technical-accessibility.md +0 -119
  220. package/src/claude/skills/frontend-design/references/technical-best-practices.md +0 -97
  221. package/src/claude/skills/frontend-design/references/technical-optimization.md +0 -44
  222. package/src/claude/skills/frontend-design/references/technical-overview.md +0 -90
  223. package/src/claude/skills/frontend-design/references/technical-workflows.md +0 -150
  224. package/src/claude/skills/frontend-design/references/visual-analysis-overview.md +0 -95
  225. package/src/claude/skills/frontend-design/references/workflow-3d.md +0 -102
  226. package/src/claude/skills/frontend-design/references/workflow-describe.md +0 -87
  227. package/src/claude/skills/frontend-design/references/workflow-immersive.md +0 -87
  228. package/src/claude/skills/frontend-design/references/workflow-quick.md +0 -57
  229. package/src/claude/skills/frontend-design/references/workflow-screenshot.md +0 -63
  230. package/src/claude/skills/frontend-design/references/workflow-video.md +0 -74
  231. package/src/claude/skills/frontend-development/SKILL.md +0 -406
  232. package/src/claude/skills/frontend-development/resources/common-patterns.md +0 -331
  233. package/src/claude/skills/frontend-development/resources/complete-examples.md +0 -872
  234. package/src/claude/skills/frontend-development/resources/component-patterns.md +0 -502
  235. package/src/claude/skills/frontend-development/resources/data-fetching.md +0 -767
  236. package/src/claude/skills/frontend-development/resources/file-organization.md +0 -502
  237. package/src/claude/skills/frontend-development/resources/loading-and-error-states.md +0 -501
  238. package/src/claude/skills/frontend-development/resources/performance.md +0 -406
  239. package/src/claude/skills/frontend-development/resources/routing-guide.md +0 -364
  240. package/src/claude/skills/frontend-development/resources/styling-guide.md +0 -428
  241. package/src/claude/skills/frontend-development/resources/typescript-standards.md +0 -418
  242. package/src/claude/skills/mobile-development/SKILL.md +0 -218
  243. package/src/claude/skills/mobile-development/references/mobile-android.md +0 -604
  244. package/src/claude/skills/mobile-development/references/mobile-best-practices.md +0 -544
  245. package/src/claude/skills/mobile-development/references/mobile-debugging.md +0 -1089
  246. package/src/claude/skills/mobile-development/references/mobile-frameworks.md +0 -465
  247. package/src/claude/skills/mobile-development/references/mobile-ios.md +0 -496
  248. package/src/claude/skills/mobile-development/references/mobile-mindset.md +0 -542
  249. package/src/claude/skills/react-best-practices/AGENTS.md +0 -2249
  250. package/src/claude/skills/react-best-practices/README.md +0 -123
  251. package/src/claude/skills/react-best-practices/SKILL.md +0 -128
  252. package/src/claude/skills/react-best-practices/metadata.json +0 -15
  253. package/src/claude/skills/react-best-practices/rules/_sections.md +0 -46
  254. package/src/claude/skills/react-best-practices/rules/_template.md +0 -28
  255. package/src/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +0 -55
  256. package/src/claude/skills/react-best-practices/rules/advanced-use-latest.md +0 -49
  257. package/src/claude/skills/react-best-practices/rules/async-api-routes.md +0 -38
  258. package/src/claude/skills/react-best-practices/rules/async-defer-await.md +0 -80
  259. package/src/claude/skills/react-best-practices/rules/async-dependencies.md +0 -36
  260. package/src/claude/skills/react-best-practices/rules/async-parallel.md +0 -28
  261. package/src/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +0 -99
  262. package/src/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +0 -59
  263. package/src/claude/skills/react-best-practices/rules/bundle-conditional.md +0 -31
  264. package/src/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +0 -49
  265. package/src/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +0 -35
  266. package/src/claude/skills/react-best-practices/rules/bundle-preload.md +0 -50
  267. package/src/claude/skills/react-best-practices/rules/client-event-listeners.md +0 -74
  268. package/src/claude/skills/react-best-practices/rules/client-swr-dedup.md +0 -56
  269. package/src/claude/skills/react-best-practices/rules/js-batch-dom-css.md +0 -82
  270. package/src/claude/skills/react-best-practices/rules/js-cache-function-results.md +0 -80
  271. package/src/claude/skills/react-best-practices/rules/js-cache-property-access.md +0 -28
  272. package/src/claude/skills/react-best-practices/rules/js-cache-storage.md +0 -70
  273. package/src/claude/skills/react-best-practices/rules/js-combine-iterations.md +0 -32
  274. package/src/claude/skills/react-best-practices/rules/js-early-exit.md +0 -50
  275. package/src/claude/skills/react-best-practices/rules/js-hoist-regexp.md +0 -45
  276. package/src/claude/skills/react-best-practices/rules/js-index-maps.md +0 -37
  277. package/src/claude/skills/react-best-practices/rules/js-length-check-first.md +0 -49
  278. package/src/claude/skills/react-best-practices/rules/js-min-max-loop.md +0 -82
  279. package/src/claude/skills/react-best-practices/rules/js-set-map-lookups.md +0 -24
  280. package/src/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +0 -57
  281. package/src/claude/skills/react-best-practices/rules/rendering-activity.md +0 -26
  282. package/src/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
  283. package/src/claude/skills/react-best-practices/rules/rendering-conditional-render.md +0 -40
  284. package/src/claude/skills/react-best-practices/rules/rendering-content-visibility.md +0 -38
  285. package/src/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +0 -46
  286. package/src/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
  287. package/src/claude/skills/react-best-practices/rules/rendering-svg-precision.md +0 -28
  288. package/src/claude/skills/react-best-practices/rules/rerender-defer-reads.md +0 -39
  289. package/src/claude/skills/react-best-practices/rules/rerender-dependencies.md +0 -45
  290. package/src/claude/skills/react-best-practices/rules/rerender-derived-state.md +0 -29
  291. package/src/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +0 -74
  292. package/src/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +0 -58
  293. package/src/claude/skills/react-best-practices/rules/rerender-memo.md +0 -44
  294. package/src/claude/skills/react-best-practices/rules/rerender-transitions.md +0 -40
  295. package/src/claude/skills/react-best-practices/rules/server-after-nonblocking.md +0 -73
  296. package/src/claude/skills/react-best-practices/rules/server-cache-lru.md +0 -41
  297. package/src/claude/skills/react-best-practices/rules/server-cache-react.md +0 -26
  298. package/src/claude/skills/react-best-practices/rules/server-parallel-fetching.md +0 -79
  299. 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']) {
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',
@@ -228,8 +228,8 @@ test('combined install keeps CORE neutral and records native shared-root trade-o
228
228
  [agents, CODEX_START]
229
229
  ]) assert.equal((content.match(new RegExp(marker, 'g')) || []).length, 1);
230
230
 
231
- assert.doesNotMatch(core, /Claude|Codex|\.claude|\.codex|\/hapo:|\$hapo-/i);
232
- assert.doesNotMatch(claudeBlock, /Codex|\.codex|\$hapo-/i);
231
+ assert.doesNotMatch(core, /Claude|Codex|\.claude|\.codex|\/cf:|\$cf-/i);
232
+ assert.doesNotMatch(claudeBlock, /Codex|\.codex|\$cf-/i);
233
233
  assert.match(codex, /native project instruction surface is root `AGENTS\.md`/);
234
234
  assert.match(agents, /shared-root trade-off is intentional/);
235
235
  // H5 remediation: ownership/ignore contract must be explicit — not just marker presence
@@ -241,7 +241,7 @@ test('combined install keeps CORE neutral and records native shared-root trade-o
241
241
  assert.doesNotMatch(codex, /If you are Codex CLI or any other runtime, ignore this entire Codex block/i);
242
242
  assert.match(codex, /fail-safe/i);
243
243
  // CORE must not contain runtime-specific directives that would leak
244
- assert.doesNotMatch(core, /\$hapo-|hapo:/i);
244
+ assert.doesNotMatch(core, /\$cf-|cf:/i);
245
245
  // No cross-runtime directive leakage: Claude block must not contain foreign runtime directives
246
246
  assert.doesNotMatch(claudeBlock, /<!-- CAFEKIT CODEX /);
247
247
  assert.doesNotMatch(codex, /<!-- CAFEKIT CORE /);