@haposoft/cafekit 0.16.0-rc.1 → 0.16.0-rc.3

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 (51) hide show
  1. package/bin/__tests__/codex-hooks.test.js +355 -12
  2. package/bin/__tests__/codex-native.test.js +1 -1
  3. package/bin/__tests__/develop-contract.test.js +1374 -78
  4. package/bin/__tests__/installer-safety.test.js +43 -29
  5. package/bin/__tests__/opencode-spec-gate.test.js +791 -0
  6. package/bin/__tests__/package-inventory.test.js +157 -0
  7. package/bin/__tests__/validator-grounding.test.js +707 -2
  8. package/package.json +1 -1
  9. package/src/claude/agents/code-auditor.md +8 -1
  10. package/src/claude/agents/spec-maker.md +107 -200
  11. package/src/claude/hooks/__tests__/completion-authority.test.js +720 -0
  12. package/src/claude/hooks/__tests__/spec-gate.test.js +511 -38
  13. package/src/claude/hooks/completion-authority-check.cjs +307 -0
  14. package/src/claude/hooks/completion-authority-state.cjs +525 -0
  15. package/src/claude/hooks/completion-authority.cjs +148 -0
  16. package/src/claude/hooks/session.cjs +33 -19
  17. package/src/claude/hooks/spec-gate.cjs +173 -61
  18. package/src/claude/hooks/spec-state.cjs +104 -45
  19. package/src/claude/migration-manifest.json +6 -1
  20. package/src/claude/scripts/provenance.cjs +315 -0
  21. package/src/claude/scripts/spec-resolver.cjs +450 -0
  22. package/src/claude/scripts/spec-scaffold.cjs +716 -41
  23. package/src/claude/scripts/validate-spec-output.cjs +355 -53
  24. package/src/claude/scripts/workflow-policy.cjs +1529 -162
  25. package/src/claude/settings/settings.json +8 -0
  26. package/src/claude/skills/backend-development/references/backend-security.md +52 -0
  27. package/src/claude/skills/code-review/SKILL.md +85 -66
  28. package/src/claude/skills/develop/SKILL.md +153 -285
  29. package/src/claude/skills/develop/references/quality-gate.md +112 -124
  30. package/src/claude/skills/develop/references/subagent-patterns.md +42 -37
  31. package/src/claude/skills/specs/SKILL.md +170 -381
  32. package/src/claude/skills/specs/rules/task-scoring-rubric.md +45 -46
  33. package/src/claude/skills/specs/rules/tasks-generation.md +49 -20
  34. package/src/claude/skills/specs/templates/spec-state.json +23 -2
  35. package/src/claude/skills/specs/templates/task.md +20 -0
  36. package/src/claude/skills/sync/SKILL.md +15 -8
  37. package/src/claude/skills/sync/references/sync-protocols.md +4 -4
  38. package/src/claude/skills/test/SKILL.md +141 -167
  39. package/src/codex/hooks/completion-authority-check.cjs +260 -0
  40. package/src/codex/hooks/completion-authority-state.cjs +525 -0
  41. package/src/codex/hooks/completion-authority.cjs +100 -0
  42. package/src/codex/hooks/lib/hook-context.cjs +42 -2
  43. package/src/codex/hooks/lib/spec-receipt.cjs +76 -43
  44. package/src/codex/hooks/lib/spec-utils.cjs +53 -27
  45. package/src/codex/hooks/session.cjs +2 -0
  46. package/src/codex/hooks/spec-gate.cjs +132 -20
  47. package/src/codex/hooks/spec-state.cjs +56 -10
  48. package/src/codex/hooks.json +10 -0
  49. package/src/opencode/AGENTS.md +4 -2
  50. package/src/opencode/plugins/spec-gate.ts +296 -122
  51. package/src/opencode/plugins/spec-state.ts +183 -116
@@ -761,50 +761,64 @@ test('copyRecursive skips generated artifacts while copying normal files', () =>
761
761
  });
762
762
  });
763
763
 
