
    ޡi:6              	       L   U d Z ddlZddlZddlZdZdZdZdZh dZde	d	e
fd
Zd/de	de	d	e
fdZde	d	e
fdZde	d	eee   ee	   f   fdZde	dee   d	edz  fdZdee	   d	efdZ	 	 d0de	dee	   dz  de	d	efdZedk(  r ddlZ ej.                  dd      Zej3                  dd       ej3                  dddd !       ej3                  d"d#dd$e d%!       ej5                         ZdZee	   dz  ed&<   ej<                  rHej<                  j?                  d'      D  cg c]#  } | jA                         s| jA                         % c} Z eejB                  eejD                  (      Z# e$ ejJ                  e#d)d*+              ejL                  e#d,   d-v rdnd.       yyc c} w )1u<  
tdd_check.py - TDD 순서 검증 verifier

audit-trail.jsonl에서 파일 수정 순서를 분석하여 테스트 파일이
구현 파일보다 먼저 작성되었는지(Test-Driven Development) 확인합니다.

task_id 필드가 audit-trail에 없는 경우 check_files 기반 fallback 검증을 수행합니다.
    Nz1/home/jay/workspace/memory/logs/audit-trail.jsonl)test_)z.test.tsz	.test.tsxz.spec.tsz	.spec.tsx)z/tests/z/test/>   .cfg.css.csv.env.gif.ico.ini.jpg.log.png.svg.tsv.txt.xml.yml.html.jpeg.json.lock.toml.yaml.example.gitkeep
.gitignore.prettierrc.editorconfig.mdfilepathreturnc                 r    t         j                  j                  |       \  }}|j                         t        v S )u2   비코드 파일(문서, 설정 등)인지 판별.)ospathsplitextlowerNON_CODE_EXTENSIONS)r   _exts      8/home/jay/workspace/teams/dev1/qc/verifiers/tdd_check.py_is_non_code_filer*      s,    WWh'FAs99;---    task_idworkspace_rootc                 P   t         j                  j                  |dd|  d      }t         j                  j                  |      sy	 t	        |dd      5 }|j                         }ddd       d	dl}t        |j                  d
            S # 1 sw Y   (xY w# t        $ r Y yw xY w)uW   task 파일에서 non-code 키워드 감지. 코드 수정이 없는 작업이면 True.memorytasksr   Frutf-8encodingNr   ui   코드 수정 없음|문서만|문서 업데이트만|DB 업데이트만|분석만|검증만|리서치만)
r"   r#   joinisfileopenreadreboolsearchOSError)r,   r-   	task_pathfcontentr9   s         r)   _is_non_code_taskr@   $   s    ^Xw7)3PI77>>)$	)S73 	qffhG	BIIx
  		 	  s*   B B&&B BB 	B%$B%c                     t         j                  j                  |       }t        D ]  }|j	                  |      s y t
        D ]  }|j                  |      s y | j                  dd      }t        D ]  }||v s y y)u  
    주어진 파일 경로가 테스트 파일인지 판별합니다.

    판별 기준:
    - 파일명이 'test_'로 시작하는 경우 (Python unittest 관례)
    - 파일명이 '.test.ts', '.test.tsx', '.spec.ts', '.spec.tsx'로 끝나는 경우
    - 경로에 '/tests/' 또는 '/test/' 디렉토리가 포함된 경우

    Args:
        filepath: 검사할 파일 절대경로 또는 상대경로

    Returns:
        테스트 파일이면 True, 아니면 False
    T\/F)	r"   r#   basenameTEST_PREFIXES
startswithTEST_SUFFIXESendswithreplace	TEST_DIRS)r   rD   prefixsuffix
normalizedtest_dirs         r)   _is_test_filerO   5   s     ww)H v&   V$
 !!$,J z! r+   
trail_pathc           	      ,   g }g }	 t        | dd      5 }t        |d      D ]=  \  }}|j                         }|s	 |j                  t	        j
                  |             ? 	 ddd       ||fS # t        j                  $ r!}|j                  d| d|        Y d}~|d}~ww xY w# 1 sw Y   DxY w# t        $ r |j                  d	|         Y ||fS t        $ r7}|j                  d
t        |      j                   d|        Y d}~||fS d}~ww xY w)un  
    audit-trail.jsonl 파일을 읽어 엔트리 목록과 파싱 에러 메시지를 반환합니다.

    Args:
        trail_path: audit-trail.jsonl 파일 경로

    Returns:
        (entries, error_messages) 튜플.
        entries: 파싱에 성공한 JSON 객체 목록
        error_messages: 파싱 실패 또는 파일 읽기 오류 메시지 목록
    r1   r2   r3      )startzJSON parse error at line z: Nzaudit-trail not found: zFailed to read audit-trail: )r7   	enumeratestripappendjsonloadsJSONDecodeErrorFileNotFoundErrorr<   type__name__)rP   entrieserrorsr>   linenolinees          r)   _load_audit_entriesrb   W   s5    GFN*cG4 	M )!1 5 Mzz|MNN4::d#34M	M F? ++ MMM$=fXRs"KLLM	M 	M  >/
