
    xi
I                       U 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ZddlZddlmZ ddlmZ ddlZddlZ ee      j-                         j.                  d   Z ee      ej4                  vr"ej4                  j7                  d ee             ddlmZmZmZm Z m!Z! ddl"m#Z#m$Z$m%Z% d	Z&d
e'd<   	 dZ(d
e'd<   	 edz  dz  dz  dz  Z)de'd<   	 edz  dz  Z*de'd<   	 dZ+de'd<   	 e&fd*dZ,d+dZ-d,dZ.d-dZ/d.dZ0d/d Z1ejd                  jg                  dg d!      d0d"       Z4ejd                  jg                  d#g d$      	 	 	 	 	 	 	 	 d1d%       Z5d/d&Z6d.d'Z7d.d(Z8d.d)Z9y)2u  task-2488 Phase B PoC ``cycle_advancer`` 회귀 테스트.

본 모듈은 :mod:`tools.poc.cycle_advancer`의 5단계 분석 파이프라인 출력이

* deterministic (동일 입력 + 동일 fixed-timestamp → 동일 SHA-256)
* schema strict (YAML frontmatter 필수 필드 + 타입)
* safety-flag invariant (``proposal_only=True`` / ``ready_for_dispatch=False``)

세 조건을 모두 만족하는지 검증한다. PoC는 production lifecycle을 절대 건드려서는
안 되므로, 다음 forbidden 항목도 함께 보호한다:

* 실제 ``memory/events/task-XXXX.done`` / ``.escalate`` / ``.fail`` 생성 금지.
* ``scripts/`` / ``utils/`` / ``teams/`` / ``.github/`` 미터치.
* 외부 네트워크 호출 금지 (mock_ai_adapter 외 LLM 어댑터 미사용).

테스트는 모두 ``tmp_path`` fixture로 출력을 격리하여 실 ``memory/poc/`` 디렉토리를
오염시키지 않는다.
    )annotationsN)Path)Any   )DETERMINISTIC_SEEDGENERATOR_IDSCHEMA_VERSIONCycleAdvancerload_fixture)DraftPayloadrender_draftwrite_draftz2026-05-08T00:00:00ZstrFIXED_TIMESTAMP@d352e941f19a1620b08d4655d9644fc2056fd87779af0dde7281302d199a5333EXPECTED_SHA_TASK_2486toolspoccycle_advancerfixturesr   FIXTURE_DIRmemoryevents
EVENTS_DIR)scriptsutilsteamsz.githubtuple[str, ...]FORBIDDEN_DIRStask_idc                    t        t        |       }t               }|j                  |      }|j	                  |||      }t        |      S )uM  fixture에서 evidence를 로드하여 draft 본문(str)을 결정적으로 생성한다.

    Args:
        task_id: source task_id (fixture 파일 ``{task_id}.json``에 매칭).
        generated_at: ISO8601 + ``Z`` 형식 deterministic timestamp.

    Returns:
        :func:`render_draft`가 산출한 draft markdown 본문.
    evidenceanalysisgenerated_at)r   r   r
   analyzebuild_draft_payloadr   )r    r%   r#   advancerr$   payloads         4/home/jay/workspace/tests/poc/test_cycle_advancer.py_build_draft_textr+   M   sR      ,KAHH)H$88! 9 G
       c                   | j                   }d} ||      }|st        j                  d      dz   dt        j                         v st        j
                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |      dz  }t        t        j                  |            dx}x}}| j                  dd      }t        |      }d}||k(  }|st        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      dz   d|iz  }t        t        j                  |            dx}x}}t        j                  |d         }	t        |	t              }|s!t        j                  d      dz   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  }
t        t        j                  |
            d}|	S )uV  draft markdown에서 YAML frontmatter 블록을 strict하게 파싱한다.

    Args:
        draft_text: ``render_draft`` 출력 (frontmatter는 항상 ``---`` 으로 둘러싸임).

    Returns:
        ``yaml.safe_load`` 결과 dict.

    Raises:
        AssertionError: frontmatter 구조가 비정상이거나 dict가 아닌 경우.
    z---
