@haposoft/cafekit 0.18.0 → 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 (83) hide show
  1. package/README.md +1 -1
  2. package/bin/__tests__/codex-hooks.test.js +169 -236
  3. package/bin/__tests__/codex-native.test.js +54 -114
  4. package/bin/__tests__/develop-contract.test.js +101 -1361
  5. package/bin/__tests__/legacy-authority-decoupled.test.js +14 -19
  6. package/bin/__tests__/package-inventory.test.js +1 -395
  7. package/bin/__tests__/spec-narrowing.test.js +30 -134
  8. package/bin/__tests__/specs-v2-execution-closeout.test.js +0 -372
  9. package/bin/__tests__/test-runtime-dependency-closure.cjs +1 -2
  10. package/bin/__tests__/validator-grounding.test.js +17 -2929
  11. package/bin/lib/managed-writer.js +2 -3
  12. package/bin/phases/codex-runtime.js +6 -3
  13. package/bin/phases/copy-payload.js +13 -28
  14. package/bin/phases/omp-runtime.js +6 -3
  15. package/package.json +2 -3
  16. package/src/claude/agents/code-auditor.md +9 -6
  17. package/src/claude/agents/debugger.md +3 -9
  18. package/src/claude/agents/docs-keeper.md +4 -7
  19. package/src/claude/agents/git-ops.md +2 -4
  20. package/src/claude/agents/implementer.md +6 -7
  21. package/src/claude/agents/inspector.md +2 -0
  22. package/src/claude/agents/project-manager.md +2 -2
  23. package/src/claude/agents/spec-maker.md +4 -4
  24. package/src/claude/agents/test-runner.md +8 -11
  25. package/src/claude/agents/ui-ux-designer.md +0 -1
  26. package/src/claude/hooks/__tests__/spec-gate.test.js +170 -330
  27. package/src/claude/hooks/__tests__/spec-state-touched.test.js +26 -0
  28. package/src/claude/hooks/spec-gate.cjs +8 -63
  29. package/src/claude/hooks/spec-state.cjs +47 -38
  30. package/src/claude/migration-manifest.json +12 -7
  31. package/src/claude/output-styles/coding-level-0-eli5.md +9 -9
  32. package/src/claude/output-styles/coding-level-1-junior.md +3 -3
  33. package/src/claude/output-styles/coding-level-2-mid.md +3 -3
  34. package/src/claude/output-styles/coding-level-3-senior.md +2 -2
  35. package/src/claude/output-styles/coding-level-4-lead.md +5 -5
  36. package/src/claude/output-styles/coding-level-5-god.md +6 -6
  37. package/src/claude/rules/ai-dev-rules.md +1 -2
  38. package/src/claude/rules/workflow.md +1 -1
  39. package/src/claude/scripts/spec-receipt.cjs +0 -114
  40. package/src/claude/scripts/spec-resolver.cjs +41 -457
  41. package/src/claude/scripts/workflow-policy.cjs +3 -1801
  42. package/src/claude/skills/agent-browser/SKILL.md +1 -1
  43. package/src/claude/skills/chrome-devtools/SKILL.md +14 -31
  44. package/src/claude/skills/code-review/SKILL.md +25 -108
  45. package/src/claude/skills/code-review/references/spec-compliance-review.md +4 -7
  46. package/src/claude/skills/develop/SKILL.md +22 -70
  47. package/src/claude/skills/develop/references/subagent-patterns.md +0 -6
  48. package/src/claude/skills/research/SKILL.md +24 -55
  49. package/src/claude/skills/sync/SKILL.md +12 -34
  50. package/src/claude/skills/sync/references/rebind-and-audit.md +2 -2
  51. package/src/claude/skills/test/SKILL.md +39 -123
  52. package/src/claude/skills/test/references/execution-strategy.md +11 -22
  53. package/src/claude/skills/ui-ux/SKILL.md +1 -1
  54. package/src/claude/skills/ui-ux-pro-max/SKILL.md +24 -79
  55. package/src/claude/skills/web-testing/SKILL.md +2 -20
  56. package/src/codex/hooks/lib/spec-receipt.cjs +2 -30
  57. package/src/codex/hooks/lib/spec-utils.cjs +6 -45
  58. package/src/codex/hooks/spec-gate.cjs +11 -68
  59. package/src/codex/hooks/spec-state.cjs +37 -41
  60. package/src/common/AGENTS.md +4 -10
  61. package/bin/__tests__/change-firewall.semantic-firewall.test.js +0 -2133
  62. package/bin/__tests__/finalizer-pass-gate.semantic-firewall.test.js +0 -342
  63. package/bin/__tests__/release-preflight.semantic-firewall.test.js +0 -586
  64. package/bin/__tests__/review-receipt-c2.semantic-firewall.test.js +0 -378
  65. package/bin/__tests__/specs-v2-policy-and-scaffold.test.js +0 -665
  66. package/bin/__tests__/specs-v2-validator-grounder-contract.test.js +0 -1132
  67. package/src/claude/hooks/lib/context.cjs +0 -527
  68. package/src/claude/hooks/lib/detect.cjs +0 -474
  69. package/src/claude/scripts/change-firewall.cjs +0 -1748
  70. package/src/claude/scripts/spec-authoring-digest.cjs +0 -151
  71. package/src/claude/scripts/spec-authoring-validation.cjs +0 -126
  72. package/src/claude/scripts/spec-final-state.cjs +0 -290
  73. package/src/claude/scripts/spec-ground.cjs +0 -650
  74. package/src/claude/scripts/spec-readiness.cjs +0 -425
  75. package/src/claude/scripts/spec-scaffold.cjs +0 -2317
  76. package/src/claude/scripts/spec-semantic-model.cjs +0 -396
  77. package/src/claude/scripts/validate-spec-output.cjs +0 -3914
  78. package/src/claude/skills/code-review/references/verification-gate.md +0 -41
  79. package/src/claude/skills/specs/templates/design.md +0 -82
  80. package/src/claude/skills/specs/templates/requirements-init.md +0 -8
  81. package/src/claude/skills/specs/templates/requirements.md +0 -42
  82. package/src/claude/skills/specs/templates/spec-state.json +0 -56
  83. package/src/claude/skills/specs/templates/task.md +0 -47
