From 76f15815d3f9fabbdc2aff27d4e1bde3f2e41f23 Mon Sep 17 00:00:00 2001 From: garrytan Date: Sat, 3 Oct 2026 22:36:46 +0000 Subject: [PATCH] I1: DX floor actor strips the editor hint from the footer line only (from #2971) The custom-field reply normalization removed 'ctrl+g to edit in ' from every line, so question text that resembled the menu footer could be rewritten and still authenticate. Only the last non-empty line is the footer. Ported from Garry's #2971 with its negative control. --- test/helpers/pty/runners/floor.ts | 7 ++++--- test/plan-floor-dx-actor.test.ts | 11 +++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/test/helpers/pty/runners/floor.ts b/test/helpers/pty/runners/floor.ts index 70c1f5549..a8d6d1802 100644 --- a/test/helpers/pty/runners/floor.ts +++ b/test/helpers/pty/runners/floor.ts @@ -120,9 +120,10 @@ export function planFloorDXReplyInput(visible: string, call: NativePlanQuestionC const first = lines.findIndex(line => /^ 1\. /.test(line)); if (first < 0) return null; lines[first] = lines[first]!.replace(/^ 1\./, '❯ 1.'); - const pane = planFloorDXPane(lines.map(line => line.replace( - /^(Enter to select · ↑\/↓ to navigate · (?:n to add notes · )?)ctrl\+g to edit in [^\x00-\x1f\x7f·]+ · (Esc to cancel)$/, - '$1$2')).join('\n'), call); + // Only the final footer line is presentation; question text that resembles it stays verbatim. + const footer = lines.findLastIndex(line => line.trim().length > 0); + lines[footer] = lines[footer]!.replace(/^[ \t]*(Enter to select · ↑\/↓ to navigate(?: · n to add notes)?) · ctrl\+g to edit in [^\x00-\x1f\x7f·]+( · Esc to cancel)[ \t]*$/, '$1$2'); + const pane = planFloorDXPane(lines.join('\n'), call); if (!pane || compact(pane) !== compact(state.pane)) return null; return state.stage === 'paste' ? { input: '\x1b[200~' + state.reply + '\x1b[201~', stage: 'submit' } diff --git a/test/plan-floor-dx-actor.test.ts b/test/plan-floor-dx-actor.test.ts index d532e1f60..ab6ba799e 100644 --- a/test/plan-floor-dx-actor.test.ts +++ b/test/plan-floor-dx-actor.test.ts @@ -146,3 +146,14 @@ test.each(['Vim','Nano','Visual Studio Code'])('custom input with %s hint still expect(planFloorDXReplyInput(hint(captured.filledViewport).replace('Confirmed review context:','Unapproved context:'),call,state('submit'))).toBeNull(); expect(planFloorDXReplyInput(hint(captured.filledViewport),{...call,answered:true},state('submit'))).toBeNull(); }); + +test('editor-hint normalization cannot rewrite question content that resembles a footer',()=>{ + const footer='Enter to select · ↑/↓ to navigate · Esc to cancel'; + const short=structuredClone(call);short.questions[0]!.question='Review the documented controls:\n'+footer; + const pane='☐ Empathy\n'+short.questions[0]!.question+'\n'+captured.questionViewport.slice(captured.questionViewport.indexOf('❯ 1.')); + const reply:PlanFloorDXReply={call:short,pane,reply:captured.reply,stage:'focus'}; + expect(planFloorDXReplyInput(pane,short,reply)).toEqual({input:'4',stage:'paste'}); + const focused=pane.replace('❯ 1.',' 1.').replace(' 4. Type something.','❯ 4. Type something.'); + const changed=focused.replaceAll(footer,footer.replace(' · Esc to cancel',' · ctrl+g to edit in Vim · Esc to cancel')); + expect(planFloorDXReplyInput(changed,short,{...reply,stage:'paste'})).toBeNull(); +});