
    ?# j                     6   U d Z ddlZddlZddlZddlZddlmZmZmZ ddlm	Z	 ddl
mZmZ 	  e e	e      j                         j                         Zeej$                  vrej$                  j'                  de       ddlmZ ej.                  j1                  d e e	e      j                         j                   j                               Z e	e      dz  Z e	e      d	z  d
z  Z	  eed      5 Z ej<                  e      Zddd       ej1                  di       j1                  dd      Z"ej1                  di       j1                  dd      Z#	 ddlZ$eZ%e%e$j$                  vre$j$                  j'                  de%       ddl&m'Z( ddl&m)Z* ddl&m+Z,  e*       Z-e-j]                         D  ci c]  \  } }| j_                  dd      | c}} Z0e1eef   e2d<   de0d<    e,       Z3e4e   e2d<    e(       Z5e1eef   e2d<   g d*Z7e4e   e2d+<    e	d,      Z8dZ9efd-e	d.e1eef   fd/Z:efd-e	d.e1eef   fd0Z;efd-e	d.e4e1eef      fd1Z<efd2ed-e	d.ee1eef      fd3Z=efd-e	d.e1eef   fd4Z>defd5ee   d-e	d.e1eef   fd6Z?efd-e	d.e4e1eef      fd7Z@efd-e	d.e4e   fd8ZAefd-e	d.e4e1eef      fd9ZBefd-e	d.e4e1eef      fd:ZCefd-e	d.e4e1eef      fd;ZDe8fd<e	d.e4e1eef      fd=ZEefd-e	d.eFfd>ZGd?e1eef   d.e4e1eef      fd@ZHdAe1eef   d.e4e1eef      fdBZId2edCedDe1eef   d.efdEZJdFed.efdGZKdee8fd5ee   d-e	d<e	d.eLee1eef   f   fdHZMdAe1eef   dIe1eef   d.e1ee1eef   f   fdJZNdKed.efdLZOdMed.eeF   fdNZPdOe	d.efdPZQdZdQedReFd.efdSZRd[dTedReFd.efdUZSefdVe1eef   d-e	d.dfdWZTd\dXZUeVdYk(  r eU        yy# e$ r dZY Ew xY w# 1 sw Y   xY w# e ejB                  f$ r i ZY w xY wc c}} w # e6$ r dddddZ0g d Z3d!d"d#d$d%d&d'd(d)Z5Y w xY w)]u  
whisper-compile.py

bot-activity.json, task-timers.json, .done 파일, 보고서, session-guidance.json,
프로젝트 context, 질문 파일을 읽어서 XML 포맷 브리핑을 stdout으로 출력.

사용법:
    python3 whisper-compile.py [CWD]

exit code 항상 0.
    N)datetime	timedeltatimezone)Path)AnyOptional)resolveWORKSPACE_ROOTmemoryconfigzconstants.jsonutf-8encoding
thresholds
idle_hours   ghost_hours   )build_bot_to_key_map)build_short_labels)get_dev_short_ids-team TEAM_NAME_MAPu   아누anu_DEV_SHORT_IDS_BOT_TO_DEVu   1팀u   2팀u   3팀)dev1dev2dev3r   )r   r   r    dev4dev5dev6dev7dev8r   r   r    r!   r"   r#   r$   r%   )zbot-bzbot-czbot-dzbot-ezbot-fzbot-gzbot-hzbot-i)insuwiki
threadautoz
dev-systemPROJECT_KEYWORDSzG/home/jay/.claude/projects/-home-jay--cokacdir-workspace-autoset/memorybase_dirreturnc                     | dz  dz  }	 t        j                  |j                  d            }|j                  di       S # t        $ r i cY S w xY w)u=   bot-activity.json 로드 → {bot_id: {status, since}} 반환eventszbot-activity.jsonr   r   bots)jsonloads	read_textget	Exceptionr)   pathdatas      H/home/jay/workspace/.worktrees/task-2537-dev4/scripts/whisper-compile.pyload_bot_activityr7   ]   sU    h!44Dzz$..'.:;xx## 	s   6A AAc                    | dz  }	 t        j                  |j                  d            }|j                  di       }t        j                  t        j                        }|t        d      z
  }i }|j                         D ]R  \  }}|j                  dd      }	|	d	k(  r|||<   #|	d
k(  s)|j                  d      }
|
s=	 t        |
      }||k\  r|||<   T |S # t        $ r i cY S w xY w# t        $ r Y sw xY w)uT   task-timers.json 로드 → running 작업 + 24시간 이내 completed 작업 반환task-timers.jsonr   r   tasks   hoursstatusr   running	completedend_time)r.   r/   r0   r1   r2   r   nowr   utcr   items	_parse_dt)r)   r4   r5   r:   rB   cutoffresulttask_idtaskr>   end_time_strrA   s               r6   load_task_timersrK   g   s   ((Dzz$..'.:; $" 5 ,,x||
