mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-12 16:08:59 +02:00
fix(auq): spawned trigger is objective — explicit declaration or STATUS echo, never inference (periodic-lane AUQ collapse)
The v1.76 spawned rule's parenthetical '(or your dispatch prompt marks this
session as spawned)' let the model INFER spawned status from a scripted-looking
prompt in a CI-looking session and silently auto-choose every review-phase
question: reviewCount=0 across the plan-review periodic E2Es (weekly run
33363624506, 9 of 14 failed shards; reproduced locally, zero AUQ fingerprints).
Env and hook paths were excluded by inspection: hermetic children echo
SESSION_KIND: interactive (CLAUDE_CODE_ENTRYPOINT=cli beats CI markers) and the
question-preference hook isn't installed there.
The trigger is now objective: the echoed SESSION_KIND: spawned STATUS line, or
an EXPLICIT dispatch-prompt declaration ("you are a SPAWNED subagent") —
declared, never inferred — with an absence-safe interactive fence: CI env vars,
scripted-looking or pasted prompts, and write-to-this-exact-file instructions
are NOT spawned markers. The prose channel stays because Task-tool subagents
inherit the parent env (no spawned prefix) — their dispatch prompt is the only
signal; #2733's env-prefix channel is untouched.
19 carve skeleton ceilings re-pinned with measured values (+~440 bytes/skill);
ship goldens refreshed for all three hosts; resolver pins extended with the
no-inference regression tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e76f65a8da
commit
95d3aa17e9
@@ -249,16 +249,35 @@ describe('generateAskUserFormat — runtime-failure prose fallback', () => {
|
||||
});
|
||||
|
||||
test('Spawned: self-check carries the never-reach-this-checklist clause', () => {
|
||||
expect(out).toMatch(/in `SESSION_KIND: spawned` you should never reach this checklist/);
|
||||
expect(out).toMatch(/in `SESSION_KIND: spawned`[\s\S]{0,120}you should never reach this checklist/);
|
||||
expect(out).toMatch(/never inferred/);
|
||||
});
|
||||
|
||||
test('Spawned: rule scopes markings to the creating dispatch prompt (anti-injection)', () => {
|
||||
// "(or your dispatch prompt marks this session as spawned)" is a
|
||||
// text-claimable trigger — the rule must explicitly refuse spawned
|
||||
// claims sourced from files/tool output/web content read mid-run.
|
||||
// The spawned trigger is text-claimable — the rule must explicitly refuse
|
||||
// spawned claims sourced from files/tool output/web content read mid-run.
|
||||
expect(out).toMatch(/NEVER count[\s\S]*prompt injection/);
|
||||
});
|
||||
|
||||
// Periodic-lane regression (v1.76 → v1.78): the old parenthetical
|
||||
// "(or your dispatch prompt marks this session as spawned)" let the model
|
||||
// INFER spawned status from a scripted-looking prompt in a CI-looking
|
||||
// session and silently auto-decide every review question (reviewCount=0
|
||||
// across the plan-review E2Es). The trigger must be objective: the echoed
|
||||
// STATUS line or an EXPLICIT dispatch-prompt declaration — never inference —
|
||||
// and the rule must carry an absence-safe interactive fence.
|
||||
test('Spawned: trigger is explicit-declaration-only, never inference (AUQ-collapse regression)', () => {
|
||||
expect(out).not.toContain('marks this session as spawned');
|
||||
expect(out).toMatch(/EXPLICITLY declares this session a spawned subagent/);
|
||||
expect(out).toMatch(/declared, never inferred/);
|
||||
});
|
||||
|
||||
test('Spawned: absence-safe interactive fence present (CI env / scripted prompts are not markers)', () => {
|
||||
expect(out).toMatch(/With neither trigger present, the session is interactive/);
|
||||
expect(out).toMatch(/CI env vars, scripted-looking or pasted prompts[\s\S]{0,120}NOT spawned markers/);
|
||||
expect(out).toMatch(/an unanswered question is recoverable/);
|
||||
});
|
||||
|
||||
// Conductor-default-prose contract (the proactive path, distinct from the
|
||||
// failure fallback). Guards the Tool-resolution rule + self-check wording.
|
||||
test('Conductor: do-not-call rule present in Tool resolution', () => {
|
||||
|
||||
Reference in New Issue
Block a user