@haposoft/cafekit 0.16.0-rc.7 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/README.md +116 -27
  2. package/bin/__tests__/codex-hooks-ownership.test.js +239 -0
  3. package/bin/__tests__/codex-hooks.test.js +295 -0
  4. package/bin/__tests__/codex-native.test.js +2289 -34
  5. package/bin/__tests__/develop-contract.test.js +400 -23
  6. package/bin/__tests__/installer-safety.test.js +3 -3
  7. package/bin/__tests__/optional-skill-inventory.test.js +249 -0
  8. package/bin/__tests__/output-styles.test.js +214 -0
  9. package/bin/__tests__/package-inventory.test.js +2131 -16
  10. package/bin/__tests__/runtime-schema.test.js +111 -0
  11. package/bin/__tests__/secret-output-guardrail.test.js +122 -0
  12. package/bin/__tests__/skill-routing-source.test.js +172 -0
  13. package/bin/__tests__/specs-v2-execution-closeout.test.js +1 -1
  14. package/bin/install.js +10 -0
  15. package/bin/lib/codex-install.js +170 -5
  16. package/bin/lib/context.js +14 -0
  17. package/bin/lib/i18n.js +12 -6
  18. package/bin/lib/skill-deps.js +5 -5
  19. package/bin/phases/claude-runtime.js +23 -0
  20. package/bin/phases/claude-settings.js +8 -0
  21. package/bin/phases/codex-hooks.js +156 -0
  22. package/bin/phases/codex-runtime.js +29 -38
  23. package/bin/phases/copy-payload.js +8 -2
  24. package/bin/phases/select-skill-bundles.js +96 -0
  25. package/bin/phases/skill-inventory.js +71 -0
  26. package/bin/phases/skills-setup.js +9 -2
  27. package/bin/phases/write-metadata.js +8 -5
  28. package/package.json +8 -2
  29. package/src/claude/CLAUDE.md +3 -3
  30. package/src/claude/agents/brainstormer.md +52 -69
  31. package/src/claude/agents/code-auditor.md +32 -14
  32. package/src/claude/agents/debugger.md +35 -20
  33. package/src/claude/agents/deployer.md +12 -5
  34. package/src/claude/agents/docs-keeper.md +11 -1
  35. package/src/claude/agents/implementer.md +25 -13
  36. package/src/claude/agents/inspector.md +6 -3
  37. package/src/claude/agents/project-manager.md +1 -1
  38. package/src/claude/agents/researcher.md +26 -55
  39. package/src/claude/agents/spec-maker.md +24 -13
  40. package/src/claude/agents/strategist.md +52 -0
  41. package/src/claude/agents/test-runner.md +117 -132
  42. package/src/claude/agents/ui-ux-designer.md +1 -1
  43. package/src/claude/hooks/__tests__/fixtures/statusline-default.golden +1 -0
  44. package/src/claude/hooks/__tests__/precompact.test.js +158 -0
  45. package/src/claude/hooks/__tests__/spec-gate.test.js +87 -4
  46. package/src/claude/hooks/__tests__/state.test.js +254 -3
  47. package/src/claude/hooks/__tests__/statusline.test.js +196 -0
  48. package/src/claude/hooks/agent.cjs +1 -1
  49. package/src/claude/hooks/docs-sync.cjs +12 -10
  50. package/src/claude/hooks/inspect-block.cjs +1 -1
  51. package/src/claude/hooks/lib/config.cjs +3 -0
  52. package/src/claude/hooks/lib/hook-state-dir.cjs +39 -0
  53. package/src/claude/hooks/lib/secret-keywords.cjs +42 -0
  54. package/src/claude/hooks/precompact.cjs +105 -0
  55. package/src/claude/hooks/rules.cjs +1 -1
  56. package/src/claude/hooks/secret-output-guardrail.cjs +43 -0
  57. package/src/claude/hooks/session.cjs +27 -1
  58. package/src/claude/hooks/spec-gate.cjs +3 -3
  59. package/src/claude/hooks/spec-state.cjs +32 -5
  60. package/src/claude/hooks/state.cjs +1 -1
  61. package/src/claude/hooks/task-scaffold-guard.cjs +1 -1
  62. package/src/claude/hooks/usage.cjs +1 -1
  63. package/src/claude/migration-manifest.json +29 -15
  64. package/src/claude/output-styles/coding-level-0-eli5.md +103 -0
  65. package/src/claude/output-styles/coding-level-1-junior.md +124 -0
  66. package/src/claude/output-styles/coding-level-2-mid.md +146 -0
  67. package/src/claude/output-styles/coding-level-3-senior.md +148 -0
  68. package/src/claude/output-styles/coding-level-4-lead.md +159 -0
  69. package/src/claude/output-styles/coding-level-5-god.md +91 -0
  70. package/src/claude/references/debugger/core-philosophy.md +5 -3
  71. package/src/claude/references/debugger/frontend-verification.md +2 -2
  72. package/src/claude/references/debugger/log-ci-analysis.md +13 -1
  73. package/src/claude/references/debugger/parallel-agent-hydration.md +19 -34
  74. package/src/claude/references/debugger/performance-diagnostics.md +2 -2
  75. package/src/claude/references/debugger/root-cause-tracing.md +37 -6
  76. package/src/claude/references/debugger/side-effect-gate.md +3 -1
  77. package/src/claude/references/debugger/verification-protocol.md +13 -12
  78. package/src/claude/rules/orchestrator.md +7 -1
  79. package/src/claude/rules/process-management.md +39 -0
  80. package/src/claude/rules/review-audit-self-decision.md +48 -0
  81. package/src/claude/rules/skill-domain-routing.md +26 -24
  82. package/src/claude/rules/skill-workflow-routing.md +22 -14
  83. package/src/claude/rules/workflow.md +3 -3
  84. package/src/claude/runtime.json +1 -0
  85. package/src/claude/runtime.schema.json +205 -0
  86. package/src/claude/scripts/generate-skill-catalog.cjs +156 -122
  87. package/src/claude/scripts/provenance.cjs +18 -5
  88. package/src/claude/scripts/spec-receipt.cjs +76 -21
  89. package/src/claude/scripts/spec-resolver.cjs +144 -4
  90. package/src/claude/scripts/workflow-policy.cjs +1 -1
  91. package/src/claude/settings/settings.json +16 -0
  92. package/src/claude/skills/agent-browser/SKILL.md +1 -1
  93. package/src/claude/skills/ai-multimodal/SKILL.md +1 -1
  94. package/src/claude/skills/brainstorm/SKILL.md +193 -172
  95. package/src/claude/skills/brainstorm/references/question-framework.md +105 -167
  96. package/src/claude/skills/chrome-devtools/SKILL.md +1 -1
  97. package/src/claude/skills/code-review/SKILL.md +43 -25
  98. package/src/claude/skills/code-review/references/spec-compliance-review.md +3 -4
  99. package/src/claude/skills/code-review/references/verification-gate.md +1 -1
  100. package/src/claude/skills/debug/SKILL.md +48 -19
  101. package/src/claude/skills/delegate/SKILL.md +1 -1
  102. package/src/claude/skills/develop/SKILL.md +92 -65
  103. package/src/claude/skills/develop/references/parallel-waves.md +36 -63
  104. package/src/claude/skills/develop/references/quality-gate.md +31 -4
  105. package/src/claude/skills/develop/references/subagent-patterns.md +21 -7
  106. package/src/claude/skills/docs/SKILL.md +42 -23
  107. package/src/claude/skills/docs/references/init-workflow.md +4 -4
  108. package/src/claude/skills/docs/references/reconstruct-workflow.md +6 -6
  109. package/src/claude/skills/docs/references/standard-docs-workflow.md +6 -2
  110. package/src/claude/skills/docs/references/summarize-workflow.md +2 -2
  111. package/src/claude/skills/docs/references/update-workflow.md +9 -7
  112. package/src/claude/skills/docs/templates/requirements-as-is.md +1 -1
  113. package/src/claude/skills/docx/SKILL.md +1 -1
  114. package/src/claude/skills/git/SKILL.md +1 -1
  115. package/src/claude/skills/git/references/commit-protocols.md +1 -1
  116. package/src/claude/skills/git/references/finish-branch.md +1 -1
  117. package/src/claude/skills/hotfix/SKILL.md +159 -170
  118. package/src/claude/skills/hotfix/references/diagnosis-protocol.md +14 -9
  119. package/src/claude/skills/hotfix/references/parallel-patterns.md +27 -13
  120. package/src/claude/skills/hotfix/references/prevention-gate.md +5 -0
  121. package/src/claude/skills/hotfix/references/review-cycle.md +21 -15
  122. package/src/claude/skills/hotfix/references/workflow-specialized.md +53 -21
  123. package/src/claude/skills/inspect/SKILL.md +55 -46
  124. package/src/claude/skills/inspect/references/internal-inspection.md +21 -5
  125. package/src/claude/skills/loop/SKILL.md +114 -0
  126. package/src/claude/skills/loop/references/bounded-loop-protocol.md +104 -0
  127. package/src/claude/skills/loop/references/metric-and-guard-contract.md +76 -0
  128. package/src/claude/skills/pdf/SKILL.md +1 -1
  129. package/src/claude/skills/pptx/SKILL.md +1 -1
  130. package/src/claude/skills/question/SKILL.md +16 -13
  131. package/src/claude/skills/research/SKILL.md +70 -43
  132. package/src/claude/skills/route/SKILL.md +94 -0
  133. package/src/claude/skills/route/references/agent-timing.md +46 -0
  134. package/src/claude/skills/route/references/chaining-patterns.md +53 -0
  135. package/src/claude/skills/route/references/task-taxonomy.md +48 -0
  136. package/src/claude/skills/specs/SKILL.md +48 -57
  137. package/src/claude/skills/specs/references/review.md +26 -8
  138. package/src/claude/skills/specs/references/templates.md +76 -31
  139. package/src/claude/skills/specs/templates/requirements-init.md +1 -1
  140. package/src/claude/skills/specs/templates/research.md +2 -2
  141. package/src/claude/skills/sync/SKILL.md +6 -6
  142. package/src/claude/skills/sync/references/sync-protocols.md +1 -1
  143. package/src/claude/skills/test/SKILL.md +120 -120
  144. package/src/claude/skills/test/references/execution-strategy.md +208 -361
  145. package/src/claude/skills/test/references/failure-triage.md +55 -121
  146. package/src/claude/skills/test/references/test-memory.md +20 -34
  147. package/src/claude/skills/ui-ux-pro-max/SKILL.md +1 -1
  148. package/src/claude/skills/web-testing/SKILL.md +1 -1
  149. package/src/claude/skills/xlsx/SKILL.md +1 -1
  150. package/src/claude/status.cjs +135 -33
  151. package/src/codex/AGENTS.md +14 -4
  152. package/src/codex/hooks/docs-sync.cjs +8 -3
  153. package/src/codex/hooks/lib/hook-context.cjs +18 -4
  154. package/src/codex/hooks/rules.cjs +29 -1
  155. package/src/codex/hooks/secret-output-guardrail.cjs +30 -0
  156. package/src/codex/hooks/spec-gate.cjs +3 -8
  157. package/src/codex/hooks/spec-state.cjs +34 -5
  158. package/src/codex/hooks.json +5 -0
  159. package/src/codex/runtime.json +2 -0
  160. package/src/common/.env.example +1 -1
  161. package/src/claude/skills/backend-development/SKILL.md +0 -101
  162. package/src/claude/skills/backend-development/references/backend-api-design.md +0 -495
  163. package/src/claude/skills/backend-development/references/backend-architecture.md +0 -454
  164. package/src/claude/skills/backend-development/references/backend-authentication.md +0 -338
  165. package/src/claude/skills/backend-development/references/backend-code-quality.md +0 -659
  166. package/src/claude/skills/backend-development/references/backend-debugging.md +0 -904
  167. package/src/claude/skills/backend-development/references/backend-devops.md +0 -491
  168. package/src/claude/skills/backend-development/references/backend-mindset.md +0 -387
  169. package/src/claude/skills/backend-development/references/backend-performance.md +0 -397
  170. package/src/claude/skills/backend-development/references/backend-security.md +0 -342
  171. package/src/claude/skills/backend-development/references/backend-technologies.md +0 -256
  172. package/src/claude/skills/backend-development/references/backend-testing.md +0 -429
  173. package/src/claude/skills/devops/.env.example +0 -76
  174. package/src/claude/skills/devops/SKILL.md +0 -102
  175. package/src/claude/skills/devops/references/browser-rendering.md +0 -305
  176. package/src/claude/skills/devops/references/cloudflare-d1-kv.md +0 -123
  177. package/src/claude/skills/devops/references/cloudflare-platform.md +0 -271
  178. package/src/claude/skills/devops/references/cloudflare-r2-storage.md +0 -280
  179. package/src/claude/skills/devops/references/cloudflare-workers-advanced.md +0 -312
  180. package/src/claude/skills/devops/references/cloudflare-workers-apis.md +0 -309
  181. package/src/claude/skills/devops/references/cloudflare-workers-basics.md +0 -418
  182. package/src/claude/skills/devops/references/docker-basics.md +0 -297
  183. package/src/claude/skills/devops/references/docker-compose.md +0 -292
  184. package/src/claude/skills/devops/references/gcloud-platform.md +0 -297
  185. package/src/claude/skills/devops/references/gcloud-services.md +0 -304
  186. package/src/claude/skills/devops/references/kubernetes-basics.md +0 -95
  187. package/src/claude/skills/devops/references/kubernetes-helm-advanced.md +0 -70
  188. package/src/claude/skills/devops/references/kubernetes-helm.md +0 -81
  189. package/src/claude/skills/devops/references/kubernetes-kubectl.md +0 -74
  190. package/src/claude/skills/devops/references/kubernetes-security-advanced.md +0 -94
  191. package/src/claude/skills/devops/references/kubernetes-security.md +0 -89
  192. package/src/claude/skills/devops/references/kubernetes-troubleshooting-advanced.md +0 -74
  193. package/src/claude/skills/devops/references/kubernetes-troubleshooting.md +0 -49
  194. package/src/claude/skills/devops/references/kubernetes-workflows-advanced.md +0 -75
  195. package/src/claude/skills/devops/references/kubernetes-workflows.md +0 -76
  196. package/src/claude/skills/devops/scripts/cloudflare_deploy.py +0 -269
  197. package/src/claude/skills/devops/scripts/docker_optimize.py +0 -332
  198. package/src/claude/skills/devops/scripts/requirements.txt +0 -20
  199. package/src/claude/skills/devops/scripts/tests/requirements.txt +0 -3
  200. package/src/claude/skills/devops/scripts/tests/test_cloudflare_deploy.py +0 -285
  201. package/src/claude/skills/devops/scripts/tests/test_docker_optimize.py +0 -436
  202. package/src/claude/skills/frontend-design/SKILL.md +0 -135
  203. package/src/claude/skills/frontend-design/references/ai-multimodal-overview.md +0 -165
  204. package/src/claude/skills/frontend-design/references/analysis-best-practices.md +0 -80
  205. package/src/claude/skills/frontend-design/references/analysis-prompts.md +0 -141
  206. package/src/claude/skills/frontend-design/references/analysis-techniques.md +0 -118
  207. package/src/claude/skills/frontend-design/references/animejs.md +0 -396
  208. package/src/claude/skills/frontend-design/references/anti-slop-rules.md +0 -103
  209. package/src/claude/skills/frontend-design/references/asset-generation.md +0 -337
  210. package/src/claude/skills/frontend-design/references/bento-motion-engine.md +0 -142
  211. package/src/claude/skills/frontend-design/references/design-extraction-overview.md +0 -71
  212. package/src/claude/skills/frontend-design/references/extraction-best-practices.md +0 -141
  213. package/src/claude/skills/frontend-design/references/extraction-output-templates.md +0 -162
  214. package/src/claude/skills/frontend-design/references/extraction-prompts.md +0 -127
  215. package/src/claude/skills/frontend-design/references/magicui-components.md +0 -129
  216. package/src/claude/skills/frontend-design/references/performance-guardrails.md +0 -169
  217. package/src/claude/skills/frontend-design/references/premium-design-patterns.md +0 -93
  218. package/src/claude/skills/frontend-design/references/redesign-audit-checklist.md +0 -114
  219. package/src/claude/skills/frontend-design/references/technical-accessibility.md +0 -119
  220. package/src/claude/skills/frontend-design/references/technical-best-practices.md +0 -97
  221. package/src/claude/skills/frontend-design/references/technical-optimization.md +0 -44
  222. package/src/claude/skills/frontend-design/references/technical-overview.md +0 -90
  223. package/src/claude/skills/frontend-design/references/technical-workflows.md +0 -150
  224. package/src/claude/skills/frontend-design/references/visual-analysis-overview.md +0 -95
  225. package/src/claude/skills/frontend-design/references/workflow-3d.md +0 -102
  226. package/src/claude/skills/frontend-design/references/workflow-describe.md +0 -87
  227. package/src/claude/skills/frontend-design/references/workflow-immersive.md +0 -87
  228. package/src/claude/skills/frontend-design/references/workflow-quick.md +0 -57
  229. package/src/claude/skills/frontend-design/references/workflow-screenshot.md +0 -63
  230. package/src/claude/skills/frontend-design/references/workflow-video.md +0 -74
  231. package/src/claude/skills/frontend-development/SKILL.md +0 -406
  232. package/src/claude/skills/frontend-development/resources/common-patterns.md +0 -331
  233. package/src/claude/skills/frontend-development/resources/complete-examples.md +0 -872
  234. package/src/claude/skills/frontend-development/resources/component-patterns.md +0 -502
  235. package/src/claude/skills/frontend-development/resources/data-fetching.md +0 -767
  236. package/src/claude/skills/frontend-development/resources/file-organization.md +0 -502
  237. package/src/claude/skills/frontend-development/resources/loading-and-error-states.md +0 -501
  238. package/src/claude/skills/frontend-development/resources/performance.md +0 -406
  239. package/src/claude/skills/frontend-development/resources/routing-guide.md +0 -364
  240. package/src/claude/skills/frontend-development/resources/styling-guide.md +0 -428
  241. package/src/claude/skills/frontend-development/resources/typescript-standards.md +0 -418
  242. package/src/claude/skills/mobile-development/SKILL.md +0 -218
  243. package/src/claude/skills/mobile-development/references/mobile-android.md +0 -604
  244. package/src/claude/skills/mobile-development/references/mobile-best-practices.md +0 -544
  245. package/src/claude/skills/mobile-development/references/mobile-debugging.md +0 -1089
  246. package/src/claude/skills/mobile-development/references/mobile-frameworks.md +0 -465
  247. package/src/claude/skills/mobile-development/references/mobile-ios.md +0 -496
  248. package/src/claude/skills/mobile-development/references/mobile-mindset.md +0 -542
  249. package/src/claude/skills/react-best-practices/AGENTS.md +0 -2249
  250. package/src/claude/skills/react-best-practices/README.md +0 -123
  251. package/src/claude/skills/react-best-practices/SKILL.md +0 -128
  252. package/src/claude/skills/react-best-practices/metadata.json +0 -15
  253. package/src/claude/skills/react-best-practices/rules/_sections.md +0 -46
  254. package/src/claude/skills/react-best-practices/rules/_template.md +0 -28
  255. package/src/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +0 -55
  256. package/src/claude/skills/react-best-practices/rules/advanced-use-latest.md +0 -49
  257. package/src/claude/skills/react-best-practices/rules/async-api-routes.md +0 -38
  258. package/src/claude/skills/react-best-practices/rules/async-defer-await.md +0 -80
  259. package/src/claude/skills/react-best-practices/rules/async-dependencies.md +0 -36
  260. package/src/claude/skills/react-best-practices/rules/async-parallel.md +0 -28
  261. package/src/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +0 -99
  262. package/src/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +0 -59
  263. package/src/claude/skills/react-best-practices/rules/bundle-conditional.md +0 -31
  264. package/src/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +0 -49
  265. package/src/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +0 -35
  266. package/src/claude/skills/react-best-practices/rules/bundle-preload.md +0 -50
  267. package/src/claude/skills/react-best-practices/rules/client-event-listeners.md +0 -74
  268. package/src/claude/skills/react-best-practices/rules/client-swr-dedup.md +0 -56
  269. package/src/claude/skills/react-best-practices/rules/js-batch-dom-css.md +0 -82
  270. package/src/claude/skills/react-best-practices/rules/js-cache-function-results.md +0 -80
  271. package/src/claude/skills/react-best-practices/rules/js-cache-property-access.md +0 -28
  272. package/src/claude/skills/react-best-practices/rules/js-cache-storage.md +0 -70
  273. package/src/claude/skills/react-best-practices/rules/js-combine-iterations.md +0 -32
  274. package/src/claude/skills/react-best-practices/rules/js-early-exit.md +0 -50
  275. package/src/claude/skills/react-best-practices/rules/js-hoist-regexp.md +0 -45
  276. package/src/claude/skills/react-best-practices/rules/js-index-maps.md +0 -37
  277. package/src/claude/skills/react-best-practices/rules/js-length-check-first.md +0 -49
  278. package/src/claude/skills/react-best-practices/rules/js-min-max-loop.md +0 -82
  279. package/src/claude/skills/react-best-practices/rules/js-set-map-lookups.md +0 -24
  280. package/src/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +0 -57
  281. package/src/claude/skills/react-best-practices/rules/rendering-activity.md +0 -26
  282. package/src/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
  283. package/src/claude/skills/react-best-practices/rules/rendering-conditional-render.md +0 -40
  284. package/src/claude/skills/react-best-practices/rules/rendering-content-visibility.md +0 -38
  285. package/src/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +0 -46
  286. package/src/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
  287. package/src/claude/skills/react-best-practices/rules/rendering-svg-precision.md +0 -28
  288. package/src/claude/skills/react-best-practices/rules/rerender-defer-reads.md +0 -39
  289. package/src/claude/skills/react-best-practices/rules/rerender-dependencies.md +0 -45
  290. package/src/claude/skills/react-best-practices/rules/rerender-derived-state.md +0 -29
  291. package/src/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +0 -74
  292. package/src/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +0 -58
  293. package/src/claude/skills/react-best-practices/rules/rerender-memo.md +0 -44
  294. package/src/claude/skills/react-best-practices/rules/rerender-transitions.md +0 -40
  295. package/src/claude/skills/react-best-practices/rules/server-after-nonblocking.md +0 -73
  296. package/src/claude/skills/react-best-practices/rules/server-cache-lru.md +0 -41
  297. package/src/claude/skills/react-best-practices/rules/server-cache-react.md +0 -26
  298. package/src/claude/skills/react-best-practices/rules/server-parallel-fetching.md +0 -79
  299. package/src/claude/skills/react-best-practices/rules/server-serialization.md +0 -38
