
    jT                        d 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
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efd	Zdefd
Zdededededdf
dZdedefdZdZdedefdZ ej>                         d        Z dedede!de"eeef   fdZ#dedefdZ$d Z%dededdfdZ&d Z'd Z(d  Z)d! Z*d" Z+d# Z,d$ Z-d% Z.d& Z/y)'u  
test_stash_lifecycle_failstop.py
task: task-2571+1 (Gemini HIGH carry-over) — spec §3.2.2.3 fail-stop 회귀

검증 목표 (Gemini HIGH finding 회귀 차단):
- spec §3.2.2.3 "실패 시 stop (다음 stash 건드리지 않음)" 강제
- scripts/finish-task.sh 의 stash-lifecycle dispatch heredoc 내부에서 git stash
  pop/drop 이 실패한 경우, 루프가 BREAK 되어 다음 stash 를 건드리지 않아야 한다.
- 박제된 audit log (memory/events/stash-lifecycle-action.<ts>.json) 가 다음을
  포함해야 한다:
    * top-level fail_stop == True
    * 실패한 decision 의 action == "failed"
    * 실패한 decision 의 stderr 필드 (non-empty)
    * decisions 배열 길이 == 실패까지 처리된 stash 수 (전체 stash 수보다 적음)

작성자: 하누만 (개발4팀 QA)
관련 PR: #123 (Gemini HIGH BLOCK 사유)
관련 fix: 카르티케야 (scripts/finish-task.sh)
    N)Path   scriptszfinish-task.shstash_audit.pyreturnc                  j    t         j                  j                         } d| d<   d| d<   d| d<   d| d<   | S )uI   git 명령 실행용 환경변수 (테스트용 author/committer 고정).testGIT_AUTHOR_NAMEtest@example.comGIT_AUTHOR_EMAILGIT_COMMITTER_NAMEGIT_COMMITTER_EMAIL)osenvironcopy)envs    E/home/jay/workspace/tests/regression/test_stash_lifecycle_failstop.py_git_envr   )   sC    
**//
C#C0C &C!3CJ    c                  D   t        j                  d      } t               }t        j                  g d| d|       t        j                  g d| d|       t        j                  g d| d|       t        |       dz  j                  d	       t        j                  g d
| d|       t        j                  g d| d|       t        |       dz  }|j                  dd       |dz  j                  t               t        |       dz  dz  j                  dd       | S )u   
    격리된 임시 git repo 초기화 + scripts/stash_audit.py 심볼릭 링크 + memory/events 생성.
    finish-task.sh 의 lifecycle dispatch heredoc 가 기대하는 워크스페이스 레이아웃 구성.
    zstash-failstop-test-)prefix)gitinit-qz-bmainTcwdcheckr   )r   configz
user.emailr   )r   r   z	user.namer	   base.txtzbase initial
)r   addr    )r   commitr   -mr   r   )parentsexist_okr   memoryevents)
tempfilemkdtempr   
subprocessrunr   
write_textmkdir
symlink_toSTASH_AUDIT_PY)dr   scripts_dirs      r   _init_temp_workspacer2   3   s    
 	 67A
*CNN6ATsSNNFAUY_bcNN9qRUV	!Wz%%&67NN-1DcJNN8atQTU q'I%KdT2##//? 
!Wx("))$)FHr   repo_dirmessagefilenamecontentc                     t               }t        |       |z  }|j                  |       t        j                  dd|g| d|       t        j                  dddd|g| d|       y)	u   
    파일을 dirty 상태로 만든 뒤 stash push.
    동일 filename 을 반복 호출하면 stash 충돌을 의도적으로 유발 가능.
    r   r!   Tr   stashpushr#   N)r   r   r,   r*   r+   )r3   r4   r5   r6   r   fpaths         r   _stash_push_modifyr;   L   s_    
 *CNX%E	WNNE5(+3ONNE7FD':PTZ]^r   c                 0   t               }t        j                  g d| dd|      }|j                  dk7  s|j                  j                         syt        |j                  j                         D cg c]  }|j                         s| c}      S c c}w )N)r   r8   listT)r   capture_outputtextr   r   )r   r*   r+   
returncodestdoutstriplen
splitlines)r3   r   rlines       r   _stash_countrG   X   so    
*C ht$TW	A 	||q 0!4!4!6G$**,GHHGs   2BBz#task-2571: stash lifecycle dispatchfinish_task_shc                    | j                  d      }|j                         }d}t        |      D ]  \  }}t        |v s|} n d}||u}|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }t	        j                  |  dt              d	z   d
|iz  }	t        t	        j                  |	            dx}}d}
t        |t        |            D ]  }d||   v s|dz   }
 n d}|
