diff --git a/src/tui/tui-model.ts b/src/tui/tui-model.ts index 4d6957be..30f15fbf 100644 --- a/src/tui/tui-model.ts +++ b/src/tui/tui-model.ts @@ -349,10 +349,6 @@ export class TuiModelStore implements TuiRenderModel { }); } - approvalFor(sessionId: string): ApprovalItem | undefined { - return this.approvalsBySession.get(sessionId); - } - sessions(): TuiSessionRow[] { return [...this.sessionsById.values()]; } @@ -440,7 +436,12 @@ export class TuiModelStore implements TuiRenderModel { this.touch(); } - /** Arm the typed-name confirmation for `x` (kill). */ + /** + * Arm the typed-name confirmation for `x` (kill). Whether what the user typed + * AUTHORIZES the kill is `confirmAccepts()` in tui-app, which owns that rule + * for every caller: a second copy here answered the same question differently + * (it refused the id prefix a mux name carries) and nothing consulted it. + */ beginConfirmKill(row: TuiRow): void { this.confirm = { sessionId: row.session.sessionId, @@ -457,11 +458,6 @@ export class TuiModelStore implements TuiRenderModel { this.touch(); } - /** Does the typed text authorize the kill? Exact match on the name shown. */ - confirmSatisfied(): boolean { - return this.confirm !== null && this.confirm.typed.trim() === this.confirm.name; - } - /** Drop whatever overlay owns the keyboard and go back to the list. */ closeOverlay(): void { this.confirm = null; diff --git a/test/tui/tui-model.test.ts b/test/tui/tui-model.test.ts index 4076d5b4..3a490831 100644 --- a/test/tui/tui-model.test.ts +++ b/test/tui/tui-model.test.ts @@ -262,28 +262,32 @@ describe('the store', () => { expect(flattenRows(model.groups())).toHaveLength(3); }); - it('tracks the confirm-kill overlay and only accepts the exact name', () => { + // Whether the typed text AUTHORIZES the kill is `confirmAccepts()` in + // tui-app, tested there; the store only carries what was typed. + it('tracks the confirm-kill overlay, keyed to the name it showed', () => { const model = createTuiModel(); model.replaceSessions([session({ sessionId: 'a', name: 'w4-api' })]); model.beginConfirmKill(model.rows()[0]); expect(model.mode).toBe('confirm-kill'); - expect(model.confirmSatisfied()).toBe(false); + expect(model.confirm).toEqual({ sessionId: 'a', name: 'w4-api', typed: '' }); model.setConfirmInput('w4-ap'); - expect(model.confirmSatisfied()).toBe(false); - model.setConfirmInput('w4-api'); - expect(model.confirmSatisfied()).toBe(true); + expect(model.confirm?.typed).toBe('w4-ap'); model.closeOverlay(); expect(model.mode).toBe('list'); expect(model.confirm).toBeNull(); }); - it('drops a session approval along with the session', () => { + it('drops a session approval along with the session, and never resurrects it', () => { const model = createTuiModel(); model.replaceSessions([session({ sessionId: 'a' })]); model.setApprovals([approval({ sessionId: 'a' })]); - expect(model.approvalFor('a')).toBeDefined(); + expect(model.rows()[0].approval).toBeDefined(); model.removeSession('a'); - expect(model.approvalFor('a')).toBeUndefined(); + expect(model.rows()).toHaveLength(0); + // The same id coming back must not inherit the dead session's dialog. + model.upsertSession(session({ sessionId: 'a' })); + expect(model.rows()[0].approval).toBeUndefined(); + expect(model.rows()[0].state).toBe('idle'); }); });