fix(#2289): context-monitor emits injection envelope only for supported events (#2324)

* fix(#2289): context-monitor emits injection envelope only for supported events

gsd-context-monitor is wired to Codex Stop/SubagentStart/SubagentStop/PreCompact
(#772), but it emitted a hookSpecificOutput.additionalContext envelope for every
event. Codex's Stop schema rejects that shape ("hook returned invalid stop hook
JSON output") exactly when context is low.

Use a positive allowlist: emit only for context-injection events (PostToolUse,
AfterTool, and the pre-existing Gemini missing-name fallback); exit 0 silently
for Stop and every other event. Debounce and critical-session bookkeeping still
run on silenced events. Behavioral regression tests drive the real hook.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(#2289): backfill PR number 2324 into changeset

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(#2285): de-flake per-plan use_worktree property test on duplicate briefs

The fast-check property in claude-orchestration.test.cjs located each plan's
agent() call via indexOf on the brief, but only plan IDs were unique — briefs
could collide (fast-check shrinks toward short strings). On a colliding seed the
lookup found the first duplicate's line and misattributed its isolation, failing
"use_worktree:true must carry isolation" intermittently (surfaced on macOS CI).
emitWorkflowScript is correct for duplicate briefs (verified); suffix the unique
id onto each agent() label so the test probe is unambiguous.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-07-16 00:56:07 -04:00
committed by GitHub
parent 52fab7d9d7
commit 5ea3401d4f
16 changed files with 309 additions and 82 deletions

View File

@@ -546,11 +546,20 @@ describe('emitWorkflowScript — per-plan use_worktree (#2772 / #2285 finding 1)
{ minLength: 1, maxLength: 4 },
).filter((plans) => new Set(plans.map((p) => p.id)).size === plans.length), // unique ids
(planSpecs) => {
// Only plan IDs are unique — briefs may legitimately collide (fast-check
// shrinks toward short/empty strings, so duplicate briefs are common).
// emitWorkflowScript emits one agent() per plan keyed by the brief label
// and is correct for duplicate briefs, but the per-plan line lookup below
// (indexOf) would find the FIRST occurrence and misattribute its isolation
// when two plans share a brief. Make each agent() label unique by suffixing
// the unique id, so the lookup is unambiguous — this disambiguates the TEST
// probe, it does not change what the code under test does.
const labelFor = (p) => p.brief + ' [' + p.id + ']';
const waves = [{
id: 'w1',
plans: planSpecs.map((p, i) => ({
id: p.id,
brief: p.brief,
brief: labelFor(p),
files_modified: ['src/file' + i + '.cts'], // disjoint -> no overlap-driven staging noise
use_worktree: p.useWorktree,
})),
@@ -558,7 +567,7 @@ describe('emitWorkflowScript — per-plan use_worktree (#2772 / #2285 finding 1)
const r = emitWorkflowScript({ phaseDir: '.p', runId: 'r', waves });
assert.strictEqual(r.ok, true);
for (const p of planSpecs) {
const briefEsc = JSON.stringify(p.brief);
const briefEsc = JSON.stringify(labelFor(p));
const idx = r.script.indexOf('agent(' + briefEsc + ',');
assert.ok(idx !== -1, 'agent() call for plan must exist');
const lineEnd = r.script.indexOf('\n', idx);