|u}|st	        j
                  d|fd|
|f      dt        j                         v st	        j                  |
      rt	        j                  |
      ndt	        j                  |      dz  }t	        j                  d| d      d	z   d
|iz  }	t        t	        j                  |	            dx}}d}t        |
t        |            D ]  }||   j                         dk(  s|} n d}||u}|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }t	        j                  d|
 d      d	z   d
|iz  }	t        t	        j                  |	            dx}}dj!                  ||
|       dz   }|S )u  
    finish-task.sh 에서 stash-lifecycle dispatch heredoc 본문을 추출한다.
    카르티케야가 finish-task.sh 를 수정해도 마커 기반이라 lock-in 되지 않음.

    추출 규칙:
        1. LIFECYCLE_DISPATCH_MARKER 가 포함된 라인의 위치를 찾음.
        2. 그 이후 첫 <<'PYEOF' 라인을 heredoc 시작점으로 한다.
        3. 다음 ^PYEOF$ 라인을 heredoc 종료점으로 한다.
    utf-8encodingN)is not)z%(py0)s is not %(py3)s
marker_idxpy0py3u    에서 마커 미발견: 
>assert %(py5)spy5	<<'PYEOF'   	start_idxu   마커(u   ) 이후 <<'PYEOF' 미발견PYEOFend_idxu%   PYEOF 종료 토큰 미발견 (start=)
)	read_textrD   	enumerateLIFECYCLE_DISPATCH_MARKER
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgAssertionError_format_explanationrangerC   rB   join)rH   r?   linesrN   irF   @py_assert2@py_assert1@py_format4@py_format6rV   rX   bodys                r   #_extract_lifecycle_dispatch_heredocrp   k   s    ##W#5DOOEJU# 4$,J " :T!  :T              "    
45N4QR    
 I:s5z* %("AI ! 9D   9D              !    *9:    
 G9c%j) 8>>w&G  7$  7$                  0	{!<     99U9W-.5DKr   c               #      K   t               } t        | ddd       t        | ddd       t        |       dz  j                  d       | df t	        j
                  | d	
       yw)u   
    pre-task 분류 stash 2건 + working tree dirty conflict 시나리오.
    yield: (workspace_dir, expected_stash_count_before)
    z WIP: pre-task-2571-A first stashzc.txtzstash_A_content
)r4   r5   r6   z!WIP: pre-task-2571-B second stashzstash_B_content
zdirty_uncommitted_change
r   T)ignore_errorsN)r2   r;   r   r,   shutilrmtree)r3   s    r   'two_pretask_stashes_with_dirty_conflictru      sn      $%H 2#	 3#	 
(^g))*FG
A+
MM($/s   A$A&	workspacetask_idapprovec           	      r   t        t              }t        j                  dddd      }	 |j	                  |       |j                          t        t        |       dz  dz  | dz        }t        j                  |j                  ||rd	nd
dd	| |dg	}t        j                  |ddt                     }|j                  |j                  |j                   f	 t#        j$                  |j                         S # t&        $ r Y S w xY w# 	 t#        j$                  |j                         w # t&        $ r Y w w xY wxY w)u   
    finish-task.sh 에서 추출한 lifecycle dispatch heredoc 본문을 임시 .py 로 쓴 뒤
    동일 argv 시그니처로 실행한다. 박제 audit log 는 workspace/memory/events/ 에 생성된다.

    Returns: (exit_code, stdout, stderr)
    wz.pyFrJ   )modesuffixdeleterL   r&   r'   z.done10 30T)r>   r?   r   )rp   FINISH_TASK_SHr(   NamedTemporaryFilewriteclosestrr   sys
