
    -iCQ                       d Z ddlmZ ddlZddlmc mZ ddl	Z	ddl
Z
ddlZddlmZ ddlZ ee      j!                         j"                  d   Zedz  dz  Zedz  dz  Zd%d	Zd&d
Z	 	 d'	 	 	 	 	 	 	 d(dZd)dZdddd	 	 	 	 	 	 	 	 	 	 	 	 	 d*dZd+dZej6                  d,d       Zd Zd Zd Zd Z d Z!d Z"d Z#d Z$d Z%d Z&d Z'd Z(d Z)d  Z*d! Z+d" Z,d# Z-d$ Z.y)-u  tests/verify/test_taskctl_verify.py — taskctl_verify.py 11 검사 시나리오 테스트.

task-2459 Phase 2-C / dev5팀 닌기르수 (테스터).

scripts/taskctl_verify.py 의 11 개 검사를 임시 worktree + lock + task md 로
재현하여 PASS / FAIL / WARN / N/A 동작을 검증.

원칙:
  - 모듈 코드 read-only.
  - subprocess 로만 호출.
  - workspace 와 cwd 는 독립적 — `.worktrees/<id>-<bot>` 경로 패턴을 cwd 로 사용.
    )annotationsN)Path   scriptsztaskctl_verify.pymixed_commit_detector.pyc                N    t        j                  dg|t        |       ddd      S )NgitTFcwdcapture_outputtextcheck)
subprocessrunstr)r   argss     Q/home/jay/workspace/.worktrees/task-2459-dev5/tests/verify/test_taskctl_verify.py_gitr       s(    >>	CHTE     c                    | dz  dz  | dz  }|j                   j                  dd       dj                  d |D              }|j                  d| d	| d
d       |S )u2   task md 파일을 yaml 블록 포함하여 작성.memorytasks.mdTparentsexist_ok
c              3  (   K   | ]
  }d | d  yw)z    - ""N ).0xs     r   	<genexpr>z _make_task_md.<locals>.<genexpr>*   s     =XaS+=s   # z&

```yaml
allowed_resources:
  paths:
z
```
utf-8encoding)parentmkdirjoin
write_text)	workspacetask_idallowedp
paths_yamls        r   _make_task_mdr1   &   s~    Hw&G9C8AHHNN4$N/=W==JLLwi 
  	   
 Hr   c                   | dz  }|j                          t        dddd|       t        ddd	|       t        dd
d|       t        ddd|       |dz  dz  }|j                  d       || dz  j                  dd       |dz  dz  j                  d       |dz  }|j                  d       |dz  }|j                  t        j	                  d      d       t        ||g d       |dz  | d| z  }|j                  d       |dz  j                  dd       t        dd |       t        d!dd"d#|       t        d$d%|      j                  j                         }t        d&d'||       d(| d| }	t        d)dd|	|       |d*z  }
|
j                  d+       |
d,z  j                  d-d       t        dd.|       t        d!dd"d/| d0|       |||||	|d1S )2u  테스트용 전체 셋업.

    Layout (workspace 자체가 git repo, .worktrees 는 같은 repo 내 하위 디렉토리):
      tmp_path/
        workspace/                       <- WORKSPACE_ROOT, git repo
          .git/
          .tasks/locks/<task>.lock
          memory/tasks/<task>.md
          memory/events/
          scripts/mixed_commit_detector.py  (실제 detector 복사)
          src/                            (allowed_resources 매치용)
          .worktrees/<task>-<bot>/        <- cwd. 같은 repo 의 하위 디렉토리.

    git worktree 분리 모킹 대신 단일 repo 의 subdir 로 구성.
    git 명령은 어떤 cwd 에서도 같은 repo 를 인식한다.
    r,   init-q-bmainr   configz
