
    uiR             -       H   d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	m	Z	m
Z
 ddlmZ ddlmZmZmZ 	 ej"                  j%                  d e ee      j*                               ddlmZ  e        	 ddlmZ 	 ddlmZ  ej:                         ZdZ	 ddl m!Z! ddl m"Z# ddl$m%Z% ddl'm(Z(m)Z)  e%e*      Z+	 ddl,m-Z. dZ/	 ddl0m1Z2 dZ3	 ddl4m5Z6 dZ7	 ddl8m9Z: dZ;	 ddl<m=Z> dZ?	 ddl@mAZB dZC	 ddlDmEZF dZG	 ddlHmIZJ dZK	 ddlLmMZN  eNj                  d e ee      j*                  dz  d z              ZP eNj                  eP      ZRePj                  j                  eR       eRj                  ZVdZWererej                  d!      nd"ZY eej                  j                  d#eY            Z\e\d$z  d%z  Z]e\d$z  d&z  Z^ererej                  d'      nd(Z`ej                  j                  d)e`      Zad*Zb ej                         d+z  d,z  d-z  d$z  Zdej                  j                  d.      ej                  j                  d/      ej                  j                  d0      ej                  j                  d1      ej                  j                  d2      ej                  j                  d3      ej                  j                  d4      ej                  j                  d5      ej                  j                  d6      d7	Ze	 dd8lfmgZgmhZhmiZi  eh       Zj eg       Zk ei       ZldMdNdOdPdQdOdRdSdOdTdUdOdVZqh dWZrelj                         D ci c]  \  }}||
 c}}ZsddXedz  deoeeoeef   f   fdYZtddZedz  defd[Zud\eoeeoeef   f   deveoeef      fd]Zw	 dd^edZedz  defd_Zxeafd'edeoeef   fd`Zyeafdaedbed'ededz  fdcZzddaedbedde{ddfdeZ|dfedeofdgZ}ddhZ~diedjeddfdkZdledeofdmZddiednedoedpedz  ddf
dqZdledeodz  fdrZdsedeodz  fdtZ	 ddiedueodvedwedz  def
dxZdledevfdyZdzevd{edevfd|Zdzevdefd}Zdzevd~edevfdZdevddfdZdlede{fdZh dZdled{edefdZdled{edefdZdzevdeofdZdedeofdZh dZg dZdledzevdefdZdlede{fdZddiedledededee   f
dZdiede{ddfdZdeofdZdedefdZdde{defdZdede{fdZdefdZdieddfdZddedfedlededeof
dZdledee   defdZ	 	 	 	 ddfedlediededee   dee   dedefdZ"dedfedieddfdZdieddfdZdleddfdZdlediededdfdZdledee   fdZdfedee   fdZddzevde{devfdZdledee   fdZdlededdfdZdfedleddfdZddiedledededdf
dZdeofdZdledee   fdZddfedlededee   fdZdedee   fdZ	 	 	 	 ddee   dledediee   dedbee   deofdZdedeveo   fdZdededeveo   fdZdedfedeofdZ	 ddededfediede{deeo   fdZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddfee   dlededeee      dee   dee   dee   dedediee   dee{   dedee   dedbee   dedee   dededueeo   dedeof,dɄZdiedeofdʄZdd˄Ze*dk(  r e        yy# e$ r Y 1w xY w# e$ r ded	ed
eddfdZY Bw xY w# e$ r dZdZY 8w xY w# e&$ r ddl m!Z! ddl m"Z# ddl$m%Z% Y Cw xY w# e&$ r dedefdZ.dZ/Y @w xY w# e&$ r dZ2dZ3Y Gw xY w# e&$ r dZ6dZ7Y Nw xY w# e&$ r dZ:dZ;Y Uw xY w# e&$ r dZ>dZ?Y \w xY w# e&$ r dZBdZCY cw xY w# e$ r dZFdZGY jw xY w# e&$ r dZJdZKY qw xY w# e$ r dZVdZWY w xY w# e&$ r erceraej                  d9      Zmej                  d:      Zn eoem      Zj eoen      Zlenj                         D  ci c]  \  } }|em|     nc c}} w c}} Zkn!d;d<d=d>d?d@dAdBdCZjd;d<d=d>d?d@dAdBdDZkdEdFdGdHdIdJdKdLdCZlY w xY wc c}}w )u#  
작업 위임 디스패처 (dispatch.py)

아누가 팀장에게 작업을 위임할 때 사용.
cokacdir --cron으로 독립 세션을 생성하여 현재 대화를 막지 않음.

Usage:
    # 권장: 지시 내용을 파일에 먼저 작성 후 --task-file로 전달
    python3 dispatch.py --team dev1-team --task-file /path/to/task-desc.md [--level normal|critical|security]
    # 짧은 한 줄 작업만 --task 직접 전달 (100자 이내 권장)
    python3 dispatch.py --team dev2-team --task "간단한 버그 수정" --level critical
    N)datetime	timedelta)Path)AnyListOptionalload_env_keys)atomic_json_writepathdata_kwreturnc                 F   ddl }t        |       }|j                  j                  dd       |j	                  t        |j                        d      \  }}	 t        j                  |dd	      5 }t        j                  ||d
d       |j                          t        j                  |j                                ddd       t        j                  |t        |             y# 1 sw Y   )xY w# t        $ r' 	 t        j                  |        # t         $ r Y  w xY ww xY w)u@   fallback: utils.atomic_write 로드 실패 시 인라인 구현.r   NTparentsexist_ok.tmp)dirsuffixwutf-8encoding   F)indentensure_ascii)tempfiler   parentmkdirmkstempstrosfdopenjsondumpflushfsyncfilenoreplaceBaseExceptionunlinkOSError)r   r   r   _tftargetfdtmp_pathfs           9/home/jay/workspace/.worktrees/task-2374-dev7/dispatch.pyr   r   )   s    dD48{{s6=='9&{IH	2sW5 %		$!%@	$% JJxV-	% %
  			(#   	sI   C0 /AC$<'C0 $C-)C0 0	D :DD 	DD DD )ConfigManagerTF)	TEAM_INFO)build_prompt)
get_logger)COMPOSITE_ALLOWED_TEAMSMAX_COMPOSITE_TEAMS)redact_sensitive_texttextc                     | S N )r;   s    r3   _redact_textr?   `   s        )scan_content)check_command)log_file_operation)route_model)should_sanitize)BotStatusManager)SessionResilienceimage_skill_routertoolszimage-skill-router.pyzroots.workspacez/home/jay/workspaceWORKSPACE_ROOTmemoryzorganization-structure.jsonztask-timer.pychat_id
6937032012COKACDIR_CHAT_IDi   z.claudeprojectsz%-home-jay--cokacdir-workspace-autosetCOKACDIR_KEY_ANUCOKACDIR_KEY_DEV1COKACDIR_KEY_DEV2COKACDIR_KEY_DEV3COKACDIR_KEY_DEV4COKACDIR_KEY_DEV5COKACDIR_KEY_DEV6COKACDIR_KEY_DEV7COKACDIR_KEY_DEV8)	anudev1dev2dev3dev4dev5dev6dev7dev8)build_bot_to_key_mapbuild_team_bot_mapbuild_team_to_bot_id_mapteamsteam_to_botrZ   r[   r\   r]   r^   r_   r`   ra   )	dev1-team	dev2-team	dev3-team	dev4-team	dev5-team	dev6-team	dev7-team	dev8-teambot-bbot-cbot-dbot-ebot-fbot-gbot-hbot-irp   rq   rr   rs   rt   ru   rv   rw   u   마아트 (Ma'at)u   QC 매니저)nameroleu   로키 (Loki)u   레드팀 리더u   비너스 (Venus)u   디자인 디렉터u   야누스 (Janus)DevOps)qcredteamdesigndevops>   r}   content	marketing
consulting
publishingexclude_task_idc                    t         r&t         t        t              j                  |       S t        dz  dz  }i }|j	                         s|S 	 t        |dd      5 }t        j                  |      }ddd       j                  d	i       j                         D ]i  \  }}|j                  d
      dk7  r| r|| k(  r#|j                  dd      }|t        v rt        |   }||d||<   |j                  d      }	|	sb||d||	<   k 	 |S # 1 sw Y   xY w# t        j                  t        f$ r#}
t        j                  d|
        Y d}
~
|S d}
~
ww xY w)u   task-timers.json에서 running 상태 봇의 점유 정보 반환.

    Args:
        exclude_task_id: 이 task_id의 엔트리는 결과에서 제외 (자기 자신 제외용)

    Returns:
        {bot_id: {"task_id": ..., "team_id": ...}} 매핑
    Nworkspace_rootr   rK   task-timers.jsonrr   r   tasksstatusrunningteam_id )task_idr   botu4   task-timers.json 읽기 실패 (봇 점유 정보): )_BOT_STATUS_AVAILABLE_BotStatusManager	WORKSPACEget_busy_botsexistsopenr%   loadgetitemsTEAM_TO_BOT_IDJSONDecodeErrorr-   loggerwarning)r   
timer_filebusyr2   r   r   
task_entryr   r   	bot_fieldes              r3   _get_busy_bots_infor     s`    !2!> 	:HHYhHii X%(::J&(DS*cG4 	 99Q<D	 #'88GR#8#>#>#@ 	KGZ~~h'947o#= nnY3G.($W-(/GDS	"u-I.5'"JY	K" K'	  	     '* SMaSQRRKSs7   D D	2B	D <
D 	DD E.EErequired_modelc           
         t        t               j                               }i }| rct               }dD ]T  }t        j                  |d      }t        j                  |d      }t        j                  |d      }|sH||v sM||   ||<   V dD ]P  }||v r| rD|j                  |d      | k7  r/t        j                  d| d|j                  |d       d|         N|c S  | rt        d|  d      t        d	      )
u  task-timers.json에서 running 상태 확인 후 가용 봇 반환.

    우선순위: bot-b > bot-c > bot-d > bot-e > bot-f > bot-g > bot-h > bot-i
    dev team running → 해당 봇 사용 중
    composite/marketing/design running → bot 필드로 판별

    Args:
        required_model: 필요한 모델명 (예: "claude-opus-4-6"). 지정 시 해당 모델의 봇만 선택.

    Returns:
        "bot-b" | "bot-c" | ... | "bot-i"
    Raises:
        RuntimeError: 조건에 맞는 가용 봇이 없을 때
    ro   r   [model-filter]     스킵: model=unknown    ≠    모델 조건(i   )에 맞는 가용 봇이 없습니다. 모든 opus 봇이 작업 중이거나 모델 미설정입니다.E   모든 봇이 작업 중입니다. 잠시 후 다시 시도하세요.)setr   keys_read_bot_modelsBOT_TO_DEFAULT_TEAMr   TEAM_BOTBOT_KEYSr   infoRuntimeError)	r   	busy_botsbot_model_map
key_modelsbot_iddefault_teamkey_namekey_hashr   s	            r3   _find_available_botr   2  s)    ')..01I %'M%'
^ 	=F.2262>L||L"5H||Hb1HH
2(28(<f%	= X )m//R8NJKK/#om>O>OPSU^>_=``efteuvw
 ^, -P Q
 	
 ^
__r@   r   c                 z    g d}g }|D ]/  }|| vs|j                  |t        j                  |d      d       1 |S )u  전체 봇 중 busy가 아닌 가용 봇 목록과 기본 팀 매핑을 반환.

    Args:
        busy_bots: _get_busy_bots_info()의 반환값 {bot_id: {"task_id": ..., "team_id": ...}}

    Returns:
        [{"bot_id": "bot-c", "default_team": "dev2-team"}, ...]
    ro   r   )r   r   )appendr   r   )r   all_bots	availabler   s       r3   _get_available_bots_with_teamsr   ^  sT     XHI i!$7$;$;C$K r@   timer_task_idc           
      &   t         dz  dz  }t         dz  dz  }|j                  j                  dd       d}	 t        |d      }t	        j
                  |t        j                         t               }|j                         r	 t        |dd	
      5 }t        j                  |      }ddd       j                  di       j                         D ]w  \  }}	|	j                  d      dk7  r|| k(  r!|	j                  dd      }
|
t        v r|j                  t        |
          |	j                  d      }|sg|j                  |       y 	 i }|rct'               }dD ]T  }t(        j                  |d      }t*        j                  |d      }t,        j                  |d      }|sH||v sM||   ||<   V d}dD ]P  }||v r|rD|j                  |d      |k7  r/t"        j/                  d| d|j                  |d       d|        N|} n ||rt1        d| d      t1        d      |j                         r	 t        |dd	
      5 }t        j                  |      }ddd       j                  di       j                  |       }|rQ||d<   t        |dd	
      5 }t        j2                  ||dd       ddd       t"        j/                  d|  d| d       ||6	 t	        j
                  |t        j4                         |j7                          S S # 1 sw Y   lxY w# t        j                  t         f$ r#}t"        j%                  d|        Y d}~d}~ww xY w# 1 sw Y   xY w# 1 sw Y   xY w# t        j                  t         f$ r%}t"        j%                  d |  d!|        Y d}~d}~ww xY w# t8        $ r Y S w xY w# |E	 t	        j
                  |t        j4                         |j7                          w # t8        $ r Y w w xY ww xY w)"u   봇 선택과 타이머 기록을 원자적으로 수행.

    파일 락을 잡은 상태에서:
    1. busy bots 확인
    2. 가용 봇 선택
    3. task-timers.json에 bot 필드 즉시 기록

    이렇게 해야 동시 dispatch 간 봇 충돌을 방지할 수 있음.

    Args:
        timer_task_id: 봇을 예약할 태스크 ID
        required_model: 필요한 모델명 (예: "claude-opus-4-6")

    Returns:
        선택된 봇 ID (예: "bot-b")
    Raises:
        RuntimeError: 조건에 맞는 가용 봇이 없을 때
    rK   r   .task-timers.lockTr   Nr   r   r   r   r   r   r   r   r   r   u-   task-timers.json 읽기 실패 (봇 예약): ro   r   r   r   r   r   r   r   Fr   r   r   u   [봇 예약]     → u    (원자적 예약 완료)u   봇 예약 기록 실패 (): )r   r   r    r   fcntlflockLOCK_EXr   r   r%   r   r   r   r   addr   r-   r   r   r   r   r   r   r   r   r&   LOCK_UNclose	Exception)r   r   r   lock_file_pathlock_fdr   r2   r   tidentryteamr   r   r   r   r   r   r   r   selected_botr   r   s                         r3   _select_and_reserve_botr   t  s   , X%(::J),??Nt<GK~s+GU]]+ "e	T*cG< (99Q<D("&((7B"7"="="? 
1JCyy*i7 m+  99Y3D~-!nT&:; %		% 0I !i0
1 )+)+Jb A266vrB#<<b9#<<"5J 6,6x,@M&)A $([ 	Ci-"3"3C"<"NocU/-BSBSTWYbBcAddijxiyz{L	 "$^$4 5X Y  fgg 
S*cG< (99Q<D(!XXgr266}E
(4Ju%j#@ IA		$aHIKK-eL>Qk lm GU]]3 }( ( (('2 T!NqcRSSTH( (
I I (('2 S!;M?#aSQRRS  	 GU]]3 	 s   A
O L L(BL 7L 
AO #O (BO 8M7 M>M7 M+4$M7 O 4N8LL M8MO MO M(#M7 +M40M7 7N5N0+O 0N55O 8	OOP4P ?P 	P	PPPc                    t        j                         dz  dz  }|j                         si S 	 t        |dd      5 }t	        j
                  |      }ddd       i }j                         D ]:  \  }}|j                  di       }|j                  t        |       d      }|s6|||<   < |S # 1 sw Y   ZxY w# t        $ r$}	t        j                  d	|	        i cY d}	~	S d}	~	ww xY w)
u   bot_settings.json에서 봇 키 해시별 모델 설정을 읽어 반환.

    Returns:
        {key_hash: model_name} 예: {"c38fb9955616e24d": "claude-opus-4-6"}
    	.cokacdirbot_settings.jsonr   r   r   Nmodelsr   u.   [bot_models] bot_settings.json 읽기 실패: )r   homer   r   r%   r   r   r   r"   r   r   debug)
rL   settings_pathr2   settingsresultr   cfgr   modelr   s
             r3   r   r     s     IIK+-0CCM!	-w7 	$1yy|H	$%^^- 	)MHcWWXr*FJJs7|R0E#(x 		)
 	$ 	$  EaSIJ	s;   B7 B+AB7 "B7 +B40B7 7	C$ CC$C$r   r   c                    t        j                         dz  dz  }|j                         st        j	                  d       y	 t        |dd      5 }t        j                  |      }ddd       j                  |       }|st        j	                  d|         y|j                  d	i       }|j                  t        |            }||t        |      <   ||d	<   ||| <   t        |d
d      5 }t        j                  ||dd       ddd       t        j                  d|  d| d|        |S # 1 sw Y   xY w# 1 sw Y   5xY w# t        $ r"}	t        j                  d|	        Y d}	~	yd}	~	ww xY w)u"  bot_settings.json에서 특정 봇의 모델을 변경한다.

    Args:
        key_hash: 봇 키 해시 (예: "c38fb9955616e24d")
        model: 설정할 모델명 (예: "claude-opus-4-6")
        chat_id: 채팅 ID

    Returns:
        이전 모델명 또는 None (변경 실패)
    r   r   u)   [model-override] bot_settings.json 없음Nr   r   r   u(   [model-override] 봇 설정 없음: key=r   r   Fr   r   u(   [model-override] 봇 모델 변경: key=, r   u+   [model-override] 봇 모델 변경 실패: )r   r   r   r   r   r   r%   r   r   r"   r&   r   r   error)
r   r   rL   r   r2   r   bot_cfgr   
prev_modelr   s
             r3   _set_bot_modelr     sa    IIK+-0CCM!BC-w7 	$1yy|H	$,,x(NNEhZPQXr*ZZG-
$s7|"$-w7 	A1IIha@	A>xj:,V[\a[bcd	$ 	$	A 	A  B1#FGsO   E D.&3E AE ,D:'E .D73E :E?E 	E1E,,E1delayc                     d|  dt          d| d}t        j                  ddd| d| gd	t        j                  t        j                  
       t        j                  d| d|  d|        y)u   delay초 후 봇 모델을 복원하는 백그라운드 프로세스를 포크한다.

    세션이 시작된 후 모델을 원래대로 복원하여 다음 일반 작업에서 Sonnet이 사용되도록 한다.
    z}import json, pathlib; p=pathlib.Path.home()/'.cokacdir'/'bot_settings.json'; d=json.loads(p.read_text(encoding='utf-8')); d['z']['models']['z']='zK'; p.write_text(json.dumps(d,ensure_ascii=False,indent=2),encoding='utf-8')python3z-czimport time; time.sleep(z); T)start_new_sessionstdoutstderrz[model-override] u"   초 후 모델 복원 예약: key=r   N)CHAT_ID
subprocessPopenDEVNULLr   r   )r   r   r   restore_scripts       r3   _schedule_model_restorer     s    	 Z~gYd5' :S	T  	D4UG3~>NOP!!!!	 KK#E7*LXJV[\a[bcdr@   r   c           	         ddd| d}	 d}t         j                         rt        t         dd      5 }t        j                  |      }ddd       j                  di       j                  d	i       j                  d
g       }|D ]  }|j                  d      | k(  r&|j                  di       }|j                  dd      } nW|j                  dg       D ]<  }|j                  d      | k(  s|j                  di       }|j                  dd      } n |s n d}	t        j                  |       }
|
rt        j                  |
      }|rt        j                         dz  dz  }|j                         rmt        |dd      5 }t        j                  |      }ddd       j                  |i       }|j                  di       j                  t        t              d      }	||d<   |	|d<   |r-|	r+||	k7  r&d|d<   t        j                  d|  d| d|	 d       |S t        j                  d|  d|xs d d|	xs d d       	 |S # 1 sw Y   xY w# 1 sw Y   xY w# t        $ r#}t        j                  d |        Y d}~|S d}~ww xY w)!u  org-structure.json과 bot_settings.json의 모델 설정을 비교 검증한다.

    org-structure.json에서 해당 team_id의 lead.model 값과
    bot_settings.json에서 해당 봇의 models[chat_id] 값을 비교하여
    불일치 시 WARNING 로그를 출력한다.
    디스패치는 bot_settings.json 기준을 유지한다.

    Args:
        team_id: 검증할 팀 ID (예: "dev8-team")

    Returns:
        dict: {"consistent": bool, "org_model": str, "bot_model": str, "team_id": str}
    Tr   )
consistent	org_model	bot_modelr   r   r   r   N	structurecolumnsre   r   leadr   	sub_teamssub_team_idr   r   r   r   r   Fr   u   [모델 불일치] z: org-structure=z, bot_settings=u/    — 디스패치는 bot_settings 기준 유지u   [모델 일관성] z: org=N/A, bot=u     — 일치 또는 확인 불가u(   [모델 검증] 검증 실패 (무시): )ORG_FILEr   r   r%   r   r   r   r   r   r   r"   r   r   r   r   r   )r   r   r   r2   org_datare   r   r   subr   r   r   r   r   r   r   s                   r3   _validate_model_consistencyr  .  s    #'RbU\]F1G	??hg6 (!99Q<(LLb155iDHHRTUE 88I&'188FB/D $" 5I88K4 Cww}-8"wwvr2$(HHWb$9		
   	<<(||H-H $		k 9<O O '')mS7C 0q#'99Q<0&ll8R8G 'Hb 9 = =c'lB OI'{'{yI'=#(F< NN%gY.>yk J  ){*Y[ M KK%gYfY5G%4H
S\Se`eRf  gG  H M_( (40 0$  GA!EFFMGs[   (I IB"I *)I A+I ?IB I 'I II II 	J#JJc                  n   ddl } 	 t        j                         dz  dz  }|j                         st        j                  d       yt        |dd      5 }t        j                  |      }ddd       | j                        }|j                         D ]  \  }}d	|v sd
|d	<    t        dz  dz  }|j                  j                  dd       t        |dd      5 }t        j                  ||dd       ddd       t        j                  d|        	 t        j                         dz  dz  }|j                         syt        |dd      5 }t        j                  |      }ddd       t        dz  dz  }	t        |	dd      5 }t        j                  |      }
ddd       
j%                  dd      }j                         D ]R  \  }}|j%                  dd      }|j%                  dd      }|j%                  di       }|j%                  di       }|r|sT|j'                  d      }t)        |      dk\  r0|d   j+                  d      r|d   dd j-                         r|d   }nd }|r
||v r||   }n'|r#t/        t1        |j3                                     }nd}|r
||v r||   }n'|r#t/        t1        |j3                                     }nd}||||d!|
j5                  d"i       |<   |d k7  s,| d#}|
j5                  d$i       j5                  ||       U t7        j8                         j;                  d%      |
j5                  d&i       d'<   t        |	dd      5 }t        j                  |
|dd       ddd       t        j                  d(       y# 1 sw Y   2xY w# 1 sw Y   xY w# t         $ r#}t        j#                  d|        Y d}~d}~ww xY w# 1 sw Y   xY w# 1 sw Y   TxY w# 1 sw Y   xY w# t         $ r"}t        j#                  d)|        Y d}~yd}~ww xY w)*uY  bot_settings.json의 민감 정보(token)를 마스킹한 사본을 workspace에 저장한다.

    원본의 "token" 키 값을 "***REDACTED***"로 교체한 사본을
    {WORKSPACE}/memory/bot_settings_sync.json에 저장한다.
    기타 필드는 그대로 유지된다.
    실패해도 디스패치 자체는 중단되지 않는다.
    r   Nr   r   u>   [bot_settings_sync] bot_settings.json 없음, 동기화 스킵r   r   r   tokenz***REDACTED***rK   zbot_settings_sync.jsonTr   r   Fr   r   u&   [bot_settings_sync] 동기화 완료: u/   [bot_settings_sync] 동기화 실패 (무시): configconstants.jsonrL   r   display_nameusernamer   last_sessions_   devrY   )r  r  team_dirr   bots-teamre   z%Y-%m-%dmetalast_updatedu;   [bot_settings_sync] constants.json 봇 정보 반영 완료uA   [bot_settings_sync] constants.json 업데이트 실패 (무시): )copyr   r   r   r   r   r   r%   r   deepcopyr   r   r   r    r&   r   r   r   r   splitlen