executablenamer*   r+   r   r@   rA   stderrr   unlinkOSError)rv   rw   rx   ro   tmp_py	done_fileargvprocs           r   _run_lifecycle_dispatchr      s    /~>D((uwFT Y(2X=7)5@QQR	NNKKC

 ~~d4d
ST[[8	IIfkk" 			IIfkk" 		sB   B+D C66	DDD6D'&D6'	D30D62D33D6c           	      (   t        |       dz  dz  }t        |j                  d            }|st        j                  d| d|j                         rt        |j                               nd       dz   dd	t        j                         v st        j                  |      rt        j                  |      nd	iz  }t        t        j                  |            t        |d
   dd      5 }t        j                   |      cddd       S # 1 sw Y   yxY w)uc   
    workspace/memory/events/stash-lifecycle-action.*.json 중 가장 최근 파일을 로드.
    r&   r'   zstash-lifecycle-action.*.jsonu   audit log 미발견: u@    내 stash-lifecycle-action.*.json 없음.
실제 디렉토리: z
NOT EXISTS
>assert %(py0)srP   
candidatesrE   rJ   rK   N)r   sortedglobr^   rd   existsr=   iterdirr`   ra   rb   rc   re   rf   openjsonload)rv   
events_dirr   @py_format1fs        r   _latest_audit_logr      s     i8+h6J
(GHIJ    
| , >H>O>O>QZ%7%7%9 :Wcd	f               
jncG	4 yy|  s   )DDc                  0   t        t              } d}|| v }|st        j                  d|fd|| f      t        j                  |      dt        j                         v st        j                  |       rt        j                  |       nddz  }t        j                  d      dz   d|iz  }t        t        j                  |            d	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  }t        j                  d      dz   d|iz  }t        t        j                  |            d	x}}g }d}|| v }|}|rd}|| v }|}|snt        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  }	|j                  |	       |rt        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  }|j                  |       t        j                  |d      i z  }t        j                  d      dz   d|iz  }t        t        j                  |            d	x}x}x}x}x}}y	)u   
    finish-task.sh 에서 stash-lifecycle dispatch heredoc 본문을 마커 기반으로 추출 가능해야 한다.
    추출본에 핵심 토큰들이 포함되어야 한다.
    zpre-taskinz%(py1)s in %(py3)sro   py1rQ   u+   heredoc 추출본에 pre-task 분기 없음rR   rS   Nzstash-lifecycle-actionu3   heredoc 추출본에 audit log 박제 토큰 없음r8   pop)z%(py3)s in %(py5)s)rQ   rS   z%(py7)spy7)z%(py10)s in %(py12)s)py10py12z%(py14)spy14r   u,   heredoc 추출본에 stash pop 호출 없음z
>assert %(py17)spy17)rp   r   r^   r_   rc   r`   ra   rb   rd   re   rf   append_format_boolop)ro   @py_assert0rk   rm   rn   rl   @py_assert4@py_assert9@py_assert11@py_format8@py_format13@py_format15@py_format16@py_format18s                 r   &test_failstop_heredoc_extraction_worksr   	  s(   
 /~>DL:LLL:LLL:LLLLLLLLLLLLLLLLLLLL#b#t+bbb#tbbb#bbbbbbtbbbtbbbb-bbbbbbb7 7d? u u}   7d                   u     %      )-    )-        	7     r   rcr   c                 .    | dvrt        d|  d|       y)uc  
    dispatch heredoc 의 exit 코드 검증.
    spec §3.2.2.3 fail-stop 시 sys.exit(1) 가 정상 동작이므로 rc ∈ {0, 1} 모두 허용.
    그 외 (예: IndexError → 2, 기타 예외 → 1 with traceback) 는 실패로 간주.
    fail_stop 시그널은 stderr 에서 식별: "FAIL-STOP" 또는 audit log 자체 존재 여부로 검증.
    )r   rU   u*   lifecycle dispatch 비정상 종료: exit=z
stderr=N)re   )r   r   s     r   _assert_dispatch_rc_acceptabler     s.     
8IfXN
 	
 r   c                 
   | \  }}t        |dd      \  }}}t        ||       t        |      }d}||v }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      nddz  }t        j                  d	t        |j                                      d