user.emailztest@example.comz	user.nametesterzcommit.gpgsignfalse.taskslocksT)r   .lockzok
r%   r&   r   eventsr   r   )zsrc/**z	memory/**ztests/**z
scripts/**z	.tasks/**z
.worktrees-z.keepzkeep
add-Acommit-mzbase: initial setupz	rev-parseHEADz
update-refzrefs/remotes/origin/mainztask/checkoutsrc)r   z	module.pyz# implementation
zsrc/module.py[z] feat: add module)r,   worktreer-   botbranchbase_sha)r)   r   r+   DETECTOR_SRC	read_textr1   stdoutstrip)tmp_pathr-   rI   r,   r<   scripts_dirdetector_dstrH   rK   rJ   src_dirs              r   setup_full_reporT   9   s7   * ;&IOO 	tV3<!3C;i8#W)<  7*E	KKK
y**6G*D H$++D+9
 i'Kd#!;;LL22G2DwW F <'WIQse*<<HNN4N ##Hw#? 	)$44)D KY7>>DDFH18K WIQse$FT4Y7 %GMM4M {&&';g&NY/47)+=>IN  r   c                @    t        dd|        t        ddd||        y)u:   현재 staged/untracked 모두 commit (dirty_tree 정리).r@   rA   r7   rB   r4   rC   N)r   )r,   messages     r   _commit_allrW      s    )$4wI6r   Tr    )rI   	json_modeextrac                   t         j                  t        t              | dt        |      g}|r|d|gz  }|r|dgz  }|t	        |      z  }t        j                  |t        |      ddd      S )Nz--workspacez--botz--jsonTFr
   )sys
executabler   VERIFYlistr   r   )r-   rI   r,   r   rX   rY   cmds          r   
run_verifyr`      sn     >>3v;I
OC
~z4;C>>S$T r   c                   | j                   }|st        j                  d| j                         dz   dt	        j
                         v st        j                  |       rt        j                  |       ndt        j                  |      dz  }t        t        j                  |            d }t        j                  | j                         S )Nu   stdout 비어있음: stderr=z,
>assert %(py2)s
{%(py2)s = %(py0)s.stdout
}res)py0py2)rN   
@pytest_ar_format_assertmsgstderr@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationjsonloads)rb   @py_assert1@py_format3s      r   _parse_payloadrr      sq    ::B:BB5cjj\BBBBBBB3BBB3BBB:BBBBBB::cjj!!r   c                    t        |       S )N)rT   )rP   s    r   
full_setuprt      s    8$$r   c                   | }t        |d   |d   |d   |d         }t        |      }|d   }d}||k(  }|st        j                  d|fd	||f      t        j                  |      t        j                  |      d
z  }t        j
                  d|d    d|d   j                  d             dz   d|iz  }t        t        j                  |            dx}x}}|j                  }	d}
|	|
k(  }|st        j                  d|fd|	|
f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
dD ]  }|d   |   }d}||k(  }|st        j                  d|fd	||f      t        j                  |      t        j                  |      d
z  }t        j
                  | d|d   |          dz   d|iz  }t        t        j                  |            dx}x}} |d   d   }d}||k(  }|slt        j                  d|fd	||f      t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}}|d   d   }d}||k(  }|slt        j                  d|fd	||f      t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}}|d   d   }d}||k(  }|slt        j                  d|fd	||f      t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}}y) uB   모든 필수 검사 PASS, 옵션은 N/A → overall=PASS, exit 0.r-   rI   r,   rH   rI   r,   r   overallPASS==z%(py1)s == %(py4)spy1py4zoverall != PASS
results=results
fail_reasons=detailsfail_reasons
>assert %(py6)spy6Nr   z2%(py2)s
{%(py2)s = %(py0)s.returncode
} == %(py5)srb   rc   rd   py5assert %(py7)spy7)
start_lockbranch_matchworktree_pathcancelled_check
dirty_treechanged_pathsscope_matrixmixed_commitu    != PASS — actual handoff_chainN/Aassert %(py6)sqc_report_guardguard_sh)r`   rr   re   _call_reprcomparerk   rf   getrl   rm   
returncoderh   ri   rj   )rt   srb   payload@py_assert0@py_assert3@py_assert2@py_format5@py_format7rp   @py_assert4@py_format6@py_format8chks                 r   test_all_pass_baseliner      s   A
	)!E(an!J-C S!G9  '          "(    $GI$6#7 8	*..~>?	A     >>Q>Q>Q33>Q	 
 y!#& 	
& 	
&&0 	
 	
&& 	
 	
 		 ' 	
 	
 		 +1 	
 	
  e'	(:3(?'@A	
 	
 	
 	
 	
 	

 9o.7%7.%7777.%777.777%77777779/09E90E99990E9990999E99999999j)2U2)U2222)U222)222U2222222r   c                .   | }|d   dz  dz  |d    dz  j                          t        |d   |d   |d   |d         }t        |      }|d	   d
   }d}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }	d}
|	|
k(  }|st        j                  d|fd|	|
f      dt        j                         v st        j                  |      rt        j
                  |      ndt        j
                  |	      t        j
                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
y)u'   1. start_lock FAIL: lock 파일 삭제.r,   r;   r<   r-   r=   rI   rH   rv   r   r   FAILry   r{   r|   r   r   Nrw      r   rb   r   r   r   )unlinkr`   rr   re   r   rk   rl   rm   r   rh   ri   rj   rt   r   rb   r   r   r   r   r   r   rp   r   r   r   s                r   test_fail_start_lockr      sv   A{^h(a	l^5+AAIIK
	)!E(an!J-C S!G9l+5v5+v5555+v555+555v55555559'''''''''''''''''''>>Q>Q>Q33>Qr   c                   | }t        dddd|d          t        |d   |d   |d	   |d   
      }t        |      }|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }	d}
|	|
k(  }|st        j                  d|fd|	|
f      dt        j                         v st        j                  |      rt        j
                  |      ndt        j
                  |	      t        j
                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
y)u4   2. branch_match FAIL: 다른 브랜치 체크아웃.rE   r4   r5   zwrong-branchrH   r7   r-   rI   r,   rv   r   r   r   ry   r{   r|   r   r   Nrw   r   r   rb   r   r   r   )r   r`   rr   re   r   rk   rl   rm   r   rh   ri   rj   r   s                r   test_fail_branch_matchr      s`   AT4Qz]C
	)!E(an!J-C S!G9n-77-7777-777-77777777779'''''''''''''''''''>>Q>Q>Q33>Qr   c                8   | }|d   dz  dz  |d    dz  }|j                  dd       t        |d   |d	   |d   |d
         }t        |      }|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|j                  }
d}|
|k(  }|st        j                  d|fd|
|f      dt        j                         v st        j                  |      rt        j
                  |      ndt        j
                  |
      t        j
                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}}y)u2   3. cancelled_check FAIL: .cancelled 마커 생성.r,   r   r>   r-   z
.cancelledzcancelled by chairman
r%   r&   rI   rH   rv   r   r   r   ry   r{   r|   r   r   Nrw   r   r   rb   r   r   r   )r+   r`   rr   re   r   rk   rl   rm   r   rh   ri   rj   )rt   r   cancelrb   r   r   r   r   r   r   rp   r   r   r   s                 r   test_fail_cancelled_checkr      s   A{^h&1q|nJ4OOF
/'B
	)!E(an!J-C S!G9/0:F:0F::::0F:::0:::F:::::::9'''''''''''''''''''>>Q>Q>Q33>Qr   c                   | }|d   dz  j                  dd       t        |d   |d   |d   |d   	      }t        |      }|d
   d   }d}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }	d}
|	|
k(  }|st        j                  d|fd|	|
f      dt        j                         v st        j                  |      rt        j
                  |      ndt        j
                  |	      t        j
                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
d |d   d   D        }	t        |	      }|sddt        j                         v st        j                  t              rt        j
                  t              ndt        j
                  |	      t        j
                  |      dz  }t        t        j                  |            dx}	}y) u,   4. dirty_tree FAIL: untracked 파일 추가.rH   	stray.txtz
untracked
r%   r&   r-   rI   r,   rv   r   r   r   ry   r{   r|   r   r   Nrw   r   r   rb   r   r   r   c              3  $   K   | ]  }d |v  
 yw)r   Nr    )r!   fs     r   r#   z'test_fail_dirty_tree.<locals>.<genexpr>  s     KA{aK   r   dirty_files,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}anyrc   rd   r~   )r+   r`   rr   re   r   rk   rl   rm   r   rh   ri   rj   r   r   s                r   test_fail_dirty_treer   	  s   Az][ ,,]W,M
	)!E(an!J-C S!G9l+5v5+v5555+v555+555v55555559'''''''''''''''''''>>Q>Q>Q33>QK);M)JKK3KKKKKKKKK3KKK3KKKKKKKKKKKKKKr   c                   | }|d   dz  j                          |d   dz  dz  j                  dd       t        dd|d   	       t        d
ddd|d    d|d   	       t        |d   |d   |d   |d         }t	        |      }|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }	d}
|	|
k(  }|st        j                  d|fd|	|
f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      t        j                  |
      d z  }d!d"|iz  }t        t        j                  |            dx}	x}}
|d#   d$   }d% |D        }	t        |	      }|sd&d't        j                         v st        j                  t              rt        j                  t              nd't        j                  |	      t        j                  |      d(z  }t        t        j                  |            dx}	}y))u3   5. scope_matrix FAIL: allowed 외 경로에 commit.rH   	forbiddenzx.pyx
r%   r&   r@   forbidden/x.pyr7   rB   r4   rC   rG   r-   z] add forbiddenrI   r,   rv   r   r   r   ry   r{   r|   r   r   Nrw   r   r   rb   r   r   r   r   scope_violationsc              3  $   K   | ]  }d |v  
 yw)r   Nr    )r!   vs     r   r#   z)test_fail_scope_matrix.<locals>.<genexpr>*  s     91$9r   r   r   r   )r)   r+   r   r`   rr   re   r   rk   rl   rm   r   rh   ri   rj   r   )rt   r   rb   r   r   r   r   r   r   rp   r   r   r   
violationss                 r   test_fail_scope_matrixr     s)   Az][ '')z][ 6)55eg5N a
m4$!I,?jM 	)!E(an!J-C S!G9n-77-7777-777-77777777779'''''''''''''''''''>>Q>Q>Q33>Q#$67J9j9939999999993999399999999999999r   c                   | }t        |d   |d   |d   |d         }t        |      }|d   d   }d}||k(  }|slt        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|d   d   }d}||k(  }|slt        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|d   d   }d}||k(  }|slt        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|d   }d}||k(  }|slt        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }	d}
|	|
k(  }|st        j                  d	|fd|	|
f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
y)uL   handoff/guard.sh/qc 보고서 부재 → 옵션 검사는 N/A, overall PASS.r-   rI   r,   rH   rv   r   r   r   ry   r{   r|   r   r   Nr   r   rw   rx   r   r   rb   r   r   r   )r`   rr   re   r   rk   rl   rm   r   rh   ri   rj   r   s                r   $test_optional_checks_na_when_missingr   1  s   A
	)!E(an!J-C S!G9o.7%7.%7777.%777.777%77777779/09E90E99990E9990999E99999999j)2U2)U2222)U222)222U22222229'''''''''''''''''''>>Q>Q>Q33>Qr   c                P   | }|d   dz  dz  |d    dz  }|j                  d|d    dd	       t        |d   d
|d    d       t        |d   |d   |d   |d         }t        |      }|d   d   }d}||k(  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }t	        j                  d|d    d|d   j                  d             dz   d|iz  }	t        t	        j                  |	            dx}x}}d}|d   }|j                  }
 |
       }||v}|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      t	        j                  |
      t	        j                  |      dz  }d d!|iz  }t        t	        j                  |            dx}x}x}x}
}|d"   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }d#d|iz  }	t        t	        j                  |	            dx}x}}|j                  }d$}||k(  }|st	        j
                  d|fd%||f      d&t        j                         v st	        j                  |      rt	        j                  |      nd&t	        j                  |      t	        j                  |      d'z  }d(d)|iz  }t        t	        j                  |            dx}x}}y)*u  WARN 시나리오: task md 에 ``allowed_resources`` 블록 부재 (구버전 호환) →
    scope_matrix WARN, FAIL 0건 → overall=WARN, exit 2.

    spec 4.1 우선순위: FAIL 0 + WARN 1+ → exit 2 / overall=WARN.
    Codex 사전 검증 후 정책 변경: task md 부재/읽기 실패/paths 빈 리스트 = FAIL,
    allowed_resources 블록 자체가 없는 구버전 task md 만 WARN.
    r,   r   r   r-   r   r$   z2

legacy task md without allowed_resources block.
r%   r&   rG   z] downgrade task md (legacy)rI   rH   rv   r   r   WARNry   r{   r|   zresults=r   r   r   r   r   Nr   )not in)zI%(py1)s not in %(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s.values
}()
})r}   r~   r   py8zassert %(py10)spy10rw   r   r   r   rb   r   r   r   )r+   rW   r`   rr   re   r   rk   rf   r   rl   rm   valuesr   rh   ri   rj   )rt   r   task_mdrb   r   r   r   r   r   r   @py_assert5@py_assert7@py_format9@py_format11rp   r   r   r   s                     r   (test_warn_overall_when_scope_matrix_warnr   @  s    	Anx''1q|nC4HHG
Qy\NOP   +!AiL>1M NO
	)!E(an!J-C S!G9n-  -7  -    .    28    79%& '	*..~>?	A    
 4+4+224244644444644446444+4442444444444449'''''''''''''''''''>>Q>Q>Q33>Qr   c                   | }|d   dz  dz  |d    dz  }|j                          t        |d   d|d    d       t        |d   |d   |d   |d	   
      }t        |      }|d   d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            dx}x}}|d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            dx}x}}|j                  }
d}|
|k(  }|st	        j
                  d|fd|
|f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |
      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}
x}}d |d   D        }
t        |
      }|sddt        j                         v st	        j                  t              rt	        j                  t              ndt	        j                  |
      t	        j                  |      dz  }t        t	        j                  |            dx}
}y) u   task md 부재 시 scope_matrix FAIL (변경된 정책).

    Codex 결함 #2 — task md 부재 / 읽기 실패 / paths 빈 리스트는 차단(FAIL)이
    설계 의도다. 이전 구현은 WARN 으로 처리했으나 현 spec 2.7 에 따라 FAIL.
    r,   r   r   r-   r   rG   z] remove task mdrI   rH   rv   r   r   r   ry   r{   r|   r   r   Nrw   r   r   rb   r   r   r   c              3  $   K   | ]  }d |v  
 yw)r   Nr    r!   rs     r   r#   z9test_fail_scope_matrix_task_md_missing.<locals>.<genexpr>r  s     Dq~"Dr   r   r   r   r   )r   rW   r`   rr   re   r   rk   rl   rm   r   rh   ri   rj   r   )rt   r   r   rb   r   r   r   r   r   r   rp   r   r   r   s                 r   &test_fail_scope_matrix_task_md_missingr   _  s    	Anx''1q|nC4HHGNN+!AiL>1A BC
	)!E(an!J-C S!G9n-77-7777-777-77777777779'''''''''''''''''''>>Q>Q>Q33>QDGN,CDD3DDDDDDDDD3DDD3DDDDDDDDDDDDDDr   c                   | }|d   dz  dz  |d    dz  }|j                   j                  dd       |j                  t        j                  d|d   i      d	       t        |d   d
|d    d       t        |d   |d   |d   |d         }t        |      }|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|j                  }
d}|
|k(  }|st        j                  d|fd|
|f      dt        j                         v st        j                   |      rt        j                  |      ndt        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}}y)uF   handoff JSON 부서진 schema → handoff_chain FAIL → overall FAIL.r,   r   handoffsr-   .jsonTr   r%   r&   rG   z] add handoff (invalid)rI   rH   rv   r   r   r   ry   r{   r|   r   r   Nrw   r   r   rb   r   r   r   )r(   r)   r+   rn   dumpsrW   r`   rr   re   r   rk   rl   rm   r   rh   ri   rj   )rt   r   handoffrb   r   r   r   r   r   r   rp   r   r   r   s                 r   "test_warn_optional_handoff_invalidr   u  s   Anx'*4!I,u7MMGNN5tzz9a	l";<wO+!AiL>1H IJ
	)!E(an!J-C S!G9o.8&8.&8888.&888.888&88888889'''''''''''''''''''>>Q>Q>Q33>Qr   c                   | }|d   dz  dz  |d    dz  }|j                   j                  dd       |j                  dd	
       t        |d   d|d    d       t	        |d   |d   |d   |d         }t        |      }|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|j                  }
d}|
|k(  }|st        j                  d|fd|
|f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}}y) uA   qc 보고서 PASS verdict → qc_report_guard=PASS, overall PASS.r,   r   reportsr-   r   Tr   z# QC Report

qc_verdict: PASS
r%   r&   rG   ] add qc reportrI   rH   rv   r   r   rx   ry   r{   r|   r   r   Nrw   r   r   rb   r   r   r   r(   r)   r+   rW   r`   rr   re   r   rk   rl   rm   r   rh   ri   rj   rt   r   reportrb   r   r   r   r   r   r   rp   r   r   r   s                 r   test_qc_report_pass_verdictr     s   A{^h&2)~S5IIF
MMt4
+g   +!AiL> AB
	)!E(an!J-C S!G9/0:F:0F::::0F:::0:::F:::::::9'''''''''''''''''''>>Q>Q>Q33>Qr   c                   | }|d   dz  dz  |d    dz  }|j                   j                  dd       |j                  dd	
       t        |d   d|d    d       t	        |d   |d   |d   |d         }t        |      }|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|j                  }
d}|
|k(  }|st        j                  d|fd|
|f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}}y) uA   qc 보고서 FAIL verdict → qc_report_guard=FAIL, overall FAIL.r,   r   r   r-   r   Tr   z# QC Report

qc_verdict: FAIL
r%   r&   rG   r   rI   rH   rv   r   r   r   ry   r{   r|   r   r   Nrw   r   r   rb   r   r   r   r   r   s                 r   test_qc_report_fail_verdictr     s   A{^h&2)~S5IIF
MMt4
+g   +!AiL> AB
	)!E(an!J-C S!G9/0:F:0F::::0F:::0:::F:::::::9'''''''''''''''''''>>Q>Q>Q33>Qr   c           	        | }t        |d   |d   |d   |d   d      }|j                  }d}||k(  }|st        j                  d|fd	||f      d
t	        j
                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }t        j                  d|j                   d|j                         dz   d|iz  }t        t        j                  |            dx}x}}t        j                  |j                        }|d   }	d}|	|k(  }
|
slt        j                  d|
fd|	|f      t        j                  |	      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}	x}
}d}	|	|v }
|
st        j                  d|
fd|	|f      t        j                  |	      dt	        j
                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            dx}	}
|d   dz  dz  |d   z  }|j                  } |       }|sdd t	        j
                         v st        j                  |      rt        j                  |      nd t        j                  |      t        j                  |      d!z  }t        t        j                  |            dx}}t!        |j#                  d"            }t%        |      }
d#}|
|k\  }|st        j                  d$|fd%|
|f      d&t	        j
                         v st        j                  t$              rt        j                  t$              nd&d't	        j
                         v st        j                  |      rt        j                  |      nd't        j                  |
      t        j                  |      d(z  }d)d*|iz  }t        t        j                  |            dx}
x}}t        j                  |d+   j'                               }h d,}|d-   }|j(                  } |       }t+        |      }||k(  }|st        j                  d|fd.||f      d/t	        j
                         v st        j                  t*              rt        j                  t*              nd/t        j                  |      t        j                  |      t        j                  |      t        j                  |      d0t	        j
                         v st        j                  |      rt        j                  |      nd0d1z  }d2d3|iz  }t        t        j                  |            dx}x}x}x}}|d   }	d4}|	|v }
|
slt        j                  d|
fd5|	|f      t        j                  |	      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}	x}
}d6}	|	|v }
|
st        j                  d|
fd|	|f      t        j                  |	      d7t	        j
                         v st        j                  |      rt        j                  |      nd7dz  }dd|iz  }t        t        j                  |            dx}	}
d8}	|	|v }
|
st        j                  d|
fd|	|f      t        j                  |	      d7t	        j
                         v st        j                  |      rt        j                  |      nd7dz  }dd|iz  }t        t        j                  |            dx}	}
y)9uY   --json 미사용 시 evidence 파일이 .tasks/evidence/<id>/verify-*.json 에 저장됨.r-   rI   r,   rH   F)rI   r,   r   rX   r   ry   r   rb   r   zstdout=z
stderr=z
>assert %(py7)sr   Nrw   rx   r{   r|   r   r   evidenceinz%(py1)s in %(py3)ssummaryr}   py3assert %(py5)sr   r;   zAassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}ev_dirr   zverify-*.jsonr   )>=)z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} >= %(py6)slenevs)rc   r}   r   r   zassert %(py8)sr   >   r   r   r   r   r   r   r   r   r   r   r   r   )zb%(py8)s
{%(py8)s = %(py0)s(%(py6)s
{%(py6)s = %(py4)s
{%(py4)s = %(py2)s.keys
}()
})
} == %(py10)ssetexpected_keys)rc   rd   r~   r   r   r   zassert %(py12)spy12)rx   r   r   )z%(py1)s in %(py4)sverified_atr   r   )r`   r   re   r   rh   ri   rj   rk   rf   rN   rg   rl   rm   rn   ro   existssortedglobr   rM   keysr   )rt   r   rb   rp   r   r   r   r   r   r   r   r   r   @py_format4r   r   r   r   r   r   r   @py_assert9r   @py_format13s                           r   test_evidence_file_writtenr     s   A
	)eHK.jMC >>KQK>QKKK>QKKKKKK3KKK3KKK>KKKQKKK'#**Yszzl KKKKKKKKjj$G9''''''''''''''''''' :    :   :                {^h&3a	lBF===??66=?
_-
.Cs8q8q=8q33ss8qjjR**,-GM y!:!&&:&(:3():)]::::)]::::::3:::3:::!:::&:::(:::)::::::]:::]::::::::99!99!99999!9999999!99999999#=G####=G###=######G###G#######999r   c                |   | }|d   dz  j                  dd       t        |d   |d   |d   |d   	      }t        |      }|d
   }d}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}d}||v }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      nddz  }	dd|	iz  }
t        t        j                  |
            dx}}|d   }t        |t              }|sddt        j                         v st        j                  t              rt        j
                  t              ndt        j
                  |      dt        j                         v st        j                  t              rt        j
                  t              ndt        j
                  |      dz  }
t        t        j                  |
            dx}}|d   }|sNt        j                  d      dz   dt        j
                  |      iz  }t        t        j                  |            d}|d    d   }|d   }||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)!u   top-level ``fail_reasons`` 가 FAIL 사유 리스트를 노출 (Codex 결함 #5).

    spec 3.x: evidence JSON 최상위에 ``fail_reasons`` 필수.
    rH   r   r   r%   r&   r-   rI   r,   rv   rw   r   ry   r{   r|   r   r   Nr   r   r   r   r   r   r   z5assert %(py5)s
{%(py5)s = %(py0)s(%(py2)s, %(py3)s)
}
isinstancer^   )rc   rd   r   r   u   fail_reasons 가 비어있음z
>assert %(py1)sr}   r   )r+   r`   rr   re   r   rk   rl   rm   rh   ri   rj   r  r^   rf   )rt   r   rb   r   r   r   r   r   r   r   r   rp   r   @py_format2s                 r   #test_top_level_fail_reasons_on_failr    s   
 	Az][ ,,UW,E
	)!E(an!J-C S!G9'''''''''''''''''''$>W$$$$>W$$$>$$$$$$W$$$W$$$$$$$n-4:-t44444444:444:444-444444t444t4444444444>"C"CC$CCCC"CCCCC9n-H1HH-1HHHHH-1HHHH-HHH1HHHHHHHHr   c           
        | }|d   dz  dz  |d    dz  }|j                   j                  dd       |j                  t        j                  dd	d
ddd      d       t        |d   d|d    d       t        |d   |d   |d   |d         }t        |      }|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}d |d   D        }
t        |
      }|sd d!t        j                         v st        j                   t              rt        j                  t              nd!t        j                  |
      t        j                  |      d"z  }t        t        j                  |            dx}
}y)#uQ   handoff JSON 의 task_id 가 인자 task_id 와 다르면 FAIL (Codex 결함 #3).r,   r   r   r-   r   Tr   h-1dev1dev5z	task-99992026-05-05T00:00:00Z
handoff_idfrom_botto_botr-   	timestampr%   r&   rG   z] add handoff (mismatch)rI   rH   rv   r   r   r   ry   r{   r|   r   r   Nc              3  $   K   | ]  }d |v  
 yw)mismatchNr    r   s     r   r#   z5test_handoff_task_id_mismatch_fail.<locals>.<genexpr>  s     @1zQ@r   r   r   r   r   )r(   r)   r+   rn   r   rW   r`   rr   re   r   rk   rl   rm   r   rh   ri   rj   )rt   r   r   rb   r   r   r   r   r   r   rp   s              r   "test_handoff_task_id_mismatch_failr    s   Anx'*4!I,u7MMGNN5

#" &3	
    +!AiL>1I JK
	)!E(an!J-C S!G9o.8&8.&8888.&888.888&8888888@(?@@3@@@@@@@@@3@@@3@@@@@@@@@@@@@@r   c           
     F   | }|d   dz  dz  |d    dz  }|j                   j                  dd       |j                  t        j                  dd	d
|d   dd      d       t        |d   d|d    d       t        |d   |d   |d   |d         }t        |      }|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)uI   handoff JSON 의 from_bot 이 빈 문자열이면 FAIL (Codex 결함 #3).r,   r   r   r-   r   Tr   r   r  r  r	  r%   r&   rG   z] add handoff (empty from_bot)rI   rH   rv   r   r   r   ry   r{   r|   r   r   N)r(   r)   r+   rn   r   rW   r`   rr   re   r   rk   rl   rm   )
rt   r   r   rb   r   r   r   r   r   r   s
             r    test_handoff_from_bot_empty_failr    s!   Anx'*4!I,u7MMGNN5

# Y<3	
    +!AiL>1O PQ
	)!E(an!J-C S!G9o.8&8.&8888.&888.888&8888888r   c                L   | }|d   dz  dz  }|j                          t        |d   d|d    d       t        |d   |d   |d   |d   	      }t        |      }|d
   d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            dx}x}}|d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            dx}x}}|j                  }
d}|
|k(  }|st	        j
                  d|fd|
|f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |
      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}
x}}y)uN   detector 부재 시 mixed_commit FAIL (안전 측 차단 — Codex 결함 #4).r,   r   r   rG   r-   z] remove detectorrI   rH   rv   r   r   r   ry   r{   r|   r   r   Nrw   r   r   rb   r   r   r   )r   rW   r`   rr   re   r   rk   rl   rm   r   rh   ri   rj   )rt   r   detectorrb   r   r   r   r   r   r   rp   r   r   r   s                 r   'test_mixed_commit_detector_missing_failr     s   A~	),FFHOO+!AiL>1B CD
	)!E(an!J-C S!G9n-77-7777-777-77777777779'''''''''''''''''''>>Q>Q>Q33>Qr   c                .   t        |       }|d   dz  j                  dd       t        |d   |d   |d   |d   	      }t        |      }|d
   d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}}|d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}}|j                  }	d}
|	|
k(  }|st	        j
                  d|fd|	|
f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |	      t	        j                  |
      dz  }dd|iz  }t        t	        j                  |            dx}	x}}
y)u5   FAIL 1건 + 다른 PASS 들 → overall FAIL, exit 1.rH   z	extra.tmpr   r%   r&   r-   rI   r,   rv   r   r   r   ry   r{   r|   r   r   Nrw   r   r   rb   r   r   r   )rT   r+   r`   rr   re   r   rk   rl   rm   r   rh   ri   rj   )rP   r   rb   r   r   r   r   r   r   rp   r   r   r   s                r   $test_overall_priority_fail_over_warnr  /  sm   !Az][ ,,UW,E
	)!E(an!J-C S!G9l+5v5+v5555+v555+555v55555559'''''''''''''''''''>>Q>Q>Q33>Qr   )r   r   r   r   returnsubprocess.CompletedProcess)r,   r   r-   r   r.   z	list[str]r  r   )z	task-2459r  )rP   r   r-   r   rI   r   r  dict)r,   r   rV   r   r  None)r-   r   rI   z
str | Noner,   r   r   r   rX   boolrY   ztuple[str, ...]r  r  )rb   r  r  r  )rP   r   )/__doc__
__future__r   builtinsrh   _pytest.assertion.rewrite	assertionrewritere   rn   r   r[   pathlibr   pytest__file__resolver   	REPO_ROOTr]   rL   r   r1   rT   rW   r`   rr   fixturert   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r    r   r   <module>r*     sz   #     
   N""$,,Q/		Y	!4	49$'AA* UUU 
U 
	Up7   
 	
 
   !(" % %3H

L:4>E,"&,& RI*A492r   