u0   frontmatter는 첫 줄이 '---' 이어야 한다zN
>assert %(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.startswith
}(%(py4)s)
}
draft_text)py0py2py4py6Nr      ==z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenpartsr/   py1py3r2   uA   frontmatter 구분자 '---'가 정확히 두 번 나와야 한다
>assert %(py8)spy8   u1   frontmatter는 YAML 매핑(dict) 이어야 한다z7
>assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstanceparseddictr/   r:   r0   r1   )
startswith
@pytest_ar_format_assertmsg@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationsplitr7   _call_reprcompareyaml	safe_loadr?   rA   )r.   @py_assert1@py_assert3@py_assert5@py_format7r8   @py_assert2@py_assert4@py_format9r@   @py_format5s              r*   _parse_frontmatterrX   b   s         ) )   	;             !    ")    *      Wa(Eu:__:?___:______3___3______u___u___:______________^^E!H%Ffd#X#XX%XXXXXXX:XXX:XXXXXXfXXXfXXXXXXdXXXdXXX#XXXXXXMr,   c                    t         j                         s
t               S t               }t         j                         D ]4  }|j                  t        fd| D              s$|j                         6 |S )u@  ``memory/events/``에서 주어진 prefix로 시작하는 파일명 집합을 반환한다.

    Args:
        prefixes: ``task-2486`` 같은 파일명 prefix 튜플.

    Returns:
        조건을 만족하는 파일명들의 집합. ``memory/events`` 디렉토리가 없으면
        빈 집합을 반환한다.
    c              3  @   K   | ]  }j                  |        y w)N)rC   ).0pnames     r*   	<genexpr>z(_snapshot_event_files.<locals>.<genexpr>   s     4atq!4s   )r   is_dirsetiterdirr]   anyadd)prefixessnapshotentryr]   s      @r*   _snapshot_event_filesrg   y   sa     uH##% zz4844LL Or,   c                 6   i } t         D ]x  }t        |z  }|j                         st        j                  |d      D ]B  \  }}}|D ]7  }t        |      |z  }	 |j                         j                  | t        |      <   9 D z | S # t        t        f$ r Y Qw xY w)u   forbidden 디렉토리 하위 파일들의 mtime snapshot을 반환한다.

    Returns:
        ``{relative_path_str: mtime}`` dict. 디렉토리가 존재하지 않으면 해당
        엔트리는 누락된다.
    F)followlinks)r   _WORKSPACE_ROOTexistsoswalkr   statst_mtimer   FileNotFoundErrorPermissionError)snapsubrootdirpath_	filenamesfnamefpaths           r*   _snapshot_forbidden_mtimesrz      s      D ${{}%'WWTu%E 	!GQ	" W-',zz|'<'<DU$	 K *?; s   &BB	B	c                	   | dz  }t        d      }t        j                  |j                  d            j	                         }|t
        k(  }|st        j                  d|fd|t
        f      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dz  }t        j                  d	t
         d
|       dz   d|iz  }t        t        j                  |            d}t        t         d      }t#               }|j%                  |      }	|j'                  ||	t(              }
t+        |
|      }|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}}|j.                  }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                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            dx}x}x}}t        d      }t        j                  |j                  d            j	                         }||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z   d|iz  }t        t        j                  |            d}y)uL   동일 fixture + 동일 fixed-timestamp → 동일 SHA-256 (두 번 실행).zrun-1	task-2485utf-8r4   z%(py0)s == %(py2)ssha_ar   r/   r0   z'task-2485 draft SHA mismatch: expected , got 
>assert %(py4)sr1   Nr"   zBassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.is_file
}()
}writtenr/   r0   r1   )encoding)zX%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.read_text
}(encoding=%(py4)s)
} == %(py8)stext_a)r/   r0   r1   r2   r=   zassert %(py10)spy10sha_buA   동일 입력에 대해 두 번 실행한 결과 SHA가 달라짐)r+   hashlibsha256encode	hexdigestr   rD   rM   rF   rG   rH   rI   rE   rJ   rK   r   r   r
   r&   r'   r   r   is_file	read_text)tmp_path
output_dirr   r   rP   @py_format3rW   r#   r(   r$   r)   r   rQ   rR   @py_assert7rV   @py_format11text_br   s                      r*   +test_deterministic_output_task_2485_to_2486r      s   G#J{+FNN6==12<<>E**  5*                +    +    22H1I Jg	     K5HH)H**H? + G ':.G???77?8g8g.8.&8888.&88888878887888888g888.888888&888&8888888 {+FNN6==12<<>EE>^^^5E^^^^^^5^^^5^^^^^^E^^^E^^^^^^^^^^^r,   c                    t        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
   }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}}|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}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}|d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}|d   }d}||u }|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}}|d   }|t        k(  }|st        j                  d|fd|t        f      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }t        t        j                  |            d	x}}|d   }|t        k(  }|st        j                  d|fd|t        f      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }t        t        j                  |            d	x}}|d   }|sNt        j                  d      d z   d!t        j                  |      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  }dd|iz  }t        t        j                  |            d	x}}|d"   }