z   d|iz  }	t        t        j                  |	            dx}}y)u  
    pre-task 2건 시드 + dirty conflict 환경에서 lifecycle dispatch 를 approve=1 로 실행하면
    workspace/memory/events/ 에 audit log 가 박제되어야 한다.
    fail-stop 시 sys.exit(1) 동작 (spec §3.2.2.3 의 시그널 전파) 도 정상으로 간주.
    task-failstop-testTrw   rx   	decisionsr   r   logr   u'   audit log 에 decisions 필드 없음: rR   rS   N)r   r   r   r^   r_   rc   r`   ra   rb   rd   r=   keysre   rf   )
ru   rv   _r   r   r   r   rk   rm   rn   s
             r   #test_failstop_audit_log_is_producedr   &  s     ;LIq+I?S]abMB6"2v.
I
&C[;#[[[;#[[[;[[[[[[#[[[#[[[[!HchhjIYHZ[[[[[[[r   c                    | \  }}t        |dd      \  }}}t        ||       t        |      }|j                  }d} ||      }d}	||	u }
|
st	        j
                  d|
fd||	f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      t	        j                  |      t	        j                  |	      dz  }t	        j                  d	t        j                  |d
d             dz   d|iz  }t        t	        j                  |            dx}x}x}x}
}	y)u   
    spec §3.2.2.3: 실패 시 fail_stop=True 가 audit log top-level 에 박제되어야 한다.
    카르티케야의 fix 적용 전에는 이 assertion 이 RED 가 된다 (예상된 동작).
    r   Tr   	fail_stopiszI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} is %(py9)sr   rP   py2py4py6py9uC   spec §3.2.2.3: top-level fail_stop=True 기대, 실제 audit log:
Fr   ensure_asciiindent
>assert %(py11)spy11N)r   r   r   getr^   r_   r`   ra   rb   rc   rd   r   dumpsre   rf   )ru   rv   r   r   r   r   rl   @py_assert3@py_assert5@py_assert8@py_assert7@py_format10@py_format12s                r   )test_failstop_top_level_fail_stop_is_truer   4  sK   
 ;LIq+I?S]abMB6"2v.
I
&C77 ; 7; 4 4'   4                           $(    O::ca8
9	;     r   c           
      n   | \  }}t        |dd      \  }}}t        ||       t        |      }|j                  dg       }|D cg c]  }|j                  d      dk(  s| }}t	        |      }	d}
|	|
k\  }|s1t        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  }t        j                  dt	        |       dt        j                  |dd             dz   d|iz  }t        t        j                  |            dx}	x}}
yc c}w )u   
    spec §3.2.2.3: 실패한 stash 의 decision.action == "failed" (기존 'skipped' 가 아님).
    Gemini HIGH 의 핵심 회귀 포인트 — 'skipped' 로 표기 시 fail-stop 식별 불가.
    r   Tr   r   actionfailedrU   )>=)z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} >= %(py6)srC   failed_decisions)rP   r   rQ   r   u6   action=='failed' decision 1건 이상 기대, 실제: z
decisions: Fr   r   z
>assert %(py8)spy8N)r   r   r   r   rC   r^   r_   r`   ra   rb   rc   rd   r   r   re   rf   )ru   rv   r   r   r   r   r   r0   r   rk   r   r   @py_format7@py_format9s                 r   /test_failstop_failed_decision_has_action_failedr   D  s   
 ;LIq+I?S]abMB6"2v.
