# 함수 설계서: `ConfirmGate.show` (#260) > **부모 설계서**: ./README.md · **상태**: Draft > **작성**: [AI] Architect · **구현**: `lib/ai/tools/confirm_gate.dart:show` · **테스트**: `test/ui/confirm_gate_test.dart` ## 1. 시그니처 ```dart class ConfirmGate { static Future show( BuildContext context, ToolDefinition tool, Map args, ); } ``` ## 2. 책임 (단일 책임, 1줄) destructive tool 실행 직전 모달 AlertDialog 을 띄워 사용자 confirm 여부를 `Future` 로 반환한다. ## 3. 입력 | 파라미터 | 타입 | 제약 | 설명 | |---|---|---|---| | `context` | BuildContext | `context.mounted == true` | chat screen 의 context | | `tool` | ToolDefinition | isDestructive=true | 어떤 도구인가 | | `args` | Map | 이미 validate 통과 | 사용자에게 보여줄 인자 | ## 4. 출력 - **반환**: `Future` - `true` = 사용자가 "수행" 탭 - `false` = 사용자가 "취소" 또는 outside-tap dismiss 또는 `context.mounted == false` - **부수효과**: 모달 표시 (UI). DB 변경 없음. ## 5. 동작 / 알고리즘 ``` 1. if !context.mounted: return false 2. summary = _summarize(tool.name, args) // tool 별 사람 친화 요약 함수 (per-tool overridable) 3. result = await showDialog( context: context, barrierDismissible: true, // outside-tap = 취소 builder: (ctx) => AlertDialog( title: Text('이 작업을 수행할까요?'), content: Column(mainAxisSize: min, crossAxisAlignment: start, children: [ Text(tool.description, style: bodyMedium), SizedBox(height: 12), Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Theme.surfaceVariant, borderRadius: BorderRadius.circular(8), ), child: Text(summary), ), ]), actions: [ TextButton(onPressed: () => Navigator.pop(ctx, false), child: Text('취소')), FilledButton( autofocus: true, onPressed: () => Navigator.pop(ctx, true), child: Text('수행'), ), ], ), ) 4. return result ?? false // dismiss 시 null → false ``` ### `_summarize` 규칙 (tool 별) - `add_habit` → "프로토콜 '$title'을 ${frame_level} 프레임으로 새 습관으로 추가합니다.\n • 문장: \"$framed_text\"\n • 앵커: ${anchor_when ?? '-'} / ${anchor_after_what ?? '-'}" - `log_tracker_entry` → "$habit_title 의 ${date ?? '오늘'} 기록을 '${value == 'done' ? '완료' : '공란'}' 으로 저장합니다." - 기타 → JSON pretty (fallback) `title` 은 핸들러가 호출 직전 lookup 해서 args 에 채워줄 수 있지만, 단순화를 위해 ConfirmGate 가 직접 catalog/habit 조회는 안 함 — args 에 이미 있는 값만 사용. (안 채워졌으면 protocol_id 그대로 노출 — 트레이드오프 수용.) ## 6. 에러 & 실패 모드 | 조건 | 처리 | 반환 | |---|---|---| | context dispose 후 호출 | guard 즉시 | `false` | | showDialog 자체 예외 (이론상 없음) | rethrow X — catch 후 false 반환 | `false` | | args 가 _summarize 가 기대 안 한 형태 | toString fallback | 정상 동작 (dialog 노출) | ## 7. 엣지케이스 - **chat 모달 위에 chat 모달**: 동시에 호출되지 않도록 ChatSessionController 가 직렬화 (한 turn = 한 tool call 만). 다중 destructive tool 병렬 호출 시 첫 confirm 만 처리, 나머지는 `ToolCancelled` 자동 반환 (OQ-1 영향). - **시스템 back press**: Android 뒤로가기 → dialog dismiss → false. 의도된 cancel. - **autofocus + 키보드 enter**: 수행 버튼 기본 포커스. 의도치 않은 enter 누름 위험 — 사용자 결정으로 수용 (단축키 활용성 ↑). ## 8. 복잡도 / 성능 - O(1). 사용자 대기 시간 = 무한 (사용자 입력 대기). - 호출 빈도: 사용자 대화 turn 당 0 또는 1. ## 9. 테스트 케이스 (필수) | 케이스 | 셋업 | 입력 | 기대 | |---|---|---|---| | confirm | MaterialApp + 모달 진입 → "수행" tap | add_habit args | true | | cancel | "취소" tap | 동일 | false | | outside dismiss | barrier tap | 동일 | false | | unmounted context | context dispose 후 호출 | — | false 즉시 | ## 10. 의존 - Flutter `showDialog`, `AlertDialog` - `ToolDefinition` (description 출처) - per-tool summary 규칙은 별도 함수로 분리 (`_summarize(toolName, args)`) — 단위 테스트 용이.