@@ -182,12 +182,7 @@ const V3_SPECS_BUNDLE = [
182
182
  'SKILL.md',
183
183
  'references/review.md',
184
184
  'references/templates.md',
185
- 'templates/design.md',
186
- 'templates/requirements-init.md',
187
- 'templates/requirements.md',
188
- 'templates/research.md',
189
- 'templates/spec-state.json',
190
- 'templates/task.md'
185
+ 'templates/research.md'
191
186
  ];
192
187
  const OBSOLETE_SPECS_FILES = [
193
188
  'references/archive-workflow.md',
@@ -272,64 +267,6 @@ function replaceGeneratedTomlString(content, key, value) {
272
267
  return content.replace(pattern, `${key} = ${JSON.stringify(value)}`);
273
268
  }
274
269
 
275
- function renderCanonicalVerificationExample(template) {
276
- const values = new Map([
277
- ['SUBJECT_REQ', '1'],
278
- ['X', '1'],
279
- ['PROOF_REQ', '1'],
280
- ['Y', '2'],
281
- ['exact command', 'node --test test/installed.test.js'],
282
- ['exact anchored target', 'src/installed.js#entry'],
283
- ['exact/repository/entrypoint', 'src/installed.js'],
284
- ['observable result', 'the subject behavior and verifier proof both pass'],
285
- ['concrete observable result and proof', 'the subject behavior and verifier proof both pass'],
286
- ['concrete rejected or recovery case', 'invalid input remains rejected and observable'],
287
- ['real entrypoint/caller and grounded anchor expectation', 'the installed entrypoint reaches A-D-01'],
288
- ]);
289
- return template.replace(/\{\{([^}]+)\}\}/g, (placeholder, name) => (
290
- values.has(name) ? values.get(name) : placeholder
291
- ));
292
- }
293
-
294
- function assertInstalledVerificationModel(grounderPath, designTemplate) {
295
- const { parseVerificationDefinitions } = require(grounderPath);
296
- assert.equal(typeof parseVerificationDefinitions, 'function');
297
- const concreteDesign = renderCanonicalVerificationExample(designTemplate);
298
- const errors = [];
299
- const definitions = parseVerificationDefinitions(concreteDesign, errors);
300
- assert.deepEqual(errors, []);
301
- assert.equal(definitions.size, 1);
302
- const definition = definitions.get('V1');
303
- assert.ok(definition);
304
- assert.deepEqual(definition.subject_criteria, ['R1.1']);
305
- assert.deepEqual(definition.proof_criteria, []);
306
- assert.equal(definition.proof_owner, null);
307
- assert.equal(definition.evidence_anchor, null);
308
- assert.deepEqual(definition.decision_refs, ['D1', 'I1', 'C1']);
309
- for (const field of [
310
- 'subject_criteria', 'subject_owner', 'decision_refs', 'method', 'expected',
311
- 'negative', 'reachability'
312
- ]) {
313
- const value = definition[field];
314
- assert.ok(
315
- Array.isArray(value)
316
- ? value.length > 0
317
- : (typeof value === 'string' ? value.trim() !== '' : value && Object.keys(value).length > 0)
318
- );
319
- }
320
- for (const mutation of [
321
- concreteDesign.replace('- **V1**:', '### V1 —'),
322
- concreteDesign.replace('- **V1**:', '| V1 |'),
323
- concreteDesign.replace('; Expected ', '\nExpected '),
324
- concreteDesign.replace('Decision refs ', 'Decisions '),
325
- ]) {
326
- const mutationErrors = [];
327
- const mutated = parseVerificationDefinitions(mutation, mutationErrors);
328
- assert.ok(mutationErrors.length > 0);
329
- assert.equal(mutated.has('V1'), false);
330
- }
331
- }
332
-
333
270
  function markdownSection(content, heading) {
334
271
  const lines = String(content).split('\n');
335
272
  const start = lines.findIndex((line) => line.trim() === `## ${heading}`);
@@ -2160,22 +2097,12 @@ test('Codex installed Specs and spec-maker reject adaptive coverage mutations',
2160
2097
  '.codex/rules/state-sync.md',
2161
2098
  '.codex/rules/review-audit-self-decision.md',
2162
2099
  '.codex/rules/process-management.md',
2163
- '.codex/scripts/spec-ground.cjs',
2164
- '.codex/scripts/validate-spec-output.cjs',
2165
2100
  '.agents/.gitignore',
2166
2101
  ...V3_SPECS_BUNDLE.map((file) => `.agents/skills/specs/${file}`)
2167
2102
  ]) {
2168
2103
  assert.equal(fs.existsSync(path.join(root, relative)), true, `missing ${relative}`);
2169
2104
  }