I
&CR(I#,Lah80KLL  A  A%    A                          !    %&    AEUAV@W XjjqIJ	L      Ms   F2F2c                     | \  }}t        |dd      \  }}}t        ||       t        |      }|j                  dg       D cg c]  }|j                  d      dk(  s| }}|s{t	        j
                  d      dz   d	d
t        j                         v st	        j                  |      rt	        j                  |      nd
iz  }t        t	        j                  |            |D ]$  }	|	j                  dd      }
g }t        |
t              }|}|r|
j                  } |       }|}|sd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dt        j                         v st	        j                  t              rt	        j                  t              ndt	        j                  |      dz  }|j                  |       |rddt        j                         v st	        j                  |
      rt	        j                  |
      ndt	        j                        t	        j                        dz  }|j                  |       t	        j                   |d      i z  }t	        j
                  d|	      dz   d|iz  }t        t	        j                  |            dx}x}x}x}}' yc c}w )u   
    spec §3.2.2.3: 실패 decision 은 stderr 필드에 git 의 실제 오류 메시지를 박제해야 한다.
    회장결정 박제 doctrine — 사후 진단 가능성 확보.
    r   Tr   r   r   r   ua   전제: action=='failed' decision 존재 (test_failstop_failed_decision_has_action_failed 참조)r   rP   r   r   r   z.%(py6)s
{%(py6)s = %(py2)s(%(py3)s, %(py4)s)
}
isinstancestderr_valuer   )r   rQ   r   r   z=%(py12)s
{%(py12)s = %(py10)s
{%(py10)s = %(py8)s.strip
}()
})r   r   r   r   u=   failed decision 에 stderr 필드(non-empty) 기대, 실제: z
>assert %(py15)spy15N)r   r   r   r   r^   rd   r`   ra   rb   rc   re   rf   r   r   rB   r   r   )ru   rv   r   r   r   r   r0   r   r   fdr   rl   r   r   r   r   r   r   @py_format14r   s                       r   1test_failstop_failed_decision_has_nonempty_stderrr   V  s   
 ;LIq+I?S]abMB6"2v.
I
&C#&77;#;[aquuXRZ?Z[[  A  A  A  A  A  A  A  A  A  A  A  A  A  A  A  A  A 
vvh+	
z,, 	
, 	
,1C1C 	
1C1E 	
1E 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
  ' 	
 	
 		 ' 	
 	
	6	
 	
  ), 	
 	
 		 ), 	
 	
 		 - 	
 	
	6	
	 - 	
	6	
 	
  2> 	
 	
 		 2> 	
 	
 		 2D 	
 	
 		 2F 	
 	
	6	
		
 	
 	
  LB6R	
 	
 	
 	
 	
 	

 \s   LLc                    | \  }}t        |      }||k(  }|sHt        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                  |      dt        j                         v st        j
                  |      rt        j                  |      nddz  }t        j                  d| dt        |             d	z   d
|iz  }t        t        j                  |            dx}}t        |dd      \  }}}	t        ||	       t        |      }
|
j                  dg       }|
j                  }d} ||      }d}||u }|st        j                  d|fd||f      dt        j                         v st        j
                  |
      rt        j                  |
      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }t        j                  d      dz   d|iz  }t        t        j                  |            dx}x}x}x}}|s{t        j                  d      dz   ddt        j                         v st        j
                  |      rt        j                  |      ndiz  }t        t        j                  |            |d   }|j                  }d} ||      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }t        j                  d |j                  d      d!t        j                  |d"d#$             dz   d|iz  }t        t        j                  |            dx}x}x}x}}t!        |      }||k  }|sat        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                  |      dt        j                         v st        j
                  |      rt        j                  |      nddz  }t        j                  d(| d)t!        |       d*t        j                  |d"d#$             d	z   d
|iz  }t        t        j                  |            dx}}y)+u2  
    spec §3.2.2.3 핵심: 실패 발생 시 루프가 BREAK 되어야 하며, 그 이후 stash 는
    decisions 배열에 등장하지 않아야 한다.

    이 시나리오:
        - stash 2건 시드 (양쪽 모두 pre-task)
        - 역순 처리 (높은 index 부터): stash@{1} 가 먼저 처리됨
        - stash@{1} pop 은 dirty conflict 로 실패
        - spec 통과 시: decisions 길이 == 1 (stash@{1} 만 등장; stash@{0} 미처리)
        - spec 위반(현행 버그) 시: decisions 길이 == 2 (stash@{0} 도 'skipped' 로 등장)
    ==)z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py5)srG   rv   expected_count)rP   r   rQ   rS   u   전제: stash u   건 시드 기대, 실제: z
>assert %(py7)sr   Nr   Tr   r   r   r   r   r   r   uI   전제: fail_stop=True (test_failstop_top_level_fail_stop_is_true 참조)r   r   u   decisions 비어있음r   rP   r   r   r   )zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} == %(py9)slastu   spec §3.2.2.3 fail-stop 위반: failed decision 이후 다른 decision 추가됨.
마지막 decision.action 기대: 'failed', 실제: u   
전체 decisions: Fr   r   <)z/%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} < %(py5)srC   u.   spec §3.2.2.3 fail-stop 위반: 전체 stash u%   건 모두 처리됨 (decisions 수: u;   ). 실패 시 break 되지 않았다는 증거.
decisions: )rG   r^   r_   r`   ra   rb   rc   rd   re   rf   r   r   r   r   r   r   rC   )ru   rv   r   rk   r   rn   r   r   r   r   r   r   rl   r   r   r   r   r   r   r   r   s                        r   .test_failstop_loop_breaks_no_further_decisionsr   j  s    !HI~	" "n4   "n                "    "    #      '5    '5    ((CLQZD[C\]     ,I?S]abMB6"2v.
I
&CR(I77 ; 7; 4 4'  4                           $(    T      .........9...9.....R=D88 H 8H  )   	   	  	    	    	    	    	    	 "*   ??Cxx?Q>T U!ZZ	aPQ	S   	   y> >N*   	>N   	  	    	    	  	    	    	    	  	 +   	 +    98H Iy>* +jjqIJ	L   	 r   c                 6   | \  }}t        |dd      \  }}}t        ||       t        |      }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                  d	| d
|       dz   d|iz  }	t        t        j                  |	            d}y)u  
    spec §3.2.2.3: 실패 후 break 되었으므로 처리되지 않은 stash 는 그대로 남아있어야 한다.
    stash 2건 중 stash@{1} pop 실패 시:
        - pop 실패 → stash@{1} 도 그대로 남음 (git stash pop 의 keep-on-conflict 동작)
        - stash@{0} 은 break 로 인해 미처리 → 그대로 남음
    => 최종 stash 수 == 2 (변동 없음)
    r   Tr   r   )z%(py0)s == %(py2)safterr   rP   r   uF   spec §3.2.2.3 위반 가능성: 실패 후 stash 수 변동.