startswithisdigitnextitervalues
setdefaultr   nowstrftime)r  r   r2   r   maskedr   r   	sync_pathr   constants_path	constantschat_id_key	_key_hashr  r  r   r  partsbot_short_idr   r  r   s                         r3   _sync_bot_settingsr%  s  s    N		k14GG##%LLYZ-w7 	$1yy|H	$ x(#\\^ 	0MHc#~/G	0 (+CC	td;)S73 	?qIIfaeA>	? 	<YKHI
C`		k14GG##%-w7 	$1yy|H	$ #X-0@@.#8 	%A		!I	%  mmIr2&nn. +	TNIs # ;LGGJ3H778R0F"%''/2">Mx !&&s+E5zQ58#6#6u#=%(12,BVBVBX$Qx  % {f4#K0T&--/23{m; -k :]%9%9%; <= !-$$	>I  ,\: u$)N%0$$Wb1<<WlSW+	T\ <D<<>;R;RS];^	VR(8.#8 	BAIIiqA	B 	QRi	$ 	$	? 	?  NHLMMN	$ 	$	% 	%j	B 	B  `Z[\Z]^__`s   ?N4 N4 N*3N4 >N4 N'6 N4 *P	 P	 O#&"P	 O0EP	 4A.P	 "O=<P	 N$N4 'N1,N4 4	O =OO #O-(P	 0O:5P	 =PP	 		P4P//P4r   metadatac                 4   t         dz  dz  }t         dz  dz  }|j                  j                  dd       d}	 t        |d      }t	        j
                  |t        j                         |j                         s:	 |6	 t	        j
                  |t        j                         |j                          yyt        |dd	
      5 }t        j                  |      }ddd       j                  di       j                  |       }|r8|j                  |       t        ||       t         j#                  d|  d|        |6	 t	        j
                  |t        j                         |j                          yy# t        $ r Y yw xY w# 1 sw Y   xY w# t        $ r%}t         j%                  d|  d|        Y d}~}d}~ww xY w# t        $ r Y yw xY w# |E	 t	        j
                  |t        j                         |j                          w # t        $ r Y w w xY ww xY w)uI   task-timers.json에서 지정된 task_id 항목에 메타데이터 추가rK   r   r   Tr   Nr   r   r   r   r   u   메타데이터 패치 완료: r   u   메타데이터 패치 실패 (r   )r   r   r    r   r   r   r   r   r   r   r   r%   r   r   updater   r   r   r   )	r   r&  r   r   r   r2   r   r   r   s	            r3   _patch_timer_metadatar)    s   X%(::J),??Nt<G~s+GU]]+  " GU]]3  *cG4 	 99Q<D	 XXgr*..w7
h'j$/KK9'%zRS GU]]3   	  	   J8	QCHIIJ  	 GU]]3 	 s   A F =4E3 3F FA#F =4F? 3	E?>E?FF 	F<F72G 7F<<G ?	G
GH4HH	HHHH	task_descc                    dddd}| s|S | j                         j                  d      r	 | j                         }|j                  dd      }|d| }	 dd	l}|j	                  |      }t        |t              rD|j                  d
      dv r|d
   |d
<   d|d<   d|v r t        |d   t              r|d   |d<   d|d<   |d   dk(  rt        j$                  d| t        j&                        }
|
D ]l  }	 dd	l}|j	                  |      }t        |t              rD|j                  d
      dv r|d
   |d
<   d|d<   d|v r t        |d   t              r|d   |d<   d|d<   n |d
   dk(  r|d   du rd| j!                         vrd|d<   |S # t        $ r |j                         D ]  }|j                         }t        j                  d|      }|r|j                  d      |d
<   d|d<   t        j                  d|t        j                        }	|	sl|	j                  d      j!                         dk(  |d<   d|d<    Y zw xY w# t"        $ r Y w xY w# t        $ r Y kw xY w)u  task_desc에서 옵션 D 시맨틱 메타데이터(kind/merge_required) 추출 (task-2374).

    우선순위:
    1. 최상단 YAML frontmatter (`---\nkind: code\nmerge_required: true\n---`)
    2. fenced ```yaml 블록 안의 top-level `kind`/`merge_required`
    3. legacy 기본값: kind=code, merge_required=True

    Returns:
        {"kind": "code"|"meta", "merge_required": bool, "source": "frontmatter"|"fenced"|"default"}
    codeTdefaultkindmerge_requiredsourcez---z
---r
  r   Nr/  )r,  r  frontmatterr1  r0  z$^kind:\s*['\"]?(code|meta)['\"]?\s*$   z#^merge_required:\s*(true|false)\s*$true```yaml\s*\n(.*?)```fencedr  zmerge_required:F)lstripr  indexyaml	safe_load
isinstancedictr   boolr   
splitlinesstriprematchgroup
IGNORECASElower
ValueErrorfindallDOTALL)r*  r   strippedend_idxfm_textr9  fmlinem1m2fenced_blocksblockr   s                r3   _parse_task_metadatarQ    s    #diPF $$U+	 '')HnnWa0Gq)G9^^G,b$'vvf~)99)+Fv+8x('2-*R@P=QSW2X356F3G/0+8x(" h9$

#:IryyQ" 	E~~e,dD)xx'+;;)-fv+3x('4/JtDT?UW[4\378H3I/0+3x(	 f~F+;$<$DIOO$55',F#$MK  9#..0 	9D::<D"I4PB)+!v+8x("H$PRP]P]^B46HHQK4E4E4G64Q/0+8x(	99  		"  sJ   'H: A)F /A)I
BH7,H73H: 6H77H: :	II
	IIr/  r0  r   c                    ddl }t        t              dz  dz  }|j                  dd       ||  dz  }ddlm}m}m}	  |	 |d	
            }
|j                  |
      j                  d      }| d||||ddddddddd}|j                         r{	 t        j                  |j                  d            }t        |t              rEh d}|j                  d      |v r||d<   ||d<   |}n!dD ]  }|j                  |      s||   ||<    |j%                  t'        |      d|  dd      \  }}	 t)        j*                  |dd      5 }t        j,                  ||dd       ddd       t)        j.                  ||       y# t        j                   t"        f$ r Y w xY w# 1 sw Y   ?xY w# t0        $ r) 	 t)        j2                  |       Y y# t"        $ r Y Y yw xY ww xY w)u   memory/state/{task_id}.json 사전 생성 (phase: dispatched).

    옵션 D (task-2374): dispatch 시점에 SoT 초기화. 이미 진행된 phase는 보존.
    r   NrK   stateTr   .json)r   r   timezone	   )hoursz%Y-%m-%dT%H:%M:%S+09:00
dispatched)r   phaser/  r0  r   timestamp_dispatchedtimestamp_work_donetimestamp_mergedtimestamp_merge_failedproject_pathpr_urlmerge_commit_sharetry_count
last_errorr   r   >   	work-donesystem-donemerge-failedmergedrY  r/  r0  )r[  r\  r]  r_  r^  .z.state.r   )r   prefixr   r   Fr   r   )r   r   r   r    r   r   rU  r  r  r   r%   loads	read_textr;  r<  r   r   r-   r!   r"   r#   r$   r&   renamer   r,   )r   r/  r0  r   r   	state_dir
state_file_dt_td_tz_ksttsbaseexistingadvancedkr0   tmpr2   s                      r3   _ensure_state_jsonrx  F  s   
 Y(*W4IOOD4O0y..JKKs|D			 9	:B ( "# "& D" 	zz*"6"6"6"HIH(D)Q<<(H4'+HV$1?H-.#D } 2#<<?&.qkDG2 3y>AgYg:NW]^GBYYr31 	=QIIdAE!<	=
		#z" $$g. 		
	= 	=  	IIcN 		s[   A/F   
F  /F+ F!F+  FFF($F+ +	G5G	GGGGc                    t        j                  d| t         j                        }|D ]E  }d|vr	 ddl}|j	                  |      }t        |t              rd|v r|d   c S 	 t        |      c S  y# t        $ r Y w xY w# t        $ r Y cw xY w)uN  task_desc 안의 fenced yaml block에서 allowed_resources를 파싱한다.

    ```yaml
    allowed_resources:
      paths: [...]
      forbidden_paths: [...]
      commands: [...]
      merge_policy: manual
      ttl_hours: 48
    ```
    형태의 블록을 찾아 dict로 반환한다.
    못 찾으면 None을 반환한다.
    r5  allowed_resources:r   Nallowed_resources)	r@  rF  rG  r9  r:  r;  r<  r   _parse_allowed_resources_regex)r*  rO  rP  r9  r   s        r3   _parse_allowed_resourcesr}    s     JJ6	299MM u,	>>%(D$%*=*E/00	1%88    		
  		s#   -A1"
B 1	A=<A= 	BB
yaml_blockc                 p   d| vryi }t        j                  d|       }|rs|j                  d      j                         D cg c]F  }|j	                         j                  d      r%t        j                  dd|      j	                         H c}|d<   n}t        j                  d	|       }|r`|j                  d      j                  d
      D cg c]2  }|j	                         s|j	                         j	                  d      4 c}|d<   ng |d<   t        j                  d|       }|rs|j                  d      j                         D cg c]F  }|j	                         j                  d      r%t        j                  dd|      j	                         H c}|d<   n}t        j                  d|       }|r`|j                  d      j                  d
      D cg c]2  }|j	                         s|j	                         j	                  d      4 c}|d<   ng |d<   t        j                  d|       }|rs|j                  d      j                         D cg c]F  }|j	                         j                  d      r%t        j                  dd|      j	                         H c}|d<   n}t        j                  d|       }|r`|j                  d      j                  d
      D cg c]2  }|j	                         s|j	                         j	                  d      4 c}|d<   ng |d<   t        j                  d|       }|r|j                  d      nd|d<   t        j                  d|       }	|	rt        |	j                  d            nd|d<   |j                  d      |S dS c c}w c c}w c c}w c c}w c c}w c c}w )uR   yaml 파서 없이 정규식으로 allowed_resources 키를 추출하는 fallback.rz  Nzpaths:\s*\n((?:\s*-\s*.+\n?)*)r3  -z^\s*-\s*["\']?|["\']?\s*$r   pathszpaths:\s*\[([^\]]*)\],z"'z(forbidden_paths:\s*\n((?:\s*-\s*.+\n?)*)forbidden_pathszforbidden_paths:\s*\[([^\]]*)\]z!commands:\s*\n((?:\s*-\s*.+\n?)*)commandszcommands:\s*\[([^\]]*)\]z!merge_policy:\s*["\']?(\w+)["\']?manualmerge_policyzttl_hours:\s*(\d+)   	ttl_hours)
r@  searchrB  r>  r?  r  r   r  intr   )
r~  r   paths_matchrL  inline_matchpfp_match	cmd_matchmp_match	ttl_matchs
             r3   r|  r|    sB   :-F ))=zJK $))!,779
zz|&&s+ FF/T:@@B
w yy!9:F0<0B0B10E0K0KC0P+,TUT[T[T]	&F7O !F7O yyDjQH !q)446%
zz|&&s+ FF/T:@@B%
 ! yy!CZP0<0B0B10E0K0KC0P)+,TUT[T[T]	&)F$% )+F$% 		>
KI "*557
zz|&&s+ FF/T:@@B
z yy!<jI0<0B0B10E0K0KC0P"+,TUT[T[T]	&"F: "$F: yy=zJH2:X^^A.F> 		/<I5>#iooa01BF;ZZ(46>$>s
%
)
"s9   ANN#!N	AN$N)+!N)AN.N33!N3r{  source_textsource_pathc                    t        t              dz  dz  }|j                  dd       ||  dz  }t        |j	                  dg             }d|vr|j                  d       t        |      }||d<   | t        j                         j                         |xs d|  d	t        j                  |j                               j                         |d
}t        ||       |S )u   allowed_resources를 immutable snapshot으로 저장한다.

    저장 경로: memory/capabilities/{task_id}.json
    forbidden_paths에 memory/capabilities/** 자동 보강 (봇 변조 방지).
    반환: snapshot 파일 경로
    rK   capabilitiesTr   rT  r  zmemory/capabilities/**memory/tasks/.md)r   captured_atr1  source_sha256r{  )r   r   r    listr   r   r<  r   r  	isoformathashlibsha256encode	hexdigestr   )	r   r{  r  r  snapshot_dirsnapshot_file	forbiddenenrichedsnapshot_datas	            r3   _save_capability_snapshotr    s     	?X->Ltd3 gYe#44M &**+<bABIy012%&H"+H ||~//1==	!= (:(:(<=GGI%M m]3r@   c           
         | j                         }|D ]  }|j                         }|j                  d      s%|t        d      d j                         }|sg c S |j	                  d      D cg c]#  }|j                         s|j                         % c}c S  d}g }|D ]  }|j                         }|r|j                  d      r |S |s,|j                  d      r#|j                  |dd j                                `|sg|j	                  d      D cg c]8  }|j                         st        j                  dd	|j                               : }}|D 	cg c]  }	|	s|		 c}	c S  |S |d
k(  sd} |S c c}w c c}w c c}	w )u  task_desc 문자열에서 affected_files 정보를 찾아 파일명 리스트를 반환한다.

    세 가지 형식을 지원한다:
    - 인라인: 'affected_files: server.py, app.js'
    - 섹션 목록: '## affected_files' 헤더 이후 '- path' 줄들
    - 섹션 인라인: '## affected_files' 헤더 이후 쉼표 구분 값 (괄호 코멘트 자동 제거)

    인라인 → 섹션 목록 → 섹션 인라인 순서로 시도한다.
    없으면 빈 리스트를 반환한다.
    zaffected_files:Nr  Fz##- r   z\s*\(.*?\)\s*$r   ## affected_filesT)r>  r?  r  r  r  r   r@  r   )
r*  linesrL  rH  valuer2   
collectingr   r   items
             r3   _parse_affected_filesr    s      "E  F::<01S!2356<<>E	',{{3'7E!1779AGGIEEF JF ::<""4( M ""4(hqrl0023KS>>Z]K^labcbibibk 12qwwyAll).7$77 M ,,J!$ M/ F  m7s$   -E9E9E>,'E>F!Faffected_filesr   c                    | s| S t        |      dz  dz  }|j                         st        j                  d|        | S 	 dt	        |      d|dg| d}t        j                  |ddd	
      }|j                  dk7  rBt        j                  d|j                   d|j                  j                         dd         | S t        j                  |j                        }t        |       }g }dD ]C  }|j                  |g       D ],  }	|	s|	|vs|j                  |	       |j!                  |	       . E |r't        j#                  dt%        |       d       | |z   S | S # t
        j&                  $ r t        j                  d       | cY S t        j(                  $ r%}
t        j                  d|
 d       | cY d}
~
S d}
~
wt*        $ r%}
t        j                  d|
 d       | cY d}
~
S d}
~
ww xY w)u  AST 의존성 스크립트를 호출하여 blast radius를 계산하고 affected_files를 보강한다.

    scripts/ast_dependency_map.py를 subprocess로 호출하여 직접 임포터(direct_importers)와
    테스트 파일(test_files)을 추출한 뒤 기존 목록에 중복 없이 병합한다.
    스크립트 실패·타임아웃·JSON 파싱 오류 시 원래 목록을 그대로 반환한다 (graceful fallback).
    scriptszast_dependency_map.pyu:   [ast-blast-radius] AST 스크립트를 찾을 수 없음: r   --root--filesz--jsonT<   capture_outputr;   timeoutr   u5   [ast-blast-radius] 스크립트 비정상 종료 (rc=r   N   )direct_importers
