@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.
- package/README.md +1 -1
- package/bin/__tests__/codex-hooks.test.js +169 -236
- package/bin/__tests__/codex-native.test.js +54 -114
- package/bin/__tests__/develop-contract.test.js +101 -1361
- package/bin/__tests__/legacy-authority-decoupled.test.js +14 -19
- package/bin/__tests__/package-inventory.test.js +1 -395
- package/bin/__tests__/spec-narrowing.test.js +30 -134
- package/bin/__tests__/specs-v2-execution-closeout.test.js +0 -372
- package/bin/__tests__/test-runtime-dependency-closure.cjs +1 -2
- package/bin/__tests__/validator-grounding.test.js +17 -2929
- package/bin/lib/managed-writer.js +2 -3
- package/bin/phases/codex-runtime.js +6 -3
- package/bin/phases/copy-payload.js +13 -28
- package/bin/phases/omp-runtime.js +6 -3
- package/package.json +2 -3
- package/src/claude/agents/code-auditor.md +9 -6
- package/src/claude/agents/debugger.md +3 -9
- package/src/claude/agents/docs-keeper.md +4 -7
- package/src/claude/agents/git-ops.md +2 -4
- package/src/claude/agents/implementer.md +6 -7
- package/src/claude/agents/inspector.md +2 -0
- package/src/claude/agents/project-manager.md +2 -2
- package/src/claude/agents/spec-maker.md +4 -4
- package/src/claude/agents/test-runner.md +8 -11
- package/src/claude/agents/ui-ux-designer.md +0 -1
- package/src/claude/hooks/__tests__/spec-gate.test.js +170 -330
- package/src/claude/hooks/__tests__/spec-state-touched.test.js +26 -0
- package/src/claude/hooks/spec-gate.cjs +8 -63
- package/src/claude/hooks/spec-state.cjs +47 -38
- package/src/claude/migration-manifest.json +12 -7
- package/src/claude/output-styles/coding-level-0-eli5.md +9 -9
- package/src/claude/output-styles/coding-level-1-junior.md +3 -3
- package/src/claude/output-styles/coding-level-2-mid.md +3 -3
- package/src/claude/output-styles/coding-level-3-senior.md +2 -2
- package/src/claude/output-styles/coding-level-4-lead.md +5 -5
- package/src/claude/output-styles/coding-level-5-god.md +6 -6
- package/src/claude/rules/ai-dev-rules.md +1 -2
- package/src/claude/rules/workflow.md +1 -1
- package/src/claude/scripts/spec-receipt.cjs +0 -114
- package/src/claude/scripts/spec-resolver.cjs +41 -457
- package/src/claude/scripts/workflow-policy.cjs +3 -1801
- package/src/claude/skills/agent-browser/SKILL.md +1 -1
- package/src/claude/skills/chrome-devtools/SKILL.md +14 -31
- package/src/claude/skills/code-review/SKILL.md +25 -108
- package/src/claude/skills/code-review/references/spec-compliance-review.md +4 -7
- package/src/claude/skills/develop/SKILL.md +22 -70
- package/src/claude/skills/develop/references/subagent-patterns.md +0 -6
- package/src/claude/skills/research/SKILL.md +24 -55
- package/src/claude/skills/sync/SKILL.md +12 -34
- package/src/claude/skills/sync/references/rebind-and-audit.md +2 -2
- package/src/claude/skills/test/SKILL.md +39 -123
- package/src/claude/skills/test/references/execution-strategy.md +11 -22
- package/src/claude/skills/ui-ux/SKILL.md +1 -1
- package/src/claude/skills/ui-ux-pro-max/SKILL.md +24 -79
- package/src/claude/skills/web-testing/SKILL.md +2 -20
- package/src/codex/hooks/lib/spec-receipt.cjs +2 -30
- package/src/codex/hooks/lib/spec-utils.cjs +6 -45
- package/src/codex/hooks/spec-gate.cjs +11 -68
- package/src/codex/hooks/spec-state.cjs +37 -41
- package/src/common/AGENTS.md +4 -10
- package/bin/__tests__/change-firewall.semantic-firewall.test.js +0 -2133
- package/bin/__tests__/finalizer-pass-gate.semantic-firewall.test.js +0 -342
- package/bin/__tests__/release-preflight.semantic-firewall.test.js +0 -586
- package/bin/__tests__/review-receipt-c2.semantic-firewall.test.js +0 -378
- package/bin/__tests__/specs-v2-policy-and-scaffold.test.js +0 -665
- package/bin/__tests__/specs-v2-validator-grounder-contract.test.js +0 -1132
- package/src/claude/hooks/lib/context.cjs +0 -527
- package/src/claude/hooks/lib/detect.cjs +0 -474
- package/src/claude/scripts/change-firewall.cjs +0 -1748
- package/src/claude/scripts/spec-authoring-digest.cjs +0 -151
- package/src/claude/scripts/spec-authoring-validation.cjs +0 -126
- package/src/claude/scripts/spec-final-state.cjs +0 -290
- package/src/claude/scripts/spec-ground.cjs +0 -650
- package/src/claude/scripts/spec-readiness.cjs +0 -425
- package/src/claude/scripts/spec-scaffold.cjs +0 -2317
- package/src/claude/scripts/spec-semantic-model.cjs +0 -396
- package/src/claude/scripts/validate-spec-output.cjs +0 -3914
- package/src/claude/skills/code-review/references/verification-gate.md +0 -41
- package/src/claude/skills/specs/templates/design.md +0 -82
- package/src/claude/skills/specs/templates/requirements-init.md +0 -8
- package/src/claude/skills/specs/templates/requirements.md +0 -42
- package/src/claude/skills/specs/templates/spec-state.json +0 -56
- package/src/claude/skills/specs/templates/task.md +0 -47
|
@@ -19,7 +19,6 @@ const LEGACY_FILES = [
|
|
|
19
19
|
'semantic-review-authority.cjs',
|
|
20
20
|
'task-scaffold-guard.cjs',
|
|
21
21
|
];
|
|
22
|
-
const MIGRATION = /Legacy spec\.json closeout is no longer supported/;
|
|
23
22
|
|
|
24
23
|
function inTempProject(run) {
|
|
25
24
|
const root = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-decoupled-')));
|
|
@@ -122,26 +121,24 @@ function checkProcessFirst(kind) {
|
|
|
122
121
|
});
|
|
123
122
|
}
|
|
124
123
|
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
// CafeKit no longer reads spec.json packets: a leftover one, closed or mid-execution, never
|
|
125
|
+
// blocks Stop on its own, and beside a process-first packet the decision is that packet's alone.
|
|
126
|
+
function checkLegacyIgnored(kind) {
|
|
127
|
+
for (const legacy of [{ status: 'done' }, { status: 'completed' }, { status: 'in_progress', doneTask: true }]) {
|
|
127
128
|
inTempProject((root) => {
|
|
128
|
-
writeLegacy(root,
|
|
129
|
+
writeLegacy(root, legacy);
|
|
129
130
|
removeLegacyFiles(root);
|
|
130
|
-
assert.
|
|
131
|
+
assert.equal(stopGate(root, kind), '', `${kind} ${legacy.status} legacy packet alone`);
|
|
132
|
+
writeProcessFirst(root, { receipt: false });
|
|
133
|
+
const reason = blockReason(stopGate(root, kind));
|
|
134
|
+
assert.match(reason, /missing_receipt/);
|
|
135
|
+
assert.doesNotMatch(reason, /task-R1-01-legacy\.md/);
|
|
136
|
+
writeProcessFirst(root, { receipt: true });
|
|
137
|
+
assert.equal(stopGate(root, kind), '', `${kind} ${legacy.status} legacy packet beside a proven process-first packet`);
|
|
131
138
|
});
|
|
132
139
|
}
|
|
133
140
|
}
|
|
134
141
|
|
|
135
|
-
function checkLegacyMidExecution(kind) {
|
|
136
|
-
inTempProject((root) => {
|
|
137
|
-
writeLegacy(root, { status: 'in_progress', doneTask: true });
|
|
138
|
-
removeLegacyFiles(root);
|
|
139
|
-
const reason = blockReason(stopGate(root, kind));
|
|
140
|
-
assert.doesNotMatch(reason, MIGRATION);
|
|
141
|
-
assert.match(reason, /task-R1-01-legacy\.md/);
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
142
|
function checkSession(kind) {
|
|
146
143
|
inTempProject((root) => {
|
|
147
144
|
removeLegacyFiles(root);
|
|
@@ -151,9 +148,7 @@ function checkSession(kind) {
|
|
|
151
148
|
|
|
152
149
|
test('claude gate passes and blocks process-first with and without the legacy files', () => checkProcessFirst('claude'));
|
|
153
150
|
test('codex gate passes and blocks process-first with and without the legacy files', () => checkProcessFirst('codex'));
|
|
154
|
-
test('claude gate
|
|
155
|
-
test('codex gate
|
|
156
|
-
test('claude gate still receipt-checks a legacy 2.1 packet mid-execution', () => checkLegacyMidExecution('claude'));
|
|
157
|
-
test('codex gate still receipt-checks a legacy 2.1 packet mid-execution', () => checkLegacyMidExecution('codex'));
|
|
151
|
+
test('claude gate ignores a leftover legacy packet', () => checkLegacyIgnored('claude'));
|
|
152
|
+
test('codex gate ignores a leftover legacy packet', () => checkLegacyIgnored('codex'));
|
|
158
153
|
test('claude session starts without the legacy files', () => checkSession('claude'));
|
|
159
154
|
test('codex session starts without the legacy files', () => checkSession('codex'));
|
|
@@ -114,13 +114,6 @@ const REQUIRED_PAYLOAD = [
|
|
|
114
114
|
'src/claude/scripts/workflow-policy.cjs',
|
|
115
115
|
'src/claude/scripts/provenance.cjs',
|
|
116
116
|
'src/claude/scripts/spec-receipt.cjs',
|
|
117
|
-
'src/claude/scripts/spec-ground.cjs',
|
|
118
|
-
'src/claude/scripts/spec-final-state.cjs',
|
|
119
|
-
'src/claude/scripts/spec-readiness.cjs',
|
|
120
|
-
'src/claude/scripts/spec-semantic-model.cjs',
|
|
121
|
-
'src/claude/scripts/validate-spec-output.cjs',
|
|
122
|
-
'src/claude/scripts/spec-authoring-validation.cjs',
|
|
123
|
-
'src/claude/scripts/spec-authoring-digest.cjs',
|
|
124
117
|
'src/claude/scripts/generate-skill-catalog.cjs',
|
|
125
118
|
'src/claude/rules/process-management.md',
|
|
126
119
|
'src/claude/rules/review-audit-self-decision.md',
|
|
@@ -1522,13 +1515,11 @@ function assertInstalledProvenance(root, platform, fixture) {
|
|
|
1522
1515
|
const policy = path.join(scripts, 'workflow-policy.cjs');
|
|
1523
1516
|
const resolver = path.join(scripts, 'spec-resolver.cjs');
|
|
1524
1517
|
const receipt = path.join(scripts, 'spec-receipt.cjs');
|
|
1525
|
-
const finalState = path.join(scripts, 'spec-final-state.cjs');
|
|
1526
|
-
const readiness = path.join(scripts, 'spec-readiness.cjs');
|
|
1527
1518
|
const gate = path.join(
|
|
1528
1519
|
root,
|
|
1529
1520
|
platform === 'claude' ? '.claude/hooks/spec-gate.cjs' : '.codex/hooks/spec-gate.cjs'
|
|
1530
1521
|
);
|
|
1531
|
-
for (const file of [helper, policy, resolver, receipt
|
|
1522
|
+
for (const file of [helper, policy, resolver, receipt]) {
|
|
1532
1523
|
assert.equal(fs.existsSync(file), true, `${platform} installed file missing: ${file}`);
|
|
1533
1524
|
}
|
|
1534
1525
|
assert.equal(fs.existsSync(gate), true, `${platform} installed gate missing: ${gate}`);
|
|
@@ -1660,26 +1651,13 @@ function assertInstalledScripts(root, platform) {
|
|
|
1660
1651
|
const scripts = path.join(root, RUNTIMES[platform].root, 'scripts');
|
|
1661
1652
|
const policy = path.join(scripts, 'workflow-policy.cjs');
|
|
1662
1653
|
const scanner = path.join(scripts, 'scan-staged-secrets.cjs');
|
|
1663
|
-
const grounder = path.join(scripts, 'spec-ground.cjs');
|
|
1664
|
-
const finalState = path.join(scripts, 'spec-final-state.cjs');
|
|
1665
|
-
const readiness = path.join(scripts, 'spec-readiness.cjs');
|
|
1666
|
-
const validator = path.join(scripts, 'validate-spec-output.cjs');
|
|
1667
1654
|
assert.ok(fs.existsSync(policy), `installed policy missing: ${policy}`);
|
|
1668
1655
|
assert.ok(fs.existsSync(scanner), `installed scanner missing: ${scanner}`);
|
|
1669
|
-
assert.ok(fs.existsSync(grounder), `installed validator dependency missing: ${grounder}`);
|
|
1670
|
-
assert.ok(fs.existsSync(finalState), `installed final-state dependency missing: ${finalState}`);
|
|
1671
|
-
assert.ok(fs.existsSync(readiness), `installed readiness finalizer missing: ${readiness}`);
|
|
1672
|
-
assert.ok(fs.existsSync(validator), `installed validator missing: ${validator}`);
|
|
1673
1656
|
|
|
1674
1657
|
const policyRun = spawnSync(process.execPath, [policy, '--json'], { cwd: root, encoding: 'utf8' });
|
|
1675
1658
|
assert.equal(policyRun.status, 0, `${policyRun.stdout}\n${policyRun.stderr}`);
|
|
1676
1659
|
assert.equal(JSON.parse(policyRun.stdout).contract, 'execution-policy');
|
|
1677
1660
|
|
|
1678
|
-
const validatorRun = spawnSync(process.execPath, [validator], { cwd: root, encoding: 'utf8' });
|
|
1679
|
-
assert.equal(validatorRun.status, 2, `${validatorRun.stdout}\n${validatorRun.stderr}`);
|
|
1680
|
-
assert.match(validatorRun.stderr, /Usage: .*validate-spec-output\.cjs/);
|
|
1681
|
-
assert.doesNotMatch(validatorRun.stderr, /MODULE_NOT_FOUND|Cannot find module/);
|
|
1682
|
-
|
|
1683
1661
|
const safe = path.join(root, 'safe.txt');
|
|
1684
1662
|
fs.writeFileSync(safe, 'mode=safe\n');
|
|
1685
1663
|
spawnSync('git', ['init', '-q'], { cwd: root });
|
|
@@ -1689,347 +1667,10 @@ function assertInstalledScripts(root, platform) {
|
|
|
1689
1667
|
assert.match(scannerRun.stdout, /No staged secrets found/);
|
|
1690
1668
|
}
|
|
1691
1669
|
|
|
1692
|
-
function installedSemanticPaths(root, platform) {
|
|
1693
|
-
const runtimeRoot = path.join(root, RUNTIMES[platform].root);
|
|
1694
|
-
const skillRoot = platform === 'codex'
|
|
1695
|
-
? path.join(root, '.agents', 'skills')
|
|
1696
|
-
: path.join(runtimeRoot, 'skills');
|
|
1697
|
-
const paths = {
|
|
1698
|
-
policy: path.join(runtimeRoot, 'scripts', 'workflow-policy.cjs'),
|
|
1699
|
-
scaffold: path.join(runtimeRoot, 'scripts', 'spec-scaffold.cjs'),
|
|
1700
|
-
validator: path.join(runtimeRoot, 'scripts', 'validate-spec-output.cjs'),
|
|
1701
|
-
authoringValidator: path.join(runtimeRoot, 'scripts', 'spec-authoring-validation.cjs'),
|
|
1702
|
-
authoringDigest: path.join(runtimeRoot, 'scripts', 'spec-authoring-digest.cjs'),
|
|
1703
|
-
grounder: path.join(runtimeRoot, 'scripts', 'spec-ground.cjs'),
|
|
1704
|
-
finalState: path.join(runtimeRoot, 'scripts', 'spec-final-state.cjs'),
|
|
1705
|
-
readiness: path.join(runtimeRoot, 'scripts', 'spec-readiness.cjs'),
|
|
1706
|
-
semanticModel: path.join(runtimeRoot, 'scripts', 'spec-semantic-model.cjs'),
|
|
1707
|
-
resolver: path.join(runtimeRoot, 'scripts', 'spec-resolver.cjs'),
|
|
1708
|
-
provenance: path.join(runtimeRoot, 'scripts', 'provenance.cjs'),
|
|
1709
|
-
stateTemplate: path.join(skillRoot, 'specs', 'templates', 'spec-state.json'),
|
|
1710
|
-
};
|
|
1711
|
-
for (const [name, target] of Object.entries(paths)) {
|
|
1712
|
-
assert.equal(fs.existsSync(target), true, `${platform} installed ${name} missing: ${target}`);
|
|
1713
|
-
assert.equal(path.relative(root, target).startsWith('..'), false, `${platform} ${name} escaped install root`);
|
|
1714
|
-
}
|
|
1715
|
-
return paths;
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
|
-
function runInstalled(script, args, root, expected = 0) {
|
|
1719
|
-
const result = spawnSync(process.execPath, [script, ...args], { cwd: root, encoding: 'utf8' });
|
|
1720
|
-
assert.equal(result.status, expected, `${script}\n${result.stdout}\n${result.stderr}`);
|
|
1721
|
-
return result;
|
|
1722
|
-
}
|
|
1723
|
-
|
|
1724
1670
|
function writeJson(target, value) {
|
|
1725
1671
|
fs.writeFileSync(target, `${JSON.stringify(value, null, 2)}\n`);
|
|
1726
1672
|
}
|
|
1727
1673
|
|
|
1728
|
-
function materializeSpecState(paths, root, feature, { strict = false } = {}) {
|
|
1729
|
-
const featureDir = path.join(root, 'specs', feature);
|
|
1730
|
-
runInstalled(paths.scaffold, [feature], root);
|
|
1731
|
-
const state = JSON.parse(fs.readFileSync(path.join(featureDir, 'spec.json'), 'utf8'));
|
|
1732
|
-
state.scope_lock.in_scope = [`${feature} behavior`];
|
|
1733
|
-
state.scope_lock.out_of_scope = ['unrelated behavior'];
|
|
1734
|
-
if (strict) {
|
|
1735
|
-
state.workflow_policy.assurance_level = 'Strict';
|
|
1736
|
-
state.workflow_policy.classified_minimum.assurance_level = 'Strict';
|
|
1737
|
-
state.workflow_policy.risks = ['auth'];
|
|
1738
|
-
}
|
|
1739
|
-
writeJson(path.join(featureDir, 'spec.json'), state);
|
|
1740
|
-
return { featureDir, specFile: path.join(featureDir, 'spec.json'), state };
|
|
1741
|
-
}
|
|
1742
|
-
|
|
1743
|
-
function requirementsProjection({ proof = false } = {}) {
|
|
1744
|
-
return `# Requirements
|
|
1745
|
-
|
|
1746
|
-
## Requirements
|
|
1747
|
-
|
|
1748
|
-
### Requirement 1: Installed behavior
|
|
1749
|
-
|
|
1750
|
-
- **R1.1**: The CommonJS entrypoint \`src/entry.js\` shall return an object containing only status \`enabled\` when its sole \`valid\` argument is the boolean \`true\`; it shall return an object containing only status \`rejected\` for \`false\`, a missing argument, or any non-boolean value.${proof ? '\n- **R1.2**: The installed verifier shall prove the observed entry behavior through its grounded evidence boundary.' : ''}
|
|
1751
|
-
`;
|
|
1752
|
-
}
|
|
1753
|
-
|
|
1754
|
-
const TYPED_ANCHOR_COLUMNS = ['ID', 'Type', 'Target', 'Role', 'Access', 'Action'];
|
|
1755
|
-
|
|
1756
|
-
function typedAnchorTable(rows) {
|
|
1757
|
-
assert.ok(Array.isArray(rows) && rows.length > 0, 'typed anchor projection requires rows');
|
|
1758
|
-
const header = `| ${TYPED_ANCHOR_COLUMNS.join(' | ')} |`;
|
|
1759
|
-
const separator = `|${TYPED_ANCHOR_COLUMNS.map(() => '---').join('|')}|`;
|
|
1760
|
-
const body = rows.map((row) => {
|
|
1761
|
-
assert.deepEqual(Object.keys(row), ['id', 'type', 'target', 'role', 'access', 'action']);
|
|
1762
|
-
assert.ok(Object.values(row).every((value) => typeof value === 'string' && value.trim() !== ''));
|
|
1763
|
-
return `| ${row.id} | ${row.type} | \`${row.target}\` | ${row.role} | ${row.access} | ${row.action} |`;
|
|
1764
|
-
});
|
|
1765
|
-
return [header, separator, ...body].join('\n');
|
|
1766
|
-
}
|
|
1767
|
-
|
|
1768
|
-
function designProjection({
|
|
1769
|
-
expected = 'exit 0; boolean true returns the exact enabled object; false, undefined, null, `"true"`, 1, and an object return the exact rejected object',
|
|
1770
|
-
taskful = false,
|
|
1771
|
-
} = {}) {
|
|
1772
|
-
const anchors = typedAnchorTable([{
|
|
1773
|
-
id: 'A-D-01', type: 'file', target: taskful ? 'src/design-boundary.js' : 'src/entry.js',
|
|
1774
|
-
role: taskful ? 'existing design boundary for the entrypoint' : 'existing runtime entrypoint and contract owner',
|
|
1775
|
-
access: 'read', action: 'read',
|
|
1776
|
-
}]);
|
|
1777
|
-
const reachabilityAnchors = taskful ? 'A-R1-01-01, A-R1-02-02' : 'A-D-01';
|
|
1778
|
-
return `# Design
|
|
1779
|
-
|
|
1780
|
-
## Boundary
|
|
1781
|
-
|
|
1782
|
-
- **Owns:** The exact return contract of the CommonJS function exported by \`src/entry.js\`.
|
|
1783
|
-
- **Reads:** One \`valid\` argument; only the boolean \`true\` is valid.
|
|
1784
|
-
- **Writes/exposes:** Exactly one status field whose value is \`enabled\` or \`rejected\`.
|
|
1785
|
-
- **Outside boundary:** Unrelated runtime behavior.
|
|
1786
|
-
|
|
1787
|
-
## Typed Anchors
|
|
1788
|
-
|
|
1789
|
-
${anchors}
|
|
1790
|
-
|
|
1791
|
-
## Decisions and Invariants
|
|
1792
|
-
|
|
1793
|
-
### D1 — Installed result decision
|
|
1794
|
-
|
|
1795
|
-
- **Decision:** \`valid === true\` returns an object containing only status \`enabled\`; every other value returns an object containing only status \`rejected\`.
|
|
1796
|
-
- **Rejects ambiguity:** Truthy non-booleans such as \`"true"\` never count as valid.
|
|
1797
|
-
- **Negative path:** \`false\`, \`undefined\`, \`null\`, strings, numbers, and objects return rejected.
|
|
1798
|
-
- **Anchors:** A-D-01
|
|
1799
|
-
|
|
1800
|
-
### I1 — Rejection invariant
|
|
1801
|
-
|
|
1802
|
-
Any input other than the boolean \`true\` never returns enabled.
|
|
1803
|
-
|
|
1804
|
-
### C1 — Result contract
|
|
1805
|
-
|
|
1806
|
-
- **Owner:** A-D-01
|
|
1807
|
-
- **Consumers:** \`test/entry.test.js\` and callers of the CommonJS export.
|
|
1808
|
-
- **Shape/behavior:** The result is an object with exactly one \`status\` field set to \`enabled\` or \`rejected\`.
|
|
1809
|
-
- **Compatibility:** The strict boolean discriminator, exact object shape, and two status values remain stable.
|
|
1810
|
-
|
|
1811
|
-
## Verification Definitions
|
|
1812
|
-
|
|
1813
|
-
- **V1**: Criteria R1.1; Owner ${taskful ? 'R1-01' : 'A-D-01'}; ${taskful ? 'Proof criteria R1.2; Proof owner R1-02; Evidence anchor A-R1-02-02; ' : ''}Decision refs D1, I1, C1; Method command \`node --test test/entry.test.js\`; Expected ${expected}; Negative/failure \`false\`, missing input, \`null\`, and truthy non-booleans all return the exact rejected object; Reachability/grounding entrypoint \`src/entry.js\` via ${reachabilityAnchors}.
|
|
1814
|
-
`;
|
|
1815
|
-
}
|
|
1816
|
-
|
|
1817
|
-
function completeSemanticReview(paths, root, fixture, counterexamples) {
|
|
1818
|
-
// C16/D13: only the installed spec-authoring-validation.cjs coordinator may
|
|
1819
|
-
// flip authoring.* to validated and write the matching receipt (I21/R3.9) —
|
|
1820
|
-
// run it over the fixture's current bytes instead of hand-writing the enum.
|
|
1821
|
-
// Lifecycle order (I15/R3.7): authoring -> coordinator -> semantic review ->
|
|
1822
|
-
// readiness, never the reverse.
|
|
1823
|
-
runInstalled(paths.scaffold, [fixture.state.feature_name, '--sync-semantic-model'], root);
|
|
1824
|
-
runInstalled(paths.authoringValidator, [fixture.featureDir], root);
|
|
1825
|
-
const state = JSON.parse(fs.readFileSync(fixture.specFile, 'utf8'));
|
|
1826
|
-
fixture.reviewResult = {
|
|
1827
|
-
verdict: 'PASS',
|
|
1828
|
-
findings: [],
|
|
1829
|
-
unresolved_decisions: [],
|
|
1830
|
-
graph_coverage: [
|
|
1831
|
-
'criterion_local', 'cross_criterion', 'runtime_path',
|
|
1832
|
-
'assumption_provenance', 'compatibility_migration',
|
|
1833
|
-
].map((surface) => ({
|
|
1834
|
-
surface, covered: true,
|
|
1835
|
-
notes: 'The review covers this semantic surface against the canonical model.',
|
|
1836
|
-
})),
|
|
1837
|
-
reviewed_criteria: counterexamples.map(({ criterion }) => criterion),
|
|
1838
|
-
counterexamples,
|
|
1839
|
-
reviewer_evidence: null,
|
|
1840
|
-
};
|
|
1841
|
-
if (state.workflow_policy.assurance_level === 'Strict') {
|
|
1842
|
-
const digest = runInstalled(paths.validator, [fixture.featureDir, '--semantic-digest'], root).stdout.trim();
|
|
1843
|
-
assert.match(digest, /^sha256:[a-f0-9]{64}$/);
|
|
1844
|
-
return digest;
|
|
1845
|
-
}
|
|
1846
|
-
const reviewFile = path.join(fixture.featureDir, '.review-result.json');
|
|
1847
|
-
writeJson(reviewFile, fixture.reviewResult);
|
|
1848
|
-
runInstalled(paths.readiness, [fixture.featureDir, '--review-result', reviewFile], root);
|
|
1849
|
-
fs.unlinkSync(reviewFile);
|
|
1850
|
-
runInstalled(paths.validator, [fixture.featureDir], root);
|
|
1851
|
-
runInstalled(paths.grounder, [fixture.featureDir, '--root', root], root);
|
|
1852
|
-
return JSON.parse(fs.readFileSync(fixture.specFile, 'utf8')).validation.semantic_review.semantic_digest;
|
|
1853
|
-
}
|
|
1854
|
-
|
|
1855
|
-
function createTasklessFixture(paths, root, feature, options = {}) {
|
|
1856
|
-
fs.mkdirSync(path.join(root, 'src'), { recursive: true });
|
|
1857
|
-
fs.mkdirSync(path.join(root, 'test'), { recursive: true });
|
|
1858
|
-
fs.writeFileSync(path.join(root, 'src', 'entry.js'), 'module.exports = (valid) => ({ status: valid === true ? "enabled" : "rejected" });\n');
|
|
1859
|
-
fs.writeFileSync(path.join(root, 'src', 'design-boundary.js'), 'module.exports = { entry: "src/entry.js" };\n');
|
|
1860
|
-
fs.writeFileSync(path.join(root, 'test', 'entry.test.js'), `const test = require('node:test');
|
|
1861
|
-
const assert = require('node:assert/strict');
|
|
1862
|
-
const entry = require('../src/entry.js');
|
|
1863
|
-
|
|
1864
|
-
test('entry accepts only the boolean true discriminator', () => {
|
|
1865
|
-
assert.deepEqual(entry(true), { status: 'enabled' });
|
|
1866
|
-
for (const invalid of [false, undefined, null, 'true', 1, {}]) {
|
|
1867
|
-
assert.deepEqual(entry(invalid), { status: 'rejected' });
|
|
1868
|
-
}
|
|
1869
|
-
});
|
|
1870
|
-
`);
|
|
1871
|
-
fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify({ scripts: { test: 'node --test' } }));
|
|
1872
|
-
const fixture = materializeSpecState(paths, root, feature, options);
|
|
1873
|
-
fs.writeFileSync(path.join(fixture.featureDir, 'requirements.md'), requirementsProjection());
|
|
1874
|
-
fs.writeFileSync(path.join(fixture.featureDir, 'design.md'), designProjection());
|
|
1875
|
-
fixture.digest = completeSemanticReview(paths, root, fixture, [{
|
|
1876
|
-
criterion: 'R1.1', case_kind: 'failure',
|
|
1877
|
-
scenario: 'The installed entry receives input without the required discriminator.',
|
|
1878
|
-
expected: 'The installed entry returns rejected and never returns enabled.',
|
|
1879
|
-
decision_refs: ['D1', 'I1', 'C1'], verification_ref: 'V1',
|
|
1880
|
-
}]);
|
|
1881
|
-
return fixture;
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
function taskProjection({ id, title, ownedPath, criterion, verificationRef, role, artifact = false }) {
|
|
1885
|
-
const artifactPath = `artifacts/${id}.json`;
|
|
1886
|
-
const anchors = typedAnchorTable([
|
|
1887
|
-
{ id: `A-${id}-01`, type: 'file', target: ownedPath, role: 'owner', access: 'write', action: 'modify' },
|
|
1888
|
-
...(artifact ? [{
|
|
1889
|
-
id: `A-${id}-02`, type: 'artifact', target: artifactPath,
|
|
1890
|
-
role: 'verifier', access: 'write', action: 'create',
|
|
1891
|
-
}] : []),
|
|
1892
|
-
{
|
|
1893
|
-
id: `A-${id}-${artifact ? '03' : '02'}`, type: 'command',
|
|
1894
|
-
target: 'node --test test/entry.test.js', role: 'verifier', access: 'read', action: 'read',
|
|
1895
|
-
},
|
|
1896
|
-
]);
|
|
1897
|
-
return `# Task ${id}: ${title}
|
|
1898
|
-
**Status:** pending
|
|
1899
|
-
|
|
1900
|
-
## Outcome
|
|
1901
|
-
|
|
1902
|
-
Deliver observable ${title.toLowerCase()} behavior through the installed entrypoint.
|
|
1903
|
-
|
|
1904
|
-
## Scope
|
|
1905
|
-
|
|
1906
|
-
- **In scope:** Exact behavior owned at ${ownedPath}.
|
|
1907
|
-
- **Out of scope:** Unrelated runtime behavior.
|
|
1908
|
-
|
|
1909
|
-
## Anchors and Ownership
|
|
1910
|
-
|
|
1911
|
-
${anchors}
|
|
1912
|
-
|
|
1913
|
-
## Changes
|
|
1914
|
-
|
|
1915
|
-
- [ ] Implement the exact owned behavior. _Requirements: ${criterion.slice(1)}_
|
|
1916
|
-
|
|
1917
|
-
## Acceptance
|
|
1918
|
-
|
|
1919
|
-
- **${criterion}:** The installed command returns the criterion-specific observable state.
|
|
1920
|
-
|
|
1921
|
-
## Dependencies
|
|
1922
|
-
|
|
1923
|
-
- none
|
|
1924
|
-
|
|
1925
|
-
## Verification Plan
|
|
1926
|
-
|
|
1927
|
-
- **Verification ref:** ${verificationRef}
|
|
1928
|
-
- **Task role:** ${role}
|
|
1929
|
-
- **Command:** \`node --test test/entry.test.js\`
|
|
1930
|
-
- **Expected:** Exit code 0 and the criterion-specific state is observed.${artifact ? ` The artifact \`${artifactPath}\` must exist, and SHA-256 over its current bytes must match the recorded digest.` : ''}
|
|
1931
|
-
- **Negative path:** Invalid input returns the named rejected state.
|
|
1932
|
-
- **Reachability:** \`src/entry.js\` is reached by the installed test command.
|
|
1933
|
-
`;
|
|
1934
|
-
}
|
|
1935
|
-
|
|
1936
|
-
function createTaskFixture(paths, root, feature) {
|
|
1937
|
-
fs.mkdirSync(path.join(root, 'artifacts'), { recursive: true });
|
|
1938
|
-
const fixture = materializeSpecState(paths, root, feature);
|
|
1939
|
-
fs.writeFileSync(path.join(fixture.featureDir, 'requirements.md'), requirementsProjection({ proof: true }));
|
|
1940
|
-
fs.writeFileSync(path.join(fixture.featureDir, 'design.md'), designProjection({
|
|
1941
|
-
expected: 'exit 0, status enabled, and verifier artifact `artifacts/R1-02.json` whose SHA-256 over current bytes matches the recorded digest',
|
|
1942
|
-
taskful: true,
|
|
1943
|
-
}));
|
|
1944
|
-
const taskOne = 'tasks/task-R1-01-implement.md';
|
|
1945
|
-
const taskTwo = 'tasks/task-R1-02-verify.md';
|
|
1946
|
-
fs.mkdirSync(path.join(fixture.featureDir, 'tasks'), { recursive: true });
|
|
1947
|
-
fs.writeFileSync(path.join(fixture.featureDir, taskOne), taskProjection({
|
|
1948
|
-
id: 'R1-01', title: 'Implement behavior', ownedPath: 'src/entry.js', criterion: 'R1.1',
|
|
1949
|
-
verificationRef: 'V1', role: 'subject implements R1.1',
|
|
1950
|
-
}));
|
|
1951
|
-
fs.writeFileSync(path.join(fixture.featureDir, taskTwo), taskProjection({
|
|
1952
|
-
id: 'R1-02', title: 'Verify behavior', ownedPath: 'test/entry.test.js', criterion: 'R1.2',
|
|
1953
|
-
verificationRef: 'V1', role: 'verifier verifies V1 through separately owned R1.2 proof', artifact: true,
|
|
1954
|
-
}));
|
|
1955
|
-
const state = fixture.state;
|
|
1956
|
-
state.authoring.tasks = 'draft';
|
|
1957
|
-
state.task_files = [taskOne, taskTwo];
|
|
1958
|
-
state.task_registry = Object.fromEntries([
|
|
1959
|
-
[taskOne, { id: 'R1-01', title: 'Implement behavior', status: 'pending', dependencies: [], blocker: null, started_at: null, completed_at: null, last_updated_at: null }],
|
|
1960
|
-
[taskTwo, { id: 'R1-02', title: 'Verify behavior', status: 'pending', dependencies: [], blocker: null, started_at: null, completed_at: null, last_updated_at: null }],
|
|
1961
|
-
]);
|
|
1962
|
-
state.coordination.boundaries = [{
|
|
1963
|
-
id: 'B-OWN', type: 'ownership', tasks: ['R1-01', 'R1-02'],
|
|
1964
|
-
write_sets: { 'R1-01': ['src/entry.js'], 'R1-02': ['test/entry.test.js', 'artifacts/R1-02.json'] },
|
|
1965
|
-
}, {
|
|
1966
|
-
id: 'B-PROOF', type: 'proof', subject: 'R1-01', verifier: 'R1-02',
|
|
1967
|
-
verification_ref: 'V1', artifact_anchor: 'A-R1-02-02',
|
|
1968
|
-
}];
|
|
1969
|
-
writeJson(fixture.specFile, state);
|
|
1970
|
-
fixture.digest = completeSemanticReview(paths, root, fixture, [{
|
|
1971
|
-
criterion: 'R1.1', case_kind: 'failure',
|
|
1972
|
-
scenario: 'Invalid input reaches the installed implementation boundary.',
|
|
1973
|
-
expected: 'The implementation returns rejected and never reports enabled.',
|
|
1974
|
-
decision_refs: ['D1', 'I1', 'C1'], verification_ref: 'V1',
|
|
1975
|
-
}, {
|
|
1976
|
-
criterion: 'R1.2', case_kind: 'failure',
|
|
1977
|
-
scenario: 'The verification command completes without producing its proof artifact.',
|
|
1978
|
-
expected: 'The verification criterion remains unsatisfied until `artifacts/R1-02.json` exists and its SHA-256 over current bytes matches the recorded digest.',
|
|
1979
|
-
decision_refs: ['D1', 'I1', 'C1'], verification_ref: 'V1',
|
|
1980
|
-
}]);
|
|
1981
|
-
return fixture;
|
|
1982
|
-
}
|
|
1983
|
-
|
|
1984
|
-
function initializeGit(root) {
|
|
1985
|
-
fs.writeFileSync(path.join(root, '.gitignore'), `${'node_' + 'modules'}/\n`);
|
|
1986
|
-
for (const args of [
|
|
1987
|
-
['init', '-q'], ['config', 'user.name', 'CafeKit Packed E2E'],
|
|
1988
|
-
['config', 'user.email', 'packed-e2e@example.invalid'],
|
|
1989
|
-
['add', '--', '.gitignore', 'src/entry.js', 'src/design-boundary.js', 'test/entry.test.js', 'package.json'],
|
|
1990
|
-
['commit', '--no-gpg-sign', '-qm', 'packed semantic fixture'],
|
|
1991
|
-
]) {
|
|
1992
|
-
const result = spawnSync('git', args, { cwd: root, encoding: 'utf8' });
|
|
1993
|
-
assert.equal(result.status, 0, `${args.join(' ')}\n${result.stdout}\n${result.stderr}`);
|
|
1994
|
-
}
|
|
1995
|
-
}
|
|
1996
|
-
|
|
1997
|
-
function assertStrictRefusedAsUnsupported(paths, root, fixture) {
|
|
1998
|
-
const specFile = path.join(fixture.featureDir, 'spec.json');
|
|
1999
|
-
const before = fs.readFileSync(specFile);
|
|
2000
|
-
const reviewFile = path.join(root, 'strict-review.json');
|
|
2001
|
-
fs.writeFileSync(reviewFile, JSON.stringify(fixture.reviewResult));
|
|
2002
|
-
const result = spawnSync(process.execPath, [paths.readiness, fixture.featureDir, '--review-result', reviewFile], { cwd: root, encoding: 'utf8' });
|
|
2003
|
-
fs.rmSync(reviewFile, { force: true });
|
|
2004
|
-
assert.notEqual(result.status, 0, `${result.stdout}\n${result.stderr}`);
|
|
2005
|
-
assert.match(`${result.stdout}\n${result.stderr}`, /Strict assurance is no longer supported/);
|
|
2006
|
-
assert.equal(fs.readFileSync(specFile).equals(before), true, 'a refused Strict readiness must leave spec.json unchanged');
|
|
2007
|
-
}
|
|
2008
|
-
|
|
2009
|
-
function assertStaleDigestMutations(paths, root, taskless, taskBearing) {
|
|
2010
|
-
const requirementFile = path.join(taskless.featureDir, 'requirements.md');
|
|
2011
|
-
const requirementBytes = fs.readFileSync(requirementFile);
|
|
2012
|
-
fs.appendFileSync(requirementFile, '\nThe installed Markdown meaning changed.\n');
|
|
2013
|
-
assert.match(runInstalled(paths.validator, [taskless.featureDir], root, 1).stderr, /semantic_digest: stale/);
|
|
2014
|
-
fs.writeFileSync(requirementFile, requirementBytes);
|
|
2015
|
-
|
|
2016
|
-
const tasklessState = JSON.parse(fs.readFileSync(taskless.specFile, 'utf8'));
|
|
2017
|
-
const originalPolicy = JSON.parse(JSON.stringify(tasklessState.workflow_policy));
|
|
2018
|
-
tasklessState.workflow_policy.planning_depth = 'Full';
|
|
2019
|
-
writeJson(taskless.specFile, tasklessState);
|
|
2020
|
-
assert.match(runInstalled(paths.validator, [taskless.featureDir], root, 1).stderr, /semantic_digest: stale/);
|
|
2021
|
-
tasklessState.workflow_policy = originalPolicy;
|
|
2022
|
-
writeJson(taskless.specFile, tasklessState);
|
|
2023
|
-
|
|
2024
|
-
const taskState = JSON.parse(fs.readFileSync(taskBearing.specFile, 'utf8'));
|
|
2025
|
-
const originalId = taskState.coordination.boundaries[0].id;
|
|
2026
|
-
taskState.coordination.boundaries[0].id = 'B-OWN-MUTATED';
|
|
2027
|
-
writeJson(taskBearing.specFile, taskState);
|
|
2028
|
-
assert.match(runInstalled(paths.validator, [taskBearing.featureDir], root, 1).stderr, /semantic_digest: stale/);
|
|
2029
|
-
taskState.coordination.boundaries[0].id = originalId;
|
|
2030
|
-
writeJson(taskBearing.specFile, taskState);
|
|
2031
|
-
}
|
|
2032
|
-
|
|
2033
1674
|
function assertTransforms(root, platform) {
|
|
2034
1675
|
const skillRoot = path.join(
|
|
2035
1676
|
root,
|
|
@@ -2745,41 +2386,6 @@ test('repository and package guides document adaptive Docs usage', () => {
|
|
|
2745
2386
|
}
|
|
2746
2387
|
});
|
|
2747
2388
|
|
|
2748
|
-
test('packed Claude and Codex installs execute semantic kernel behavior without package source', () => {
|
|
2749
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-packed-semantic-'));
|
|
2750
|
-
const destination = path.join(root, 'pack');
|
|
2751
|
-
fs.mkdirSync(destination, { recursive: true });
|
|
2752
|
-
try {
|
|
2753
|
-
const packed = npmPack(['--pack-destination', destination, '--json'], PACKAGE_ROOT);
|
|
2754
|
-
const tarball = path.join(destination, packed.filename);
|
|
2755
|
-
const runtimeClosure = packedRuntimeClosure(path.join(root, 'runtime-closure'));
|
|
2756
|
-
assertCleanInventory(packedInventory(tarball));
|
|
2757
|
-
|
|
2758
|
-
for (const platform of ['claude', 'codex']) {
|
|
2759
|
-
const project = path.join(root, platform);
|
|
2760
|
-
const installer = installPacked(tarball, project, runtimeClosure);
|
|
2761
|
-
runInstaller(installer, project, [platform], null);
|
|
2762
|
-
const packedSource = path.resolve(path.dirname(installer), '..', 'src');
|
|
2763
|
-
fs.rmSync(packedSource, { recursive: true, force: true });
|
|
2764
|
-
assert.equal(fs.existsSync(packedSource), false, `${platform} package source must be unavailable`);
|
|
2765
|
-
|
|
2766
|
-
const paths = installedSemanticPaths(project, platform);
|
|
2767
|
-
const taskless = createTasklessFixture(paths, project, 'compact-installed');
|
|
2768
|
-
initializeGit(project);
|
|
2769
|
-
|
|
2770
|
-
const strict = createTasklessFixture(paths, project, 'strict-installed', { strict: true });
|
|
2771
|
-
const taskBearing = createTaskFixture(paths, project, 'tasks-installed');
|
|
2772
|
-
assertStrictRefusedAsUnsupported(paths, project, strict);
|
|
2773
|
-
assertStaleDigestMutations(paths, project, taskless, taskBearing);
|
|
2774
|
-
runInstalled(paths.validator, [taskless.featureDir], project);
|
|
2775
|
-
runInstalled(paths.validator, [taskBearing.featureDir], project);
|
|
2776
|
-
runInstalled(paths.grounder, [taskBearing.featureDir, '--root', project], project);
|
|
2777
|
-
}
|
|
2778
|
-
} finally {
|
|
2779
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
2780
|
-
}
|
|
2781
|
-
});
|
|
2782
|
-
|
|
2783
2389
|
test('packed Claude and Codex installs self-contain runtime provenance and fail closed without it', () => {
|
|
2784
2390
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-provenance-package-'));
|
|
2785
2391
|
const destination = path.join(root, 'pack');
|