기대: uB    (stash@{1} pop 실패 keep + stash@{0} break 미처리),
실제: 
>assert %(py4)sr   N)r   r   rG   r^   r_   r`   ra   rb   rc   rd   re   rf   )
ru   rv   r   r   r   r   r   rl   @py_format3@py_format5s
             r   'test_failstop_remaining_stash_preservedr     s     !HI~+I?S]abMB6"2v.#EN"  	5N   	  	    	    	  	 #   	 #   !" #'	   	 r   c                     t        t              } d}|| v}|st        j                  d|fd|| f      t        j                  |      dt        j                         v st        j                  |       rt        j                  |       nddz  }t        j                  d      dz   d|iz  }t        t        j                  |            d	x}}y	)
u   
    Codex G1 MEDIUM: lifecycle dispatch heredoc 본문에 `timeout=30` 리터럴이
    재등장하지 않아야 한다. 상수 (STASH_AUDIT_TIMEOUT_SEC → sys.argv[7]) 연결을 사용해야 함.
    z
timeout=30)not in)z%(py1)s not in %(py3)sro   r   u   Codex G1 MEDIUM 회귀: heredoc 본문에 `timeout=30` 리터럴 재등장.
STASH_AUDIT_TIMEOUT_SEC 상수 연결이 끊겼을 가능성. sys.argv[7] 경로 확인 필요.rR   rS   Nrp   r   r^   r_   rc   r`   ra   rb   rd   re   rf   ro   r   rk   rm   rn   s        r   6test_medium_no_timeout_30_literal_in_lifecycle_heredocr     s    
 /~>D <t#  <t           $     $   	j    r   c                     t        t              } d}|| v }|st        j                  d|fd|| f      t        j                  |      dt        j                         v st        j                  |       rt        j                  |       nddz  }t        j                  d      dz   d|iz  }t        t        j                  |            d	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  }t        j                  d      dz   d|iz  }t        t        j                  |            d	x}}y	)u   
    Codex G1 MEDIUM: heredoc 본문이 `audit_timeout = int(sys.argv[7])` 로 상수를 받고,
    `timeout=audit_timeout` 으로 subprocess 에 전달해야 한다. 두 토큰 모두 존재해야 상수 연결 증명.
    z audit_timeout = int(sys.argv[7])r   r   ro   r   u   Codex G1 MEDIUM 회귀: heredoc 에 `audit_timeout = int(sys.argv[7])` 미존재.