test_filesu)   [ast-blast-radius] blast radius 보강: +u   개 파일 추가uE   [ast-blast-radius] 타임아웃(60s) — 원래 affected_files 사용u'   [ast-blast-radius] JSON 파싱 실패: u!    — 원래 affected_files 사용u"   [ast-blast-radius] 예외 발생: )r   r   r   r   r"   r   run
returncoder   r?  r%   ri  r   r   r   r   r   r   r  TimeoutExpiredr   r   )r  r   
ast_scriptcmdr   r   rt  extrakeyr2   r   s              r3   _enrich_affected_files_with_astr  B  s    n%	14KKJST^S_`a-
O
 
 
 	
 !NNGHYHYGZZ]bhbobobububwx|y|b}a~ "!zz&--(~&5 	$CXXc2& $(*LLOLLO$	$ KKCCJ<O`ab!E))$$ ^_ @Cdef ;A3>_`asQ   A=E ?AE E AE E *G-
G-F<6G-<G-G("G-(G-c                 ,    | syt        d | D              S )u  affected_files 중 InsuRo FastAPI 서버 코드를 건드리는 항목이 있는지 검사한다 (task-2339).

    매칭 규칙:
    - 'server/main.py' 부분 매치
    - 'server/'로 시작
    - '/projects/InsuRo/server' 절대 경로 부분 매치
    Fc              3      K   | ]<  }d t        |      v xs) t        |      j                  d      xs dt        |      v  > yw)zserver/main.pyzserver/z/projects/InsuRo/serverN)r"   r  ).0r  s     r3   	<genexpr>z+_is_insuro_server_change.<locals>.<genexpr>  sJ       
SV	#kA(9(9)(DkHaehijekHkks   AA)anyr  s    r3   _is_insuro_server_changer    s$        r@   current_task_idc                 `   t         dz  dz  }|j                         sg S 	 t        |dd      5 }t        j                  |      }ddd       g }j                  di       }t        |       }|j                         D ]t  \  }	}
|	|k(  r|
j                  d	      d
k7  r!t        |
j                  dg             }||z  }|sDdj                  t        |            }|j                  d|	 d|        v |S # 1 sw Y   xY w# t
        $ r$}t        j                  d|        g cY d}~S d}~ww xY w)u  task-timers.json의 running 상태 task들과 affected_files 교집합을 확인한다.

    current_task_id는 제외(자기 자신).
    겹침 발견 시 경고 메시지 문자열 리스트를 반환한다.
    파일이 없으면 빈 리스트를 반환한다.
    rK   r   r   r   r   Nu@   [_check_affected_files_overlap] task-timers.json 읽기 실패: r   r   r   r  r   u   [파일 충돌 경고] u   (running)와 파일 겹침: )r   r   r   r%   r   r   r   r   r   r   r   joinsortedr   )r  r  r   r2   r   r   warningsr   affected_setr   	task_infoother_filesoverlapoverlap_lists                 r3   _check_affected_files_overlapr    s9    X%(::J	*cG4 	 99Q<D	  HHHWb!E~&L#kkm 	ko%=="i/)--(8"=>,99VG_5LOO5gY>Z[gZhij	k O+	  	  YZ[Y\]^	s4   D  C4D  4C=9D   	D-	D("D-(D-r  c                 (   | syt         j                  j                  dd      }|st        j	                  d       yddj                  |       z   }	 ddl}d| d	}t        j                  t        |d
      j                  d      }|j                  j                  ||ddi      }|j                  j                  |d       t        j                  dt        |        d       y# t         $ r"}t        j	                  d|        Y d}~yd}~ww xY w)u^   겹침 경고를 Telegram으로 발송한다. 실패해도 dispatch를 중단하지 않는다.NANU_BOT_TOKENr   uB   [overlap-telegram] ANU_BOT_TOKEN 미설정, Telegram 경고 스킵u%   ⚠️ affected_files 겹침 감지:

r   zhttps://api.telegram.org/botz/sendMessage)rL   r;   r   zContent-Typezapplication/json)r   headers
   )r  u9   [overlap-telegram] 겹침 경고 Telegram 발송 완료 (u   건)u+   [overlap-telegram] Telegram 발송 실패: )r#   environr   r   r   r  urllib.requestr%   dumpsr   r  requestRequesturlopenr   r  r   )r  	bot_tokenmessageurlliburlr   reqr   s           r3   _send_overlap_telegram_warningr    s    

3I[\689LLG	J,YK|Dzzgw?@GGPnn$$StnN`=a$bsB/OPST\P]^bcd JDQCHIIJs   BC& &	D/DD
task_levelc                 6    |dk  ryt        |       }|sd| dS y)u   task_level >= 2이고 affected_files가 없으면 경고 문자열을 반환한다.

    그 외에는 None을 반환한다.
    r   Nu   [경고] Lv.um    작업에 affected_files가 기재되지 않았습니다. 영향받는 파일 목록을 추가해 주세요.)r  )r*  r  filess      r3   _warn_missing_affected_filesr    s8    
 A~!),E:, 'A A	
 r@   >	   jqcatnpmnpxtsccurlgreppytestr   c                 X   d| v r| S t        j                  d|       }h d}|D cg c]5  }|j                  d      j                         |vs%|j                  d      7 }}t	        t
        j                  |            dd }t               }|D ]  }	 t        j                  ddd	d
ddd||g	ddd      }|j                  j                         j                  d      D ]4  }|s|j                  t        j                  j!                  ||             6  t)        |      dkD  r$t$        j'                  dt)        |       d       | S |r)ddj+                  d t-        |      D              z   }	| |	z  } | S c c}w # t        j"                  $ r t$        j'                  d|        Y w xY w)uW   task_desc에 ## affected_files 미기재 시, 백틱 코드 토큰 기반 자동 탐지r  z`([A-Za-z_]\w*(?:\(\))?)`>   idr  r   r  r  rx   r   typer   eventr8  r   propsrS  r  r  r   optionsz()Nr  r  z-rlz--include=*.pyz--include=*.tsz--include=*.tsxz--exclude-dir=node_modulesz--exclude-dir=.gitTr  r  u#   [auto-affected] grep 타임아웃:    z[auto-affected] u*   개 파일 감지 (>20) — 주입 안 함z$

## affected_files (auto-detected)
c              3   &   K   | ]	  }d |   yw)r  Nr>   )r  r2   s     r3   r  z._auto_inject_affected_files.<locals>.<genexpr>  s     GkUV"QCGks   )r@  rF  rstriprD  r  r<  fromkeysr   r   r  r   r?  r  r   r#   r   relpathr  r   r   r  r  r  )
r*  r   tokensCOMMON_FILTERtaffectedr  r   rL  sections
             r3   _auto_inject_affected_filesr    s   i' ZZ4i@FGM '-\0D0D0Fm0[ahhtn\F\$--'("-F uH 	^^ 02BDU-/C(  $$	F ++-33D9 HLL~!FGH  8}r)#h-8bcd ;diiGkZ`aiZjGk>kkW	= ]  (( 	NN@HI	s$   &E5E5AE:#1E::+F)(F)c                     d| v r| S t        j                  d|       }|s| S g }|D ]/  }|j                         d   }|t        v s|j	                  |       1 |rd}|dd D ]  }|d| dz  } | |z  } | S )	uH   검증 시나리오에서 실행 가능한 goal_assertions 자동 생성z## goal_assertionsz:`((?:grep|curl|pytest|python3|tsc|cat|jq|npx|npm)\s[^`]+)`r   z&

## goal_assertions (auto-generated)
N   z- `z`
)r@  rF  r  ALLOWED_COMMANDSr   )r*  r   r  safe_commandsr  
first_wordr   s          r3   _auto_generate_goal_assertionsr    s    y( zzWYbcH M &YY[^
))  %&
 = !$ 	&CSE~%G	&W	r@   c                    d}g }g }| D cg c]  }t        |      j                  d      s|! }}|sg g ddS |D ]  }t        |      }t        t        |      j                        }	t        |      j                  }
	 t        j                  d|d|	d|
gddd	
      }|j                  dk7  r6t        j                  d|
 d|j                   d|j                  dd         t        j                  |j                        }|j                  di       }|j                  |j                  dg              |j                  |j                  dg               t'        t(        j+                  |            }t'        t(        j+                  |            }||t-        |      t-        |      z   dS c c}w # t
        j                   $ r t        j                  d|
 d       Y t        j"                  $ r&}t        j                  d|
 d|        Y d}~d}~wt$        $ r&}t        j                  d|
 d|        Y d}~d}~ww xY w)u  affected_files의 .py 파일을 AST 분석하여 blast radius(영향 범위)를 반환한다.

    각 .py 파일에 대해 ast_dependency_map.py 스크립트를 호출하여
    direct_importers와 test_files를 수집한다.

    Args:
        affected_files: 영향받는 파일 경로 리스트
        workspace_root: 워크스페이스 루트 경로 (Path 또는 str)

    Returns:
        {
            "direct_importers": [...],  # 직접 임포터 목록 (중복 제거)
            "test_files": [...],        # 관련 테스트 파일 목록 (중복 제거)
            "total_affected": int,      # 총 영향 파일 수
        }
    z1/home/jay/workspace/scripts/ast_dependency_map.pyz.pyr   )r  r  total_affectedr   r  r  Tr  r  z[ast-blast-radius] u    분석 실패 (returncode=r   Nr  blast_radiusr  r  u)    분석 timeout(60s) 초과 — 건너뜀u    JSON 파싱 실패: u    예외 발생: )r"   endswithr   r   rx   r   r  r  r   r   r   r%   ri  r   r   extendr  r   r   r  r<  r  r  )r  r   r  r  r  r2   py_filesfpath	fpath_strroot_dirfilenamer   r   blastr   s                  r3   _get_ast_blast_radiusr  )  s3   " EJJ)DaSV__U-CDHD$&bANN PJ	tI--.	?''	P^^J(IxP#	F   A%)(3NvO`O`Naadekererswtwexdyz ::fmm,DHH^R0E##EII.@"$EFeiib9:'P8 DMM*:;<dmmJ/0J - ./#j/A G E0 (( 	fNN0
:cde## 	UNN0
:OPQsSTT 	PNN0
:J1#NOO	Ps<   F/F/>A#F4"A3F44,I#I5HI"IIbatch_idc                    t         dz  dz  }|j                         sdddg dS 	 t        |dd      5 }t        j                  |      }d	d	d	       j                  di       }|j                         D ci c]  \  }}|j                  d      | k(  s|| }}}t        |      }	|	dk(  rdddg dS d}
g }|j                         D ]2  \  }}|j                  dd      }|dv r|
dz  }
"|j                  |       4 |
|	k(  }||	|
|dS # 1 sw Y   xY w# t
        $ r)}t        j                  d
|        dddg dcY d	}~S d	}~ww xY wc c}}w )u  task-timers.json에서 batch_id가 일치하는 모든 task의 완료 여부를 반환한다.

    Returns:
        {
            "complete": bool,   # total > 0이고 모두 done/completed인 경우 True
            "total": int,       # 해당 batch_id task 수
            "done": int,        # done 또는 completed 상태 수
            "pending": list[str],  # 미완료 task id 목록
        }
    rK   r   Fr   )completetotaldonependingr   r   r   Nu9   [check_batch_completion] task-timers.json 읽기 실패: r   r  r   r   )r  	completedr3  )r   r   r   r%   r   r   r   r   r   r   r  r   )r  r   r2   r   r   r   r   r   matchedr  
done_countr  r   r  s                 r3   check_batch_completionr  h  sp    X%(::J!AqRHHI*cG4 	 99Q<D	  HHWb!E27++-d$488JCW[cCcw}dGdLEz!AqRHHJG  $(B'**!OJNN7#$ U"H 5*QXYY/	  	  IRSTRUVW!AqRHHI
 es@   D D	D 6EEDD 	EE :E E>
   app.pyasgi.pymain.pywsgi.py	celery.py	config.py	manage.py	server.pydispatch.pysettings.py)u   아키텍처u   리아키텍처u   전체 구조architecturec                     t         D ]  }|| v sdd| dfc S  |D ](  }t        |      j                  }|t        v s!dd| fc S  t	        |      }|dk\  rdd| dfS |dk\  rdd| d	fS y
)u  task_desc와 affected_files를 분석하여 권장 레벨과 이유를 반환한다.

    우선순위:
    1. 고레벨 키워드('아키텍처' 등) → (3, 이유)
    2. 핵심 파일 포함(server.py 등) → (2, 이유)
    3. affected_files >= 5 → (3, 이유)
    4. affected_files >= 3 → (2, 이유)
    5. 그 외 → (1, "")

    Returns:
        (level: int, reason: str)
    r
  'u   ' 키워드 감지r   u   핵심 파일 포함: r  zaffected_files u   개로 Lv.3 이상 권장u   개로 Lv.2 이상 권장)r3  r   )_HIGH_LEVEL_KEYWORDSr   rx   _CORE_FILESr  )r*  r  keywordr2   r  ns         r3   _estimate_task_levelr/    s     ( 8i7)#56778
  <7<<{"/z:;;< 	NAAv_QC'@ABBAv_QC'@ABBr@   c                 h    t        j                  d|       }|rt        |j                  d            S y)uu   task_desc에서 Lv.{숫자} 패턴을 찾아 정수로 반환한다.

    없으면 기본값 1을 반환한다.
    z	Lv\.(\d+)r3  )r@  r  r  rB  )r*  rA  s     r3   _parse_task_levelr1    s-    
 IIlI.E5;;q>""r@   levelskip_meetingc                    dddd}|j                  |d      }|dk  ryt        j                  dt        j                        }|j	                  |      sy|rt
        j                  d|         yt        dz  d	z  d
|  dz  }|j                         s-d|  d|j                   d}t
        j                  d|        |S 	 |j                  d      g d}	t        fd|	D              }