t        |
t        j                        rFt        j                  }d&}d'}d(}d)}d)}d)}t        j                   }|j"                  } ||||||||*      }|
|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                  t              rt        j                  t              nd,t        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |      d,t        j                         v st        j                  t              rt        j                  t              nd,t        j                  |      t        j                  |      t        j                  |      d-z  }d.d/|iz  }t        t        j                  |            d	x}x}x}x}x}x}x}x}x}x}}nt        |
t$              }|sd0d1t        j                         v st        j                  t              rt        j                  t              nd1d"t        j                         v st        j                  |
      rt        j                  |
      nd"d2t        j                         v st        j                  t$              rt        j                  t$              nd2t        j                  |      d3z  }t        t        j                  |            d	}|
t&        k(  }|st        j                  d|fd4|
t&        f      d"t        j                         v st        j                  |
      rt        j                  |
      nd"d5t        j                         v st        j                  t&              rt        j                  t&              nd5d6z  }d7d8|iz  }t        t        j                  |            d	}d9t&         }|| 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=   }d	}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}|d   }|t(        k(  }|st        j                  d|fd|t(        f      t        j                  |      d>t        j                         v st        j                  t(              rt        j                  t(              nd>dz  }dd|iz  }t        t        j                  |            d	x}}y	)?uL   draft md의 YAML frontmatter 필수 필드와 타입을 strict 검증한다.r|   schemazcycle_advancer/v1r4   )z%(py1)s == %(py4)sr:   r1   assert %(py6)sr2   Nsource_task_idproposed_task_id	task-2486classificationMERGE_PENDING_DEPENDENCYproposal_onlyTisz%(py1)s is %(py4)sready_for_dispatchFchairman_required	generatorzcycle_advancer/v1-mockz%(py1)s == %(py3)sr   r:   r;   assert %(py5)spy5deterministic_seedr   u0   deterministic_seed는 비어있으면 안 된다z
>assert %(py1)sr:   r%   inz%(py1)s in %(py3)sfmi        r   )tzinfo)z%(py0)s == %(py23)s
{%(py23)s = %(py4)s
{%(py4)s = %(py2)s.datetime
}(%(py6)s, %(py8)s, %(py10)s, %(py12)s, %(py14)s, %(py16)s, tzinfo=%(py21)s
{%(py21)s = %(py19)s
{%(py19)s = %(py17)s.timezone
}.utc
})
}_dt)r/   r0   r1   r2   r=   r   py12py14py16py17py19py21py23zassert %(py25)spy25z5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}r?   r   rB   r~   r   r   zassert %(py4)sr1   zgenerated_at: draftuJ   원문 frontmatter에는 정확한 timestamp 문자열이 있어야 한다
>assert %(py5)sconflict_summaryr	   )r+   rX   rD   rM   rI   rJ   rK   r   rF   rG   rH   r   rE   r?   r   datetimetimezoneutcr   r   r	   )r   r   @py_assert0rQ   rT   rW   rS   @py_format4@py_format6@py_format2r%   rR   r   @py_assert9@py_assert11@py_assert13@py_assert15@py_assert18@py_assert20@py_assert22rP   @py_format24@py_format26r   s                           r*   test_frontmatter_schema_strictr      s   k*E	E	"B h<...<.....<....<............;.;....;......;....... !0[0![0000![000!000[0000000=#==#=====#=======#======== o&$&$&&&&$&&&&&&$&&&&&&&"#,u,#u,,,,#u,,,#,,,u,,,,,,,!"+e+"e++++"e+++"+++e+++++++ k?666?66666?6666?66666666666k?*?l****?l***?******l***l*******"#9#'99999#'9999#999999'9999'99999999"#W#WW%WWWW#WWWWW
 >R>R>RRn%L,-"|| 	
	
	
	
	
	
	