@@ -1,184 +1,218 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * CafeKit skill catalog scanner.
4
- *
5
- * This mirrors the Research/.claude approach: expose the installed skill set as
6
- * a readable catalog so the model can choose skills from rules, not from an
7
- * automatic prompt-scoring hook.
8
- */
2
+ /** Generate a fail-closed catalog from the current runtime's installed skills. */
3
+
4
+ 'use strict';
9
5
 
10
6
  const fs = require('fs');
11
7
  const path = require('path');
12
8
 
9
+ // Keep synchronized with migration-manifest.json obsolete.skills. Modified
10
+ // retired directories may remain on disk, but they are never automatic routes.
11
+ const RETIRED_DIRECTORIES = new Set([
12
+ 'backend-development',
13
+ 'frontend-development',
14
+ 'frontend-design',
15
+ 'mobile-development',
16
+ 'devops',
17
+ 'react-best-practices',
18
+ ]);
19
+ const PUBLIC_SUFFIX_BY_DIRECTORY = {
20
+ hotfix: 'fix',
21
+ inspect: 'scout',
22
+ question: 'ask',
23
+ };
24
+
13
25
  function usage() {
14
26
  console.log(`Usage:
15
- node .claude/scripts/generate-skill-catalog.cjs [--skills] [--json] [--root <skills-dir>]
27
+ node <runtime>/scripts/generate-skill-catalog.cjs [--skills] [--json] [--root <skills-dir>]
16
28
 
17
- Examples:
18
- node .claude/scripts/generate-skill-catalog.cjs --skills
19
- node .claude/scripts/generate-skill-catalog.cjs --json --root .claude/skills`);
29
+ The default root is bound to the script runtime: .claude/skills for Claude,
30
+ .agents/skills for Codex, and ../skills for the source tree.`);
20
31
  }
21
32
 
22
33
  function parseArgs(argv) {
23
34
  const args = { json: false, help: false, root: null };
24
- for (let i = 0; i < argv.length; i += 1) {
25
- const arg = argv[i];
35
+ for (let index = 0; index < argv.length; index += 1) {
36
+ const arg = argv[index];
26
37
  if (arg === '--help' || arg === '-h') args.help = true;
27
38
  else if (arg === '--json') args.json = true;
28
39
  else if (arg === '--skills') continue;
29
40
  else if (arg === '--root') {
30
- args.root = argv[i + 1] || null;
31
- i += 1;
32
- } else {
33
- args.root = args.root || arg;
34
- }
41
+ if (!argv[index + 1] || argv[index + 1].startsWith('--')) {
42
+ throw new Error('--root requires a skills directory');
43
+ }
44
+ args.root = argv[++index];
45
+ } else throw new Error(`unknown option: ${arg}`);
35
46
  }
36
47
  return args;
37
48
  }
38
49
 
39
50
  function defaultSkillsRoot() {
40
- const candidates = [
41
- path.resolve(__dirname, '..', 'skills'),
42
- path.resolve(process.cwd(), '.claude', 'skills'),
43
- path.resolve(process.cwd(), '.agents', 'skills'),
44
- path.resolve(__dirname, '..', '..', '.agents', 'skills')
45
- ];
46
- return candidates.find((candidate) => fs.existsSync(candidate)) || candidates[0];
47
- }
48
-
49
- function readText(filePath) {
50
- return fs.readFileSync(filePath, 'utf8');
51
+ const runtimeRoot = path.dirname(__dirname);
52
+ const runtimeName = path.basename(runtimeRoot);
53
+ if (runtimeName === '.codex') return path.join(path.dirname(runtimeRoot), '.agents', 'skills');
54
+ const claudeRuntimeName = `.${['cla', 'ude'].join('')}`;
55
+ if (runtimeName === claudeRuntimeName) return path.join(runtimeRoot, 'skills');
56
+ return path.resolve(__dirname, '..', 'skills');
51
57
  }
52
58
 
53
59
  function extractFrontmatter(content) {
54
- const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
55
- if (!match) return {};
60
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
61
+ if (!match) return null;
56
62
  const frontmatter = {};
57
63
  const lines = match[1].split(/\r?\n/);
58
- for (let i = 0; i < lines.length; i += 1) {
59
- const line = lines[i];
60
- const kv = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
61
- if (!kv) continue;
62
- const key = kv[1];
63
- let value = kv[2].trim();
64
- if (value === '>' || value === '>-' || value === '|' || value === '|-') {
64
+ const seen = new Set();
65
+ let nestedKey = null;
66
+ for (let index = 0; index < lines.length; index += 1) {
67
+ const line = lines[index];
68
+ if (line.trim() === '') continue;
69
+ if (/^\s+/.test(line)) {
70
+ const nested = line.match(/^ ([A-Za-z0-9_-]+):\s*(\S.*)$/);
71
+ if (!nestedKey || !nested) return null;
72
+ const identity = `${nestedKey}.${nested[1]}`;
73
+ if (seen.has(identity) || quotedValue(nested[2]) === null) return null;
74
+ seen.add(identity);
75
+ continue;
76
+ }
77
+ nestedKey = null;
78
+ const parsed = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
79
+ if (!parsed || seen.has(parsed[1])) return null;
80
+ seen.add(parsed[1]);
81
+ let value = parsed[2].trim();
82
+ if (['>', '>-', '|', '|-'].includes(value)) {
65
83
  const block = [];
66
- i += 1;
67
- while (i < lines.length && /^\s+/.test(lines[i])) {
68
- block.push(lines[i].trim());
69
- i += 1;
84
+ while (index + 1 < lines.length && /^\s+/.test(lines[index + 1])) {
85
+ block.push(lines[++index].trim());
70
86
  }
71
- i -= 1;
87
+ if (block.length === 0) return null;
72
88
  value = block.join(' ');
73
89
  }
74
- if (
75
- (value.startsWith('"') && value.endsWith('"')) ||
76
- (value.startsWith("'") && value.endsWith("'"))
77
- ) {
78
- value = value.slice(1, -1);
90
+ if (value === '') {
91
+ nestedKey = parsed[1];
92
+ frontmatter[parsed[1]] = '';
93
+ continue;
79
94
  }
80
- frontmatter[key] = value;
95
+ value = quotedValue(value);
96
+ if (value === null) return null;
97
+ frontmatter[parsed[1]] = value;
81
98
  }
82
99
  return frontmatter;
83
100
  }
84
101
 
85
- function firstParagraph(content) {
86
- const body = content.replace(/^---\s*\n[\s\S]*?\n---\s*\n/, '');
87
- const paragraph = [];
88
- for (const rawLine of body.split(/\r?\n/)) {
89
- const line = rawLine.trim();
90
- if (!line || line.startsWith('#')) {
91
- if (paragraph.length > 0) break;
92
- continue;
93
- }
94
- paragraph.push(line);
95
- if (paragraph.join(' ').length > 160) break;
102
+ function quotedValue(value) {
103
+ const startsQuote = value.startsWith('"') || value.startsWith("'");
104
+ const endsQuote = value.endsWith('"') || value.endsWith("'");
105
+ const startsFlow = value.startsWith('[') || value.startsWith('{');
106
+ const endsFlow = value.endsWith(']') || value.endsWith('}');
107
+ if (startsFlow || endsFlow) {
108
+ const expected = value.startsWith('[') ? ']' : value.startsWith('{') ? '}' : null;
109
+ if (!expected || !value.endsWith(expected)) return null;
96
110
  }
97
- return paragraph.join(' ').slice(0, 360);
111
+ if (!startsQuote && !endsQuote) return value;
112
+ if (value.length < 2 || value[0] !== value[value.length - 1]) return null;
113
+ return value.slice(1, -1);
98
114
  }
99
115
 
100
- function categorize(name, description) {
101
- const text = `${name} ${description}`.toLowerCase();
102
- if (/spec|brainstorm|develop|sync/.test(text)) return 'workflow';
103
- if (/debug|hotfix|inspect|impact|review|test|agent-browser|chrome/.test(text)) return 'quality';
104
- if (/frontend|react|ui|ux|mobile/.test(text)) return 'frontend-mobile';
105
- if (/pdf|docx|pptx|xlsx|multimodal/.test(text)) return 'artifacts-tools';
106
- if (/backend|devops|api|database|deploy/.test(text)) return 'backend-infra';
107
- if (/docs|research|graph/.test(text)) return 'knowledge';
108
- if (/git/.test(text)) return 'git';
109
- return 'other';
116
+ function parseKeywords(value) {
117
+ if (!value || !/^\[[^\]]*\]$/.test(value)) return [];
118
+ return value.slice(1, -1).split(',').map((item) => item.trim()).filter(Boolean);
110
119
  }
111
120
 
112
- function scanSkills(root) {
113
- if (!fs.existsSync(root)) {
114
- throw new Error(`skills root not found: ${root}`);
115
- }
116
- const entries = fs.readdirSync(root, { withFileTypes: true });
117
- const skills = [];
121
+ function publicIdentity(name) {
122
+ const match = String(name).match(/^cf(?::|-)([a-z0-9]+(?:-[a-z0-9]+)*)$/);
123
+ return match ? `cf:${match[1]}` : null;
124
+ }
125
+
126
+ function expectedIdentity(directory) {
127
+ return `cf:${PUBLIC_SUFFIX_BY_DIRECTORY[directory] || directory}`;
128
+ }
129
+
130
+ function diagnostic(code, directory, message) {
131
+ return { code, directory, message };
132
+ }
133
+
134
+ function scanSkills(requestedRoot) {
135
+ if (!fs.existsSync(requestedRoot)) throw new Error(`skills root not found: ${requestedRoot}`);
136
+ const root = fs.realpathSync(requestedRoot);
137
+ const diagnostics = [];
138
+ const candidates = [];
139
+ const entries = fs.readdirSync(root, { withFileTypes: true })
140
+ .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))
141
+ .sort((left, right) => left.name.localeCompare(right.name));
142
+
118
143
  for (const entry of entries) {
119
- if (!entry.isDirectory() || entry.name.startsWith('.')) continue;
120
144
  const skillPath = path.join(root, entry.name, 'SKILL.md');
121
145
  if (!fs.existsSync(skillPath)) continue;
122
- const content = readText(skillPath);
123
- const frontmatter = extractFrontmatter(content);
124
- const name = frontmatter.name || entry.name;
125
- const description = frontmatter.description || firstParagraph(content);
126
- skills.push({
127
- name,
146
+ if (RETIRED_DIRECTORIES.has(entry.name)) {
147
+ diagnostics.push(diagnostic('retired_skill', entry.name, 'excluded from automatic routing'));
148
+ continue;
149
+ }
150
+ const frontmatter = extractFrontmatter(fs.readFileSync(skillPath, 'utf8'));
151
+ if (!frontmatter) {
152
+ diagnostics.push(diagnostic('malformed_frontmatter', entry.name, 'missing frontmatter block'));
153
+ continue;
154
+ }
155
+ const identity = publicIdentity(frontmatter.name);
156
+ if (!identity || !frontmatter.description || frontmatter.description.trim() === '') {
157
+ diagnostics.push(diagnostic('missing_routing_metadata', entry.name, 'requires valid name and description'));
158
+ continue;
159
+ }
160
+ if (identity !== expectedIdentity(entry.name)) {
161
+ diagnostics.push(diagnostic('folder_name_mismatch', entry.name, `${identity} does not match directory`));
162
+ continue;
163
+ }
164
+ candidates.push({
165
+ name: frontmatter.name,
166
+ public_id: identity,
128
167
  directory: entry.name,
129
- description,
130
- category: categorize(name, description),
168
+ description: frontmatter.description,
169
+ when_to_use: frontmatter.when_to_use || '',
170
+ category: frontmatter.category || 'other',
171
+ keywords: parseKeywords(frontmatter.keywords),
172
+ user_invocable: frontmatter['user-invocable'] !== 'false',
131
173
  has_references: fs.existsSync(path.join(root, entry.name, 'references')),
132
174
  has_scripts: fs.existsSync(path.join(root, entry.name, 'scripts')),
133
175
  });
134
176
  }
135
- return skills.sort((a, b) => a.name.localeCompare(b.name));
177
+
178
+ const counts = new Map();
179
+ for (const skill of candidates) counts.set(skill.public_id, (counts.get(skill.public_id) || 0) + 1);
180
+ const skills = candidates.filter((skill) => {
181
+ if (counts.get(skill.public_id) === 1) return true;
182
+ diagnostics.push(diagnostic('duplicate_public_name', skill.directory, `${skill.public_id} requires explicit user disambiguation`));
183
+ return false;
184
+ }).sort((left, right) => left.public_id.localeCompare(right.public_id));
185
+
186
+ return { root, skills, diagnostics: diagnostics.sort((left, right) =>
187
+ `${left.directory}:${left.code}`.localeCompare(`${right.directory}:${right.code}`)) };
136
188
  }
137
189
 
138
- function renderMarkdown(skills, root) {
139
- const lines = [
140
- '# CafeKit Skills Catalog',
141
- '',
142
- `Skills root: \`${root}\``,
143
- `Total skills: ${skills.length}`,
144
- '',
145
- ];
146
- const categories = [...new Set(skills.map((skill) => skill.category))].sort();
147
- for (const category of categories) {
148
- lines.push(`## ${category}`, '');
149
- for (const skill of skills.filter((item) => item.category === category)) {
150
- const badges = [
151
- skill.has_references ? 'refs' : null,
152
- skill.has_scripts ? 'scripts' : null,
153
- ].filter(Boolean);
154
- lines.push(
155
- `- \`${skill.name}\` (${skill.directory})${badges.length ? ` [${badges.join(', ')}]` : ''}: ${skill.description}`
156
- );
157
- }
158
- lines.push('');
190
+ function renderMarkdown(catalog) {
191
+ const lines = ['# CafeKit Skills Catalog', '', `Skills root: \`${catalog.root}\``,
192
+ `Routable skills: ${catalog.skills.length}`, ''];
193
+ for (const skill of catalog.skills) {
194
+ const intent = skill.when_to_use ? ` Use: ${skill.when_to_use}` : '';
195
+ lines.push(`- \`${skill.name}\` (${skill.directory}, ${skill.category}): ${skill.description}${intent}`);
159
196
  }
160
- return lines.join('\n').trim() + '\n';
197
+ if (catalog.diagnostics.length > 0) {
198
+ lines.push('', '## Diagnostics', '');
199
+ for (const item of catalog.diagnostics) lines.push(`- ${item.code} (${item.directory}): ${item.message}`);
200
+ }
201
+ return `${lines.join('\n').trim()}\n`;
161
202
  }
162
203
 
163
204
  function main() {
164
205
  const args = parseArgs(process.argv.slice(2));
165
- if (args.help) {
166
- usage();
167
- return;
168
- }
169
-
170
- const root = path.resolve(process.cwd(), args.root || defaultSkillsRoot());
171
- const skills = scanSkills(root);
172
- if (args.json) {
173
- console.log(JSON.stringify({ root, total: skills.length, skills }, null, 2));
174
- return;
175
- }
176
- console.log(renderMarkdown(skills, root));
206
+ if (args.help) return usage();
207
+ const requestedRoot = path.resolve(process.cwd(), args.root || defaultSkillsRoot());
208
+ const catalog = scanSkills(requestedRoot);
209
+ if (args.json) console.log(JSON.stringify({ ...catalog, total: catalog.skills.length }, null, 2));
210
+ else console.log(renderMarkdown(catalog));
177
211
  }
178
212
 
179
213
  try {
180
214
  main();
181
215
  } catch (error) {
182
216
  console.error(`[skill-catalog] ${error.message}`);
183
- process.exit(1);
217
+ process.exitCode = 1;
184
218
  }
@@ -83,9 +83,22 @@ function oneLine(value, label) {
83
83
  return output.replace(/\n$/, '');
84
84
  }
85
85
 
86
- function gitBase(root) {
87
- const output = oneLine(runGit(root, ['rev-parse', 'HEAD'], 'git rev-parse HEAD')).toLowerCase();
88
- if (!/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/.test(output)) fail('malformed_git_output', 'git rev-parse HEAD did not return a commit id');
86
+ function gitBase(root, specsRoot) {
87
+ const specsRelative = path.relative(root, specsRoot).split(path.sep).join('/');
88
+ const sourceHistory = decode(runGit(
89
+ root,
90
+ ['log', '-1', '--format=%H', '--', '.', `:(exclude,literal)${specsRelative}`],
91
+ 'git log source base',
92
+ )).trim();
93
+ const roots = sourceHistory ? [] : decode(
94
+ runGit(root, ['rev-list', '--max-parents=0', '--reverse', 'HEAD'], 'git rev-list root'),
95
+ 'git rev-list root',
96
+ ).trim().split('\n');
97
+ if (!sourceHistory && (roots.length === 0 || roots.some((rootId) => !/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/i.test(rootId)))) {
98
+ fail('malformed_git_output', 'git rev-list root did not return commit ids');
99
+ }
100
+ const output = (sourceHistory || roots[0]).toLowerCase();
101
+ if (!/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/.test(output)) fail('malformed_git_output', 'git source base did not return a commit id');
89
102
  return output;
90
103
  }
91
104
 
@@ -211,11 +224,11 @@ function identity(input) {
211
224
 
212
225
  function deriveRuntimeProvenance(input) {
213
226
  const current = identity(input);
214
- const base = gitBase(current.root);
227
+ const base = gitBase(current.root, current.specsRoot);
215
228
  const first = manifest(current.root, current.specsRoot);
216
229
  const head = manifestDigest(first);
217
230
  const second = manifest(current.root, current.specsRoot);
218
- if (JSON.stringify(first) !== JSON.stringify(second) || gitBase(current.root) !== base) fail('worktree_race', 'checkout changed during provenance capture');
231
+ if (JSON.stringify(first) !== JSON.stringify(second) || gitBase(current.root, current.specsRoot) !== base) fail('worktree_race', 'checkout changed during provenance capture');
219
232
  const stable = {
220
233
  schema_version: CONTEXT_SCHEMA_VERSION,
221
234
  project_root: current.root,
@@ -3,6 +3,7 @@
3
3
  const crypto = require('crypto');
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
+ const { annotatedMarkdownLines } = require('./spec-resolver.cjs');
6
7
 
7
8
  const EVIDENCE_HEADINGS = new Set([
8
9
  'Evidence',
@@ -67,21 +68,46 @@ function evidenceBody(text, policy = {}) {
67
68
  }
68
69
 
69
70
  function workflowReceiptBody(text, policy = {}) {
70
- const lines = String(text || '').split('\n');
71
+ const lines = annotatedMarkdownLines(text);
71
72
  const starts = [];
72
73
  for (let index = 0; index < lines.length; index += 1) {
73
- if (/^##\s+Receipt\s*$/.test(lines[index])) starts.push(index + 1);
74
+ if (lines[index].outsideFence && /^##\s+Receipt\s*$/.test(lines[index].line)) starts.push(index + 1);
74
75
  }
75
76
  if (starts.length !== 1) return null;
76
77
 
77
78
  const start = starts[0];
78
79
  let end = lines.length;
79
80
  for (let index = start; index < lines.length; index += 1) {
80
- const heading = lines[index].match(/^(#{1,6})\s+/);
81
- const tap = typeof policy.isTapMetadataHeading === 'function' && policy.isTapMetadataHeading(lines[index]);
81
+ if (!lines[index].outsideFence) continue;
82
+ const heading = lines[index].line.match(/^(#{1,6})\s+/);
83
+ const tap = typeof policy.isTapMetadataHeading === 'function' && policy.isTapMetadataHeading(lines[index].line);
82
84
  if (heading && heading[1].length <= 2 && !tap) { end = index; break; }
83
85
  }
84
- return lines.slice(start, end).join('\n');
86
+ return lines.slice(start, end).map(({ line }) => line).join('\n');
87
+ }
88
+
89
+ function workflowCanonicalValidationBody(body) {
90
+ return annotatedMarkdownLines(body)
91
+ .filter(({ outsideFence }) => outsideFence)
92
+ .map(({ line }) => line)
93
+ .join('\n');
94
+ }
95
+
96
+ function hasNonEmptyFencedBlock(body) {
97
+ let open = false;
98
+ let nonEmpty = false;
99
+ for (const { line, fenceEvent } of annotatedMarkdownLines(body)) {
100
+ if (fenceEvent === 'open') {
101
+ open = true;
102
+ nonEmpty = false;
103
+ } else if (fenceEvent === 'close') {
104
+ if (open && nonEmpty) return true;
105
+ open = false;
106
+ } else if (open && line.trim() !== '') {
107
+ nonEmpty = true;
108
+ }
109
+ }
110
+ return false;
85
111
  }
86
112
 
87
113
  function fieldValues(body, name) {
@@ -105,22 +131,14 @@ function normalizedCommand(value) {
105
131
  function workflowVerificationCommand(taskText) {
106
132
  const sections = [];
107
133
  let current = null;
108
- let fence = null;
109
- for (const line of String(taskText || '').split('\n')) {
110
- const marker = line.match(/^\s{0,3}(`{3,}|~{3,})/);
111
- if (marker) {
112
- const kind = marker[1][0];
113
- if (fence === null) fence = kind;
114
- else if (fence === kind) fence = null;
115
- continue;
116
- }
117
- if (fence !== null) continue;
118
- if (fence === null && /^##\s+Verification Plan\s*$/.test(line)) {
134
+ for (const { line, outsideFence } of annotatedMarkdownLines(taskText)) {
135
+ if (!outsideFence) continue;
136
+ if (/^##\s+Verification Plan\s*$/.test(line)) {
119
137
  current = [];
120
138
  sections.push(current);
121
139
  continue;
122
140
  }
123
- if (fence === null && current && /^#{1,2}\s+/.test(line)) {
141
+ if (current && /^#{1,2}\s+/.test(line)) {
124
142
  current = null;
125
143
  continue;
126
144
  }
@@ -227,7 +245,10 @@ function checkWorkflowTaskReceipt(featureDir, taskPath, runtimeContext, policy)
227
245
  }
228
246
 
229
247
  const taskText = taskFile.bytes.toString('utf8');
230
- const statusLines = [...taskText.matchAll(/^\s*(?:\*\*)?Status(?:\*\*)?\s*:\s*(?:\*\*)?\s*([A-Za-z_-]+)(?:\*\*)?\s*$/gim)];
248
+ const statusLines = annotatedMarkdownLines(taskText)
249
+ .filter(({ outsideFence }) => outsideFence)
250
+ .map(({ line }) => line.match(/^\s*(?:\*\*)?Status(?:\*\*)?\s*:\s*(?:\*\*)?\s*([A-Za-z_-]+)(?:\*\*)?\s*$/i))
251
+ .filter(Boolean);
231
252
  const body = workflowReceiptBody(taskText, policy);
232
253
  const failures = [];
233
254
  if (statusLines.length !== 1 || statusLines[0][1].toLowerCase().replaceAll('-', '_') !== 'done') {
@@ -236,12 +257,15 @@ function checkWorkflowTaskReceipt(featureDir, taskPath, runtimeContext, policy)
236
257
  if (body === null) {
237
258
  failures.push('missing_receipt');
238
259
  } else {
260
+ const canonicalBody = workflowCanonicalValidationBody(body);
239
261
  const options = typeof policy?.receiptValidatorOptions === 'function'
240
262
  ? policy.receiptValidatorOptions({}, { runtimeContext, requireProvenanceBinding: true })
241
263
  : { requireProvenanceBinding: true };
242
- failures.push(...canonicalFailures(body, {}, runtimeContext, policy));
243
- failures.push(...workflowCommandFailures(taskText, body));
244
- if (!/```[^\n]*\n[\s\S]*?\S[\s\S]*?\n```/m.test(body)) failures.push('command_output');
264
+ failures.push(...canonicalFailures(canonicalBody, {}, runtimeContext, policy));
265
+ failures.push(...canonicalFailures(body, {}, runtimeContext, policy)
266
+ .filter((failure) => ['placeholder', 'verification_state', 'exit_result'].includes(failure)));
267
+ failures.push(...workflowCommandFailures(taskText, canonicalBody));
268
+ if (!hasNonEmptyFencedBlock(body)) failures.push('command_output');
245
269
  if (!options.expectedProvenance) failures.push('provenance');
246
270
  }
247
271
  return {
@@ -256,6 +280,36 @@ function checkWorkflowTaskReceipt(featureDir, taskPath, runtimeContext, policy)
256
280
  };
257
281
  }
258
282
 
283
+ function workflowProofSignature(taskPath, proof) {
284
+ const hash = crypto.createHash('sha256');
285
+ hash.update('cafekit-workflow-proof-v1\0');
286
+ hash.update(String(taskPath));
287
+ hash.update('\0');
288
+ hash.update(proof.taskFile?.bytes || Buffer.alloc(0));
289
+ hash.update('\0');
290
+ hash.update(proof.receiptBytes || Buffer.alloc(0));
291
+ hash.update('\0');
292
+ hash.update(String(proof.status || 'unknown'));
293
+ return hash.digest('hex');
294
+ }
295
+
296
+ function workflowDependencyProofState(featureDir, taskRegistry, runtimeContext, policy) {
297
+ const tasks = {};
298
+ for (const taskPath of Object.keys(taskRegistry || {}).sort()) {
299
+ const done = taskRegistry[taskPath]?.status === 'done';
300
+ const proof = checkWorkflowTaskReceipt(featureDir, taskPath, runtimeContext, policy);
301
+ const valid = proof.failures.length === 0;
302
+ tasks[taskPath] = {
303
+ done,
304
+ valid,
305
+ eligible: done && valid,
306
+ signature: workflowProofSignature(taskPath, proof),
307
+ failures: [...proof.failures],
308
+ };
309
+ }
310
+ return tasks;
311
+ }
312
+
259
313
  function checkWorkflowReceiptSet(candidates, projectRoot, runtimeSession, policy) {
260
314
  const failures = [];
261
315
  for (const candidate of Array.isArray(candidates) ? candidates : []) {
@@ -298,5 +352,6 @@ module.exports = {
298
352
  evidenceBody,
299
353
  readTaskProof,
300
354
  safeRead,
355
+ workflowDependencyProofState,
301
356
  workflowReceiptBody,
302
357
  };