dv xs dj                         v }|
s|s,d|  d|j                   }t
        j                  d|        |S 	 y# t        $ r"}t
        j                  d|        Y d}~yd}~ww xY w)u  Lv.4 작업의 Agent 미팅 만장일치 결과 파일 존재 여부를 검증한다.

    Lv.4 판정: level이 'critical' 또는 'security'이고, task_desc에 Lv.4 패턴이 포함.
    미팅 파일 경로: memory/meetings/agent-meeting-{task_id}.md
    파일이 있으면 '만장일치' 또는 'unanimous' 키워드 포함 여부도 체크.
    부정 패턴('만장일치 실패', 'unanimous not', 'not unanimous')은 제외.

    Returns:
        WARNING 메시지 문자열, 또는 None (체크 통과/대상 아님)
    r   r
     normalcriticalsecurityNu%   (?:Lv\.4|레벨:\s*4|레벨:\s*Lv\.4)uB   [agent-meeting] --skip-meeting 플래그로 미팅 체크 스킵: rK   meetingszagent-meeting-r  u   ⚠️ Lv.4 작업(u1   )에 Agent 미팅 결과 파일이 없습니다: u,   . --skip-meeting 플래그로 스킵 가능.z[agent-meeting] r   r   )u   만장일치 실패zunanimous notznot unanimousu   만장일치하지c              3   &   K   | ]  }|v  
 y wr=   r>   )r  negr   s     r3   r  z'_check_agent_meeting.<locals>.<genexpr>   s     Gc3'>G   u   만장일치	unanimousuE   ) 미팅 파일에 만장일치 합의가 확인되지 않습니다: u-   [agent-meeting] 미팅 파일 읽기 실패: )r   r@  compilerC  r  r   r   r   r   rx   r   rj  r  rD  r-   )r   r*  r2  r3  _level_to_int_meetingdispatch_levellv4_patternmeeting_filemsgnegative_patternshas_negativehas_unanimousr   r   s                @r3   _check_agent_meetingrH    s    ()aQG*..ua8N **Er}}UKi( XY`Xabc x'*4yPS7TTL !'*[\h\m\m[n o9 : 	 	)#/0
L(('(:kG5FGG&'1S[GMMO5S}%gY.s  uA  uF  uF  tG  H  NN-cU34J  -   LFqcJKKLs   A1D6 6	E!?EE!zOptional[Path]c                    ddl }ddl}ddlm} |j	                  d|       st
        j                  d|        yt        dz  dz  dz  }|| z  }t        t        j                  j                  t        |                  }t        t        j                  j                  t        |                  }	 |j                  |       t        dz  dz  dz  }	g d}
|j                         j!                         }	 |j#                  ddd       |
D ]  \  }}||z  }|j)                         rt
        j+                  d|        4|	|z  }|j)                         st
        j                  d|        b	 |j-                  d      }|j/                  d|       j/                  d|      }|j1                  |d       t
        j+                  d|         |S # t        $ r t
        j                  d	| d
| d       Y yw xY w# t$        $ r%}t
        j'                  d| d|        Y d}~yd}~ww xY w# t$        $ r&}t
        j'                  d| d|        Y d}~Ld}~ww xY w)u  작업 3문서(계획서/맥락노트/체크리스트)를 자동 생성한다.

    템플릿 파일을 복사하고 YAML의 {task_id}, {date} 플레이스홀더를 치환한다.
    파일이 이미 존재하면 개별 파일 단위로 스킵(덮어쓰기 금지).

    Args:
        task_id: 작업 ID (예: task-1872, task-1872.1, task-1872_6.2)
        level:   작업 레벨 정수 (3 이상일 때만 호출됨)

    Returns:
        생성된 디렉토리 Path, 또는 검증 실패/에러 시 None
    r   N)datez^task-[\d._]+$u?   [3docs] task_id 검증 실패 (허용 패턴: ^task-[\d._]+$): rK   plansr   u   [3docs] path traversal 감지: u    는 u    하위가 아님prompts	templatesz	task-docs))zplan.template.mdzplan.md)zcontext-notes.template.mdzcontext-notes.md)zchecklist.template.mdzchecklist.mdTi  )r   r   modeu$   [3docs] 디렉토리 생성 실패: : u   [3docs] 이미 존재, 스킵: u!   [3docs] 템플릿 파일 없음: r   r   z	{task_id}z{date}u   [3docs] 생성: u   [3docs] 파일 쓰기 실패: )r@  shutilr   rJ  rA  r   r   r   r   r#   r   realpathr"   relative_torE  todayr  r    r-   r   r   r   rj  r*   
write_text)r   r2  _re_shutil_date
tasks_root
target_dirreal_target	real_roottemplate_dirrM  rS  exc	tmpl_nameout_nameout_path	tmpl_pathr   s                     r3   _create_task_docsrb    sV    & 99&0YZaYdef X%/'9Jg%Jrww''J89KRWW%%c*o67I	* y(;6DLI KKM##%E$EB
  ) M	8)??LL:8*EF 9,	!NN>ykJK	M))7);Gook7;CCHeTG':LL+H:67M" K  8U9+Ufgh   ;K=3%PQ$  	MLL9(2cUKLL	MsC   1G 4G; -AH,%G87G8;	H)H$$H),	I5IIc                  6   t         rt        	dddg g ddS t        t        t                    } | j	                         }t
        j                  d|j                  dd      t        |j                  dg             t        |j                  d	g                    |S )
u$  모든 running 세션의 토큰 사용량 체크 및 자동 대응.

    SessionResilience를 사용하여:
    - 70% 이상: WARNING 이벤트 기록
    - 85% 이상: CRITICAL 이벤트 + 세션 요약 저장 + resume 트리거

    Returns:
        check_all_sessions()의 결과 dict
    unavailableu7   SessionResilience 모듈을 사용할 수 없습니다.r   )r   r  checkedr  	criticalsnormalsr   u;   세션 체크 완료: checked=%d, warnings=%d, criticals=%dre  r  rf  )	_SESSION_RESILIENCE_AVAILABLE_SessionResiliencer"   r   check_all_sessionsr   r   r   r  )
resiliencer   s     r3   check_sessionsrl  U  s     ),>,F#P
 	
 $3y>BJ**,F
KKE

9a FJJz2&'FJJ{B'(	 Mr@   bot_key_hashc                     | sy	 t        j                  ddd|  gddd      }|j                  dk(  S # t        $ r Y yw xY w)	uM  봇의 cokacdir 프로세스가 실행 중인지 확인.

    pgrep -f로 'cokacdir.*{key_hash}' 패턴을 검색하여
    해당 봇의 cokacdir 프로세스 존재 여부를 판별한다.

    Args:
        bot_key_hash: 봇의 키 해시 (BOT_KEYS 값)

    Returns:
        프로세스 존재 시 True, 미존재 시 False
    Fpgrepz-fz
cokacdir.*Tr  r  r   )r   r  r  r   )rm  r   s     r3   _check_bot_processrp  t  s\     
dj78	
   A%% s   -3 	??delay_secondsc                 f    t        j                         t        |       z   }|j                  d      S )u9   현재 시간 + delay_seconds 후의 절대 시간 반환)secondsz%Y-%m-%d %H:%M:%S)r   r  r   r  )rq  r  s     r3   get_dispatch_timert    s'    =99A::)**r@   r   c                    | j                         sy	 t        | d      5 }t        j                  |      }ddd       t        j                  di       j                               }|syg }|D ]o  }|j                  dd      }t        j                  d|      }|r*|j                  t        |j                  d                   Wt         j#                  d	| d
       q |syt%        t'        |            }	|	d   }
t)        dt+        |	            D ]  }|	|   |	|dz
     z
  dk\  r |
dz   S |	|   }
  |
dz   S # 1 sw Y   xY w# t        j                  t
        f$ r}t        d|       d}~ww xY w)uD   task-timers.json에서 다음 ID 계산 (이상치 필터링 적용)r3  r   Nu5   task-timers.json이 손상됨. 수동 복구 필요: r   task-r   ^(\d+)u   task ID 파싱 실패 ()r     )r   r   r%   r   r   r-   r   r  r   r   r*   r@  rA  r   r  rB  r   r   r  r   ranger  )r   r2   r   r   rt  numsr  rs  msorted_numsfiltered_maxis               r3   _compute_next_id_from_timersr    s   X*c" 	 a99Q<D	 
 DHHWb)..01HD ;yy"%HHY%KKAGGAJ(NN4QCq9:;  T#Kq>L1c+&' &q>KA..$6 ! #1~&
 !=	  	   '* XRSTRUVWWXs-   E D6E 6E ;E E/E**E/c                  ~   t         dz  dz  } t         dz  dz  }t         dz  dz  }|j                  j                  dd       t        |d      }	 t	        j
                  |t        j                         d}|j                         rR	 t        |j                         j                               }|d	k  r"t        j                  d
|       t        |       }nd}nt        |       }|rKt        |       }||k  rt        j                  d||       |}n!||z
  dk\  rt        j                  d||       |}d| }|j!                  t#        |dz                | j                         r,	 t        | d      5 }t%        j&                  |      }	ddd       ndi i}	d	vri |	d<   dt+        j,                         j/                         d|	d   |<   | j                  j                  dd       	 t        | d      5 }t%        j0                  |	|dd       ddd       |t	        j
                  |t        j4                         |j7                          S # t        t        f$ r$ t        j                  d       t        |       }Y w xY w# 1 sw Y   xY w# t$        j(                  t        f$ r'}
t        j                  d|
        di i}	Y d}
~
3d}
~
ww xY w# 1 sw Y   xY w# t        $ r"}
t        j3                  d|
        Y d}
~
d}
~
ww xY w# t	        j
                  |t        j4                         |j7                          w xY w)uT   자동 태스크 ID 생성 (카운터 파일 기반, 파일 락으로 중복 방지)rK   r   .task-counterr   Tr   r   Fr   uB   카운터 파일 값 비정상 (%d), task-timers.json에서 복구u6   카운터 파일 손상, task-timers.json에서 복구u2   카운터(%d) < timers 최대(%d), 보정합니다ry  uW   카운터(%d)가 timers 최대(%d) 대비 1000 이상 큼, 이상치로 보정합니다rv  r3  r   Nu.   task-timers.json 재읽기 실패, 초기화: r   reserved)r   reserved_atr   r   u    task-timers.json 쓰기 실패: )r   r   r    r   r   r   r   r   r  rj  r?  r   r   r  rE  r-   rT  r"   r%   r   r   r   r  r  r&   r   r   r   )r   counter_filer   	lock_filefrom_counternext_num
timers_maxnext_idr2   
timer_datar   s              r3   generate_task_idr    s   X%(::Jx'/9L),??Nt<^S)I<Iu}}-  	D|557==?@q=NN#giqr;JGH#'L 4J?H 5jAJ*$SU]_ij%J&$.mow  zD &($ 	HqL 12 +*c* .a!%1J. "2J*$"$Jw2<X\\^MeMeMg'h
7G$t<	Aj#& G!		*aeAFG
 Iu}}-_ ( DWX7
CD6. .(('2 +!OPQsST%r]
+G G 	ALL;A3?@@	A
 	Iu}}-s   6L AI BL "J	 .I=J	 AL #K /K	K L /I:6L 9I::L =JJ	 	K	"K>L K		L KK 	L!K>9L >LL 6L<c                    t         dz  dz  }t         dz  dz  }| j                  dd      }t        j                  d|      }|syt	        |j                  d            }t        |d	      }	 t        j                  |t        j                         d
}|j                         r(	 t	        |j                         j                               }||k\  r8|j                  t!        |dz                t"        j%                  d||dz   |        t        j                  |t        j&                         |j)                          y# t        t        f$ r d
}Y w xY w# t        j                  |t        j&                         |j)                          w xY w)uS   외부 지정된 task_id가 카운터보다 크면 카운터를 업데이트한다.rK   r  r   rv  r   rw  Nr3  r   r   u2   카운터 동기화: %d → %d (외부 task_id=%s))r   r*   r@  rA  r  rB  r   r   r   r   r   rj  r?  rE  r-   rT  r"   r   r   r   r   )r   r  r   rs  rA  	given_numr  currents           r3   _sync_counter_if_neededr    s@   x'/9L),??N??7B'DHHY%EEKKN#I^S)IIu}}- l446<<>? ##C	A$67KKLgW`cdWdfmnIu}}- (  	Iu}}-s0   +6E "'D; 	=E ;EE EE 6Fbase_task_idforcec                    | j                  d      s	dd|  ddS t        t              dz  dz  |  dz  }|j                         sdd	| dS t        t              dz  d
z  |  dz  }|j                         rdddS t	        j
                  dd|       }t        t              dz  d
z  |  dz  }d}|j                         r*	 t        |j                  d      j                               }t        t              dz  dz  }	d}
|	j                         rt	        j                  dt	        j                  |       d      }|	j                         D ]B  }|j                  |j                        }|s!t        |j!                  d            }||
kD  sA|}
D t#        ||
      dz   }|dk\  r|s	dd| ddS | d| }t        t              dz  dz  | dz  }|j                         s|}t        t              dz  dz  | dz  }	 |j                  d      }d|  d| d| d}|j%                  ||z   d       t        t              dz  d
z  | dz  }	 |j&                  j)                  d d !       |j%                  t+        |      d       d#||d$S # t        t        f$ r d}Y w xY w# t        $ r}dd| dcY d}~S d}~ww xY w# t        $ r"}t,        j/                  d"|        Y d}~bd}~ww xY w)%u   --resume 옵션 처리: base task 상태 확인 + 자동 채번 + task 파일 복사.

    Returns:
        성공: {"status": "ok", "new_task_id": "task-XXXX+N", "retry_count": N}
        실패: {"status": "error", "message": "..."}
    rv  r   u#   잘못된 task ID 형식입니다: u    (task- 접두사 필요)r   r  rK   r   r  u*   task 파일이 존재하지 않습니다: eventsz.doneu=   이미 완료된 작업입니다. 새 task로 위임하세요\+\d+$r   z.retry_countr   r   r   ^z\+(\d+)\.md$r3  r
  uE   3회 이상 재시도. 계속하려면 --force 추가 (현재 retry #rx  +u   > **재시도**: r   	 (retry #u7   )
> **재시도 사유**: 이전 세션 실패/중단

u   task 파일 복사 실패: NTr   u"   retry_count 파일 쓰기 실패: ok)r   new_task_idra  )r  r   r   r   r@  r   r  rj  r?  rE  r-   r?  escapeiterdirrA  rx   rB  maxrT  r   r    r"   r   r   )r  r   r*  r  	task_file	done_filebase_without_plusretry_count_filefile_retry_count	tasks_dirmax_sibling_nsibling_patternr2   r|  r.  new_retry_countr  origin_task_filenew_task_fileoriginal_content
retry_metar   new_retry_count_files                          r3   _resolve_resumer     s    ""7+!0ST`Saaz.{|| Y(*W4,s7KKI!0Z[dZe.fgg Y(*X5<.8NNI!.mnn y"l; I1H<,|?\\ 	!"#3#=#=w#=#O#U#U#WX
 Y(*W4IM**"))4E*F)G|%TU""$ 	&A%%aff-A
O}$$%M	& *M:Q>O !E!0u  wF  vG  GH  /I  J  	J ''q(9:K I1G;AR@SSV>WW""$$Oh.8k]#;NNMQ+55w5G~U;-yHY ZF G 	 	  .>!> Q
  	?X5@k]R^C__A##))$)F''O(<w'O
 ;WWi G$ 	! 	!P  Q!0KA3.OPPQ  A;A3?@@AsH   )J 4J* 9K J'&J'*	K3K :K K	K3K..K3
project_idc                 \   |s| S t         dz  dz  | dz  }|j                         s| S 	 |j                  d      }g d}g }|j	                         D ]  }|j                         j                         }|D ]\  }||v sd|v sd|v s	d	|v sd
|v s|j                         j                  d      }	|	r"|	j                  d      s|j                  |	          |rCt        t        j                  |            }dj                  |dd       }
d| d|
 d| d}| |z  } | S # t        $ r | cY S w xY w)u
  프로젝트 맵에서 네비게이션/라우팅 파일 목록을 추출하여 task_desc에 자동 삽입.

    project_id가 지정된 경우, project-maps/{project_id}.md에서
    routes, navigation, config 관련 파일을 찾아 경고 메시지를 주입.
    rK   project-mapsr  r   r   )route
navigationnavsidebarmenulayoutrg  u   ├u   │u   └u   ├─│└ #r   Nr  u5   

## ★ 프로젝트 네비게이션 구조 주의 (u+   )
네비게이션/라우팅 관련 파일: uy   
- 이 파일들은 페이지 추가/수정 시 반드시 함께 확인하세요
- 프로젝트 맵: memory/project-maps/u   .md 참조
)r   r   rj  r   r>  rD  r?  r7  r  r   r  r<  r  r  )r*  r  map_pathmap_contentnav_patterns	nav_filesrL  
line_lowerpatterncleaned
files_listr   s               r3   _inject_project_map_contextr  v  su    8#n4*S7IIH??(('(:
 OLI&&( 	ZZ\'')
# 	G*$#*;u}PUY]P]afjnan**,--o>7#5#5c#:$$W-		 y12	YYy"~.
Ej\ R88B| D77Al,P 	 	W	;  s   D D+*D+chain_id	task_typec           	      |    | t         vr#t        d|         t        j                  d       t	        | ||||||      S )uA   팀장에게 전달할 프롬프트 생성 (공통 모듈 위임)u   Error: 알 수 없는 팀 ID: r3  r  r  r  )r5   printsysexit_build_team_prompt)r   r*  r   r2  r  r  r  s          r3   r6   r6     sD     i.wi89)UzH`i r@   c                    t         dz  dz  |  dz  }|j                         st        j                  d|        y|j	                  d      }t        |d      }	 t        j                  |t        j                         t        |dd	
      5 }t        j                  |      }ddd       j                  dd      }|t        |j                  dg             k  re|d   |   }	|	d   D ]U  }
|
d   |k(  s|
d   dv s|
j                  d      &||
d<   t        j                         j                         |
d<   d|
d<    n t        |dd	
      5 }t        j                   ||dd       ddd       t        j#                  d|  d| d|        t        j                  |t        j(                         |j+                          y# 1 sw Y   "xY w# 1 sw Y   ixY w# t$        $ r"}t        j'                  d|        Y d}~td}~ww xY w# t        j                  |t        j(                         |j+                          w xY w)ug   chain.json 파일에서 해당 팀의 pending task에 task_id, dispatched_at, status=in_progress 기록rK   chainsrT  u   chain 파일 없음: Nz.lockr   r   r   r   current_phase_idxr   phasesr   r   r   )r  in_progressr   dispatched_atr  Fr   r   u   chain 업데이트: chain=, team=
, task_id=u   chain 업데이트 실패: )r   r   r   r   with_suffixr   r   r   r   r%   r   r   r  r   r  r  r&   r   r   r   r   r   )r  r   r   
chain_file	lock_pathr   r2   r   current_idxrY  taskr   s               r3   _update_chain_taskr    s   X%0hZu3EEJ.zl;<&&w/I9c"GGU]]+*cG4 	 99Q<D	  hh2A6TXXh344N;/Eg 	LG+X*DD+3&-DO,4LLN,D,D,FD)%2DN	 *cG4 	=IIdAE!<	=0
''*U\T]^_ 	GU]]+/	  	  	= 	=  821#6778 	GU]]+sn   2G G"AG 3G ;G A G G'&G GG GG 	H$H<H	 HH	 	6H?c                 B   t         dz  dz  }t         dz  dz  }|j                  j                  dd       d}	 t        |d      }t	        j
                  |t        j                         |j                         s:	 |6	 t	        j
                  |t        j                         |j                          yyt        |dd	
      5 }t        j                  |      }ddd       j                  di       }|j                  |       }|St        j                  d|  d       	 |6	 t	        j
                  |t        j                         |j                          yy|j                  d      }|dvrVt        j                  d|  d| d       	 |6	 t	        j
                  |t        j                         |j                          yy|| = ||d<   t        |dd	
      5 }t        j                   ||dd       ddd       t        j#                  d|  d| d       |6	 t	        j
                  |t        j                         |j                          yy# t        $ r Y yw xY w# 1 sw Y   xY w# t        $ r Y yw xY w# t        $ r Y yw xY w# 1 sw Y   xY w# t        $ r%}	t        j%                  d|  d|	        Y d}	~	d}	~	ww xY w# t        $ r Y yw xY w# |E	 t	        j
                  |t        j                         |j                          w # t        $ r Y w w xY ww xY w)uO  task-timers.json에서 해당 task_id의 reserved 또는 running 엔트리 삭제.

    dispatch() 실패 시 호출하여 orphan 항목을 정리한다.
    task-timer 자동 시작 도입으로 reserved뿐 아니라 running 상태도 정리 대상.
    에러 발생 시 로깅만 하고 무시 (본래 에러 반환이 우선).
    rK   r   r   Tr   Nr   r   r   r   r   z_cleanup_task: task_id u    없음, 정리 불필요r   )r  r   z_cleanup_task: u$    상태가 정리 대상이 아님 (u   ), 건너뜀Fr   r    u    엔트리 삭제 완료u   _cleanup_task 실패 (task_id=r   )r   r   r    r   r   r   r   r   r   r   r   r%   r   r   r   r   r&   r   r   )
r   r   r   r   r2   r  r   r   r   r   s
             r3   _cleanup_taskr    s    X%(::J),??Nt<G&~s+GU]]+  "8 GU]]3 5 *cG4 	&1J	& w+YYw'
LL27);TUV$ GU]]3 ! )00LL?7)3WX^W__klm GU]]3  'N#
7*cG4 	CIIj!%B	C 	ogYax7OPQ
 GU]]3   =	& 	&<  9 	C 	C
  G5gYc!EFFG  	 GU]]3 	 s   A J =4I 3J IAJ !4I+ 1J 4I: J J	2$J 4K 	III(#J +	I76I7:	JJ	JJ 	KJ>9K >KK 	KKL4LL	LLLLc                 f    t        |       dkD  r#t        j                  dt        |        d       yy)uR   지시서 크기가 3000자 이상이면 Phase 분리 권고 WARNING 로그 출력i  u#   [large-task-desc] 지시서 크기 u   자 (3000자 초과). Phase 분리를 권장합니다. 한 세션에서 모든 작업을 처리하면 성능이 저하됩니다.N)r  r   r   )r*  s    r3   _warn_large_task_descr    s8    
9~1#i.1A By y	
 r@   generated_idc                 t    ||k7  ryt        j                  d|       rt        j                  d| d|        yy)u  Phase 작업 감지 시 --task-id 미지정 경고.

    task_desc에서 Phase 패턴(Phase N, phase N, Phase N.N)을 감지하고,
    task_id가 자동 생성(generated_id와 동일)이면 WARNING 로그를 출력한다.
    작업을 차단하지 않는다 (경고만).
    Nz(?<![a-zA-Z])[Pp]hase\s+\duT   ⚠️ Phase 작업 감지됨. --task-id를 수동 지정하세요. (예: --task-id u   _N.N) 자동 생성 ID: )r@  r  r   r   )r*  r   r  s      r3   _warn_phase_without_task_idr    sH     ,	yy.	:&i (!!(	+	
 ;r@   c           
         t        j                  t        t                    dz   }t        j                  ||       }g }d}g }|D ]  }t
        j                  j                  |      s#	 t
        j                  j                  |      }||z  }|dkD  sNt
        j                  j                  |      }t        |dz  d      }	t        |dz        }
|j                  ||	|
f       |j                  d| d|	 d	|
 d
        t        |dz  d      }|t        kD  r!|j                  dd| dt        dz   d       n,|t        dz  kD  r |j                  dd| dt        dz   d       |rdj                  |      S dS # t        $ r Y Aw xY w)uh   task_desc에서 참조 파일을 추출하여 대용량 파일 경고 + 총 크기 한도 경고 반환.z[a-zA-Z0-9/_.\-]+r   ia  i   r3  g      @u    ⚠️ 대용량 참조 파일:  (zKB, ~u#   tok) → offset/limit 사용 필수u   🚨 참조 파일 총 크기 u   KB (한도: ug   KB 초과!) — 전체 읽기 금지. 반드시 offset/limit 분할 읽기 또는 요약 파일 참조.gffffff?u    ⚠️ 참조 파일 총 크기 uD   KB의 70% 초과) — offset/limit 사용을 강력 권장합니다.r  N)r@  r  r"   r   rF  r#   r   isfilegetsizer-   basenameroundr   MAX_REF_FILE_TOTAL_BYTESinsertr  )r*  r  r  r  
total_sizelarge_filesr   sizer  size_kbestimated_tokenstotal_kbs               r3   _check_referenced_file_sizesr  *  s   iiI'*>>GJJw	*EHJK ww~~d#	77??4(D 	d
%<ww''-HD4K+G$TCZ0'3CDEOO28*BwiuM]L^  _B  C$ Z$&*H,,,XJlC[_cCcBd ej k	

 
.4	4.xjE]aeEeDf gA B	
 #+499X447  		s   (E99	FFc                    | dk(  ry| j                  d      sy| j                  dd      }t        dz  |z  dz  dz  }|j                         sd	|  d