$C92&&FF (B'Y"F7O{"88J/L(6H6)*.w M+  	$ ! s#   7C  C*C'&C'*	C65C6c                 *   | dz  }g }|j                         s|S |j                         D ]W  }|j                  }|j                  d      s!	 t	        j
                  |j                  d            }|j                  |       Y |S # t        $ r Y gw xY w)u   events/*.done 파일 스캔 (.done.acked, .done.escalated 등 확장자 파일 자동 제외) → 미처리 완료 작업 목록r,   z.doner   r   	existsiterdirnameendswithr.   r/   r0   appendr2   )r)   
events_dirresultsfrP   r5   s         r6   scan_done_filesrV      s    H$J$&G! 	vv}}W%	::akk7k;<DNN4 	 N  		s   6B	BBrH   c                    |dz  }||  dz  }|j                         sy	 |j                  d      }i }dD ]v  }d| d}t        j                  ||t        j
                        }|s1|j                  d	      j                         }	|	j                  d
      d   j                         }
|
||<   x |r|S dS # t        $ r Y yw xY w)u8   reports/<task_id>.md 에서 SCQA 추출. 없으면 None.reportsz.mdNr   r   )SCQAz\*\*z%\*\*:\s*(.+?)(?=\n\*\*[SCQA]\*\*:|\Z)   
r   )	rN   r0   r2   researchDOTALLgroupstripsplit)rH   r)   reports_dirr4   contentscqakeypatternmatchvalue
first_lines              r6   extract_report_scqarm      s    Y&KG9C(D;;=..'.2 D# ##CD		'7BII6KKN((*ET*1-335J"DI# 4!T!  s   B5 5	C Cc                     | dz  dz  }	 t        j                  |j                  d            }|S # t        $ r i cY S w xY w)u   session-guidance.json 로드whisperzsession-guidance.jsonr   r   )r.   r/   r0   r2   r3   s      r6   load_guidancerp      sI    i"99Dzz$..'.:; 	s   &1 ??cwdc                    |dz  }i }|j                         s|S d}| rt        D ]
  }|| v s|} n |r-||z  dz  }|j                         r	 t        |      }|||<   |S |S t	        |j                               D ]D  }|j                         s|dz  }|j                         s*	 t        |      }|||j                  <   F |S # t        $ r Y |S w xY w# t        $ r Y dw xY w)u   
    프로젝트 context.md 로드.

    cwd 인자에서 프로젝트명 추출 (insuwiki/threadauto/dev-system 포함 시).
    없으면 모든 프로젝트 context.md 첫 줄 요약.

    반환: {project_name: first_line}
    projectsNz
context.md)rN   r(   _read_first_liner2   sortedrO   is_dirrP   )	rq   r)   projects_dirrG   matched_projectkeywordcontext_pathrl   proj_dirs	            r6   load_project_contextr|      s$    j(LF  &*O
' 	G#~")	
 #o5D -l;
*4'  M6M |3356 		H??$#l2L""$!1,!?J,6F8==)		 M   M ! s$   C $C	CC	CCc                 :   | dz  dz  }g }|j                         s|S |j                         D ]\  }|j                  }d|v r|j                  d      s&	 t	        j
                  |j                  d            }|j                  |       ^ |S # t        $ r Y lw xY w)u1   events/questions/*.json 로드 (.answered 제외)r,   	questionsz	.answeredz.jsonr   r   rM   )r)   questions_dirrT   rU   rP   r5   s         r6   load_questionsr      s    x'+5M$&G!""$ vv$}}W%	::akk7k;<DNN4  N  		s   6B	BBc                 >   | dz  }	 |j                  d      }g }|j                         D ]  }d|vr|}t        j                  dd|      }t        j                  dd|j                               }t        j                  dd|j                               }t        j                  d	d
