@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,259 +0,0 @@
1
- 'use strict';
2
-
3
- const assert = require('node:assert/strict');
4
- const fs = require('node:fs');
5
- const os = require('node:os');
6
- const path = require('node:path');
7
- const { spawnSync } = require('node:child_process');
8
- const { test } = require('node:test');
9
-
10
- const PACKAGE_ROOT = path.resolve(__dirname, '../../../..');
11
- const VALIDATOR = path.join(PACKAGE_ROOT, 'src/claude/scripts/validate-spec-output.cjs');
12
- const HOOKS = ['claude', 'codex'].map((runtime) => path.join(PACKAGE_ROOT, `src/${runtime}/hooks/semantic-review-authority.cjs`));
13
-
14
- function fixture(base, name) {
15
- const root = path.join(base, name);
16
- const specDir = path.join(root, 'specs', 'feature');
17
- fs.mkdirSync(specDir, { recursive: true });
18
- fs.writeFileSync(path.join(specDir, 'requirements.md'), '# Requirements\n\n- **R1.1** The service shall return one result.\n');
19
- fs.writeFileSync(path.join(specDir, 'design.md'), '# Design\n\n### D1 — Stable result\n\nReturn one stable result.\n');
20
- fs.writeFileSync(path.join(specDir, 'spec.json'), `${JSON.stringify({ feature_name: 'feature', status: 'in_progress', task_registry: {}, workflow_policy: { assurance_level: 'Strict' } })}\n`);
21
- return { root, specDir, specFile: path.join(specDir, 'spec.json') };
22
- }
23
-
24
- function installSemanticRuntime(target, runtime) {
25
- const runtimeRoot = path.join(target.root, runtime === 'claude' ? '.claude' : '.codex');
26
- const hooks = path.join(runtimeRoot, 'hooks');
27
- const scripts = path.join(runtimeRoot, 'scripts');
28
- fs.mkdirSync(path.join(hooks, 'lib'), { recursive: true });
29
- fs.cpSync(path.join(PACKAGE_ROOT, 'src/claude/scripts'), scripts, { recursive: true });
30
- for (const name of ['semantic-review-authority.cjs', 'completion-authority-state.cjs']) {
31
- fs.copyFileSync(path.join(PACKAGE_ROOT, `src/${runtime}/hooks`, name), path.join(hooks, name));
32
- }
33
- fs.copyFileSync(path.join(PACKAGE_ROOT, 'src/claude/hooks/lib/runtime-dir.cjs'), path.join(hooks, 'lib/runtime-dir.cjs'));
34
- fs.copyFileSync(path.join(PACKAGE_ROOT, 'src/claude/hooks/lib/hook-payload.cjs'), path.join(hooks, 'lib/hook-payload.cjs'));
35
- fs.copyFileSync(
36
- path.join(PACKAGE_ROOT, 'src/claude/hooks/lib/runtime-path-safety.cjs'),
37
- path.join(hooks, 'lib/runtime-path-safety.cjs'),
38
- );
39
- return {
40
- hook: path.join(hooks, 'semantic-review-authority.cjs'),
41
- validator: path.join(scripts, 'validate-spec-output.cjs'),
42
- };
43
- }
44
-
45
- function envFor(root, home) {
46
- return { ...process.env, HOME: home, USERPROFILE: home, PROJECT_ROOT: root };
47
- }
48
-
49
- function digest(target, home) {
50
- const result = spawnSync(process.execPath, [VALIDATOR, target.specDir, '--semantic-digest'], { cwd: target.root, env: envFor(target.root, home), encoding: 'utf8' });
51
- assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
52
- return result.stdout.trim();
53
- }
54
-
55
- function claim(target, semanticDigest, overrides = {}) {
56
- return `CAFEKIT_SEMANTIC_REVIEW_ATTESTATION ${JSON.stringify({ feature_name: 'feature', spec_file: 'specs/feature/spec.json', semantic_digest: semanticDigest, verdict: 'PASS', ...overrides })}`;
57
- }
58
-
59
- function runHook(hook, target, home, message, event = 'SubagentStop', payload = {}) {
60
- return spawnSync(process.execPath, [hook], {
61
- cwd: target.root, env: envFor(target.root, home), encoding: 'utf8',
62
- input: JSON.stringify({ hook_event_name: event, session_id: 'host-session', agent_id: 'review-agent', agent_type: 'code_auditor', last_assistant_message: message, cwd: target.root, ...payload }),
63
- });
64
- }
65
-
66
- function verify(hook, target, home, semanticDigest) {
67
- const script = 'const a=require(process.argv[1]); console.log(JSON.stringify(a.verifyAttestation(process.argv[2],process.argv[3],process.argv[4],process.argv[5])))';
68
- const result = spawnSync(process.execPath, ['-e', script, hook, target.root, target.specFile, 'feature', semanticDigest], { env: envFor(target.root, home), encoding: 'utf8' });
69
- assert.equal(result.status, 0, result.stderr);
70
- return JSON.parse(result.stdout);
71
- }
72
-
73
- function observationFile(home) {
74
- const files = fs.readdirSync(home, { recursive: true }).filter((entry) => String(entry).endsWith('.json') && String(entry).includes('observation-'));
75
- assert.equal(files.length, 1);
76
- return path.join(home, files[0]);
77
- }
78
-
79
- test('Claude and Codex observe equivalent allowlisted SubagentStop claims with HMAC integrity', () => {
80
- for (const hook of HOOKS) {
81
- const base = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-semantic-authority-'));
82
- try {
83
- const target = fixture(base, 'project');
84
- const home = path.join(base, 'home');
85
- const current = digest(target, home);
86
- assert.equal(runHook(hook, target, home, claim(target, current), 'SessionStart').stderr, '');
87
- assert.equal(fs.existsSync(path.join(home, '.cafekit')), false, 'non-SubagentStop event wrote authority state');
88
- assert.equal(runHook(hook, target, home, 'Ordinary worker finished without a semantic review claim.').stderr, '');
89
- assert.equal(fs.existsSync(path.join(home, '.cafekit')), false, 'ordinary SubagentStop wrote authority state');
90
- const recorded = runHook(hook, target, home, claim(target, current));
91
- assert.equal(recorded.status, 0);
92
- assert.equal(recorded.stderr, '');
93
- const observed = verify(hook, target, home, current);
94
- assert.equal(observed.ok, true);
95
- assert.equal(observed.record.kind, 'semantic-review-observation');
96
- assert.equal(observed.record.event, 'SubagentStop');
97
-
98
- const before = current;
99
- const spec = JSON.parse(fs.readFileSync(target.specFile, 'utf8'));
100
- spec.status = 'done';
101
- spec.updated_at = '2026-08-13T12:00:00+07:00';
102
- fs.writeFileSync(target.specFile, `${JSON.stringify(spec)}\n`);
103
- assert.equal(digest(target, home), before);
104
- assert.equal(verify(hook, target, home, before).ok, true);
105
-
106
- fs.appendFileSync(path.join(target.specDir, 'requirements.md'), '\nSemantic behavior changed.\n');
107
- const changed = digest(target, home);
108
- assert.notEqual(changed, before);
109
- assert.match(verify(hook, target, home, changed).reason, /stale/);
110
- } finally { fs.rmSync(base, { recursive: true, force: true }); }
111
- }
112
- });
113
-
114
- test('observation rejects wrong identity, digest, verdict, tampered MAC, copied record, and self-authored strings', () => {
115
- const base = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-semantic-adversarial-'));
116
- try {
117
- const home = path.join(base, 'home');
118
- const first = fixture(base, 'first');
119
- const second = fixture(base, 'second');
120
- const firstDigest = digest(first, home);
121
- const secondDigest = digest(second, home);
122
- const hook = HOOKS[0];
123
-
124
- for (const badClaim of [
125
- claim(first, `sha256:${'0'.repeat(64)}`),
126
- claim(first, firstDigest, { verdict: 'FAIL' }),
127
- claim(first, firstDigest, { feature_name: 'copied-feature' }),
128
- claim(first, firstDigest, { spec_file: 'specs/missing/spec.json' }),
129
- ]) {
130
- const result = runHook(hook, first, home, badClaim);
131
- assert.match(result.stderr, /rejected/);
132
- }
133
- assert.equal(verify(hook, first, home, firstDigest).ok, false, 'self-authored claims created authority');
134
-
135
- assert.equal(runHook(hook, first, home, claim(first, firstDigest)).stderr, '');
136
- const firstRecordPath = observationFile(home);
137
- const firstRecord = fs.readFileSync(firstRecordPath);
138
- assert.equal(runHook(hook, second, home, claim(second, secondDigest)).stderr, '');
139
- const records = fs.readdirSync(home, { recursive: true }).filter((entry) => String(entry).includes('observation-'));
140
- assert.equal(records.length, 2);
141
- const secondRecordPath = records.map((entry) => path.join(home, entry)).find((file) => file !== firstRecordPath);
142
- fs.writeFileSync(secondRecordPath, firstRecord);
143
- assert.equal(verify(hook, second, home, secondDigest).ok, false, 'copied project record was accepted');
144
-
145
- const tampered = JSON.parse(fs.readFileSync(firstRecordPath, 'utf8'));
146
- tampered.verdict = 'FAIL';
147
- fs.writeFileSync(firstRecordPath, `${JSON.stringify(tampered)}\n`);
148
- assert.match(verify(hook, first, home, firstDigest).reason, /MAC|malformed/);
149
- } finally { fs.rmSync(base, { recursive: true, force: true }); }
150
- });
151
-
152
- test('SubagentStop markers and claimed capabilities cannot turn a non-reviewer into authority', () => {
153
- for (const hook of HOOKS) {
154
- const base = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-semantic-role-'));
155
- try {
156
- const target = fixture(base, 'project');
157
- const home = path.join(base, 'home');
158
- const current = digest(target, home);
159
- for (const payload of [
160
- { agent_type: 'implementer' },
161
- { agent_type: 'test_runner', capability: 'reviewer' },
162
- { agent_type: 'worker', reviewer_capability: true },
163
- ]) {
164
- const result = runHook(hook, target, home, claim(target, current), 'SubagentStop', payload);
165
- assert.equal(result.status, 0);
166
- assert.equal(result.stderr, '');
167
- assert.equal(verify(hook, target, home, current).ok, false);
168
- }
169
- assert.equal(runHook(hook, target, home, claim(target, current), 'SubagentStop', { agent_type: 'code-auditor' }).stderr, '');
170
- assert.equal(verify(hook, target, home, current).ok, true);
171
- } finally { fs.rmSync(base, { recursive: true, force: true }); }
172
- }
173
- });
174
-
175
- test('canonicalizes and contains the validator before semantic digest spawn', () => {
176
- for (const runtime of ['claude', 'codex']) {
177
- const base = fs.mkdtempSync(path.join(os.tmpdir(), `cafekit-semantic-validator-${runtime}-`));
178
- try {
179
- const target = fixture(base, 'project');
180
- const installed = installSemanticRuntime(target, runtime);
181
- const digestValue = digest(target, path.join(base, 'digest-home'));
182
- const validHome = path.join(base, 'valid-home');
183
- const valid = runHook(installed.hook, target, validHome, claim(target, digestValue));
184
- assert.equal(valid.status, 0);
185
- assert.equal(valid.stderr, '', `${runtime} contained validator should be accepted`);
186
- assert.equal(verify(installed.hook, target, validHome, digestValue).ok, true);
187
-
188
- const outsideValidator = path.join(base, 'outside-validator.cjs');
189
- fs.copyFileSync(VALIDATOR, outsideValidator);
190
- fs.rmSync(installed.validator);
191
- fs.symlinkSync(outsideValidator, installed.validator);
192
- const escapedHome = path.join(base, 'escaped-home');
193
- const escaped = runHook(installed.hook, target, escapedHome, claim(target, digestValue));
194
- assert.equal(escaped.status, 0);
195
- assert.match(escaped.stderr, /semantic validator|escapes|rejected/i, `${runtime} validator escape must be rejected`);
196
- assert.equal(verify(installed.hook, target, escapedHome, digestValue).ok, false);
197
-
198
- fs.rmSync(installed.validator);
199
- fs.copyFileSync(VALIDATOR, installed.validator);
200
- const dependency = path.join(path.dirname(installed.validator), 'workflow-policy.cjs');
201
- const outsideDependency = path.join(base, 'outside-workflow-policy.cjs');
202
- fs.copyFileSync(dependency, outsideDependency);
203
- fs.rmSync(dependency);
204
- fs.symlinkSync(outsideDependency, dependency);
205
- const dependencyHome = path.join(base, 'dependency-home');
206
- const dependencyEscape = runHook(installed.hook, target, dependencyHome, claim(target, digestValue));
207
- assert.equal(dependencyEscape.status, 0);
208
- assert.match(dependencyEscape.stderr, /dependency|symlink|rejected/i, `${runtime} transitive dependency escape must be rejected`);
209
- assert.equal(verify(installed.hook, target, dependencyHome, digestValue).ok, false);
210
-
211
- fs.rmSync(dependency);
212
- fs.copyFileSync(path.join(PACKAGE_ROOT, 'src/claude/scripts/workflow-policy.cjs'), dependency);
213
- const runtimeRoot = path.dirname(path.dirname(installed.hook));
214
- const outsideRuntime = path.join(base, `outside-${runtime}`);
215
- fs.renameSync(runtimeRoot, outsideRuntime);
216
- fs.symlinkSync(outsideRuntime, runtimeRoot);
217
- const rootHome = path.join(base, 'root-home');
218
- const rootEscape = runHook(installed.hook, target, rootHome, claim(target, digestValue));
219
- assert.equal(rootEscape.status, 0);
220
- assert.match(rootEscape.stderr, /symlink|identity|rejected/i, `${runtime} adapter-root symlink must be rejected`);
221
- assert.equal(verify(installed.hook, target, rootHome, digestValue).ok, false);
222
- } finally { fs.rmSync(base, { recursive: true, force: true }); }
223
- }
224
- });
225
-
226
- test('rejects scratch/spec.json and traversal feature identities', () => {
227
- for (const hook of HOOKS) {
228
- const base = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-semantic-scratch-'));
229
- try {
230
- const target = fixture(base, 'project');
231
- const home = path.join(base, 'home');
232
- const current = digest(target, home);
233
- const badScratch = claim(target, current, { spec_file: 'scratch/spec.json' });
234
- const resScratch = runHook(hook, target, home, badScratch);
235
- assert.match(resScratch.stderr, /rejected|spec identity/);
236
- assert.equal(verify(hook, target, home, current).ok, false, 'scratch identity should not create observation');
237
-
238
- const traversal = `CAFEKIT_SEMANTIC_REVIEW_ATTESTATION ${JSON.stringify({ feature_name: '../evil', spec_file: 'specs/../evil/spec.json', semantic_digest: current, verdict: 'PASS' })}`;
239
- const resTraversal = runHook(hook, target, home, traversal);
240
- assert.match(resTraversal.stderr, /rejected|malformed/);
241
- assert.equal(verify(hook, target, home, current).ok, false);
242
-
243
- const slash = claim(target, current, { feature_name: 'evil/traversal' });
244
- const resSlash = runHook(hook, target, home, slash);
245
- assert.match(resSlash.stderr, /rejected|malformed/);
246
- } finally { fs.rmSync(base, { recursive: true, force: true }); }
247
- }
248
- });
249
-
250
- test('code-auditor agent ships Strict conditional marker contract', () => {
251
- const agentPath = path.join(PACKAGE_ROOT, 'src/claude/agents/code-auditor.md');
252
- const content = fs.readFileSync(agentPath, 'utf8');
253
- assert.match(content, /Strict Semantic Review Attestation/);
254
- assert.match(content, /CAFEKIT_SEMANTIC_REVIEW_ATTESTATION/);
255
- assert.match(content, /Emit only for `Strict`/);
256
- assert.match(content, /specs\/<feature>\/spec\.json/);
257
- assert.match(content, /MAC-protected host-hook observation/);
258
- assert.doesNotMatch(content, /host-signed|proves causal/);
259
- });
@@ -1,56 +0,0 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const STATE = require('./completion-authority-state.cjs');
6
- const RECEIPT = require('../scripts/spec-receipt.cjs');
7
- const RESOLVER = require('../scripts/spec-resolver.cjs');
8
-
9
- const scripts = path.join(__dirname, '..', 'scripts');
10
-
11
- function runtimeModule(root, name) {
12
- const target = path.join(root, name);
13
- const stat = fs.lstatSync(target);
14
- if (stat.isSymbolicLink() || !stat.isFile()) throw new Error(`runtime dependency must be a regular non-symlink file: ${name}`);
15
- const canonicalRoot = fs.realpathSync(root);
16
- const canonical = fs.realpathSync(target);
17
- const relative = path.relative(canonicalRoot, canonical);
18
- if (relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) throw new Error(`runtime dependency escaped scripts root: ${name}`);
19
- return require(canonical);
20
- }
21
-
22
- const FINAL_STATE = runtimeModule(scripts, 'spec-final-state.cjs');
23
-
24
- function explicitTarget(payload) {
25
- return typeof RESOLVER.extractExplicitTarget === 'function' ? RESOLVER.extractExplicitTarget(payload) : null;
26
- }
27
-
28
- function resolveCandidate({ resolver = RESOLVER, projectRoot, runtime, payload }) {
29
- const target = typeof resolver.extractExplicitTarget === 'function' ? resolver.extractExplicitTarget(payload) : explicitTarget(payload);
30
- if (typeof resolver.resolvePersistedSpec === 'function') return resolver.resolvePersistedSpec({ projectRoot, runtime, target });
31
- if (target) return resolver.resolveActiveSpec({ projectRoot, runtime, explicitFeature: target.explicitFeature, explicitPath: target.explicitPath });
32
- const candidates = resolver.findAllSpecCandidates(projectRoot, runtime);
33
- if (candidates.length > 1) return { error: 'multiple_persisted', candidates: candidates.map((candidate) => candidate.featureName) };
34
- return candidates[0] || null;
35
- }
36
-
37
- function dependencies(overrides = {}) {
38
- if (!overrides.grounder) runtimeModule(scripts, 'spec-semantic-model.cjs');
39
- const grounder = overrides.grounder || runtimeModule(scripts, 'spec-ground.cjs');
40
- const validator = overrides.validator || runtimeModule(scripts, 'validate-spec-output.cjs');
41
- return {
42
- validator,
43
- grounder,
44
- semanticAuthority: overrides.semanticAuthority || require('./semantic-review-authority.cjs'),
45
- };
46
- }
47
-
48
- module.exports = FINAL_STATE.createAuthority({
49
- state: STATE,
50
- resolveCandidate,
51
- dependencies,
52
- receipts: {
53
- task: (featureDir, taskPath, task, context, policy) => RECEIPT.checkTaskReceipt(featureDir, taskPath, task, context, policy),
54
- feature: (featureDir, context, policy) => RECEIPT.checkFeatureReceipt(featureDir, context, policy),
55
- },
56
- });