S t        j
                  j                  t        |            sd	|  dS t        j
                  j                  t        |            }t        j
                  j                  ddd      }|j                  |      s	d	|  d| dS y)u   팀의 QC 환경(verifiers symlink) 사전 검증.

    dev8은 독립 구조 허용으로 예외.
    비정상이면 WARNING 메시지 반환 (블로킹 아닌 경고).
    2026-04-17 에이전트 미팅 합의 항목 3.
    rn   Nr  r  r   re   r{   	verifiersu   ⚠️ QC 환경 경고: u*   의 verifiers 디렉토리가 없습니다uY   의 verifiers가 symlink가 아닙니다. shared/verifiers로의 symlink여야 합니다.shareduP   의 verifiers symlink가 shared/verifiers를 가리키지 않습니다 (실제: rx  )r  r*   r   r   r#   r   islinkr"   rQ  r  r  )r   
team_shortverifiers_path	real_pathexpected_suffixs        r3   _check_team_qc_envr  U  s     + e$"-J(:5<{JN  "*7)3]^^77>>#n-.'y  1N  O	
   ^!45Iggll7HkBOo.'y 1FFO[PQS	

 r@   	thresholdc                    g }| D ]  }|}t         j                  j                  |      s-t         j                  j                  t	        t
              |      }t         j                  j                  |      sq	 t        |ddd      5 }t        d |D              }ddd       |kD  r|j                  ||d        |S # 1 sw Y   &xY w# t        $ r Y w xY w)u   affected_files 중 threshold줄을 초과하는 대형 파일 목록 반환.

    Returns:
        list of dict: [{"path": "...", "lines": 3036}, ...]
    r   r   ignore)r   errorsc              3       K   | ]  }d   ywr3  Nr>   )r  r	  s     r3   r  z#_get_large_files.<locals>.<genexpr>  s     qs   N)r   r  )r#   r   isabsr  r"   r   r  r   sumr   r-   )r  r  largefilepathresolvedr2   
line_counts          r3   _get_large_filesr  z  s     E" ww}}X&ww||C	NH=Hww~~h'	hghG /1 A.
/I%hDE L/ /  		s*   7C
B>!C
>C	C

	CCc                      j                         g d}d}dv xs t        t        j                  d|              xsg t        t        j                  | d             xsC t        t        j                  d             xs" t        t        j                  d|             }|rt	         fd|D              rd	}|sy
t
        dz  dz  }|j                         st        j                  d|       y
	 |j                  d      }t        j                  d       |S # t        $ r }t        j                  d|       Y d
}~y
d
}~ww xY w)u=  지시서 키워드 기반 플랫폼 규칙 자동 주입.

    네이버 블로그 관련 키워드가 감지되면 config/naver-blog-rules.md 내용을 반환.
    향후 티스토리 등 다른 플랫폼 규칙도 동일 패턴으로 확장 가능.

    Returns:
        주입할 규칙 텍스트 또는 None
    )u   검증u   폐기u   리팩토링u   마이그레이션u   삭제
deprecatedu   코드 검토u   셀프체크u	   일원화verifyrefactoru   테스트 실행u   코드 분석u   버그 수정u	   디버깅r   u   코드 리뷰uM   (작성|발행|글\s*쓰기|글쓰기|포스팅|올리기|publish|write|post)zblog-publish-naveru   네이버\s*블로그.{0,20}u   .{0,20}네이버\s*블로그u%   네이버.{0,10}(블로그\s*)?발행znaver.{0,10}blog.{0,20}c              3   2   K   | ]  }|v xs |v   y wr=   r>   )r  kwr*  
task_lowers     r3   r  z)_inject_platform_rules.<locals>.<genexpr>  s"     "c22?#FbJ6F#F"cs   FNr  znaver-blog-rules.mdu+   [naver-blog-rules] 규칙 파일 없음: %sr   r   uB   [naver-blog-rules] 네이버 블로그 규칙 자동 주입 완료u2   [naver-blog-rules] 규칙 파일 읽기 실패: %s)rD  r=  r@  r  r  r   r   r   r   rj  r   r-   )r*  _EXCLUDE_KEYWORDS_WRITE_INTENTnaver_blog_detected
rules_pathrules_contentr   r
  s   `      @r3   _inject_platform_rulesr    sO    "J* eM 	
* 	W		;M?KYWX	W 		m_,JKYWX		W 		BINO	W 		8H*UV  s"cQb"cc# X%(==JDjQ",,g,>XY KQOs   :(D# #	E,EEc                      g d}g d}t         fd|D              }t         fd|D              }|r|r|dk7  rt        j                  d       yyyy)uB   리서치+구현 혼합 지시서 감지 시 WARNING 로그 출력)u	   리서치u   조사u   파악u   현재 상태u   가능한지u   방법 조사u
   API 문서u   HTML 구조u	   셀렉터u   외부 서비스u   인증 방식2FAOTPresearchinvestigatefeasibility)
u   구현u   구축u   코딩u   코드 작성u   테스트 작성u   파이프라인	implementbuild	PublisherPipelinec              3   &   K   | ]  }|v  
 y wr=   r>   r  r	  r*  s     r3   r  z*_warn_research_impl_mix.<locals>.<genexpr>  s     C2rYCr=  c              3   &   K   | ]  }|v  
 y wr=   r>   r  s     r3   r  z*_warn_research_impl_mix.<locals>.<genexpr>  s     ;r2?;r=  r  u   [research-impl-mix] 지시서에 리서치와 구현이 혼합되어 있습니다. Phase 분리를 권장합니다. (specs/research-impl-separation.md 참조) 세션 경량화: 리서치 후 /compact 실행 후 구현에 진입하세요.N)r  r   r   )r*  r  research_keywordsimpl_keywordshas_researchhas_impls   `     r3   _warn_research_impl_mixr"    s\    $M C1BCCL;];;HY*%<]	
 &=|r@   c           	         t         dz  }|j                         st        j                  d       y	 |j	                  d      }|j                  d      D cg c]  }d|v s|j                          }}t               }d	}|D ]e  }	t        j                  d
|	      D ]J  }
|
|v rt         |
z  }|j                         s"	 |j	                  d       |j                  |
       |dz  }L g g d}|j                         }|D cg c]	  }||v s| }}t        |      }|rHd}||vrBt         |z  }|j                         r)	 |j	                  d       |j                  |       |dz  }d	}	 d	dlm}  |       }	 |j!                  |dd      }t#        |      }|j%                          	 t        j)                  dt#        |       d| d| d       | j+                  d      r|rt        j-                  d|  d|        yyy# t
        $ r"}t        j                  d|        Y d}~yd}~ww xY wc c}w # t
        $ r&}t        j                  d|
 d|        Y d}~d}~ww xY wc c}w # t
        $ r&}t        j                  d| d|        Y d}~.d}~ww xY w# |j%                          w xY w# t&        $ r#}t        j                  d|        Y d}~1d}~ww xY w)u   MEMORY.md ★ 항목 확인 및 디자인 작업 dev팀 위임 위반 감지.

    파일 I/O 실패 시 예외를 전파하지 않고 debug 로그만 남긴다.
    z	MEMORY.mdu5   [memory_check] MEMORY.md 파일 없음, 체크 스킵Nr   r   u(   [memory_check] MEMORY.md 읽기 실패: r  u   ★r   z\[.*?\]\((.*?\.md)\)r3  u,   [memory_check] 링크 파일 읽기 실패: u    – )		   디자인   배너	   이미지u	   포스터u   일러스트bannerimageposterr}   z"feedback_design_team_routing_v2.mdu/   [memory_check] 피드백 파일 읽기 실패: )MemoryIndexerr
  r8  )limitlayeru4   [memory_check] FTS5 Layer 1 검색 실패 (무시): u   [memory_check] ★ u   개 확인, 관련 피드백 u"   개 확인, FTS5 관련 메모리 u   건r  uT   ⚠️ [memory_check] 디자인 작업을 dev팀에 위임하려고 합니다! team=u   , 감지 키워드: )_MEMORY_BASE_PATHr   r   r   rj  r-   r  r?  r   r@  rF  r   rD  r=  utils.memory_indexerr*  r  r  r   r   r   r  r   )r   r*  memory_file	memory_mdr   rL  
star_itemsloaded_filesmatched_countr  fnameref_pathdesign_keywordsr
  r	  detecteddesign_keywords_foundextra_fname
extra_path	fts_countr*  _indexerfts_resultss                          r3   _check_memory_before_dispatchr>  	  s   
 $k1KLM))7);	 ,5??4+@R4ETM$**,RJR LM aZZ 7> 
	aE$(50H a&&&8 $$U+!Q&M
	aa O"J,Arj0@AHA N :l**[8J  "j(('(: $$[1!Q&M
 I
Q6 ?	"//)1G/LKK(INN KK
c*o..KM? [##,+S	2 % %:efmen  oC  DL  CM  N	
 &; }  ?sCD
 S  aLL#OPUwV[\][^!_``a B  jLL#RS^R__defdg!hiij NN QKA3OPPQs   H 	H/ H/>(H4	I&I&?(I+ *J2 8J J2 	H,
H''H,4	I#=II#+	J4JJJ//J2 2	K;KKskip_brainstormingc                    |rt         j                  d       ydddd}|j                  |d      }|dk  ryg d}|j                         }|D cg c]  }|j                         |v s| }	}|	syt        dz  d	|  d
z  }
|
j                         rt         j                  d|
        yt         j                  d| d|	 d       yc c}w )u*  Lv.3+ UX 작업에 brainstorming 사전 실행 여부를 확인한다.

    skip_brainstorming=True이면 즉시 반환한다.
    level이 3 미만이면 즉시 반환한다.
    UX 관련 키워드가 없으면 즉시 반환한다.
    brainstorming 파일이 없으면 warning을 남긴다.
    u6   [brainstorming-gate] --skip-brainstorming으로 스킵Nr   r
  r5  r6  )UXUIr$  u   사용자 경험u   인터페이스u   레이아웃u   화면u	   페이지u   컴포넌트u	   프론트frontendr:  zbrainstorming-r  u5   [brainstorming-gate] brainstorming 파일 확인됨: u   ⚠️ [brainstorming-gate] Lv.u9    UX 작업에 brainstorming 미실행. 감지 키워드: uf   . /brainstorming 스킬 실행 후 재위임하세요. --skip-brainstorming 플래그로 스킵 가능)r   r   r   rD  r-  r   r   r   )r   r*  r2  r?  	level_map	level_intux_keywordsr
  r	  detected_keywordsr   s              r3   _check_brainstorming_gaterH  R	  s     MN!;IeQ'I1}K "J&1NRXXZ:5MNNz)nWIS,IID{{}KD6RS
NN
)) 5./ 0o	p Os   C'Cc                     t         r*t        r$	 t        j                  d      } | rt        |       S 	 t
        r<t        6t        t              }|j                         }|j                  di       } | r| S t        dz  dz  }|j                         r?	 t        |dd      5 }t        j                  |      }ddd       j                  di       S i S # t        $ r Y w xY w# 1 sw Y   ,xY w# t        j                  t        f$ r Y i S w xY w)	uE   config/constants.json에서 logical_teams 도메인 매핑을 로드.logical_teamsNr   r  r  r   r   r   )_CONFIG_AVAILABLE_cfgget_constantr<  r   r   r   r   _load_constantsr   r   r   r%   r   r   r-   )r   mgrr   r  r2   r   s         r3   _load_logical_teamsrP  x	  s    T	&&7FF|#  !2!>y9'')	3M ),<<N	ncG< $yy|$88OR00 I+  		 $ $ $$g. 	I	s;   !C C+ C4C+ 	CCC($C+ +DDc                 x    t         r%t        t        t              j                         S t	               }|syd}d}|j                         D ]b  \  }}|dk(  r|j                  dg       }|j                  dg       }t         fd|D              rEt         fd|D              }||kD  s_|}|}d |dkD  r|S dS )	u   task 설명에서 키워드 매칭 → 적합한 논리적 팀 추천.

    Returns:
        추천 팀 ID (예: "design") 또는 None (매칭 없음)
    Nr   r   	compositekeywordsanti_keywordsc              3   &   K   | ]  }|v  
 y wr=   r>   )r  akr*  s     r3   r  z _suggest_team.<locals>.<genexpr>	  s     72rY7r=  c              3   ,   K   | ]  }|v sd   ywr  r>   r  s     r3   r  z _suggest_team.<locals>.<genexpr>	  s     <"B)OA<s   	)	r   r   r   suggest_teamrP  r   r   r  r  )	r*  rJ  	best_team
best_scorer   r  rS  rT  scores	   `        r3   _suggest_teamr\  	  s     !2!> 	:GG	RR ()M#IJ(..0  k!J3$jj"= 777 <<<:JI! $ #Q90D0r@   override_routingc                    t         rTt        Nt        t              j                  | ||      }|y|j	                  dd      }|j                  d      sd|z   }|S | t        v ryt        |      }|y|rt        j                  d|  d| d	       yt               }|j                  |i       j                  d
d      }d| d| dS )u  dev팀에 논리적 팀 소관 작업이 위임되면 WARNING 반환.

    Args:
        team_id: 위임 대상 팀 ID
        task_desc: 작업 설명
        override_routing: True이면 라우팅 경고를 무시

    Returns:
        경고 메시지 또는 None (문제 없음)
    Nr   u(   override_routing=True를 사용하세요u%   --override-routing을 추가하세요u   ⚠️u   ⚠️ z[routing-override] u   에 u1    소관 작업 위임 (--override-routing 적용)descriptionr   u   ⚠️ 이 작업은 --team u   이 적합합니다 (u9   ). 계속하려면 --override-routing을 추가하세요.)r   r   r   validate_routingr*   r  DYNAMIC_BOT_TEAMSr\  r   r   rP  r   )r   r*  r]  rD  	suggestedrJ  r_  s          r3   _validate_team_routingrc  	  s     !2!>y9JJ7T]_op;kkDFmn~~h'c/C
 ## i(I,WIT)Duvw')M##Ir266}bIK
&yk1F{m  TQ  	Rr@   	teams_strc                    | j                  d      D cg c]#  }|j                         s|j                         % }}t        |      dk  rt        d| d      t        |      t        kD  rt        dt         dt        |       d      |D cg c]  }|t
        vs| }}|rt        d| d	t        t
                     t        |      t        t        |            k7  rt        d
|       |S c c}w c c}w )u   composite teams 문자열 파싱 + 유효성 검증.

    Args:
        teams_str: 쉼표 구분 팀 ID 문자열 (예: "marketing,design")

    Returns:
        검증된 팀 ID 리스트

    Raises:
        ValueError: 유효하지 않은 입력
    r  r   u<   composite는 2개 이상의 팀이 필요합니다 (입력: rx  u   최대 u   개 팀까지 허용 (입력: u   개)u   알 수 없는 팀 ID: u   . 허용 목록(소문자): u   중복 팀 ID: )r  r?  r  rE  r9   r8   r  r   )rd  r  re   r   s       r3   _validate_composite_teamsrf  	  s     !* 4B1	QWWYBEB
5zA~WXaWddefgg
5z''7#6"77UVYZ_V`UaaefggDQ1,C#CqDGD27);WX^_vXwWxyzz
5zSU_$?5'233L C
 Es   C4C4C9C9composite_teamsc           
         d}|t               }|}nt        |       |t        |||       t        dj	                  |              t
        rst        m	 t        |      }|j                  sU|j                  D cg c]  }|j                  dv s| }	}|	r+t        |       dd|	D cg c]  }|j                   c} dS t        rIt         C	 d| }t!        |      }t        j#                  d	|j$                   d
|  d|j&                          |}|4t(        r.t*        (	 t+        |      }t        j#                  d| d
|         n|rt        j#                  d| d
|         t        j#                  d|  d|        |dd t-        |      dkD  rdndz   }dt/        t0              d|ddd|g}t3        j4                  |ddd      }|j6                  dk7  r3t        j9                  d| d|j:                  j=                                 t>        d z  d!z  }|jA                         r	 tC        |d"d#$      5 }tE        jF                  |      }ddd       jI                  d%i       jK                         D ]  \  }}|jI                  d&      d'k7  s||k(  r |jI                  d(d      }|jI                  d)g       }|rtM        |      n|h}|tM        |       z  }|se|st        |       dd*| d+| d,dc S t        j9                  d-| d.| d/        n tS        jT                  d1d2| d3|d45      }t>        d z  d%z  | d6z  }|jV                  jY                  dd7       |j[                  |d#$       t\        rt^        	 t_        |t/        |      d8d9:       	 dd<l0m1}  || |||      }d?d@dAdB}|jI                  |d?      }tf        r/th        )ti        |      r|dCz  }t        j#                  dD| dE       	 tk        ||F      } tn        |    }!tp        jI                  |!      }"|"Ht        j9                  dG|!js                          dH       t        |       ddI|!js                          dS tu        |d| | J       dK}#dLdM|dNtw        |#      dOtx        dP|"dQg
}$	 t3        j4                  |$ddd      }%|%j6                  dk(  rd	 tE        j|                  |%j~                        }&ddTl@mA}'  |'ddU       t                |dd t-        |      dkD  rdndz   }t        r	 t        |      }dVj	                  |       }(dW| dX|( dY| })t3        j4                  dt/        t0              dZ|)d[d8gddd       t        j#                  d\| d]|(        d^|d| d_||d`|( da|&db	}*t        |&t              r|&jI                  dc      nd}+|+r|+dd?dd| d6de},tu        |fi |, 	 ddflGmH}-  |-||      }.|.r>t        j#                  dg|.jI                  dh       di|        |.jI                  dh      |*dh<   |*S t        je                  dk|%j:                  j=                                 t        |       d|%j:                  j=                         dlj	                  |$ddA dmgz         dnS c c}w c c}w # t        $ r#}
t        j                  d|
        Y d}
~
d}
~
ww xY w# t        $ r#}
t        j                  d|
        Y d}
~
d}
~
ww xY w# t        $ r#}
t        j                  d|
        Y d}
~
d}
~
ww xY w# 1 sw Y   xY w# tD        jN                  tP        f$ r#}t        j9                  d0|        Y d}~[d}~ww xY w# t        $ r#}
t        j                  d;|
        Y d}
~
d}
~
ww xY w# t        $ r5}t        je                  d=|        t        |       dd>| dcY d}~S d}~ww xY w# tl        $ r#}t        |       dt/        |      dcY d}~S d}~ww xY w# t2        jz                  $ r( t        je                  dR       t        |       ddRdcY S w xY w# tD        jN                  $ r  dS|%j~                  j=                         i}&Y w xY w# t        $ r Y rw xY w# t        $ r#}/t        j9                  dj|/        Y d}/~/|*S d}/~/ww xY w)ou   복합업무 임시팀 디스패치.

    하나의 봇 세션에 여러 논리적 팀을 조합하여 Phase별로 순차 실행.
    임시팀장(Opus)이 Phase 관리, 핸드오프, 품질 검수를 일괄 수행.
    Nr  highr8  r   u)   보안 위협 감지 (composite 차단): r  *   [injection_guard] 스캔 실패 (무시): z
composite [approval] risk_level=z, composite_teams=, patterns=#   [approval] 검사 실패 (무시):    [model_router] 권장 모델: *   [model_router] 라우팅 실패 (무시): (   [model_router] 명시적 모델 지정: u,   복합업무 위임 시작: composite_teams=r  r  ...r   r   start--teamrR  --descT   r  r   !   task-timer start 실패 (task_id=r   rK   r   r   r   r   r   r   r   r   rg  u   겹치는 팀(/   )에 이미 running 태스크가 있습니다: uB   . 완료 후 재시도하거나 --force로 강제 실행하세요.u   [force] 겹치는 팀(&   )에 이미 running 태스크 있음: u   . --force로 강제 진행.!   running 태스크 확인 실패: ^# task-\d+\.\d+:# :r3  countr  r   dispatchwriter   r   tool	operation.   [audit_logger] 감사 기록 실패 (무시): )build_composite_promptu   build_composite_prompt 실패: u   프롬프트 생성 실패: r   r
  r5  r6    

## ⚠️ Sanitize 게이트 (Lv.3+)
외부 AI(Codex/Gemini) 호출 전 반드시 sanitize 검사를 수행하세요:
- 대상: affected_files의 코드 + 설계 문서
- 마스킹 항목: 주민등록번호, 연락처, API 키, 계좌번호, 보험 증권번호
- 방법: `from utils.sanitize_gate import sanitize_text` 사용
- PII가 마스킹된 코드만 외부 AI에 전달할 것
[sanitize-gate] Lv.u5    composite 작업에 sanitize 게이트 지시 삽입r      환경변수 COKACDIR_KEY_
    미설정6   봇 키가 설정되지 않았습니다: COKACDIR_KEY_)ry   r   rg  r  cokacdir--cron--at--chat--key--once+   cokacdir 호출 타임아웃 (60초 초과)rawset_bot_status
