@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
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Native spec-driven workflow and runtime bundle for Claude Code and Codex CLI.
4
4
 
5
- [![Version](https://img.shields.io/badge/version-0.16.0-blue.svg)](https://github.com/haposoft/cafekit)
5
+ [![Version](https://img.shields.io/badge/version-0.16.1-blue.svg)](https://github.com/haposoft/cafekit)
6
6
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
7
7
  [![Claude%20Code](https://img.shields.io/badge/Claude%20Code-Native-orange.svg)](https://claude.ai/code)
8
8
  [![Codex%20CLI](https://img.shields.io/badge/Codex%20CLI-Native-111111.svg)](https://developers.openai.com/codex)
@@ -138,21 +138,97 @@ cat .claude/cafekit.json
138
138
  cat .codex/cafekit.json
139
139
  ```
140
140
 
141
+ ### Statusline configuration
142
+
143
+ The Claude statusline reads three keys from `.claude/runtime.json`:
144
+
145
+ - `"statusline"`: render mode — `full` (default), `compact`, `minimal`, or `none`.
146
+ - `statuslineColors`: set `false` to disable ANSI colors (`NO_COLOR` also wins).
147
+ - `statuslineLayout`: optional custom layout. Global shape
148
+ `{ "lines": [["model", "context"], ["directory", "git"]] }` — each inner
149
+ array is one output line; modes only slice the line count (minimal = first
150
+ line, compact = first two, full = all). Valid section ids: `model`,
151
+ `context`, `quota`, `directory`, `git`, `plan`, `cost`, `changes`. Unknown
152
+ ids are ignored; an empty or invalid layout falls back to the default
153
+ renderers, and leaving the key out keeps the default output unchanged. The
154
+ `cost` section renders only when enabled here, billing mode is API, and cost
155
+ data exists. Quota shows five-hour and weekly windows only while the usage
156
+ cache is fresh.
157
+
141
158
  ## Core Skills
142
159
 
143
160
  CafeKit ships many skills, but the main release surface is:
144
161
 
145
- - `/hapo:question <question> [--repo|--web|--both|--brief|--deep]`: answer questions using repo evidence first, then external/current sources when local evidence is insufficient
146
- - `/hapo:brainstorm <idea-or-problem>`: scout the repo, clarify exact requirements, compare approaches, and hand off to specs
147
- - `/hapo:specs <feature-description>`: create or resume a structured spec workflow
148
- - `/hapo:develop <feature-name>`: implement from approved spec artifacts
149
- - `/hapo:debug <issue>`: diagnose bugs, incidents, CI failures, flaky tests, UI regressions, and performance issues before fixing
150
- - `/hapo:hotfix <issue>`: fix diagnosed bugs with root-cause, verification, prevention, and side-effect gates
151
- - `/hapo:docs [--init|--update|--summarize|--reconstruct]`: create project docs or reconstruct as-is system documentation from source code
152
- - `/hapo:test [scope|--full]`: run verification and return a structured verdict
153
- - `/hapo:code-review [scope|--pending]`: adversarial review focused on correctness, regressions, and security
162
+ - `/cf:ask <question> [--repo|--web|--both|--brief|--deep]`: answer questions using repo evidence first, then external/current sources when local evidence is insufficient
163
+ - `/cf:scout <search-target>`: discover relevant files locally first, delegating only for permitted broad independent scopes
164
+ - `/cf:brainstorm <idea-or-problem>`: scout the repo, clarify exact requirements, compare approaches, and hand off to specs
165
+ - `/cf:research <decision>`: choose proportional depth and return traceable evidence for an uncertain technical decision
166
+ - `/cf:route <material-request>`: classify ambiguous, multi-step, multi-domain, or elevated-risk work and compose the shortest valid installed chain
167
+ - `/cf:loop <bounded-experiment>`: run explicit-only numeric optimization in an isolated worktree and return a base-bound patch handoff
168
+ - `/cf:specs <feature-description>`: create or resume a structured spec workflow
169
+ - `/cf:develop <feature-name>`: implement from approved spec artifacts
170
+ - `/cf:debug <issue>`: run adaptive-depth, diagnostic-only root-cause analysis with elimination and prevention-aware handoff
171
+ - `/cf:fix <issue>`: repair the diagnosed root cause with proportional depth; Quick/local stays direct, while Standard and Incident/deep bound the repair and use post-diagnosis research, brainstorm, or staged planning only when evidence leaves a real decision; every path consumes the `cf:debug` handoff and reports shared `PASS | PASS_WITH_WARNINGS | FAIL | BLOCKED` verdicts through verification, prevention, and side-effect gates
172
+ - `/cf:test [scope|--full]`: run verification and return a structured verdict
173
+ - `/cf:code-review [scope|--pending]`: adversarial review focused on correctness, regressions, and security
174
+
175
+ ### Adaptive Brainstorm controls
176
+
177
+ Brainstorm stays proportional by default: Direct classification runs before any
178
+ analysis overlay, then non-direct work selects the smallest adequate Standard or
179
+ Deep depth. Controls are parsed only from the leading control segment. Combine
180
+ the single-use exact flags `--deep`, `--visual`, and `--advice` in any order;
181
+ an unknown or duplicate leading `--*` stops with usage and no action. `--` ends
182
+ controls, so literal flag content can start with `/cf:brainstorm -- --dry-run`.
183
+
184
+ `--deep` raises non-direct analysis depth only. `--visual` changes presentation
185
+ and falls back to text. `--advice` uses the advisory brainstormer only after a
186
+ material choice exists. External visual/adviser context is minimized and
187
+ redacted first; neither overlay writes, approves, persists, dispatches, or
188
+ completes work. Chat is the default output. A durable file needs explicit user
189
+ authority, and no Brainstorm output is live proof or Specs/Develop approval or
190
+ execution authority.
191
+
192
+ ### Adaptive Research and bounded Loop
193
+
194
+ Use `/cf:research` when the result is a decision: Quick, Standard, or Deep
195
+ research binds material claims to a URL or repository anchor, authority,
196
+ date/version, applicability, and `confirmed`, `inferred`, or `unresolved`
197
+ state. Research returns evidence and tradeoffs; it does not implement the
198
+ recommendation or guarantee correctness.
199
+
200
+ Use `/cf:loop` only by explicit request and only after its preflight freezes
201
+ Goal, isolated Scope, finite numeric Metric and Direction, reproducible
202
+ Baseline, distinct Guard, noise policy and minimum delta, budget, and stop
203
+ conditions. Each iteration stays in a detached worktree. The result is a
204
+ base-bound isolated patch handoff, never an automatic apply, commit, push, or
205
+ guarantee of improvement.
206
+
207
+ ```text
208
+ /cf:research Compare current local-first search libraries for this repository
209
+ /cf:loop Goal="reduce parser latency" Scope="packages/parser" Metric="median ms, lower" Baseline="pinned base" Guard="npm test" Noise="MAD; minimum delta 2%" Budget="10 iterations" Stop="budget, drift, or failed guard"
210
+ $cf-research Compare current local-first search libraries for this repository
211
+ $cf-loop Goal="reduce parser latency" Scope="packages/parser" Metric="median ms, lower" Baseline="pinned base" Guard="npm test" Noise="MAD; minimum delta 2%" Budget="10 iterations" Stop="budget, drift, or failed guard"
212
+ ```
213
+
214
+ Optional document skills are `docs`, `docx`, `pdf`, `pptx`, `xlsx`, and
215
+ `ai-multimodal`. Select them interactively or run:
216
+
217
+ ```bash
218
+ npx @haposoft/cafekit --with-document-skills
219
+ ```
220
+
221
+ Fresh non-interactive installs leave them out by default. Use
222
+ `--without-document-skills` to skip them or remove pristine CafeKit-owned
223
+ copies; user-modified files are preserved. Documentation validators, hooks,
224
+ and the `docs-keeper` agent remain part of the core runtime.
154
225
 
155
- Common companion skills bundled in this package include `inspect`, `research`, `ai-multimodal`, `frontend-development`, `backend-development`, and `react-best-practices`.
226
+ When installed, `cf:docs` consumes a post-task docs checkpoint from
227
+ Develop/Sync (`none | minor | major` — `none` reports only, `minor`/`major`
228
+ update only affected existing docs, and a checkpoint never invents a new
229
+ document), dispatches `docs-keeper` or parallel readers only through the
230
+ Delegation Gate, and keeps every reconstructed claim on the
231
+ `Observed | Inferred | Unknown` evidence taxonomy.
156
232
 
157
233
  CafeKit uses rule-based skill routing guidance instead of an automatic prompt-scoring hook. See `.claude/rules/skill-workflow-routing.md`, `.claude/rules/skill-domain-routing.md`, or run:
158
234
 
@@ -160,7 +236,16 @@ CafeKit uses rule-based skill routing guidance instead of an automatic prompt-sc
160
236
  node .claude/scripts/generate-skill-catalog.cjs --skills
161
237
  ```
162
238
 
163
- On Codex, invoke the transformed native skill names as `$hapo-<name>` or browse
239
+ Routing uses progressive disclosure: live catalog metadata selects a candidate,
240
+ the selected `SKILL.md` supplies its contract, and only relevant references are
241
+ loaded. Invoke an installed skill directly for explicit or obvious low-risk
242
+ work; use `cf:route` only when classification or chaining is material. When a
243
+ capability or agent is absent, continue inline when safe or name the gap.
244
+ Hooks are not a skill router and never grant capability presence or authority. Source
245
+ and installed projections are verified; live-model adherence remains
246
+ `UNPROVEN`, not deterministic.
247
+
248
+ On Codex, invoke the transformed native skill names as `$cf-<name>` or browse
164
249
  them with `/skills`. Explicit custom-agent delegation uses snake_case agent
165
250
  names and `fork_turns: "none"`.
166
251
 
@@ -169,32 +254,36 @@ names and `fork_turns: "none"`.
169
254
  Claude Code:
170
255
 
171
256
  ```bash
172
- /hapo:question "Which files define the current CafeKit install/runtime behavior?" --repo
173
- /hapo:brainstorm Explore approaches for a Google Meet transcript extension
174
- /hapo:specs Build a Google Meet transcript extension with AI summaries
175
- /hapo:develop meet-transcript-mvp
176
- /hapo:test meet-transcript-mvp --full
177
- /hapo:code-review meet-transcript-mvp --pending
257
+ /cf:ask "Which files define the current CafeKit install/runtime behavior?" --repo
258
+ /cf:scout "Find the runtime entrypoints for skill installation"
259
+ /cf:brainstorm Explore approaches for a Google Meet transcript extension
260
+ /cf:research Compare current transcript storage options for this repository
261
+ /cf:specs Build a Google Meet transcript extension with AI summaries
262
+ /cf:develop meet-transcript-mvp
263
+ /cf:test meet-transcript-mvp --full
264
+ /cf:code-review meet-transcript-mvp --pending
178
265
  ```
179
266
 
180
267
  Codex CLI:
181
268
 
182
269
  ```text
183
- $hapo-question "Which files define the current CafeKit runtime?" --repo
184
- $hapo-brainstorm Explore approaches for a Google Meet transcript extension
185
- $hapo-specs Build a Google Meet transcript extension with AI summaries
186
- $hapo-develop meet-transcript-mvp
187
- $hapo-test meet-transcript-mvp --full
188
- $hapo-code-review meet-transcript-mvp --pending
270
+ $cf-ask "Which files define the current CafeKit runtime?" --repo
271
+ $cf-scout "Find the runtime entrypoints for skill installation"
272
+ $cf-brainstorm Explore approaches for a Google Meet transcript extension
273
+ $cf-research Compare current transcript storage options for this repository
274
+ $cf-specs Build a Google Meet transcript extension with AI summaries
275
+ $cf-develop meet-transcript-mvp
276
+ $cf-test meet-transcript-mvp --full
277
+ $cf-code-review meet-transcript-mvp --pending
189
278
  ```
190
279
 
191
280
  Reconstruct as-is docs from a legacy codebase:
192
281
 
193
282
  ```bash
194
- /hapo:docs --reconstruct apps/legacy-admin
283
+ /cf:docs --reconstruct apps/legacy-admin
195
284
  ```
196
285
 
197
- The reconstruct bundle includes as-is markdown/JSON evidence and a self-contained `overview.html` review dashboard before the approved docs are handed to `/hapo:specs`.
286
+ The reconstruct bundle includes as-is markdown/JSON evidence and a self-contained `overview.html` review dashboard before the approved docs are handed to `/cf:specs`.
198
287
 
199
288
  ## Codex hook safety
200
289
 
@@ -215,9 +304,9 @@ specs/<feature-name>/
215
304
  └── task-NN-<slug>.md
216
305
  ```
217
306
 
218
- The process-first workflow has three human decision gates. `hapo:specs` opens
307
+ The process-first workflow has three human decision gates. `cf:specs` opens
219
308
  C1 to fix scope before authoring and C2 to resolve adversarial findings, then
220
- stops. A later explicit `hapo:develop` invocation executes the tasks and
309
+ stops. A later explicit `cf:develop` invocation executes the tasks and
221
310
  presents C3 for closeout only after real execution proof. Each task has one
222
311
  `Status:` field and keeps its canonical `## Receipt` inline, including the
223
312
  exact Verification Plan command, `Exit: 0`, `Verification: PASS`,
@@ -0,0 +1,239 @@
1
+ 'use strict';
2
+
3
+ // `.codex/hooks.json` is a shared configuration file, not a CafeKit payload. It used to be
4
+ // copied verbatim, so a single user-added hook turned the whole file into a user-modified
5
+ // artifact: a normal install then preserved CafeKit's stale hooks, and `--force-overwrite`
6
+ // destroyed the user's. These tests pin the merge that replaced that copy, mirroring how
7
+ // `.claude/settings.json` has always been handled.
8
+
9
+ const assert = require('node:assert/strict');
10
+ const fs = require('node:fs');
11
+ const os = require('node:os');
12
+ const path = require('node:path');
13
+ const { spawnSync } = require('node:child_process');
14
+ const { test } = require('node:test');
15
+
16
+ const PACKAGE_ROOT = path.join(__dirname, '../..');
17
+ const INSTALLER = path.join(PACKAGE_ROOT, 'bin/install.js');
18
+ const TEMPLATE = path.join(PACKAGE_ROOT, 'src/codex/hooks.json');
19
+ const MANIFEST = require(path.join(PACKAGE_ROOT, 'src/claude/migration-manifest.json'));
20
+ const { hookScript } = require(path.join(PACKAGE_ROOT, 'bin/phases/codex-hooks.js'));
21
+
22
+ const USER_STOP_COMMAND = 'node "$(git rev-parse --show-toplevel)/scripts/my-own-stop.js"';
23
+ const USER_PRETOOL_COMMAND = 'echo user-owned-pretooluse';
24
+
25
+ function inTempProject(run) {
26
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-codex-hooks-own-'));
27
+ try {
28
+ return run(root);
29
+ } finally {
30
+ fs.rmSync(root, { recursive: true, force: true });
31
+ }
32
+ }
33
+
34
+ function install(root, extraArgs = []) {
35
+ return spawnSync(
36
+ process.execPath,
37
+ [INSTALLER, '--platform', 'codex', '--yes', ...extraArgs],
38
+ { cwd: root, encoding: 'utf8', env: { ...process.env, PATH: '/usr/bin:/bin' } }
39
+ );
40
+ }
41
+
42
+ function hooksPath(root) {
43
+ return path.join(root, '.codex', 'hooks.json');
44
+ }
45
+
46
+ function readHooks(root) {
47
+ return JSON.parse(fs.readFileSync(hooksPath(root), 'utf8'));
48
+ }
49
+
50
+ /** Every handler across every event, flattened. */
51
+ function allHandlers(config) {
52
+ return Object.values(config.hooks || {})
53
+ .flatMap((groups) => (Array.isArray(groups) ? groups : []))
54
+ .flatMap((group) => (Array.isArray(group?.hooks) ? group.hooks : []));
55
+ }
56
+
57
+ function allCommands(config) {
58
+ return allHandlers(config).map((handler) => handler?.command).filter(Boolean);
59
+ }
60
+
61
+ /** The CafeKit hook scripts the template registers, per event. */
62
+ function managedScripts() {
63
+ const template = JSON.parse(fs.readFileSync(TEMPLATE, 'utf8'));
64
+ const byEvent = {};
65
+ for (const [eventName, groups] of Object.entries(template.hooks || {})) {
66
+ byEvent[eventName] = groups
67
+ .flatMap((group) => group.hooks || [])
68
+ .map((handler) => hookScript(handler.command))
69
+ .filter(Boolean);
70
+ }
71
+ return byEvent;
72
+ }
73
+
74
+ /** Add two hooks CafeKit does not author: one under an event it owns, one under a new event. */
75
+ function addUserHooks(root) {
76
+ const config = readHooks(root);
77
+ config.hooks.Stop.push({ matcher: '*', hooks: [{ type: 'command', command: USER_STOP_COMMAND }] });
78
+ config.hooks.PreToolUse.push({ matcher: 'Bash', hooks: [{ type: 'command', command: USER_PRETOOL_COMMAND }] });
79
+ fs.writeFileSync(hooksPath(root), `${JSON.stringify(config, null, 2)}\n`);
80
+ }
81
+
82
+ test('a fresh install registers every CafeKit hook under its own event', () => {
83
+ inTempProject((root) => {
84
+ const result = install(root);
85
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
86
+
87
+ const installed = readHooks(root);
88
+ for (const [eventName, scripts] of Object.entries(managedScripts())) {
89
+ const registered = (installed.hooks[eventName] || [])
90
+ .flatMap((group) => group.hooks || [])
91
+ .map((handler) => hookScript(handler.command))
92
+ .filter(Boolean);
93
+ for (const script of scripts) {
94
+ assert.ok(registered.includes(script), `${script} missing from ${eventName}`);
95
+ }
96
+ }
97
+ });
98
+ });
99
+
100
+ test('a reinstall preserves user-added hooks instead of overwriting them', () => {
101
+ inTempProject((root) => {
102
+ assert.equal(install(root).status, 0);
103
+ addUserHooks(root);
104
+
105
+ const second = install(root);
106
+ assert.equal(second.status, 0, `${second.stdout}\n${second.stderr}`);
107
+
108
+ const commands = allCommands(readHooks(root));
109
+ assert.ok(commands.includes(USER_STOP_COMMAND), 'user Stop hook was erased by the reinstall');
110
+ assert.ok(commands.includes(USER_PRETOOL_COMMAND), 'user PreToolUse hook was erased by the reinstall');
111
+ });
112
+ });
113
+
114
+ test('--force-overwrite does not destroy hooks CafeKit did not author', () => {
115
+ inTempProject((root) => {
116
+ assert.equal(install(root).status, 0);
117
+ addUserHooks(root);
118
+
119
+ const forced = install(root, ['--force-overwrite']);
120
+ assert.equal(forced.status, 0, `${forced.stdout}\n${forced.stderr}`);
121
+
122
+ const commands = allCommands(readHooks(root));
123
+ assert.ok(commands.includes(USER_STOP_COMMAND), '--force-overwrite erased the user Stop hook');
124
+ assert.ok(commands.includes(USER_PRETOOL_COMMAND), '--force-overwrite erased the user PreToolUse hook');
125
+ });
126
+ });
127
+
128
+ test('repeated installs never duplicate a CafeKit hook', () => {
129
+ inTempProject((root) => {
130
+ assert.equal(install(root).status, 0);
131
+ const afterFirst = allHandlers(readHooks(root)).length;
132
+
133
+ assert.equal(install(root).status, 0);
134
+ assert.equal(install(root).status, 0);
135
+
136
+ const config = readHooks(root);
137
+ assert.equal(allHandlers(config).length, afterFirst, 'a reinstall added handlers that were already registered');
138
+
139
+ // Uniqueness is per event, not global: state.cjs and completion-authority.cjs each
140
+ // register under several events by design.
141
+ for (const [eventName, groups] of Object.entries(config.hooks)) {
142
+ const scripts = groups
143
+ .flatMap((group) => group.hooks || [])
144
+ .map((handler) => hookScript(handler.command))
145
+ .filter(Boolean);
146
+ assert.equal(
147
+ new Set(scripts).size, scripts.length,
148
+ `duplicate hook scripts under ${eventName}: ${scripts.join(', ')}`
149
+ );
150
+ }
151
+ });
152
+ });
153
+
154
+ test('a hook the user moved to another event is not re-added to the one CafeKit chose', () => {
155
+ inTempProject((root) => {
156
+ assert.equal(install(root).status, 0);
157
+
158
+ // Move a CafeKit hook out of the group the template put it in, the way a user editing
159
+ // matchers would. Re-adding it on the next install would fire it twice.
160
+ const config = readHooks(root);
161
+ let moved = null;
162
+ for (const group of config.hooks.PreToolUse) {
163
+ const index = (group.hooks || []).findIndex((handler) => hookScript(handler.command) === 'inspect-block.cjs');
164
+ if (index >= 0) { moved = group.hooks.splice(index, 1)[0]; break; }
165
+ }
166
+ assert.ok(moved, 'fixture expects inspect-block.cjs under PreToolUse');
167
+ config.hooks.PreToolUse.push({ matcher: 'Read|Grep', hooks: [moved] });
168
+ fs.writeFileSync(hooksPath(root), `${JSON.stringify(config, null, 2)}\n`);
169
+
170
+ assert.equal(install(root).status, 0);
171
+
172
+ const scripts = allCommands(readHooks(root)).map(hookScript).filter(Boolean);
173
+ assert.equal(
174
+ scripts.filter((script) => script === 'inspect-block.cjs').length, 1,
175
+ 'the moved hook was re-added under its original matcher'
176
+ );
177
+ });
178
+ });
179
+
180
+ test('obsolete CafeKit hooks are pruned while similarly named foreign hooks stay', () => {
181
+ const obsolete = MANIFEST.obsolete.settingsHookCommandSubstrings;
182
+ assert.ok(obsolete.length > 0, 'fixture requires at least one retired hook substring');
183
+
184
+ inTempProject((root) => {
185
+ assert.equal(install(root).status, 0);
186
+
187
+ const config = readHooks(root);
188
+ const retired = `node ".codex/${obsolete[0]}"`;
189
+ const foreign = 'node "scripts/my-skill-router.cjs"';
190
+ config.hooks.UserPromptSubmit.push({
191
+ matcher: '*',
192
+ hooks: [{ type: 'command', command: retired }, { type: 'command', command: foreign }]
193
+ });
194
+ fs.writeFileSync(hooksPath(root), `${JSON.stringify(config, null, 2)}\n`);
195
+
196
+ const second = install(root);
197
+ assert.equal(second.status, 0, `${second.stdout}\n${second.stderr}`);
198
+
199
+ const commands = allCommands(readHooks(root));
200
+ assert.ok(!commands.includes(retired), 'retired CafeKit hook survived the prune');
201
+ assert.ok(commands.includes(foreign), 'prune removed a hook CafeKit never authored');
202
+ });
203
+ });
204
+
205
+ test('a malformed hooks.json is reported and left byte-identical, never overwritten', () => {
206
+ inTempProject((root) => {
207
+ fs.mkdirSync(path.join(root, '.codex'), { recursive: true });
208
+ const broken = '{ "hooks": { "Stop": [ }\n';
209
+ fs.writeFileSync(hooksPath(root), broken);
210
+
211
+ const result = install(root);
212
+ assert.notEqual(result.status, 0, 'a malformed hooks.json must fail the install, not pass silently');
213
+ assert.match(`${result.stdout}${result.stderr}`, /not valid JSON/);
214
+ assert.equal(fs.readFileSync(hooksPath(root), 'utf8'), broken, 'the malformed file was overwritten');
215
+ });
216
+ });
217
+
218
+ test('a dry run reports the merge without writing hooks.json', () => {
219
+ inTempProject((root) => {
220
+ const result = install(root, ['--dry-run']);
221
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
222
+ assert.match(result.stdout, /Codex hooks/);
223
+ assert.equal(fs.existsSync(hooksPath(root)), false, 'a dry run wrote hooks.json');
224
+ });
225
+ });
226
+
227
+ test('hooks.json stays outside the ownership manifest, like settings.json', () => {
228
+ inTempProject((root) => {
229
+ assert.equal(install(root).status, 0);
230
+
231
+ const manifestPath = path.join(root, '.codex', 'cafekit-manifest.json');
232
+ assert.equal(fs.existsSync(manifestPath), true, 'the install manifest is missing');
233
+ const tracked = Object.keys(JSON.parse(fs.readFileSync(manifestPath, 'utf8')).files || {});
234
+ assert.ok(
235
+ !tracked.some((file) => file.endsWith('hooks.json')),
236
+ 'hooks.json is tracked as a payload file, so a user edit would be classified user-modified again'
237
+ );
238
+ });
239
+ });
@@ -156,6 +156,56 @@ function canonicalProcessTask(root, title, dependency = 'none') {
156
156
  ].join('\n');
157
157
  }
158
158
 
159
+ test('Claude and Codex docs sync ignore committed Specs state but report source changes', () => {
160
+ inHookFixture((root, hooks) => {
161
+ const docsDir = path.join(root, 'docs');
162
+ fs.mkdirSync(docsDir, { recursive: true });
163
+ fs.writeFileSync(path.join(root, 'package.json'), '{"name":"docs-sync-fixture"}\n');
164
+ fs.writeFileSync(path.join(docsDir, 'project-overview-pdr.md'), '# Fixture\n');
165
+ let result = spawnSync('git', ['-C', root, 'add', 'package.json', 'docs/project-overview-pdr.md'], { encoding: 'utf8' });
166
+ assert.equal(result.status, 0, result.stderr);
167
+ result = spawnSync('git', ['-C', root, 'commit', '-qm', 'source baseline'], { encoding: 'utf8' });
168
+ assert.equal(result.status, 0, result.stderr);
169
+ const sourceBase = spawnSync('git', ['-C', root, 'rev-parse', 'HEAD'], { encoding: 'utf8' }).stdout.trim();
170
+ fs.writeFileSync(path.join(docsDir, '.sync_hash'), `${sourceBase}\n`);
171
+ result = spawnSync('git', ['-C', root, 'add', 'docs/.sync_hash'], { encoding: 'utf8' });
172
+ assert.equal(result.status, 0, result.stderr);
173
+ result = spawnSync('git', ['-C', root, 'commit', '-qm', 'sync docs'], { encoding: 'utf8' });
174
+ assert.equal(result.status, 0, result.stderr);
175
+
176
+ const featureDir = path.join(root, 'specs', 'demo');
177
+ fs.mkdirSync(featureDir, { recursive: true });
178
+ fs.writeFileSync(path.join(featureDir, 'plan.md'), '# Completed packet\n');
179
+ result = spawnSync('git', ['-C', root, 'add', 'specs/demo/plan.md'], { encoding: 'utf8' });
180
+ assert.equal(result.status, 0, result.stderr);
181
+ result = spawnSync('git', ['-C', root, 'commit', '-qm', 'refresh receipt state'], { encoding: 'utf8' });
182
+ assert.equal(result.status, 0, result.stderr);
183
+
184
+ const payload = { cwd: root, session_id: 'docs-sync-session', hook_event_name: 'SessionStart' };
185
+ const docsSyncHooks = [
186
+ path.join(hooks, 'docs-sync.cjs'),
187
+ path.join(PACKAGE_ROOT, 'src/claude/hooks/docs-sync.cjs'),
188
+ ];
189
+ for (const hook of docsSyncHooks) {
190
+ const afterSpecCommit = runHook(hook, root, payload);
191
+ assert.equal(afterSpecCommit.status, 0, afterSpecCommit.stderr);
192
+ assert.equal(afterSpecCommit.stdout, '', `${hook}: spec-only commits must not stale docs sync`);
193
+ }
194
+
195
+ fs.mkdirSync(path.join(root, 'src'), { recursive: true });
196
+ fs.writeFileSync(path.join(root, 'src', 'app.js'), 'module.exports = true;\n');
197
+ result = spawnSync('git', ['-C', root, 'add', 'src/app.js'], { encoding: 'utf8' });
198
+ assert.equal(result.status, 0, result.stderr);
199
+ result = spawnSync('git', ['-C', root, 'commit', '-qm', 'change source'], { encoding: 'utf8' });
200
+ assert.equal(result.status, 0, result.stderr);
201
+ for (const hook of docsSyncHooks) {
202
+ const afterSourceCommit = runHook(hook, root, payload);
203
+ assert.equal(afterSourceCommit.status, 0, afterSourceCommit.stderr);
204
+ assert.match(afterSourceCommit.stdout, /Docs sync needed/);
205
+ }
206
+ });
207
+ });
208
+
159
209
  function inInstalledProcessSpecStateFixture(run) {
160
210
  return inHookFixture((root, hooks) => {
161
211
  const feature = path.join(root, 'specs', 'auth');
@@ -1696,3 +1746,128 @@ test('Claude and Codex fail closed consistently for malformed cwd payloads', ()
1696
1746
  assert.equal(JSON.parse(codex.stdout).hookSpecificOutput.permissionDecision, 'deny');
1697
1747
  });
1698
1748
  });
1749
+
1750
+ test('Codex emits the required decision for all six receipt states', () => {
1751
+ // Codex owns no checker of its own: both wrappers in .codex/hooks/lib forward to
1752
+ // the shared module, so the two receipt modes arrive by inheritance. This case is
1753
+ // the regression lock on that inheritance. Each state asserts the decision Codex
1754
+ // must emit on its own terms; parity with Claude is checked in addition, never
1755
+ // instead, because both runtimes execute the same module and a broken
1756
+ // implementation would agree with itself.
1757
+ //
1758
+ // The dirty-tree state runs the hook from a directory below the project root with
1759
+ // the uncommitted change outside that directory's subtree. Without that, an
1760
+ // implementation resolving the repository root from the working directory returns
1761
+ // the same decision as a correct one and the case cannot fail.
1762
+ inHookFixture((root, hooks) => {
1763
+ const nested = path.join(root, 'packages', 'app');
1764
+ const feature = path.join(root, 'specs', 'auth');
1765
+ const taskFile = path.join(feature, 'task-01-auth.md');
1766
+ const outsideDirt = path.join(root, 'src', 'outside.txt');
1767
+ fs.mkdirSync(nested, { recursive: true });
1768
+ fs.mkdirSync(feature, { recursive: true });
1769
+ fs.mkdirSync(path.dirname(outsideDirt), { recursive: true });
1770
+ fs.writeFileSync(path.join(feature, 'plan.md'), '# Auth plan\n');
1771
+ fs.writeFileSync(outsideDirt, 'committed\n');
1772
+
1773
+ const staleBase = 'f'.repeat(40);
1774
+ const staleHead = 'e'.repeat(40);
1775
+ const taskBody = [
1776
+ '# Task 01: auth', '', 'Status: done', '',
1777
+ '## Dependencies', '', '- none', '',
1778
+ '## Verification Plan', '', '- Command: node --test', '',
1779
+ '## Receipt', '',
1780
+ 'Verification: PASS', 'Command: node --test', 'Exit: 0',
1781
+ `Base: ${staleBase}`, `Head: ${staleHead}`,
1782
+ '```text', '$ node --test', 'pass: 1', '```', '',
1783
+ ].join('\n');
1784
+ fs.writeFileSync(taskFile, taskBody);
1785
+
1786
+ const gate = path.join(hooks, 'spec-gate.cjs');
1787
+ const payload = {
1788
+ cwd: nested,
1789
+ featureName: 'auth',
1790
+ session_id: 'session-a',
1791
+ hook_event_name: 'Stop',
1792
+ stop_hook_active: false,
1793
+ };
1794
+ const git = (...args) => {
1795
+ const result = spawnSync('git', ['-C', root, ...args], { encoding: 'utf8' });
1796
+ assert.equal(result.status, 0, result.stderr);
1797
+ return result;
1798
+ };
1799
+ const decide = () => {
1800
+ const result = runHook(gate, nested, payload);
1801
+ assert.equal(result.status, 0, result.stderr);
1802
+ return result.stdout.trim() === '' ? 'accept' : 'block';
1803
+ };
1804
+ // Use the same module instance that minted the context: a trusted runtime
1805
+ // context is tracked per module, so mixing the package copy with the fixture
1806
+ // copy would report a spurious binding failure.
1807
+ const RECEIPT = require(path.join(PACKAGE_ROOT, 'src/claude/scripts/spec-receipt.cjs'));
1808
+ const sharedDecides = () => {
1809
+ const failures = RECEIPT.checkWorkflowTaskReceipt(
1810
+ feature, 'task-01-auth.md', workflowRuntimeContext(root), POLICY,
1811
+ ).failures;
1812
+ return failures.length === 0 ? 'accept' : 'block';
1813
+ };
1814
+
1815
+ // 1. untracked task file — never committed, so the binding stays.
1816
+ assert.equal(decide(), 'block', 'an untracked task file must keep full binding');
1817
+
1818
+ // 2. staged but not committed — `git add` alone must not read as committed.
1819
+ git('add', '-A');
1820
+ assert.equal(decide(), 'block', 'a staged-only task file must keep full binding');
1821
+
1822
+ // 3. committed on a clean tree — the one accepting state.
1823
+ git('commit', '-qm', 'receipt and source');
1824
+ // Generated runtime state is excluded by design, so assert cleanliness the way
1825
+ // the selector sees it: nothing dirty outside the specs root and the runtime
1826
+ // state roots. The gate's own cache write must not read as a dirty worktree.
1827
+ const cleanStatus = spawnSync('git', ['-C', root, 'status', '--porcelain', '--untracked-files=all',
1828
+ '--', '.', ':(exclude,literal)specs', ':(exclude,literal).codex/hooks/.logs',
1829
+ ':(exclude,literal).claude/hooks/.logs', ':(exclude,literal).codex/runtime.json'], { encoding: 'utf8' });
1830
+ assert.equal(cleanStatus.stdout.trim(), '', 'nothing outside specs and runtime state may be dirty for this state to mean anything');
1831
+ assert.equal(decide(), 'accept', 'a committed, unchanged receipt on a clean tree must be accepted');
1832
+ assert.equal(sharedDecides(), 'accept', 'the shared checker must agree');
1833
+
1834
+ // 4. dirty tree outside the specs root, hook run from a nested directory with
1835
+ // the change outside that directory's subtree.
1836
+ fs.writeFileSync(outsideDirt, 'uncommitted\n');
1837
+ assert.ok(!path.relative(nested, outsideDirt).startsWith('..') === false,
1838
+ 'the dirt must lie outside the working directory used for the run');
1839
+ assert.equal(decide(), 'block', 'an uncommitted change outside the specs root must rebind');
1840
+ assert.equal(sharedDecides(), 'block', 'the shared checker must agree');
1841
+ git('checkout', '--', 'src/outside.txt');
1842
+
1843
+ // 5. work confined to the specs root does not reopen a committed receipt.
1844
+ fs.writeFileSync(path.join(feature, 'plan.md'), '# Auth plan\n\nEdited after the receipt landed.\n');
1845
+ assert.equal(decide(), 'accept', 'edits inside the specs root must not rebind');
1846
+ git('checkout', '--', 'specs/auth/plan.md');
1847
+
1848
+ // 6. committed then modified — the receipt itself changed after the commit.
1849
+ fs.writeFileSync(taskFile, taskBody.replace(`Head: ${staleHead}`, `Head: ${'d'.repeat(40)}`));
1850
+ assert.equal(decide(), 'block', 'a receipt edited after commit must be rebound');
1851
+ assert.equal(sharedDecides(), 'block', 'the shared checker must agree');
1852
+ });
1853
+ });
1854
+
1855
+ test('Codex wrappers stay pass-through and carry no receipt-mode logic', () => {
1856
+ // The mode selector must exist in exactly one place. If it is ever duplicated
1857
+ // into the Codex wrapper, the two runtimes can drift apart silently.
1858
+ const wrapper = fs.readFileSync(
1859
+ path.join(PACKAGE_ROOT, 'src/codex/hooks/lib/spec-receipt.cjs'),
1860
+ 'utf8',
1861
+ );
1862
+ for (const marker of ['receiptBindingMode', 'skip-worktree', 'ls-files', 'cat-file', 'structure']) {
1863
+ assert.ok(
1864
+ !wrapper.includes(marker),
1865
+ `the Codex wrapper must not reimplement receipt-mode logic; found ${marker}`,
1866
+ );
1867
+ }
1868
+ assert.match(wrapper, /checkWorkflowTaskReceipt\(featureDir, taskPath, runtimeContext, policy\)/);
1869
+ assert.match(
1870
+ fs.readFileSync(path.join(PACKAGE_ROOT, 'src/codex/hooks/spec-state.cjs'), 'utf8'),
1871
+ /Stop re-checks every done receipt/,
1872
+ );
1873
+ });