bash → python 상수 전달 경로가 끊김.rR   rS   Nztimeout=audit_timeoutu   Codex G1 MEDIUM 회귀: heredoc 에 `timeout=audit_timeout` 미존재.
subprocess timeout 인자에 상수가 반영되지 않음.r   r   s        r   )test_medium_audit_timeout_uses_sys_argv_7r    sE   
 /~>D- -5  -    .      26    26   	:     # "d*  "d    #      '+    '+   	F    r   c                     t         j                  d      } | j                  t              }d}| }||k7  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }t	        j                  dt              d	z   d
|iz  }t        t	        j                  |            dx}x}}| |d }|j                  d      }|j                  d      }	d}| }||k7  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }t	        j                  d      d	z   d
|iz  }t        t	        j                  |            dx}x}}d}| }|	|k7  }|st	        j
                  d|fd|	|f      dt        j                         v st	        j                  |	      rt	        j                  |	      ndt	        j                  |      dz  }t	        j                  d      d	z   d
|iz  }t        t	        j                  |            dx}x}}||	k  }|st	        j
                  d|fd||	f      dt        j                         v st	        j                  |      rt	        j                  |      nddt        j                         v st	        j                  |	      rt	        j                  |	      nddz  }
t	        j                  d| d|	 d      dz   d|
iz  }t        t	        j                  |            d}|||	 }d}||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }t	        j                  d|       d z   d!|iz  }t        t	        j                  |            dx}}y)"u  
    Codex G1 MEDIUM: scripts/finish-task.sh 의 dispatch 호출 블록에서
    `"$STASH_AUDIT_TIMEOUT_SEC"` 가 `"$DONE_FILE"` 와 `<<'PYEOF'` 사이에 위치해야 한다.
    이는 bash 측 상수 → python sys.argv[7] 매핑을 정적으로 검증한다.
    rJ   rK   rU   )!=)z%(py0)s != -%(py3)s
marker_posrO   u   마커 미발견: z
>assert %(py6)sr   Nz"$DONE_FILE"rT   done_posu0   dispatch 블록에 "$DONE_FILE" 인자 미발견	pyeof_posu&   dispatch 블록에 <<'PYEOF' 미발견r   )z%(py0)s < %(py2)sr   z"$DONE_FILE" (pos=u   ) 가 <<'PYEOF' (pos=u   ) 보다 뒤에 위치.r   r   z"$STASH_AUDIT_TIMEOUT_SEC"r   r   betweenr   u   Codex G1 MEDIUM 회귀: dispatch 블록 ["$DONE_FILE" ... <<'PYEOF'] 사이에
`"$STASH_AUDIT_TIMEOUT_SEC"` 인자가 없음. bash → python sys.argv[7] 연결 끊김.
실제 사이 구간:
rR   rS   )r   r[   findr]   r^   r_   r`   ra   rb   rc   rd   re   rf   )r?   r  rk   r   rl   r   r   regionr  r  r   r  r   rm   rn   s                  r   +test_medium_constant_passed_as_argv_in_bashr
    s    ##W#5D 45JO!O:OOO:OOOOOO:OOO:OOO!OOO12K1NOOOOOOOO*+F{{>*HK(IOrO8r>OOO8rOOOOOO8OOO8OOOOOOOOOOOOOODD9?DDD9DDDDDD9DDD9DDDDDDDDDDDDDDi  8i                          xj(=i[H_`     Xi(G' '72  	'7   	 (   	  	 ,3   	 ,3   	"")	,   	 r   )0__doc__builtinsr`   _pytest.assertion.rewrite	assertionrewriter^   r   r   rs   r*   r   r(   pathlibr   pytest__file__resolver$   WORKTREE_ROOTr   r/   dictr   r   r2   r;   intrG   r]   rp   fixtureru   booltupler   r   r   r   r   r   r   r   r   r   r   r  r
   r   r   <module>r     s  (   	   
   X&&(003*-==*-==$ c 2	_ 	_s 	_c 	_C 	_TX 	_I3 I3 I  B + + +x 0 0D*s *S *4 *ERUWZ\_R_L` *Z  $ 

s 

C 

D 

\ $
(*Z8	 r   