@haposoft/cafekit 0.16.7 → 0.18.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 (231) hide show
  1. package/README.md +14 -22
  2. package/bin/__tests__/codex-hooks-ownership.test.js +2 -2
  3. package/bin/__tests__/codex-hooks.test.js +169 -272
  4. package/bin/__tests__/codex-native.test.js +100 -202
  5. package/bin/__tests__/develop-contract.test.js +122 -1413
  6. package/bin/__tests__/docs-sync-on-demand.test.js +93 -0
  7. package/bin/__tests__/grok-envelope.test.js +0 -56
  8. package/bin/__tests__/hook-payload.test.js +4 -6
  9. package/bin/__tests__/legacy-authority-decoupled.test.js +154 -0
  10. package/bin/__tests__/legacy-hooks-retired.test.js +221 -0
  11. package/bin/__tests__/omp-bridge.test.js +20 -17
  12. package/bin/__tests__/omp-hooks.test.js +0 -19
  13. package/bin/__tests__/optional-skill-inventory.test.js +112 -4
  14. package/bin/__tests__/orca-session.test.js +0 -2
  15. package/bin/__tests__/package-inventory.test.js +90 -925
  16. package/bin/__tests__/runtime-dir.test.js +0 -3
  17. package/bin/__tests__/runtime-schema.test.js +3 -2
  18. package/bin/__tests__/skill-routing-source.test.js +27 -15
  19. package/bin/__tests__/spec-narrowing.test.js +30 -161
  20. package/bin/__tests__/specs-v2-execution-closeout.test.js +0 -437
  21. package/bin/__tests__/test-runtime-dependency-closure.cjs +1 -2
  22. package/bin/__tests__/ui-ux-skill.test.js +101 -0
  23. package/bin/__tests__/usage-hook-retired.test.js +114 -0
  24. package/bin/__tests__/validator-grounding.test.js +18 -2949
  25. package/bin/lib/managed-writer.js +2 -3
  26. package/bin/phases/codex-runtime.js +27 -0
  27. package/bin/phases/copy-payload.js +13 -28
  28. package/bin/phases/omp-runtime.js +25 -0
  29. package/package.json +2 -3
  30. package/src/claude/CLAUDE.md +3 -10
  31. package/src/claude/agents/brainstormer.md +7 -3
  32. package/src/claude/agents/code-auditor.md +44 -56
  33. package/src/claude/agents/debugger.md +8 -88
  34. package/src/claude/agents/deployer.md +3 -5
  35. package/src/claude/agents/docs-keeper.md +6 -14
  36. package/src/claude/agents/git-ops.md +2 -4
  37. package/src/claude/agents/implementer.md +12 -16
  38. package/src/claude/agents/inspector.md +4 -3
  39. package/src/claude/agents/project-manager.md +9 -17
  40. package/src/claude/agents/researcher.md +9 -3
  41. package/src/claude/agents/spec-maker.md +15 -15
  42. package/src/claude/agents/test-runner.md +8 -11
  43. package/src/claude/agents/ui-ux-designer.md +0 -1
  44. package/src/claude/hooks/__tests__/fixtures/statusline-default.golden +1 -1
  45. package/src/claude/hooks/__tests__/spec-gate.test.js +244 -333
  46. package/src/claude/hooks/__tests__/spec-state-touched.test.js +160 -0
  47. package/src/claude/hooks/__tests__/state.test.js +68 -0
  48. package/src/claude/hooks/__tests__/statusline.test.js +140 -11
  49. package/src/claude/hooks/agent.cjs +2 -1
  50. package/src/claude/hooks/lib/hook-payload.cjs +2 -2
  51. package/src/claude/hooks/rules.cjs +2 -1
  52. package/src/claude/hooks/session.cjs +0 -1
  53. package/src/claude/hooks/spec-gate.cjs +17 -83
  54. package/src/claude/hooks/spec-state.cjs +142 -39
  55. package/src/claude/hooks/state.cjs +21 -2
  56. package/src/claude/migration-manifest.json +42 -22
  57. package/src/claude/output-styles/coding-level-0-eli5.md +9 -9
  58. package/src/claude/output-styles/coding-level-1-junior.md +3 -3
  59. package/src/claude/output-styles/coding-level-2-mid.md +3 -3
  60. package/src/claude/output-styles/coding-level-3-senior.md +2 -2
  61. package/src/claude/output-styles/coding-level-4-lead.md +5 -5
  62. package/src/claude/output-styles/coding-level-5-god.md +6 -6
  63. package/src/claude/rules/ai-dev-rules.md +2 -3
  64. package/src/claude/rules/hook-protocols.md +8 -15
  65. package/src/claude/rules/manage-docs.md +9 -23
  66. package/src/claude/rules/orchestrator.md +3 -4
  67. package/src/claude/rules/review-audit-self-decision.md +3 -3
  68. package/src/claude/rules/skill-domain-routing.md +1 -3
  69. package/src/claude/rules/skill-workflow-routing.md +4 -5
  70. package/src/claude/rules/state-sync.md +3 -12
  71. package/src/claude/rules/workflow.md +11 -19
  72. package/src/claude/runtime.schema.json +5 -3
  73. package/src/claude/scripts/generate-skill-catalog.cjs +7 -6
  74. package/src/claude/scripts/provenance.cjs +23 -5
  75. package/src/claude/scripts/spec-receipt.cjs +24 -121
  76. package/src/claude/scripts/spec-resolver.cjs +41 -458
  77. package/src/claude/scripts/workflow-policy.cjs +3 -1801
  78. package/src/claude/settings/settings.json +1 -32
  79. package/src/claude/skills/agent-browser/SKILL.md +1 -1
  80. package/src/claude/skills/{question → ask}/SKILL.md +9 -75
  81. package/src/claude/skills/brainstorm/SKILL.md +43 -131
  82. package/src/claude/skills/chrome-devtools/SKILL.md +14 -31
  83. package/src/claude/skills/code-review/SKILL.md +29 -81
  84. package/src/claude/skills/code-review/references/spec-compliance-review.md +4 -8
  85. package/src/claude/skills/debug/SKILL.md +29 -149
  86. package/src/claude/skills/develop/SKILL.md +26 -83
  87. package/src/claude/skills/develop/references/parallel-waves.md +0 -8
  88. package/src/claude/skills/develop/references/quality-gate.md +15 -13
  89. package/src/claude/skills/develop/references/subagent-patterns.md +0 -6
  90. package/src/claude/skills/docs/SKILL.md +2 -2
  91. package/src/claude/skills/docs/references/update-workflow.md +1 -1
  92. package/src/claude/skills/fix/SKILL.md +153 -0
  93. package/src/claude/skills/{hotfix → fix}/references/diagnosis-protocol.md +2 -2
  94. package/src/claude/skills/{hotfix → fix}/references/parallel-patterns.md +2 -2
  95. package/src/claude/skills/{hotfix → fix}/references/prevention-gate.md +3 -3
  96. package/src/claude/skills/{hotfix → fix}/references/review-cycle.md +1 -1
  97. package/src/claude/skills/git/SKILL.md +48 -9
  98. package/src/claude/skills/git/references/commit-protocols.md +35 -16
  99. package/src/claude/skills/git/references/worktree-blueprint.md +42 -6
  100. package/src/claude/skills/orca/SKILL.md +9 -8
  101. package/src/claude/skills/research/SKILL.md +17 -46
  102. package/src/claude/skills/scout/SKILL.md +61 -0
  103. package/src/claude/skills/{inspect → scout}/references/internal-inspection.md +26 -10
  104. package/src/claude/skills/specs/SKILL.md +22 -29
  105. package/src/claude/skills/specs/references/review.md +8 -6
  106. package/src/claude/skills/specs/references/templates.md +34 -41
  107. package/src/claude/skills/sync/SKILL.md +16 -45
  108. package/src/claude/skills/sync/references/rebind-and-audit.md +59 -0
  109. package/src/claude/skills/sync/references/sync-protocols.md +4 -13
  110. package/src/claude/skills/test/SKILL.md +39 -104
  111. package/src/claude/skills/test/references/execution-strategy.md +93 -21
  112. package/src/claude/skills/test/references/failure-triage.md +1 -1
  113. package/src/claude/skills/ui-ux/LICENSE +21 -0
  114. package/src/claude/skills/ui-ux/SKILL.md +497 -0
  115. package/src/claude/skills/ui-ux/references/brand-tokens.md +90 -0
  116. package/src/claude/skills/ui-ux/references/budgets.md +113 -0
  117. package/src/claude/skills/ui-ux/references/checklist.md +271 -0
  118. package/src/claude/skills/ui-ux/references/components/accordion.md +246 -0
  119. package/src/claude/skills/ui-ux/references/components/avatar.md +153 -0
  120. package/src/claude/skills/ui-ux/references/components/banner.md +43 -0
  121. package/src/claude/skills/ui-ux/references/components/breadcrumb.md +201 -0
  122. package/src/claude/skills/ui-ux/references/components/button.md +142 -0
  123. package/src/claude/skills/ui-ux/references/components/card.md +113 -0
  124. package/src/claude/skills/ui-ux/references/components/charts.md +376 -0
  125. package/src/claude/skills/ui-ux/references/components/chat.md +132 -0
  126. package/src/claude/skills/ui-ux/references/components/choice-controls.md +369 -0
  127. package/src/claude/skills/ui-ux/references/components/comment-thread.md +17 -0
  128. package/src/claude/skills/ui-ux/references/components/description-list.md +116 -0
  129. package/src/claude/skills/ui-ux/references/components/empty-state.md +98 -0
  130. package/src/claude/skills/ui-ux/references/components/file-upload.md +107 -0
  131. package/src/claude/skills/ui-ux/references/components/inline-edit.md +128 -0
  132. package/src/claude/skills/ui-ux/references/components/input.md +161 -0
  133. package/src/claude/skills/ui-ux/references/components/list-row.md +56 -0
  134. package/src/claude/skills/ui-ux/references/components/loading.md +198 -0
  135. package/src/claude/skills/ui-ux/references/components/logo.md +147 -0
  136. package/src/claude/skills/ui-ux/references/components/otp-input.md +43 -0
  137. package/src/claude/skills/ui-ux/references/components/quantity-input.md +100 -0
  138. package/src/claude/skills/ui-ux/references/components/range-slider.md +16 -0
  139. package/src/claude/skills/ui-ux/references/components/small-controls.md +340 -0
  140. package/src/claude/skills/ui-ux/references/components/sortable-header.md +86 -0
  141. package/src/claude/skills/ui-ux/references/components/tag-input.md +14 -0
  142. package/src/claude/skills/ui-ux/references/components/timeline.md +38 -0
  143. package/src/claude/skills/ui-ux/references/components/tree.md +30 -0
  144. package/src/claude/skills/ui-ux/references/design-process.md +650 -0
  145. package/src/claude/skills/ui-ux/references/layouts/app-kanban.html +303 -0
  146. package/src/claude/skills/ui-ux/references/layouts/app.md +1369 -0
  147. package/src/claude/skills/ui-ux/references/layouts/form.md +430 -0
  148. package/src/claude/skills/ui-ux/references/layouts/overlay.md +552 -0
  149. package/src/claude/skills/ui-ux/references/layouts/pricing.md +204 -0
  150. package/src/claude/skills/ui-ux/references/locked-rules.md +35 -0
  151. package/src/claude/skills/ui-ux/references/principles.md +408 -0
  152. package/src/claude/skills/ui-ux/references/refactor.md +245 -0
  153. package/src/claude/skills/ui-ux/references/responsive.md +195 -0
  154. package/src/claude/skills/ui-ux/references/review.md +528 -0
  155. package/src/claude/skills/ui-ux/references/rules-color.md +623 -0
  156. package/src/claude/skills/ui-ux/references/rules-form.md +235 -0
  157. package/src/claude/skills/ui-ux/references/rules-state.md +680 -0
  158. package/src/claude/skills/ui-ux/references/rules-type.md +369 -0
  159. package/src/claude/skills/ui-ux/references/styles.md +311 -0
  160. package/src/claude/skills/ui-ux/references/system.md +219 -0
  161. package/src/claude/skills/ui-ux/references/tailwind-v4-traps.md +237 -0
  162. package/src/claude/skills/ui-ux/references/tokens.css +362 -0
  163. package/src/claude/skills/ui-ux/scripts/lint-skill.mjs +216 -0
  164. package/src/claude/skills/ui-ux/scripts/package.json +9 -0
  165. package/src/claude/skills/ui-ux/scripts/probe.mjs +4447 -0
  166. package/src/claude/skills/ui-ux-pro-max/SKILL.md +25 -80
  167. package/src/claude/skills/web-testing/SKILL.md +2 -20
  168. package/src/claude/status.cjs +149 -144
  169. package/src/codex/AGENTS.md +8 -13
  170. package/src/codex/hooks/agent.cjs +2 -1
  171. package/src/codex/hooks/lib/spec-receipt.cjs +4 -32
  172. package/src/codex/hooks/lib/spec-utils.cjs +6 -52
  173. package/src/codex/hooks/rules.cjs +2 -1
  174. package/src/codex/hooks/session.cjs +0 -2
  175. package/src/codex/hooks/spec-gate.cjs +13 -81
  176. package/src/codex/hooks/spec-state.cjs +37 -41
  177. package/src/codex/hooks.json +0 -20
  178. package/src/codex/rules/state-sync.md +3 -12
  179. package/src/common/AGENTS.md +6 -12
  180. package/src/omp/extensions/cafekit-bridge.mjs +6 -6
  181. package/bin/__tests__/change-firewall.semantic-firewall.test.js +0 -2133
  182. package/bin/__tests__/finalizer-pass-gate.semantic-firewall.test.js +0 -342
  183. package/bin/__tests__/release-preflight.semantic-firewall.test.js +0 -586
  184. package/bin/__tests__/review-receipt-c2.semantic-firewall.test.js +0 -378
  185. package/bin/__tests__/specs-v2-policy-and-scaffold.test.js +0 -665
  186. package/bin/__tests__/specs-v2-validator-grounder-contract.test.js +0 -1132
  187. package/src/claude/hooks/__tests__/completion-authority.test.js +0 -990
  188. package/src/claude/hooks/__tests__/semantic-review-authority.test.js +0 -259
  189. package/src/claude/hooks/completion-authority-check.cjs +0 -56
  190. package/src/claude/hooks/completion-authority-state.cjs +0 -527
  191. package/src/claude/hooks/completion-authority.cjs +0 -163
  192. package/src/claude/hooks/lib/context.cjs +0 -616
  193. package/src/claude/hooks/lib/detect.cjs +0 -474
  194. package/src/claude/hooks/semantic-review-authority.cjs +0 -154
  195. package/src/claude/hooks/task-scaffold-guard.cjs +0 -114
  196. package/src/claude/hooks/usage.cjs +0 -197
  197. package/src/claude/scripts/change-firewall.cjs +0 -1748
  198. package/src/claude/scripts/spec-authoring-digest.cjs +0 -151
  199. package/src/claude/scripts/spec-authoring-validation.cjs +0 -126
  200. package/src/claude/scripts/spec-final-state.cjs +0 -290
  201. package/src/claude/scripts/spec-ground.cjs +0 -650
  202. package/src/claude/scripts/spec-readiness.cjs +0 -425
  203. package/src/claude/scripts/spec-scaffold.cjs +0 -2317
  204. package/src/claude/scripts/spec-semantic-model.cjs +0 -396
  205. package/src/claude/scripts/validate-spec-output.cjs +0 -3941
  206. package/src/claude/skills/code-review/references/verification-gate.md +0 -46
  207. package/src/claude/skills/delegate/SKILL.md +0 -124
  208. package/src/claude/skills/delegate/references/codex-delegation.md +0 -71
  209. package/src/claude/skills/delegate/references/grok-delegation.md +0 -84
  210. package/src/claude/skills/hotfix/SKILL.md +0 -289
  211. package/src/claude/skills/inspect/SKILL.md +0 -142
  212. package/src/claude/skills/loop/SKILL.md +0 -114
  213. package/src/claude/skills/loop/references/bounded-loop-protocol.md +0 -104
  214. package/src/claude/skills/loop/references/metric-and-guard-contract.md +0 -76
  215. package/src/claude/skills/route/SKILL.md +0 -94
  216. package/src/claude/skills/route/references/agent-timing.md +0 -46
  217. package/src/claude/skills/route/references/chaining-patterns.md +0 -53
  218. package/src/claude/skills/route/references/task-taxonomy.md +0 -48
  219. package/src/claude/skills/specs/templates/design.md +0 -82
  220. package/src/claude/skills/specs/templates/requirements-init.md +0 -8
  221. package/src/claude/skills/specs/templates/requirements.md +0 -42
  222. package/src/claude/skills/specs/templates/spec-state.json +0 -56
  223. package/src/claude/skills/specs/templates/task.md +0 -47
  224. package/src/codex/hooks/completion-authority-check.cjs +0 -67
  225. package/src/codex/hooks/completion-authority-state.cjs +0 -527
  226. package/src/codex/hooks/completion-authority.cjs +0 -109
  227. package/src/codex/hooks/semantic-review-authority.cjs +0 -130
  228. package/src/codex/hooks/task-scaffold-guard.cjs +0 -63
  229. /package/src/claude/skills/{question → ask}/templates/question.md +0 -0
  230. /package/src/claude/skills/{hotfix → fix}/references/escalation-tactics.md +0 -0
  231. /package/src/claude/skills/{hotfix → fix}/references/workflow-specialized.md +0 -0
