test: guard Codex default-on + config reject-on-set

skill-validation: assert plan reviews no longer carry the opt-in question
and render the default-on outside-voice, document-release carries the doc
review, and the codex host strips all of it.

gstack-config: codex_reviews defaults to enabled, accepts enabled/disabled,
and rejects an invalid value while preserving the existing one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-06-09 11:21:03 -07:00
parent 8992bac084
commit 2a899a41fa
2 changed files with 66 additions and 5 deletions
+23
View File
@@ -151,6 +151,29 @@ describe('gstack-config', () => {
expect(content).toContain('skip_eng_review:');
});
// ─── codex_reviews (paid-calls switch: reject-on-set, preserve existing) ──
test('codex_reviews defaults to enabled', () => {
const { exitCode, stdout } = run(['get', 'codex_reviews']);
expect(exitCode).toBe(0);
expect(stdout).toBe('enabled');
});
test('codex_reviews accepts enabled and disabled', () => {
expect(run(['set', 'codex_reviews', 'disabled']).exitCode).toBe(0);
expect(run(['get', 'codex_reviews']).stdout).toBe('disabled');
expect(run(['set', 'codex_reviews', 'enabled']).exitCode).toBe(0);
expect(run(['get', 'codex_reviews']).stdout).toBe('enabled');
});
test('codex_reviews rejects an invalid value and preserves the existing one', () => {
run(['set', 'codex_reviews', 'disabled']);
const { exitCode, stderr } = run(['set', 'codex_reviews', 'disabledd']);
expect(exitCode).not.toBe(0); // rejected, not warn-and-default
expect(stderr).toContain('not recognized');
// existing value must be untouched — a typo never silently flips paid Codex on/off
expect(run(['get', 'codex_reviews']).stdout).toBe('disabled');
});
test('header written only once, not duplicated on second set', () => {
run(['set', 'foo', 'bar']);
run(['set', 'baz', 'qux']);
+43 -5
View File
@@ -1386,15 +1386,16 @@ describe('Codex skill', () => {
expect(content).toContain('Adversarial review (always-on)');
// Always-on: both Claude and Codex adversarial
expect(content).toContain('Claude adversarial subagent (always runs)');
expect(content).toContain('Codex adversarial challenge (always runs when available)');
expect(content).toContain('Codex adversarial challenge (runs whenever');
// Claude adversarial subagent dispatch
expect(content).toContain('Agent tool');
expect(content).toContain('FIXABLE');
expect(content).toContain('INVESTIGATE');
// Codex availability check
expect(content).toContain('CODEX_NOT_AVAILABLE');
// OLD_CFG only gates Codex, not Claude
expect(content).toContain('skip Codex passes only');
// Probe-based availability via the shared codexPreflight() (install + auth)
expect(content).toContain('CODEX_MODE');
expect(content).toContain('command -v codex'); // install check kept literal
// codex_reviews=disabled gates Codex passes only; Claude adversarial still runs
expect(content).toContain('skip the Codex passes ONLY');
// Review log
expect(content).toContain('adversarial-review');
expect(content).toContain('reasoning_effort="high"');
@@ -1449,6 +1450,43 @@ describe('Codex skill', () => {
expect(content).toContain('codex exec');
});
// D5 regression guard: the Codex outside voice is default-on, not opt-in. A future
// gen-skill-docs change must not silently reintroduce the "Want an outside voice?"
// AskUserQuestion. The CODEX_PLAN_REVIEW content renders into each skill's
// sections/review-sections.md (the skeleton points at it). plan-design-review uses
// DESIGN_OUTSIDE_VOICES, not CODEX_PLAN_REVIEW, so it is excluded here.
test('plan reviews run the Codex outside voice default-on (no opt-in question)', () => {
for (const skill of ['plan-eng-review', 'plan-ceo-review', 'plan-devex-review']) {
const content = fs.readFileSync(
path.join(ROOT, skill, 'sections', 'review-sections.md'), 'utf-8');
expect(content).not.toContain('Want an outside voice');
expect(content).toContain('Outside Voice — Independent Plan Challenge (default-on)');
expect(content).toContain('CODEX_MODE');
expect(content).toContain('command -v codex'); // preflight install check (e2e relies on it)
}
});
test('/document-release includes the default-on Codex documentation review', () => {
// The doc-review renders into the carved release-body section (kept out of the
// always-loaded skeleton to respect the skeleton-byte budget).
const content = fs.readFileSync(
path.join(ROOT, 'document-release', 'sections', 'release-body.md'), 'utf-8');
expect(content).toContain('Codex Documentation Review (default-on)');
expect(content).toContain('CODEX_MODE');
expect(content).toContain('codex-doc-review');
});
test('codex-host document-release does NOT contain the Codex doc review', () => {
// .agents/ is gitignored — generate on demand (codex never invokes itself)
Bun.spawnSync(['bun', 'run', 'scripts/gen-skill-docs.ts', '--host', 'codex'], {
cwd: ROOT, stdout: 'pipe', stderr: 'pipe',
});
const content = fs.readFileSync(
path.join(ROOT, '.agents', 'skills', 'gstack-document-release', 'SKILL.md'), 'utf-8');
expect(content).not.toContain('Codex Documentation Review');
expect(content).not.toContain('codex-doc-review');
});
test('codex review invocations avoid the prompt plus --base argument shape', () => {
for (const rel of ['codex/SKILL.md', 'review/SKILL.md', 'ship/SKILL.md']) {
// ship's codex command moved into sections/adversarial.md (T9 carve).