@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
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const assert = require('node:assert/strict');
4
+ const { createHash } = require('node:crypto');
4
5
  const fs = require('node:fs');
5
6
  const os = require('node:os');
6
7
  const path = require('node:path');
@@ -8,6 +9,7 @@ const { spawnSync } = require('node:child_process');
8
9
  const { test } = require('node:test');
9
10
 
10
11
  const {
12
+ convertCodexAgentContent,
11
13
  normalizeCodexBody,
12
14
  transformManagedCodexContent,
13
15
  upsertManagedCodexBlock
@@ -29,14 +31,85 @@ const MANIFEST = JSON.parse(
29
31
  fs.readFileSync(path.join(PACKAGE_ROOT, 'src/claude/migration-manifest.json'), 'utf8')
30
32
  );
31
33
  const SPECS_SOURCE_ROOT = path.join(PACKAGE_ROOT, 'src/claude/skills/specs');
34
+ const SPEC_MAKER_SOURCE_PATH = path.join(PACKAGE_ROOT, 'src/claude/agents/spec-maker.md');
35
+ const BRAINSTORM_SOURCE_ROOT = path.join(PACKAGE_ROOT, 'src/claude/skills/brainstorm');
36
+ const BRAINSTORM_SKILL_SOURCE_PATH = path.join(BRAINSTORM_SOURCE_ROOT, 'SKILL.md');
37
+ const BRAINSTORM_REFERENCE_SOURCE_PATH = path.join(
38
+ BRAINSTORM_SOURCE_ROOT,
39
+ 'references/question-framework.md'
40
+ );
41
+ const BRAINSTORM_AGENT_SOURCE_PATH = path.join(PACKAGE_ROOT, 'src/claude/agents/brainstormer.md');
42
+ const BRAINSTORM_SOURCE_PATHS = [
43
+ BRAINSTORM_SKILL_SOURCE_PATH,
44
+ BRAINSTORM_REFERENCE_SOURCE_PATH,
45
+ BRAINSTORM_AGENT_SOURCE_PATH
46
+ ];
47
+ const RESEARCH_SOURCE_ROOT = path.join(PACKAGE_ROOT, 'src/claude/skills/research');
48
+ const RESEARCH_SKILL_SOURCE_PATH = path.join(RESEARCH_SOURCE_ROOT, 'SKILL.md');
49
+ const RESEARCH_AGENT_SOURCE_PATH = path.join(PACKAGE_ROOT, 'src/claude/agents/researcher.md');
50
+ const DEVELOP_SOURCE_ROOT = path.join(PACKAGE_ROOT, 'src/claude/skills/develop');
51
+ const DEVELOP_BUNDLE = [
52
+ 'SKILL.md',
53
+ 'references/quality-gate.md',
54
+ 'references/parallel-waves.md',
55
+ 'references/subagent-patterns.md'
56
+ ];
57
+ const TEST_SOURCE_ROOT = path.join(PACKAGE_ROOT, 'src/claude/skills/test');
58
+ const TEST_BUNDLE = [
59
+ 'SKILL.md',
60
+ 'references/execution-strategy.md',
61
+ 'references/failure-triage.md',
62
+ 'references/test-memory.md'
63
+ ];
64
+ const TEST_RUNNER_SOURCE_PATH = path.join(PACKAGE_ROOT, 'src/claude/agents/test-runner.md');
65
+ const DOCS_ADAPTIVE_SOURCE_ROOT = path.join(PACKAGE_ROOT, 'src/claude/skills/docs');
66
+ const DOCS_ADAPTIVE_BUNDLE = [
67
+ 'SKILL.md',
68
+ 'references/init-workflow.md',
69
+ 'references/update-workflow.md',
70
+ 'references/standard-docs-workflow.md'
71
+ ];
72
+ const HOTFIX_SOURCE_ROOT = path.join(PACKAGE_ROOT, 'src/claude/skills/hotfix');
73
+ const HOTFIX_BUNDLE = [
74
+ 'SKILL.md',
75
+ 'references/diagnosis-protocol.md',
76
+ 'references/review-cycle.md',
77
+ 'references/parallel-patterns.md',
78
+ 'references/prevention-gate.md',
79
+ 'references/workflow-specialized.md'
80
+ ];
81
+ const DEBUG_SKILL_SOURCE_PATH = path.join(PACKAGE_ROOT, 'src/claude/skills/debug/SKILL.md');
82
+ const DEBUG_AGENT_SOURCE_PATH = path.join(PACKAGE_ROOT, 'src/claude/agents/debugger.md');
83
+ const DEBUG_REFERENCE_FILES = [
84
+ 'core-philosophy.md',
85
+ 'root-cause-tracing.md',
86
+ 'verification-protocol.md',
87
+ 'log-ci-analysis.md',
88
+ 'parallel-agent-hydration.md',
89
+ 'side-effect-gate.md'
90
+ ];
91
+ const CODE_REVIEW_SOURCE_ROOT = path.join(PACKAGE_ROOT, 'src/claude/skills/code-review');
32
92
  const IMPLEMENTATION_READINESS_BOUNDARY_ROWS = [
93
+ ['Interaction/UI', 'entry journey; visible/loading/empty/error states; input/focus/keyboard; accessibility; responsive/native/device behavior'],
33
94
  ['API/CLI', 'entrypoint/route or command grammar; identity/auth; input/default/normalization; success output; error/status/exit; duplicate/retry/idempotency; compatibility'],
34
- ['Schema', 'version; exact keys/nesting/types; required/optional; enum/format/bounds/cardinality; unknown-field behavior; compatibility/migration'],
35
- ['State/concurrency', 'initial/terminal states; event + guard + effect + next + error; ordering; duplicate/retry; writer/lock acquire/contention/release; rollback/recovery'],
95
+ ['Data/schema', 'authority/storage/transaction; version; exact keys/nesting/types; required/optional; enum/format/bounds/cardinality; unknown-field behavior; compatibility/migration'],
96
+ ['Async/state', 'initial/terminal states; event + guard + effect + next + error; ordering/concurrency; duplicate/retry; writer/lock acquire/contention/release; cancellation; rollback/recovery'],
36
97
  ['Filesystem/security', 'authoritative root; trusted/untrusted segment grammar; lexical + canonical containment and symlink policy; flags/mode; temp/rename/fsync; lock/stale reclaim; crash cleanup'],
98
+ ['Runtime/deploy', 'config/env/flags; registration/packaging; OS/arch; rollout/rollback; health/logging; operator recovery'],
37
99
  ['Time/retention', 'clock source; unit/precision/timezone; endpoints and inclusion/comparator; anomaly behavior; expiry/purge/recovery'],
100
+ ['AI/model', 'provider/model/prompt/tool schema; nondeterminism/bounds; safety/privacy; fallback; cost/token limit; eval oracle'],
38
101
  ['Integration/proof', 'caller; export/registration; packaging/config; native path/consumer; proof level (`source`/`installed`/`live`); observable failure oracle']
39
102
  ];
103
+
104
+ function boundaryTableHasRequiredRows(table) {
105
+ const [header, ...rows] = table;
106
+ const labels = rows.map((row) => row[0]);
107
+ return JSON.stringify(header || []) === JSON.stringify(['Boundary', 'Required contract when material'])
108
+ && new Set(labels).size === labels.length
109
+ && IMPLEMENTATION_READINESS_BOUNDARY_ROWS.every((expected) =>
110
+ rows.some((row) => JSON.stringify(row) === JSON.stringify(expected)));
111
+ }
112
+
40
113
  const IMPLEMENTATION_READINESS_CLAUSES = {
41
114
  noInvention: 'Before implementation handoff, apply the **no-invention gate**: if two implementations conform to the packet text yet can produce different externally observable output, state, error, security, or compatibility behavior, surface the missing choice as an explicit C1 or C2 question and block handoff.',
42
115
  materialDefinition: 'A boundary is material when the task creates, changes, or depends on it and a different choice changes an external observation, security, durable data, compatibility, or proof reachability. Require only the matching material row; omit nonmaterial categories.',
@@ -44,15 +117,16 @@ const IMPLEMENTATION_READINESS_CLAUSES = {
44
117
  proofPlanLines: [
45
118
  '- Command: `<exact runnable command>`',
46
119
  '- Named probe: <existing concrete probe/test/hook ID; never only a suite label>',
47
- '- Reachability: <real entrypoint/caller at each `source`/`installed`/`live` level; `UNKNOWN` if unexecuted>',
120
+ '- Reachability: <known command/caller/environment per required level; `UNKNOWN` only when the path cannot yet be established>',
48
121
  '- Oracle: <externally observable success or failure>',
49
122
  '- Counterexample: <material alternative behavior that must make this proof fail>',
50
123
  '- Artifacts: <required artifact path plus digest algorithm/comparison rule, or explicitly ephemeral with cleanup rule>'
51
124
  ],
52
125
  proofTrace: 'Trace `Command → Named probe → Reachability → Oracle`.',
53
- namedProbeOwnership: 'Aggregate suites\nname the owning concrete probe.',
54
- proofLevelSeparation: 'Proof at `source`/`installed`/`live` stays\nseparate; one level never promotes another.',
126
+ namedProbeOwnership: 'Aggregate suites name the\nowning concrete probe.',
127
+ proofLevelSeparation: 'Levels stay separate and never promote one another.',
55
128
  disposableTemplateControls: 'Run mutation or destructive\nnegative controls only on disposable copies under a verified temporary root,\nnever tracked worktree or canonical source bytes.',
129
+ proofLevelMapping: 'For every required level in each referenced CP row, map its named probe and\nreachability here; one command may own several explicitly named level probes.',
56
130
  disposableReviewControls: 'Run mutation or destructive negative controls only on disposable copies below a verified temporary root, never tracked worktree or canonical source bytes.',
57
131
  failureSemantics: '`Crash` means abrupt unhandled termination before the claimed catch point; a catchable failure returns/raises an error or exits nonzero. Never use them interchangeably.',
58
132
  privacyIdentifiers: 'Any privacy/security claim names the exact identifier surface at risk, such as an env var, header, path, token class, or field name; generic “sensitive data” is insufficient.',
@@ -62,6 +136,48 @@ const IMPLEMENTATION_READINESS_CLAUSES = {
62
136
  unknownBlocks: 'Only `PASS` closes it; `FAIL` remains open for the remaining paper-review round; `UNKNOWN` blocks implementation handoff.',
63
137
  scopeReturnsToC1: 'A repair that adds user semantics or scope returns to C1.'
64
138
  };
139
+ const ADAPTIVE_COVERAGE_PROFILE_HEADER = [
140
+ 'ID', 'Outcome', 'Change kinds', 'Material surfaces', 'Ambiguity/action',
141
+ 'Risk/evidence', 'Required proof'
142
+ ];
143
+ const ADAPTIVE_COVERAGE_PROFILE_ROW = [
144
+ 'CP-01', '<externally observable outcome>', '<all kinds>', '<all material surfaces>',
145
+ '<state + action>', '<level + evidence>', '<source/installed/live set>'
146
+ ];
147
+ const ADAPTIVE_COVERAGE_AMBIGUITY_ROWS = [
148
+ ['State', 'Required action'],
149
+ ['`none`', 'proceed'],
150
+ ['`examples-needed`', 'add two or three examples only for an already decided rule; promote to `decision-needed` if an example changes observable behavior'],
151
+ ['`decision-needed`', 'ask the user at C1/C2 and keep affected tasks blocked'],
152
+ ['`design-needed`', 'after user-owned decisions settle, route material competing technical designs through Brainstorm']
153
+ ];
154
+ const ADAPTIVE_REVIEWER_ROWS = [
155
+ ['Groups', 'Reviewers', 'Roles', 'Claim budget'],
156
+ ['1-2', '2', 'Fact Checker plus all matching material lenses', 'about 5 per group'],
157
+ ['3-5', '3', 'Fact Checker plus all matching material lenses', 'about 10 per group'],
158
+ ['6+', '4', 'Fact Checker plus all matching material lenses', 'at least 15 total']
159
+ ];
160
+ const ADAPTIVE_COVERAGE_CLAUSES = {
161
+ riskFirst: 'Classify material risk before choosing a workflow; user wording never lowers an observed floor.',
162
+ criticalFloor: '`critical`: auth/secrets/privacy; destructive/irreversible work or possible data loss/corruption; money/privilege/safety; production-state mutation.',
163
+ elevatedFloor: '`elevated`: cross-component contracts, compatibility, concurrency, external integration, or installed/runtime behavior.',
164
+ frontmatterGate: 'skip only when a change is clear, isolated, reversible, routine, and likely limited to one or two files.',
165
+ directGate: 'Work directly only when the cause and change are clear, isolated, reversible,\n`routine`, and likely limited to one or two files.',
166
+ splitRoute: 'Split three or more independent\nsubsystems; otherwise use one Specs packet for any material work that does not qualify for direct work or Brainstorm-only exploration.',
167
+ independentSubsystem: 'A subsystem is independent only when its outcome, boundary, and verification/deployment path can move through the lifecycle separately.',
168
+ profileAuthority: 'For a Specs route, `plan.md` owns one `## Coverage profile` row per externally observable outcome; direct and Brainstorm-only routes do not persist it.',
169
+ openKinds: 'Change kinds are multi-valued (`add`, `modify`, `fix`, `refactor`, `remove`, `migrate`, `integrate`), and unfamiliar kinds or surfaces use `other:<verbatim>` rather than disappearing.',
170
+ scopedUnion: 'Each task references its CP IDs; authoring, review, edge, and proof obligations union only inside affected rows/tasks.',
171
+ profileRederivation: 'Rederive affected CP rows after any accepted scope, outcome, criteria, ownership, dependency, risk, or proof delta before task status.',
172
+ plannedProof: '`Required proof` is a planned level set, not execution\nevidence: known but unrun proof may be `pending`; `UNKNOWN` reachability blocks\n`pending`; missing, failed, or unavailable required evidence blocks `done`/C3.',
173
+ proofSeparation: 'Levels stay separate and never promote one another.',
174
+ liveLimit: 'Source/static checks prove the written contract, not live-model adherence.',
175
+ specMakerAuthority: 'they are the canonical risk and coverage authority. Do not duplicate\ntheir taxonomy here.',
176
+ specMakerAmbiguity: 'Apply the canonical ambiguity action; examples never decide observable behavior.',
177
+ specMakerRoute: 'Apply their risk-first route before C1 and stop when the\nrequest qualifies for direct work; hand off when it requires Brainstorm-only exploration.',
178
+ reviewRisk: 'Keep Fact Checker as the baseline. Assign every remaining material CP risk to a\nnamed reviewer lens; a critical row includes both relevant security-adversary and\nfailure-mode coverage, and nonmaterial lenses are not added.',
179
+ reviewCapacity: 'Reviewer count is fixed by the table, not lens count. Give each reviewer a distinct primary lens; when material lenses exceed reviewers, combine related named lenses on one reviewer and keep every material lens assigned.'
180
+ };
65
181
  const V3_SPECS_BUNDLE = [
66
182
  'SKILL.md',
67
183
  'references/review.md',
@@ -112,6 +228,21 @@ function install(root, extraArgs = []) {
112
228
  );
113
229
  }
114
230
 
231
+ function installPlatforms(root, platforms, extraArgs = []) {
232
+ return spawnSync(
233
+ process.execPath,
234
+ [INSTALLER, '--platform', platforms.join(','), '--yes', ...extraArgs],
235
+ { cwd: root, encoding: 'utf8', env: { ...process.env, PATH: '/usr/bin:/bin' } }
236
+ );
237
+ }
238
+
239
+ function installedCatalog(root, runtime) {
240
+ const script = path.join(root, runtime === 'codex' ? '.codex' : '.claude', 'scripts', 'generate-skill-catalog.cjs');
241
+ const result = spawnSync(process.execPath, [script, '--json'], { cwd: root, encoding: 'utf8' });
242
+ assert.equal(result.status, 0, result.stderr);
243
+ return JSON.parse(result.stdout);
244
+ }
245
+
115
246
  function allFiles(root, predicate) {
116
247
  const found = [];
117
248
  for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
@@ -134,6 +265,13 @@ function parseGeneratedTomlString(content, key) {
134
265
  return JSON.parse(match[1]);
135
266
  }
136
267
 
268
+ function replaceGeneratedTomlString(content, key, value) {
269
+ const pattern = new RegExp(`^${key} = .+$`, 'gm');
270
+ const matches = [...content.matchAll(pattern)];
271
+ assert.equal(matches.length, 1, `expected exactly one TOML key: ${key}`);
272
+ return content.replace(pattern, `${key} = ${JSON.stringify(value)}`);
273
+ }
274
+
137
275
  function renderCanonicalVerificationExample(template) {
138
276
  const values = new Map([
139
277
  ['SUBJECT_REQ', '1'],
@@ -227,30 +365,34 @@ function markdownBetweenHeadings(content, startHeading, endHeading) {
227
365
  return value.slice(startIndex + startMarker.length, endIndex);
228
366
  }
229
367
 
230
- function implementationReadinessIssues(templates, review) {
231
- if (typeof templates !== 'string' || typeof review !== 'string') {
368
+ function implementationReadinessIssues(input) {
369
+ const keys = input && typeof input === 'object' && !Array.isArray(input)
370
+ ? Object.keys(input).sort()
371
+ : [];
372
+ if (keys.join(',') !== 'review,templates'
373
+ || typeof input.templates !== 'string' || typeof input.review !== 'string') {
232
374
  throw new TypeError('implementation-readiness checker expects templates and review UTF-8 strings');
233
375
  }
234
376
 
235
377
  const issues = new Set();
378
+ const { templates, review } = input;
236
379
  const authoring = markdownSection(templates, 'No-invention and conditional boundary contracts');
237
380
  if (!authoring.includes(IMPLEMENTATION_READINESS_CLAUSES.noInvention)) {
238
381
  issues.add('no-invention');
239
382
  }
383
+ const contradictoryNoInvention = /\b(?:exception|however)\b.{0,160}\bimplementation handoff\b.{0,80}\b(?:may|can)\s+(?:proceed|continue)\b.{0,160}\bunresolved\b/i;
384
+ if (contradictoryNoInvention.test(normalizeMarkdownWhitespace(authoring))) {
385
+ issues.add('no-invention');
386
+ }
240
387
 
241
388
  const boundaryTable = markdownTableUnderHeading(
242
389
  templates,
243
390
  'No-invention and conditional boundary contracts'
244
391
  );
245
- const expectedBoundaryTable = [
246
- ['Boundary', 'Required contract when material'],
247
- ...IMPLEMENTATION_READINESS_BOUNDARY_ROWS
248
- ];
249
392
  if (!authoring.includes(IMPLEMENTATION_READINESS_CLAUSES.materialDefinition)
250
393
  || !authoring.includes(IMPLEMENTATION_READINESS_CLAUSES.exactBoundaryChoices)
251
- || IMPLEMENTATION_READINESS_BOUNDARY_ROWS.length !== 6
252
- || boundaryTable.length !== expectedBoundaryTable.length
253
- || JSON.stringify(boundaryTable) !== JSON.stringify(expectedBoundaryTable)) {
394
+ || IMPLEMENTATION_READINESS_BOUNDARY_ROWS.length !== 9
395
+ || !boundaryTableHasRequiredRows(boundaryTable)) {
254
396
  issues.add('boundary-contract');
255
397
  }
256
398
 
@@ -265,7 +407,8 @@ function implementationReadinessIssues(templates, review) {
265
407
  IMPLEMENTATION_READINESS_CLAUSES.proofTrace,
266
408
  IMPLEMENTATION_READINESS_CLAUSES.namedProbeOwnership,
267
409
  IMPLEMENTATION_READINESS_CLAUSES.proofLevelSeparation,
268
- IMPLEMENTATION_READINESS_CLAUSES.disposableTemplateControls
410
+ IMPLEMENTATION_READINESS_CLAUSES.disposableTemplateControls,
411
+ IMPLEMENTATION_READINESS_CLAUSES.proofLevelMapping
269
412
  ].map(normalizeMarkdownWhitespace);
270
413
  const reviewNegativeControls = normalizeMarkdownWhitespace(
271
414
  markdownSection(review, 'B2 — fresh-context red team')
@@ -305,9 +448,107 @@ function implementationReadinessIssues(templates, review) {
305
448
  return [...issues].sort();
306
449
  }
307
450
 
451
+ function adaptiveCoverageIssues(input) {
452
+ const expectedKeys = ['review', 'skill', 'specMaker', 'templates'];
453
+ const keys = input && typeof input === 'object' && !Array.isArray(input)
454
+ ? Object.keys(input).sort()
455
+ : [];
456
+ if (JSON.stringify(keys) !== JSON.stringify(expectedKeys)
457
+ || expectedKeys.some((key) => typeof input[key] !== 'string')) {
458
+ throw new TypeError('adaptive-coverage checker expects skill, specMaker, templates, and review UTF-8 strings');
459
+ }
460
+
461
+ const issues = new Set();
462
+ const skill = normalizeMarkdownWhitespace(input.skill);
463
+ const templates = normalizeMarkdownWhitespace(input.templates);
464
+ const review = normalizeMarkdownWhitespace(input.review);
465
+ const specMaker = normalizeMarkdownWhitespace(input.specMaker);
466
+ const has = (source, clause) => source.includes(normalizeMarkdownWhitespace(clause));
467
+
468
+ const riskClauses = [
469
+ ADAPTIVE_COVERAGE_CLAUSES.riskFirst,
470
+ ADAPTIVE_COVERAGE_CLAUSES.criticalFloor,
471
+ ADAPTIVE_COVERAGE_CLAUSES.elevatedFloor,
472
+ ADAPTIVE_COVERAGE_CLAUSES.frontmatterGate,
473
+ ADAPTIVE_COVERAGE_CLAUSES.directGate,
474
+ ADAPTIVE_COVERAGE_CLAUSES.splitRoute,
475
+ ADAPTIVE_COVERAGE_CLAUSES.independentSubsystem
476
+ ];
477
+ const riskDowngrade = /\buser\b.{0,80}\b(?:may|can)\b.{0,80}\blower\b.{0,40}\brisk\b/i;
478
+ const riskyDirectOverride = /\b(?:exception|even if|regardless)\b.{0,160}\b(?:auth|secret|privacy|destructive|irreversible|data loss|corruption|production[- ]state|critical)\b.{0,160}\b(?:direct|work directly|go direct)\b/i;
479
+ if (riskClauses.some((clause) => !has(skill, clause))
480
+ || riskDowngrade.test(skill) || riskyDirectOverride.test(skill)) {
481
+ issues.add('risk-first-routing');
482
+ }
483
+
484
+ const profileTable = markdownTableUnderHeading(input.templates, 'Coverage profile');
485
+ const profileHeadingCount = (input.templates.match(/^## Coverage profile\s*$/gm) || []).length;
486
+ const taskReferencesCoverage = /## Coverage\s*\n- <exact `CP-NN` IDs owned by this task>/.test(input.templates);
487
+ if (JSON.stringify(profileTable[0] || []) !== JSON.stringify(ADAPTIVE_COVERAGE_PROFILE_HEADER)
488
+ || profileHeadingCount !== 1 || profileTable.length !== 2
489
+ || JSON.stringify(profileTable[1]) !== JSON.stringify(ADAPTIVE_COVERAGE_PROFILE_ROW)
490
+ || !taskReferencesCoverage
491
+ || !has(templates, ADAPTIVE_COVERAGE_CLAUSES.profileAuthority)
492
+ || !has(templates, ADAPTIVE_COVERAGE_CLAUSES.openKinds)) {
493
+ issues.add('coverage-profile-shape');
494
+ }
495
+
496
+ const ambiguityTable = markdownTableUnderHeading(input.templates, 'Example Mapping rule');
497
+ if (JSON.stringify(ambiguityTable) !== JSON.stringify(ADAPTIVE_COVERAGE_AMBIGUITY_ROWS)
498
+ || !templates.includes('retention of 30 versus 90 days is `decision-needed`')) {
499
+ issues.add('ambiguity-actions');
500
+ }
501
+
502
+ const globalCeremony = /\b(?:critical|security|failure|proof|review|edge|obligations?|lenses?)\b[^.!?\n]{0,120}\b(?:union|apply|spread|require)\w*\b[^.!?\n]{0,120}\b(?:all|every)\s+(?:cp\s+)?(?:rows?|outcomes?|tasks?)\b|\b(?:union|apply|spread)\w*\b[^.!?\n]{0,120}\b(?:all|every)\s+(?:cp\s+)?(?:rows?|outcomes?|tasks?)\b/i;
503
+ if (!has(templates, ADAPTIVE_COVERAGE_CLAUSES.scopedUnion)
504
+ || globalCeremony.test(templates)
505
+ || !review.includes('nonmaterial lenses are not added')) {
506
+ issues.add('scoped-coverage');
507
+ }
508
+
509
+ const rederiveSources = [templates, skill, specMaker, review];
510
+ if (!has(templates, ADAPTIVE_COVERAGE_CLAUSES.profileRederivation)
511
+ || rederiveSources.some((source) => !/rederive affected cp rows?/.test(source.toLowerCase()))) {
512
+ issues.add('profile-lifecycle');
513
+ }
514
+
515
+ const statusMatrix = markdownTableUnderHeading(input.templates, 'Status matrix');
516
+ if (!has(templates, ADAPTIVE_COVERAGE_CLAUSES.plannedProof)
517
+ || !has(templates, ADAPTIVE_COVERAGE_CLAUSES.proofSeparation)
518
+ || !has(templates, IMPLEMENTATION_READINESS_CLAUSES.proofLevelMapping)
519
+ || !has(skill, ADAPTIVE_COVERAGE_CLAUSES.liveLimit)
520
+ || !statusMatrix.some((row) => row[0] === 'accepted finding open or `UNKNOWN` reachability' && row[1] === '`blocked`')) {
521
+ issues.add('proof-lifecycle');
522
+ }
523
+
524
+ const reviewerRowsPresent = ADAPTIVE_REVIEWER_ROWS.every((row) =>
525
+ input.review.includes(`| ${row.join(' | ')} |`));
526
+ if (!has(review, ADAPTIVE_COVERAGE_CLAUSES.reviewRisk)
527
+ || !has(review, ADAPTIVE_COVERAGE_CLAUSES.reviewCapacity)
528
+ || !reviewerRowsPresent) {
529
+ issues.add('reviewer-routing');
530
+ }
531
+ if (!specMaker.includes('skills/specs/SKILL.md')
532
+ || !specMaker.includes('skills/specs/references/templates.md')
533
+ || !has(specMaker, ADAPTIVE_COVERAGE_CLAUSES.specMakerAuthority)
534
+ || !has(specMaker, ADAPTIVE_COVERAGE_CLAUSES.specMakerAmbiguity)
535
+ || !has(specMaker, ADAPTIVE_COVERAGE_CLAUSES.specMakerRoute)) {
536
+ issues.add('spec-maker-authority');
537
+ }
538
+
539
+ const boundaryTable = markdownTableUnderHeading(
540
+ input.templates,
541
+ 'No-invention and conditional boundary contracts'
542
+ );
543
+ if (!boundaryTableHasRequiredRows(boundaryTable)) {
544
+ issues.add('adaptive-boundary-lenses');
545
+ }
546
+ return [...issues].sort();
547
+ }
548
+
308
549
  function authoringProjectionIssues(files) {
309
550
  const issues = [];
310
- issues.push(...implementationReadinessIssues(files.templates, files.review));
551
+ issues.push(...implementationReadinessIssues({ templates: files.templates, review: files.review }));
311
552
  const foreignRuntimeBoundary = 'If you are Claude Code or any other runtime, ignore this entire Codex block';
312
553
  if (!files.skill.includes('Task files are flat beside `plan.md`')
313
554
  || !files.skill.includes('Do not create a nested task directory.')) {
@@ -334,7 +575,7 @@ function authoringProjectionIssues(files) {
334
575
  ]) {
335
576
  if (!files.templates.includes(field)) issues.push(`receipt-${field.toLowerCase()}`);
336
577
  }
337
- if (!/^name: hapo-specs$/m.test(files.skill)) issues.push('codex-skill-name');
578
+ if (!/^name: cf-specs$/m.test(files.skill)) issues.push('codex-skill-name');
338
579
  const bundle = [files.skill, files.review, files.templates, files.legacyTemplates].join('\n');
339
580
  const runtimeProjection = `${bundle}\n${files.codex.replace(foreignRuntimeBoundary, '')}`;
340
581
  if (!files.codex.includes(foreignRuntimeBoundary)
@@ -343,11 +584,11 @@ function authoringProjectionIssues(files) {
343
584
  }
344
585
  for (const claudeOnly of [
345
586
  'AskUserQuestion', 'TaskCreate', 'TaskGet', 'TaskUpdate', 'TaskList',
346
- 'WebSearch', 'WebFetch', 'SendMessage', 'Claude Code', '.claude', '/hapo:', 'hapo:'
587
+ 'WebSearch', 'WebFetch', 'SendMessage', 'Claude Code', '.claude', '/cf:', 'cf:'
347
588
  ]) {
348
589
  if (runtimeProjection.includes(claudeOnly)) issues.push(`claude-vocabulary-${claudeOnly}`);
349
590
  }
350
- for (const command of ['$hapo-specs', '$hapo-develop', '$hapo-sync']) {
591
+ for (const command of ['$cf-specs', '$cf-develop', '$cf-sync']) {
351
592
  if (!files.codex.includes(command)) issues.push(`codex-command-${command}`);
352
593
  }
353
594
  for (const state of ['pending', 'in_progress', 'paused', 'blocked', 'done']) {
@@ -379,10 +620,15 @@ function canonicalInstalledSpecsRoot(projectRoot, installedRoot) {
379
620
  function readInstalledAuthoringProjection(projectRoot, installedRoot) {
380
621
  const canonicalRoot = canonicalInstalledSpecsRoot(projectRoot, installedRoot);
381
622
  const read = (relative) => fs.readFileSync(path.join(canonicalRoot, relative), 'utf8');
623
+ const specMakerToml = fs.readFileSync(
624
+ path.join(projectRoot, '.codex/agents/spec_maker.toml'),
625
+ 'utf8'
626
+ );
382
627
  return {
383
628
  skill: read('SKILL.md'),
384
629
  review: read('references/review.md'),
385
630
  templates: read('references/templates.md'),
631
+ specMaker: parseGeneratedTomlString(specMakerToml, 'developer_instructions'),
386
632
  legacyTemplates: V3_SPECS_BUNDLE.filter((relative) => relative.startsWith('templates/')).map(read).join('\n'),
387
633
  codex: [
388
634
  fs.readFileSync(path.join(projectRoot, 'AGENTS.md'), 'utf8'),
@@ -395,21 +641,40 @@ function installedAuthoringProjectionIssues(projectRoot, installedRoot) {
395
641
  return authoringProjectionIssues(readInstalledAuthoringProjection(projectRoot, installedRoot));
396
642
  }
397
643
 
398
- function assertInstalledMutationTarget(projectRoot, installedRoot, relative) {
644
+ function installedAdaptiveCoverageIssues(projectRoot, installedRoot) {
645
+ const { skill, specMaker, templates, review } = readInstalledAuthoringProjection(
646
+ projectRoot,
647
+ installedRoot
648
+ );
649
+ return adaptiveCoverageIssues({ skill, specMaker, templates, review });
650
+ }
651
+
652
+ function assertDisposableInstalledMutationTarget({
653
+ projectRoot,
654
+ installedScope,
655
+ targetPath,
656
+ expectedTargetPath,
657
+ canonicalSourcePaths,
658
+ scopeLabel
659
+ }) {
399
660
  const canonicalProject = fs.realpathSync(projectRoot);
400
- const canonicalInstalled = canonicalInstalledSpecsRoot(projectRoot, installedRoot);
401
- const targetPath = path.join(canonicalInstalled, relative);
661
+ const canonicalScope = fs.realpathSync(installedScope);
402
662
  const targetLstat = fs.lstatSync(targetPath);
403
663
  assert.equal(targetLstat.isSymbolicLink(), false, 'installed mutation target must not be a symlink');
404
664
  assert.equal(targetLstat.isFile(), true, 'installed mutation target must be a regular file');
405
665
  const canonicalTarget = fs.realpathSync(targetPath);
406
- const installedRelative = path.relative(canonicalInstalled, canonicalTarget);
666
+ assert.equal(
667
+ canonicalTarget,
668
+ fs.realpathSync(expectedTargetPath),
669
+ `installed mutation target must be the exact ${scopeLabel} projection`
670
+ );
671
+ const scopeRelative = path.relative(canonicalScope, canonicalTarget);
407
672
  const projectRelative = path.relative(canonicalProject, canonicalTarget);
408
673
  assert.ok(
409
- installedRelative && installedRelative !== '..'
410
- && !installedRelative.startsWith(`..${path.sep}`)
411
- && !path.isAbsolute(installedRelative),
412
- 'installed mutation target must stay inside .agents/skills/specs'
674
+ scopeRelative && scopeRelative !== '..'
675
+ && !scopeRelative.startsWith(`..${path.sep}`)
676
+ && !path.isAbsolute(scopeRelative),
677
+ `installed mutation target must stay inside ${scopeLabel}`
413
678
  );
414
679
  assert.ok(
415
680
  projectRelative && projectRelative !== '..'
@@ -417,28 +682,257 @@ function assertInstalledMutationTarget(projectRoot, installedRoot, relative) {
417
682
  && !path.isAbsolute(projectRelative),
418
683
  'installed mutation target must stay inside the disposable project'
419
684
  );
420
- const canonicalSource = fs.realpathSync(path.join(SPECS_SOURCE_ROOT, relative));
421
- assert.notEqual(
422
- canonicalTarget,
423
- canonicalSource,
424
- 'installed mutation target must not resolve to canonical source'
685
+ assert.ok(
686
+ Array.isArray(canonicalSourcePaths) && canonicalSourcePaths.length > 0,
687
+ 'installed mutation guard requires the full canonical source set'
425
688
  );
426
689
  const targetStat = fs.statSync(canonicalTarget);
427
- const sourceStat = fs.statSync(canonicalSource);
428
- assert.notDeepEqual(
429
- [targetStat.dev, targetStat.ino],
430
- [sourceStat.dev, sourceStat.ino],
431
- 'installed mutation target must not share an inode with canonical source'
432
- );
690
+ for (const sourcePath of canonicalSourcePaths) {
691
+ const canonicalSource = fs.realpathSync(sourcePath);
692
+ assert.notEqual(
693
+ canonicalTarget,
694
+ canonicalSource,
695
+ 'installed mutation target must not resolve to any canonical source'
696
+ );
697
+ const sourceStat = fs.statSync(canonicalSource);
698
+ assert.notDeepEqual(
699
+ [targetStat.dev, targetStat.ino],
700
+ [sourceStat.dev, sourceStat.ino],
701
+ `installed mutation target must not share an inode with canonical source set: ${sourcePath}`
702
+ );
703
+ }
704
+ assert.equal(targetStat.nlink, 1, 'installed mutation target must have exactly one hard link');
433
705
  return canonicalTarget;
434
706
  }
435
707
 
708
+ function assertInstalledMutationTarget(projectRoot, installedRoot, relative) {
709
+ const canonicalInstalled = canonicalInstalledSpecsRoot(projectRoot, installedRoot);
710
+ const targetPath = path.join(canonicalInstalled, relative);
711
+ return assertDisposableInstalledMutationTarget({
712
+ projectRoot,
713
+ installedScope: canonicalInstalled,
714
+ targetPath,
715
+ expectedTargetPath: path.join(projectRoot, '.agents/skills/specs', relative),
716
+ canonicalSourcePaths: canonicalProjectionSourcePaths(),
717
+ scopeLabel: '.agents/skills/specs'
718
+ });
719
+ }
720
+
721
+ function assertInstalledSpecMakerMutationTarget(projectRoot) {
722
+ const installedScope = path.join(projectRoot, '.codex/agents');
723
+ const targetPath = path.join(installedScope, 'spec_maker.toml');
724
+ return assertDisposableInstalledMutationTarget({
725
+ projectRoot,
726
+ installedScope,
727
+ targetPath,
728
+ expectedTargetPath: targetPath,
729
+ canonicalSourcePaths: canonicalProjectionSourcePaths(),
730
+ scopeLabel: '.codex/agents'
731
+ });
732
+ }
733
+
734
+ function canonicalProjectionSourcePaths() {
735
+ return [
736
+ ...V3_SPECS_BUNDLE.map((relative) => path.join(SPECS_SOURCE_ROOT, relative)),
737
+ SPEC_MAKER_SOURCE_PATH
738
+ ];
739
+ }
740
+
741
+ function canonicalProjectionSourceBytes() {
742
+ const sourcePaths = canonicalProjectionSourcePaths();
743
+ return new Map(sourcePaths.map((sourcePath) => [
744
+ sourcePath,
745
+ fs.readFileSync(sourcePath)
746
+ ]));
747
+ }
748
+
749
+ function sha256(content) {
750
+ return createHash('sha256').update(content).digest('hex');
751
+ }
752
+
753
+ function canonicalBrainstormSourceBytes() {
754
+ return new Map(BRAINSTORM_SOURCE_PATHS.map((sourcePath) => [
755
+ sourcePath,
756
+ fs.readFileSync(sourcePath)
757
+ ]));
758
+ }
759
+
760
+ function assertCanonicalBrainstormSourceBytesUnchanged(sourceBytes) {
761
+ for (const [sourcePath, expected] of sourceBytes) {
762
+ const actual = fs.readFileSync(sourcePath);
763
+ assert.deepEqual(
764
+ actual,
765
+ expected,
766
+ `canonical Brainstorm source changed: ${path.relative(PACKAGE_ROOT, sourcePath)}`
767
+ );
768
+ assert.equal(sha256(actual), sha256(expected));
769
+ }
770
+ }
771
+
772
+ function readInstalledBrainstormProjection(projectRoot) {
773
+ const skillRoot = path.join(projectRoot, '.agents/skills/brainstorm');
774
+ const agentPath = path.join(projectRoot, '.codex/agents/brainstormer.toml');
775
+ return {
776
+ skill: fs.readFileSync(path.join(skillRoot, 'SKILL.md'), 'utf8'),
777
+ framework: fs.readFileSync(path.join(skillRoot, 'references/question-framework.md'), 'utf8'),
778
+ agent: parseGeneratedTomlString(fs.readFileSync(agentPath, 'utf8'), 'developer_instructions')
779
+ };
780
+ }
781
+
782
+ function brainstormProjectionClausePrefix(content, index) {
783
+ const starts = ['.', '!', '?', ';', '\n'].map((marker) => content.lastIndexOf(marker, index - 1));
784
+ return content.slice(Math.max(...starts) + 1, index).trim();
785
+ }
786
+
787
+ function brainstormProjectionIsLocallyNegated(content, index) {
788
+ const clausePrefix = brainstormProjectionClausePrefix(content, index);
789
+ const localBoundary = Math.max(
790
+ clausePrefix.lastIndexOf(','),
791
+ clausePrefix.lastIndexOf(':'),
792
+ clausePrefix.lastIndexOf('—')
793
+ );
794
+ const localPrefix = clausePrefix.slice(localBoundary + 1).trim().toLowerCase();
795
+ const directNegation = /\b(?:never|do not|does not|must not|may not|should not|will not|cannot|can't|is forbidden to|are forbidden to|is not (?:permitted|allowed) to|are not (?:permitted|allowed) to|not (?:permitted|allowed) to)(?:\s+(?:ever|directly|automatically|implicitly|explicitly|immediately|intentionally|silently))*\s*$/;
796
+ if (directNegation.test(localPrefix)) return true;
797
+ const fullPrefix = clausePrefix.toLowerCase();
798
+ if (/\b(?:but|yet|however|except|instead)\b[^.!?;\n]*$/.test(fullPrefix)) return false;
799
+ return /\b(?:never|do not|does not|must not|may not|should not|will not|cannot|can't|is forbidden to|are forbidden to|is not (?:permitted|allowed) to|are not (?:permitted|allowed) to|not (?:permitted|allowed) to)\b(?:(?!\b(?:but|yet|however|except|instead)\b).){0,160}\b(?:and|or|nor)(?:\s+then)?(?:\s+(?:ever|directly|automatically|implicitly|explicitly|immediately|intentionally|silently))*\s*$/.test(fullPrefix);
800
+ }
801
+
802
+ function brainstormProjectionHasUnauthorizedAuthority(content) {
803
+ const actionPattern = /\b(?:ask|question|contact|invoke|start|dispatch|launch|route|forward|run|execute|hand(?:\s+|-)?off|write|edit|update|mutate|delegate|claim)\b/gi;
804
+ const actions = [...content.matchAll(actionPattern)];
805
+ for (let index = 0; index < actions.length; index += 1) {
806
+ const match = actions[index];
807
+ const normalizedAction = match[0].toLowerCase().replace(/[-\s]+/g, ' ');
808
+ const action = normalizedAction === 'handoff' ? 'hand off' : normalizedAction;
809
+ const start = match.index + match[0].length;
810
+ const nextAction = actions[index + 1]?.index ?? content.length;
811
+ const punctuation = content.slice(start).search(/[.!?;\n]/);
812
+ const punctuationEnd = punctuation < 0 ? content.length : start + punctuation;
813
+ const tail = content.slice(start, Math.min(start + 100, nextAction, punctuationEnd));
814
+ const hasTarget = ['ask', 'question', 'contact'].includes(action)
815
+ ? /\b(?:the )?user\b/i.test(tail)
816
+ : ['invoke', 'start', 'dispatch', 'launch', 'route', 'forward', 'run', 'execute', 'hand off'].includes(action)
817
+ ? /\b(?:Specs|Fix|Develop)\b/i.test(tail)
818
+ : ['write', 'edit', 'update', 'mutate'].includes(action)
819
+ ? /\b(?:files?|task state|shared state)\b/i.test(tail)
820
+ : action === 'delegate'
821
+ ? /\b(?:work|tasks?)\b/i.test(tail)
822
+ : /\bapproval\b/i.test(tail);
823
+ if (hasTarget && !brainstormProjectionIsLocallyNegated(content, match.index)) return true;
824
+ }
825
+ return false;
826
+ }
827
+
828
+ function brainstormProjectionIssues(files) {
829
+ const compact = (value) => String(value).replace(/\s+/g, ' ').trim();
830
+ const skill = compact(files.skill);
831
+ const framework = compact(files.framework);
832
+ const agent = compact(files.agent);
833
+ const issues = new Set();
834
+ const specialistBoundary = 'Do not ask the user directly, write files, mutate shared task state, delegate work, invoke Specs/Fix/Develop, or claim approval.';
835
+ const specialistHandoff = 'Non-bug exploration may end in chat; feature/docs delivery may only prepare a future explicit Specs invocation; bug handoff requires evidenced root cause and the user\'s explicit fix request.';
836
+ const agentAuthorityRemainder = agent
837
+ .replace(specialistBoundary, '')
838
+ .replace(specialistHandoff, '');
839
+ const adaptiveClauses = {
840
+ 'adaptive-direct-precedence': 'Route Direct first, then apply controls only to requests that remain in Brainstorm.',
841
+ 'adaptive-ordered-depth': 'With no Deep signal, use Standard. `--deep` raises Standard to Deep.',
842
+ 'adaptive-leading-flags': 'Parse controls only from the leading consecutive token segment.',
843
+ 'adaptive-lens-trigger-skip': 'failure isolation for partial or cascading failure across boundaries',
844
+ 'adaptive-evidence-semantics': 'Missing evidence forces feasibility `unknown` and confidence `low`.',
845
+ 'adaptive-numeric-estimates': 'A numeric estimate requires range, unit, basis, evidence, and assumptions; otherwise report `unknown`.',
846
+ 'adaptive-pre-tool-redaction': 'Before an external visual tool or adviser handoff, minimize context and redact secrets, credentials, private keys, access tokens, and unnecessary PII.',
847
+ 'adaptive-adviser-gate': '`--advice` invokes `brainstormer` only after the material-choice gate;',
848
+ 'adaptive-decision-freshness': 'The first section records target identity, current source revision and worktree state or `[UNVERIFIED]`, an evidence-as-of value, and what change invalidates the brief.',
849
+ 'adaptive-non-authority': 'Neither overlay writes, approves, persists, dispatches, or completes work.'
850
+ };
851
+
852
+ if (!skill.includes('leave Brainstorm before scout, questions, approval, or persistence.')
853
+ || !skill.includes('Hydration is not a terminal route; continue to exactly one intent route below.')) {
854
+ issues.add('front-door-routing');
855
+ }
856
+ if (!skill.includes('Then use `cf-debug` until root cause is evidenced.')
857
+ || !skill.includes('Hand off to `cf-fix` only when the user explicitly requested a fix')) {
858
+ issues.add('bug-routing');
859
+ }
860
+ if (!skill.includes('Do not request design approval, persist a report, or invoke another workflow without a new explicit request.')) {
861
+ issues.add('exploration-stop');
862
+ }
863
+ if (!skill.includes('For a material choice, compare 2–3 mechanically distinct viable approaches')
864
+ || !skill.includes('Never create strawman options.')
865
+ || !agent.includes('never invent strawmen to fill a quota.')) {
866
+ issues.add('option-cardinality');
867
+ }
868
+ if (!agent.includes('You advise `cf-brainstorm`; you do not replace its routing, question, approval, persistence, or handoff ownership.')
869
+ || !agent.includes(specialistBoundary)
870
+ || brainstormProjectionHasUnauthorizedAuthority(agentAuthorityRemainder)) {
871
+ issues.add('specialist-boundary');
872
+ }
873
+ if (!agent.includes('If the request is a symptom without an evidenced root cause, return it to `cf-debug`.')
874
+ || !agent.includes('If the controller has not identified whether the work is feature delivery, an explicitly authorized fix, or non-bug exploration, request that routing context instead of guessing.')) {
875
+ issues.add('specialist-routing');
876
+ }
877
+ if (!agent.includes(specialistHandoff)) {
878
+ issues.add('specialist-handoff');
879
+ }
880
+ if (!skill.includes('Persist only approved decisions and semantics, only with user authority')
881
+ || !skill.includes('redact live secrets, credentials, private keys, access tokens, and unnecessary PII;')
882
+ || !framework.includes('Do not write "user selected" unless direct user text or the native input tool confirms it.')) {
883
+ issues.add('approval-persistence');
884
+ }
885
+ for (const [issue, clause] of Object.entries(adaptiveClauses)) {
886
+ if (!skill.includes(clause)) issues.add(issue);
887
+ }
888
+ const visible = `${skill}\n${framework}\n${agent}`;
889
+ if (/\bAskUserQuestion\b/.test(visible)
890
+ || /\b(?:a|an|one|each|every|another|this|that|the|[0-9]+)\s+a structured user-input request\b/i.test(visible)
891
+ || /\ban structured user-input request\b/i.test(visible)
892
+ || /\ba structured user-input request\s+(?:calls|batches)\b/i.test(visible)) {
893
+ issues.add('projection-grammar');
894
+ }
895
+ return [...issues].sort();
896
+ }
897
+
898
+ function assertInstalledBrainstormMutationTarget(projectRoot, source) {
899
+ const skillRoot = path.join(projectRoot, '.agents/skills/brainstorm');
900
+ if (source === 'agent') {
901
+ const agentRoot = path.join(projectRoot, '.codex/agents');
902
+ const targetPath = path.join(agentRoot, 'brainstormer.toml');
903
+ return assertDisposableInstalledMutationTarget({
904
+ projectRoot,
905
+ installedScope: agentRoot,
906
+ targetPath,
907
+ expectedTargetPath: targetPath,
908
+ canonicalSourcePaths: BRAINSTORM_SOURCE_PATHS,
909
+ scopeLabel: '.codex/agents'
910
+ });
911
+ }
912
+ const relative = source === 'skill' ? 'SKILL.md' : 'references/question-framework.md';
913
+ const targetPath = path.join(skillRoot, relative);
914
+ return assertDisposableInstalledMutationTarget({
915
+ projectRoot,
916
+ installedScope: skillRoot,
917
+ targetPath,
918
+ expectedTargetPath: targetPath,
919
+ canonicalSourcePaths: BRAINSTORM_SOURCE_PATHS,
920
+ scopeLabel: '.agents/skills/brainstorm'
921
+ });
922
+ }
923
+
436
924
  function assertCanonicalSpecsSourceBytesUnchanged(sourceBytes) {
437
- for (const [relative, expected] of sourceBytes) {
925
+ for (const [sourcePath, expected] of sourceBytes) {
926
+ const actual = fs.readFileSync(sourcePath);
438
927
  assert.deepEqual(
439
- fs.readFileSync(path.join(SPECS_SOURCE_ROOT, relative)),
928
+ actual,
440
929
  expected,
441
- `canonical Specs source bytes changed during installed mutation: ${relative}`
930
+ `canonical source bytes changed during installed mutation: ${path.relative(PACKAGE_ROOT, sourcePath)}`
931
+ );
932
+ assert.equal(
933
+ sha256(actual),
934
+ sha256(expected),
935
+ `canonical source SHA changed during installed mutation: ${path.relative(PACKAGE_ROOT, sourcePath)}`
442
936
  );
443
937
  }
444
938
  }
@@ -457,34 +951,94 @@ function assertInstalledAuthoringProjection(root, installedRoot) {
457
951
  const actual = fs.readFileSync(path.join(installedRoot, relative), 'utf8');
458
952
  assert.equal(actual, expected, `Codex Specs projection drifted: ${relative}`);
459
953
  }
954
+ const sourceSpecMaker = fs.readFileSync(SPEC_MAKER_SOURCE_PATH, 'utf8');
955
+ const installedSpecMakerPath = path.join(root, '.codex/agents/spec_maker.toml');
956
+ assert.equal(
957
+ fs.readFileSync(installedSpecMakerPath, 'utf8'),
958
+ convertCodexAgentContent(sourceSpecMaker, path.basename(SPEC_MAKER_SOURCE_PATH)),
959
+ 'Codex spec_maker projection drifted'
960
+ );
460
961
  for (const relative of OBSOLETE_SPECS_FILES) {
461
962
  assert.equal(fs.existsSync(path.join(installedRoot, relative)), false, `obsolete Specs file installed: ${relative}`);
462
963
  }
463
964
 
464
965
  const files = readInstalledAuthoringProjection(root, installedRoot);
465
- const sourceBytes = new Map(V3_SPECS_BUNDLE.map((relative) => [
466
- relative,
467
- fs.readFileSync(path.join(SPECS_SOURCE_ROOT, relative))
468
- ]));
966
+ const sourceBytes = canonicalProjectionSourceBytes();
469
967
  assert.deepEqual(installedAuthoringProjectionIssues(root, installedRoot), []);
968
+ assert.deepEqual(installedAdaptiveCoverageIssues(root, installedRoot), []);
470
969
  assertCanonicalSpecsSourceBytesUnchanged(sourceBytes);
471
970
 
472
971
  if (process.platform !== 'win32') {
473
- const relative = 'references/templates.md';
474
- const installedPath = path.join(installedRoot, relative);
475
- const backupPath = `${installedPath}.cafekit-backup`;
476
- const sourcePath = path.join(SPECS_SOURCE_ROOT, relative);
477
- fs.renameSync(installedPath, backupPath);
972
+ for (const [installedPath, sourcePath, assertTarget] of [
973
+ [
974
+ path.join(installedRoot, 'references/templates.md'),
975
+ path.join(SPECS_SOURCE_ROOT, 'references/templates.md'),
976
+ () => assertInstalledMutationTarget(root, installedRoot, 'references/templates.md')
977
+ ],
978
+ [
979
+ installedSpecMakerPath,
980
+ SPEC_MAKER_SOURCE_PATH,
981
+ () => assertInstalledSpecMakerMutationTarget(root)
982
+ ]
983
+ ]) {
984
+ const backupPath = `${installedPath}.cafekit-backup`;
985
+ fs.renameSync(installedPath, backupPath);
986
+ try {
987
+ fs.linkSync(sourcePath, installedPath);
988
+ assert.throws(assertTarget, /must not share an inode with canonical source/);
989
+ assertCanonicalSpecsSourceBytesUnchanged(sourceBytes);
990
+ } finally {
991
+ try { fs.unlinkSync(installedPath); } catch { /* best-effort disposable cleanup */ }
992
+ fs.renameSync(backupPath, installedPath);
993
+ }
994
+ }
995
+
996
+ const crossSourceRoot = path.join(root, 'cross-source-native-control');
997
+ const crossSourceProject = path.join(crossSourceRoot, 'project');
998
+ const crossSourceScope = path.join(crossSourceProject, '.agents/skills/specs');
999
+ const crossSourceTarget = path.join(crossSourceScope, 'target-a.md');
1000
+ const crossSourcePaths = [
1001
+ path.join(crossSourceRoot, 'sources/source-a.md'),
1002
+ path.join(crossSourceRoot, 'sources/source-b.md')
1003
+ ];
1004
+ fs.mkdirSync(crossSourceScope, { recursive: true });
1005
+ fs.mkdirSync(path.dirname(crossSourcePaths[0]), { recursive: true });
1006
+ fs.writeFileSync(crossSourceTarget, 'native installed target A\n');
1007
+ fs.writeFileSync(crossSourcePaths[0], 'native canonical source A\n');
1008
+ fs.writeFileSync(crossSourcePaths[1], 'native canonical source B\n');
1009
+ const targetBytes = fs.readFileSync(crossSourceTarget);
1010
+ const sourceState = new Map(crossSourcePaths.map((sourcePath) => {
1011
+ const bytes = fs.readFileSync(sourcePath);
1012
+ return [sourcePath, { bytes, sha: sha256(bytes) }];
1013
+ }));
1014
+ assert.notEqual(crossSourcePaths[0], crossSourcePaths[1], 'cross-source control requires A != B');
1015
+ fs.unlinkSync(crossSourceTarget);
1016
+ fs.linkSync(crossSourcePaths[1], crossSourceTarget);
478
1017
  try {
479
- fs.linkSync(sourcePath, installedPath);
480
1018
  assert.throws(
481
- () => assertInstalledMutationTarget(root, installedRoot, relative),
482
- /must not share an inode with canonical source/
1019
+ () => fs.writeFileSync(
1020
+ assertDisposableInstalledMutationTarget({
1021
+ projectRoot: crossSourceProject,
1022
+ installedScope: crossSourceScope,
1023
+ targetPath: crossSourceTarget,
1024
+ expectedTargetPath: crossSourceTarget,
1025
+ canonicalSourcePaths: crossSourcePaths,
1026
+ scopeLabel: '.agents/skills/specs'
1027
+ }),
1028
+ 'forbidden native cross-source mutation\n'
1029
+ ),
1030
+ /must not share an inode with canonical source set/
483
1031
  );
484
- assertCanonicalSpecsSourceBytesUnchanged(sourceBytes);
485
1032
  } finally {
486
- try { fs.unlinkSync(installedPath); } catch { /* best-effort disposable cleanup */ }
487
- fs.renameSync(backupPath, installedPath);
1033
+ fs.unlinkSync(crossSourceTarget);
1034
+ fs.writeFileSync(crossSourceTarget, targetBytes);
1035
+ fs.writeFileSync(crossSourcePaths[1], sourceState.get(crossSourcePaths[1]).bytes);
1036
+ }
1037
+ assert.deepEqual(fs.readFileSync(crossSourceTarget), targetBytes, 'cross-source control must restore exact target bytes');
1038
+ for (const [sourcePath, expected] of sourceState) {
1039
+ const actual = fs.readFileSync(sourcePath);
1040
+ assert.deepEqual(actual, expected.bytes, 'cross-source rejection must preserve every source byte');
1041
+ assert.equal(sha256(actual), expected.sha, 'cross-source rejection must preserve every source SHA');
488
1042
  }
489
1043
  }
490
1044
 
@@ -514,6 +1068,13 @@ function assertInstalledAuthoringProjection(root, installedRoot) {
514
1068
  from: IMPLEMENTATION_READINESS_CLAUSES.noInvention,
515
1069
  to: 'Before implementation handoff, note ambiguous choices without blocking handoff.'
516
1070
  },
1071
+ {
1072
+ name: 'no-invention-contradictory-override',
1073
+ issue: 'no-invention',
1074
+ relative: 'references/templates.md',
1075
+ from: IMPLEMENTATION_READINESS_CLAUSES.noInvention,
1076
+ to: `${IMPLEMENTATION_READINESS_CLAUSES.noInvention}\n\nException: implementation handoff may proceed with an unresolved material choice.`
1077
+ },
517
1078
  {
518
1079
  name: 'material-boundary-definition',
519
1080
  issue: 'boundary-contract',
@@ -528,16 +1089,23 @@ function assertInstalledAuthoringProjection(root, installedRoot) {
528
1089
  from: IMPLEMENTATION_READINESS_CLAUSES.exactBoundaryChoices,
529
1090
  to: 'For every required row, describe the listed choices generally.'
530
1091
  },
1092
+ boundaryMutation('interaction-accessibility', 'Interaction/UI', [
1093
+ ['input/focus/keyboard; ', ''],
1094
+ ['accessibility; ', '']
1095
+ ]),
531
1096
  boundaryMutation('api-success-and-error-semantics', 'API/CLI', [
532
1097
  ['success output; ', ''],
533
1098
  ['error/status/exit; ', '']
534
1099
  ]),
535
- boundaryMutation('schema-shape-and-unknown-fields', 'Schema', [
1100
+ boundaryMutation('schema-shape-and-unknown-fields', 'Data/schema', [
536
1101
  ['exact keys/nesting/types; ', ''],
537
1102
  ['unknown-field behavior; ', '']
538
1103
  ]),
539
- boundaryMutation('state-lock-lifecycle', 'State/concurrency', [
540
- ['writer/lock acquire/contention/release', 'writer/lock']
1104
+ boundaryMutation('schema-enum-and-format', 'Data/schema', [
1105
+ ['enum/format/', '']
1106
+ ]),
1107
+ boundaryMutation('state-lock-lifecycle', 'Async/state', [
1108
+ ['writer/lock acquire/contention/release; ', 'writer/lock; ']
541
1109
  ]),
542
1110
  boundaryMutation('filesystem-segment-grammar', 'Filesystem/security', [
543
1111
  ['trusted/untrusted segment grammar; ', '']
@@ -545,11 +1113,19 @@ function assertInstalledAuthoringProjection(root, installedRoot) {
545
1113
  boundaryMutation('filesystem-stale-lock-reclaim', 'Filesystem/security', [
546
1114
  ['lock/stale reclaim; ', '']
547
1115
  ]),
1116
+ boundaryMutation('runtime-rollout-and-recovery', 'Runtime/deploy', [
1117
+ ['rollout/rollback; ', ''],
1118
+ ['operator recovery', '']
1119
+ ]),
548
1120
  boundaryMutation('retention-clock-and-endpoints', 'Time/retention', [
549
1121
  ['clock source; ', ''],
550
1122
  ['unit/precision/timezone; ', ''],
551
1123
  ['endpoints and inclusion/comparator; ', '']
552
1124
  ]),
1125
+ boundaryMutation('ai-model-safety-and-eval', 'AI/model', [
1126
+ ['safety/privacy; ', ''],
1127
+ ['eval oracle', '']
1128
+ ]),
553
1129
  boundaryMutation('proof-level-partition', 'Integration/proof', [
554
1130
  ['proof level (`source`/`installed`/`live`)', 'proof level']
555
1131
  ]),
@@ -595,6 +1171,13 @@ function assertInstalledAuthoringProjection(root, installedRoot) {
595
1171
  from: IMPLEMENTATION_READINESS_CLAUSES.disposableReviewControls,
596
1172
  to: 'Run mutation or destructive negative controls against the available project copy.'
597
1173
  },
1174
+ {
1175
+ name: 'required-proof-level-mapping',
1176
+ issue: 'proof-chain',
1177
+ relative: 'references/templates.md',
1178
+ from: IMPLEMENTATION_READINESS_CLAUSES.proofLevelMapping,
1179
+ to: 'Map one required proof level to one probe; other levels may remain implicit.'
1180
+ },
598
1181
  {
599
1182
  name: 'artifact-path-and-digest-or-ephemeral',
600
1183
  issue: 'proof-chain',
@@ -652,6 +1235,7 @@ function assertInstalledAuthoringProjection(root, installedRoot) {
652
1235
  to: 'Any privacy/security claim names the sensitive data at risk.'
653
1236
  }
654
1237
  ];
1238
+ assert.equal(readinessMutations.length, 30, 'installed readiness mutation set must match canonical source');
655
1239
 
656
1240
  for (const { name, issue, relative, from, to } of readinessMutations) {
657
1241
  const target = assertInstalledMutationTarget(root, installedRoot, relative);
@@ -675,6 +1259,152 @@ function assertInstalledAuthoringProjection(root, installedRoot) {
675
1259
  assertCanonicalSpecsSourceBytesUnchanged(sourceBytes);
676
1260
  }
677
1261
 
1262
+ const integrationRow = IMPLEMENTATION_READINESS_BOUNDARY_ROWS
1263
+ .find(([label]) => label === 'Integration/proof');
1264
+ const integrationLine = `| ${integrationRow.join(' | ')} |`;
1265
+ const templatesTarget = assertInstalledMutationTarget(
1266
+ root,
1267
+ installedRoot,
1268
+ 'references/templates.md'
1269
+ );
1270
+ const templatesBytes = fs.readFileSync(templatesTarget, 'utf8');
1271
+ const extendedTemplates = templatesBytes.replace(
1272
+ integrationLine,
1273
+ `${integrationLine}\n| other:domain-specific | task-specific material choices and observable oracle |`
1274
+ );
1275
+ assert.notEqual(extendedTemplates, templatesBytes, 'open-boundary positive control must change installed templates');
1276
+ try {
1277
+ fs.writeFileSync(assertInstalledMutationTarget(root, installedRoot, 'references/templates.md'), extendedTemplates);
1278
+ assert.deepEqual(installedAuthoringProjectionIssues(root, installedRoot), []);
1279
+ assertCanonicalSpecsSourceBytesUnchanged(sourceBytes);
1280
+ } finally {
1281
+ fs.writeFileSync(assertInstalledMutationTarget(root, installedRoot, 'references/templates.md'), templatesBytes);
1282
+ }
1283
+ assert.deepEqual(installedAuthoringProjectionIssues(root, installedRoot), [], 'open-boundary restore');
1284
+ assertCanonicalSpecsSourceBytesUnchanged(sourceBytes);
1285
+
1286
+ const adaptiveMutations = [
1287
+ ['frontmatter-risk-bypass', 'skill', ADAPTIVE_COVERAGE_CLAUSES.frontmatterGate,
1288
+ 'skip for any clear one-file or two-file change.', ['risk-first-routing']],
1289
+ ['destructive-routine-direct', 'skill', ADAPTIVE_COVERAGE_CLAUSES.directGate,
1290
+ 'Work directly when the change is routine and likely limited to one or two files.', ['risk-first-routing']],
1291
+ ['destructive-direct-exception', 'skill', ADAPTIVE_COVERAGE_CLAUSES.directGate,
1292
+ `${ADAPTIVE_COVERAGE_CLAUSES.directGate} Exception: destructive one-file work labeled routine may go direct.`, ['risk-first-routing']],
1293
+ ['user-risk-downgrade', 'skill', ADAPTIVE_COVERAGE_CLAUSES.riskFirst,
1294
+ `${ADAPTIVE_COVERAGE_CLAUSES.riskFirst} A user may lower critical risk to routine.`, ['risk-first-routing']],
1295
+ ['four-subsystem-split', 'skill', ADAPTIVE_COVERAGE_CLAUSES.splitRoute,
1296
+ 'Split four or more independent subsystems; otherwise use one Specs packet for substantial work.', ['risk-first-routing']],
1297
+ ['forced-single-kind', 'templates', ADAPTIVE_COVERAGE_CLAUSES.openKinds,
1298
+ 'Choose one primary change kind and ignore unfamiliar kinds or surfaces.', ['coverage-profile-shape']],
1299
+ ['missing-profile-column', 'templates', '| ID | Outcome | Change kinds | Material surfaces | Ambiguity/action | Risk/evidence | Required proof |',
1300
+ '| ID | Outcome | Change kinds | Material surfaces | Risk/evidence | Required proof |', ['coverage-profile-shape']],
1301
+ ['duplicate-profile-heading', 'templates', '## Coverage profile\n',
1302
+ '## Coverage profile\n\n## Coverage profile\n', ['coverage-profile-shape']],
1303
+ ['truncated-profile-row', 'templates', `| ${ADAPTIVE_COVERAGE_PROFILE_ROW.join(' | ')} |`,
1304
+ '| CP-01 | <externally observable outcome> |', ['coverage-profile-shape']],
1305
+ ['examples-promote-to-design', 'templates', `| ${ADAPTIVE_COVERAGE_AMBIGUITY_ROWS[2].join(' | ')} |`,
1306
+ '| `examples-needed` | add examples and promote to `design-needed` if behavior changes |', ['ambiguity-actions']],
1307
+ ['examples-select-retention', 'templates', 'retention of 30 versus 90 days is\n`decision-needed`',
1308
+ 'retention of 30 versus 90 days may remain\n`examples-needed`', ['ambiguity-actions']],
1309
+ ['global-critical-ceremony', 'templates', ADAPTIVE_COVERAGE_CLAUSES.scopedUnion,
1310
+ 'Each task copies its CP values; authoring, review, edge, and proof obligations union across every task.', ['scoped-coverage']],
1311
+ ['scoped-union-contradiction', 'templates', ADAPTIVE_COVERAGE_CLAUSES.scopedUnion,
1312
+ `${ADAPTIVE_COVERAGE_CLAUSES.scopedUnion} Exception: critical proof obligations apply across every CP row.`, ['scoped-coverage']],
1313
+ ['stale-profile-after-c2', 'templates', ADAPTIVE_COVERAGE_CLAUSES.profileRederivation,
1314
+ 'Keep existing coverage rows after accepted plan changes.', ['profile-lifecycle']],
1315
+ ['planned-proof-blocks-start', 'templates', ADAPTIVE_COVERAGE_CLAUSES.plannedProof,
1316
+ '`Required proof` is execution evidence: known but unrun proof blocks `pending`; `UNKNOWN` may proceed; missing evidence may still reach `done`/C3.', ['proof-lifecycle']],
1317
+ ['source-promotes-live', 'templates', ADAPTIVE_COVERAGE_CLAUSES.proofSeparation,
1318
+ 'Source proof may promote installed and live proof.', ['proof-lifecycle']],
1319
+ ['unmapped-proof-level', 'templates', IMPLEMENTATION_READINESS_CLAUSES.proofLevelMapping,
1320
+ 'A task may map only one required proof level to its probe.', ['proof-lifecycle']],
1321
+ ['critical-reviewer-omitted', 'review', ADAPTIVE_COVERAGE_CLAUSES.reviewRisk,
1322
+ 'Keep Fact Checker as the baseline and choose any remaining reviewer; critical rows need no matching risk role.', ['reviewer-routing', 'scoped-coverage']],
1323
+ ['reviewer-lens-overflow', 'review', ADAPTIVE_COVERAGE_CLAUSES.reviewCapacity,
1324
+ 'Each reviewer owns exactly one lens; skip excess material lenses when the fixed reviewer count is full.', ['reviewer-routing']],
1325
+ ['spec-maker-local-taxonomy', 'specMaker', ADAPTIVE_COVERAGE_CLAUSES.specMakerAuthority,
1326
+ 'this agent owns a separate risk and coverage taxonomy.', ['spec-maker-authority']],
1327
+ ['spec-maker-examples-decide', 'specMaker', ADAPTIVE_COVERAGE_CLAUSES.specMakerAmbiguity,
1328
+ 'Use examples to settle every ambiguous observable behavior.', ['spec-maker-authority']],
1329
+ ['spec-maker-skips-brainstorm', 'specMaker', ADAPTIVE_COVERAGE_CLAUSES.specMakerRoute,
1330
+ 'Apply the risk-first route before C1 and stop only for direct work.', ['spec-maker-authority']],
1331
+ ['static-proves-live', 'skill', ADAPTIVE_COVERAGE_CLAUSES.liveLimit,
1332
+ 'Source/static checks prove live-model adherence.', ['proof-lifecycle']]
1333
+ ];
1334
+ assert.equal(adaptiveMutations.length, 23, 'installed adaptive mutation set must match canonical source');
1335
+
1336
+ const adaptiveTarget = (source) => {
1337
+ if (source === 'specMaker') {
1338
+ const target = assertInstalledSpecMakerMutationTarget(root);
1339
+ const bytes = fs.readFileSync(target);
1340
+ const content = bytes.toString('utf8');
1341
+ return {
1342
+ target,
1343
+ bytes,
1344
+ content: parseGeneratedTomlString(content, 'developer_instructions'),
1345
+ render: (value) => replaceGeneratedTomlString(content, 'developer_instructions', value)
1346
+ };
1347
+ }
1348
+ const relative = {
1349
+ skill: 'SKILL.md',
1350
+ templates: 'references/templates.md',
1351
+ review: 'references/review.md'
1352
+ }[source];
1353
+ assert.ok(relative, `unknown adaptive mutation source: ${source}`);
1354
+ const target = assertInstalledMutationTarget(root, installedRoot, relative);
1355
+ const bytes = fs.readFileSync(target);
1356
+ return { target, bytes, content: bytes.toString('utf8'), render: (value) => value };
1357
+ };
1358
+
1359
+ for (const [name, source, from, to, expected] of adaptiveMutations) {
1360
+ const targetState = adaptiveTarget(source);
1361
+ const anchorIndex = targetState.content.indexOf(from);
1362
+ assert.ok(anchorIndex >= 0, `${name} mutation anchor must exist in installed bytes`);
1363
+ assert.equal(
1364
+ targetState.content.indexOf(from, anchorIndex + from.length),
1365
+ -1,
1366
+ `${name} mutation anchor must be unique in installed bytes`
1367
+ );
1368
+ const weakened = `${targetState.content.slice(0, anchorIndex)}${to}${targetState.content.slice(anchorIndex + from.length)}`;
1369
+ try {
1370
+ fs.writeFileSync(adaptiveTarget(source).target, targetState.render(weakened));
1371
+ assertCanonicalSpecsSourceBytesUnchanged(sourceBytes);
1372
+ assert.deepEqual(installedAdaptiveCoverageIssues(root, installedRoot), [...expected].sort(), name);
1373
+ } finally {
1374
+ fs.writeFileSync(adaptiveTarget(source).target, targetState.bytes);
1375
+ }
1376
+ assert.deepEqual(fs.readFileSync(adaptiveTarget(source).target), targetState.bytes, `${name} byte restore`);
1377
+ assert.deepEqual(installedAdaptiveCoverageIssues(root, installedRoot), [], `${name} restore`);
1378
+ assertCanonicalSpecsSourceBytesUnchanged(sourceBytes);
1379
+ }
1380
+
1381
+ const intactAdaptive = readInstalledAuthoringProjection(root, installedRoot);
1382
+ const adaptivePositiveControls = [
1383
+ ['open material surface', intactAdaptive.templates.replace(
1384
+ integrationLine,
1385
+ `${integrationLine}\n| other:domain-specific | task-specific material choices and observable oracle |`
1386
+ )],
1387
+ ['task CP reference requirement', intactAdaptive.templates.replace(
1388
+ ADAPTIVE_COVERAGE_CLAUSES.scopedUnion,
1389
+ `${ADAPTIVE_COVERAGE_CLAUSES.scopedUnion} Require every task to reference a CP row.`
1390
+ )]
1391
+ ];
1392
+ for (const [name, templates] of adaptivePositiveControls) {
1393
+ assert.notEqual(templates, intactAdaptive.templates, `${name} positive control must change installed templates`);
1394
+ const target = assertInstalledMutationTarget(root, installedRoot, 'references/templates.md');
1395
+ const original = fs.readFileSync(target);
1396
+ try {
1397
+ fs.writeFileSync(target, templates);
1398
+ assertCanonicalSpecsSourceBytesUnchanged(sourceBytes);
1399
+ assert.deepEqual(installedAdaptiveCoverageIssues(root, installedRoot), [], name);
1400
+ } finally {
1401
+ fs.writeFileSync(assertInstalledMutationTarget(root, installedRoot, 'references/templates.md'), original);
1402
+ }
1403
+ assert.deepEqual(fs.readFileSync(target), original, `${name} byte restore`);
1404
+ assert.deepEqual(installedAdaptiveCoverageIssues(root, installedRoot), [], `${name} restore`);
1405
+ assertCanonicalSpecsSourceBytesUnchanged(sourceBytes);
1406
+ }
1407
+
678
1408
  const mutations = [
679
1409
  ['skill', (value) => value.replace('Task files are flat beside `plan.md`', 'Task files may be nested')],
680
1410
  ['review', (value) => value.replace('list at 15', 'list without a cap')],
@@ -685,6 +1415,12 @@ function assertInstalledAuthoringProjection(root, installedRoot) {
685
1415
  const changed = { ...files, [key]: mutate(files[key]) };
686
1416
  assert.notDeepEqual(authoringProjectionIssues(changed), []);
687
1417
  }
1418
+ assert.equal(
1419
+ fs.readFileSync(installedSpecMakerPath, 'utf8'),
1420
+ convertCodexAgentContent(sourceSpecMaker, path.basename(SPEC_MAKER_SOURCE_PATH)),
1421
+ 'Codex spec_maker projection must be byte-exact after mutation restores'
1422
+ );
1423
+ assertCanonicalSpecsSourceBytesUnchanged(sourceBytes);
688
1424
  }
689
1425
 
690
1426
  test('Codex payload transform emits native skill and subagent syntax', () => {
@@ -697,13 +1433,165 @@ test('Codex payload transform emits native skill and subagent syntax', () => {
697
1433
  transformed,
698
1434
  /spawn_agent\(agent_type="implementer", fork_turns="none", message="Implement it", task_name="code_feature"\)/
699
1435
  );
700
- assert.match(transformed, /\$hapo-specs auth/);
1436
+ assert.match(transformed, /\$cf-specs auth/);
701
1437
  assert.match(transformed, /`send_message`/);
702
1438
  assert.match(transformed, /`exec_command`/);
703
1439
  assert.match(transformed, /`apply_patch`/);
704
1440
  assert.doesNotMatch(transformed, /Agent\(|subagent_type|\/specs\b|Claude Code/);
705
1441
  });
706
1442
 
1443
+ test('Codex payload transform keeps structured user-input grammar across determiners', () => {
1444
+ const cases = [
1445
+ ['standalone bare token', 'AskUserQuestion', 'a structured user-input request'],
1446
+ ['standalone code token', '`AskUserQuestion`', 'a structured user-input request'],
1447
+ ['standalone multi-backtick token', '``AskUserQuestion``', 'a structured user-input request'],
1448
+ ['standalone padded multi-backtick token', '`` AskUserQuestion ``', 'a structured user-input request'],
1449
+ ['one', 'one AskUserQuestion call', 'one structured user-input request call'],
1450
+ ['one multi-backtick', 'one ``AskUserQuestion`` call', 'one structured user-input request call'],
1451
+ ['one padded multi-backtick', 'one `` AskUserQuestion `` call', 'one structured user-input request call'],
1452
+ ['each', 'each AskUserQuestion call', 'each structured user-input request call'],
1453
+ ['every', 'every AskUserQuestion batch', 'every structured user-input request batch'],
1454
+ ['another', 'another AskUserQuestion call', 'another structured user-input request call'],
1455
+ ['this', 'this AskUserQuestion call', 'this structured user-input request call'],
1456
+ ['that', 'that AskUserQuestion call', 'that structured user-input request call'],
1457
+ ['a', 'a AskUserQuestion call', 'a structured user-input request call'],
1458
+ ['an', 'an AskUserQuestion call', 'a structured user-input request call'],
1459
+ ['capitalized each', 'Each AskUserQuestion call', 'Each structured user-input request call'],
1460
+ ['capitalized an', 'An AskUserQuestion call', 'A structured user-input request call'],
1461
+ ['the', 'the AskUserQuestion call', 'the structured user-input request call'],
1462
+ ['numeric', '27 AskUserQuestion calls', '27 structured user-input request calls'],
1463
+ ['plural calls', 'AskUserQuestion calls', 'structured user-input request calls'],
1464
+ ['plural batches', 'AskUserQuestion batches', 'structured user-input request batches']
1465
+ ];
1466
+
1467
+ for (const [name, input, expected] of cases) {
1468
+ const actual = normalizeCodexBody(input, '/fixture/instructions.md');
1469
+ assert.equal(actual, expected, name);
1470
+ assert.equal(normalizeCodexBody(actual, '/fixture/instructions.md'), expected, `${name} idempotence`);
1471
+ assert.doesNotMatch(actual, /\bAskUserQuestion\b/, `${name} Claude-only token`);
1472
+ assert.doesNotMatch(actual, /\ban structured user-input request\b/i, `${name} incompatible article`);
1473
+ assert.doesNotMatch(
1474
+ actual,
1475
+ /\b(?:a|an|one|each|every|another|this|that|the|[0-9]+)\s+a structured user-input request\b/i,
1476
+ `${name} doubled article`
1477
+ );
1478
+ }
1479
+
1480
+ assert.equal(
1481
+ normalizeCodexBody('one\nAskUserQuestion call', '/fixture/instructions.md'),
1482
+ 'one\nstructured user-input request call',
1483
+ 'a single Markdown soft break must preserve its determiner relationship'
1484
+ );
1485
+ assert.equal(
1486
+ normalizeCodexBody('AskUserQuestion\nCalls', '/fixture/instructions.md'),
1487
+ 'structured user-input request\nCalls',
1488
+ 'a single Markdown soft break must preserve a plural relationship'
1489
+ );
1490
+ assert.equal(
1491
+ normalizeCodexBody('one\n\nAskUserQuestion call', '/fixture/instructions.md'),
1492
+ 'one\n\na structured user-input request call',
1493
+ 'a paragraph break must not bind a prior determiner'
1494
+ );
1495
+ assert.equal(
1496
+ normalizeCodexBody('MyAskUserQuestionHelper AskUserQuestionFactory', '/fixture/instructions.md'),
1497
+ 'MyAskUserQuestionHelper AskUserQuestionFactory',
1498
+ 'balanced token matching must preserve identifiers'
1499
+ );
1500
+ assert.equal(
1501
+ normalizeCodexBody('[tool](https://example.test/AskUserQuestion)', '/fixture/instructions.md'),
1502
+ '[tool](https://example.test/AskUserQuestion)',
1503
+ 'Markdown link destinations must stay byte-exact'
1504
+ );
1505
+ assert.equal(
1506
+ normalizeCodexBody('https://example.test/AskUserQuestion', '/fixture/instructions.md'),
1507
+ 'https://example.test/AskUserQuestion',
1508
+ 'raw URL tokens must stay byte-exact'
1509
+ );
1510
+ assert.equal(
1511
+ normalizeCodexBody('one ``AskUserQuestion``` call', '/fixture/instructions.md'),
1512
+ 'one ``AskUserQuestion``` call',
1513
+ 'mismatched backtick runs must stay byte-exact'
1514
+ );
1515
+ assert.equal(
1516
+ normalizeCodexBody('``Use AskUserQuestion here``', '/fixture/instructions.md'),
1517
+ '``Use AskUserQuestion here``',
1518
+ 'non-token code spans must stay byte-exact'
1519
+ );
1520
+ assert.equal(
1521
+ normalizeCodexBody('```js\nAskUserQuestion\n````', '/fixture/instructions.md'),
1522
+ '```js\nAskUserQuestion\n````',
1523
+ 'a longer closing backtick fence must protect its body'
1524
+ );
1525
+ assert.equal(
1526
+ normalizeCodexBody('~~~text\nAskUserQuestion\n~~~~', '/fixture/instructions.md'),
1527
+ '~~~text\nAskUserQuestion\n~~~~',
1528
+ 'a longer closing tilde fence must protect its body'
1529
+ );
1530
+ const markerCollision = '\uE000CAFEKIT_0_ASK_999\uE001';
1531
+ assert.equal(
1532
+ normalizeCodexBody(markerCollision, '/fixture/instructions.md'),
1533
+ markerCollision,
1534
+ 'literal internal-marker-shaped input must stay byte-exact'
1535
+ );
1536
+ const codeMarkerCollision = '\uE000CAFEKIT_0_CODE_0\uE001 and ``not token``';
1537
+ assert.equal(
1538
+ normalizeCodexBody(codeMarkerCollision, '/fixture/instructions.md'),
1539
+ codeMarkerCollision,
1540
+ 'mask restoration must not replace a literal marker-shaped prefix'
1541
+ );
1542
+ assert.equal(
1543
+ normalizeCodexBody('one AskUserQuestion call', '/fixture/session.cjs'),
1544
+ 'one AskUserQuestion call',
1545
+ 'non-instruction assets must preserve executable text'
1546
+ );
1547
+ assert.equal(
1548
+ normalizeCodexBody('No structured input token here.', '/fixture/instructions.md'),
1549
+ 'No structured input token here.',
1550
+ 'token-free instruction text must stay byte-exact'
1551
+ );
1552
+ });
1553
+
1554
+ test('Codex structured-input corpus oracle stays differential and production-aware', () => {
1555
+ const sourceRoot = path.join(PACKAGE_ROOT, 'src/claude');
1556
+ const occurrenceFiles = allFiles(sourceRoot, (file) => /\.(?:md|mdx|txt|cjs|js)$/i.test(file))
1557
+ .filter((file) => fs.readFileSync(file, 'utf8').includes('AskUserQuestion'));
1558
+ const relative = (file) => path.relative(PACKAGE_ROOT, file).split(path.sep).join('/');
1559
+ const actualPaths = occurrenceFiles.map(relative).sort();
1560
+ const expectedPaths = [
1561
+ 'src/claude/agents/spec-maker.md',
1562
+ 'src/claude/hooks/session.cjs',
1563
+ 'src/claude/skills/git/SKILL.md',
1564
+ 'src/claude/skills/inspect/SKILL.md'
1565
+ ];
1566
+ assert.deepEqual(actualPaths, expectedPaths, 'every source occurrence needs an explicit projection oracle');
1567
+
1568
+ const instructionExpectedSnippets = new Map([
1569
+ ['src/claude/skills/git/SKILL.md', 'Present options via a structured user-input request — header'],
1570
+ ['src/claude/skills/inspect/SKILL.md', '**Fallback to a structured user-input request:**']
1571
+ ]);
1572
+ for (const [relativePath, expectedSnippet] of instructionExpectedSnippets) {
1573
+ const sourcePath = path.join(PACKAGE_ROOT, relativePath);
1574
+ const source = fs.readFileSync(sourcePath, 'utf8');
1575
+ const sentinel = '__CAFEKIT_STRUCTURED_INPUT_SENTINEL__';
1576
+ const withSentinel = source.replace(/`?\bAskUserQuestion\b`?/g, sentinel);
1577
+ assert.notEqual(withSentinel, source, `${relativePath} differential control must replace a token`);
1578
+ const expected = normalizeCodexBody(withSentinel, sourcePath)
1579
+ .replaceAll(sentinel, 'a structured user-input request');
1580
+ const actual = normalizeCodexBody(source, sourcePath);
1581
+ assert.equal(actual, expected, `${relativePath} independent differential oracle`);
1582
+ assert.match(actual, new RegExp(expectedSnippet.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
1583
+ }
1584
+
1585
+ const specMaker = fs.readFileSync(SPEC_MAKER_SOURCE_PATH, 'utf8');
1586
+ const convertedAgent = convertCodexAgentContent(specMaker, path.basename(SPEC_MAKER_SOURCE_PATH));
1587
+ assert.doesNotMatch(convertedAgent, /\bAskUserQuestion\b/);
1588
+ assert.doesNotMatch(convertedAgent, /structured user-input request/);
1589
+
1590
+ const sessionPath = path.join(PACKAGE_ROOT, 'src/claude/hooks/session.cjs');
1591
+ const session = normalizeCodexBody(fs.readFileSync(sessionPath, 'utf8'), sessionPath);
1592
+ assert.equal((session.match(/\bAskUserQuestion\b/g) || []).length, 2);
1593
+ });
1594
+
707
1595
  test('Codex payload transform preserves executable keyword arguments', () => {
708
1596
  const transformed = normalizeCodexBody(
709
1597
  'parser = ArgumentParser(description="Analyze it")\n' +
@@ -1046,6 +1934,43 @@ test('Codex dry-run leaves both managed roots untouched', () => {
1046
1934
  });
1047
1935
  });
1048
1936
 
1937
+ test('Codex install preserves the adaptive diagnostic-only Debug contract', () => {
1938
+ inTempProject((root) => {
1939
+ const result = install(root);
1940
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
1941
+
1942
+ const installedSkill = fs.readFileSync(
1943
+ path.join(root, '.agents/skills/debug/SKILL.md'), 'utf8'
1944
+ );
1945
+ assert.match(installedSkill, /^name: cf-debug$/m);
1946
+ assert.match(installedSkill, /## Proportional depth/);
1947
+ assert.match(installedSkill, /Evidence Timeline/);
1948
+ assert.match(installedSkill, /### Elimination Path/);
1949
+ assert.match(installedSkill, /### Recurrence-Prevention Handoff/);
1950
+ assert.match(installedSkill, /`cf-debug` is read-only for product code/);
1951
+
1952
+ const installedAgent = fs.readFileSync(
1953
+ path.join(root, '.codex/agents/debugger.toml'), 'utf8'
1954
+ );
1955
+ assert.equal(
1956
+ installedAgent,
1957
+ convertCodexAgentContent(fs.readFileSync(DEBUG_AGENT_SOURCE_PATH, 'utf8'), 'debugger.md')
1958
+ );
1959
+ assert.match(installedAgent, /Never implement the repair/);
1960
+
1961
+ for (const relative of DEBUG_REFERENCE_FILES) {
1962
+ const source = fs.readFileSync(
1963
+ path.join(PACKAGE_ROOT, 'src/claude/references/debugger', relative), 'utf8'
1964
+ );
1965
+ const installed = fs.readFileSync(
1966
+ path.join(root, '.codex/references/debugger', relative), 'utf8'
1967
+ );
1968
+ assert.equal(installed, normalizeCodexBody(source, relative), relative);
1969
+ }
1970
+ assert.equal(fs.existsSync(DEBUG_SKILL_SOURCE_PATH), true);
1971
+ });
1972
+ });
1973
+
1049
1974
  test('Codex Windows hook launchers stay project-bound without Git from nested cwd', () => {
1050
1975
  inTempProject((root) => {
1051
1976
  const projectRoot = path.join(root, 'project with spaces');
@@ -1110,12 +2035,12 @@ test('Codex Windows hook launchers stay project-bound without Git from nested cw
1110
2035
  });
1111
2036
  });
1112
2037
 
1113
- test('Codex fresh install is exact while refresh and upgrade preserve existing files', () => {
2038
+ test('Codex installed Specs and spec-maker reject adaptive coverage mutations', () => {
1114
2039
  inTempProject((root) => {
1115
2040
  const userInstructions = '# User rules\n\nKeep this exact.\n';
1116
2041
  fs.writeFileSync(path.join(root, 'AGENTS.md'), userInstructions);
1117
2042
 
1118
- const first = install(root);
2043
+ const first = install(root, ['--with-document-skills']);
1119
2044
  assert.equal(first.status, 0, `${first.stdout}\n${first.stderr}`);
1120
2045
  assert.equal(fs.existsSync(path.join(root, '.claude')), false);
1121
2046
  assert.equal(fs.existsSync(path.join(root, '.codex', 'config.toml')), false);
@@ -1127,6 +2052,8 @@ test('Codex fresh install is exact while refresh and upgrade preserve existing f
1127
2052
  '.codex/rules/workflow.md',
1128
2053
  '.codex/rules/hook-protocols.md',
1129
2054
  '.codex/rules/state-sync.md',
2055
+ '.codex/rules/review-audit-self-decision.md',
2056
+ '.codex/rules/process-management.md',
1130
2057
  '.codex/scripts/spec-ground.cjs',
1131
2058
  '.codex/scripts/validate-spec-output.cjs',
1132
2059
  '.agents/.gitignore',
@@ -1199,6 +2126,12 @@ test('Codex fresh install is exact while refresh and upgrade preserve existing f
1199
2126
  const name = path.basename(fileName, '.md').replace(/-/g, '_');
1200
2127
  const agentPath = path.join(root, '.codex', 'agents', `${name}.toml`);
1201
2128
  const content = fs.readFileSync(agentPath, 'utf8');
2129
+ const sourcePath = path.join(PACKAGE_ROOT, 'src', 'claude', 'agents', fileName);
2130
+ assert.equal(
2131
+ content,
2132
+ convertCodexAgentContent(fs.readFileSync(sourcePath, 'utf8'), fileName),
2133
+ `Codex agent projection drifted: ${fileName}`
2134
+ );
1202
2135
  assert.equal(parseGeneratedTomlString(content, 'name'), name);
1203
2136
  assert.ok(parseGeneratedTomlString(content, 'description').length > 8);
1204
2137
  assert.ok(parseGeneratedTomlString(content, 'developer_instructions').length > 20);
@@ -1217,6 +2150,27 @@ test('Codex fresh install is exact while refresh and upgrade preserve existing f
1217
2150
  assert.equal(new Set(skillNames).size, skillNames.length);
1218
2151
  assert.ok(skillNames.every((name) => /^[a-z0-9-]+$/.test(name)));
1219
2152
 
2153
+ const scoutSkill = fs.readFileSync(
2154
+ path.join(root, '.agents', 'skills', 'inspect', 'SKILL.md'),
2155
+ 'utf8'
2156
+ );
2157
+ assert.match(scoutSkill, /^name:\s*cf-scout$/m);
2158
+ assert.equal(
2159
+ fs.existsSync(path.join(root, '.agents', 'skills', 'scout')),
2160
+ false,
2161
+ 'public Scout rename must keep the manifest-owned inspect directory'
2162
+ );
2163
+ const askSkill = fs.readFileSync(
2164
+ path.join(root, '.agents', 'skills', 'question', 'SKILL.md'),
2165
+ 'utf8'
2166
+ );
2167
+ assert.match(askSkill, /^name:\s*cf-ask$/m);
2168
+ assert.equal(
2169
+ fs.existsSync(path.join(root, '.agents', 'skills', 'ask')),
2170
+ false,
2171
+ 'public Ask rename must keep the manifest-owned question directory'
2172
+ );
2173
+
1220
2174
  const catalog = spawnSync(
1221
2175
  process.execPath,
1222
2176
  [path.join(root, '.codex', 'scripts', 'generate-skill-catalog.cjs'), '--json'],
@@ -1230,12 +2184,13 @@ test('Codex fresh install is exact while refresh and upgrade preserve existing f
1230
2184
  fs.realpathSync(path.join(root, '.agents', 'skills'))
1231
2185
  );
1232
2186
 
1233
- const dockerScript = fs.readFileSync(
1234
- path.join(root, '.agents', 'skills', 'devops', 'scripts', 'docker_optimize.py'),
1235
- 'utf8'
1236
- );
1237
- assert.match(dockerScript, /description="Analyze Dockerfile for optimization opportunities"/);
1238
- assert.doesNotMatch(dockerScript, /task_name="analyze_dockerfile/);
2187
+ for (const retired of MANIFEST.obsolete.skills) {
2188
+ assert.equal(
2189
+ fs.existsSync(path.join(root, '.agents', 'skills', retired)),
2190
+ false,
2191
+ `retired skill should not be installed: ${retired}`
2192
+ );
2193
+ }
1239
2194
 
1240
2195
  const multimodalScript = fs.readFileSync(
1241
2196
  path.join(root, '.agents', 'skills', 'ai-multimodal', 'scripts', 'gemini_batch_process.py'),
@@ -1256,10 +2211,10 @@ test('Codex fresh install is exact while refresh and upgrade preserve existing f
1256
2211
  assert.doesNotMatch(visible, /If you are Codex CLI or any other runtime, ignore this entire Codex block/);
1257
2212
  assert.doesNotMatch(
1258
2213
  visible.replaceAll(foreignRuntimeBoundary, ''),
1259
- /\bAgent\(|subagent_type|`Agent`|\bSendMessage\b|\/hapo:|\bhapo:|Claude Code/,
2214
+ /\bAgent\(|subagent_type|`Agent`|\bSendMessage\b|\/cf:|\bcf:|Claude Code/,
1260
2215
  );
1261
2216
  assert.doesNotMatch(visible, /@@PRIVACY_PROMPT_START@@|Claude Tasks/);
1262
- assert.match(visible, /\$hapo-specs/);
2217
+ assert.match(visible, /\$cf-specs/);
1263
2218
 
1264
2219
  const agentsMd = fs.readFileSync(path.join(root, 'AGENTS.md'), 'utf8');
1265
2220
  assert.ok(agentsMd.startsWith(userInstructions));
@@ -1316,6 +2271,732 @@ test('Codex fresh install is exact while refresh and upgrade preserve existing f
1316
2271
  });
1317
2272
  });
1318
2273
 
2274
+ test('Codex installed Brainstorm skill reference and agent preserve proportional routing parity', () => {
2275
+ inTempProject((root) => {
2276
+ const sourceBytes = canonicalBrainstormSourceBytes();
2277
+ const result = install(root);
2278
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
2279
+
2280
+ const installedSkillPath = path.join(root, '.agents/skills/brainstorm/SKILL.md');
2281
+ const installedReferencePath = path.join(
2282
+ root,
2283
+ '.agents/skills/brainstorm/references/question-framework.md'
2284
+ );
2285
+ const installedAgentPath = path.join(root, '.codex/agents/brainstormer.toml');
2286
+ const sourceSkill = fs.readFileSync(BRAINSTORM_SKILL_SOURCE_PATH, 'utf8');
2287
+ const sourceReference = fs.readFileSync(BRAINSTORM_REFERENCE_SOURCE_PATH, 'utf8');
2288
+ const sourceAgent = fs.readFileSync(BRAINSTORM_AGENT_SOURCE_PATH, 'utf8');
2289
+
2290
+ assert.equal(
2291
+ fs.readFileSync(installedSkillPath, 'utf8'),
2292
+ normalizeCodexBody(sourceSkill, BRAINSTORM_SKILL_SOURCE_PATH),
2293
+ 'installed Brainstorm skill must equal the generic Codex projection'
2294
+ );
2295
+ assert.equal(
2296
+ fs.readFileSync(installedReferencePath, 'utf8'),
2297
+ normalizeCodexBody(sourceReference, BRAINSTORM_REFERENCE_SOURCE_PATH),
2298
+ 'installed Brainstorm reference must equal the generic Codex projection'
2299
+ );
2300
+ assert.equal(
2301
+ fs.readFileSync(installedAgentPath, 'utf8'),
2302
+ convertCodexAgentContent(sourceAgent, path.basename(BRAINSTORM_AGENT_SOURCE_PATH)),
2303
+ 'installed brainstormer agent must equal the Codex TOML projection'
2304
+ );
2305
+
2306
+ assert.deepEqual(brainstormProjectionIssues(readInstalledBrainstormProjection(root)), []);
2307
+ assertCanonicalBrainstormSourceBytesUnchanged(sourceBytes);
2308
+
2309
+ const safelyStrengthened = readInstalledBrainstormProjection(root);
2310
+ safelyStrengthened.agent += '\nThe specialist may never ask the user directly or contact the user. Do not write files or mutate shared task state. Do not delegate work or delegate tasks. The specialist is not permitted to launch Specs or run Develop.';
2311
+ assert.deepEqual(
2312
+ brainstormProjectionIssues(safelyStrengthened),
2313
+ [],
2314
+ 'safe specialist prohibitions must not produce authority issues'
2315
+ );
2316
+
2317
+ const mutations = [
2318
+ {
2319
+ name: 'adaptive-direct-precedence-removed', source: 'skill',
2320
+ from: 'Route Direct first, then\napply controls only to requests that remain in Brainstorm.',
2321
+ to: 'Apply controls before Direct classification.', expected: ['adaptive-direct-precedence']
2322
+ },
2323
+ {
2324
+ name: 'adaptive-ordered-depth-removed', source: 'skill',
2325
+ from: 'With no Deep signal, use Standard.', to: 'Deep is always the default.',
2326
+ expected: ['adaptive-ordered-depth']
2327
+ },
2328
+ {
2329
+ name: 'adaptive-leading-flags-removed', source: 'skill',
2330
+ from: 'Parse controls only from the leading consecutive token segment.',
2331
+ to: 'Parse flag-like tokens anywhere.', expected: ['adaptive-leading-flags']
2332
+ },
2333
+ {
2334
+ name: 'adaptive-failure-isolation-removed', source: 'skill',
2335
+ from: 'failure isolation for partial or cascading failure\nacross boundaries',
2336
+ to: 'generic failure notes', expected: ['adaptive-lens-trigger-skip']
2337
+ },
2338
+ {
2339
+ name: 'adaptive-evidence-fallback-removed', source: 'skill',
2340
+ from: 'Missing evidence\nforces feasibility `unknown` and confidence `low`.',
2341
+ to: 'Missing evidence permits a confident guess.', expected: ['adaptive-evidence-semantics']
2342
+ },
2343
+ {
2344
+ name: 'adaptive-numeric-evidence-removed', source: 'skill',
2345
+ from: 'A numeric estimate requires\nrange, unit, basis, evidence, and assumptions; otherwise report `unknown`.',
2346
+ to: 'A numeric estimate may be a best-effort number.', expected: ['adaptive-numeric-estimates']
2347
+ },
2348
+ {
2349
+ name: 'adaptive-pre-tool-redaction-removed', source: 'skill',
2350
+ from: 'Before an\nexternal visual tool or adviser handoff, minimize context and redact secrets,\ncredentials, private keys, access tokens, and unnecessary PII.',
2351
+ to: 'Forward full context to every external tool and adviser.', expected: ['adaptive-pre-tool-redaction']
2352
+ },
2353
+ {
2354
+ name: 'adaptive-adviser-gate-removed', source: 'skill',
2355
+ from: '`--advice` invokes\n`brainstormer` only after the material-choice gate;',
2356
+ to: '`--advice` invokes `brainstormer` before routing;', expected: ['adaptive-adviser-gate']
2357
+ },
2358
+ {
2359
+ name: 'adaptive-decision-freshness-removed', source: 'skill',
2360
+ from: 'The first section records target\nidentity, current source revision and worktree state or `[UNVERIFIED]`, an\nevidence-as-of value, and what change invalidates the brief.',
2361
+ to: 'The handoff has no revision or freshness binding.', expected: ['adaptive-decision-freshness']
2362
+ },
2363
+ {
2364
+ name: 'adaptive-overlay-authority-removed', source: 'skill',
2365
+ from: 'Neither overlay\nwrites, approves, persists, dispatches, or completes work.',
2366
+ to: 'Overlays may persist, approve, dispatch, and complete work.', expected: ['adaptive-non-authority']
2367
+ },
2368
+ {
2369
+ name: 'debug-route-removed',
2370
+ source: 'skill',
2371
+ from: 'Then use `cf-debug` until',
2372
+ to: 'Skip diagnosis and choose a remedy before',
2373
+ expected: ['bug-routing']
2374
+ },
2375
+ {
2376
+ name: 'fix-authority-removed',
2377
+ source: 'skill',
2378
+ from: 'only when the user explicitly requested a fix',
2379
+ to: 'automatically whenever root cause is known',
2380
+ expected: ['bug-routing']
2381
+ },
2382
+ {
2383
+ name: 'exploration-stop-removed',
2384
+ source: 'skill',
2385
+ from: 'Do not request design approval, persist a report, or\n invoke another workflow without a new explicit request.',
2386
+ to: 'Request approval, persist a report, and invoke another workflow.',
2387
+ expected: ['exploration-stop']
2388
+ },
2389
+ {
2390
+ name: 'conditional-options-removed',
2391
+ source: 'skill',
2392
+ from: 'For a material choice, compare 2–3 mechanically distinct viable approaches',
2393
+ to: 'For every request, present one predetermined approach',
2394
+ expected: ['option-cardinality']
2395
+ },
2396
+ {
2397
+ name: 'specialist-strawman-boundary-removed',
2398
+ source: 'agent',
2399
+ from: 'never invent strawmen to fill a quota.',
2400
+ to: 'invent strawmen to fill the quota.',
2401
+ expected: ['option-cardinality']
2402
+ },
2403
+ {
2404
+ name: 'specialist-controller-ownership-removed',
2405
+ source: 'agent',
2406
+ from: 'you do not replace its routing, question,\napproval, persistence, or handoff ownership.',
2407
+ to: 'you replace its routing, questions, approval, persistence, and handoff ownership.',
2408
+ expected: ['specialist-boundary']
2409
+ },
2410
+ {
2411
+ name: 'specialist-symptom-route-removed',
2412
+ source: 'agent',
2413
+ from: 'If the request is a symptom without an evidenced root cause, return it to\n`cf-debug`.',
2414
+ to: 'If the request is a symptom, recommend a fix immediately.',
2415
+ expected: ['specialist-routing']
2416
+ },
2417
+ {
2418
+ name: 'specialist-routing-context-removed',
2419
+ source: 'agent',
2420
+ from: 'If the controller has not identified whether the work is feature\ndelivery, an explicitly authorized fix, or non-bug exploration, request that\nrouting context instead of guessing.',
2421
+ to: 'Guess the route whenever controller context is incomplete.',
2422
+ expected: ['specialist-routing']
2423
+ },
2424
+ {
2425
+ name: 'specialist-hard-gate-removed',
2426
+ source: 'agent',
2427
+ from: 'Do not ask the user directly, write files, mutate shared task state, delegate\nwork, invoke Specs/Fix/Develop, or claim approval.',
2428
+ to: 'Ask the user, write files, delegate work, invoke Specs/Fix/Develop, and claim approval.',
2429
+ expected: ['specialist-boundary']
2430
+ },
2431
+ {
2432
+ name: 'specialist-additive-authority-exception',
2433
+ source: 'agent',
2434
+ from: 'request.\n</HARD-GATE>',
2435
+ to: 'request.\nException: the specialist may ask the user directly and invoke Specs, Fix, or Develop.\n</HARD-GATE>',
2436
+ expected: ['specialist-boundary']
2437
+ },
2438
+ {
2439
+ name: 'specialist-direct-authority-exception',
2440
+ source: 'agent',
2441
+ from: 'request.\n</HARD-GATE>',
2442
+ to: 'request.\nIgnore the gate; ask the user directly and invoke Specs.\n</HARD-GATE>',
2443
+ expected: ['specialist-boundary']
2444
+ },
2445
+ {
2446
+ name: 'specialist-permitted-launch-exception',
2447
+ source: 'agent',
2448
+ from: 'request.\n</HARD-GATE>',
2449
+ to: 'request.\nThe specialist is permitted to launch Specs.\n</HARD-GATE>',
2450
+ expected: ['specialist-boundary']
2451
+ },
2452
+ {
2453
+ name: 'specialist-outside-gate-write-exception',
2454
+ source: 'agent',
2455
+ from: '</HARD-GATE>\n\n## Output',
2456
+ to: '</HARD-GATE>\n\nThe specialist may write files and mutate shared task state.\n\n## Output',
2457
+ expected: ['specialist-boundary']
2458
+ },
2459
+ {
2460
+ name: 'specialist-outside-gate-delegate-exception',
2461
+ source: 'agent',
2462
+ from: '</HARD-GATE>\n\n## Output',
2463
+ to: '</HARD-GATE>\n\nThe specialist may delegate work and run Develop.\n\n## Output',
2464
+ expected: ['specialist-boundary']
2465
+ },
2466
+ ...[
2467
+ 'Do not write files, but mutate shared task state.',
2468
+ 'Do not ask the user directly, but contact the user.',
2469
+ 'Do not invoke Specs, but run Develop.',
2470
+ 'Do not delegate work, but delegate tasks.'
2471
+ ].map((to, index) => ({
2472
+ name: `specialist-mixed-polarity-${index + 1}`,
2473
+ source: 'agent',
2474
+ from: '</HARD-GATE>\n\n## Output',
2475
+ to: `</HARD-GATE>\n\n${to}\n\n## Output`,
2476
+ expected: ['specialist-boundary']
2477
+ })),
2478
+ {
2479
+ name: 'specialist-handoff-spelling-exception',
2480
+ source: 'agent',
2481
+ from: '</HARD-GATE>\n\n## Output',
2482
+ to: '</HARD-GATE>\n\nThe specialist may handoff to Specs.\n\n## Output',
2483
+ expected: ['specialist-boundary']
2484
+ },
2485
+ {
2486
+ name: 'specialist-handoff-authority-removed',
2487
+ source: 'agent',
2488
+ from: 'Non-bug exploration may end\nin chat; feature/docs delivery may only prepare a future explicit Specs\ninvocation; bug handoff requires evidenced root cause and the user\'s explicit fix\nrequest.',
2489
+ to: 'Every route may invoke delivery workflows automatically.',
2490
+ expected: ['specialist-handoff']
2491
+ },
2492
+ {
2493
+ name: 'decision-provenance-removed',
2494
+ source: 'framework',
2495
+ from: 'Do not write "user selected" unless direct user text or the native input tool\n confirms it.',
2496
+ to: 'Write "user selected" for inferred defaults.',
2497
+ expected: ['approval-persistence']
2498
+ },
2499
+ {
2500
+ name: 'installed-grammar-corruption',
2501
+ source: 'skill',
2502
+ from: '## Completion bar',
2503
+ to: 'one a structured user-input request call\n\n## Completion bar',
2504
+ expected: ['projection-grammar']
2505
+ }
2506
+ ];
2507
+
2508
+ for (const mutation of mutations) {
2509
+ const target = assertInstalledBrainstormMutationTarget(root, mutation.source);
2510
+ const original = fs.readFileSync(target);
2511
+ const fullContent = original.toString('utf8');
2512
+ const projectedContent = mutation.source === 'agent'
2513
+ ? parseGeneratedTomlString(fullContent, 'developer_instructions')
2514
+ : fullContent;
2515
+ const anchor = projectedContent.indexOf(mutation.from);
2516
+ assert.ok(anchor >= 0, `${mutation.name} mutation anchor must exist`);
2517
+ assert.equal(
2518
+ projectedContent.indexOf(mutation.from, anchor + mutation.from.length),
2519
+ -1,
2520
+ `${mutation.name} mutation anchor must be unique`
2521
+ );
2522
+ const weakened = `${projectedContent.slice(0, anchor)}${mutation.to}${projectedContent.slice(anchor + mutation.from.length)}`;
2523
+ const rendered = mutation.source === 'agent'
2524
+ ? replaceGeneratedTomlString(fullContent, 'developer_instructions', weakened)
2525
+ : weakened;
2526
+ try {
2527
+ fs.writeFileSync(assertInstalledBrainstormMutationTarget(root, mutation.source), rendered);
2528
+ assertCanonicalBrainstormSourceBytesUnchanged(sourceBytes);
2529
+ assert.deepEqual(
2530
+ brainstormProjectionIssues(readInstalledBrainstormProjection(root)),
2531
+ mutation.expected,
2532
+ mutation.name
2533
+ );
2534
+ } finally {
2535
+ fs.writeFileSync(assertInstalledBrainstormMutationTarget(root, mutation.source), original);
2536
+ }
2537
+ assert.deepEqual(fs.readFileSync(target), original, `${mutation.name} byte restore`);
2538
+ assert.deepEqual(brainstormProjectionIssues(readInstalledBrainstormProjection(root)), []);
2539
+ assertCanonicalBrainstormSourceBytesUnchanged(sourceBytes);
2540
+ }
2541
+ });
2542
+ });
2543
+
2544
+ test('Codex installed Develop preserves plan-native execution and references', () => {
2545
+ inTempProject((root) => {
2546
+ const sourceBytes = new Map(DEVELOP_BUNDLE.map((relative) => [
2547
+ path.join(DEVELOP_SOURCE_ROOT, relative),
2548
+ fs.readFileSync(path.join(DEVELOP_SOURCE_ROOT, relative))
2549
+ ]));
2550
+ const result = install(root);
2551
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
2552
+
2553
+ const installedRoot = path.join(root, '.agents/skills/develop');
2554
+ for (const relative of DEVELOP_BUNDLE) {
2555
+ const sourcePath = path.join(DEVELOP_SOURCE_ROOT, relative);
2556
+ assert.equal(
2557
+ fs.readFileSync(path.join(installedRoot, relative), 'utf8'),
2558
+ normalizeCodexBody(fs.readFileSync(sourcePath, 'utf8'), sourcePath),
2559
+ `Codex Develop projection drifted: ${relative}`
2560
+ );
2561
+ }
2562
+ const installedSkill = fs.readFileSync(path.join(installedRoot, 'SKILL.md'), 'utf8');
2563
+ assert.match(installedSkill, /\$cf-develop <feature>/);
2564
+ assert.match(installedSkill, /\.codex\/scripts\/workflow-policy\.cjs/);
2565
+ assert.doesNotMatch(installedSkill, /\/cf:develop|\.claude\/scripts/);
2566
+ assert.equal(fs.existsSync(path.join(root, '.claude/skills/develop')), false);
2567
+ for (const [sourcePath, expected] of sourceBytes) {
2568
+ assert.deepEqual(fs.readFileSync(sourcePath), expected, `canonical source changed: ${sourcePath}`);
2569
+ }
2570
+ });
2571
+ });
2572
+
2573
+ test('Codex installed Research preserves adaptive evidence semantics', () => {
2574
+ inTempProject((root) => {
2575
+ const sourceBytes = new Map([
2576
+ [RESEARCH_SKILL_SOURCE_PATH, fs.readFileSync(RESEARCH_SKILL_SOURCE_PATH)],
2577
+ [RESEARCH_AGENT_SOURCE_PATH, fs.readFileSync(RESEARCH_AGENT_SOURCE_PATH)],
2578
+ ]);
2579
+ const result = install(root);
2580
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
2581
+
2582
+ const installedSkillPath = path.join(root, '.agents/skills/research/SKILL.md');
2583
+ const installedAgentPath = path.join(root, '.codex/agents/researcher.toml');
2584
+ assert.equal(
2585
+ fs.readFileSync(installedSkillPath, 'utf8'),
2586
+ normalizeCodexBody(fs.readFileSync(RESEARCH_SKILL_SOURCE_PATH, 'utf8'), RESEARCH_SKILL_SOURCE_PATH)
2587
+ );
2588
+ assert.equal(
2589
+ fs.readFileSync(installedAgentPath, 'utf8'),
2590
+ convertCodexAgentContent(fs.readFileSync(RESEARCH_AGENT_SOURCE_PATH, 'utf8'), 'researcher.md')
2591
+ );
2592
+ const installedSkill = fs.readFileSync(installedSkillPath, 'utf8');
2593
+ const installedAgent = parseGeneratedTomlString(
2594
+ fs.readFileSync(installedAgentPath, 'utf8'), 'developer_instructions'
2595
+ );
2596
+ assert.match(installedSkill, /^name: cf-research$/m);
2597
+ assert.match(installedSkill, /Quick \| One low-risk, reversible fact or known option/);
2598
+ assert.match(installedSkill, /research sequentially with the same evidence bar/);
2599
+ assert.match(installedSkill, /Default to a concise answer in chat/);
2600
+ assert.match(installedSkill, /confirmed \| inferred \| unresolved/);
2601
+ assert.match(installedAgent, /do not implement code,\s+write files, mutate task state/);
2602
+ assert.match(installedAgent, /owns any\s+authorized persistence/);
2603
+ assert.doesNotMatch(installedSkill, /must (?:instantly |always )?delegate/i);
2604
+
2605
+ const manifest = JSON.parse(
2606
+ fs.readFileSync(path.join(root, '.codex/cafekit-manifest.json'), 'utf8')
2607
+ );
2608
+ assert.ok(manifest.files['.agents/skills/research/SKILL.md']);
2609
+ assert.ok(manifest.files['.codex/agents/researcher.toml']);
2610
+ assert.equal(fs.existsSync(path.join(root, '.claude/skills/research')), false);
2611
+ for (const [sourcePath, expected] of sourceBytes) {
2612
+ assert.deepEqual(fs.readFileSync(sourcePath), expected, `canonical source changed: ${sourcePath}`);
2613
+ }
2614
+ });
2615
+ });
2616
+
2617
+ test('Codex installed Test preserves plan-native proof and references', () => {
2618
+ inTempProject((root) => {
2619
+ const sourcePaths = [
2620
+ ...TEST_BUNDLE.map((relative) => path.join(TEST_SOURCE_ROOT, relative)),
2621
+ TEST_RUNNER_SOURCE_PATH,
2622
+ path.join(CODE_REVIEW_SOURCE_ROOT, 'SKILL.md')
2623
+ ];
2624
+ const sourceBytes = new Map(sourcePaths.map((sourcePath) => [
2625
+ sourcePath, fs.readFileSync(sourcePath)
2626
+ ]));
2627
+ const result = install(root);
2628
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
2629
+
2630
+ const installedRoot = path.join(root, '.agents/skills/test');
2631
+ for (const relative of TEST_BUNDLE) {
2632
+ const sourcePath = path.join(TEST_SOURCE_ROOT, relative);
2633
+ assert.equal(
2634
+ fs.readFileSync(path.join(installedRoot, relative), 'utf8'),
2635
+ normalizeCodexBody(fs.readFileSync(sourcePath, 'utf8'), sourcePath),
2636
+ `Codex Test projection drifted: ${relative}`
2637
+ );
2638
+ }
2639
+ const installedSkill = fs.readFileSync(path.join(installedRoot, 'SKILL.md'), 'utf8');
2640
+ assert.match(installedSkill, /\$cf-test/);
2641
+ assert.match(installedSkill, /test-proof-v1/);
2642
+ assert.doesNotMatch(installedSkill, /\/cf:test/);
2643
+
2644
+ const installedRunnerPath = path.join(root, '.codex/agents/test_runner.toml');
2645
+ assert.equal(
2646
+ fs.readFileSync(installedRunnerPath, 'utf8'),
2647
+ convertCodexAgentContent(
2648
+ fs.readFileSync(TEST_RUNNER_SOURCE_PATH, 'utf8'),
2649
+ path.basename(TEST_RUNNER_SOURCE_PATH)
2650
+ ),
2651
+ 'Codex test_runner projection drifted'
2652
+ );
2653
+ const installedRunner = parseGeneratedTomlString(
2654
+ fs.readFileSync(installedRunnerPath, 'utf8'), 'developer_instructions'
2655
+ );
2656
+ assert.match(installedRunner, /test-proof-v1/);
2657
+ assert.match(installedRunner, /sole writer of[\s\S]*Status and inline Receipt/i);
2658
+
2659
+ const reviewSourcePath = path.join(CODE_REVIEW_SOURCE_ROOT, 'SKILL.md');
2660
+ const installedReview = fs.readFileSync(path.join(root, '.agents/skills/code-review/SKILL.md'), 'utf8');
2661
+ assert.equal(installedReview, normalizeCodexBody(
2662
+ fs.readFileSync(reviewSourcePath, 'utf8'), reviewSourcePath
2663
+ ));
2664
+ assert.match(installedReview, /controller-validated `test-proof-v1`/);
2665
+ assert.match(installedReview, /Never create or search for a separate[\s\S]*process-first receipt/i);
2666
+ assert.equal(fs.existsSync(path.join(root, '.claude/skills/test')), false);
2667
+ for (const [sourcePath, expected] of sourceBytes) {
2668
+ assert.deepEqual(fs.readFileSync(sourcePath), expected, `canonical source changed: ${sourcePath}`);
2669
+ }
2670
+ });
2671
+ });
2672
+
2673
+ function hotfixProjectionIssues(files) {
2674
+ const compact = (value) => String(value).replace(/\s+/g, ' ').trim();
2675
+ const skill = compact(files.skill);
2676
+ const review = compact(files.review);
2677
+ const parallel = compact(files.parallel);
2678
+ const specialized = compact(files.specialized);
2679
+ const issues = new Set();
2680
+ if (!skill.includes('name: cf-fix')
2681
+ || !skill.includes('# Fix — root-cause repair workflow')
2682
+ || skill.includes('name: cf-hotfix')) {
2683
+ issues.add('public-rename');
2684
+ }
2685
+ if (!skill.includes('## Proportional depth')
2686
+ || !skill.includes('Quick mode only reduces depth; it never skips scout, pre-fix evidence, diagnosis, or before/after verification.')) {
2687
+ issues.add('adaptive-depth');
2688
+ }
2689
+ if (!skill.includes('`Timeline: skipped - <reason>` or `- skipped: <reason>`')
2690
+ || !skill.includes('`Recurrence-Prevention Handoff`, when present, carries evidence-backed candidates only.')
2691
+ || !skill.includes('routes back to diagnosis (`cf-debug`)')) {
2692
+ issues.add('debug-handoff');
2693
+ }
2694
+ if (!skill.includes('report `PASS | PASS_WITH_WARNINGS | FAIL | BLOCKED`')
2695
+ || !skill.includes('The definition of `PASS` defers to `cf-code-review`.')
2696
+ || skill.includes('Confidence score')
2697
+ || !review.includes('Only `FAIL` and `PASS_WITH_WARNINGS` enter remediation retry')
2698
+ || !review.includes('only a fresh literal `PASS` enters finalization')
2699
+ || review.includes('"Approve anyway"')
2700
+ || review.includes('"Approve with known issues"')
2701
+ || review.includes('at most one Medium')) {
2702
+ issues.add('verdict-surface');
2703
+ }
2704
+ if (!skill.includes('The user explicitly requested or permitted delegation or parallel agents.')
2705
+ || !skill.includes('The active runtime exposes an Explore/delegation capability.')
2706
+ || !skill.includes('at least two distinct, non-overlapping scopes')
2707
+ || !parallel.includes('The user explicitly requested or permitted delegation or parallel agents.')) {
2708
+ issues.add('delegation-gate');
2709
+ }
2710
+ if (!skill.includes('The original symptom no longer reproduces with the exact pre-fix command/user flow.')
2711
+ || !skill.includes('Do not silently patch around the regression.')) {
2712
+ issues.add('side-effect-gate');
2713
+ }
2714
+ if (!skill.includes('## Bounded repair frame')
2715
+ || !skill.includes('Quick/local does not add a separate framing ceremony')
2716
+ || !skill.includes('**Outcome:**')
2717
+ || !skill.includes('**Constraints:**')
2718
+ || !skill.includes('**Non-goals:**')
2719
+ || !skill.includes('**Acceptance:**')) {
2720
+ issues.add('bounded-repair-frame');
2721
+ }
2722
+ if (!skill.includes('after diagnosis, research only unresolved external facts')
2723
+ || !skill.includes('`cf-brainstorm` to compare 2-3 options')
2724
+ || !skill.includes('When diagnosis leaves one safe direct repair, skip research and')) {
2725
+ issues.add('deep-decision-route');
2726
+ }
2727
+ if (!skill.includes('Load only the matching')
2728
+ || !specialized.includes('Load only the matching section')
2729
+ || (specialized.match(/\*\*Baseline:\*\*/g) || []).length < 5
2730
+ || (specialized.match(/\*\*Proof:\*\*/g) || []).length < 5) {
2731
+ issues.add('specialized-proof-overlays');
2732
+ }
2733
+ if (!parallel.includes('Diagnosis still starts only')
2734
+ || !parallel.includes('after the required scout outputs are synthesized')
2735
+ || !parallel.includes('Research begins only after Step 2 diagnosis')
2736
+ || parallel.includes('scout + diagnose + research together')
2737
+ || parallel.includes("You don't need to wait for scouting")) {
2738
+ issues.add('scout-before-diagnosis');
2739
+ }
2740
+ return [...issues].sort();
2741
+ }
2742
+
2743
+ test('Codex installed Fix preserves the adaptive repair contract', () => {
2744
+ inTempProject((root) => {
2745
+ const sourceBytes = new Map(HOTFIX_BUNDLE.map((relative) => [
2746
+ path.join(HOTFIX_SOURCE_ROOT, relative),
2747
+ fs.readFileSync(path.join(HOTFIX_SOURCE_ROOT, relative))
2748
+ ]));
2749
+ const result = install(root);
2750
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
2751
+
2752
+ const installedRoot = path.join(root, '.agents/skills/hotfix');
2753
+ assert.equal(fs.existsSync(path.join(root, '.agents/skills/fix')), false,
2754
+ 'public Fix rename must keep the manifest-owned hotfix directory');
2755
+ for (const relative of HOTFIX_BUNDLE) {
2756
+ const sourcePath = path.join(HOTFIX_SOURCE_ROOT, relative);
2757
+ assert.equal(
2758
+ fs.readFileSync(path.join(installedRoot, relative), 'utf8'),
2759
+ normalizeCodexBody(fs.readFileSync(sourcePath, 'utf8'), sourcePath),
2760
+ `Codex Fix projection drifted: ${relative}`
2761
+ );
2762
+ }
2763
+ const readProjection = () => ({
2764
+ skill: fs.readFileSync(path.join(installedRoot, 'SKILL.md'), 'utf8'),
2765
+ review: fs.readFileSync(path.join(installedRoot, 'references/review-cycle.md'), 'utf8'),
2766
+ parallel: fs.readFileSync(path.join(installedRoot, 'references/parallel-patterns.md'), 'utf8'),
2767
+ specialized: fs.readFileSync(path.join(installedRoot, 'references/workflow-specialized.md'), 'utf8'),
2768
+ });
2769
+ const installedSkill = readProjection().skill;
2770
+ assert.match(installedSkill, /name: cf-fix/);
2771
+ assert.doesNotMatch(installedSkill, /name: cf-hotfix|\/cf:fix|cf:debug|cf:code-review/);
2772
+ assert.deepEqual(hotfixProjectionIssues(readProjection()), []);
2773
+
2774
+ const mutations = [
2775
+ {
2776
+ name: 'public-name-reverts', file: 'SKILL.md',
2777
+ from: 'name: cf-fix', to: 'name: cf-hotfix',
2778
+ expected: ['public-rename']
2779
+ },
2780
+ {
2781
+ name: 'quick-skips-diagnosis', file: 'SKILL.md',
2782
+ from: 'Quick mode only reduces depth', to: 'Quick mode may shorten scope',
2783
+ expected: ['adaptive-depth']
2784
+ },
2785
+ {
2786
+ name: 'handoff-drops-skip-forms', file: 'SKILL.md',
2787
+ from: '`Timeline: skipped - <reason>` or `- skipped: <reason>`', to: '`Timeline: omitted`',
2788
+ expected: ['debug-handoff']
2789
+ },
2790
+ {
2791
+ name: 'warnings-auto-accept', file: 'references/review-cycle.md',
2792
+ from: 'Only `FAIL` and `PASS_WITH_WARNINGS` enter remediation retry',
2793
+ to: 'Only `FAIL` enters remediation retry; `PASS_WITH_WARNINGS` auto-approves',
2794
+ expected: ['verdict-surface']
2795
+ },
2796
+ {
2797
+ name: 'warnings-user-approve', file: 'references/review-cycle.md',
2798
+ from: 'only a fresh literal `PASS` enters finalization', to: '"Approve anyway" enters finalization',
2799
+ expected: ['verdict-surface']
2800
+ },
2801
+ {
2802
+ name: 'pass-redefined-locally', file: 'references/review-cycle.md',
2803
+ from: 'The definition of `PASS` defers to `cf-code-review`; Fix never redefines it with local severity thresholds.',
2804
+ to: 'The definition of `PASS` is local: no Critical, no High, at most one Medium.',
2805
+ expected: ['verdict-surface']
2806
+ },
2807
+ {
2808
+ name: 'confidence-score-returns', file: 'SKILL.md',
2809
+ from: '**Report:** root cause, changes made',
2810
+ to: '**Report:** Confidence score, root cause, changes made',
2811
+ expected: ['verdict-surface']
2812
+ },
2813
+ {
2814
+ name: 'delegation-loses-user-clause', file: 'SKILL.md',
2815
+ from: 'The user explicitly requested or permitted delegation or parallel agents.',
2816
+ to: 'Delegation is at the agent\'s discretion.',
2817
+ expected: ['delegation-gate']
2818
+ },
2819
+ {
2820
+ name: 'sweep-permits-silent-patch', file: 'SKILL.md',
2821
+ from: 'Do not silently patch around the regression.', to: 'Patch around regressions quietly.',
2822
+ expected: ['side-effect-gate']
2823
+ },
2824
+ {
2825
+ name: 'quick-gains-mandatory-frame', file: 'SKILL.md',
2826
+ from: 'Quick/local does not add a separate framing ceremony', to: 'Quick/local always requires a separate framing ceremony',
2827
+ expected: ['bounded-repair-frame']
2828
+ },
2829
+ {
2830
+ name: 'deep-researches-before-diagnosis', file: 'SKILL.md',
2831
+ from: 'after diagnosis, research only unresolved external facts', to: 'research broadly before diagnosis',
2832
+ expected: ['deep-decision-route']
2833
+ },
2834
+ {
2835
+ name: 'overlays-load-everything', file: 'references/workflow-specialized.md',
2836
+ from: 'Load only the matching section', to: 'Load every section',
2837
+ expected: ['specialized-proof-overlays']
2838
+ },
2839
+ {
2840
+ name: 'diagnosis-starts-before-scout', file: 'references/parallel-patterns.md',
2841
+ from: 'Diagnosis still starts only', to: 'Diagnosis may start',
2842
+ expected: ['scout-before-diagnosis']
2843
+ },
2844
+ {
2845
+ name: 'research-starts-before-diagnosis', file: 'references/parallel-patterns.md',
2846
+ from: 'Research begins only after Step 2 diagnosis', to: 'Research may begin before Step 2 diagnosis',
2847
+ expected: ['scout-before-diagnosis']
2848
+ }
2849
+ ];
2850
+ for (const mutation of mutations) {
2851
+ const target = path.join(installedRoot, mutation.file);
2852
+ const original = fs.readFileSync(target);
2853
+ const content = original.toString('utf8');
2854
+ const anchor = content.indexOf(mutation.from);
2855
+ assert.ok(anchor >= 0, `${mutation.name} mutation anchor must exist`);
2856
+ assert.equal(
2857
+ content.indexOf(mutation.from, anchor + mutation.from.length),
2858
+ -1,
2859
+ `${mutation.name} mutation anchor must be unique`
2860
+ );
2861
+ const weakened = `${content.slice(0, anchor)}${mutation.to}${content.slice(anchor + mutation.from.length)}`;
2862
+ try {
2863
+ fs.writeFileSync(target, weakened);
2864
+ assert.deepEqual(hotfixProjectionIssues(readProjection()), mutation.expected, mutation.name);
2865
+ } finally {
2866
+ fs.writeFileSync(target, original);
2867
+ }
2868
+ assert.deepEqual(fs.readFileSync(target), original, `${mutation.name} byte restore`);
2869
+ assert.deepEqual(hotfixProjectionIssues(readProjection()), []);
2870
+ }
2871
+ for (const [sourcePath, expected] of sourceBytes) {
2872
+ assert.deepEqual(fs.readFileSync(sourcePath), expected, `canonical source changed: ${sourcePath}`);
2873
+ }
2874
+ });
2875
+ });
2876
+
2877
+ function docsProjectionIssues(files) {
2878
+ const compact = (value) => String(value).replace(/\s+/g, ' ').trim();
2879
+ const skill = compact(files.skill);
2880
+ const init = compact(files.init);
2881
+ const update = compact(files.update);
2882
+ const standard = compact(files.standard);
2883
+ const issues = new Set();
2884
+ if (!skill.includes('The user explicitly requested or permitted delegation or parallel agents.')
2885
+ || !skill.includes('The active runtime exposes an Explore/delegation capability.')
2886
+ || !skill.includes('at least two distinct, non-overlapping scopes')
2887
+ || !skill.includes('Otherwise continue sequentially in the main agent')
2888
+ || !skill.includes('only through the Delegation Gate above')
2889
+ || !init.includes('only through the Delegation Gate in `../SKILL.md`')
2890
+ || !update.includes('only through the Delegation Gate in `../SKILL.md`')
2891
+ || skill.includes('when delegation is available')
2892
+ || init.includes('when delegation is available')
2893
+ || update.includes('when delegation is available')) {
2894
+ issues.add('delegation-gate');
2895
+ }
2896
+ if (!skill.includes('## Post-Task Docs Checkpoint')
2897
+ || !skill.includes('`none`: report that no docs change is needed and edit nothing.')
2898
+ || !skill.includes('A checkpoint never invents a new document')
2899
+ || !skill.includes('never auto-selects `init` and overrides')
2900
+ || !skill.includes('report the gap and recommend an explicit')
2901
+ || !standard.includes('checkpoint contract in `../SKILL.md`')
2902
+ || skill.includes('checkpoint may create')) {
2903
+ issues.add('docs-checkpoint');
2904
+ }
2905
+ if (!skill.includes('Type: Observed | Inferred | Unknown')
2906
+ || !skill.includes('Confidence: High | Medium | Low')
2907
+ || skill.includes('evidence is optional')) {
2908
+ issues.add('evidence-taxonomy');
2909
+ }
2910
+ if (!skill.includes('## Reconstruction Is Not Specs')
2911
+ || skill.includes('are advisory')) {
2912
+ issues.add('reconstruction-boundary');
2913
+ }
2914
+ return [...issues].sort();
2915
+ }
2916
+
2917
+ test('Codex installed Docs preserves the adaptive contract', () => {
2918
+ inTempProject((root) => {
2919
+ const sourceBytes = new Map(DOCS_ADAPTIVE_BUNDLE.map((relative) => [
2920
+ path.join(DOCS_ADAPTIVE_SOURCE_ROOT, relative),
2921
+ fs.readFileSync(path.join(DOCS_ADAPTIVE_SOURCE_ROOT, relative))
2922
+ ]));
2923
+ const result = install(root, ['--with-document-skills']);
2924
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
2925
+
2926
+ const installedRoot = path.join(root, '.agents/skills/docs');
2927
+ for (const relative of DOCS_ADAPTIVE_BUNDLE) {
2928
+ const sourcePath = path.join(DOCS_ADAPTIVE_SOURCE_ROOT, relative);
2929
+ assert.equal(
2930
+ fs.readFileSync(path.join(installedRoot, relative), 'utf8'),
2931
+ normalizeCodexBody(fs.readFileSync(sourcePath, 'utf8'), sourcePath),
2932
+ `Codex Docs projection drifted: ${relative}`
2933
+ );
2934
+ }
2935
+ const readProjection = () => ({
2936
+ skill: fs.readFileSync(path.join(installedRoot, 'SKILL.md'), 'utf8'),
2937
+ init: fs.readFileSync(path.join(installedRoot, 'references/init-workflow.md'), 'utf8'),
2938
+ update: fs.readFileSync(path.join(installedRoot, 'references/update-workflow.md'), 'utf8'),
2939
+ standard: fs.readFileSync(path.join(installedRoot, 'references/standard-docs-workflow.md'), 'utf8'),
2940
+ });
2941
+ assert.match(readProjection().skill, /name: cf-docs/);
2942
+ assert.deepEqual(docsProjectionIssues(readProjection()), []);
2943
+
2944
+ const mutations = [
2945
+ {
2946
+ name: 'gate-loses-user-clause', file: 'SKILL.md',
2947
+ from: 'The user explicitly requested or permitted delegation or parallel agents.',
2948
+ to: 'Delegation is at the agent\'s discretion.',
2949
+ expected: ['delegation-gate']
2950
+ },
2951
+ {
2952
+ name: 'keeper-ungated-returns', file: 'SKILL.md',
2953
+ from: 'only through the Delegation Gate above', to: 'when delegation is available',
2954
+ expected: ['delegation-gate']
2955
+ },
2956
+ {
2957
+ name: 'checkpoint-invents-doc', file: 'SKILL.md',
2958
+ from: 'A checkpoint never invents a new document', to: 'A checkpoint may create missing documents',
2959
+ expected: ['docs-checkpoint']
2960
+ },
2961
+ {
2962
+ name: 'taxonomy-drops-inferred', file: 'SKILL.md',
2963
+ from: 'Type: Observed | Inferred | Unknown', to: 'Type: Observed | Unknown',
2964
+ expected: ['evidence-taxonomy']
2965
+ },
2966
+ {
2967
+ name: 'reconstruction-becomes-advisory', file: 'SKILL.md',
2968
+ from: '## Reconstruction Is Not Specs',
2969
+ to: '## Reconstruction Is Not Specs\n\nThe prohibitions below are advisory.',
2970
+ expected: ['reconstruction-boundary']
2971
+ }
2972
+ ];
2973
+ for (const mutation of mutations) {
2974
+ const target = path.join(installedRoot, mutation.file);
2975
+ const original = fs.readFileSync(target);
2976
+ const content = original.toString('utf8');
2977
+ const anchor = content.indexOf(mutation.from);
2978
+ assert.ok(anchor >= 0, `${mutation.name} mutation anchor must exist`);
2979
+ assert.equal(
2980
+ content.indexOf(mutation.from, anchor + mutation.from.length),
2981
+ -1,
2982
+ `${mutation.name} mutation anchor must be unique`
2983
+ );
2984
+ const weakened = `${content.slice(0, anchor)}${mutation.to}${content.slice(anchor + mutation.from.length)}`;
2985
+ try {
2986
+ fs.writeFileSync(target, weakened);
2987
+ assert.deepEqual(docsProjectionIssues(readProjection()), mutation.expected, mutation.name);
2988
+ } finally {
2989
+ fs.writeFileSync(target, original);
2990
+ }
2991
+ assert.deepEqual(fs.readFileSync(target), original, `${mutation.name} byte restore`);
2992
+ assert.deepEqual(docsProjectionIssues(readProjection()), []);
2993
+ }
2994
+ for (const [sourcePath, expected] of sourceBytes) {
2995
+ assert.deepEqual(fs.readFileSync(sourcePath), expected, `canonical source changed: ${sourcePath}`);
2996
+ }
2997
+ });
2998
+ });
2999
+
1319
3000
  test('Codex install on top of existing Claude installation preserves content and adds Codex', () => {
1320
3001
  inTempProject((root) => {
1321
3002
  // Setup existing Claude installation with content
@@ -1404,3 +3085,134 @@ test('Codex scaffold resolver rejects symlink template', () => {
1404
3085
  fs.writeFileSync(template, original);
1405
3086
  });
1406
3087
  });
3088
+
3089
+ test('Claude and Codex installed Route preserve proportional live-catalog semantics', () => {
3090
+ inTempProject((root) => {
3091
+ const result = installPlatforms(root, ['claude', 'codex']);
3092
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
3093
+ const sourceRoot = path.join(PACKAGE_ROOT, 'src/claude/skills/route');
3094
+ const expectedFiles = [
3095
+ 'SKILL.md', 'references/task-taxonomy.md',
3096
+ 'references/chaining-patterns.md', 'references/agent-timing.md'
3097
+ ];
3098
+ const expectedRules = ['skill-workflow-routing.md', 'skill-domain-routing.md'];
3099
+ for (const runtime of ['claude', 'codex']) {
3100
+ const installedRoot = path.join(root, runtime === 'codex' ? '.agents/skills/route' : '.claude/skills/route');
3101
+ for (const relative of expectedFiles) {
3102
+ const source = fs.readFileSync(path.join(sourceRoot, relative), 'utf8');
3103
+ const installed = fs.readFileSync(path.join(installedRoot, relative), 'utf8');
3104
+ const expected = runtime === 'codex'
3105
+ ? normalizeCodexBody(source, path.join(sourceRoot, relative))
3106
+ : source;
3107
+ assert.equal(installed, expected, `${runtime}:${relative}`);
3108
+ }
3109
+ for (const relative of expectedRules) {
3110
+ const sourcePath = path.join(PACKAGE_ROOT, 'src/claude/rules', relative);
3111
+ const source = fs.readFileSync(sourcePath, 'utf8');
3112
+ const installed = fs.readFileSync(path.join(
3113
+ root, runtime === 'codex' ? '.codex/rules' : '.claude/rules', relative
3114
+ ), 'utf8');
3115
+ const expected = runtime === 'codex' ? normalizeCodexBody(source, sourcePath) : source;
3116
+ assert.equal(installed, expected, `${runtime}:rules/${relative}`);
3117
+ }
3118
+ const route = fs.readFileSync(path.join(installedRoot, 'SKILL.md'), 'utf8');
3119
+ assert.match(route, /names a valid installed skill[\s\S]*one installed skill clearly covers[\s\S]*Direct factual conversation/);
3120
+ assert.match(route, /highest-link[\s\S]*risk[\s\S]*number of material domains/);
3121
+ assert.match(route, /never expand it/);
3122
+ const catalog = installedCatalog(root, runtime);
3123
+ assert.ok(catalog.skills.some((skill) => skill.public_id === 'cf:route'));
3124
+ assert.equal(catalog.skills.some((skill) => skill.public_id === 'cf:docs'), false);
3125
+ }
3126
+ });
3127
+ });
3128
+
3129
+ test('combined installs bind each catalog to its native runtime inventory', () => {
3130
+ inTempProject((root) => {
3131
+ const result = installPlatforms(root, ['claude', 'codex']);
3132
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
3133
+ fs.cpSync(
3134
+ path.join(PACKAGE_ROOT, 'src/claude/skills/docs'),
3135
+ path.join(root, '.claude/skills/docs'),
3136
+ { recursive: true }
3137
+ );
3138
+ const claude = installedCatalog(root, 'claude');
3139
+ const codex = installedCatalog(root, 'codex');
3140
+ assert.equal(claude.root, fs.realpathSync(path.join(root, '.claude/skills')));
3141
+ assert.equal(codex.root, fs.realpathSync(path.join(root, '.agents/skills')));
3142
+ assert.equal(claude.skills.some((skill) => skill.public_id === 'cf:docs'), true);
3143
+ assert.equal(codex.skills.some((skill) => skill.public_id === 'cf:docs'), false);
3144
+ });
3145
+ });
3146
+
3147
+ test('Claude and Codex installed rules preserve the ported review and process guidance', () => {
3148
+ inTempProject((root) => {
3149
+ const result = installPlatforms(root, ['claude', 'codex']);
3150
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
3151
+ const ported = ['review-audit-self-decision.md', 'process-management.md'];
3152
+ const renameOnly = (content) => content
3153
+ .replace(/\/cf:([a-z0-9-]+)/gi, (_match, name) => `$cf-${name}`)
3154
+ .replace(/\bcf:([a-z0-9-]+)/gi, (_match, name) => `cf-${name}`);
3155
+ for (const relative of ported) {
3156
+ const sourcePath = path.join(PACKAGE_ROOT, 'src/claude/rules', relative);
3157
+ const source = fs.readFileSync(sourcePath, 'utf8');
3158
+ assert.equal(
3159
+ fs.existsSync(path.join(PACKAGE_ROOT, 'src/codex/rules', relative)),
3160
+ false,
3161
+ `${relative} must not be shadowed by a Codex native override`
3162
+ );
3163
+ assert.equal(
3164
+ fs.readFileSync(path.join(root, '.claude/rules', relative), 'utf8'),
3165
+ source,
3166
+ `claude:rules/${relative}`
3167
+ );
3168
+ const installedCodex = fs.readFileSync(path.join(root, '.codex/rules', relative), 'utf8');
3169
+ assert.equal(installedCodex, normalizeCodexBody(source, sourcePath), `codex:rules/${relative}`);
3170
+ assert.equal(
3171
+ installedCodex,
3172
+ renameOnly(source),
3173
+ `codex:rules/${relative} must differ from source only by skill-name renames`
3174
+ );
3175
+ }
3176
+ const agentsMd = fs.readFileSync(path.join(root, 'AGENTS.md'), 'utf8');
3177
+ const pointerLines = agentsMd
3178
+ .split('\n')
3179
+ .filter((line) => ported.some((relative) => line.includes(relative)));
3180
+ assert.equal(pointerLines.length, ported.length, 'AGENTS.md must point at both ported rules');
3181
+ for (const relative of ported) {
3182
+ assert.ok(
3183
+ pointerLines.some((line) => line.includes(`.codex/rules/${relative}`)),
3184
+ `AGENTS.md must name .codex/rules/${relative}`
3185
+ );
3186
+ }
3187
+ const pointerText = pointerLines.join('\n');
3188
+ for (const forbidden of ['cf:', 'Claude Code', 'Claude Tasks', 'SendMessage', '.claude/']) {
3189
+ assert.equal(
3190
+ pointerText.includes(forbidden),
3191
+ false,
3192
+ `AGENTS.md pointer must stay Codex-native: ${forbidden}`
3193
+ );
3194
+ }
3195
+ });
3196
+ });
3197
+
3198
+ test('installed Route degrades safely when an agent is absent', () => {
3199
+ inTempProject((root) => {
3200
+ const result = installPlatforms(root, ['claude', 'codex']);
3201
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
3202
+ const cases = [
3203
+ ['claude', '.claude/agents/researcher.md', '.claude/skills/route'],
3204
+ ['codex', '.codex/agents/researcher.toml', '.agents/skills/route'],
3205
+ ];
3206
+ for (const [runtime, agentRelative, routeRelative] of cases) {
3207
+ fs.rmSync(path.join(root, agentRelative), { force: true });
3208
+ assert.equal(fs.existsSync(path.join(root, agentRelative)), false);
3209
+ const timing = fs.readFileSync(path.join(root, routeRelative, 'references/agent-timing.md'), 'utf8');
3210
+ const route = fs.readFileSync(path.join(root, routeRelative, 'SKILL.md'), 'utf8');
3211
+ const normalizedRoute = route.replace(/\s+/g, ' ');
3212
+ assert.match(timing, /If the preferred agent is absent[\s\S]*never synthesize a role/);
3213
+ assert.match(normalizedRoute, /continue inline when safe or return the named gap/);
3214
+ assert.match(normalizedRoute, /Never synthesize an unavailable agent/);
3215
+ assert.doesNotMatch(`${timing}\n${route}`, /researcher(?:\.md|\.toml)/i, runtime);
3216
+ }
3217
+ });
3218
+ });