764
- test('OpenCode parity: AGENTS marks advisory completion gate and plugin provides best-effort spec-gate', () => {
764
+ test('OpenCode gate uses a real before-hook block and documents the host boundary', () => {
765
765
  const opencodeAgents = fs.readFileSync(path.join(__dirname, '../../src/opencode/AGENTS.md'), 'utf8');
766
- assert.match(opencodeAgents, /advisory/i);
767
- assert.match(opencodeAgents, /tier-2/i);
768
- assert.match(opencodeAgents, /completion gate/i);
769
- assert.match(opencodeAgents, /Do not claim parity with Claude\/Codex on `completion_gate`/);
770
- assert.match(opencodeAgents, /spec-gate/);
771
- // docs must describe actual after-hook, not before, and warning/banner semantics (not advisory-pass)
772
- assert.match(opencodeAgents, /tool\.execute\.after/);
773
- assert.doesNotMatch(opencodeAgents, /tool\.execute\.before/);
774
- assert.match(opencodeAgents, /emits an advisory warning\/banner but does not hard-block the turn/);
775
- assert.doesNotMatch(opencodeAgents, /treat `done` without a receipt as advisory-pass/);
776
- // plugin file must exist and be advisory (never hard-block)
766
+ const assertOpenCodeBoundary = (content) => {
767
+ assert.match(content, /tool\.execute\.before/);
768
+ assert.match(content, /hard-block supported completion\/state tools/);
769
+ assert.match(content, /`task`, `taskupdate`, and `todowrite`/);
770
+ assert.match(content, /CAFEKIT_SPEC_GATE_BLOCKED/);
771
+ assert.match(content, /`session\.idle`\/Stop/);
772
+ assert.match(content, /final assistant-turn cancellation boundary/);
773
+ assert.match(content, /observational only/);
774
+ assert.match(content, /full-turn parity with Claude\/Codex/);
775
+ assert.doesNotMatch(content, /tool\.execute\.after/);
776
+ assert.doesNotMatch(content, /best-effort/);
777
+ assert.doesNotMatch(content, /tier-2/);
778
+ assert.doesNotMatch(content, /\badvisory\b/i);
779
+ };
780
+ assertOpenCodeBoundary(opencodeAgents);
781
+
777
782
  const pluginPath = path.join(__dirname, '../../src/opencode/plugins/spec-gate.ts');
778
- assert.equal(fs.existsSync(pluginPath), true, 'OpenCode spec-gate plugin must exist for parity contract');
783
+ assert.equal(fs.existsSync(pluginPath), true, 'OpenCode spec-gate plugin must exist');
779
784
  const plugin = fs.readFileSync(pluginPath, 'utf8');
780
- assert.match(plugin, /advisory/i);
781
785
  assert.match(plugin, /completion_gate/);
782
- assert.match(plugin, /fail-open/i);
783
- assert.match(plugin, /session\.idle|tool\.execute\.after/);
786
+ assert.match(plugin, /tool\.execute\.before/);
787
+ assert.match(plugin, /CAFEKIT_SPEC_GATE_BLOCKED/);
788
+ assert.match(plugin, /throw createBlockError/);
789
+ assert.match(plugin, /session\.idle/);
790
+ assert.match(plugin, /final assistant message[\s\S]*outside this adapter's boundary/);
791
+ assert.doesNotMatch(plugin, /advisory/i);
792
+ assert.doesNotMatch(plugin, /fail-open/i);
784
793
  // JSDoc-safe wording: must not contain block-comment-closing pattern specs/*/spec.json
785
794
  assert.doesNotMatch(plugin, /specs\/\*\/spec\.json/);
786
- assert.match(plugin, /specs\/<feature>\/spec\.json/);
787
- // traversal check must be sibling-prefix safe (resolve + relative, not simple startsWith)
795
+ // Traversal check must be sibling-prefix safe (resolve + relative, not simple startsWith).
788
796
  assert.match(plugin, /relative\s*\(\s*resolvedFeatureDir/);
789
797
  assert.match(plugin, /isAbsolute/);
790
798
  assert.doesNotMatch(plugin, /if \(!abs\.startsWith\(featureDir\)\)/);
791
- // provenance must require both endpoints (Base+Head or base_sha+head_sha) matching Claude/Codex policy
792
- assert.match(plugin, /hasBase\s*&&\s*hasHead/);
793
- assert.match(plugin, /hasBaseSha\s*&&\s*hasHeadSha/);
794
- assert.match(plugin, /hasBase.*hasHead.*hasBaseSha.*hasHeadSha/s);
795
- // hardened: non-empty same-line values required, not just field names
796
- assert.match(plugin, /Base\\s\*:\[ \\t\]\*\\S/);
797
- assert.match(plugin, /base_sha\\s\*:\[ \\t\]\*\\S/);
798
- assert.match(plugin, /head_sha\\s\*:\[ \\t\]\*\\S/);
799
- // shipped docs must not claim OpenCode parity for hard completion gate
799
+ // Single executable authorities: OpenCode delegates both validation and resolution.
800
+ assert.match(plugin, /workflow-policy\.cjs/);
801
+ assert.match(plugin, /spec-resolver\.cjs/);
802
+ assert.match(plugin, /getSharedValidate|validateCanonicalReceipt/);
803
+ assert.doesNotMatch(plugin, /const hasBase\s*&&\s*hasHead/);
804
+ assert.doesNotMatch(plugin, /PLACEHOLDER_TOKENS/);
805
+
800
806
  const installerDocs = fs.readFileSync(path.join(__dirname, '../../../../docs/installer-architecture.md'), 'utf8');
801
807
  assert.doesNotMatch(installerDocs, /OpenCode.*hard.*completion gate/i);
802
- // combined install must copy the plugin
808
+
809
+ // Combined install must copy the plugin and both shared authorities.
803
810
  inTempProject((root) => {
804
811
  const result = spawnSync(process.execPath, [INSTALLER, '--platform', 'opencode', '--yes'], { cwd: root, encoding: 'utf8', env: { ...process.env, PATH: '/usr/bin:/bin' } });
805
812
  assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
806
813
  assert.equal(fs.existsSync(path.join(root, '.opencode/plugins/spec-gate.ts')), true);
814
+ assert.equal(fs.existsSync(path.join(root, '.opencode/scripts/workflow-policy.cjs')), true);
815
+ assert.equal(fs.existsSync(path.join(root, '.opencode/scripts/spec-resolver.cjs')), true);
816
+ const installedAgents = fs.readFileSync(path.join(root, 'AGENTS.md'), 'utf8');
817
+ assertOpenCodeBoundary(managedBody(installedAgents, OPENCODE_START, OPENCODE_END));
807
818
  const installed = fs.readFileSync(path.join(root, '.opencode/plugins/spec-gate.ts'), 'utf8');
808
- assert.match(installed, /advisory/i);
819
+ assert.match(installed, /tool\.execute\.before/);
820
+ assert.match(installed, /CAFEKIT_SPEC_GATE_BLOCKED/);
821
+ assert.match(installed, /workflow-policy\.cjs/);
822
+ assert.match(installed, /spec-resolver\.cjs/);
809
823
  });
810
824
  });