@haposoft/cafekit 0.16.0-rc.2 → 0.16.0-rc.4

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 (98) hide show
  1. package/bin/__tests__/codex-hooks.test.js +794 -21
  2. package/bin/__tests__/codex-native.test.js +228 -7
  3. package/bin/__tests__/develop-contract.test.js +1838 -112
  4. package/bin/__tests__/installer-safety.test.js +82 -29
  5. package/bin/__tests__/opencode-spec-gate.test.js +799 -0
  6. package/bin/__tests__/package-inventory.test.js +841 -10
  7. package/bin/__tests__/specs-v2-execution-closeout.test.js +421 -0
  8. package/bin/__tests__/specs-v2-policy-and-scaffold.test.js +601 -0
  9. package/bin/__tests__/specs-v2-validator-grounder-contract.test.js +952 -0
  10. package/bin/__tests__/test-runtime-dependency-closure.cjs +182 -0
  11. package/bin/__tests__/validator-grounding.test.js +2469 -71
  12. package/bin/lib/managed-writer.js +12 -3
  13. package/bin/phases/codex-runtime.js +17 -1
  14. package/package.json +1 -1
  15. package/src/claude/CLAUDE.md +6 -0
  16. package/src/claude/agents/code-auditor.md +20 -0
  17. package/src/claude/agents/spec-maker.md +153 -202
  18. package/src/claude/hooks/__tests__/completion-authority.test.js +953 -0
  19. package/src/claude/hooks/__tests__/privacy-block.test.js +172 -0
  20. package/src/claude/hooks/__tests__/semantic-review-authority.test.js +257 -0
  21. package/src/claude/hooks/__tests__/spec-gate.test.js +604 -56
  22. package/src/claude/hooks/completion-authority-check.cjs +56 -0
  23. package/src/claude/hooks/completion-authority-state.cjs +527 -0
  24. package/src/claude/hooks/completion-authority.cjs +154 -0
  25. package/src/claude/hooks/docs-sync.cjs +5 -2
  26. package/src/claude/hooks/lib/privacy-command-analysis.cjs +393 -0
  27. package/src/claude/hooks/lib/runtime-path-safety.cjs +119 -0
  28. package/src/claude/hooks/privacy-block.cjs +77 -30
  29. package/src/claude/hooks/semantic-review-authority.cjs +152 -0
  30. package/src/claude/hooks/session.cjs +33 -19
  31. package/src/claude/hooks/spec-gate.cjs +181 -139
  32. package/src/claude/hooks/spec-state.cjs +112 -47
  33. package/src/claude/migration-manifest.json +15 -2
  34. package/src/claude/rules/state-sync.md +52 -21
  35. package/src/claude/rules/workflow.md +17 -9
  36. package/src/claude/scripts/provenance.cjs +315 -0
  37. package/src/claude/scripts/spec-final-state.cjs +244 -0
  38. package/src/claude/scripts/spec-ground.cjs +535 -173
  39. package/src/claude/scripts/spec-readiness.cjs +118 -0
  40. package/src/claude/scripts/spec-receipt.cjs +162 -0
  41. package/src/claude/scripts/spec-resolver.cjs +501 -0
  42. package/src/claude/scripts/spec-scaffold.cjs +1945 -55
  43. package/src/claude/scripts/spec-semantic-model.cjs +396 -0
  44. package/src/claude/scripts/validate-spec-output.cjs +3204 -262
  45. package/src/claude/scripts/workflow-policy.cjs +1888 -203
  46. package/src/claude/settings/settings.json +12 -0
  47. package/src/claude/skills/code-review/SKILL.md +94 -66
  48. package/src/claude/skills/code-review/references/spec-compliance-review.md +10 -2
  49. package/src/claude/skills/code-review/references/verification-gate.md +13 -0
  50. package/src/claude/skills/develop/SKILL.md +138 -283
  51. package/src/claude/skills/develop/references/parallel-waves.md +9 -6
  52. package/src/claude/skills/develop/references/quality-gate.md +123 -148
  53. package/src/claude/skills/develop/references/subagent-patterns.md +42 -37
  54. package/src/claude/skills/specs/SKILL.md +192 -397
  55. package/src/claude/skills/specs/references/ask-user-question-gates.md +27 -18
  56. package/src/claude/skills/specs/references/cross-spec-dependency.md +8 -4
  57. package/src/claude/skills/specs/references/research-strategy.md +4 -1
  58. package/src/claude/skills/specs/references/review.md +200 -260
  59. package/src/claude/skills/specs/references/scope-inquiry.md +18 -5
  60. package/src/claude/skills/specs/rules/design-discovery-full.md +33 -81
  61. package/src/claude/skills/specs/rules/design-discovery-light.md +21 -42
  62. package/src/claude/skills/specs/rules/design-principles.md +136 -180
  63. package/src/claude/skills/specs/rules/design-review.md +42 -97
  64. package/src/claude/skills/specs/rules/ears-format.md +42 -50
  65. package/src/claude/skills/specs/rules/phase-decision-matrix.md +42 -41
  66. package/src/claude/skills/specs/rules/task-scoring-rubric.md +46 -46
  67. package/src/claude/skills/specs/rules/tasks-generation.md +189 -271
  68. package/src/claude/skills/specs/templates/design.md +49 -263
  69. package/src/claude/skills/specs/templates/requirements.md +26 -36
  70. package/src/claude/skills/specs/templates/research.md +9 -98
  71. package/src/claude/skills/specs/templates/spec-state.json +26 -67
  72. package/src/claude/skills/specs/templates/task.md +30 -82
  73. package/src/claude/skills/sync/SKILL.md +45 -15
  74. package/src/claude/skills/sync/references/sync-protocols.md +34 -16
  75. package/src/claude/skills/test/SKILL.md +151 -167
  76. package/src/codex/AGENTS.md +6 -0
  77. package/src/codex/hooks/completion-authority-check.cjs +63 -0
  78. package/src/codex/hooks/completion-authority-state.cjs +527 -0
  79. package/src/codex/hooks/completion-authority.cjs +106 -0
  80. package/src/codex/hooks/lib/hook-context.cjs +58 -4
  81. package/src/codex/hooks/lib/privacy-command-analysis.cjs +3 -0
  82. package/src/codex/hooks/lib/privacy-state.cjs +38 -16
  83. package/src/codex/hooks/lib/runtime-path-safety.cjs +3 -0
  84. package/src/codex/hooks/lib/spec-receipt.cjs +86 -115
  85. package/src/codex/hooks/lib/spec-utils.cjs +104 -27
  86. package/src/codex/hooks/privacy-approval.cjs +1 -1
  87. package/src/codex/hooks/privacy-block.cjs +67 -11
  88. package/src/codex/hooks/semantic-review-authority.cjs +130 -0
  89. package/src/codex/hooks/session.cjs +2 -0
  90. package/src/codex/hooks/spec-gate.cjs +151 -24
  91. package/src/codex/hooks/spec-state.cjs +65 -12
  92. package/src/codex/hooks/state.cjs +2 -0
  93. package/src/codex/hooks.json +16 -1
  94. package/src/codex/rules/state-sync.md +47 -27
  95. package/src/common/AGENTS.md +1 -1
  96. package/src/opencode/AGENTS.md +4 -2
  97. package/src/opencode/plugins/spec-gate.ts +296 -122
  98. package/src/opencode/plugins/spec-state.ts +183 -116
