@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
@@ -6,12 +6,6 @@ const path = require('path');
6
6
  const { spawnSync } = require('child_process');
7
7
  const { annotatedMarkdownLines } = require('./spec-resolver.cjs');
8
8
 
9
- const EVIDENCE_HEADINGS = new Set([
10
- 'Evidence',
11
- 'Task Test Plan & Verification Evidence',
12
- 'Verification & Evidence',
13
- ]);
14
-
15
9
  function inside(root, target) {
16
10
  const relative = path.relative(path.resolve(root), path.resolve(target));
17
11
  return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
@@ -40,34 +34,6 @@ function safeRead(root, relativePath) {
40
34
  }
41
35
  }
42
36
 
43
- function canonicalTaskReceiptPath(taskPath) {
44
- if (typeof taskPath !== 'string') return null;
45
- const match = taskPath.match(/^tasks\/(task-[A-Za-z0-9][A-Za-z0-9._-]*\.md)$/);
46
- return match ? `receipts/${match[1]}` : null;
47
- }
48
-
49
- function evidenceBody(text, policy = {}) {
50
- const lines = String(text || '').split('\n');
51
- let start = -1;
52
- let level = 0;
53
- for (let index = 0; index < lines.length; index += 1) {
54
- const match = lines[index].match(/^(#{2,3})\s+(.+?)\s*$/);
55
- if (match && EVIDENCE_HEADINGS.has(match[2])) {
56
- start = index + 1;
57
- level = match[1].length;
58
- break;
59
- }
60
- }
61
- if (start < 0) return null;
62
- let end = lines.length;
63
- for (let index = start; index < lines.length; index += 1) {
64
- const heading = lines[index].match(/^(#{1,6})\s+/);
65
- const tap = typeof policy.isTapMetadataHeading === 'function' && policy.isTapMetadataHeading(lines[index]);
66
- if (heading && heading[1].length <= level && !tap) { end = index; break; }
67
- }
68
- return lines.slice(start, end).join('\n');
69
- }
70
-
71
37
  function workflowReceiptBody(text, policy = {}) {
72
38
  const lines = annotatedMarkdownLines(text);
73
39
  const starts = [];
@@ -174,20 +140,6 @@ function workflowCommandFailures(taskText, body) {
174
140
  : ['command_identity'];
175
141
  }
176
142
 
177
- function plannedField(taskText, name) {
178
- const plan = String(taskText || '').split(/^##\s+Verification Plan\s*$/m)[1];
179
- if (!plan) return null;
180
- const value = fieldValues(plan.split(/^##\s+/m)[0], `- **${name}**`)[0];
181
- return value ? value.replace(/^`|`$/g, '').trim() : null;
182
- }
183
-
184
- function identity(body) {
185
- const names = ['Verification', 'Command', 'Commands', 'Exit', 'Result', 'Base', 'Head', 'base_sha', 'head_sha'];
186
- const fields = names.flatMap((name) => fieldValues(body, name).map((value) => [name.toLowerCase(), value.trim()]));
187
- const artifacts = String(body || '').split('\n').filter((line) => /^\s*Artifacts?\s*:|^\s*Artifact\s+produced\b/i.test(line)).map((line) => line.trim());
188
- return crypto.createHash('sha256').update(JSON.stringify({ fields, artifacts })).digest('hex');
189
- }
190
-
191
143
  function tryGitBytes(root, args) {
192
144
  const result = spawnSync('git', ['-C', root, ...args], { encoding: 'buffer', maxBuffer: 64 * 1024 * 1024 });
193
145
  if (result.error || result.status !== 0) return null;
@@ -247,57 +199,6 @@ function canonicalFailures(body, task, runtimeContext, policy, { bindProvenance
247
199
  return policy.validateCanonicalReceipt(body, options);
248
200
  }
249
201
 
250
- function separateMetadataFailures(body, taskPath, taskText) {
251
- const failures = [];
252
- const basename = path.posix.basename(taskPath);
253
- if (fieldValues(body, 'Task').length !== 1 || fieldValues(body, 'Task')[0] !== basename) failures.push('task_identity');
254
- if (fieldValues(body, 'Task path').length !== 1 || fieldValues(body, 'Task path')[0] !== taskPath) failures.push('task_identity');
255
- if (!concrete(fieldValues(body, 'Expected')[0]) || !concrete(fieldValues(body, 'Observed')[0])) failures.push('expected_observed');
256
- const plannedCommand = plannedField(taskText, 'Command');
257
- const actualCommand = fieldValues(body, 'Command')[0] || fieldValues(body, 'Commands')[0];
258
- if (plannedCommand && !/^N\/A\b/i.test(plannedCommand) && actualCommand !== plannedCommand) failures.push('command_identity');
259
- for (const name of ['Negative path', 'Reachability']) {
260
- const planned = plannedField(taskText, name);
261
- if (planned && !/^(?:N\/A|none|not relevant)\b/i.test(planned) && !concrete(fieldValues(body, name)[0])) failures.push(name.toLowerCase().replace(' ', '_'));
262
- }
263
- return [...new Set(failures)];
264
- }
265
-
266
- function readTaskProof(featureDir, taskPath, policy = {}) {
267
- const receiptPath = canonicalTaskReceiptPath(taskPath);
268
- if (typeof taskPath !== 'string' || !/^tasks\/[^/]+\.md$/.test(taskPath)) return { status: 'unsafe', taskPath, receiptPath: null };
269
- const taskFile = safeRead(featureDir, taskPath);
270
- if (taskFile.status !== 'ok') return { status: taskFile.status, taskPath, receiptPath };
271
- const taskText = taskFile.bytes.toString('utf8');
272
- const separate = receiptPath ? safeRead(featureDir, receiptPath) : { status: 'missing' };
273
- const legacyBody = evidenceBody(taskText, policy);
274
- if (separate.status === 'unsafe') return { status: 'unsafe', taskPath, receiptPath, taskFile };
275
- if (separate.status === 'ok') {
276
- const body = separate.bytes.toString('utf8');
277
- return { status: 'ok', source: 'separate', body, receiptBytes: separate.bytes, receiptFile: separate, taskFile, taskText, taskPath, receiptPath, legacyBody };
278
- }
279
- if (legacyBody !== null) return { status: 'ok', source: 'legacy', body: legacyBody, receiptBytes: Buffer.from(legacyBody), taskFile, taskText, taskPath, receiptPath, legacyBody };
280
- return { status: 'missing', taskPath, receiptPath, taskFile, taskText };
281
- }
282
-
283
- function checkTaskReceipt(featureDir, taskPath, task, runtimeContext, policy) {
284
- const proof = readTaskProof(featureDir, taskPath, policy);
285
- const failures = [];
286
- if (proof.status !== 'ok') return { ...proof, failures: [proof.status === 'unsafe' ? 'unsafe_path' : 'missing_receipt'] };
287
- const status = proof.taskText.split('\n').find((line) => /^\s*(?:\*\*)?Status(?:\*\*)?\s*:/i.test(line));
288
- if (!status || !/\bdone\b/i.test(status)) failures.push('task_status');
289
- failures.push(...canonicalFailures(proof.body, task, runtimeContext, policy));
290
- if (proof.source === 'separate') {
291
- failures.push(...separateMetadataFailures(proof.body, taskPath, proof.taskText));
292
- if (proof.legacyBody !== null
293
- && /^\s*Verification:\s*PASS\s*$/m.test(proof.legacyBody)
294
- && identity(proof.legacyBody) !== identity(proof.body)) failures.push('receipt_conflict');
295
- }
296
- const completedAt = task && task.completed_at;
297
- if (typeof completedAt !== 'string' || !/^\d{4}-\d{2}-\d{2}T/.test(completedAt) || Number.isNaN(Date.parse(completedAt))) failures.push('completed_at');
298
- return { ...proof, failures: [...new Set(failures)] };
299
- }
300
-
301
202
  function checkWorkflowTaskReceipt(featureDir, taskPath, runtimeContext, policy) {
302
203
  const safeTaskPath = typeof taskPath === 'string' && /^task-[A-Za-z0-9][A-Za-z0-9._-]*\.md$/.test(taskPath);
303
204
  if (!safeTaskPath) return { status: 'unsafe', taskPath, failures: ['unsafe_path'] };
@@ -433,25 +334,10 @@ function checkWorkflowReceiptSet(candidates, projectRoot, runtimeSession, policy
433
334
  return { failures };
434
335
  }
435
336
 
436
- function checkFeatureReceipt(featureDir, runtimeContext, policy) {
437
- const receipt = safeRead(featureDir, 'feature-receipt.md');
438
- if (receipt.status !== 'ok') return { status: receipt.status, failures: [receipt.status === 'unsafe' ? 'unsafe_path' : 'missing_receipt'] };
439
- const body = receipt.bytes.toString('utf8');
440
- const failures = canonicalFailures(body, {}, runtimeContext, policy);
441
- if (fieldValues(body, 'Feature').length !== 1 || fieldValues(body, 'Feature')[0] !== path.basename(featureDir)) failures.push('feature_identity');
442
- if (!concrete(fieldValues(body, 'Expected')[0]) || !concrete(fieldValues(body, 'Observed')[0])) failures.push('expected_observed');
443
- return { status: 'ok', source: 'feature', body, receiptBytes: receipt.bytes, receiptFile: receipt, failures: [...new Set(failures)] };
444
- }
445
-
446
337
  module.exports = {
447
- canonicalTaskReceiptPath,
448
- checkFeatureReceipt,
449
- checkTaskReceipt,
450
338
  checkWorkflowReceiptSet,
451
339
  checkWorkflowTaskReceipt,
452
340
  receiptFixHint,
453
- evidenceBody,
454
- readTaskProof,
455
341
  safeRead,
456
342
  workflowDependencyProofState,
457
343
  workflowReceiptBody,