|      }t        j                  dd
|      }t        j                  dd
|      }|j                         }|s|j                  t        |d              |S # t        $ r g cY S w xY w)u  MEMORY.md에서 ★ 포함 라인만 추출하여 간결한 리마인더 문자열 목록 반환.

    마크다운 서식(##, **, ~~, 취소선 등)을 제거하고 핵심 설명만 추출.
    파일 없거나 에러 시 빈 리스트 반환 (best-effort).
    z	MEMORY.mdr   r   u   ★z^#+\s*r   z	^\d+\.\s*z^[-*]\s*z	~~(.+?)~~z\1z\*{1,2}(.+?)\*{1,2}z_{1,2}(.+?)_{1,2}(   )max_len)r0   r2   
splitlinesr_   subrc   rR   	_truncate)r)   r4   rf   	reminderslinecleaneds         r6   load_memory_remindersr     s    k!D..'.2 I""$ =&&B0&&r7==?;&&b'--/:&&ug6&&/@&&-ug>--/Yw;<#=& /  	s   D DDc                 Z    	 ddl m} | dz  }| dz  } |||      S # t        $ r g cY S w xY w)u   memory-check-log.json과 task-timers.json을 비교하여 MC 미발급 태스크를 반환.

    best-effort: 모듈 임포트 실패 시 빈 리스트 반환.
    r   )get_unchecked_taskszmemory-check-log.jsonr9   )log_pathtimers_path)utils.memory_checkr   r2   )r)   r   r   r   s       r6   _load_unchecked_tasksr   *  sA    
:55!33"H+NN 	s    **c                 (   | dz  }	 t        j                  |j                  d            }g }|j                  dg       D ]>  }|j                  d      }|j                  d      }|(|+|j	                  ||d       @ |S # t
        $ r g cY S w xY w)u   active-projects.json에서 진행 중인 프로젝트 이름과 진행률 반환.

    반환: [{"name": "Memory Automation System", "progress": 90}, ...]
    파일 없거나 에러 시 빈 리스트 반환.
    zactive-projects.jsonr   r   activerP   progress)rP   r   )r.   r/   r0   r1   rR   r2   )r)   r4   r5   rG   projrP   r   s          r6   load_project_progressr   9  s     ,,D
zz$..'.:;')HHXr* 	DD88F#Dxx
+HH$8tBC		D
  	s   A B (B +B BBc                 \   | dz  }	 t        j                  |j                  d            }|j                  di       }t        j                  t        j                        }t        t              }g }|j                         D ]  \  }}|j                  d      dk7  r|j                  dd	      }	|	s0	 t        |	      }
||
z
  }||k\  rAt        |j                         d
z        }|j                  ||j                  dd	      |d        |S # t        $ r g cY S w xY w# t        $ r Y w xY w)u   task-timers.json에서 status==pending이고 STALE_DAYS_THRESHOLD일 이상 경과한 태스크 반환.

    반환: [{"task_id": "...", "description": "...", "days_since": 5}, ...]
    파일 없거나 에러 시 빈 리스트 반환.
    r9   r   r   r:   )daysr>   pending
start_timer   iQ description)rH   r   
days_since)r.   r/   r0   r1   r2   r   rB   r   rC   r   STALE_DAYS_THRESHOLDrD   rE   inttotal_secondsrR   )r)   r4   r5   r:   rB   	thresholdrG   rH   rI   	start_strstart_dtelapsedr   s                r6   load_stale_tasksr   M  s2    ((Dzz$..'.:; $" 5 ,,x||
$C34I#%F 88H*HH\2.		 +HHnG)# !6!6!85!@A
#*'+xxr'B&0* M9  	2  		s$   7D 4ADDD	D+*D+feedback_dirc                 l   	 | j                         sg S t        | j                  d            }|sg S t        |d d      dd }g }t	        j
                  dt        j                        }|D ]  }	 |j                  d	      }|j                         j                  }t        j                  |      j                  d
      }|j                  |      }	|	r|	j                  d      j                         n|j                   }
