From 78e4b770fa65cccc48b90871f5ad6dee417a6299 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Thu, 30 Apr 2026 22:27:33 -0700 Subject: [PATCH] test(harness): fix detection order + whitespace-tolerant pattern matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs surfaced when validating the v1.21 fix end-to-end: 1. PlanSkillObservation outcome detection ran 'asked' (any numbered options list) BEFORE 'plan_ready'. Plan-mode's "Ready to execute?" confirmation IS a numbered options list (1=auto, 2=manual, ...), so any skill that successfully reached the native confirmation got misclassified as 'asked'. Reorder: 'auto_decided' (most specific, requires AUTO_DECIDE annotation) > 'plan_ready' (next, requires the "ready to execute" stem) > 'asked' (any remaining numbered list). 2. isPlanReadyVisible and isAutoDecidedVisible regexes only matched spaced forms ("ready to execute", "(your preference)"). stripAnsi removes cursor-positioning escapes (`\x1b[40C`) entirely instead of replacing them with spaces, so the same text can render as "readytoexecute" or "(yourpreference)". Both detectors now test the spaced form first, fall through to a whitespace-collapsed comparison. Inline unit smoke confirms both forms match. Updates to the 5 strict 'asked' regression test cases (plan-ceo, plan-eng, plan-devex, autoplan, office-hours): with the detection order corrected, the model's plan-file fallback flow legitimately lands at 'plan_ready' instead of 'asked'. Pass envelope expanded to ['asked', 'plan_ready'] (matching plan-design-review's existing pattern). Failure signals tightened to include 'auto_decided' (catches AUTO_DECIDE without opt-in) plus the standard silent_write/exited/timeout. plan-design was already on this contract from v1.21's first commit, no change needed. The expanded envelope is correct: under --disallowedTools AskUserQuestion the Tool resolution preamble routes the question through plan-mode's native "Ready to execute?" surface — the user still sees the decision, just via the plan-file flow rather than a numbered prompt. Co-Authored-By: Claude Opus 4.7 (1M context) --- test/helpers/claude-pty-runner.ts | 58 +++++++++++++------ test/skill-e2e-autoplan-auto-mode.test.ts | 15 ++++- test/skill-e2e-office-hours-auto-mode.test.ts | 11 +++- test/skill-e2e-plan-ceo-plan-mode.test.ts | 28 +++++++-- test/skill-e2e-plan-devex-plan-mode.test.ts | 15 +++-- test/skill-e2e-plan-eng-plan-mode.test.ts | 15 +++-- 6 files changed, 103 insertions(+), 39 deletions(-) diff --git a/test/helpers/claude-pty-runner.ts b/test/helpers/claude-pty-runner.ts index 3b56fb07..ac9c7f07 100644 --- a/test/helpers/claude-pty-runner.ts +++ b/test/helpers/claude-pty-runner.ts @@ -133,22 +133,34 @@ export function isTrustDialogVisible(visible: string): boolean { return visible.includes('trust this folder'); } -/** Detect plan-mode's native "ready to execute" confirmation. */ +/** + * Detect plan-mode's native "ready to execute" confirmation. Tests both the + * spaced and whitespace-collapsed forms because stripAnsi removes cursor- + * positioning escapes (e.g. `\x1b[40C`) that render visually as spaces but + * leave no character behind — so "ready to execute" can come through as + * "readytoexecute" depending on the rendering path. + */ export function isPlanReadyVisible(visible: string): boolean { - return /ready to execute|Would you like to proceed/i.test(visible); + if (/ready to execute|Would you like to proceed/i.test(visible)) return true; + const collapsed = visible.replace(/\s+/g, ''); + return /readytoexecute|Wouldyouliketoproceed/i.test(collapsed); } /** * Detect the AUTO_DECIDE preamble template firing. The model prints * "Auto-decided