2170
2105
 
2171
- const installedDesign = fs.readFileSync(
2172
- path.join(root, '.agents/skills/specs/templates/design.md'), 'utf8'
2173
- );
2174
- assert.equal((installedDesign.match(/^## Verification Definitions$/gm) || []).length, 1);
2175
- assertInstalledVerificationModel(
2176
- path.join(root, '.codex/scripts/spec-ground.cjs'),
2177
- installedDesign
2178
- );
2179
2106
  const installedSpecsRoot = path.join(root, '.agents/skills/specs');
2180
2107
  assert.throws(
2181
2108
  () => installedAuthoringProjectionIssues(root, '.agents/skills/specs'),
@@ -2191,22 +2118,6 @@ test('Codex installed Specs and spec-maker reject adaptive coverage mutations',
2191
2118
  );
2192
2119
  assertInstalledAuthoringProjection(root, installedSpecsRoot);
2193
2120
 
2194
- const installedTask = fs.readFileSync(
2195
- path.join(root, '.agents/skills/specs/templates/task.md'), 'utf8'
2196
- );
2197
- assert.deepEqual(
2198
- [...installedTask.matchAll(/^## (.+)$/gm)].map((match) => match[1]),
2199
- ['Outcome', 'Scope', 'Anchors and Ownership', 'Changes', 'Acceptance', 'Dependencies', 'Verification Plan']
2200
- );
2201
- assert.match(installedTask, /^- \*\*Task role:\*\*/m);
2202
-
2203
- const installedState = JSON.parse(fs.readFileSync(
2204
- path.join(root, '.agents/skills/specs/templates/spec-state.json'), 'utf8'
2205
- ));
2206
- assert.deepEqual(Object.keys(installedState.workflow_policy).sort(), [
2207
- 'assurance_level', 'classified_minimum', 'planning_depth', 'risks', 'version'
2208
- ]);
2209
-
2210
2121
  const agentsGitignore = fs.readFileSync(
2211
2122
  path.join(root, '.agents', '.gitignore'),
2212
2123
  'utf8'
@@ -2217,7 +2128,6 @@ test('Codex installed Specs and spec-maker reject adaptive coverage mutations',
2217
2128
 
2218
2129
  if (process.platform !== 'win32') {
2219
2130
  for (const [sourceRelative, installedRelative] of [
2220
- ['src/claude/scripts/validate-spec-output.cjs', '.codex/scripts/validate-spec-output.cjs'],
2221
2131
  ['src/claude/skills/chrome-devtools/scripts/install.sh', '.agents/skills/chrome-devtools/scripts/install.sh'],
2222
2132
  ['src/claude/skills/ai-multimodal/scripts/check_setup.py', '.agents/skills/ai-multimodal/scripts/check_setup.py']
2223
2133
  ]) {
@@ -2338,7 +2248,7 @@ test('Codex installed Specs and spec-maker reject adaptive coverage mutations',
2338
2248
  const questionSkill = path.join(root, '.agents', 'skills', 'ask', 'SKILL.md');
2339
2249
  fs.appendFileSync(questionSkill, '\nUSER-CODEX-SENTINEL\n');
2340
2250
  // Fresh installs are exact. Refresh/upgrade intentionally documents the
2341
- // current Codex limitation: removed skill paths are not pruned.
2251
+ // A skill file the manifest lists as obsolete is removed on refresh; edited skills stay.
2342
2252
  const obsoleteSpecsRule = path.join(
2343
2253
  root, '.agents', 'skills', 'specs', 'rules', 'design-principles.md'
2344
2254
  );
@@ -2351,14 +2261,15 @@ test('Codex installed Specs and spec-maker reject adaptive coverage mutations',
2351
2261
  assert.match(fs.readFileSync(questionSkill, 'utf8'), /USER-CODEX-SENTINEL/);
2352
2262
  assert.equal(
2353
2263
  fs.existsSync(obsoleteSpecsRule),
2354
- true,
2355
- 'known limitation: Codex refresh does not prune obsolete skill files'
2264
+ false,
2265
+ 'Codex refresh removes obsolete skill files'
2356
2266
  );
2357
2267
 
2358
2268
  const metadataPath = path.join(root, '.codex', 'cafekit.json');
2359
2269
  const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
2360
2270
  metadata.version = '0.14.1';
2361
2271
  fs.writeFileSync(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`);
2272
+ fs.writeFileSync(obsoleteSpecsRule, 'LEGACY-CODEX-ORPHAN\n');
2362
2273
 
2363
2274
  const upgrade = install(root);
2364
2275
  assert.equal(upgrade.status, 0, `${upgrade.stdout}\n${upgrade.stderr}`);
@@ -2366,8 +2277,8 @@ test('Codex installed Specs and spec-maker reject adaptive coverage mutations',
2366
2277
  assert.match(fs.readFileSync(questionSkill, 'utf8'), /USER-CODEX-SENTINEL/);
2367
2278
  assert.equal(
2368
2279
  fs.existsSync(obsoleteSpecsRule),
2369
- true,
2370
- 'known limitation: Codex upgrade does not prune obsolete skill files'
2280
+ false,
2281
+ 'Codex upgrade removes obsolete skill files'
2371
2282
  );
2372
2283
  assert.ok(fs.readFileSync(path.join(root, 'AGENTS.md'), 'utf8').startsWith(userInstructions));
2373
2284
 
@@ -3136,8 +3047,6 @@ test('Codex install on top of existing Claude installation preserves content and
3136
3047
  assert.equal(fs.existsSync(path.join(root, '.codex', 'runtime.json')), true, '.codex/runtime.json should exist');
3137
3048
  assert.equal(fs.existsSync(path.join(root, '.codex', 'hooks', 'privacy-block.cjs')), true, '.codex/hooks/privacy-block.cjs should exist');
3138
3049
  assert.equal(fs.existsSync(path.join(root, '.codex', 'rules', 'workflow.md')), true, '.codex/rules/workflow.md should exist');
3139
- assert.equal(fs.existsSync(path.join(root, '.codex', 'scripts', 'spec-ground.cjs')), true, '.codex/scripts/spec-ground.cjs should exist');
3140
- assert.equal(fs.existsSync(path.join(root, '.codex', 'scripts', 'validate-spec-output.cjs')), true, '.codex/scripts/validate-spec-output.cjs should exist');
3141
3050
  assert.equal(fs.existsSync(path.join(root, '.agents', '.gitignore')), true, '.agents/.gitignore should exist');
3142
3051
  assert.equal(fs.existsSync(path.join(root, '.agents', 'skills', 'specs', 'SKILL.md')), true, '.agents/skills/specs/SKILL.md should exist');
3143
3052
 
@@ -3170,23 +3079,54 @@ test('Codex installed code_auditor carries no Strict attestation marker', () =>
3170
3079
  });
3171
3080
  });
3172
3081
 
3173
- test('Codex scaffold resolver rejects symlink template', () => {
3082
+ test('reinstall removes obsolete scripts and skill files from Codex and omp installs', () => {
3174
3083
  inTempProject((root) => {
3175
- const result = install(root);
3176
- assert.equal(result.status, 0);
3177
- const template = path.join(root, '.agents', 'skills', 'specs', 'templates', 'task.md');
3178
- const target = path.join(root, 'outside.md');
3179
- fs.writeFileSync(target, 'evil');
3180
- const original = fs.readFileSync(template);
3181
- fs.unlinkSync(template);
3182
- fs.symlinkSync(target, template);
3183
- const scaffold = path.join(root, '.codex', 'scripts', 'spec-scaffold.cjs');
3184
- const out = spawnSync(process.execPath, [scaffold, 'symlink-test', '--tasks', 'R1-01-foo,R1-02-bar', '--boundaries', '[{"id":"B-OWN","type":"ownership","tasks":["R1-01","R1-02"],"write_sets":{"R1-01":["src/a.js"],"R1-02":["src/b.js"]}}]'], { cwd: root, encoding: 'utf8' });
3185
- // Should fail because template is symlink and resolver rejects it
3186
- assert.notEqual(out.status, 0, 'scaffold should reject symlink template');
3187
- assert.match(`${out.stdout}\n${out.stderr}`, /template not found|symlink/i);
3188
- fs.unlinkSync(template);
3189
- fs.writeFileSync(template, original);
3084
+ const first = installPlatforms(root, ['codex', 'omp']);
3085
+ assert.equal(first.status, 0, `${first.stdout}\n${first.stderr}`);
3086
+ const retired = ['change-firewall.cjs', 'spec-final-state.cjs'];
3087
+ for (const runtime of ['.codex', '.omp']) {
3088
+ for (const name of retired) fs.writeFileSync(path.join(root, runtime, 'scripts', name), 'legacy\n');
3089
+ }
3090
+ const retiredSkillFiles = ['code-review/references/verification-gate.md', 'specs/references/archive-workflow.md'];
3091
+ for (const rel of retiredSkillFiles) fs.writeFileSync(path.join(root, '.agents', 'skills', rel), 'legacy\n');
3092
+ const again = installPlatforms(root, ['codex', 'omp'], ['--force-overwrite']);
3093
+ assert.equal(again.status, 0, `${again.stdout}\n${again.stderr}`);
3094
+ for (const rel of retiredSkillFiles) {
3095
+ assert.equal(fs.existsSync(path.join(root, '.agents', 'skills', rel)), false, `.agents/skills/${rel} must be removed`);
3096
+ }
3097
+ assert.equal(fs.existsSync(path.join(root, '.agents', 'skills', 'code-review', 'SKILL.md')), true, 'the code-review skill stays');
3098
+ for (const runtime of ['.codex', '.omp']) {
3099
+ for (const name of retired) {
3100
+ assert.equal(fs.existsSync(path.join(root, runtime, 'scripts', name)), false, `${runtime}/scripts/${name} must be removed`);
3101
+ }
3102
+ assert.equal(fs.existsSync(path.join(root, runtime, 'scripts', 'spec-resolver.cjs')), true, `${runtime} keeps the resolver`);
3103
+ }
3104
+ });
3105
+ });
3106
+
3107
+ test('reinstall removes the retired spec.json templates from Claude and Codex but a dry run keeps them', () => {
3108
+ inTempProject((root) => {
3109
+ const first = installPlatforms(root, ['claude', 'codex']);
3110
+ assert.equal(first.status, 0, `${first.stdout}\n${first.stderr}`);
3111
+ const templateDirs = ['.claude/skills/specs/templates', '.agents/skills/specs/templates'];
3112
+ const retired = ['design.md', 'task.md', 'requirements.md', 'requirements-init.md', 'spec-state.json'];
3113
+ for (const dir of templateDirs) {
3114
+ for (const name of retired) fs.writeFileSync(path.join(root, dir, name), 'legacy\n');
3115
+ }
3116
+
3117
+ const dry = installPlatforms(root, ['claude', 'codex'], ['--force-overwrite', '--dry-run']);
3118
+ assert.equal(dry.status, 0, `${dry.stdout}\n${dry.stderr}`);
3119
+ for (const dir of templateDirs) {
3120
+ for (const name of retired) {
3121
+ assert.equal(fs.existsSync(path.join(root, dir, name)), true, `dry run removed ${dir}/${name}`);
3122
+ }
3123
+ }
3124
+
3125
+ const again = installPlatforms(root, ['claude', 'codex'], ['--force-overwrite']);
3126
+ assert.equal(again.status, 0, `${again.stdout}\n${again.stderr}`);
3127
+ for (const dir of templateDirs) {
3128
+ assert.deepEqual(fs.readdirSync(path.join(root, dir)), ['research.md'], `${dir} keeps only research.md`);
3129
+ }
3190
3130
  });
3191
3131
  });
3192
3132