@haposoft/cafekit 0.16.7 → 0.18.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 (193) 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 +0 -36
  4. package/bin/__tests__/codex-native.test.js +46 -88
  5. package/bin/__tests__/develop-contract.test.js +26 -57
  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 +159 -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 +100 -541
  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 +0 -27
  20. package/bin/__tests__/specs-v2-execution-closeout.test.js +4 -69
  21. package/bin/__tests__/ui-ux-skill.test.js +101 -0
  22. package/bin/__tests__/usage-hook-retired.test.js +114 -0
  23. package/bin/__tests__/validator-grounding.test.js +9 -28
  24. package/bin/phases/codex-runtime.js +24 -0
  25. package/bin/phases/omp-runtime.js +22 -0
  26. package/package.json +1 -1
  27. package/src/claude/CLAUDE.md +3 -10
  28. package/src/claude/agents/brainstormer.md +7 -3
  29. package/src/claude/agents/code-auditor.md +35 -50
  30. package/src/claude/agents/debugger.md +5 -79
  31. package/src/claude/agents/deployer.md +3 -5
  32. package/src/claude/agents/docs-keeper.md +2 -7
  33. package/src/claude/agents/implementer.md +6 -9
  34. package/src/claude/agents/inspector.md +2 -3
  35. package/src/claude/agents/project-manager.md +7 -15
  36. package/src/claude/agents/researcher.md +9 -3
  37. package/src/claude/agents/spec-maker.md +11 -11
  38. package/src/claude/hooks/__tests__/fixtures/statusline-default.golden +1 -1
  39. package/src/claude/hooks/__tests__/spec-gate.test.js +74 -3
  40. package/src/claude/hooks/__tests__/spec-state-touched.test.js +134 -0
  41. package/src/claude/hooks/__tests__/state.test.js +68 -0
  42. package/src/claude/hooks/__tests__/statusline.test.js +140 -11
  43. package/src/claude/hooks/agent.cjs +2 -1
  44. package/src/claude/hooks/lib/context.cjs +0 -89
  45. package/src/claude/hooks/lib/hook-payload.cjs +2 -2
  46. package/src/claude/hooks/rules.cjs +2 -1
  47. package/src/claude/hooks/session.cjs +0 -1
  48. package/src/claude/hooks/spec-gate.cjs +17 -28
  49. package/src/claude/hooks/spec-state.cjs +95 -1
  50. package/src/claude/hooks/state.cjs +21 -2
  51. package/src/claude/migration-manifest.json +30 -15
  52. package/src/claude/rules/ai-dev-rules.md +1 -1
  53. package/src/claude/rules/hook-protocols.md +8 -15
  54. package/src/claude/rules/manage-docs.md +9 -23
  55. package/src/claude/rules/orchestrator.md +3 -4
  56. package/src/claude/rules/review-audit-self-decision.md +3 -3
  57. package/src/claude/rules/skill-domain-routing.md +1 -3
  58. package/src/claude/rules/skill-workflow-routing.md +4 -5
  59. package/src/claude/rules/state-sync.md +3 -12
  60. package/src/claude/rules/workflow.md +10 -18
  61. package/src/claude/runtime.schema.json +5 -3
  62. package/src/claude/scripts/generate-skill-catalog.cjs +7 -6
  63. package/src/claude/scripts/provenance.cjs +23 -5
  64. package/src/claude/scripts/spec-receipt.cjs +24 -7
  65. package/src/claude/scripts/spec-resolver.cjs +1 -2
  66. package/src/claude/scripts/validate-spec-output.cjs +5 -32
  67. package/src/claude/settings/settings.json +1 -32
  68. package/src/claude/skills/{question → ask}/SKILL.md +9 -75
  69. package/src/claude/skills/brainstorm/SKILL.md +43 -131
  70. package/src/claude/skills/code-review/SKILL.md +48 -17
  71. package/src/claude/skills/code-review/references/spec-compliance-review.md +2 -3
  72. package/src/claude/skills/code-review/references/verification-gate.md +22 -27
  73. package/src/claude/skills/debug/SKILL.md +29 -149
  74. package/src/claude/skills/develop/SKILL.md +27 -36
  75. package/src/claude/skills/develop/references/parallel-waves.md +0 -8
  76. package/src/claude/skills/develop/references/quality-gate.md +15 -13
  77. package/src/claude/skills/docs/SKILL.md +2 -2
  78. package/src/claude/skills/docs/references/update-workflow.md +1 -1
  79. package/src/claude/skills/fix/SKILL.md +153 -0
  80. package/src/claude/skills/{hotfix → fix}/references/diagnosis-protocol.md +2 -2
  81. package/src/claude/skills/{hotfix → fix}/references/parallel-patterns.md +2 -2
  82. package/src/claude/skills/{hotfix → fix}/references/prevention-gate.md +3 -3
  83. package/src/claude/skills/{hotfix → fix}/references/review-cycle.md +1 -1
  84. package/src/claude/skills/git/SKILL.md +48 -9
  85. package/src/claude/skills/git/references/commit-protocols.md +35 -16
  86. package/src/claude/skills/git/references/worktree-blueprint.md +42 -6
  87. package/src/claude/skills/orca/SKILL.md +9 -8
  88. package/src/claude/skills/research/SKILL.md +2 -0
  89. package/src/claude/skills/scout/SKILL.md +61 -0
  90. package/src/claude/skills/{inspect → scout}/references/internal-inspection.md +26 -10
  91. package/src/claude/skills/specs/SKILL.md +22 -29
  92. package/src/claude/skills/specs/references/review.md +8 -6
  93. package/src/claude/skills/specs/references/templates.md +34 -41
  94. package/src/claude/skills/sync/SKILL.md +8 -15
  95. package/src/claude/skills/sync/references/rebind-and-audit.md +59 -0
  96. package/src/claude/skills/sync/references/sync-protocols.md +4 -13
  97. package/src/claude/skills/test/SKILL.md +22 -3
  98. package/src/claude/skills/test/references/execution-strategy.md +83 -0
  99. package/src/claude/skills/test/references/failure-triage.md +1 -1
  100. package/src/claude/skills/ui-ux/LICENSE +21 -0
  101. package/src/claude/skills/ui-ux/SKILL.md +497 -0
  102. package/src/claude/skills/ui-ux/references/brand-tokens.md +90 -0
  103. package/src/claude/skills/ui-ux/references/budgets.md +113 -0
  104. package/src/claude/skills/ui-ux/references/checklist.md +271 -0
  105. package/src/claude/skills/ui-ux/references/components/accordion.md +246 -0
  106. package/src/claude/skills/ui-ux/references/components/avatar.md +153 -0
  107. package/src/claude/skills/ui-ux/references/components/banner.md +43 -0
  108. package/src/claude/skills/ui-ux/references/components/breadcrumb.md +201 -0
  109. package/src/claude/skills/ui-ux/references/components/button.md +142 -0
  110. package/src/claude/skills/ui-ux/references/components/card.md +113 -0
  111. package/src/claude/skills/ui-ux/references/components/charts.md +376 -0
  112. package/src/claude/skills/ui-ux/references/components/chat.md +132 -0
  113. package/src/claude/skills/ui-ux/references/components/choice-controls.md +369 -0
  114. package/src/claude/skills/ui-ux/references/components/comment-thread.md +17 -0
  115. package/src/claude/skills/ui-ux/references/components/description-list.md +116 -0
  116. package/src/claude/skills/ui-ux/references/components/empty-state.md +98 -0
  117. package/src/claude/skills/ui-ux/references/components/file-upload.md +107 -0
  118. package/src/claude/skills/ui-ux/references/components/inline-edit.md +128 -0
  119. package/src/claude/skills/ui-ux/references/components/input.md +161 -0
  120. package/src/claude/skills/ui-ux/references/components/list-row.md +56 -0
  121. package/src/claude/skills/ui-ux/references/components/loading.md +198 -0
  122. package/src/claude/skills/ui-ux/references/components/logo.md +147 -0
  123. package/src/claude/skills/ui-ux/references/components/otp-input.md +43 -0
  124. package/src/claude/skills/ui-ux/references/components/quantity-input.md +100 -0
  125. package/src/claude/skills/ui-ux/references/components/range-slider.md +16 -0
  126. package/src/claude/skills/ui-ux/references/components/small-controls.md +340 -0
  127. package/src/claude/skills/ui-ux/references/components/sortable-header.md +86 -0
  128. package/src/claude/skills/ui-ux/references/components/tag-input.md +14 -0
  129. package/src/claude/skills/ui-ux/references/components/timeline.md +38 -0
  130. package/src/claude/skills/ui-ux/references/components/tree.md +30 -0
  131. package/src/claude/skills/ui-ux/references/design-process.md +650 -0
  132. package/src/claude/skills/ui-ux/references/layouts/app-kanban.html +303 -0
  133. package/src/claude/skills/ui-ux/references/layouts/app.md +1369 -0
  134. package/src/claude/skills/ui-ux/references/layouts/form.md +430 -0
  135. package/src/claude/skills/ui-ux/references/layouts/overlay.md +552 -0
  136. package/src/claude/skills/ui-ux/references/layouts/pricing.md +204 -0
  137. package/src/claude/skills/ui-ux/references/locked-rules.md +35 -0
  138. package/src/claude/skills/ui-ux/references/principles.md +408 -0
  139. package/src/claude/skills/ui-ux/references/refactor.md +245 -0
  140. package/src/claude/skills/ui-ux/references/responsive.md +195 -0
  141. package/src/claude/skills/ui-ux/references/review.md +528 -0
  142. package/src/claude/skills/ui-ux/references/rules-color.md +623 -0
  143. package/src/claude/skills/ui-ux/references/rules-form.md +235 -0
  144. package/src/claude/skills/ui-ux/references/rules-state.md +680 -0
  145. package/src/claude/skills/ui-ux/references/rules-type.md +369 -0
  146. package/src/claude/skills/ui-ux/references/styles.md +311 -0
  147. package/src/claude/skills/ui-ux/references/system.md +219 -0
  148. package/src/claude/skills/ui-ux/references/tailwind-v4-traps.md +237 -0
  149. package/src/claude/skills/ui-ux/references/tokens.css +362 -0
  150. package/src/claude/skills/ui-ux/scripts/lint-skill.mjs +216 -0
  151. package/src/claude/skills/ui-ux/scripts/package.json +9 -0
  152. package/src/claude/skills/ui-ux/scripts/probe.mjs +4447 -0
  153. package/src/claude/skills/ui-ux-pro-max/SKILL.md +1 -1
  154. package/src/claude/status.cjs +149 -144
  155. package/src/codex/AGENTS.md +8 -13
  156. package/src/codex/hooks/agent.cjs +2 -1
  157. package/src/codex/hooks/lib/spec-receipt.cjs +2 -2
  158. package/src/codex/hooks/lib/spec-utils.cjs +0 -7
  159. package/src/codex/hooks/rules.cjs +2 -1
  160. package/src/codex/hooks/session.cjs +0 -2
  161. package/src/codex/hooks/spec-gate.cjs +7 -18
  162. package/src/codex/hooks.json +0 -20
  163. package/src/codex/rules/state-sync.md +3 -12
  164. package/src/common/AGENTS.md +2 -2
  165. package/src/omp/extensions/cafekit-bridge.mjs +6 -6
  166. package/src/claude/hooks/__tests__/completion-authority.test.js +0 -990
  167. package/src/claude/hooks/__tests__/semantic-review-authority.test.js +0 -259
  168. package/src/claude/hooks/completion-authority-check.cjs +0 -56
  169. package/src/claude/hooks/completion-authority-state.cjs +0 -527
  170. package/src/claude/hooks/completion-authority.cjs +0 -163
  171. package/src/claude/hooks/semantic-review-authority.cjs +0 -154
  172. package/src/claude/hooks/task-scaffold-guard.cjs +0 -114
  173. package/src/claude/hooks/usage.cjs +0 -197
  174. package/src/claude/skills/delegate/SKILL.md +0 -124
  175. package/src/claude/skills/delegate/references/codex-delegation.md +0 -71
  176. package/src/claude/skills/delegate/references/grok-delegation.md +0 -84
  177. package/src/claude/skills/hotfix/SKILL.md +0 -289
  178. package/src/claude/skills/inspect/SKILL.md +0 -142
  179. package/src/claude/skills/loop/SKILL.md +0 -114
  180. package/src/claude/skills/loop/references/bounded-loop-protocol.md +0 -104
  181. package/src/claude/skills/loop/references/metric-and-guard-contract.md +0 -76
  182. package/src/claude/skills/route/SKILL.md +0 -94
  183. package/src/claude/skills/route/references/agent-timing.md +0 -46
  184. package/src/claude/skills/route/references/chaining-patterns.md +0 -53
  185. package/src/claude/skills/route/references/task-taxonomy.md +0 -48
  186. package/src/codex/hooks/completion-authority-check.cjs +0 -67
  187. package/src/codex/hooks/completion-authority-state.cjs +0 -527
  188. package/src/codex/hooks/completion-authority.cjs +0 -109
  189. package/src/codex/hooks/semantic-review-authority.cjs +0 -130
  190. package/src/codex/hooks/task-scaffold-guard.cjs +0 -63
  191. /package/src/claude/skills/{question → ask}/templates/question.md +0 -0
  192. /package/src/claude/skills/{hotfix → fix}/references/escalation-tactics.md +0 -0
  193. /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
- });