|<= F?  N4T!W5E5E4FbLMMF?Ns]   B2 'B&$A/ B&#B2 /B#BB&B##B&&B/+B2 2DD*DDr]   c           	         |D cg c]  }|j                  d      | k(  s| }}|syh d}|D cg c]*  }|j                  d      |v s|j                  d      s)|, }}|sdd|  dt        |       d	gd
S i }|D ]@  }t        j                  j	                  |d         }|j                  dd      }	||vs<|	||<   B |j                         D 
	ci c]  \  }
}	t        |
      s|
|	 }}
}	|j                         D 
	ci c]   \  }
}	t        |
      rt        |
      r|
|	" }}
}	d|  ddt        |       dt        |       dt        |       dg}|s!|s	d|dgz   d
S |j                  d       d|d
S |sN|j                  d       t        |j                               D ]  \  }
}	|j                  d|	 d|
         d|d
S t        |j                               }t        |j                               }t        |j                         d       D ]  \  }
}	|j                  d|	 d|
         t        |j                         d       D ]  \  }
}	|j                  d|	 d|
         ||k  r|j                  d| d| d        d|d
S |j                  d!| d"| d#       d$|d
S c c}w c c}w c c}	}
w c c}	}
w )%u  
    audit-trail 엔트리에서 task_id로 필터링하여 TDD 순서를 검증합니다.

    task_id 필드가 존재하는 엔트리가 없으면 None을 반환하여
    fallback 검증으로 위임합니다.

    Args:
        task_id: 검사 대상 task ID
        entries: audit-trail에서 로드한 전체 엔트리 목록 (시간순 정렬 가정)

    Returns:
        검증 결과 dict, 또는 task_id 매칭 엔트리가 없으면 None
    r,   N>   EditWriteNotebookEdittoolfileSKIPz	task_id='u   ' 엔트리 uC   개 발견, 그러나 파일 변경(Edit/Write/NotebookEdit) 없음statusdetailsts u$   audit-trail 기반 검증 (task_id='z')u   변경 파일 총    개: 테스트    개, 구현    개u   변경된 파일 없음u@   구현 파일 변경 없음, 테스트 파일만 존재 → PASSPASSu;   테스트 파일 없이 구현 파일만 변경됨 → FAILz	  IMPL  [z] FAILc                     | d   S NrR    xs    r)   <lambda>z(_verify_by_audit_trail.<locals>.<lambda>   
    1Q4 r+   )keyz	  TEST  [c                     | d   S ru   rv   rw   s    r)   ry   z(_verify_by_audit_trail.<locals>.<lambda>   rz   r+   u   테스트 먼저 수정 (u   ) → 구현 (u
   ) → PASSu   구현 먼저 수정 (u   ) → 테스트 (u   ) → WARN (TDD 순서 위반)WARN)getlenr"   r#   normpathitemsrO   r*   rV   sortedminvalues)r,   r]   ra   task_entrieswrite_toolschangedfirst_tsentryr   rm   fp
test_files
impl_filesrl   earliest_test_tsearliest_impl_tss                   r)   _verify_by_audit_trailr   x   s7   "  'F!!%%	*:g*EAFLF 4K&2eaeeFm{6RWXW\W\]cWd1eGeG9L\1B0C DJ J
 	
  "H $77##E&M2YYtR 8#!#HX	$ (0~~'7MVR=;L"b&MJM'/~~'7oVR}R?PYjkmYn"b&oJo /wir:
S]O?3z?:K<X[\fXgWhhklG
 $<U;V1VWWYZ W55TUZ--/0 	3FBNNYrd"RD12	3 W55 :,,./:,,./ ))+@ /B2$b-./))+@ /B2$b-./ ++23C2DNScRddnop W55/0@/AARScRd  eC  D  	E W55 G f& Nos9   J1J1J6	J6J6J;'J;KK"Kcheck_filesc           	         | sddgdS | D cg c]  }t        |      s| }}| D cg c]  }t        |      rt        |      r| }}ddt        |        dt        |       dt        |       dg}|s|s	d|d	gz   dS |s:t        |      D ]  }|j	                  d
