mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-19 19:32:18 +02:00
feat(pty-runner): scope-gate question/auto-select detectors + observation flags
Two render-shape-anchored detectors (whitespace-squished, like the Pattern-4/5 collapsed-form handling): isScopeGateQuestionVisible requires the question text PLUS option-body text (native AskUserQuestion renders numbered options, prose fallback renders lettered — the option body appears in both; narration doesn't), and isScopeGateAutoSelectVisible requires the announcement prefix PLUS the selected-B token. runPlanSkillObservation gains scopeGateQuestionObserved / scopeGateAutoSelectObserved high-water flags (attached at every return path) so paid smokes can assert gate behavior across the whole run instead of the lossy 2KB evidence tail. runPlanSkillFloorCheck no longer counts a scope-gate render toward auqObserved (tail-scoped exclusion) — the floor measures FINDING-driven questions, and the gate could fire inside the 3s pre-target window. Unit fixtures pin clean/native/collapsed positives, narration negatives, and the verbatim template announcement string (template rewording fails here first, before the paid smokes degrade to vacuous asserts). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
f349d7c894
commit
44cd3037f0
@@ -28,6 +28,8 @@ import {
|
||||
isPermissionDialogVisible,
|
||||
isNumberedOptionListVisible,
|
||||
isProseAUQVisible,
|
||||
isScopeGateQuestionVisible,
|
||||
isScopeGateAutoSelectVisible,
|
||||
isPlanReadyVisible,
|
||||
parseNumberedOptions,
|
||||
classifyVisible,
|
||||
@@ -194,6 +196,86 @@ describe('isNumberedOptionListVisible', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('scope-gate render detectors', () => {
|
||||
// The verbatim announcement string from the plan-eng/plan-design SKILL.md
|
||||
// templates. If the template rewording drifts, THIS fixture fails first —
|
||||
// before the paid plan-mode smokes silently degrade to vacuous asserts.
|
||||
const TEMPLATE_ANNOUNCEMENT =
|
||||
'Scope gate: plan mode — auto-selected B (reviewing <target>).';
|
||||
|
||||
describe('isScopeGateQuestionVisible', () => {
|
||||
test('matches the clean prose gate render (question + option bodies)', () => {
|
||||
const sample = `
|
||||
What should I review?
|
||||
A) The current branch diff — the work in progress on this branch.
|
||||
B) A plan or design doc I'll paste or point you to.
|
||||
C) A specific file, directory, or path.
|
||||
Recommendation: A when a branch diff exists, otherwise B.
|
||||
`;
|
||||
expect(isScopeGateQuestionVisible(sample)).toBe(true);
|
||||
});
|
||||
|
||||
test('matches the native numbered render (no lettered markers)', () => {
|
||||
const sample = `
|
||||
What should I review?
|
||||
|
||||
❯ 1. The current branch diff — the work in progress on this branch.
|
||||
2. A plan or design doc I'll paste or point you to.
|
||||
3. A specific file, directory, or path.
|
||||
`;
|
||||
expect(isScopeGateQuestionVisible(sample)).toBe(true);
|
||||
});
|
||||
|
||||
test('matches the PTY-collapsed render (stripAnsi squished spaces)', () => {
|
||||
const sample = 'WhatshouldIreview?A)Thecurrentbranchdiff—theworkinprogress';
|
||||
expect(isScopeGateQuestionVisible(sample)).toBe(true);
|
||||
});
|
||||
|
||||
test('stays false on narration quoting only the question', () => {
|
||||
const sample =
|
||||
"Normally I'd ask 'What should I review?' but plan mode is active, so I'm proceeding.";
|
||||
expect(isScopeGateQuestionVisible(sample)).toBe(false);
|
||||
});
|
||||
|
||||
test('stays false on unrelated review prose', () => {
|
||||
const sample = 'I will review the current branch diff and report findings.';
|
||||
expect(isScopeGateQuestionVisible(sample)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isScopeGateAutoSelectVisible', () => {
|
||||
test('matches the verbatim template announcement', () => {
|
||||
expect(isScopeGateAutoSelectVisible(TEMPLATE_ANNOUNCEMENT)).toBe(true);
|
||||
});
|
||||
|
||||
test('matches a real announcement with a concrete target', () => {
|
||||
const sample =
|
||||
'Scope gate: plan mode — auto-selected B (reviewing ~/.claude/plans/my-feature.md). Running the Design Doc Check next.';
|
||||
expect(isScopeGateAutoSelectVisible(sample)).toBe(true);
|
||||
});
|
||||
|
||||
test('matches the PTY-collapsed announcement', () => {
|
||||
const sample = 'Scopegate:planmode—auto-selectedB(reviewingPLAN.md).';
|
||||
expect(isScopeGateAutoSelectVisible(sample)).toBe(true);
|
||||
});
|
||||
|
||||
test('stays false on narration about the behavior', () => {
|
||||
const sample = "In plan mode I'd auto-select B and review the active plan.";
|
||||
expect(isScopeGateAutoSelectVisible(sample)).toBe(false);
|
||||
});
|
||||
|
||||
test('stays false on AUTO_DECIDE preamble output', () => {
|
||||
const sample = 'Auto-decided scope question → B (your preference). Change with /plan-tune.';
|
||||
expect(isScopeGateAutoSelectVisible(sample)).toBe(false);
|
||||
});
|
||||
|
||||
test('stays false on a bare "selected B" without the announcement prefix', () => {
|
||||
const sample = 'I selected B as the review target.';
|
||||
expect(isScopeGateAutoSelectVisible(sample)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isProseAUQVisible', () => {
|
||||
test('matches 4 lettered options A) B) C) D) at line starts (plan-eng prose AUQ shape)', () => {
|
||||
const sample = `
|
||||
|
||||
Reference in New Issue
Block a user