@@ -1,130 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const fs = require('fs');
5
- const path = require('path');
6
- const { spawnSync } = require('child_process');
7
- const STATE = require('./completion-authority-state.cjs');
8
- const PATH_SAFETY = require('./lib/runtime-path-safety.cjs');
9
-
10
- const SCHEMA_VERSION = 1;
11
- const MARKER = 'CAFEKIT_SEMANTIC_REVIEW_ATTESTATION ';
12
- const SHA256_RE = /^sha256:[a-f0-9]{64}$/;
13
- const REVIEWER_ROLE_ALLOWLIST = new Set(['code-auditor', 'code_auditor']);
14
- const RECORD_FIELDS = ['schema_version', 'kind', 'identity', 'semantic_digest', 'event', 'verdict', 'host_session_id', 'reviewer_agent_id', 'reviewer_agent_type', 'issued_at', 'mac'];
15
-
16
- function plain(value) { return Boolean(value && typeof value === 'object' && !Array.isArray(value)); }
17
- function exactKeys(value, fields) { return plain(value) && JSON.stringify(Object.keys(value).sort()) === JSON.stringify([...fields].sort()); }
18
- function inside(root, target) { const relative = path.relative(root, target); return relative !== '' && relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative); }
19
-
20
- function projectRoot(payload = {}) {
21
- const installedRoot = path.resolve(__dirname, '..', '..');
22
- if (fs.existsSync(path.join(installedRoot, '.codex', 'hooks', path.basename(__filename)))) {
23
- try { return STATE.canonicalProjectRoot(installedRoot); } catch {}
24
- }
25
- for (const candidate of [process.env.CLAUDE_PROJECT_DIR, process.env.PROJECT_ROOT]) {
26
- if (typeof candidate === 'string' && candidate.trim()) { try { return STATE.canonicalProjectRoot(candidate); } catch { /* continue */ } }
27
- }
28
- const cwd = typeof payload.cwd === 'string' && payload.cwd.trim() ? payload.cwd : process.cwd();
29
- return STATE.canonicalProjectRoot(cwd);
30
- }
31
-
32
- function identityFor(root, specFile, featureName) {
33
- if (typeof featureName !== 'string' || !featureName.trim() || featureName.includes('/') || featureName.includes('\\') || featureName === '.' || featureName === '..' || featureName.includes('..')) throw new Error('feature identity is malformed');
34
- if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(featureName)) throw new Error('feature identity is malformed');
35
- const canonicalRoot = STATE.canonicalProjectRoot(root);
36
- const canonicalSpec = fs.realpathSync(path.resolve(specFile));
37
- if (!inside(canonicalRoot, canonicalSpec) || path.basename(canonicalSpec) !== 'spec.json') throw new Error('spec identity escapes the project');
38
- const relative = path.relative(canonicalRoot, canonicalSpec);
39
- const expected = path.join('specs', featureName, 'spec.json');
40
- if (relative !== expected) throw new Error(`spec identity must be ${expected}, not ${relative}`);
41
- const spec = JSON.parse(fs.readFileSync(canonicalSpec, 'utf8'));
42
- if (spec.feature_name !== featureName) throw new Error('feature identity does not match spec.json');
43
- return { project_root: canonicalRoot, spec_file: canonicalSpec, feature_name: featureName };
44
- }
45
-
46
- function recordPath(root, identity) {
47
- const name = STATE.digest({ namespace: 'cafekit-semantic-review-observation-v1', identity });
48
- return path.join(STATE.stateDir(root), 'semantic-reviews', `observation-${name}.json`);
49
- }
50
-
51
- function parseClaim(message) {
52
- const lines = String(message || '').split(/\r?\n/).filter((line) => line.startsWith(MARKER));
53
- if (lines.length === 0) return null;
54
- if (lines.length !== 1) throw new Error('exactly one semantic review attestation marker is required');
55
- const claim = JSON.parse(lines[0].slice(MARKER.length));
56
- if (!exactKeys(claim, ['feature_name', 'spec_file', 'semantic_digest', 'verdict'])) throw new Error('semantic review claim fields are malformed');
57
- if (claim.verdict !== 'PASS' || !SHA256_RE.test(claim.semantic_digest || '')) throw new Error('semantic review claim must contain literal PASS and a sha256 digest');
58
- return claim;
59
- }
60
-
61
- function validatorFor(root) {
62
- const installedRoot = path.join(root, '.codex');
63
- const installedHook = path.join(installedRoot, 'hooks', path.basename(__filename));
64
- if (fs.existsSync(installedHook)) {
65
- const canonicalHook = PATH_SAFETY.canonicalRegularFile(root, installedHook, 'installed semantic hook');
66
- if (canonicalHook !== fs.realpathSync(__filename)) throw new Error('installed semantic hook identity is invalid');
67
- const validator = path.join(installedRoot, 'scripts', 'validate-spec-output.cjs');
68
- PATH_SAFETY.verifyCommonJsClosure(installedRoot, validator);
69
- return PATH_SAFETY.canonicalRegularFile(installedRoot, validator, 'semantic validator');
70
- }
71
-
72
- const sourceRuntime = path.resolve(__dirname, '..', '..', 'claude');
73
- const sourceRoot = path.resolve(sourceRuntime, '..');
74
- if (path.basename(sourceRuntime) !== 'claude' || path.basename(sourceRoot) !== 'src') {
75
- throw new Error('semantic validator runtime root is not recognized');
76
- }
77
- const validator = path.join(sourceRuntime, 'scripts', 'validate-spec-output.cjs');
78
- PATH_SAFETY.verifyCommonJsClosure(sourceRoot, validator);
79
- return PATH_SAFETY.canonicalRegularFile(sourceRoot, validator, 'semantic validator');
80
- }
81
-
82
- function currentDigest(root, specFile) {
83
- const validator = validatorFor(root);
84
- const result = spawnSync(process.execPath, [validator, path.dirname(specFile), '--semantic-digest'], { cwd: root, encoding: 'utf8' });
85
- const digest = result.stdout.trim();
86
- if (result.status !== 0 || !SHA256_RE.test(digest)) throw new Error(`semantic digest could not be recomputed (${result.stderr.trim() || `exit ${result.status}`})`);
87
- return digest;
88
- }
89
-
90
- function recordFromSubagentStop(payload) {
91
- // Honest-agent guardrail: the host hook observes this SubagentStop payload.
92
- // The worker-authored claim is not authenticated input or a security boundary.
93
- if (!plain(payload) || payload.hook_event_name !== 'SubagentStop') return { recorded: false, reason: 'event' };
94
- for (const field of ['session_id', 'agent_id', 'agent_type', 'last_assistant_message']) if (typeof payload[field] !== 'string' || !payload[field].trim()) return { recorded: false, reason: `missing-${field}` };
95
- if (!REVIEWER_ROLE_ALLOWLIST.has(payload.agent_type)) return { recorded: false, reason: 'reviewer-role' };
96
- const root = projectRoot(payload);
97
- const claim = parseClaim(payload.last_assistant_message);
98
- if (claim === null) return { recorded: false, reason: 'no-claim' };
99
- const identity = identityFor(root, path.resolve(root, claim.spec_file), claim.feature_name);
100
- const digest = currentDigest(root, identity.spec_file);
101
- if (digest !== claim.semantic_digest) throw new Error('claimed semantic digest does not match current artifacts');
102
- const key = STATE.ensureKey();
103
- const record = { schema_version: SCHEMA_VERSION, kind: 'semantic-review-observation', identity, semantic_digest: digest, event: 'SubagentStop', verdict: 'PASS', host_session_id: payload.session_id, reviewer_agent_id: payload.agent_id, reviewer_agent_type: payload.agent_type, issued_at: new Date().toISOString() };
104
- record.mac = STATE.recordMac(record, key);
105
- STATE.atomicWrite(recordPath(root, identity), record);
106
- return { recorded: true, record };
107
- }
108
-
109
- function verifyAttestation(root, specFile, featureName, semanticDigest) {
110
- let identity;
111
- try { identity = identityFor(root, specFile, featureName); } catch (error) { return { ok: false, reason: error.message }; }
112
- let record;
113
- try { record = JSON.parse(fs.readFileSync(recordPath(root, identity), 'utf8')); } catch (error) { return { ok: false, reason: error.code === 'ENOENT' ? 'matching host-hook observation is missing' : 'host-hook observation is unreadable' }; }
114
- const key = STATE.readKey();
115
- if (key.missing) return { ok: false, reason: 'completion authority key is missing' };
116
- if (!exactKeys(record, RECORD_FIELDS) || !STATE.macMatches(record, key.key)) return { ok: false, reason: 'host-hook observation is malformed or its MAC is invalid' };
117
- if (record.schema_version !== SCHEMA_VERSION || record.kind !== 'semantic-review-observation' || record.event !== 'SubagentStop' || record.verdict !== 'PASS') return { ok: false, reason: 'host-hook observation event or verdict is invalid' };
118
- if (STATE.stableStringify(record.identity) !== STATE.stableStringify(identity)) return { ok: false, reason: 'host-hook observation project, feature, or spec identity is invalid' };
119
- if (record.semantic_digest !== semanticDigest) return { ok: false, reason: 'host-hook observation semantic digest is stale' };
120
- return { ok: true, record };
121
- }
122
-
123
- if (require.main === module) {
124
- try { const raw = fs.readFileSync(0, 'utf8').trim(); if (raw) recordFromSubagentStop(JSON.parse(raw)); }
125
- catch (error) { process.stderr.write(`CafeKit semantic review SubagentStop claim rejected: ${error.message}\n`); }
126
- // Codex SubagentStop requires valid JSON on stdout for every exit-0 hook.
127
- process.stdout.write('{}\n');
128
- }
129
-
130
- module.exports = { MARKER, REVIEWER_ROLE_ALLOWLIST, recordFromSubagentStop, verifyAttestation, identityFor, recordPath };
@@ -1,63 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const fs = require('fs');
5
- const path = require('path');
6
- const {
7
- getHookContext,
8
- logCrash,
9
- readPayload
10
- } = require('./lib/hook-context.cjs');
11
-
12
- function addedFiles(toolName, input) {
13
- if (toolName === 'Write') {
14
- return [input.file_path || input.filePath || input.path || ''].filter(Boolean);
15
- }
16
- if (toolName !== 'apply_patch') return [];
17
- const patch = typeof input.command === 'string'
18
- ? input.command
19
- : typeof input.patch === 'string' ? input.patch : '';
20
- return [...patch.matchAll(/^\*\*\* Add File:\s+(.+)$/gm)]
21
- .map((match) => match[1].trim());
22
- }
23
-
24
- function projectRelative(filePath, projectRoot, sessionCwd) {
25
- const absolute = path.isAbsolute(filePath)
26
- ? path.normalize(filePath)
27
- : path.resolve(sessionCwd, filePath);
28
- return path.relative(projectRoot, absolute).replace(/\\/g, '/');
29
- }
30
-
31
- try {
32
- const payload = readPayload();
33
- if (!payload) process.exit(0);
34
- const { projectRoot, runtime, sessionCwd } = getHookContext(payload);
35
- const candidates = addedFiles(payload.tool_name || '', payload.tool_input || {});
36
- const taskFile = candidates
37
- .map((requested) => ({
38
- requested,
39
- relative: projectRelative(requested, projectRoot, sessionCwd)
40
- }))
41
- .find(({ relative }) => (
42
- /^specs\/[^/]+\/tasks\/task-[^/]+\.md$/.test(relative)
43
- ));
44
- if (!taskFile) process.exit(0);
45
- if (runtime.spec?.scaffold_guard === false) process.exit(0);
46
-
47
- const scaffold = path.join(projectRoot, '.codex', 'scripts', 'spec-scaffold.cjs');
48
- if (!fs.existsSync(scaffold)) process.exit(0);
49
-
50
- const feature = taskFile.relative.split('/')[1];
51
- const command = sessionCwd === projectRoot
52
- ? `node .codex/scripts/spec-scaffold.cjs ${feature}`
53
- : `node ${JSON.stringify(scaffold)} ${feature}`;
54
- process.stderr.write(
55
- 'TASK SCAFFOLD REQUIRED: task files must be generated, not hand-written.\n'
56
- + `Blocked Write: ${taskFile.requested}\n\n`
57
- + 'Generate the stubs, then fill their {{...}} placeholders with apply_patch:\n'
58
- + ` ${command} --tasks "R0-01-slug,R1-01-slug,..." --tasks-only\n`
59
- );
60
- process.exit(2);
61
- } catch (error) {
62
- logCrash('task-scaffold-guard', error);
63
- }