|j#                  |
|d        |S # t$        $ r Y w xY w# t$        $ r g cY S w xY w)u   feedback_dir에서 feedback_*.md 파일 스캔, 최신 3개의 name/date 반환.

    반환: [{"name": "직접 코딩 금지 위반", "date": "2026-04-09"}, ...]
    디렉토리 없거나 에러 시 빈 리스트 반환.
    zfeedback_*.mdc                 6    | j                         j                  S N)statst_mtime)rU   s    r6   <lambda>z&load_recent_mistakes.<locals>.<lambda>  s    16683D3D     T)rh   reverseNr   z^---\n.*?name:\s*(.+?)\n.*?---r   r   z%Y-%m-%dr]   )rP   date)rN   listglobru   r_   compilera   r0   r   r   r   fromtimestampstrftimer`   rb   rc   stemrR   r2   )r   filesfiles_sortedrG   ri   rU   rf   mtimedate_strrj   rP   s              r6   load_recent_mistakesr   v  s$   ""$I\&&78Ie)DdSTVUVW')**>		J 		A++w+7))#11%8AA*Mw/16u{{1~++-AFFtX>?		     	s@   D% D% =D% 0B"DD% 	D"D% !D""D% %D32D3c                 L   | dz  }	 |j                         syd}t        j                  dt        j                        }|j	                  d      D ]4  }	 |j                  d      }|j                  |      }|t        |      z  }6 |S # t        $ r Y Dw xY w# t        $ r Y yw xY w)u   base_dir/learnings/*.md 에서 status==pending 발생 횟수 반환.

    반환: int (pending 레코드 총 카운트)
    디렉토리 없거나 에러 시 0 반환.
    	learningsr   zX^\*\*status\*\*:\s*pending\s*$|^-\s*\*\*status\*\*:\s*pending\s*$|^status:\s*pending\s*$z*.mdr   r   )	rN   r_   r   	MULTILINEr   r0   findalllenr2   )r)   learnings_dircountri   rU   rf   matchess          r6   load_pending_learningsr     s     {*M##%**gikiuiu
 ##F+ 	A++w+7!//'2W%		     s:   B :B 1BB 	BB BB 	B#"B#r-   c                    t        j                  t        j                        }t	        t
              }g }| j                         D ]  \  }}|dk(  r|j                  d      dk7  r!|j                  dd      }	 t        |      }||z
  }||k\  r>|j                         dz  }	t        j                  ||      }
|j                  ||
|	d        |S # t        $ r Y w xY w)	u   
    bot-activity.json bots 데이터에서 3시간 이상 유휴 팀 탐지.

    반환: [{"team_id": "dev2", "team_name": "2팀", "hours": 4.2}, ...]
    r<   r   r>   idlesincer     )team_id	team_namer=   )r   rB   r   rC   r   IDLE_THRESHOLD_HOURSrD   r1   rE   r   r   rR   r2   )r-   rB   r   
idle_teamsbot_idinfo	since_strr   r   r=   r   s              r6   detect_idle_teamsr     s     ,,x||
$C 45I')J

 U?88H'HHWb)		i(EEkG)#--/$6)--ff=	!!#)%.!&,   		s   :AC	CCtask_timersc                 J   t        j                  t        j                        }t	        t
              }g }| j                         D ]  \  }}|j                  d      dk7  r|j                  dd      }|s0	 t        |      }||z
  }||k\  rt        |j                         dz  d      }	|j                  dd	      }
|
j                  d
d      }t        j                  ||      }|j                  |||	t        |j                  dd            d        |S # t        $ r Y w xY w)uC   running 상태가 GHOST_THRESHOLD_HOURS 이상인 태스크 탐지.r<   r>   r?   r   r   r   r]   r   unknownr   r   )rH   r   r=   desc)r   rB   r   rC   r   GHOST_THRESHOLD_HOURSrD   r1   rE   roundr   replacer   rR   _short_descr2   )r   rB   r   ghostsrH   rI   r   startr   r=   r   bot_keyr   s                r6   detect_ghost_tasksr     s   
,,x||
$C 56I#%F$**, 88H*HH\2.		i(EEkG)#g335<a@((9i8!//'26)--gw?	#*%.!& +DHH]B,G H	2 M  		s   7BD	D"!D"fallback_statuscachec                     | |v r||    S t         t        |      }n'	 t        |       }|j                  dt        |            }||| <   |S # t        $ r t        |      }Y w xY w)u  task의 객관적 verdict 반환. resolver 호출 1회/task만 (캐시).

    Args:
        task_id: 조사할 task ID
        fallback_status: task-timers.json의 단순 status (resolver 실패 시 사용)
        cache: 호출 측이 제공하는 dict — 같은 briefing cycle 안에서 공유

    Returns:
        verdict: 'in_progress' | 'completed' | 'stalled' | 'unknown'
    verdict)_resolve_bot_status_fallback_verdict_from_statusr1   r2   )rH   r   r   r   rG   s        r6   resolve_task_verdictr     s{     %W~ "/@	E(1Fjj,I/,Z[G E'NN	  	E3ODG	Es   &A
 
A! A!r>   c                 (    | dk(  ry| dk(  ry| dk(  ryy)uO   task-timers.json status → verdict 매핑 (resolver 사용 불가 시 폴백).r@   r?   in_progressstalestalledr    )r>   s    r6   r   r     s&    r   c                    t        |      }t        |      }t        |      }t        |      }t	        | |      }t        |      }t        |      }	dg}
g }t        D cg c]	  }||v s| }}|D cg c]  }||vs|dk7  s| }}||z   }i }i }g }|j                         D ]  \  }}|j                  d      dk7  rt        ||j                  dd      |      }|j                  dd      }|j                  d	d      }|d
k(  r"|j                  |g       j                  |       |dv r|j                  |||d       |j                  |g       j                  |        t        |t              }t!        t              }|D ]  }||   }t"        j                  ||      }|j                  dd      }|j                  |g       }||v }|r4t%        j&                  t(        j*                        } t-        t.              }!g }"d}#|dd D ]  }$t1        |$j                  dd            }%d}&|s|$j                  d      r	d|$d    d}&|$j                  dd      }'d}(|'r	 t3        |'      })| |)z
  |!k\  rd}(|(r|"j                  |$d    |& d|% d       |"j                  |$d    |& d|%        d}# |#r(|j                  | ddj7                  |"       d       V|j                  | ddj7                  |"       d       ~||v r)|r'||   }*|j                  | d|*d     d|*d    d       |d!k(  s|d"k(  rP|sN|j                  d#d      }+t9        |+      },|,|j                  | d$|, d%       |j                  | d&       |j                  | d'        |s|
j                  d(       n#|
j                  d)d*j7                  |      z          g }-|D ]  }.|.j                  dd      }|st;        ||      }/|/r|/j                  d+d      }0|/j                  d,d      }1|/j                  d-d      }2|/j                  d.d      }3d/t=        |0       d0t=        |1       d1t=        |2       d2t=        |3       }4|-j                  | d3|4        |-j                  |        |-r$|
j                  d4dj7                  |-      z          n|
j                  d5       |rrg }5|j                         D ]:  \  }6}7|7j?                  d6      jA                         }8|5j                  |6 d7|8        < |
j                  d8d*j7                  |5      z          |j                  d9d      }9|9r|
j                  d:|9        n|
j                  d;       |j                  d<g       }:|:r/d=j7                  d> |:dd? D              };|
j                  d@|;        tC        |      }<|<r>|<D =cg c]  }=|=dA    d|=dB    dC }>}=|
j                  dDd*j7                  |>      z          tE        |      }?|?rZ|?D 0cg c],  }0|0d    dt1        |0j                  dd             dE|0dF    dG. }@}0|
j                  dHdj7                  |@      z          tG        |I      }A|Ar>AD Bcg c]  }BdJ|BdA    dK|BdL    d }C}B|
j                  dM       |
jI                  |C       tK        |      }D|
j                  dN|D dO       |rg }E|D ]j  }2|2j                  dPdQ      }F|2j                  dRd      }G|2j                  dd      }H|HrEj                  F dH dSG        UEj                  F d7G        l |
j                  dTdj7                  E      z          n|
j                  dU       |	r^g }I|	D ]2  }J|Jd   |v rtM        JdV         }KIj                  |JdW    d|K dX       4 Ir#|
j                  dYdj7                  I      z          	 dZd[l'm(}L  |L       }M	 MjS                  d\d]d?d^_      }N|NrWND Kcg c])  }K|Kj                  d`dQ       d7|Kj                  dad       + }O}K|
j                  dbdj7                  |O      z          MjU                          	 |rM|
j                  dc       |D ]&  }P|
j                  dd|Pd    de|Pd    df|Pdg    dh       ( |
j                  di       |
j                  dj       tW        |      }Q|QrWg }RQD ],  }SRj                  dk|Sd    d|SdW    dl|SdV    dm|Sd    dn	       . |
jY                  dodpdj7                  R      z          t[        |      }T|Tr6TD Ucg c]  }UdJ|U 	 }V}U|
jY                  dodqdj7                  |V      z          t]        |      }W|Wrg }XWD ]V  }Y|Yj                  dd      }|j                  d	d      }t"        j                  ||      }Xj                  |Yd    d| d       X |
jY                  dodrdj7                  X      z          g }ZdZ}[dZ}\|D ]  }||   }|j                  dd      }|j                  |g       }|r.[dsz  }[Zj                  t"        j                  ||       dt       \||v r=||v r9[dsz  }[||   }*Zj                  t"        j                  ||       d|*d     d       |d!k(  s|d"k(  r0|s.\dsz  }\Zj                  t"        j                  ||       d&       Zj                  t"        j                  ||       d'        |j                  du      }]t%        j&                  t(        j*                        j_                         dvd*j7                  Z      [\ta        |      ta        |      |]ddw	}^dxj7                  |
      |^fS c c}w c c}w # t4        $ r Y 	Jw xY wc c}=w c c}0w c c}Bw c c}Kw # MjU                          w xY w# t4        $ r Y pw xY wc c}Uw )yuL   모든 소스를 읽어 XML 브리핑 문자열 + 상태 딕셔너리 반환)r)   )rq   r)   z<whisper-briefing>r   r>   r?   r   r   r   r   )r   r@   )rH   r   r   r   r<   TN   r   bot()r   FrH    u    ⚠️고스트?:z / u    (실질유휴)u
    작업중u   :봇점유(teamr   
processingr   u   :유휴(zh)u   :유휴u   :상태불명u   [팀] 정보없음u   [팀] z | rY   rZ   r[   r\   zS:z C:z Q:z A:u    — u	   [완료] u   [완료] 없음z# z: u   [프로젝트] guidanceu   [가이던스] u*   [가이던스] 이전 세션 정보 없음pending_dispatchesz, c              3   2   K   | ]  }t        |        y wr   )str).0ps     r6   	<genexpr>z#compile_briefing.<locals>.<genexpr>  s     <1A<s   r   u   [대기디스패치] rP   r   %u   [진행률] u    (마지막 수정 r   u   일 전)u   [미착수] )r   z- z (r   u   [최근실수]u#   [미처리학습] 학습 피드백 u   건 미확인 (v1)from?questionz): u	   [질문] u   [질문] 없음r=   r   u   시간째 유휴u   [유휴경고] r   )MemoryIndexeru   작업 수행 피드백diarysummary)type_filterlimitlayertitlesnippetu   [최근메모리] z  <stalled-recovered>z    <task id="z" team="z" verdict="r   z"/>z  </stalled-recovered>z</whisper-briefing>u   ⚠️ z) u/   h째 running — 고스트? `task-timer.py end u   `로 정리 필요u   [고스트경고] u   [메모리 리마인더] u   [메모리미확인] r]   u
   :작업중saved_atok	last_runr>   briefing_summaryteams_active
teams_idledone_pendingquestions_pendingguidance_last_savederrorr^   )1r7   rK   rV   rp   r|   r   r   r   rD   r1   r   r   
setdefaultrR   _build_bot_occupationr   setr   r   rB   r   rC   r   r   r   rE   r2   join_idle_hoursrm   r   lstriprc   r   r   r   extendr   r   utils.memory_indexerr   r`   closer   insertr   r   	isoformatr   )_rq   r)   r   r-   r   
done_filesr   project_ctxr~   r   lines
team_partsbordered_bot_idsother_bot_idsall_bot_idsverdict_cacherunning_by_teamstalled_recoveredrH   rI   r   r   r   bot_occupation_dev_setr   r   r   r>   running_tasksis_dev_teamrB   ghost_threshold	task_strshas_only_ghoststr   
bot_suffixr   is_ghostr   occr   idle_hcompleted_partsdonerg   scqa	parts_str
proj_parts	proj_namerl   cleanguidance_textr   pending_strprogress_datar   
prog_partsstale_tasksstale_partsmistakesmmistake_partspending_learningsq_partsfrmr   task_ref
idle_partsith_WMI
_w_indexer_diary_hits
_mem_partsitemghost_tasksghost_partsgtmemory_remindersrreminder_partsunchecked_tasksunchecked_partsutteam_parts_statusr  r	  r  status_dicts_                                                                                                  r6   compile_briefingrZ  %  s   
 h/D"H5K (3Jh/H&3BK1I"4(J,-E
 J #1>QAIq>O> $P1(@Q%ZQPMP!M1K %'M79O.0$**, A88H*&w20FV((9b)//'2.m#&&w3::4@00$$"""&  &&w3::4@#A& +;DN>"H 0;F|!%%ff5	(I.'++FB7(,,x||,C'.CDOI"O"2A& ,"155#;<
"quuU|#$QuXJaJEE,3	  )) 4%KO;'+H $$)~j\4&HY%Z[$$)~j\4&%IJ&+O%,& !!YKqI1F0G"WX!!YKqI1F0Gz"RS~%+ (C;s6{m1S^DTTUVWv&L"8"-I +F!!!YKxxr"BC!!YKw"78=9:a0;d )*X

: 667
 "$O ,((9b)"7X>b!Ab!Ab!Ab!AYq\N#il^3y|nCPYZ[P\~^I""gYeI;#?@""7+, [5::o#>>?&'
 
%0%6%6%8 	7!Iz%%d+113E2eW56	7 	&J)??@
 LLR0M}o67AB ll/4Gii<<<,[M:;
 *8<M?LM!61Qz]O15M
M^ejj&<<=
 #H5K !
 |nAk!%%r*BCDDWXYZfXgWhhpq
 
 	^ejj&==>
 $>HAIJA2ai[1V9+Q7JJ%&]#
 /A	LL67H6II[\]
  	5A%%$CuuZ,HuuY+H#azXJ?@#b
34	5 	[5::g#667&'
 
 	HB)}.BwK AK 11#5EFG	H LL*UZZ
-CCD
>V
	$++,ESZbckt+uKXcdSTw!4 5Ri8L7MNd
d1EJJz4JJK ,-% 	DLL i 1$y/9J+VZ[dVeUffij	 	-.	LL&'
 %[1K 	B"Y-"[/):"R[MIxy{  }F  zG  yH  HZ  [	 	R-

;0GGH
 -h?,<=qBqc(==R4uzz.7QQR
 ,X>O! 	DBffY+Googr2G%))'7;I""bm_Ai[#BC		D
 	R05::o3NNO $&LJ ZF|(I.'++FB7AL$$(9(9&&(I'J*%UV~%&H*<AL (C$$(9(9&&(I'J+VYZ`VaUbbc%dev&L"8!OJ$$(9(9&&(I'J'%RS$$(9(9&&(I'J-%XY!Z$ #,,z2 LL.88:!JJ'89$ J ^2
#K 99U[((S	 ?Pl % l N
 Kf e  : >s   "	n*,n*6	n/ n/n/!n4$o01o	o<o- 
o %.o%o 8o- o=4	o oo o**o- -	o:9o:
bot_to_devc                    	 ddl m}  ||       j                         S # t        $ r Y nw xY wt	        |j                               }i }| j                         D ]  \  }}|j                  d      dk7  r|j                  dd      }|j                  dd      }|sB|j                  d	d      }	|	|v rY|j                  |      }
|
sm||j                  d
|      |d||
<    |S )u   논리적 팀이 물리 봇을 점유하는 경우를 탐지.

    BotStatusManager 사용 가능 시 위임, 실패 시 기존 로직 fallback.

    Returns: {dev_short_id: {"team": team_id, "task_id": task_id, "bot_id": bot_id}}
    r   )BotStatusManager)r   r>   r?   r   r   r   r   rH   )r   rH   r   )	utils.bot_statusr]  get_bot_occupationr2   r  valuesrD   r1   r   )r   r[  r]  dev_short_ids