|         |j	                  d       d|dS |s:t        |      D ]  }|j	                  d|         |j	                  d       d|dS t        |      D ]  }|j	                  d
|         t        |      D ]  }|j	                  d|         |j	                  d       d|dS c c}w c c}w )u  
    check_files 목록에서 테스트 파일과 구현 파일을 분리하여 TDD 준수 여부를 검증합니다.

    audit-trail에 task_id가 없는 경우의 fallback 검증입니다.
    파일 수정 순서는 알 수 없으므로 테스트 파일 존재 여부만 확인합니다.

    Args:
        check_files: 검사 대상 파일 경로 목록

    Returns:
        {"status": "PASS"|"FAIL"|"SKIP", "details": [...]}
    ri   u'   check_files 미제공 — 검증 불가rj   u$   check_files 기반 검증 (fallback)u   파일 총 ro   rp   rq   u   검사할 파일 없음z  TEST  u9   구현 파일 없음, 테스트 파일만 존재 → PASSrr   z  IMPL  uC   구현 파일 존재, 대응하는 테스트 파일 없음 → FAILrs   uR   테스트 파일과 구현 파일 모두 존재 (수정 순서 미확인) → PASS)rO   r*   r   r   rV   )r   r>   r   r   rl   s        r)   _verify_by_check_filesr      s    AB
 	

 )=M!,<!=J=(^a0@IZ[\I]!^J^ 	/
c+&'s:6G|TWXbTcSddghG
 j W8Q7R-RSS
# 	+ANNXaS>*	+RS W55
# 	+ANNXaS>*	+\] W55 J '!~&'J '!~&'NNgh11? >^s   EEEEErn   audit_trail_pathc                    |r|nt         }g }t        |       rddgdS t        |      \  }}|rG|s4|j                  |       |rt	        |      }||d   z   |d<   |S d|dgz   dS |j                  |       t        | |      }||r||d   z   |d<   |S |j                  d|  d       |rt	        |      }||d   z   |d<   |S d|dgz   dS )u  
    TDD(Test-Driven Development) 준수 여부를 검증합니다.

    검증 우선순위:
    1. audit-trail.jsonl에 task_id 필드가 있는 엔트리가 존재하면
       파일 수정 타임스탬프 순서를 기반으로 검증합니다.
    2. task_id 매칭 엔트리가 없으면 check_files 목록을 기반으로
       테스트 파일 존재 여부를 확인합니다.

    판정 기준:
    - PASS : 테스트 파일이 구현 파일보다 먼저 수정됨,
             또는 테스트 파일만 존재하거나 check_files에 테스트+구현 모두 존재
    - FAIL : 테스트 파일 없이 구현 파일만 존재
    - WARN : 구현 파일이 테스트 파일보다 먼저 수정됨 (TDD 순서 위반이지만 테스트 존재)
    - SKIP : 검사 대상 파일이 없거나 audit-trail을 읽을 수 없고 check_files도 없는 경우

    Args:
        task_id: 검사 대상 task ID
        check_files: fallback 검증에 사용할 파일 경로 목록 (선택)
        audit_trail_path: audit-trail.jsonl 경로
                          (기본값: DEFAULT_AUDIT_TRAIL_PATH)

    Returns:
        {"status": "PASS"|"FAIL"|"WARN"|"SKIP", "details": [...]}
    ri   u:   non-code task (문서/DB/분석/검증) — tdd_check SKIPrj   rl   u*   check_files도 미제공 — 검증 불가u   audit-trail에 task_id='u2   ' 매칭 엔트리 없음 — check_files fallback)DEFAULT_AUDIT_TRAIL_PATHr@   rb   extendr   r   rV   )	r,   r   r   rP   details_prefixr]   load_errorsresultaudit_results	            r)   verifyr      s2   < &6!;SJ "N !TU
 	
 /z:G[!!+./<$2VI5F$Fy! )-Y,ZZ 
 	k* *'7;L&4|I7N&NL# 4WI=opq'4*VI->>y !%Q$RR r+   __main__u   TDD 순서 검증 verifieruG   예시: tdd_check.py task-42 --check-files src/foo.py,tests/test_foo.py)descriptionepilogu   검사 대상 task ID)helpz--check-fileszFILE1,FILE2,...u=   쉼표로 구분된 파일 경로 목록 (fallback 검증용))metavardefaultr   z--audit-trailPATHu%   audit-trail.jsonl 경로 (기본값: )cf,)r,   r   r   F   )ensure_asciiindentrk   )rr   ri   rR   )z/home/jay/workspace)Nrn   )'__doc__rW   r"   sysr   rE   rG   rJ   r&   strr:   r*   r@   rO   tuplelistdictrb   r   r   r   r\   argparseArgumentParserparseradd_argument
parse_argsargsr   __annotations__r   splitrU   r,   audit_trailr   printdumpsexit)r>   s   0r)   <module>r      s`    	 
N  B!	 . . .s C TX "C D DC E$t*d3i2G,H BP6P6$ZP6 
D[P6f22S	 22d 22n %)OOcT!O O 
	Od z$X$$0XF 	(?@
!L	   45M4NaP	   DBS	D!%!1!1!7!7!<JA	aggiJ))F
 
*$**V%
:;CHH&"&66QA>C 2 Ks   F!5F!