@@ -23,6 +23,7 @@ const { upsertManagedCoreBlock } = require('../lib/instruction-blocks');
23
23
  const { upsertManagedCodexBlock, normalizeCodexBody } = require('../lib/codex-install');
24
24
  const { copyRecursive } = require('../lib/copy-utils');
25
25
  const { createTracker, sha256 } = require('../lib/manifest');
26
+ const { copyManagedTree } = require('../lib/managed-writer');
26
27
 
27
28
  const START = '<!-- CAFEKIT CLAUDE START -->';
28
29
  const END = '<!-- CAFEKIT CLAUDE END -->';
@@ -672,6 +673,44 @@ test('managed targets and snapshots reject project symlink traversal', () => {
672
673
  });
673
674
  });
674
675
 
676
+ test('managed tree copy rejects source and destination symlink traversal before writes', () => {
677
+ inTempProject((root) => {
678
+ const source = path.join(root, 'source');
679
+ const outsideSource = path.join(root, 'outside-source.txt');
680
+ const destination = path.join('.codex', 'payload');
681
+ fs.mkdirSync(source);
682
+ fs.writeFileSync(outsideSource, 'outside');
683
+ fs.symlinkSync(outsideSource, path.join(source, 'linked.txt'));
684
+ const ctx = {
685
+ dryRun: false,
686
+ options: { forceOverwrite: false },
687
+ ownership: {},
688
+ results: { preservedFiles: [] },
689
+ };
690
+ const copy = () => copyManagedTree({
691
+ src: source,
692
+ dest: destination,
693
+ platformFolder: '.codex',
694
+ ctx,
695
+ tracker: null,
696
+ });
697
+ assert.throws(copy, /source tree cannot contain symlinks/i);
698
+ assert.equal(fs.existsSync(destination), false);
699
+
700
+ fs.rmSync(path.join(source, 'linked.txt'));
701
+ fs.writeFileSync(path.join(source, 'safe.txt'), 'safe');
702
+ const outsideDestination = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-managed-outside-'));
703
+ try {
704
+ fs.symlinkSync(outsideDestination, path.join(root, '.codex'));
705
+ assert.throws(copy, /Refusing to follow symlinked managed path/);
706
+ assert.deepEqual(fs.readdirSync(outsideDestination), []);
707
+ } finally {
708
+ fs.rmSync(path.join(root, '.codex'), { force: true });
709
+ fs.rmSync(outsideDestination, { recursive: true, force: true });
710
+ }
711
+ });
712
+ });
713
+
675
714
  test('managed Claude Addressing survives force refresh when template drops it', () => {
676
715
  inTempProject(() => {
677
716
  const before = '# User preface\n\n';
@@ -761,50 +800,64 @@ test('copyRecursive skips generated artifacts while copying normal files', () =>
761
800
  });
762
801
  });
763
802
 
764
- test('OpenCode parity: AGENTS marks advisory completion gate and plugin provides best-effort spec-gate', () => {
803
+ test('OpenCode gate uses a real before-hook block and documents the host boundary', () => {
765
804
  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)
805
+ const assertOpenCodeBoundary = (content) => {
806
+ assert.match(content, /tool\.execute\.before/);
807
+ assert.match(content, /hard-block supported completion\/state tools/);
808
+ assert.match(content, /`task`, `taskupdate`, and `todowrite`/);
809
+ assert.match(content, /CAFEKIT_SPEC_GATE_BLOCKED/);
810
+ assert.match(content, /`session\.idle`\/Stop/);
811
+ assert.match(content, /final assistant-turn cancellation boundary/);
812
+ assert.match(content, /observational only/);
813
+ assert.match(content, /full-turn parity with Claude\/Codex/);
814
+ assert.doesNotMatch(content, /tool\.execute\.after/);
815
+ assert.doesNotMatch(content, /best-effort/);
816
+ assert.doesNotMatch(content, /tier-2/);
817
+ assert.doesNotMatch(content, /\badvisory\b/i);
818
+ };
819
+ assertOpenCodeBoundary(opencodeAgents);
820
+
777
821
  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');
822
+ assert.equal(fs.existsSync(pluginPath), true, 'OpenCode spec-gate plugin must exist');
779
823
  const plugin = fs.readFileSync(pluginPath, 'utf8');
780
- assert.match(plugin, /advisory/i);
781
824
  assert.match(plugin, /completion_gate/);
782
- assert.match(plugin, /fail-open/i);
783
- assert.match(plugin, /session\.idle|tool\.execute\.after/);
825
+ assert.match(plugin, /tool\.execute\.before/);
826
+ assert.match(plugin, /CAFEKIT_SPEC_GATE_BLOCKED/);
827
+ assert.match(plugin, /throw createBlockError/);
828
+ assert.match(plugin, /session\.idle/);
829
+ assert.match(plugin, /final assistant message[\s\S]*outside this adapter's boundary/);
830
+ assert.doesNotMatch(plugin, /advisory/i);
831
+ assert.doesNotMatch(plugin, /fail-open/i);
784
832
  // JSDoc-safe wording: must not contain block-comment-closing pattern specs/*/spec.json
785
833
  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)
834
+ // Traversal check must be sibling-prefix safe (resolve + relative, not simple startsWith).
788
835
  assert.match(plugin, /relative\s*\(\s*resolvedFeatureDir/);
789
836
  assert.match(plugin, /isAbsolute/);
790
837
  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
838
+ // Single executable authorities: OpenCode delegates both validation and resolution.
839
+ assert.match(plugin, /workflow-policy\.cjs/);
840
+ assert.match(plugin, /spec-resolver\.cjs/);
841
+ assert.match(plugin, /getSharedValidate|validateCanonicalReceipt/);
842
+ assert.doesNotMatch(plugin, /const hasBase\s*&&\s*hasHead/);
843
+ assert.doesNotMatch(plugin, /PLACEHOLDER_TOKENS/);
844
+
800
845
  const installerDocs = fs.readFileSync(path.join(__dirname, '../../../../docs/installer-architecture.md'), 'utf8');
801
846
  assert.doesNotMatch(installerDocs, /OpenCode.*hard.*completion gate/i);
802
- // combined install must copy the plugin
847
+
848
+ // Combined install must copy the plugin and both shared authorities.
803
849
  inTempProject((root) => {
804
850
  const result = spawnSync(process.execPath, [INSTALLER, '--platform', 'opencode', '--yes'], { cwd: root, encoding: 'utf8', env: { ...process.env, PATH: '/usr/bin:/bin' } });
805
851
  assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
806
852
  assert.equal(fs.existsSync(path.join(root, '.opencode/plugins/spec-gate.ts')), true);
853
+ assert.equal(fs.existsSync(path.join(root, '.opencode/scripts/workflow-policy.cjs')), true);
854
+ assert.equal(fs.existsSync(path.join(root, '.opencode/scripts/spec-resolver.cjs')), true);
855
+ const installedAgents = fs.readFileSync(path.join(root, 'AGENTS.md'), 'utf8');
856
+ assertOpenCodeBoundary(managedBody(installedAgents, OPENCODE_START, OPENCODE_END));
807
857
  const installed = fs.readFileSync(path.join(root, '.opencode/plugins/spec-gate.ts'), 'utf8');
808
- assert.match(installed, /advisory/i);
858
+ assert.match(installed, /tool\.execute\.before/);
859
+ assert.match(installed, /CAFEKIT_SPEC_GATE_BLOCKED/);
860
+ assert.match(installed, /workflow-policy\.cjs/);
861
+ assert.match(installed, /spec-resolver\.cjs/);
809
862
  });
810
863
  });