occupationrH   rI   r   r   
team_short	dev_owners              r6   r  r  k  s    5K8KKMM  
))+,M,.J$**, 88H*((9b)%$ __Wb1
& NN6*	88Iw7 %Jy!!, s    	++r2  c                     | j                         } | j                  d      r| dd dz   } t        j                  |       }|j                   |j                  t        j                        }|S )u4   ISO8601 문자열 파싱 → timezone-aware datetimeZNr  z+00:00)tzinfo)rc   rQ   r   fromisoformatrg  r   r   rC   )r2  dts     r6   rE   rE     s[    		Azz#crFX				"B	yyZZx||Z,Ir   r   c                     	 t        |       }t        j                  t        j                        |z
  }t        |j                         dz        S # t        $ r Y yw xY w)uM   since 문자열로부터 유휴 시간(정수 시간) 계산. 실패시 None.r   N)rE   r   rB   r   rC   r   r   r2   )r   r   r   s      r6   r  r    sS    )$,,x||,u47((*T122 s   AA 	AAr4   c                 z    | j                  d      j                         D ]  }|j                         }|s|c S  y)u3   파일의 첫 번째 비어있지 않은 줄 반환r   r   r   )r0   r   rc   )r4   r   strippeds      r6   rt   rt     s=    0;;= ::<O r   r   r   c                 2    t        |       |k  r| S | d| dz   S )u&   설명을 max_len 자 이내로 축약N   …)r   )r   r   s     r6   r   r     s$    