(+	
(4(8(8	
|!Q1a(8 
 	
|  
 
 	
 	
 	
|  
 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
   # 	
 	
 		  # 	
 	
 		  , 	
 	
 		 	
 	
 		 	
 	
 		 	
 	
 		 	
 	
 		 	
 	
 		  	
 	
	6	
 	
  ),	
 	
 		 ),	
 	
 		 )5	
 	
 		 )9	
 	
 		 
 	
 	
 	
 	
 	
 	
 	
 	
 	
 ,,,,,,,,,z,,,z,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,....|......|...|................O,- -6  -    .      27    27    	U    
  !)T)!T))))!T)))!)))T))))))) h<)<>))))<>)))<))))))>)))>)))))))r,   r|   	task-2483task-2472+1c                $   t        |       }t        |      }|d   }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j
                  |  d      dz   d|iz  }t        t        j                  |            d	x}x}}|d
   }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j
                  |  d      dz   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}||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}}y	)uR   모든 fixture 출력에서 ``proposal_only=True`` / ``ready_for_dispatch=False``.r   Tr   r   r   u#   : proposal_only 안전장치 위반
>assert %(py6)sr2   Nr   Fu?   : ready_for_dispatch 안전장치 위반 (real dispatch 금지)zproposal_only: truer   r   r   r   r   r   zready_for_dispatch: false)r+   rX   rD   rM   rI   rE   rJ   rK   rF   rG   rH   )
r    r   r   r   rQ   rT   rW   rS   r   r   s
             r*   test_proposal_only_safety_flagsr      s    g&E	E	"Bo $ $&  $        #'    )67     "# u #u,  #u    $    (-    )RS    
 !) E)))) E))) ))))))E)))E)))))))&/&%////&%///&//////%///%///////r,   z@source_task_id,expected_proposed_task_id,expected_classification))r|   r   r   )r   	task-2484CLOSE_LIFECYCLE_BLOCKED)r   task-2472+2WORKFLOW_REGEX_INCOMPATIBLEc                "   t        |       }t        |      }|d   }|| k(  }|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   }||k(  }|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
   }||k(  }|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}	||	u }|slt        j                  d|fd||	f      t        j                  |      t        j                  |	      dz  }
dd|
iz  }t        t        j                  |            dx}x}}	|d   }d}	||	u }|slt        j                  d|fd||	f      t        j                  |      t        j                  |	      dz  }
dd|
iz  }t        t        j                  |            dx}x}}	y)uZ  3개 fixture가 명세된 다음 task_id + classification으로 매핑되는지 검증.

    명세상 ``task-2483`` 은 ``MERGED_CLOSE_BLOCKED_EXTERNAL`` 또는 그에 준한
    분류를 갖는다. 현재 mock 매핑은 ``CLOSE_LIFECYCLE_BLOCKED`` 으로 표기하므로
    "준한 분류" 조건을 만족한다 — 둘 다 close lifecycle 차단 의미.
    ``task-2472+1`` 은 명세 표기 ``MERGE_PENDING_DEPENDENCY`` 에 준하는
    ``WORKFLOW_REGEX_INCOMPATIBLE`` 분류를 사용한다 (workflow regex가 chain
    의존성을 차단하는 본질이므로 dependency 차단 계열).
    r   r4   r   r   r   r   Nr   expected_proposed_task_idr   expected_classificationr   Tr   r   r   r   r2   r   F)
r+   rX   rD   rM   rI   rF   rG   rH   rJ   rK   )r   r   r   r   r   r   rT   r   r   rQ   rW   rS   s               r*   test_three_fixtures_mappingr     s   , n-E	E	"B1>1111>111111111>111>1111111 !>!%>>>>>!%>>>>!>>>>>>%>>>>%>>>>>>>>:#:::::#::::::::::#::::#:::::::: o&$&$&&&&$&&&&&&$&&&&&&&"#,u,#u,,,,#u,,,#,,,u,,,,,,,r,   c                    t         dz  } | j                  } |       }|st        j                  d|        dz   dt	        j
                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}t        d      }| j                         }|j                  d      }||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z   d|iz  }t        t        j                  |            d}t        j                  |      j!                         }t        j                  |      j!                         }	||	k(  }|	t"        k(  }
|r|
st        j                  d||
fd||	t"        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t	        j
                         v st        j                  t"              rt        j                  t"              nddz  }dd|iz  }t        t        j                  |            dx}}
