@haposoft/cafekit 0.16.0-rc.3 → 0.16.0-rc.5
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/bin/__tests__/change-firewall.semantic-firewall.test.js +2134 -0
- package/bin/__tests__/codex-hooks.test.js +531 -29
- package/bin/__tests__/codex-native.test.js +228 -7
- package/bin/__tests__/develop-contract.test.js +653 -185
- package/bin/__tests__/finalizer-pass-gate.semantic-firewall.test.js +342 -0
- package/bin/__tests__/installer-safety.test.js +39 -0
- package/bin/__tests__/opencode-spec-gate.test.js +24 -16
- package/bin/__tests__/package-inventory.test.js +705 -13
- package/bin/__tests__/release-preflight.semantic-firewall.test.js +578 -0
- package/bin/__tests__/review-receipt-c2.semantic-firewall.test.js +378 -0
- package/bin/__tests__/specs-v2-execution-closeout.test.js +468 -0
- package/bin/__tests__/specs-v2-policy-and-scaffold.test.js +665 -0
- package/bin/__tests__/specs-v2-validator-grounder-contract.test.js +1132 -0
- package/bin/__tests__/test-runtime-dependency-closure.cjs +182 -0
- package/bin/__tests__/validator-grounding.test.js +2013 -221
- package/bin/lib/managed-writer.js +12 -3
- package/bin/phases/codex-runtime.js +17 -1
- package/package.json +5 -2
- package/src/claude/CLAUDE.md +6 -0
- package/src/claude/agents/code-auditor.md +20 -0
- package/src/claude/agents/spec-maker.md +143 -99
- package/src/claude/hooks/__tests__/completion-authority.test.js +321 -58
- package/src/claude/hooks/__tests__/privacy-block.test.js +254 -0
- package/src/claude/hooks/__tests__/semantic-review-authority.test.js +257 -0
- package/src/claude/hooks/__tests__/spec-gate.test.js +115 -40
- package/src/claude/hooks/completion-authority-check.cjs +37 -288
- package/src/claude/hooks/completion-authority-state.cjs +10 -8
- package/src/claude/hooks/completion-authority.cjs +13 -7
- package/src/claude/hooks/docs-sync.cjs +5 -2
- package/src/claude/hooks/lib/privacy-command-analysis.cjs +393 -0
- package/src/claude/hooks/lib/runtime-path-safety.cjs +119 -0
- package/src/claude/hooks/privacy-block.cjs +99 -28
- package/src/claude/hooks/semantic-review-authority.cjs +152 -0
- package/src/claude/hooks/spec-gate.cjs +47 -117
- package/src/claude/hooks/spec-state.cjs +8 -2
- package/src/claude/migration-manifest.json +9 -1
- package/src/claude/rules/state-sync.md +52 -21
- package/src/claude/rules/workflow.md +17 -9
- package/src/claude/scripts/change-firewall.cjs +1748 -0
- package/src/claude/scripts/spec-authoring-digest.cjs +151 -0
- package/src/claude/scripts/spec-authoring-validation.cjs +126 -0
- package/src/claude/scripts/spec-final-state.cjs +290 -0
- package/src/claude/scripts/spec-ground.cjs +571 -173
- package/src/claude/scripts/spec-readiness.cjs +425 -0
- package/src/claude/scripts/spec-receipt.cjs +162 -0
- package/src/claude/scripts/spec-resolver.cjs +67 -16
- package/src/claude/scripts/spec-scaffold.cjs +1594 -139
- package/src/claude/scripts/spec-semantic-model.cjs +396 -0
- package/src/claude/scripts/validate-spec-output.cjs +3110 -218
- package/src/claude/scripts/workflow-policy.cjs +501 -183
- package/src/claude/settings/settings.json +4 -0
- package/src/claude/skills/code-review/SKILL.md +12 -3
- package/src/claude/skills/code-review/references/spec-compliance-review.md +10 -2
- package/src/claude/skills/code-review/references/verification-gate.md +13 -0
- package/src/claude/skills/develop/SKILL.md +47 -60
- package/src/claude/skills/develop/references/parallel-waves.md +9 -6
- package/src/claude/skills/develop/references/quality-gate.md +19 -12
- package/src/claude/skills/specs/SKILL.md +183 -177
- package/src/claude/skills/specs/references/ask-user-question-gates.md +27 -18
- package/src/claude/skills/specs/references/cross-spec-dependency.md +8 -4
- package/src/claude/skills/specs/references/research-strategy.md +4 -1
- package/src/claude/skills/specs/references/review.md +214 -262
- package/src/claude/skills/specs/references/scope-inquiry.md +18 -5
- package/src/claude/skills/specs/rules/design-discovery-full.md +33 -81
- package/src/claude/skills/specs/rules/design-discovery-light.md +21 -42
- package/src/claude/skills/specs/rules/design-principles.md +136 -180
- package/src/claude/skills/specs/rules/design-review.md +42 -97
- package/src/claude/skills/specs/rules/ears-format.md +42 -50
- package/src/claude/skills/specs/rules/phase-decision-matrix.md +42 -41
- package/src/claude/skills/specs/rules/task-scoring-rubric.md +2 -1
- package/src/claude/skills/specs/rules/tasks-generation.md +189 -300
- package/src/claude/skills/specs/templates/design.md +49 -263
- package/src/claude/skills/specs/templates/requirements.md +26 -36
- package/src/claude/skills/specs/templates/research.md +9 -98
- package/src/claude/skills/specs/templates/spec-state.json +35 -85
- package/src/claude/skills/specs/templates/task.md +30 -102
- package/src/claude/skills/sync/SKILL.md +33 -10
- package/src/claude/skills/sync/references/sync-protocols.md +31 -13
- package/src/claude/skills/test/SKILL.md +17 -7
- package/src/codex/AGENTS.md +6 -0
- package/src/codex/hooks/completion-authority-check.cjs +43 -240
- package/src/codex/hooks/completion-authority-state.cjs +10 -8
- package/src/codex/hooks/completion-authority.cjs +13 -7
- package/src/codex/hooks/lib/hook-context.cjs +16 -2
- package/src/codex/hooks/lib/privacy-command-analysis.cjs +3 -0
- package/src/codex/hooks/lib/privacy-state.cjs +61 -38
- package/src/codex/hooks/lib/runtime-path-safety.cjs +3 -0
- package/src/codex/hooks/lib/spec-receipt.cjs +72 -134
- package/src/codex/hooks/lib/spec-utils.cjs +56 -5
- package/src/codex/hooks/privacy-approval.cjs +6 -2
- package/src/codex/hooks/privacy-block.cjs +94 -15
- package/src/codex/hooks/semantic-review-authority.cjs +130 -0
- package/src/codex/hooks/spec-gate.cjs +49 -34
- package/src/codex/hooks/spec-state.cjs +10 -3
- package/src/codex/hooks/state.cjs +2 -0
- package/src/codex/hooks.json +6 -1
- package/src/codex/rules/state-sync.md +47 -27
- package/src/common/AGENTS.md +1 -1
|
@@ -60,9 +60,9 @@ function allFiles(root, predicate) {
|
|
|
60
60
|
return found;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
function
|
|
64
|
-
return Object.
|
|
65
|
-
groups.flatMap((group) => group.hooks)
|
|
63
|
+
function allHookLaunchers(config) {
|
|
64
|
+
return Object.entries(config.hooks).flatMap(([event, groups]) => (
|
|
65
|
+
groups.flatMap((group) => group.hooks.map((handler) => ({ event, handler })))
|
|
66
66
|
));
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -72,6 +72,156 @@ function parseGeneratedTomlString(content, key) {
|
|
|
72
72
|
return JSON.parse(match[1]);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
function renderCanonicalVerificationExample(template) {
|
|
76
|
+
const values = new Map([
|
|
77
|
+
['SUBJECT_REQ', '1'],
|
|
78
|
+
['X', '1'],
|
|
79
|
+
['PROOF_REQ', '1'],
|
|
80
|
+
['Y', '2'],
|
|
81
|
+
['exact command', 'node --test test/installed.test.js'],
|
|
82
|
+
['exact anchored target', 'src/installed.js#entry'],
|
|
83
|
+
['exact/repository/entrypoint', 'src/installed.js'],
|
|
84
|
+
['observable result', 'the subject behavior and verifier proof both pass'],
|
|
85
|
+
['concrete observable result and proof', 'the subject behavior and verifier proof both pass'],
|
|
86
|
+
['concrete rejected or recovery case', 'invalid input remains rejected and observable'],
|
|
87
|
+
['real entrypoint/caller and grounded anchor expectation', 'the installed entrypoint reaches A-D-01'],
|
|
88
|
+
]);
|
|
89
|
+
return template.replace(/\{\{([^}]+)\}\}/g, (placeholder, name) => (
|
|
90
|
+
values.has(name) ? values.get(name) : placeholder
|
|
91
|
+
));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function assertInstalledVerificationModel(grounderPath, designTemplate) {
|
|
95
|
+
const { parseVerificationDefinitions } = require(grounderPath);
|
|
96
|
+
assert.equal(typeof parseVerificationDefinitions, 'function');
|
|
97
|
+
const concreteDesign = renderCanonicalVerificationExample(designTemplate);
|
|
98
|
+
const errors = [];
|
|
99
|
+
const definitions = parseVerificationDefinitions(concreteDesign, errors);
|
|
100
|
+
assert.deepEqual(errors, []);
|
|
101
|
+
assert.equal(definitions.size, 1);
|
|
102
|
+
const definition = definitions.get('V1');
|
|
103
|
+
assert.ok(definition);
|
|
104
|
+
assert.deepEqual(definition.subject_criteria, ['R1.1']);
|
|
105
|
+
assert.deepEqual(definition.proof_criteria, []);
|
|
106
|
+
assert.equal(definition.proof_owner, null);
|
|
107
|
+
assert.equal(definition.evidence_anchor, null);
|
|
108
|
+
assert.deepEqual(definition.decision_refs, ['D1', 'I1', 'C1']);
|
|
109
|
+
for (const field of [
|
|
110
|
+
'subject_criteria', 'subject_owner', 'decision_refs', 'method', 'expected',
|
|
111
|
+
'negative', 'reachability'
|
|
112
|
+
]) {
|
|
113
|
+
const value = definition[field];
|
|
114
|
+
assert.ok(
|
|
115
|
+
Array.isArray(value)
|
|
116
|
+
? value.length > 0
|
|
117
|
+
: (typeof value === 'string' ? value.trim() !== '' : value && Object.keys(value).length > 0)
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
for (const mutation of [
|
|
121
|
+
concreteDesign.replace('- **V1**:', '### V1 —'),
|
|
122
|
+
concreteDesign.replace('- **V1**:', '| V1 |'),
|
|
123
|
+
concreteDesign.replace('; Expected ', '\nExpected '),
|
|
124
|
+
concreteDesign.replace('Decision refs ', 'Decisions '),
|
|
125
|
+
]) {
|
|
126
|
+
const mutationErrors = [];
|
|
127
|
+
const mutated = parseVerificationDefinitions(mutation, mutationErrors);
|
|
128
|
+
assert.ok(mutationErrors.length > 0);
|
|
129
|
+
assert.equal(mutated.has('V1'), false);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function artifactRouterRecords(skill) {
|
|
134
|
+
const lines = skill.split('\n');
|
|
135
|
+
const heading = lines.findIndex((line) => line.trim() === '## Artifact router');
|
|
136
|
+
const start = lines.findIndex((line, index) => index > heading && line.trim().startsWith('|'));
|
|
137
|
+
const rows = [];
|
|
138
|
+
for (let index = start; index >= 0 && index < lines.length && lines[index].trim().startsWith('|'); index += 1) {
|
|
139
|
+
const cells = lines[index].split('|').slice(1, -1).map((cell) => cell.trim());
|
|
140
|
+
if (!cells.every((cell) => /^:?-+:?$/.test(cell))) rows.push(cells);
|
|
141
|
+
}
|
|
142
|
+
const header = rows.shift() || [];
|
|
143
|
+
return new Map(rows.map((cells) => [
|
|
144
|
+
(cells[0] || '').replaceAll('`', ''),
|
|
145
|
+
Object.fromEntries(header.map((name, index) => [name, cells[index] || '']))
|
|
146
|
+
]));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function installedAuthoringProjectionIssues(files) {
|
|
150
|
+
const issues = [];
|
|
151
|
+
const routes = artifactRouterRecords(files.skill);
|
|
152
|
+
const none = routes.get('None');
|
|
153
|
+
if (!none || none['Durable core'] !== 'none'
|
|
154
|
+
|| none['Research route'] !== 'absent' || none['Task route'] !== 'absent') {
|
|
155
|
+
issues.push('none-route');
|
|
156
|
+
}
|
|
157
|
+
for (const depth of ['Compact', 'Full']) {
|
|
158
|
+
const route = routes.get(depth);
|
|
159
|
+
if (!route || !['spec.json', 'requirements.md', 'design.md'].every((name) => (
|
|
160
|
+
route['Durable core'].includes(`\`${name}\``)
|
|
161
|
+
))) issues.push(`${depth.toLowerCase()}-core`);
|
|
162
|
+
if (!route?.['Research route'].includes('research trigger')) issues.push(`${depth.toLowerCase()}-research`);
|
|
163
|
+
if (!route?.['Task route'].includes('typed topology trigger')) issues.push(`${depth.toLowerCase()}-tasks`);
|
|
164
|
+
}
|
|
165
|
+
if (/\balways search\b|\bcomprehensive research\b|\bcapture all findings\b/i.test(files.discovery)) {
|
|
166
|
+
issues.push('default-research');
|
|
167
|
+
}
|
|
168
|
+
if (/(?:limit|maximum|at most|top|≤|<=)\s*(?:to\s*)?3\b/i.test(files.review)) {
|
|
169
|
+
issues.push('review-cap');
|
|
170
|
+
}
|
|
171
|
+
if (!/For every `RN\.M`/.test(files.review)) issues.push('criterion-coverage');
|
|
172
|
+
if (/\bcurrent_phase\b/.test(files.codex)) issues.push('phase-alias');
|
|
173
|
+
for (const state of ['in_progress', 'paused', 'blocked', 'done']) {
|
|
174
|
+
if (!files.codex.includes(`\`${state}\``)) issues.push(`lifecycle-${state}`);
|
|
175
|
+
}
|
|
176
|
+
if (/^##\s+Evidence\s*$/m.test(files.tasks)) issues.push('task-evidence');
|
|
177
|
+
if (/\(P\)/.test(files.tasks)) issues.push('task-priority-marker');
|
|
178
|
+
for (const claudeOnly of [
|
|
179
|
+
'AskUserQuestion', 'TaskCreate', 'TaskGet', 'TaskUpdate', 'TaskList',
|
|
180
|
+
'WebSearch', 'WebFetch', 'SendMessage'
|
|
181
|
+
]) {
|
|
182
|
+
if (files.codex.includes(claudeOnly)) issues.push(`claude-tool-${claudeOnly}`);
|
|
183
|
+
}
|
|
184
|
+
if (!files.codex.includes('semantic_model')
|
|
185
|
+
|| !files.codex.includes('explicit installed machine semantic-sync step')
|
|
186
|
+
|| !files.codex.includes('round-trip')
|
|
187
|
+
|| !files.codex.includes('node .codex/scripts/spec-scaffold.cjs <feature> --sync-semantic-model')) {
|
|
188
|
+
issues.push('semantic-model-promotion');
|
|
189
|
+
}
|
|
190
|
+
return issues;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function assertInstalledAuthoringProjection(root) {
|
|
194
|
+
const read = (relative) => fs.readFileSync(path.join(root, relative), 'utf8');
|
|
195
|
+
const files = {
|
|
196
|
+
skill: read('.agents/skills/specs/SKILL.md'),
|
|
197
|
+
discovery: [
|
|
198
|
+
read('.agents/skills/specs/rules/design-discovery-light.md'),
|
|
199
|
+
read('.agents/skills/specs/rules/design-discovery-full.md')
|
|
200
|
+
].join('\n'),
|
|
201
|
+
review: [
|
|
202
|
+
read('.agents/skills/specs/rules/design-review.md'),
|
|
203
|
+
read('.agents/skills/specs/references/review.md')
|
|
204
|
+
].join('\n'),
|
|
205
|
+
tasks: [
|
|
206
|
+
read('.agents/skills/specs/rules/tasks-generation.md'),
|
|
207
|
+
read('.agents/skills/specs/rules/task-scoring-rubric.md'),
|
|
208
|
+
read('.agents/skills/specs/rules/phase-decision-matrix.md')
|
|
209
|
+
].join('\n'),
|
|
210
|
+
codex: [read('AGENTS.md'), read('.codex/rules/state-sync.md')].join('\n')
|
|
211
|
+
};
|
|
212
|
+
assert.deepEqual(installedAuthoringProjectionIssues(files), []);
|
|
213
|
+
const mutations = [
|
|
214
|
+
['discovery', (value) => `${value}\nAlways search before design.`],
|
|
215
|
+
['review', (value) => `${value}\nLimit to 3 blockers.`],
|
|
216
|
+
['codex', (value) => `${value}\nSet current_phase then call TaskUpdate.`],
|
|
217
|
+
['tasks', (value) => `${value}\n## Evidence\nUse (P).`]
|
|
218
|
+
];
|
|
219
|
+
for (const [key, mutate] of mutations) {
|
|
220
|
+
const changed = { ...files, [key]: mutate(files[key]) };
|
|
221
|
+
assert.notDeepEqual(installedAuthoringProjectionIssues(changed), []);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
75
225
|
test('Codex payload transform emits native skill and subagent syntax', () => {
|
|
76
226
|
const transformed = normalizeCodexBody(
|
|
77
227
|
'Agent(subagent_type="implementer", prompt="Implement it", description="Code Feature")\n' +
|
|
@@ -441,9 +591,10 @@ test('Codex Windows hook launchers stay project-bound without Git from nested cw
|
|
|
441
591
|
const config = JSON.parse(
|
|
442
592
|
fs.readFileSync(path.join(projectRoot, '.codex', 'hooks.json'), 'utf8')
|
|
443
593
|
);
|
|
444
|
-
const
|
|
445
|
-
assert.
|
|
446
|
-
|
|
594
|
+
const launchers = allHookLaunchers(config);
|
|
595
|
+
assert.ok(launchers.length > 0, 'installed Codex config must register hook launchers');
|
|
596
|
+
const semanticReviewEvents = [];
|
|
597
|
+
for (const { event, handler } of launchers) {
|
|
447
598
|
assert.doesNotMatch(handler.commandWindows, /\$\(/);
|
|
448
599
|
assert.doesNotMatch(handler.commandWindows, /\bgit\b/i);
|
|
449
600
|
assert.doesNotMatch(handler.commandWindows, /process\.cwd\(\)|existsSync/);
|
|
@@ -456,7 +607,11 @@ test('Codex Windows hook launchers stay project-bound without Git from nested cw
|
|
|
456
607
|
true,
|
|
457
608
|
`missing installed hook: ${path.basename(target)}`
|
|
458
609
|
);
|
|
610
|
+
if (path.basename(target) === 'semantic-review-authority.cjs') {
|
|
611
|
+
semanticReviewEvents.push(event);
|
|
612
|
+
}
|
|
459
613
|
}
|
|
614
|
+
assert.deepEqual(semanticReviewEvents, ['SubagentStop']);
|
|
460
615
|
|
|
461
616
|
const nested = path.join(projectRoot, 'nested', 'workspace');
|
|
462
617
|
fs.mkdirSync(nested, { recursive: true });
|
|
@@ -486,6 +641,7 @@ test('Codex Windows hook launchers stay project-bound without Git from nested cw
|
|
|
486
641
|
assert.match(launched.stdout, /Session startup\./);
|
|
487
642
|
assert.match(launched.stdout, /CafeKit project root:/);
|
|
488
643
|
assert.equal(fs.existsSync(shadowMarker), false);
|
|
644
|
+
assert.match(session.commandWindows, /require\('module'\)\.runMain\(\)/, 'Windows launcher must execute hook main');
|
|
489
645
|
});
|
|
490
646
|
});
|
|
491
647
|
|
|
@@ -506,13 +662,44 @@ test('Codex install is project-local, native, and upgrade-safe', () => {
|
|
|
506
662
|
'.codex/hooks/privacy-block.cjs',
|
|
507
663
|
'.codex/rules/workflow.md',
|
|
508
664
|
'.codex/rules/hook-protocols.md',
|
|
665
|
+
'.codex/rules/state-sync.md',
|
|
666
|
+
'.codex/scripts/spec-ground.cjs',
|
|
509
667
|
'.codex/scripts/validate-spec-output.cjs',
|
|
510
668
|
'.agents/.gitignore',
|
|
511
|
-
'.agents/skills/specs/SKILL.md'
|
|
669
|
+
'.agents/skills/specs/SKILL.md',
|
|
670
|
+
'.agents/skills/specs/templates/design.md',
|
|
671
|
+
'.agents/skills/specs/templates/task.md',
|
|
672
|
+
'.agents/skills/specs/templates/spec-state.json'
|
|
512
673
|
]) {
|
|
513
674
|
assert.equal(fs.existsSync(path.join(root, relative)), true, `missing ${relative}`);
|
|
514
675
|
}
|
|
515
676
|
|
|
677
|
+
const installedDesign = fs.readFileSync(
|
|
678
|
+
path.join(root, '.agents/skills/specs/templates/design.md'), 'utf8'
|
|
679
|
+
);
|
|
680
|
+
assert.equal((installedDesign.match(/^## Verification Definitions$/gm) || []).length, 1);
|
|
681
|
+
assertInstalledVerificationModel(
|
|
682
|
+
path.join(root, '.codex/scripts/spec-ground.cjs'),
|
|
683
|
+
installedDesign
|
|
684
|
+
);
|
|
685
|
+
assertInstalledAuthoringProjection(root);
|
|
686
|
+
|
|
687
|
+
const installedTask = fs.readFileSync(
|
|
688
|
+
path.join(root, '.agents/skills/specs/templates/task.md'), 'utf8'
|
|
689
|
+
);
|
|
690
|
+
assert.deepEqual(
|
|
691
|
+
[...installedTask.matchAll(/^## (.+)$/gm)].map((match) => match[1]),
|
|
692
|
+
['Outcome', 'Scope', 'Anchors and Ownership', 'Changes', 'Acceptance', 'Dependencies', 'Verification Plan']
|
|
693
|
+
);
|
|
694
|
+
assert.match(installedTask, /^- \*\*Task role:\*\*/m);
|
|
695
|
+
|
|
696
|
+
const installedState = JSON.parse(fs.readFileSync(
|
|
697
|
+
path.join(root, '.agents/skills/specs/templates/spec-state.json'), 'utf8'
|
|
698
|
+
));
|
|
699
|
+
assert.deepEqual(Object.keys(installedState.workflow_policy).sort(), [
|
|
700
|
+
'assurance_level', 'classified_minimum', 'planning_depth', 'risks', 'version'
|
|
701
|
+
]);
|
|
702
|
+
|
|
516
703
|
const agentsGitignore = fs.readFileSync(
|
|
517
704
|
path.join(root, '.agents', '.gitignore'),
|
|
518
705
|
'utf8'
|
|
@@ -663,6 +850,7 @@ test('Codex install on top of existing Claude installation preserves content and
|
|
|
663
850
|
assert.equal(fs.existsSync(path.join(root, '.codex', 'runtime.json')), true, '.codex/runtime.json should exist');
|
|
664
851
|
assert.equal(fs.existsSync(path.join(root, '.codex', 'hooks', 'privacy-block.cjs')), true, '.codex/hooks/privacy-block.cjs should exist');
|
|
665
852
|
assert.equal(fs.existsSync(path.join(root, '.codex', 'rules', 'workflow.md')), true, '.codex/rules/workflow.md should exist');
|
|
853
|
+
assert.equal(fs.existsSync(path.join(root, '.codex', 'scripts', 'spec-ground.cjs')), true, '.codex/scripts/spec-ground.cjs should exist');
|
|
666
854
|
assert.equal(fs.existsSync(path.join(root, '.codex', 'scripts', 'validate-spec-output.cjs')), true, '.codex/scripts/validate-spec-output.cjs should exist');
|
|
667
855
|
assert.equal(fs.existsSync(path.join(root, '.agents', '.gitignore')), true, '.agents/.gitignore should exist');
|
|
668
856
|
assert.equal(fs.existsSync(path.join(root, '.agents', 'skills', 'specs', 'SKILL.md')), true, '.agents/skills/specs/SKILL.md should exist');
|
|
@@ -684,3 +872,36 @@ test('Codex install on top of existing Claude installation preserves content and
|
|
|
684
872
|
assert.equal(claudeMd, claudeMdContent, 'CLAUDE.md should be unchanged');
|
|
685
873
|
});
|
|
686
874
|
});
|
|
875
|
+
|
|
876
|
+
test('Codex installed code_auditor contains Strict conditional marker', () => {
|
|
877
|
+
inTempProject((root) => {
|
|
878
|
+
const result = install(root);
|
|
879
|
+
assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
|
|
880
|
+
const toml = fs.readFileSync(path.join(root, '.codex', 'agents', 'code_auditor.toml'), 'utf8');
|
|
881
|
+
assert.match(toml, /Strict Semantic Review Attestation/);
|
|
882
|
+
assert.match(toml, /CAFEKIT_SEMANTIC_REVIEW_ATTESTATION/);
|
|
883
|
+
assert.match(toml, /MAC-protected host-hook observation/);
|
|
884
|
+
assert.doesNotMatch(toml, /host-signed/);
|
|
885
|
+
assert.match(toml, /specs\/<feature>\/spec\.json/);
|
|
886
|
+
});
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
test('Codex scaffold resolver rejects symlink template', () => {
|
|
890
|
+
inTempProject((root) => {
|
|
891
|
+
const result = install(root);
|
|
892
|
+
assert.equal(result.status, 0);
|
|
893
|
+
const template = path.join(root, '.agents', 'skills', 'specs', 'templates', 'task.md');
|
|
894
|
+
const target = path.join(root, 'outside.md');
|
|
895
|
+
fs.writeFileSync(target, 'evil');
|
|
896
|
+
const original = fs.readFileSync(template);
|
|
897
|
+
fs.unlinkSync(template);
|
|
898
|
+
fs.symlinkSync(target, template);
|
|
899
|
+
const scaffold = path.join(root, '.codex', 'scripts', 'spec-scaffold.cjs');
|
|
900
|
+
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' });
|
|
901
|
+
// Should fail because template is symlink and resolver rejects it
|
|
902
|
+
assert.notEqual(out.status, 0, 'scaffold should reject symlink template');
|
|
903
|
+
assert.match(`${out.stdout}\n${out.stderr}`, /template not found|symlink/i);
|
|
904
|
+
fs.unlinkSync(template);
|
|
905
|
+
fs.writeFileSync(template, original);
|
|
906
|
+
});
|
|
907
|
+
});
|