4yG>E!!r   textc                 R    | j                         } t        |       |k  r| S | d| dz   S )u)   텍스트를 max_len 자 이내로 축약Nrn  )rc   r   )ro  r   s     r6   r   r     s/    ::<D
4yG>E!!r   rY  c                     |dz  dz  }	 |j                   j                  dd       |j                  t        j                  | dd      d	       y
# t
        $ r Y y
w xY w)u    status.json 저장 (best-effort)ro   zstatus.jsonT)parentsexist_okFr   )ensure_asciiindentr   r   N)parentmkdir
write_textr.   dumpsr2   )rY  r)   status_paths      r6   save_statusr{    sc    Y&6K   =tzz+ERST_fg s   AA 	AAc                     t        t        j                        dkD  rt        j                  d   nd } 	 t        |       \  }}t	        |       t        |       t        j                  d       y # t        $ rd}t        j                  t        j                        j                         ddddddd t        |      d	}t        |       t	        d       Y d }~~d }~ww xY w)Nr]   )rq   r  r   r   r  uO   <whisper-briefing>[에러] 위스퍼 브리핑 생성 실패</whisper-briefing>)r   sysargvrZ  printr{  r2   r   rB   r   rC   r  r   exit)rq   outputrY  eerror_statuss        r6   mainr    s    (+CHH(9!tCa.37fK   HHQK  a !X\\2<<> "!"#'V

 	L!_``as   %A. .	C7ACC__main__)   )   )r*   N)W__doc__r.   osr_   r}  r   r   r   pathlibr   typingr   r   r   __file__r	   rv  _scripts_dirr4   r  bot_status_resolverr   r2   environr1   _WORKSPACE_ROOTBASE_DIR_CONSTANTS_PATHopen_fload