processingr  [u   ] 복합업무 임시팀(   )에게 위임: log--typeu   복합업무 위임 완료: r   rX  u   복합업무 임시팀장u   복합업무 임시팀(u@   )에게 위임 완료. 즉시 독립 세션에서 작업 시작.)	r   r   r   rg  r   r2  r_  r  cron_responser  r  )schedule_idra  	max_retryr  issue_mc   [memory-check] MC 발급: mc_id for *   [memory-check] MC 발급 실패 (무시): u   복합업무 위임 실패: r     ...(생략)r   r  command)Ir  r  r  rf  r  _INJECTION_GUARD_AVAILABLE_scan_contentis_safethreatsseverityr  pattern_namer   r   r   _APPROVAL_AVAILABLE_check_commandr   
risk_levelmatched_patterns_MODEL_ROUTER_AVAILABLE_route_modelr  r"   
TASK_TIMERr   r  r  r   r   r?  r   r   r   r%   r   r   r   r   r   r-   r@  r   r   r    rT  _AUDIT_LOGGER_AVAILABLE_log_file_operationprompts.team_promptsr  r   _SANITIZE_GATE_AVAILABLE_should_sanitizer   r   
BOT_TO_KEYr   upperr)  rt  r   r  ri  r   utils.bot_activityr  r%  _REDACT_AVAILABLEr?   r;  r<  utils.memory_checkr  )0rg  r*  r2  r   r  r   r  _scan_resultr  _high_threats_e_approval_input_approval_result_recommended_model
short_desc	timer_cmdtimer_resultr   r2   existing_dataexisting_tidexisting_taskexisting_teamexisting_compositeexisting_team_setr  r   r  r  prompt_composite_level_to_int_composite_dispatch_levelr   r   r  _dispatch_delayr  r   responser  teams_labellog_msg_result_schedule_id_watchdog_metar  	mc_result_mc_errs0                                                   r3   _dispatch_compositer  
  si	    L"$ 	 ( #IwE chh78 "m&?	L(3L'',8,@,@ gqAJJRfDf g g !'*")%NhuOvcdPQP^P^OvNw#x  ~9		E *9+6O-o>KK()9)D)D(E F##2"3 4,==>@ &+!&=,BZ	L!-i!8KK89K8LL^_n^opq 
>?Q>RRdetduvw
KK>>OzZaYbcd 3BC	NR,?5RHJJ	I >>)DtUWXL!#:7)3|GZGZG`G`GbFcde X%(::J	Dj#8 -A $		!-/</@/@"/M/S/S/U +m $$X.);|w?V - 1 1)R @%2%6%67H"%M"?QC(:$;XeWf!+c/.BB %g.&-"0	9h#/.0r!t   NN0	9_'.(CE /8 +r'!_iqQI H$w.G9C@I4$7W5 #6#B	P#i.zelm
R?')US *+qI 7 ; ;E1 E$4$@EUVoEpK	
 	)*C)DDyz{6.wuM ,'H
,,x
 C
{3HNN4D3EZPQg!0fgogugugwfx.yzz 'WfgO 	/*C]DtRP A	6zz&--0H
 	6{L1 s^I0CuL
)*5
 hh/gY7}DTU_T`aJ:N		
 	27)5NO"./$0=}~%

 .8$-Gx||D)T+ ,WIS9	N "'<^<	S3 )4I8w9O8PPUV]U^_`#,==#9  3FMM4G4G4I3JKLg!fmm.A.A.CPSPXPXY\]_^_Y`dqcrYrPsttM !h
 Pw 	LLLEbTJKK	L  	ELL>rdCDD	E  	LLLEbTJKK	L6- -4 $$g. 	DNN>qcBCC	D  	PLLI"NOO	P  R6qc:;g!0LQC.PQQR,  6g!c!f556> $$ ]BCg!.[\\] ## 	6v}}2245H	6  V  	SNNGyQRR	SsM  %Z 8ZZZ %Z8Z A[	 &[8 %\4 3\'	B\4 \4 1\4 <]3 ^" 4_# ` >a b Ab 
Z 	[#[[		[5[00[58	\$\\$'\1,\4 4]0]++]03	^<^^"	_ +*__ _ #	`,`
`
`8aa/bb	bb	cb==cprd_contentc                    t        j                  dt         j                        }t        j                  dt         j                        }g }t        |j	                  |             }t        |      D ]  \  }}|j                  d      }t        |j                  d            }|j                  d      j                         }	|j                  d      j                         }
|j                         }|j                  | |      }|r|j                         n
t        |       }| || j                         }d}t        j                  dt         j                        }|j                  |      }|r|j                  d      j                         }g }t        j                  d	|
      }|r;|j                  d      j                  d
      D cg c]  }|j                          }}|j                  |||
|	|||d        |S c c}w )u!  PRD 문서에서 Phase/Sprint 섹션을 정규식으로 추출한다.

    Args:
        prd_content: PRD 파일 전체 텍스트

    Returns:
        phase 딕셔너리 리스트. 각 항목은 phase_number, phase_type, title,
        duration, body, features, dod 키를 가진다.
    u?   ^###\s+(Sprint|Phase)\s+(\d+)\s*\(([^)]+)\)\s*[-—–]\s*(.+)$z^###\s+(Sprint|Phase)\s+|^##\s+r3  r   r
  r5  Nz\*\*DoD\*\*:(.*?)(?=\n##|\Z)z\[([^\]]+)\]r  )phase_number
phase_typetitledurationbodyfeaturesdod)r@  r?  	MULTILINEr  finditer	enumeraterB  r  r?  endr  rs  r  rG  r  r   )r  header_patternnext_section_patternr  matchesr  r|  r  r  r  r  
body_startnext_mbody_endr  r  dod_patterndod_mr  feat_mr2   s                        r3   _parse_prd_regexr    s    ZZJ
N ::&H",,WF>**;78G'" $
1WWQZ
1771:771:##%
  " UUW
%,,[*E%+6<<>[1A:h/557 "jj!@"))L""4(++a.&&(C !?E2+1<<?+@+@+EFa	FHF ,($$
	
5$
L M Gs   G=prd_pathc                    d| d}d}	 t        j                  |d|ddgdddd	
      }|j                  dk7  r2t        j	                  d|j
                  j                                 g S |j                  j                         }	 t        j                  |      }t        |t              r|S t        |t              r@d|v r<|d   }t        |t              rt        j                  |      S t        |t              r|S t        j                   d|t        j"                        }|r$t        j                  |j%                  d            S t        j	                  d       g S # t        j                  $ r Y ww xY w# t         j&                  $ r t        j	                  d       g cY S t(        $ r$}	t        j	                  d|	        g cY d}	~	S d}	~	ww xY w)u  claude CLI를 호출해 PRD에서 Phase/Sprint 정보를 JSON으로 추출한다.

    Args:
        prd_path: PRD 파일 경로 (프롬프트 참조용)
        prd_content: PRD 파일 전체 텍스트

    Returns:
        phase 딕셔너리 리스트. 실패 시 빈 리스트.
    u  다음 PRD 문서에서 구현 로드맵 섹션의 Phase/Sprint별 태스크를 JSON 배열로 추출해줘.

각 항목 형식:
{"phase_number": 0, "phase_type": "Sprint" 또는 "Phase", "title": "제목", "duration": "소요시간", "body": "항목 내용 전체", "features": ["F1", "F2"], "dod": "완료조건 또는 null"}

JSON 배열만 출력하세요. 다른 텍스트 없이.

--- PRD 문서 시작 ---
u   
--- PRD 문서 끝 ---z/home/jay/.local/bin/claudez-pz--output-formatr%   Tx   z/tmp)r  r;   r  cwdr   u    [handle_prd] claude CLI 오류: r   z\[.*\]uG   [handle_prd] claude 출력에서 JSON 배열을 찾지 못했습니다.z&[handle_prd] claude CLI timeout (120s)u'   [handle_prd] claude CLI 호출 실패: N)r   r  r  r   r   r   r?  r   r%   ri  r;  r  r<  r"   r   r@  r  rG  rB  r  r   )
r  r  r  
claude_binr   r  outerinnerarr_mr   s
             r3   _parse_prd_clauder  L  s   	& - !	!  /J%v'8&A
 !LL;FMM<O<O<Q;RSTImm!!#	JJsOE%&%&8u+<heS)::e,,eT* L 		)S"))4::ekk!n--^_	 ## 		 $$ =>	 >qcBC	sa   AE7 *E7 &E ,=E *E <A
E7 E7 E41E7 3E44E7 7*G#G+G
G
Gc                 j   t        |       }|j                         s	t        | z  }|j                         sdd|  dS |j                  d      }t	        |j                               }t        |      }d}|s#t        j                  d       t        ||      }d}|j                  }|j                  d	      r|d
d n|}t        dz  dz  }	|	j                  dd       g }
g }t        |      D ]  \  }}|j                  d|      }|j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }|j                  d      xs g }|j                  d      }d| d| }|	| dz  }|j                         r|j                  t	        |             g }|dkD  r$||dz
     j                  d|dz
        }d| d| g}t!        j"                         j%                  d      }|rd j'                  |      nd}d!| d!}|r|d"| z  }t)        |      }d}|rd#| d$}d%| d&| d'| d(| d)| d*| d*| d+| d,| d-| d$| }|j+                  |d       |
j                  t	        |             t        j                  d.|         d/| |||
|t-        |      d0S )1u  PRD 파일을 읽어 Phase별 task 파일을 생성한다. 위임은 하지 않는다.

    Args:
        prd_path: PRD 파일 경로 (절대경로 또는 WORKSPACE 기준 상대경로)
        team_id:  담당 팀 ID

    Returns:
        처리 결과 딕셔너리
    r   u'   PRD 파일을 찾을 수 없습니다: r  r   r   regexuE   [handle_prd] 정규식 파싱 결과 없음, claude CLI 폴백 시도claudezprd-r5  NrK   r   Tr   r  r  Phaser  r   r  r  r  r  z	dispatch-z-phaser  r   r3  rs  )timespecr   `u    — u   
## 완료 조건 (DoD)
r  z---
task_id: "z	"
team: "z$"
level: 2
priority: P2
depends_on: z
created_at: "z"
deadline: null
---

# r  rO  u   

## PRD 참조 (필수)


---

u   [handle_prd] 파일 생성: r  )r   prdr   methodcreatedskippedtotal_phases)r   r   r   rj  r"   resolver  r   r   r  stemr  r    r  r   r   r   r  r  r  reprrT  r  )r  r   prd_filer  prd_absr  r  r  prd_stemr  r  r  idxrY  	phase_numr  r  r  r  r  r  r   	file_path
depends_onprev_phase_num
created_atfeatures_strprd_ref_linedepends_on_yamldod_sectionr   s                                  r3   
handle_prdr    s,    H~H??x'??!0WX`Wa.bcc$$g$6K(""$%G k*FF[\"7K8 ==D??62tABxHH$w.IOOD4O0GG' 6@
UIInc2	YY|W5
		'2&99Z,yy$#ii
39riihZvi[97)3/	NN3y>* !#
7#C!G_00qIN%hZvn5EFGJ\\^--y-A
.6tyy*B7)1~eL>22Lz*6se2>K 	 "Y  ++ ,&< ( 
!J<q2eW ='n fBm 	$ 	Ww7s9~&29+>?m6@t F r@   r  original_keydispatch_delayc                    g }t         j                         D ]!  \  }}|s	||k7  s|j                  ||f       # |st        j	                  d| d       yt        t               j                               }|D ]  \  }}d}	t        j                         D ]  \  }
}||k(  s|
}	 n |	r|	|v r5t        j                  d| d| d       dd| dt        |      d	t        d
|dg
}	 t        j                  |ddd      }|j                  dk(  rC	 t!        j"                  |j$                        }t        j                  d| d| d       ||dc S t        j	                  d| d| d|j*                  j)                                  t        j-                  d| d       y# t        j                  $ r  t        j	                  d| d| d       Y [w xY w# t         j&                  $ r d|j$                  j)                         i}Y w xY w)u  cokacdir --cron 실패 시 봇별 key fallback으로 재전송.

    원래 봇 키 전송 실패 시, 동일 팀의 다른 사용 가능한 봇 키로 재시도.
    BOT_KEYS에서 original_key와 다른 키를 순회하여 첫 번째 성공 시 반환.
    z[fallback] u*   : 대체 가능한 봇 키가 없습니다Nu&   : 대체 키로 재전송 시도 (key=rx  r  r  r  r  r  r  Tr  r  u   : 대체 키 u    타임아웃r   r  u   로 재전송 성공)r  fallback_keyu	    실패: u   : 모든 대체 키 실패)r   r   r   r   r   r   r   r   r  r   rt  r   r   r  r  r  r%   ri  r   r   r?  r   r   )r  r  r   r   r  fallback_keysr   r   r   bot_id_for_keybidknamer  retry_resultr  s                  r3   _retry_with_fallback_keyr!    s@    M&nn. 7(L0  (H!567 WI-WXY ')..01I+ +q($**, 	JC !$	 n	9k'*PQYPZZ[\] n-
	%>>#dWYZL
 ""a'@::l&9&9: KK+gYmH:EYZ[$ ( 
 NN[	xj	R^ReReRkRkRmQnopW+qZ LL;wi'ABC' (( 	NN[	xjVW	 '' @!<#6#6#<#<#>?@s$   %F#G#/GG/H
H
session_idrefresh_mapr  resume_from
agent_typeallow_no_scopec                 F*   |=t        |      }|j                         sdd| dS |j                  d      }d| d| }| s|sdd	dS | r|rdd
dS |r2t        |d   |       t	        |	xs d|||       t        ||||	||      S | J t        | ||      }|rd|dS d}|	t               }	|	}nt        |	       |t        ||	|       t        rt        	 t        |      }|j                  s|j                  D cg c]  }|j                  dv s| }}|r1t        j!                  d|D cg c]  }|j"                   c}        n:t        j%                  d|j                  D cg c]  }|j"                   c}        t*        rNt,        H	 | d| }t-        |      }t        j%                  d|j.                   d|  d| d|j0                          |}|4t2        r.t4        (	 t5        |      }t        j%                  d| d|         n|rt        j%                  d| d|         t        j%                  d|  d|	        t6        rt8        	 t9        t;        t<                    } | j?                         }!| jA                         }"|!jC                         D ]o  \  }#}$|$jE                  d      | k7  r|"jE                  |#i       }%| jG                  |#|$|%      }&|&d   d k(  sIt        j!                  d!|#|&jE                  d"d#             q 	 dd%l$m%}'  |'       }(|(jM                  d&      r|})nd'})d}*d}+|
0|	r.tO        jP                  d(|	      },|,r|,jS                  d)      }+d*|+ }*|xs | tT        v }-t<        d+z  d,z  }.|.j                         r	 tW        |.d-d      5 }/tY        jZ                  |/      }0ddd       0jE                  d.i       jC                         D ]  \  }1}2|2jE                  d/      d0k(  s|2jE                  d      | k(  s0|1|	k7  s6|-s/t]        |	       dd1|  d2|1 d3|2jE                  d4d5      dd6  d7dc S t        j!                  d8|  d9|1 d3|2jE                  d4d5      dd6  d:| tT        v rd;nd< d=	        n |dd? tc        |      d?kD  rd@nd5z   }4|	}5dAt;        td              dB|5dC| dD|4g}6|r|6jg                  dE|g       |r|6jg                  dF|g       ti        jj                  |6dGdGd6H      }7|7jl                  dk7  r3t        j!                  dI|5 dJ|7jn                  jq                                 |/t<        dKz  |z  }8|8j                         st]        |5       ddL|8 dS |r|rt<        d+z  dMz  | dNz  }9dO}:|9j                         sdG}:nI|9js                         jt                  };tw        jx                         j{                         |;z
  dPz  }<|<dQkD  rdG}:|:rqt<        dKz  |z  }8|8j                         rUt<        dRz  dSz  }=|=j                         r9ti        jj                  dAt;        |=      t;        |8      dTt;        |9      gdGdGd6H       tO        j|                  dUdV|	 dW|d)X      }t        | |       t	        |	|||       t        ||       t        |       t        |      }>|>rt        j!                  dY|>        t        |t;        t<                    }t        ||      }|dZk(  rt        |t;        t<                    }t<        d+z  d.z  |	 dNz  }?|?j                  j                  dGdG[       |?j                  |d       |E	 t        |	||t;        |?j                  t<                    \      }@t        j%                  d]|@        n|r	 t<        d+z  d_z  }A|Aj                  dGdG[       |A|	 d`z  }B|	tw        jx                         j                         t;        |?j                  t<                    dadb}Ct        |B|C       t        j%                  dc|B        t        rt        	 t        |	t;        |?      ded'f       t        |       }D|Drt        j!                  dhD        t        | ||	||||i      }Edjdkdldm}F|FjE                  |dj      }Gt        r/t        )t        G      rEdnz  }Et        j%                  doG dp       t        |      }Ht        |Ht;        t<                    }Ht        |      }I|HrCt        H|	      }J|JD ]  }Kt        j!                  dq|K         t        J       t        |5Hr       t        |I      }L|Lrt        j!                  dqL        t        |H      \  }M}Ndjdkdldm}F|FjE                  |dj      }O|O|Mk  r$Nr"t        j!                  dsO dt| duN dvM dw	       |rt        |5|x       |>r	Edy|> dzz  }EHr.t        H      }P|Pr!d{j                  d| PD              }QEd}|Q d~z  }EHr)t        H      rEdz  }Et        j%                  d|	 d       t        |      }R|RrEdR z  }E| dk(  r9t        r3t        -	 t        |      }SEd|S dz  }Et        j%                  d|S        t        |    }T| tT        v r|	 t        |5|      }Ut        U   }Vt        jE                  |V      }W|WHt        j!                  dVj                          d       t]        |5       dd|Vj                          dS | }XU}Ynt        jE                  |       }Z|ZrZt        vrt]        |5       dd|  ddS t        Z   }W|WHt        j!                  dZj                          d       t]        |5       dd|Zj                          dS | j                  dd5      }Xt        jE                  | d5      }Y|Yrt        |5Y       Yrt        |	      }[t        j%                  dY d|  dt        |[j                                       |Y|[v r[Y   }\|\d   }]|\d   }^|]|	k7  r^| k7  r|sHt]        |5       t        [      }_dj                  d |_D              }`ddY d^ d] d|_rd` ndz   _dS t        j!                  dY d^ d] d       nt        j%                  dY d       Yrt        |5XY|xs dd|	 dN       djdkdldm}a|ajE                  |dj      }b|bdkk\  r&t        |	b      }c|crt        j%                  dc        t        |	|||      }d|dr	Edd d{z  }Et	        |	|||       dO}ed}fW}g|dv r3gr1t        gd      }f|fr#fdk7  rdG}et        j%                  d| df d       d}hddEdt        |h      dt        dWdg
}i	 ti        jj                  idGdGd?H      }jjjl                  dk(  r	 tY        j                  jj                        }kddlvmw}l  |l| d       t        |       }mt        j%                  d|  d|md    d|md   xs d dmd   xs d        t                erfrt        gfd6       |dd? tc        |      d?kD  rd@nd5z   }4t        r	 t        |4      }4d|	 dTd    dt|  d|4 }nti        jj                  dAt;        td              d|nddegdGdGd6H       |rt        || |	       |
|*|+g }ot        d)|
d)z         D ]&  }pd|+ d|p }qd|q dN}roj                  |p|r| |qdȜ       ( dAt;        t<        dz        dd|*dtY        j                   odOͫ      d|?g	}sti        jj                  |sdGdGd6H      }t|tjl                  dk7  r4t        j!                  d|* dJtjn                  jq                                 nt        j%                  d|*        Td   }ut        r	 t        u      }ut        j%                  d|	 du        d|	| Td   |||Td    dԝkd՜}v|*|*vd<   t        kt              rkjE                  d׫      nd}w|wrwddjd؜}x|?r|?xd<   t        |	fi x 	 ddlm}y  |y|	|      }z|zr>t        j%                  dzjE                  dܫ       d|	        |zjE                  dܫ      vd<   vS t        j                  djjn                  jq                                 t        EW| |	      }|||r|d   }kt        j%                  d||d    d       ddlvmw}l  |l| d       t                erfrt        gfd6       |dd? tc        |      d?kD  rd@nd5z   }4t        r	 t        |4      }4d|	 dTd    dt|  d|4 }nti        jj                  dAt;        td              d|nddegdGdGd6H       d|	| |Td   ||dd d|d    dkdGd	}vt        |kt              rkjE                  d׫      nd}w|wrwddjd؜}x|?r|?xd<   t        |	fi x vS er&fr$t        gf       t        j%                  d|g        t]        |5       djjn                  jq                         dj                  iddl dgz         dS c c}w c c}w c c}w # t&        $ r#}t        j)                  d|        Y d}~d}~ww xY w# t&        $ r#}t        j)                  d|        Y d}~d}~ww xY w# t&        $ r#}t        j)                  d|        Y d}~d}~ww xY w# t&        $ r!}t        j)                  d$|       Y d}~d}~ww xY w# 1 sw Y   BxY w# tX        j^                  t`        f$ r#}3t        j!                  d>|3        Y d}3~3d}3~3ww xY w# t&        $ r#}t        j!                  d^|        Y d}~Dd}~ww xY w# t&        $ r#}t        j!                  dd|        Y d}~sd}~ww xY w# t&        $ r#}t        j)                  dg|        Y d}~}d}~ww xY w# t&        $ r#}3t        j!                  d|3        Y d}3~3	vd}3~3ww xY w# t        $ r#}3t]        |5       dt;        |3      dcY d}3~3S d}3~3ww xY w# th        j                  $ r( t        j                  d       t]        |5       dddcY S w xY w# tX        j^                  $ r  djj                  jq                         i}kY w xY w# t&        $ r#}t        j)                  d|        Y d}~cd}~ww xY w# t&        $ r	 Td   }uY 4w xY w# t&        $ r#}{t        j!                  d{        Y d}{~{vS d}{~{ww xY w# t&        $ r Y w xY w)un  작업을 독립 세션으로 디스패치

    목차→요약→상세 원칙:
    - task_desc를 memory/tasks/<task_id>.md에 저장 (요약 파일)
    - 프롬프트 본문에는 파일 경로만 포함 (build_prompt 내에서 처리)
    - session_id: 아누의 현재 세션 ID. 전달 시 followup이 동일 대화에서 실행됨.
    - task_type: 작업 유형 (coding/research/check). coding일 때만 QC 검증 포함.
    - allowed_resources: capability snapshot 데이터 (task-2364 P0). None이면 snapshot 미저장.
    - allow_no_scope: True면 allowed_resources 없어도 허용 + audit log 생성.
    Nr   1   resume_from 파일이 존재하지 않습니다: r  r   r   uL   ## 이전 세션 요약