y)uI   task-2485 결과가 ``expected-task-2486-draft.md``와 byte-exact 일치.zexpected-task-2486-draft.mdzexpected fixture missing: D
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.is_file
}()
}expected_pathr   Nr|   r}   r4   r~   actual_bytesexpected_bytesr   uP   draft byte mismatch — render_draft 결과가 expected fixture와 다릅니다.r   r1   )r5   r5   )%(py0)s == %(py3)sz%(py3)s == %(py4)s
sha_actualsha_expectedr   )r/   r;   r1   r   r2   )r   r   rD   rE   rF   rG   rH   rI   rJ   rK   r+   
read_bytesr   rM   r   r   r   r   )r   rP   rQ   rW   actualr   r   r   r   r   rT   rS   s               r*   $test_output_matches_expected_fixturer   >  s!   "??M  P "P"PP&@$PPPPPPP=PPP=PPP PPP"PPPPPP{+F"--/N==)L>)  <>                *    *    	[    
 -779J>>.1;;=L??)??????:)???????:???:???????????????)????)????????r,   c                	   d}t        |      }dD ]>  }t        t        |      }t               }|j	                  |      }|j                  ||t              }t        || |j                  dd      z        }t        |      }	|	j                  }
t        |       } |
|      }|st        j                  | d| d      dz   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	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#        t        j$                  |            dx}	x}
x}}A t        |      }||z
  }t'               }||k(  }|st        j(                  d|fd||f      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  }t        j                  dt+        |       d      dz   d|iz  }t#        t        j$                  |            dx}}d}| j-                  d      D ]<  }|j/                         s|j0                  }|j2                  } ||      }| }|st        j                  d|       dz   dt        j                         v st        j                  |      rt        j                   |      ndt        j                   |      t        j                   |      dt        j                         v st        j                  |      rt        j                   |      ndt        j                   |      dz  }t#        t        j$                  |            dx}x}x}}? y)u  PoC 실행이 production lifecycle 파일을 만들지 않음을 보장한다.

    실행 전 ``memory/events/task-2486*`` / ``task-2484*`` / ``task-2472+2*``
    파일 snapshot을 떠두고, dry-run CLI를 (직접 호출 형태로) 실행한 뒤
    snapshot 차이가 0건이어야 한다. tmp_path를 출력 디렉토리로 사용해 실제
    PoC 산출물이 production 영역에 떨어지지 않게 한다.
    )r   r   r   r   r"   +rv   u    : PoC 산출물이 tmp_path 밖(u   )에 기록됨z
>assert %(py11)s
{%(py11)s = %(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py0)s(%(py1)s)
}.startswith
}(%(py9)s
{%(py9)s = %(py6)s(%(py7)s)
})
}r   outr   )r/   r:   r;   r   r2   py7py9py11Nr4   )z)%(py0)s == %(py4)s
{%(py4)s = %(py2)s()
}	new_filesr`   r   u1   production lifecycle 파일이 새로 생성됨: uO    — PoC는 .done / .escalate / .fail / .merge-pending 등 절대 생성 금지r   r2   )z.donez	.escalatez.fail*u-   PoC가 금지된 확장자 파일을 생성: zj
>assert not %(py7)s
{%(py7)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.name
}.endswith
}(%(py5)s)
}producedforbidden_suffixes)r/   r0   r1   r   r   )rg   r   r   r
   r&   r'   r   r   replacer   rC   rD   rE   rF   rG   rH   rI   rJ   rK   r`   rM   sortedrglobr   r]   endswith)r   rd   beforer    r#   r(   r$   r)   r   rT   rU   @py_assert8@py_assert10@py_format12afterr   rQ   rP   rW   rS   r   r   @py_assert6rV   s                           r*   +test_no_real_done_or_dispatch_files_createdr   V  s`    9H"8,F = 
W5 ?##H-..( / 

 '8gooc3.G#GH3x 	