_CONSTANTSFileNotFoundErrorJSONDecodeErrorr   r   _sys_workspace_rootutils.org_loaderr   _build_bot_to_key_mapr   _build_short_labelsr   _get_dev_short_ids_short_labelsrD   r   r   dict__annotations__r   r   r   ImportErrorr(   ANU_FEEDBACK_DIRr   r7   rK   rV   rm   rp   r|   r   r   r   r   r   r   r   r   r   r   r   r   tuplerZ  r  rE   r  rt   r   r   r{  r  __name__)tidlabels   00r6   <module>r     s7  
  	 	 
 2 2   tH~--/667L388#<(B **..!13tH~7M7M7O7V7V7]7]3^_ 8+'(25EE	o	0 #BTYYr]
#
 "~~lB7;;L!L "|R8<<]AN !%Odii'		O,NJH ()MWdWjWjWl$meS[["%=u%D$mM4S>m#M% 2 4NDI4"7"9Kc3h9( G $s) Fab   (0  DcN  '/ t 4S> < &. d $tCH~2F , 8@ " " "HTRUWZRZ^D\ "6 $, D S#X  /3X ,hsm ,d ,RVWZ\_W_R` ,^ %- T d38n1E 0 ,4  D  S	  F ,4 D T#s(^8L  ,4 D T#s(^8L ( '/ &t &4S#X3G &R /? t 4SRUXCW > -5 T  4 DcN  tDcN/C  FDcN tDcN7K D S> 		B# # " O_~)	#~))-~)HL~)
3S#X~)L
(tCH~ (4S> (VZ[^`dehjmem`n[nVo (V	 	 	3 8C= 4 C "c "C " ""C "# "s " ?G T#s(^ t 4 0 zF a  # #4//0 J$ %n  	M VN	Ksa   AN> '
O 1OO AO9 -O3
3O9 >O	O	OO O0/O03O9 9PP