아래 요약을 읽고 이어서 작업하세요.

r  u0   team_id 또는 composite_teams 중 하나 필수u2   team_id와 composite_teams는 동시 사용 불가r   r   )r   r  r   ri  uh   [injection_guard] task_desc에서 위험 패턴 감지 (차단 없음, 워크플로우 호환): threats=uG   [injection_guard] task_desc에서 낮은 위험 패턴 감지: threats=rk  r  rl  r  z, task_type=rm  rn  ro  rp  rq  u   위임 시작: team=r  r   r   r2  r8  u>   [session-health] CRITICAL 세션 감지: task=%s, usage=%.1f%%	usage_pctg        u2   [session-health] 세션 체크 실패 (무시): %s)FeatureFlagLoaderrw_isolation_enabledr  ztask-(\d+)\.r3  zscoped-rK   r   r   r   r   r   u   같은 팀(rx  z ('r_  r   rv  uN   '). 완료 후 재시도하거나 --force 플래그로 강제 실행하세요.u   [force] 같은 팀(ry  z'). u   논리적 팀 자동 --forceu   --force 플래그로u    강제 진행.rz  r  rr  r   rs  rt  ru  	--projectz--work-levelTr  rw  r   rO   u8   프로젝트 디렉토리가 존재하지 않습니다: r  r  Fi  r  r  zproject-map.pyz--outputr{  r|  r}  r~  z[file-size-check] codingr   )r   r{  r  r  u   [capability] snapshot 저장: u.   [capability] snapshot 저장 실패 (무시): r  z.allow-no-scope.logu2   --allow-no-scope 플래그로 legacy 호환 통과)r   
allowed_atr1  reasonz)[capability] --allow-no-scope audit log: u7   [capability] allow-no-scope audit log 실패 (무시): r  r  r  z	[qc-env] r  r   r
  r5  r6  r  r  u+    작업에 sanitize 게이트 지시 삽입z[affected_files] r  u   [level-estimate] ⚠️ Lv.(u   ) 지정했지만 u    — Lv.u    이상 권장)r  u(   

## ⚠️ 참조 파일 크기 주의
u   
- 위 파일은 반드시 offset/limit 파라미터를 사용하여 분할 읽기하세요.
- 요약 파일(*.summary.md)이 있으면 원본 대신 요약 파일을 먼저 읽으세요.
- 한 번에 200줄 이상 읽지 마세요.r  c              3   :   K   | ]  }d |d    d|d    d  yw)z  - r   r  r  u   줄)Nr>   )r  lfs     r3   r  zdispatch.<locals>.<genexpr>  s'      _B46
|2bk]$!G _s   u@   

## ⚠️ 대형 파일 프로토콜 (2000줄 초과)
대상:
u  
1. 전체 읽기 금지 → offset/limit으로 수정 대상 ±200줄만 읽기
2. Edit 전 수정 위치의 정확한 라인 번호 확인
3. Edit 후 반드시 grep -n으로 변경 반영 확인
4. 한 번에 50줄 이상 삽입 금지 → 여러 Edit으로 분할
u  

## ⚠️ InsuRo 백엔드 변경 — systemd reload 필수
이 task는 InsuRo FastAPI 서버(`server/main.py` 계열) 코드를 수정합니다.
PR 머지 후 또는 코드 변경 직후 반드시 아래 명령으로 uvicorn을 재시작하세요:
```
systemctl --user restart insuro-api.service
systemctl --user status insuro-api.service --no-pager | head -10
```
재시작을 누락하면 새 endpoint가 404로 응답합니다 (2026-05-01 사건 재발 위험).
z[insuro-reload] task u3   : InsuRo 서버 변경 감지, reload 안내 삽입

r}   u?   

## 🎨 이미지 스킬 라우터 추천
- 추천 스킬: **u   **
- 이 추천은 image-skill-router.py의 `get_skill_recommendation()` 결과입니다.
- 추천 스킬을 우선 사용하되, 작업 특성상 다른 스킬이 적합하면 사유를 명시하세요.
u/   [image-skill-router] design 팀 추천 스킬: u-   [image-skill-router] 추천 실패 (무시): r  r  r  r  u   팀 uH   에 할당된 봇이 없습니다. 봇 토큰을 먼저 등록하세요.r  )r   r   u   [봇 충돌 검사] bot=z, busy_bots=r   r   c              3   8   K   | ]  }|d     d|d    d  yw)r   r0  r   rx  Nr>   )r  as     r3   r  zdispatch.<locals>.<genexpr>  s)     1k\]Qx[M1^CTBUUV2W1ks   u   봇 u   가 u    작업(u   )에 점유 중입니다. u   가용 대안: u"   모든 봇이 작업 중입니다.)r   r  available_botsu   [force] 봇 u2   )에 점유 중이지만 --force로 강제 진행.u    [봇 충돌 검사] 통과: bot=u    가용r-  r  )ry   r   r   r  u&   [3docs] 작업 3문서 생성 완료: u    

## ⚠️ Agent 미팅 경고
)r8  r9  claude-opus-4-6z[opus-upgrade] level=u   , 봇 모델 Opus 승격: u    → claude-opus-4-6r  r  r  r  r  r  r  r  r  r  r  u   [모델 검증 결과] z: consistent=r   z, org=r   r   r   r   )r   u$   [redact] 마스킹 실패 (무시): r  z] leaderr  r  r  rv  rg  )orderr  r   r   zchain_manager.pycreatez
--chain-idz--tasksr   z--original-task-fileu)   chain_manager.py create 실패 (chain_id=u)   chain_manager.py create 완료: chain_id=u   위임 완료: r   rX  u?   에게 위임 완료. 즉시 독립 세션에서 작업 시작.)r   r   r   r   r2  r_  r  r  r  r  )r  ra  r  r  r  r  r  r  r  u   위임 실패: )r  r  r   r   r  u   [fallback] 대체 키(r  u   )로 위임 성공u   )에게 fallback 위임: d   u#   원래 키 실패 → fallback 키()	r   r   r   r8  r2  r_  r  r  fallback_usedu<   [opus-upgrade] 디스패치 실패로 모델 즉시 복원: r  r  )r   r   rj  r>  rH  r  rc  r  r  r  r  r  r  r  r  r   r   r  r   r   r   r  r  r  r  r  r  rh  ri  r"   r   _load_running_tasks_load_ledger_tasksr   r   check_sessionutils.feature_flagsr*  
is_enabledr@  r  rB  ra  r   r%   r   r  r   r-   r  r  r  r   r  r  r   r?  statst_mtimer   r  	timestampr   r"  r  r  r  r  r  r   r    rT  r  rR  r  r   r  r  r  r6   r  r  r  r  r1  r  r  r)  r  r/  r  r  r  r  _IMAGE_SKILL_ROUTER_AVAILABLE_get_skill_recommendationr5   r   r   r  r   r  r   r*   r   r   r  r   r   rb  rH  r   rt  r   r  r   ri  r   r  r  r  r%  r   r  r?   r  rz  r   r  r;  r<  r  r  r!  )}r   r*  r2  rg  r"  r  r  r#  r  r   r  r  r$  r%  r   r]  r  r3  r?  r{  r&  resume_pathsummary_contentrouting_warningr  r  r  r  r  r  r  r  _sr_running_ledger_rtid_rtinfo_linfo_statusr*  	_rw_flagseffective_agent_type_phases_chain_id_phases_base_num_base_matcheffective_forcer   r2   r  r  r  r   r  r   r  r  project_dirr  needs_refreshmtime	age_hoursscriptfile_size_warningsr  
_snap_path_events_dir_audit_file_audit_dataqc_env_warningr  _level_to_int_dispatch_level_af_task_lv_overlap_warnings_ow_af_missing_warn
_est_level_est_reason_manual_level_large_files_lf_listplatform_rulesrecommended_skillr   r   r   r  ry   bot_id_metar   busy_bots_infoconflictconflict_task_idconflict_team_idr   available_str_level_to_int_docs_docs_level
_docs_path_meeting_warning_opus_upgraded_opus_original_model_opus_key_hashr  r  r   r  r  consistencyr  _tasks_json_i_t_id
_task_file_chain_create_cmd_chain_result_log_leaderr  r  r  r  r  r  fallback_results}                                                                                                                                r3   r  r  >  s   F ;'!!#!N{m\  &///AE  k	 	 ?!.`aa?!.bcc %oa&8)D!'"6Y	5J\]"?Iug]bjopp  -WiAQRO!o>>L"$ 	 ( #IwE "m&?	L(3L'',8,@,@ gqAJJRfDf g g NN#<I#JqANN#J"KM
 KK#<H<P<P#QqANN#Q"RT ~9		E!*1YK8O-o>KK()9)D)D(E FyYK 8,==>@ &+!&=,BZ	L!-i!8KK89K8LGT[S\]^ 
>?Q>RRYZaYbcd
KK&wiz'CD %);)G	S$C	NCC..0H,,.G"*.."2 
w;;y)W4 UB/++E7FC7#z1NNXK5
 6!#I23)& '+&*gii9*003!()9(:; =+< <O X%(::J	Dj#8 -A $		!-/</@/@"/M/S/S/U +m!%%h/9<%)))4?$/*%g.&-"-gY6e#/.M4E4EmUW4XY\Z\4]3^ _m!n   NN-gY6\'.M,=,=mR,PQTRT,U+VVZ=DHY=Y9_uv  wFG
 +4 3BC	NR,?5RHJMC
OWmXwX`blmI+z23.%01>>)DtUWXL!#:=/\M`M`MfMfMhLijk *,z9!!#-(%4lmxly2z{{ kx'.8j\;MM  M MMO,,E!113e;tCI2~ $#j0:=K!!#"Y.1AA==?NN"CK[1A:sS[}]'+! "	 +r'!_iqQI "'95gy%9KL Iy1)$5i@+,>+?@A ,Is9~FI+IzBIH29c)nM	 H$w.G9C@I4$7W5 $		R2"3%	 5 5i @A	J KK8EF 
	[#h.9KdT:%7)3F(GGK"&lln668i33I>?N	K k;7KKCK=QR
 #6#B	PY!	 (0N>"234GUzH`iF
  !aQ?M#''q1O$4$@EUVeEfK	
 	)/)::efg  	
*C
)#s9~
>C +H
9#wG$ 	6CNN.se45	6&'89mC@3IxH*+;*<=>29cBJ aQ?M!%%eQ/Mz!k)-%@RS^R__klvkw  xF  G	
 mh?9:L9M=>	
 ',yy _R^ __H$: &Z[F #C(x	F KK/y8klm ,I6ND()) (<AZAf
	P 9) D&&7%8 9FGF KKIJ[I\]^ WD ##	:2=QVWL l+ll8$;NN78H7ITU-(%4jkskykyk{j|2}~~" g&x/-(%D	  BJ  3K  L  Lv;NN77GzRS-(%4jkqkwkwkyjz2{||w+$(("5!-[A0INKK*;-wwiGWX\]k]p]p]rXsWtu n,)+6#+I#6 #+I#6 #w.3Cw3N %m4$B>$R	(,		1kaj1k(k&-"&{m48H7IRbQc d8 !9HQ_]O#DW{!} /8   NN&{m48H7IRbQc dH I
 >{m7ST $1	%gYc2	
 %&1!D$((2Ka&w<
KK@MN ,GY|T78H7ILL gy%9KL NN((^-n>OP$8<M$M!NKK'w.HMaLbbvw O 	/*C]DtRP A	6zz&--0H
 	6w-1':%gYmK<U;V W{+4u5VK<T<]X];^`	
 	 2#N4HPRS
 s^I0CuL
J)*5
 gYbh 0':J:,WJ:N		
 x': "2">CSC_ "KAvz* 
 012$7,UG37
""!#%/ '#(	
 I 223 

;U;&
! 'NN+<TX\fhiM''1,?@P?QQTYfYmYmYsYsYuXvw GHXGYZ[8n-*;7 	ogYeK=AB"N$x.))hi%	
 '"2GJ .8$-Gx||D)T+ N
 .7{+!'<^<	S3 )4I8w9O8PPUV]U^_`#,==#9  v}}':':'<&=>? 3	
 &z2HKK01P0QQcde :7L1  "6'8LTVW"3BC	NR4G5RPJ !-j!9J '"T(^$4AgY>WXbWcdGNNC
OUGXzR#$ '"x.(#@Q_A`@aast!)!%
G 2<Hd1K8<<-QUL#/#$!""
 2;N;/%g@@N 2>+?@KKVWeVfghm$!fmm.A.A.CPSPXPXY\]_^_Y`dqcrYrPsttO !h $K
 $R 	LLLEbTJKK	L  	ELL>rdCDD	E  	LLLEbTJKK	L0  	SLLMrRR	S<- -0 $$g. 	DNN>qcBCC	Dh  	RNNKB4PQQ	R   	[NNTUWTXYZZ	[  	PLLI"NOO	PV  	PNNJ1#NOO	P  	:-(%#a&99	:f $$ ]BDm$!.[\\] ## 	6v}}2245H	62  JCB4HIIJf  -"8n-H  	SNNGyQRR	S@ ! s[  %AH; ;AH,AH,AH; +AH1
>(AH; &AH6
9	AH; AAI* '&AJ BAK %)AK AL AK55AAL 8AL AL 0AL AAL AAM BAM0 1AN ,AO AO= 	AP, 3AQ* AR  !AS @#AAS$ D"AT H,AH; H;	AI'IAI"I"AI'I*	AJI3AJJAJJ	AKJ"AK K AKK	AK2KAK-K-AK2K5AK?K:AL LAL>LAL9L9AL>M	AM-M
AM(M(AM-M0	ANM9ANNANN	AON(AOOAOO	AO:OAO5O5AO:O=	AP)PAP$PAP)P$AP)P,8AQ'Q&AQ'Q*/ARRARR 	ASR)ASSASSAS!S AS!S$	ATS-ATTATT	AT TAT c           
          ddl }g }i }|j                  d|       sdddS t        dz  dz  |  d	z  }d
}|j                         ro	 |j	                  d      }|r||j                         d   vrDn	 |j                  | d| d       |j                  d       t        j                  d|  d       nt        j                  d|  d       t        dz  dz  }|j                  dd       ||  dz  }		 | t        j                         j!                         dd}
t#        |	dd      5 }t%        j&                  |
|dd       ddd       |j                  d       t        j                  d|  d       t        dz  d!z  }d"}|j                         r	 t#        |d#d      5 }t%        j(                  |      }ddd       j+                  di       j+                  | i       }|j+                  d$d"      }|j+                  d%d"      xs |j+                  d&d"      }|j-                  d'      rd(d)d*d+d,d-d.d/d0}|j+                  |d"      }n#|j/                  d1      r|j1                  d1d"      }|rd2d3|d4t2        d5t4        j6                  j+                  d6d"      g}t9        j:                  |ddd78      }|j<                  dk(  r.|j                  d9       t        j                  d|  d:| d;       nj|j>                  jA                         |d<<   t        j                  d|  d=|j>                  jA                                 nt        j                  d|  d>       |rt4        j6                  j+                  d@|jC                          d"      }|r	 |  dA}d2dB|dCtE        dD      d4t2        d5|dEg
}t9        j:                  |ddd78      }|j<                  dk(  r.|j                  dF       t        j                  d|  dG| dH       nP|j>                  jA                         |dI<   t        j                  d|  dJ|j>                  jA                                 n6t        j                  d|  dG| dK       nt        j                  d|  dL       	 t9        j:                  dMt        tF              dN| dOdPgddd78      }|j<                  dk(  r+|j                  dQ       t        j                  d|  dR       n3t        j                  d|  dS|j>                  jA                                 dT| |dU}|r||dV<   |S # t        $ r4}t        |      |d<   t        j                  d|  d|        Y d}~d}~ww xY w# 1 sw Y   4xY w# t        $ r4}t        |      |d<   t        j                  d|  d |        Y d}~Ed}~ww xY w# 1 sw Y   xY w# t        $ r4}t        |      |d<<   t        j                  d|  d?|        Y d}~d}~ww xY w# t        $ r4}t        |      |dI<   t        j                  d|  dJ|        Y d}~d}~ww xY w# t        $ r&}t        j                  d|  dS|        Y d}~Sd}~ww xY w)Wu2  진행 중인 task를 강제 취소한다.

    동작 순서:
    1. task_id 형식 검증
    2. task 파일 STOP 마커 prepend
    3. .cancelled 이벤트 마커 생성
    4. schedule_id 조회 → cron 제거
    5. 봇에게 즉시 중단 메시지 cron 발송
    6. task-timer end (CANCELLED)
    r   Nz
^task-\d+$r   zinvalid task_idr  rK   r   r  u0   ★★★ 작업 취소됨 (CANCELLED) ★★★r   r   Tr3  stop_marker_addedz	[cancel] u'   : task 파일 STOP 마커 추가 완료stop_markeru)   : task 파일 STOP 마커 추가 실패: u$   : task 파일 없음 (마커 스킵)r  r   z
.cancelledzmanual --cancel)r   cancelled_atr/  r   Fr   r   cancelled_eventu!   : .cancelled 마커 생성 완료u#   : .cancelled 마커 생성 실패: r   r   r   r  r   r   zbot-rZ   r[   r\   r]   r^   r_   r`   ra   ro   r  r  z--cron-remover  r  rP   rv  r  cron_removedu"   : cron 제거 완료 (schedule_id=rx  cron_removeu   : cron 제거 실패: u+   : schedule_id 없음 — cron 제거 스킵u   : schedule_id 조회 실패: COKACDIR_KEY_u    작업이 즉시 중단되었습니다. 진행 중인 모든 작업을 멈추고 .cancelled 마커를 생성한 뒤 종료하세요. finish-task.sh를 호출하지 마세요.r  r  r  r  bot_notifiedu   : 봇(u    ) 중단 메시지 발송 완료
bot_notifyu&   : 봇 중단 메시지 발송 실패: u(   ) 키 없음 — 중단 메시지 스킵u6   : 봇 이름 파악 불가 — 중단 메시지 스킵r   r  z--qc-result	CANCELLEDtimer_endedu!   : task-timer end CANCELLED 완료u"   : task-timer end 실패 (무시): r  )r   r   actionsfailed_actions)$r@  rA  r   r   rj  r>  rT  r   r   r   r   r"   r   r    r   r  r  r   r%   r&   r   r   r  r  r*   r   r#   r  r   r  r  r   r?  r  rt  r  )r   rU  r  r  r  r  r   r   
events_dircancelled_pathcancelled_datar2   r   bot_namer  r   r  bot_letter_map
cmd_remover   bot_key
cancel_msg
cmd_notifyresult_dicts                           r3   cancel_taskr  &  s    G%'N 99]G,!.?@@ H$w.G9C@IDK	^))7);G=D{'"4"4"6q"99$$$}D	%BW$U23iy0WXY
 	7)+OPQ X%0JTD1WIZ"88NT$LLN446'

 .#8 	GAIInaeAF	G()iy(IJK X%(::JH"	Rj#8 *A!YYq\
*#488"EJ$..;K!~~eR0QJNN9b4QH""6* $fvPV#fvPV" *--h;""7+#++GR8gRZZ^^,>C

 $
4d\^_$$)NN>2KK)G94VWbVccd ef4:MM4G4G4IN=1NNYwi7MfmmNaNaNcMd#efiy0[\] **..=1A0B!CRH_i  t u 
 *-b1gW
 $
