/volume1/web/gpt/repair_jobs/MODULE_OWNERSHIP.md
2026-05-01 22:24:10
# 영일전기철물 AI 모듈 소속표 1차
last_updated: 2026-05-01 21:06:45
root: `/volume1/web/gpt`
---
## 0. 한줄판단
현재 구조는 “찾아갈 수 있는 지도”는 생겼지만, 아직 “역할이 완전히 분리된 설계도”는 아니다. 1차 기준으로 위험도가 높은 `api.php`, `Agent.php`, `ToolSelector.php`, `CustomerTool.php`, `web_preflight.php`부터 책임과 분리 후보를 고정한다.
---
## 1. 책임 분리 원칙
- 공통 입력 상태는 `api.php` 또는 공통 service에서 처리한다.
- Agent는 전체 실행 루프만 담당해야 한다.
- ToolSelector는 최종 도구 선택 껍데기 역할만 하고, 실제 규칙은 `routing/*.php`로 빼야 한다.
- Tool 파일은 실행 진입점만 남기고, 검색/삭제/메시지 포맷은 Service로 분리하는 게 좋다.
- 보고서/상태/코드지도는 작업 기억용이므로 기능 로직과 섞지 않는다.
---
## 2. 핵심 모듈별 소속표
### `api.php`
- 이름: API 진입 / web_capability 앞단
- 존재: ✅
- 위험도: 높음
- 라인 수: 961
- 크기: 34008 bytes
- 클래스 수: 0
- 함수/메서드 수: 10
**현재 책임**
- 웹 입력을 받아 trace_id/context를 만들고 엔진으로 넘기는 1차 진입점
- web_capability preflight를 Agent보다 먼저 실행할 수 있음
- 건영아 세션처럼 모든 입력에 적용되어야 하는 공통 처리는 여기 또는 더 앞단에 둬야 함
**남겨도 되는 책임**
- HTTP 요청 수신
- 공통 context 구성
- 공통 세션/trace touch
- capability preflight 호출
- engine fallback 위임
**분리 후보**
- 관리자 세션 처리 → AdminSessionService.php
- capability preflight 호출부 → ApiCapabilityBridge.php
- query rewrite 계열 → ApiQueryRewriteService.php
**터졌을 때 먼저 볼 것**
- web_capability가 Agent를 우회하는지 확인
- trace에 api.admin_session / web_capability 순서 확인
- api.php 문법 오류 시 전체 엔진이 바로 죽음
**주요 함수/메서드**
- L9: `gpt_ui_history_file_v2()`
- L23: `gpt_ui_history_decode_json_v2()`
- L40: `gpt_ui_history_append_v2()`
- L89: `gpt_ui_history_load_v2()`
- L142: `gpt_ui_history_json_v2()`
- L164: `api_code_keyword_location_bypass_capability_v1()`
- L207: `api_q_person_search_rewrite_v3()`
- L235: `api_person_search_bypass_capability_v1()`
- L378: `gpt_ui_history_search_v1()`
- L552: `api_router_kernel_inject_trace_v1()`
---
### `engine_modules/agent/Agent.php`
- 이름: Agent 실행 루프 / 승인 / 호출어
- 존재: ✅
- 위험도: 매우 높음
- 라인 수: 2581
- 크기: 114682 bytes
- 클래스 수: 1
- 함수/메서드 수: 23
**현재 책임**
- ToolSelector 호출 전후 전체 대화 실행 루프 관리
- 도구 실행, pending 승인 복원, system gate 후속 처리를 담당
- 건영아 fast path처럼 LLM으로 보내면 안 되는 입력을 조기 차단
**남겨도 되는 책임**
- run() 대화 실행 루프
- ToolSelector 호출
- 도구 실행 결과 정규화
- 승인 pending 실행 오케스트레이션
**분리 후보**
- 건영아 세션 처리 → AdminSessionService.php
- pending 승인 저장/복원 → ApprovalPendingService.php
- 도구 실행 → AgentToolRunner.php
- trace 조립 → AgentTraceBuilder.php
**터졌을 때 먼저 볼 것**
- agent.input 다음에 fast path가 탔는지 확인
- ToolSelector로 넘어갔는지 확인
- pending 승인 시 saved tool_spec이 chat인지 실제 tool인지 확인
**소속 클래스/트레이트**
- L24: `Agent`
**주요 함수/메서드**
- L34: `__construct()`
- L42: `addTrace()`
- L47: `getTrace()`
- L52: `run()`
- L897: `tryResolveSystemPendingApproval()`
- L1035: `saveSystemPendingApproval()`
- L1076: `guardSystemExecution()`
- L1275: `inspectWorklogBeforeApproval()`
- L1497: `isBadWorklogApprovalTargetToken()`
- L1509: `buildWorklogPreApprovalBlockMessage()`
- L1522: `isJudgeOnlyRequest()`
- L1527: `buildJudgeOnlyMessage()`
- L1571: `buildSystemApprovalPlanMessage()`
- L1612: `executeTool()`
- L1960: `normalizeToolResult()`
- L2361: `classifyQueryForTrace()`
- L2428: `summarizeTools()`
- L2448: `compactJson()`
- L2462: `resultType()`
- L2475: `pendingPath()`
- L2485: `savePendingQuestion()`
- L2501: `resolvePendingQuestion()`
- L2572: `agent_process()`
---
### `engine_modules/tools/ToolSelector.php`
- 이름: 도구 선택 / legacy routing
- 존재: ✅
- 위험도: 매우 높음
- 라인 수: 4226
- 크기: 184833 bytes
- 클래스 수: 1
- 함수/메서드 수: 48
**현재 책임**
- 질문을 어떤 Tool로 보낼지 1차 판단
- RoutePipeline으로 못 잡은 질문을 legacy rule로 fallback
- 라우팅 규칙 누적이 많아질수록 오판 위험이 큼
**남겨도 되는 책임**
- 최종 Tool 후보 조립
- RoutePipeline 위임
- fallback rule 관리
- trace에 선택 이유 남기기
**분리 후보**
- legacy customer/list/data_lookup rule → routing/*.php로 이동
- memory 관련 rule → MemoryRoute.php
- code 관련 rule → CodeRoute.php
- 복합 intent → MultiIntentRule 유지하되 ToolSelector 밖으로 격리
**터졌을 때 먼저 볼 것**
- selected_tools 로그 확인
- route_pipeline no_match 후 legacy selectByRules 진입 여부 확인
- conversation_chat이 너무 쉽게 잡아먹는지 확인
**소속 클래스/트레이트**
- L88: `ToolSelector`
**주요 함수/메서드**
- L31: `gpt_today_deep_selector_specs_v1()`
- L93: `__construct()`
- L98: `selectTools()`
- L1717: `applyRouteDecisionObserverV3()`
- L1755: `applyRouteDecisionControlledV1()`
- L1802: `applyRouteDecisionCompareTraceV6()`
- L1844: `matchHistoryPeriodSummaryRouteSafeV2()`
- L1889: `matchWorklogPeriodListRouteV1()`
- L1930: `matchHistoryRecentListDefaultRouteV2()`
- L1985: `matchHistoryMemoRoutesV1()`
- L2042: `matchRecordContextNoteRouteV1()`
- L2075: `matchEarlyLogCodeGuardV2()`
- L2133: `matchPersonContextRoutesV2()`
- L2184: `matchPersonCustomerMemoryRoutesV1()`
- L2255: `matchMemoryHistoryLookupEarlyV2()`
- L2285: `selectByRules()`
- L3125: `normalizeQuery()`
- L3130: `registryToolNames()`
- L3145: `firstRegisteredTool()`
- L3156: `makeToolPayload()`
- L3175: `matchRule()`
- L3185: `ruleDecisionIfRegistered()`
- L3212: `ruleDecision()`
- L3242: `matchedTerms()`
- L3262: `getToolDescriptions()`
- L3276: `askLLMToSelect()`
- L3295: `createPrompt()`
- L3342: `callLLM()`
- L3415: `parseResponse()`
- L3481: `canonicalToolName()`
- L3500: `correctMisroutedTool()`
- L3579: `isMemoryListIntent()`
- L3593: `isMemoryCreateIntent()`
- L3607: `hasWorklogStrongSignal()`
- L3621: `hasPaymentSignal()`
- L3632: `hasExpenseSignal()`
- L3649: `tsLooksLikeHistoryPeriodSummarySafeV2()`
- L3690: `tsRouteDecisionObserverV2()`
- L3732: `tsRouteDecisionTraceV2()`
- L3763: `tsRouteDecisionObserverLinesV3()`
- L3803: `tsRouteDecisionCompareLinesV5()`
- L3864: `tsRouteDecisionEmitLineV5()`
- L3889: `tsScheduleWorklogComboSpecsV1()`
- L3976: `tsScheduleQueryFromWorklogComboV1()`
- L3993: `tsWorklogReadQueryFromComboV1()`
- L4016: `tsRouteDecisionControlledSpecV1()`
- L4051: `tsRouteDecisionNormalizeScheduleDateQueryV4()`
- L4102: `scheduleEntryGuardV1()`
---
### `engine_modules/tools/CustomerTool.php`
- 이름: 고객 조회 / 점검 / 삭제
- 존재: ✅
- 위험도: 높음
- 라인 수: 1680
- 크기: 66142 bytes
- 클래스 수: 1
- 함수/메서드 수: 38
**현재 책임**
- 고객 목록 조회, 고객명 검색, 고객 정보 점검
- 고객 삭제 요청 처리 및 승인 후 실행
- 이미 삭제된 고객 힌트 표시
**남겨도 되는 책임**
- 고객 조회 출력
- 고객 점검 출력
- 고객 삭제 실행 진입점
- 삭제 결과 메시지 조립
**분리 후보**
- 고객 검색 → CustomerSearchService.php
- 고객 삭제 → CustomerDeleteService.php
- 고객 점검 → CustomerAuditService.php
- 이미 삭제 힌트 → CustomerDeletedHintService.php
- 사람 말투 메시지 → CustomerMessageFormatter.php
**터졌을 때 먼저 볼 것**
- customer.delete trace 확인
- customer_name / reason 파서 결과 확인
- matched_rows=0인데 deleted export hint가 있는지 확인
**소속 클래스/트레이트**
- L18: `CustomerTool`
**주요 함수/메서드**
- L20: `name()`
- L24: `description()`
- L29: `intents()`
- L34: `validate()`
- L44: `execute()`
- L228: `isCustomerAuditRequest()`
- L250: `looksLikeGenericCustomerQuery()`
- L256: `extractCustomerName()`
- L303: `isGenericCustomerToken()`
- L333: `listCustomerAuditTargets()`
- L495: `buildCustomerAuditMessage()`
- L653: `customerAuditExampleName()`
- L665: `resolvePdo()`
- L688: `detectCustomerAuditTable()`
- L837: `isNameOnlyCustomerRow()`
- L884: `isCustomerFieldUpdateRequest()`
- L909: `handleCustomerFieldUpdate()`
- L1034: `hasCustomerUpdateApproval()`
- L1039: `parseCustomerUpdateFields()`
- L1075: `extractCustomerNameForUpdate()`
- L1094: `cleanUpdateTextValue()`
- L1103: `findCustomerRowForUpdate()`
- L1148: `mapCustomerUpdateColumns()`
- L1195: `saveCustomerUpdateFields()`
- L1249: `isCustomerDeleteRequest()`
- L1267: `handleCustomerDeleteRequest()`
- L1373: `hasCustomerDeleteApproval()`
- L1385: `extractCustomerNameForDelete()`
- L1415: `cleanCustomerDeleteName()`
- L1430: `extractCustomerDeleteReason()`
- L1463: `findDeletedCustomerExportHintV1()`
- L1529: `findCustomerRowForDelete()`
- L1586: `deleteCustomerRow()`
- L1623: `pickColumn()`
- L1634: `pickColumns()`
- L1647: `cleanValue()`
- L1660: `shorten()`
- L1674: `quoteIdent()`
---
### `engine_modules/capabilities/web_preflight.php`
- 이름: web_capability preflight / Agent 우회 읽기 처리
- 존재: ✅
- 위험도: 높음
- 라인 수: 1737
- 크기: 62259 bytes
- 클래스 수: 0
- 함수/메서드 수: 18
**현재 책임**
- 일부 읽기 전용 질의를 Agent보다 앞에서 빠르게 처리
- business.data.lookup 같은 내부 DB 조회를 preflight에서 끝낼 수 있음
- 빠르지만 공통 처리 누락 위험이 있음
**남겨도 되는 책임**
- 읽기 전용 capability 판정
- business.data.lookup 라우팅
- preflight trace 생성
**분리 후보**
- business data lookup route → BusinessLookupCapability.php
- file/search capability → FileCapability.php
- trace formatter → CapabilityTraceFormatter.php
**터졌을 때 먼저 볼 것**
- web_capability preflight start 로그 확인
- legacy_tool=data_lookup 여부 확인
- Agent를 우회했는데 필요한 공통 상태가 갱신됐는지 확인
**주요 함수/메서드**
- L6: `gpt_schedule_year_range_fix_v1()`
- L73: `gpt_web_capability_normalize_query()`
- L121: `gpt_web_capability_append_log_cause_hint()`
- L174: `gpt_web_date_fact_find_pending_day_notes()`
- L371: `gpt_web_date_fact_preflight()`
- L500: `gpt_web_review_human_trace()`
- L617: `gpt_web_is_code_stats_query_v1()`
- L628: `gpt_web_review_preflight()`
- L749: `gpt_web_schedule_overlay_human_trace()`
- L907: `gpt_web_schedule_overlay_parse_range()`
- L977: `gpt_web_schedule_overlay_extract_pending_notes()`
- L1119: `gpt_web_schedule_overlay_extract_dates()`
- L1154: `gpt_web_schedule_overlay_weekday()`
- L1166: `gpt_web_schedule_overlay_append_to_message()`
- L1197: `gpt_web_schedule_overlay_base_query()`
- L1217: `gpt_web_schedule_overlay_preflight()`
- L1338: `gpt_web_capability_human_trace()`
- L1544: `gpt_web_capability_preflight()`
---
### `engine_modules/tools/routing/RoutePipeline.php`
- 이름: 라우팅 파이프라인 / 도메인별 Route 연결
- 존재: ✅
- 위험도: 중간
- 라인 수: 151
- 크기: 4703 bytes
- 클래스 수: 1
- 함수/메서드 수: 4
**현재 책임**
- WorksiteRoute, QuotationRoute, StockRoute, MoneyRoute, ScheduleRoute 등 도메인 route를 순서대로 확인
- ToolSelector가 비대해지는 걸 줄이기 위한 중간 구조
**남겨도 되는 책임**
- 도메인 route 순서 관리
- route_category_map trace
- no_match trace
**분리 후보**
- 카테고리 맵 관리 → RouteCategoryMap.php
- route 결과 검증 → RouteDecisionValidator.php
**터졌을 때 먼저 볼 것**
- route_pipeline enter 확인
- 각 Route no_match/match trace 확인
- blocked=true 이유 확인
**소속 클래스/트레이트**
- L14: `RoutePipeline`
**주요 함수/메서드**
- L17: `match()`
- L81: `routeClassToCategory()`
- L101: `loadCategoryMapMeta()`
- L125: `loadRoutes()`
---
### `partials/operator_status.php`
- 이름: 운영자 패널 / 빠른 진입 버튼
- 존재: ✅
- 위험도: 중간
- 라인 수: 98
- 크기: 4148 bytes
- 클래스 수: 0
- 함수/메서드 수: 0
**현재 책임**
- /gpt 첫 화면 상단 운영자 상태판
- SSH 답변, 상황판, 엔진 로그, 기록, 고객 점검, 작업 상태, 코드 지도, 보고서 버튼 제공
**남겨도 되는 책임**
- 운영자 진입 버튼 HTML
- 최근 SSH 답변 표시
- 패널 구조
**분리 후보**
- 최근 SSH 답변 로딩 → OperatorLatestAnswerService.php
- 버튼 목록 → OperatorPanelConfig.php
**터졌을 때 먼저 볼 것**
- 버튼이 안 보이면 CSS display/캐시 확인
- 버튼 링크 href 확인
---
### `work_state.php`
- 이름: 작업 상태 뷰어
- 존재: ✅
- 위험도: 낮음
- 라인 수: 213
- 크기: 6253 bytes
- 클래스 수: 0
- 함수/메서드 수: 2
**현재 책임**
- WORK_STATE.md를 웹에서 보고 복사
- 새 대화 복구문 생성
**남겨도 되는 책임**
- WORK_STATE.md 읽기
- 복사 버튼
- 새 대화 복구문 구성
**분리 후보**
- 공통 md viewer → MdViewerLayout.php
**터졌을 때 먼저 볼 것**
- WORK_STATE.md 경로 확인
- 복사 버튼 JS 확인
**주요 함수/메서드**
- L204: `copyState()`
- L207: `copyHandoff()`
---
### `work_report.php`
- 이름: 작업 보고서 뷰어
- 존재: ✅
- 위험도: 낮음
- 라인 수: 219
- 크기: 6505 bytes
- 클래스 수: 0
- 함수/메서드 수: 2
**현재 책임**
- WORK_REPORT.md를 웹에서 보고 복사
- 새 대화 보고서 복사
**남겨도 되는 책임**
- WORK_REPORT.md 읽기
- 복사 버튼
- 작업 상태/코드 지도 링크
**분리 후보**
- 공통 md viewer → MdViewerLayout.php
**터졌을 때 먼저 볼 것**
- WORK_REPORT.md 내용이 오래됐는지 확인
- 우선순위 찌꺼기 확인
**주요 함수/메서드**
- L210: `copyReport()`
- L213: `copyHandoff()`
---
### `code_map.php`
- 이름: 코드 지도 뷰어
- 존재: ✅
- 위험도: 낮음
- 라인 수: 237
- 크기: 6624 bytes
- 클래스 수: 0
- 함수/메서드 수: 0
**현재 책임**
- CODE_NAV.md를 웹에서 보고 검색
- 함수명/파일명 검색
**남겨도 되는 책임**
- CODE_NAV.md 읽기
- q 검색
- 복사 버튼
**분리 후보**
- CODE_NAV.md 생성 스크립트 → tools/build_code_nav.php
- 공통 md viewer → MdViewerLayout.php
**터졌을 때 먼저 볼 것**
- CODE_NAV.md가 오래됐는지 확인
- 검색 결과가 너무 단순하면 전후 문맥 검색으로 개선 필요
---
## 3. 1차 구조 평가
| 영역 | 점수 | 판정 |
|---|---:|---|
| 파일 위치 정리 | 70 | 위치는 잡힘 |
| 함수 지도화 | 65 | CODE_NAV + MODULE_OWNERSHIP로 개선됨 |
| 역할 분리 | 50 | Agent/ToolSelector/CustomerTool에 책임이 많음 |
| 디버깅 편의성 | 80 | 보고서/지도/상태판으로 회복 가능 |
| 장기 유지보수성 | 50 | 서비스 분리 전까지 위험 |
---
## 4. 다음 구조 개선 우선순위
1. `CustomerTool.php`에서 삭제/검색/점검 formatter 분리
2. `Agent.php`에서 건영아 세션/승인 pending service 분리
3. `ToolSelector.php` legacy rule을 routing 파일로 이동
4. `CODE_NAV.md` 자동 갱신 스크립트 만들기
5. `WORK_STATE.md`, `WORK_REPORT.md`, `MODULE_OWNERSHIP.md` 갱신 루틴 만들기
---
## 추가 완료: 요청 흐름도 1차
완료 시각: 2026-05-01 21:18:00
추가된 항목:
- `repair_jobs/MODULE_FLOW.md`
- `/gpt/module_flow.php`
- 운영자 패널 `[흐름도]` 버튼
내용:
- 전체 요청 기본 흐름
- web_capability 직행 흐름
- Agent 일반 실행 흐름
- 승인 pending 흐름
- 고객 삭제 / 이미 삭제 힌트 흐름
- 건영아 호출어 / 세션 흐름
- UI 운영자 패널 흐름
- 터졌을 때 추적 순서
다음 문서화:
1. `MODULE_DEPENDENCY.md`
2. `MODULE_FUNCTION_GROUPS.md`
3. `REFACTOR_ROADMAP.md`
---
## 추가 완료: 의존성 지도 1차
완료 시각: 2026-05-01 21:20:22
추가된 항목:
- `repair_jobs/MODULE_DEPENDENCY.md`
- `/gpt/module_dependency.php`
- 운영자 패널 `[의존성]` 버튼
내용:
- PHP 파일 스캔
- require/include 연결
- class 선언 위치
- new/Class:: 참조 연결
- 핵심 파일별 in/out 의존성
- 의존성 기준 위험 판정
다음 문서화:
1. `MODULE_FUNCTION_GROUPS.md`
2. `REFACTOR_ROADMAP.md`
3. 문서 자동 갱신 스크립트
---
## 추가 완료: 의존성 지도 v2 정밀 재생성
완료 시각: 2026-05-01 21:22:24
수정 내용:
- `repair_jobs/logs` 제외
- `repair_jobs/backups` 제외
- `repair_jobs/ssh_answers` 제외
- `data`, `reports`, `vendor`, `node_modules` 제외
- 백업/임시/테스트성 PHP 파일명 제외
- `src_ToolSelector.php` 같은 로그 추출본이 실제 의존성으로 잡히는 문제 제거
판정:
- MODULE_DEPENDENCY.md는 이제 실제 운영 파일 중심으로 보는 기준점으로 사용 가능.
---
## 추가 완료: 의존성 지도 v3 엄격 스캔
완료 시각: 2026-05-01 21:24:12
수정 내용:
- `include/require`를 줄 시작 PHP 문장 기준으로만 인정
- JS 문자열/headers/fetch 옵션 오인식 제거
- `tools/,`, `headers: {` 같은 잘못된 의존성 제거
- 임시/로그/백업 경로 제외 유지
판정:
- MODULE_DEPENDENCY.md는 v3부터 실제 운영 파일 의존성 기준으로 사용 가능.
---
## 추가 완료: 의존성 지도 v4 PHP 빌더
완료 시각: 2026-05-01 21:25:53
수정 내용:
- `tools/build_module_dependency_v4.php` 생성
- Python 일회성 생성 대신 PHP 빌더로 재생성
- 운영 경로만 스캔
- 줄 시작 `include/require` PHP 문장만 인정
- JS 문자열/headers/fetch 옵션 오인식 제거
판정:
- MODULE_DEPENDENCY.md는 v4부터 실제 운영 의존성 기준으로 사용.
---
## 추가 완료: 함수 역할 그룹 지도 1차
완료 시각: 2026-05-01 21:28:12
추가된 항목:
- `repair_jobs/MODULE_FUNCTION_GROUPS.md`
- `/gpt/module_function_groups.php`
- `tools/build_module_function_groups_v1.php`
- 운영자 패널 `[함수그룹]` 버튼
내용:
- 진입/API/공통 앞단
- 건영아 호출어 / 관리자 세션
- Agent 실행 루프
- 승인 / pending / 고위험 실행 차단
- 라우팅 / ToolSelector / RoutePipeline
- 고객 조회 / 고객 삭제 / 고객 수정 / 고객 점검
- web_capability / Agent 우회 읽기
- 운영자 UI / 문서 뷰어
다음:
- `REFACTOR_ROADMAP.md` 생성
- 구조 문서 자동 갱신 통합 스크립트
---
## 추가 완료: 함수 역할 그룹 v2 인라인 마커 보강
완료 시각: 2026-05-01 21:29:33
수정 내용:
- `tools/build_module_function_groups_v2.php` 생성
- v1 함수명 분류 결과에 인라인/마커 기반 역할 블록 추가
- `건영아 호출어 / 관리자 세션`이 함수 0개로 보이는 문제 보정
- 고객 삭제 pending 재라우팅, 이미 삭제 고객 힌트, api web_capability 앞단 마커까지 별도 표시
판정:
- MODULE_FUNCTION_GROUPS.md는 v2부터 함수 + 인라인 블록 기준으로 사용.
---
## 기존 MD 합류 요약 v1
합류 시각: 2026-05-01 21:50:13
### 1. `engine_modules/README_STRUCTURE.md`에서 반영할 핵심
- 실제 Agent 위치는 `engine_modules/agent/Agent.php`다.
- Agent Gateway 관련 파일은 `engine_modules/agent/agent_gateway.php` 계열로 분리되어 있다.
- 실제 Pipeline은 `engine_modules/core/pipeline_v2.php` 계열을 기준으로 봐야 한다.
- ToolSelector는 라우팅 입구이고, 실제 Route 우선 처리는 `engine_modules/tools/routing/RoutePipeline.php`로 이동 중인 구조다.
### 2. `reports/router_map.md`에서 반영할 핵심
- ToolSelector는 아직 라우팅 guard, legacy rule, fallback 판단이 섞여 있는 고위험 파일이다.
- RoutePipeline은 ToolSelector 비대화를 줄이기 위한 중간 구조다.
- 새 라우팅 규칙은 ToolSelector에 직접 더 쌓기보다 `routing/*.php`로 분리하는 것이 원칙이다.
### 3. `responsibility_candidates.md`에서 반영할 핵심
- ToolSelector 안에는 safety, planner_hint, intent_rule, schedule, stock, history/person/worklog, fallback_chat 책임이 아직 남아 있다.
- ToolSelector의 장기 목표는 “얇은 브릿지”다.
- safety_block, schedule_overlay 같은 planner hint 복구 로직은 별도 route/policy 계층으로 분리 후보다.
---
## 완료 처리: ScheduleOverlayPolicy 1차 분리
완료 시각: 2026-05-01 22:00:52
결과:
- `ScheduleOverlayPolicy.php` 생성 완료
- `PlannerHintAdapter.php`에서 `schedule_overlay` hint 연결 완료
- `ToolSelector.php` 기존 schedule_overlay fallback 앞에 policy bridge 추가 완료
- 기존 fallback 블록은 삭제하지 않고 안전장치로 유지
live smoke 판정:
- `올해 일정에서 놓친 게 있나` → `2026년 일정 요약`으로 정규화, schedule 실행
- `2026년 4월 일정` → policy 미대상, 월간 일정 경로 유지
- `고객 리스트 조회` → data_lookup/customer_list 유지
- `견적 전체 삭제해줘` → 실제 삭제 안 됨, system_pending 승인대기 저장
최종 판정:
- `SCHEDULE_OVERLAY_POLICY_LIVE_SMOKE_OK`
다음 후보:
- ToolSelector 안의 남은 planner_hint / fallback_chat / stock_read_exception 중 작은 것부터 추가 분리 검토
---
## 완료 처리: stock_read_exception legacy marker 정리
완료 시각: 2026-05-01 22:04:33
판정:
- `SafetyHintPolicy.php`가 stock read 예외 판단을 담당한다.
- `ToolSelector.php` 내부에는 실제 `stock_read_exception` legacy method가 없다.
- 기존 `SAFETY_STOCK_READ_EXCEPTION_V1_METHODS` 마커는 오해를 줄 수 있어 `MIGRATED_TO_SAFETY_HINT_POLICY`로 정리했다.
- 기능 변경은 없고 주석/마커 정리만 수행했다.
검증:
- `차단기 얼마야` → stock read 예외 매칭
- `누전차단기 재고 있어` → stock read 예외 매칭
- `다운라이트 몇개 남았어` → stock read 예외 매칭
- `차단기 내려` → 미매칭
- `릴레이 켜` → 미매칭
- `고객 리스트 조회` → 미매칭
다음 후보:
- Planner hint observer/fallback 정리 감사
- fallback_chat 정리는 영향 범위가 커서 보류
---
## 추가 완료: PlannerHintContextResolver v1 독립 생성
완료 시각: 2026-05-01 22:08:45
내용:
- `engine_modules/routing/PlannerHintContextResolver.php` 생성
- ToolSelector 내부의 router_kernel/planner_hint context 추출 및 fallback recompute 분리 준비
- 이번 단계에서는 ToolSelector 실행 흐름을 아직 바꾸지 않았다.
- 단독 스모크로 schedule_overlay, intent_rule guard, memory guard, 위험 삭제 approval recompute를 확인했다.
다음:
- Resolver 결과가 기존 ToolSelector trace와 같은지 live 비교
- 이후 ToolSelector observer/fallback 블록을 Resolver 호출로 대체할지 판단
---
## 추가 완료: PlannerHintContextResolver stock read guard v1
완료 시각: 2026-05-01 22:16:28
내용:
- `PlannerHintContextResolver.php`에 stock read exception guard 추가
- `SafetyHintPolicy::makeStockReadExceptionSpec()`가 매칭되는 가격/재고/수량 조회는 Resolver에서 `tool_hint=stock`으로 고정
- 목적은 `차단기 얼마야` 같은 재고 조회가 Resolver fallback에서 `safety_block`으로 오염되는 문제 방지
검증:
- 50개 입력 재감사 수행
- 기대값: `resolver_stock_read_pollution_risk=0`
- 기대값: `physical_wrong_stock_exception=0`
다음:
- live 비교 smoke 재실행
- 결과 정상 시 ToolSelector observer/fallback bridge 적용 여부 판단
---
## 정정 완료: PlannerHintContextResolver stock guard 메서드 누락 수정
완료 시각: 2026-05-01 22:17:55
이전 실패:
- `makeStockReadExceptionGuard()` 호출부는 들어갔지만 실제 private method가 누락되어 fatal 발생
- 원인: 스크립트가 호출문 문자열을 보고 method already exists로 오판
이번 수정:
- 실제 `private static function makeStockReadExceptionGuard()` 존재 여부를 정규식으로 확인
- 누락된 메서드 삽입
- 50개 입력 재감사 수행
다음:
- `recommendation=STOCK_GUARD_OK_RUN_LIVE_COMPARE_NEXT` 확인 후 live 비교 smoke 재실행
---
## 추가 완료: SafetyPhysicalControlPolicy v1
완료 시각: 2026-05-01 22:21:30
목적:
- `타이머 가동해` 같은 실제 물리 동작 요청이 chat/auto로 빠지는 안전 구멍 보강
- 가격/재고/수량 조회는 stock read 예외가 먼저 처리되므로 safety_block으로 오염되지 않게 유지
변경:
- `engine_modules/routing/SafetyPhysicalControlPolicy.php` 생성
- `ToolSelector.php`에 `SAFETY_PHYSICAL_CONTROL_POLICY_V1_BRIDGE` 추가
- bridge 위치는 SafetyHintPolicy stock read 예외 다음, PlannerHintAdapter 이전
검증:
- `타이머 가동해` → safety_block 기대
- `차단기 내려` → safety_block 유지
- `차단기 얼마야` → stock 유지
- `릴레이 가격 알려줘` → stock 유지
- `차단기 내려 말고 가격만 알려줘` → safety_block 미대상
다음:
- live smoke 결과 확인 후 PlannerHintContextResolver bridge 적용 여부 재판단
---
## 추가 진행: PlannerHintContextResolver 병렬 trace v1
진행 시각: 2026-05-01 22:24:10
변경:
- `ToolSelector.php`에 `PLANNER_HINT_CONTEXT_RESOLVER_PARALLEL_TRACE_V1` 추가
- Resolver 결과를 trace에만 남긴다.
- 실제 ToolSelector 선택 결과는 변경하지 않는다.
목적:
- 기존 ToolSelector 판단과 Resolver 판단을 live에서 비교
- 바로 치환하지 않고 안전하게 차이를 관찰
확인 질의:
- `올해 일정에서 놓친 게 있나`
- `2026년 4월 일정`
- `고객 리스트 조회`
- `견적 전체 삭제해줘`
- `차단기 얼마야`
- `릴레이 가격 알려줘`
- `차단기 내려`
- `타이머 가동해`
- `차단기 내려 말고 가격만 알려줘`
다음:
- 병렬 trace 결과가 안정적이면 observer/fallback 블록 치환 검토
- 불일치가 있으면 Resolver 보강 후 다시 비교