x"" 	
3x= 	
"=1 	
1 	
  i7uNK	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
   	
 	
 		  	
 	
 		  	
 	
 		 # 	
 	
	6	
 	
  $' 	
 	
 		 $' 	
 	
	6	
 	
  (0 	
 	
 		 (0 	
 	
 		 $1 	
 	
 		 2 	
 	
 	
 	
 	
 	

 "(+EI 9   9                            <F9<M;N OU 	U     9NN3' }} }-- -.@A AA A   @zJ v      I    I %  I .  v   /A  I /A  I B      r,   c           	     X   t               }dD ]d  }t        t        |      }t               }|j	                  |      }|j                  ||t              }t        || |j                  dd      z         f t               }|j                         |j                         z  }|D 	cg c]  }	||	   ||	   k7  s|	 }
}	|
r(t        j                  d|
dd  dt        |
       d	       g }|
|k(  }|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        dz   d|iz  }t)        t        j*                  |            dx}}yc c}	w )u  PoC 실행으로 forbidden 디렉토리 mtime이 변경되지 않는지 검증한다.

    환경 의존성이 있으므로 파일 추가/삭제로 인한 차이는 ``xfail``로 처리한다.
    그러나 mtime이 변한 파일은 단 1건도 허용하지 않는다.
    r   r"   r   rv   uD   forbidden 영역에 mtime 변동 감지 (외부 봇 영향 가능): Nr   u    (총 u   건)r4   )r   changed)r/   r;   u;   forbidden 영역 파일 mtime이 PoC 실행 중 변경됨: r   r   )rz   r   r   r
   r&   r'   r   r   r   keyspytestxfailr7   rD   rM   rF   rG   rH   rI   rE   rJ   rK   )r   r   r    r#   r(   r$   r)   r   common_keyspathr   rT   rP   r   r   s                  r*   !test_forbidden_paths_not_modifiedr    s    ()F = 	CW5 ?##H-..( / 

 	GXS(AAB	C '(E
 ++-%**,.K$td(CG  Rr{m6#g,t5	

  7b=  7b                  Fgbqk]S    s   #F'4F'c                f	   t         dz  dz  dz  }|j                  } |       }|st        j                  d|       dz   dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}| d	z  }t        j                  t        j                  t        |      d
ddt        t              dt        |      dt         g
t        t               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|j(                         dz   d|iz  }	t        t        j                  |	            dx}x}}t+        |j-                  d            }
t/        |
      }d}||k(  }|st        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"|
       d#z   d$|iz  }t        t        j                  |            dx}x}}t1        j2                  |
d   j5                               j7                         }|t8        k(  }|st        j$                  d|fd%|t8        f      d&t	        j
                         v st        j                  |      rt        j                  |      nd&d't	        j
                         v st        j                  t8              rt        j                  t8              nd'd(z  }t        j                  d)t8         d*|       d+z   d,|iz  }t        t        j                  |            d}y)-u7  ``cycle_advancer_dry_run.py`` CLI를 subprocess로 직접 실행하는 e2e 테스트.

    CLI 인자 (``--task-id`` / ``--fixture-dir`` / ``--output-dir`` /
    ``--fixed-timestamp``)가 정상 동작하고, draft md가 정확히 1개 생성되며,
    SHA-256이 expected와 일치하는지 확인한다.
    r   r   zcycle_advancer_dry_run.pyzCLI entry not found: r   cli_pathr   Nzcli-outz	--task-idr|   z--fixture-dirz--output-dirz--fixed-timestampT   )cwdcapture_outputtexttimeoutr   r4   )z2%(py2)s
{%(py2)s = %(py0)s.returncode
} == %(py5)sproc)r/   r0   r   u   CLI 실패: rc=z
stdout=z
stderr=z
>assert %(py7)sr   zdraft-task-2486-*.mdr>   r6   r7   r   r9   u9   draft md가 정확히 1개 생성되어야 함. produced=r<   r=   r~   shar   r   u"   CLI 출력 SHA mismatch: expected r   r   r1   )rj   r   rD   rE   rF   rG   rH   rI   rJ   rK   
subprocessrunsys
executabler   r   r   
returncoderM   stdoutstderrlistglobr7   r   r   r   r   r   )r   r  rP   rQ   rW   r   r  rU   r   @py_format8r   rT   rR   rS   rV   r  r   s                    r*   test_dry_run_cli_e2er    s    (503NNHAAAA!6xjAAAAAAA8AAA8AAAAAAAAAAAAI%J>>NNM
O	
  !D& ?? a ?a  ?a                       $//*)DKK=	$++W     JOO$:;<Hx= A =A   =A                                DH:N     ..!//1
2
<
<
>C((  3(                )    )    --C,DF3%P    r,   )r    r   r%   r   returnr   )r.   r   r  zdict[str, Any])rd   r   r  zset[str])r  zdict[str, float])r   r   r  None)r  r  )r    r   r  r  )r   r   r   r   r   r   r  r  ):__doc__
__future__r   builtinsrF   _pytest.assertion.rewrite	assertionrewriterD   r   r   r   rl   r  r  pathlibr   typingr   r   rN   __file__resolveparentsrj   r   r  inserttools.poc.cycle_advancerr   r   r	   r
   r   &tools.poc.cycle_advancer.output_writerr   r   r   r   __annotations__r   r   r   r   r+   rX   rg   rz   r   r   markparametrizer   r   r   r   r  r   r,   r*   <module>r-     s  & #      	  
     x.((*2215sxx'HHOOAs?+,   . - \ G   S g%(88:E T  :"X-8
D 8 b"J J 9 9H !*.(:_B**d -0	0, F--"- !- 
	-->@0(`%Z+r,   