4d\^_$$)NN>2KK)G9F8*Dd ef39==3F3F3HN<0NNYwi7]^d^k^k^q^q^s]t#uv
 NNYwivhZ?ghi7)+abcSJT	
 !NN=)KK)G9,MNONNYwi/QRXR_R_ReReRgQhij
 K
 (6$%m  	^,/FN=)NNYwi/XYZX[\]]	^ 	G 	G  T,/F()7)+NqcRSST* *>  	R,/FN=)NNYwi/LQCPQQ	R6  _/21v|,7)3YZ[Y\]^^_*  S7)+MaSQRRSs   A-T4 44V (U42V W "W8F!W CX BY 4	U1=)U,,U14U>9V 	V>
)V99V>WW 	X)XX	Y)YY	Y=Y88Y=c                     ddl m}   |         t        j                  d      }|j	                  dddd	       |j	                  d
dd d       |j                  d      }|j	                  dg dd       |j	                  dd       |j                  d      }|j	                  dd       |j	                  dd       |j	                  ddg dd       |j	                  dd g d!d"       |j	                  d#d d$%       |j	                  d&d d'%       |j	                  d(d d)%       |j	                  d*t        j                  d+d,	       |j	                  d-d d.%       |j	                  d/d d0%       |j	                  d1t        d d23       |j	                  d4ddd5	       |j	                  d6d d7d89       |j	                  d:d;gd d<=       |j	                  d>ddd?	       |j	                  d@dddA	       |j	                  dBdCdDgdDdE=       |j	                  dFd dG%       |j	                  dHdddIdJK       |j	                  dLd dMdN9       |j	                  dOdddPdQK       |j	                  dRd dS%       |j	                  dTdddUdVK       |j                         }|j                  r,t               }t        t        j                  |ddWX             y |j                  r7t        |j                        }t        t        j                  |ddWX             y |j                  s|j                   s|j#                  dY       t%        |dZd       r_|j                  s|j#                  d[       t'        |j(                  |j                        }t        t        j                  |ddWX             y |j*                  s6|j,                  s*t%        |dZd       s|j.                  s|j#                  d\       d]}|j*                  r|j*                  }n2|j,                  r&	 t1        |j,                        j3                  d^_      }d`|v sda|v r8t6        j9                  db       t        t        j                  dcdddedf             |j,                  rt1        |j,                        }|j;                         sEt        t        j                  dgdh|j,                   dedf             t=        j>                  di       |j3                  d^_      jA                         st        t        j                  dgdj|j,                   dedf             t=        j>                  di       n@|j*                  xs d]tC              dkkD  r"t6        j9                  dltC               dm       |jD                  d;k(  rM	 ddnl#m$}  ||jJ                  xs dop      }	|	 dq |jL                  sdr|_&        t6        jO                  ds       |jD                  eg du}tS        fdv|D              rMt%        |dwd      rt6        j9                  dx       n*t6        j#                  dy       t=        j>                  di       |jT                  jt1        |jT                        }|j;                         sEt        t        j                  dgdz|jT                   dedf             t=        j>                  di       d }|j                   r	 tW        |j                         }|j.                  r |jJ                  r8t        t        j                  dgd{dedf             t=        j>                  di       |j                  s8t        t        j                  dgd|dedf             t=        j>                  di       |j*                  s|j,                  st]        j^                  d}d]|j.                        }t1        t`              d~z  dz  | dz  }|j;                         r!|j3                  d^_      jA                         n;t        t        j                  dgd| dedf             t=        j>                  di       tc        |j.                  |j                  dte               v rnd]|jf                        }|d   dgk(  r5t        t        j                  |df             t=        j>                  di       |d   |_%        t6        jO                  d|j.                   d|jJ                   d|d    d       |jJ                  r[t]        jh                  d      }|jk                  |jJ                        s+t6        j9                  d|jJ                  |jl                         rto              ndd+dd}|jJ                  rzjk                  |jJ                        r_	 tq        |jJ                  |d   |d   |j                         t6        jO                  d|jJ                   d|d    d|d    d|d    d	       rts              nd }|Ft%        |dUd      s9t        t        j                  dgddeddWX             t=        j>                  di       tu        di d|j                  dd|jv                  d|d|jx                  d|jz                  d|j|                  d|j~                  d|j                  d|jJ                  d|j                  d|jf                  d7|jT                  d|j                  d|jL                  dI|j                  dM|j                  d|j                  dP|j                  d|dUt%        |dUd      }t        t        j                  |ddWX             y # t4        $ r Y Tw xY w# tP        $ rF}
t        t        j                  dgdt|
 dedf             t=        j>                  di       Y d }
~
d }
~
ww xY w# tX        $ rL}
t        t        j                  dgt[        |
      dedf             t=        j>                  di       Y d }
~
5d }
~
ww xY w# t4        $ r0}t6        j9                  d|jJ                   d|        Y d }~dd }~ww xY w)Nr   r	   u   작업 위임 디스패처)r_  z--check-sessions
store_trueFu0   모든 running 세션의 토큰 사용량 체크)actionr-  helpz--cancelTASK_IDuc   진행 중인 task를 강제 취소 (STOP 마커 + .cancelled + cron 제거 + 봇 중단 메시지))metavarr-  r  )requiredrt  )rg   rh   ri   rj   rk   rl   rm   rn   r   r   r   r}   r   u   위임할 팀)choicesr  z--compositeuE   쉼표 구분 논리적 팀 ID 목록 (2~3개, 예: marketing,design))r  z--tasku%   작업 설명 (짧은 한 줄 용도)z--task-fileuG   작업 설명 파일 경로 (권장: 긴 내용은 반드시 파일로)z--levelr7  r6  u(   검증 레벨 (normal/critical/security))r-  r  r  r  r-  )r-  r  checku[   작업 유형 - coding: QC 검증 포함, research/check: QC 검증 제외 (기본: coding)z	--sessionu9   아누 세션 ID (followup을 현재 대화에서 실행))r-  r  r,  u2   프로젝트 ID (projects/ 하위 디렉토리명)z--chainu   체인 ID (chain.py 연동)z--refresh-mapTuQ   프로젝트 맵 자동 갱신 (24시간 이상 오래된 경우, 기본: 자동)z	--task-idu8   태스크 ID 직접 지정 (미지정 시 자동 생성)z--resumeu[   재시도할 base task ID (예: task-2133). 자동 채번 후 기존 task 파일 재활용.z--phasesuf   한정승인(scoped delegation) Phase 수. 지정 시 chain_manager.py create로 체인 자동 생성.)r  r-  r  z--forceuW   동일 팀에 running 태스크가 있어도 강제로 dispatch 허용 (기본: 거부)z--resume-fromr$  uj   이전 세션 요약 파일 경로. 지정 시 task_desc 앞에 요약을 prepend하여 새 세션 시작.)r-  destr  z
--workflowzimage-qc-gateuS   워크플로우 적용 (image-qc-gate: 이미지 QC 게이트 5Phase 자동 적용))r  r-  r  z--skip-qc-gateua   이미지/광고 작업의 QC 게이트를 의도적으로 스킵 (제이회장님 승인 필수)z--skip-meetinguH   Lv.4 Agent 미팅 체크를 의도적으로 스킵 (로그에 기록됨)z--agent-typereadr  uo   에이전트 유형: read(읽기 전용, worktree 미생성) | write(쓰기, worktree 생성). 기본값: writez--modeluW   모델 강제 지정 (예: claude-opus-4-6). 지정 시 해당 모델의 봇만 선택.z--override-routingr]  u?   라우팅 경고를 무시하고 지정 팀으로 강제 위임)r  r-  r  r  z
--batch-idr  uY   배치 ID. 병렬 위임 시 동일 batch_id를 부여하여 전팀 완료 추적 가능.z--skip-brainstormingr?  u?   Lv.3+ UX 작업의 brainstorming 사전 실행 체크를 스킵z--prduW   PRD 파일 경로. Phase별 task 파일 자동 생성 (위임 없음, 파일 생성만)z--allow-no-scoper&  u_   task 파일에 allowed_resources 미명시 시 통과 (legacy 호환). audit log 자동 생성.r   r   uK   --check-sessions이 없으면 --team 또는 --composite이 필수입니다.r  u+   --prd 사용 시 --team이 필수입니다.u]   --check-sessions이 없으면 --task, --task-file, --prd, 또는 --resume이 필수입니다.r   r   r   u   레벨: Lv.0u   ## 레벨: Lv.0um   ⚠️ Lv.0 마이크로 수정은 dispatch 불필요! 아누가 Task tool (haiku)로 직접 실행하세요.r   uF   Lv.0은 dispatch 불필요. Task tool (haiku)로 직접 실행 권장.r  r;  r   u3   작업 설명 파일이 존재하지 않습니다: r3  u,   작업 설명 파일이 비어있습니다: r<  u   --task 직접 전달 u;   자 — 긴 내용은 --task-file 사용을 권장합니다)build_workflow_overview_promptauto)r   r  r7  u3   [workflow] image-qc-gate: opus 모델 강제 적용u&   워크플로우 모듈 import 실패: )r&  u   광고r%  r$  r'  r(  c              3   &   K   | ]  }|v  
 y wr=   r>   r  s     r3   r  zmain.<locals>.<genexpr>  s     92rY9r=  skip_qc_gateuV   ⚠️ --skip-qc-gate로 이미지 QC 게이트 우회. 제이회장님 승인 필수.u   ❌ 이미지/광고 작업에 --workflow image-qc-gate가 필수입니다. 의도적 스킵 시 --skip-qc-gate 플래그를 추가하세요.r(  u-   --resume과 --task-id는 동시 사용 불가u.   --resume 사용 시 --team이 필수입니다.r  rK   r   r  u"   base task 파일이 없습니다: r*  )r  r   r  z	[resume] r   r  ra  rx  z'^task-\d+(_\d+\.\d+)?(_[a-z])?(\+\d+)?$uV   [task-id-format] --task-id '%s'가 포맷 v2 규칙에 맞지 않습니다. 패턴: %sr,  r-  r.  r/  r0  )r   r/  r0  r   u    [옵션D] state.json 초기화 [z]: kind=z, merge_required=z	 (source=r1  u'   [옵션D] state.json 초기화 실패 [z]: u   task 파일에 allowed_resources YAML 블록이 없습니다. memory/specs/bot-capability-model.md 참조하여 추가하거나, legacy 호환이 필요하면 --allow-no-scope 플래그를 사용하세요.r   r2  rg  r"  r  r  r#  r  r   r  r  r%  r   r3  r{  r>   )Gutils.env_loaderr
   argparseArgumentParseradd_argumentadd_mutually_exclusive_groupBooleanOptionalActionr  
parse_argsrl  r  r%   r  cancelr  r   rR  r   getattrr  r  r  r  resumer   rj  r   r   r   r   r  r  r?  r  workflowprompts.image_workflowr  r   r   r   ImportErrorr  r$  rf  rE  r"   r@  r   r   r  r   r  r?  rA  r  rQ  rx  r}  r  r2  sessionprojectchainr#  r  r  r%  r]  r  r3  r?  )r
   parserteam_or_composite
task_groupargsr   task_contenttask_file_pathr  workflow_promptr   _image_keywordsrH  composite_teams_list
base_cleanbase_task_fileresolve_resultTASK_ID_V2_PATTERN
_task_metar]  _allowed_resourcesr*  s                        @r3   mainr    sW   .O$$1MNF ?	   r	   ;;U;K""
 # # & ""T #  44e4DJH+RSM0yz
27	   /j	   T8st
T8lm
	46ST
--`	   T8rs

D  8U  V
u	   f	   y	    !b	   p	   W	   !~	   f  
 N   h	   !N   f  
 '  	 D !djjeA>? {{T[[)djjeA>? 99T^^bc tUD!yyLLFGDHHdii0djjeA>? 99T^^GD%4NW[WbWbtu Lyyyy		/9979KL %):l)J  G  	HJJ$1yz"	
 ~~dnn-$$&

&5himiwiwhx3yz!& HHQK",,g,>DDF	

&5abfbpbpaq3rs!& HHQKIIO	y>CNN23y>2BB}~ }}'	M<.O ++;ykBI::.
QR }}[999t^U3wxX  #4++,!!#

")%VW[WgWgVh#i "' HHQK  ~~	#<T^^#L  {{<<$**<kl  |A  B  CHHQKyy$**<lm  }B  C  DHHQKyy	2t{{;J!)_x7'AzlRUDVVN$$&*44g4FLLN	djjGBdesdt@u!v  FK  L  M(diik]`]bNbhjrvr|r|}(#w.$**^%@AHHQK%m4i}E$,,yXeIfHgghij||ZZ(RS!''5NNh"** 5>%i0Ffjv  DAJ||*00>	]')*:;YY	 KK24<<. A"6*++<ZHX=Y<Z [%h/03 AJ1)<t!'$8H%*Pdjj!c 
 	 	  		 jj -	
 << <<  $$ ))  {{ jj $$ ?? jj  ..!" #$ &&%&  22'( -)* t%5u=+F. 
$**V%
:;M  		d  	

&5[\][^3_`!& HHQKK	T  	$**CFCRWXYHHQKK	t  	]NNDT\\NRUVYUZ[\\	]s]   %n -An /o) 3Aq 	nn	o& ;o!!o&)	p>2Ap99p>	q:
%q55q:__main__r=   )rv  )r   N)F)r  )r7  NNr-  )i  )r7  NFN)Nr   r7  NNNNTr-  NNFNr  NFNFFNF)__doc__r  r   r  r%   r#   r@  r   r  r   r   pathlibr   typingr   r   r   r   r  r"   __file__r   r  r
   r   utils.atomic_writer   config.loaderr4   get_instancerL  rK  r  r5   r6   r  utils.loggerr7   r  utils.composite_constantsr8   r9   __name__r   utils.redactr:   r?   r  utils.injection_guardrA   r  r  utils.approvalrB   r  r  utils.audit_loggerrC   r  r  utils.model_routerrD   r  r  utils.sanitize_gaterE   r  r  utils.bot_statusrF   r   r   utils.session_resiliencerG   ri  rh  importlib.utilutil_iluspec_from_file_location	_isr_specmodule_from_spec_isr_modloaderexec_moduleget_skill_recommendationrG  rF  get_path_ws_fallbackr  r   r   r   r  rM  _chat_fallbackr   r  r   r-  r   utils.org_loaderrb   rc   rd   r   r  r   
_teams_map_team_to_bot_mapr<  r   CROSS_FUNCTIONALra  r   r   r   r  r   r   r   r   r  r   r  r%  r)  rQ  r=  rx  r}  r|  r  r  r  r  r  r  r  r  r  r  r  r  r,  r+  tupler/  r1  rH  rb  rl  rp  rt  r  r  r  r  r  r  r  r  r  r  r  r  r  r"  r>  rH  rP  r\  rc  rf  r  r  r  r  r!  r  r  r  )r   r   rv  vs   0000r3   <module>r     s       	 	  
 (  & &	HHOOAs4>0012.O
42+%=%%'D(.G'
 
H		B'C!%
 >
$L"
$>"
%G#
"F 
*P$(!
*!,,,DN!!G+.EEFI %t$$Y/H  * ( A A$(! 4Et}}./Sh 0,?@	x"??!O3
 2Ct""9-Q]
**..+^
<$  DIIK)+j8;bbemm  ::>>,-JJNN./JJNN./JJNN./JJNN./JJNN./JJNN./JJNN./JJNN./
-
cc!#H%'J-/NX '
?'1CD*4IJ*H=	  S  )7(<(<(>?1q!t? 't 'tCcSVhDW?X 'T)`d
 )`c )`Xd3S#X3F.G DQUVY[^V^Q_L` 0 "&ff$Jf 	fR %, c S#X 0 >E !S ! !s !t !Hec e# ec e4 e*B B BJf`R3 C D FDC DD DN: :3 : :CRVJ :bf :z   t  FC?s C?td{ C?T #	      t	 
 
 P+S +T +\<D <# <RV <~T d  !$ ! !QU !HJT Jd J*C S ( [ *3 * * *Zc 3 3 6<$ <4 <~'ZS 'ZT 'ZV C  % D  6# 6# 6c 6QU 6bjknbo 6tBs B3 B3C BJ >S T 8+S +# +#T #c #LD# DNS T :SX# SX SX SXD SX]a SXl,3 ,HSM ,c ,f  $"  	
  sm  	$! !s !S !T !H23 24 2j
S 
T 

3 
 
C 
TX 
"(5C (5HSM (5V" " "JT c T .Bc Bhsm BJ)
s )
s )
t )
XM
3 M
3 M
4 M
`#s #s #3 #\` #mq #LT @#1S #1Xc] #1L'C 'C '4 '\deh\i 'T c 8 !Gu#YGuGu Gu c]	Gu
 Gu C=Gu 
Gu^9# 9$t* 9x; ;# ;$t* ;|g gs gt g^ GGG G 	G
 G d^GV "+/ $ $"! !%""$(, +`uc]`u`u `u d3i(	`u
 `u `u sm`u `u `u c]`u SM`u `u #`u `u C=`u  !`u" sm#`u$ %`u& '`u(  ~)`u* +`u, 
-`uPT T TnK<\ zF UI  		   3 s t :  D  (.G''("  3 3    'M!&'   N   $#$  $L#$  %$%  "!"  *$)!*  * $$)!*P  '
T&&w/
,,];
#./CSCYCYC[\<4fj..\\
  	
 	

 !       	
='
j @s  ?Z$ <Z0 [ [  [6 	\ \ \, $\< -] 6] ?], A0]< ^ 
`$Z-,Z-0[[	[[[32[36\	\		\\	\)(\),	\98\9<	]	]		]]	])(]),	]98]9<	^	^	A`_+*-``