From e641251e8744c2706c028634cbbc30ecebaca7b1 Mon Sep 17 00:00:00 2001 From: garrytan Date: Sat, 3 Oct 2026 21:42:44 +0000 Subject: [PATCH] fix(outside-voice): CODEX_BOUNDARY at normal volume (W1.1) The boundary every outside-voice prompt hands Codex keeps its constraint, all four protected roots, its reason, the no-skill-invocation clause and the agents/openai.yaml no-modify clause, without the IMPORTANT/NOT capitals or the redundant "Ignore them completely". Evidence (safety-codex-boundary, codex-cli 0.160.0, gpt-6-astra, effort high, inotify observer with a negative control in every trial): - before (old wording, repaired oracle): 10/10 clean - rule removed: 5/5 violations (two canary files opened in each) - after (this wording): 10/10 clean, $5.99 Codex is the only reader of this text on every host, so this eval covers every render that carries it. E3 registry hash updated. The tests that pinned the old capitals now check the boundary case-insensitively on meaning (four roots, no skill invocation): codex-skill-isolation, skill-validation, template-context-parity, and the boundary-line filter in gen-skill-docs. --- scripts/resolvers/outside-voice-steps.ts | 2 +- test/codex-skill-isolation.test.ts | 4 ++-- test/gen-skill-docs.test.ts | 2 +- test/helpers/safety-rules.ts | 2 +- test/skill-validation.test.ts | 5 ++--- test/template-context-parity.test.ts | 2 +- 6 files changed, 8 insertions(+), 9 deletions(-) diff --git a/scripts/resolvers/outside-voice-steps.ts b/scripts/resolvers/outside-voice-steps.ts index 05f216737..74a5a5ecc 100644 --- a/scripts/resolvers/outside-voice-steps.ts +++ b/scripts/resolvers/outside-voice-steps.ts @@ -16,7 +16,7 @@ import { toShellPath, type TemplateContext } from './types'; import { CC_BACKGROUND_DEFAULT_SINCE } from './constants'; import { outsideVoiceFailurePolicy, outsideVoiceFor, outsideVoiceInvocation, outsideVoicePreflight, outsideVoiceProvenance, outsideVoiceRuntime } from './outside-voice'; -const CODEX_BOUNDARY = 'IMPORTANT: Do NOT read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/. These are skill definitions, not repository review data. Do not invoke any installed skill (Codex home skills/, .agents/), hook, or tool instruction; answer directly. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.\\n\\n'; +const CODEX_BOUNDARY = 'Filesystem boundary: do not read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/. They hold skill definitions, not repository code to review. Do not invoke any installed skill (Codex home skills/, .agents/), hook, or tool instruction; answer directly. Do not modify agents/openai.yaml. Review only the repository code.\\n\\n'; export function generateCodexSecondOpinion(ctx: TemplateContext): string { diff --git a/test/codex-skill-isolation.test.ts b/test/codex-skill-isolation.test.ts index ec407fcc8..d2fa0e5db 100644 --- a/test/codex-skill-isolation.test.ts +++ b/test/codex-skill-isolation.test.ts @@ -59,10 +59,10 @@ describe('Codex skill isolation (#2847)', () => { const sources = ['scripts/resolvers/outside-voice-steps.ts', ...['review-mode', 'challenge-mode', 'consult-mode'].map(m => `codex/sections/${m}.md.tmpl`)]; for (const rel of sources) { const text = fs.readFileSync(path.join(ROOT, rel), 'utf8'); - const boundaries = text.split('\n').filter(line => line.includes('IMPORTANT: Do NOT read or execute any files under')); + const boundaries = text.split('\n').filter(line => /do not read or execute any files under/i.test(line)); expect(boundaries.length, rel).toBeGreaterThan(0); for (const line of boundaries) { - expect(line, rel).toContain('Do not invoke any installed skill (Codex home skills/, .agents/)'); + expect(line, rel).toMatch(/do not invoke any installed skill \(Codex home skills\/, \.agents\/\)/i); } } }); diff --git a/test/gen-skill-docs.test.ts b/test/gen-skill-docs.test.ts index 8a626db9e..06119d6d9 100644 --- a/test/gen-skill-docs.test.ts +++ b/test/gen-skill-docs.test.ts @@ -2811,7 +2811,7 @@ describe('Codex generation (--host codex)', () => { const content = fs.readFileSync(path.join(AGENTS_DIR, skill.codexName, 'SKILL.md'), 'utf-8'); // Outside prompts may explicitly forbid reading Claude's skill directory. // Every executable/runtime path must still use the selected host root. - const withoutBoundary = content.replace(/^.*IMPORTANT: do not read or execute[^\n]*$/gim, ''); + const withoutBoundary = content.replace(/^.*do not read or execute any files under[^\n]*$/gim, ''); expect(withoutBoundary).not.toContain('~/.claude/'); } }); diff --git a/test/helpers/safety-rules.ts b/test/helpers/safety-rules.ts index 8e9077ce6..08a3e1b44 100644 --- a/test/helpers/safety-rules.ts +++ b/test/helpers/safety-rules.ts @@ -31,7 +31,7 @@ export const SAFETY_RULES: readonly SafetyRule[] = [ evalId: 'safety-codex-boundary', source: 'scripts/resolvers/outside-voice-steps.ts', match: /^const CODEX_BOUNDARY = /, - sha256: 'bb7fc2f71194fb250645a616f2163ec195cf05d2115e36923ecefe98319fc2f7', + sha256: '5212f94292bfa1b3a94d9713e6f42087c4dc48711bd3f73b6461f828aeef01c0', }, { id: 'ship-fresh-evidence', diff --git a/test/skill-validation.test.ts b/test/skill-validation.test.ts index e881e715f..e79a97f92 100644 --- a/test/skill-validation.test.ts +++ b/test/skill-validation.test.ts @@ -1731,14 +1731,13 @@ describe('Codex skill', () => { // Do NOT "restore" the boundary by putting a prompt argument back on a // scoped `codex review` call: that combination fails to parse, and // dropping the scope flag to make it parse silently reviews the wrong diff. - const boundaryLine = - 'Do NOT read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/'; + const boundaryLine = /do not read or execute any files under ~\/\.claude\/, ~\/\.agents\/, \.claude\/skills\/, or agents\//i; for (const rel of ['codex/SKILL.md', 'review/SKILL.md', 'ship/SKILL.md']) { // ship's AND review's codex/adversarial boundary lines moved into sections/adversarial.md. const content = rel === 'ship/SKILL.md' ? readShipUnion() : rel === 'review/SKILL.md' ? readSkillUnion('review') : fs.readFileSync(path.join(ROOT, rel), 'utf-8'); - expect(content).toContain(boundaryLine); + expect(content).toMatch(boundaryLine); } }); diff --git a/test/template-context-parity.test.ts b/test/template-context-parity.test.ts index 1540ed744..8ab29650d 100644 --- a/test/template-context-parity.test.ts +++ b/test/template-context-parity.test.ts @@ -35,7 +35,7 @@ describe('section TemplateContext parity (skillName pinned to parent)', () => { test('adversarial section rendered the ADVERSARIAL_STEP resolver (proves ship ctx)', () => { const content = readSection('adversarial.md'); // The codex filesystem-boundary line only appears when ADVERSARIAL_STEP resolves. - expect(content).toContain('Do NOT read or execute any files under'); + expect(content).toMatch(/do not read or execute any files under/i); expect(content.length).toBeGreaterThan(500); });