
    3jU                       U d Z ddlmZ ddlZddlZddlZddlmZmZ ddlm	Z	 ddl
mZmZmZ ddlmZ ddlmZmZmZmZmZmZmZ d	Zd
ed<   dZd
ed<   dZd
ed<   dZd
ed<   eZded<   dZd
ed<    G d de      Z d/dZ!d0dZ"ddd	 	 	 	 	 	 	 	 	 d1dZ#ed	 	 	 	 	 d2dZ$ed 	 	 	 	 	 d3d!Z%	 	 	 	 	 	 d4d"Z&d5d#Z'd/d$Z(d/d%Z)d/d&Z*d/d'Z+	 	 	 	 	 	 d6d(Z,	 	 	 	 	 	 d7d)Z-d8d*Z.d+dd,	 	 	 	 	 	 	 d9d-Z/g d.Z0y):u&  anu_v3.isolated_worktree_evidence_source — task-2553+8 isolated-worktree
evidence source (회장 §1/§2/§3, 9-R.1~9-R.6).

근본원인 (task-2553+7 첫 적용 HOLD): live workspace 가 미커밋 churn 으로
오염 → live 기반 effective-diff 가 6-file delta 로 성립 불가.

해결책 (회장 §2 verbatim, "특히 명시"): live workspace 청소가 **아니라**
fresh ``origin/main`` (7346df82) 기반 **isolated worktree** 를 만들고, 그
안에서 task-2553+1 F1-solo **6파일 delta 만 재현**한 뒤, 그 isolated
worktree 의 git diff / effective diff / source-PR preservation /
same-branch-push-0 / forbidden-scan 만 evidence source 로 사용한다.

본 모듈 한정 책임 (fail-closed, live workspace 무영향):
  - fresh origin/main isolated worktree 생성·검증·``git worktree remove``
    만으로 정리 (9-R.4: live·타 worktree 대상 reset/clean/stash/rm/unlink/
    rmtree **정적·런타임 0**).
  - task-2553+1 F1-solo 6-file delta 를 isolated worktree 안에서만 재현.
  - 9-R.2: 모든 git 호출 = absolute ``git -C <isolated_wt>`` 전용 +
    ``env=_sanitized_env()``. cwd-relative git·live path/mtime 미참조.
  - evidence bundle 은 frozen builder 의 **public** API
    (``collect_real_git_facts`` / ``build_evidence_bundle``) +
    frozen deriver public ``canonical_evidence_sha256`` 로만 조립
    (9-R.6 private helper 복제·import 0).
  - ``_provenance.source_workspace_type = "isolated_clean_worktree"`` 기록.
  - 구조적 HOLD guard: clean-replacement branch 가 다른(live/main)
    worktree 에 checkout 돼 있으면, runner primitive ①
    (``git checkout -B``) 가 그 live branch ref 를 reset → live 변형.
    → 실 write 전 ``would_mutate_live_branch`` HOLD 사유 산출
    (회장 §6 "특히 금지" / §7 / §12 fail-closed).
    )annotationsN)datetimetimezone)Path)AnyFinalMapping)canonical_evidence_sha256)FRESH_BASE_SHANEW_CLEAN_REPLACEMENT_BRANCHRECORDED_SOURCE_HEADSOURCE_BRANCHTASK_2553P1_EFFECTIVE_DIFF_6build_evidence_bundlecollect_real_git_factsz(anu_v3.isolated_worktree_evidence_sourcez
Final[str]MODULE1.0.0MODULE_VERSIONisolated_clean_worktreeSOURCE_WORKSPACE_TYPE_ISOLATEDlive_workspaceSOURCE_WORKSPACE_TYPE_LIVEzFinal[tuple[str, ...]]	SIX_FILESz$anu_v3.isolated_worktree_evidence.v1ISOLATED_EVIDENCE_SCHEMAc                      e Zd ZdZy)IsolatedWorktreeErroruN   isolated worktree 생성/검증/replay 실패. fail-closed: 부분 evidence 0.N)__name__
__module____qualname____doc__     ?/home/jay/workspace/anu_v3/isolated_worktree_evidence_source.pyr   r   A   s    Xr"   r   c                 f    t        j                  t        j                        j	                  d      S )Nz%Y-%m-%dT%H:%M:%SZ)r   nowr   utcstrftimer!   r"   r#   _now_utcr(   E   s!    <<%../CDDr"   c                     t         j                  j                         } | j                  dd       | j                  dd       | j                  dd       | j                  dd       | j                  dd       | S )u   모든 subprocess git 호출 env. GIT_* 오염 변수 제거 (isolation 강제).

    9-R.2 verbatim: GIT_DIR/GIT_WORK_TREE/GIT_INDEX_FILE/
    GIT_OBJECT_DIRECTORY/GIT_COMMON_DIR 를 pop. 이 패턴 외 git env 전달 금지.
    GIT_DIRNGIT_WORK_TREEGIT_INDEX_FILEGIT_OBJECT_DIRECTORYGIT_COMMON_DIR)osenvironcopypop)es    r#   _sanitized_envr4   L   sb     	

AEE)TEE/4 EE
D!EE
 $'EE
D!Hr"   T<   )checktimeoutc               |   t        t        |       j                               }t        j                  dd|g|dd|dt                     }|rW|j                  dk7  rHt        d| ddj                  |       d	|j                   d
|j                  j                                |j                  j                         S )u   absolute ``git -C <target>`` 전용 (9-R.2). cwd 인자 미사용,
    ``env=_sanitized_env()`` 강제. live path/mtime 미참조.
    gitz-CTF)capture_outputtextr7   r6   envr   zgit -C  z rc=z: )strr   resolve
subprocessrunr4   
returncoder   joinstderrstripstdout)targetr6   r7   argsabspathprocs         r#   _gitrK   [   s     $v,&&()G>>	g%%D A%#gYa/tDOO3DB{{  "#%
 	
 ;;r"   )branchc               ,   t        | dddd      }d}d| }|j                         D ]j  }|j                  d      r|t        d      d j	                         }1|j                  d	      sC|t        d	      d j	                         }||k(  se|h|c S  y)
ue  clean-replacement ``branch`` 가 다른 worktree 에 checkout 돼 있으면
    그 worktree 경로 반환 (없으면 None).

    회장 §6 "특히 금지" / §7 / §12: 점유 시 runner primitive ①
    ``git checkout -B <branch> <base>`` 가 그 live branch ref 를 **reset**
    → live workspace 변형. → 실 write 전 구조적 HOLD 사유.
    worktreelistz--porcelainFr6   Nzrefs/heads/z	worktree zbranch )rK   
splitlines
startswithlenrE   )	repo_pathrL   	porcelaincur_wt
target_reflinerefs          r#   detect_live_branch_conflictrZ   x   s     Y
FMOIFvh'J$$& ??;'#k*+,224F__Y's9~'--/Cj V%7 r"   )base_shac          	         t        t        j                  d            dz  }t        | dddt	        |      |       t        |dd      }||k7  rt        | |       t        d	| d
|       |S )u   fresh ``base_sha`` detached isolated worktree 생성 + base 검증.

    detached HEAD (branch ref 미생성 → live branch 무영향). live·타
    worktree 대상 파괴적 op 0.
    anu2553p8_isowt_)prefixwtrN   addz--detachz	rev-parseHEADz$isolated worktree HEAD != base_sha: z != )r   tempfilemkdtemprK   r>   remove_isolated_worktreer   )rT   r[   wt_dirheads       r#   create_isolated_worktreerg      s      23F 	Jz3v;IV,Dx F3#24&XJG
 	
 Mr"   c           	     V    t        | dddt        |      d       t        | ddd       y)u   isolated worktree 정리 = ``git worktree remove --force`` **만**
    (9-R.4: rm/unlink/rmtree/reset/clean/stash 0). 실패해도 live 무결.
    rN   removez--forceFrP   pruneN)rK   r>   )rT   re   s     r#   rd   rd      s(     	J)S[NJu5r"   c                    d| dS )u   #2 = ``test_owner_trigger_2553_plus1_high_fix.py`` (회장 §5 / 9-R.6:
    isolated wt 안에서 신규 생성). F1 = endpoint/args allowlist 강화
    invariant 회귀 (token transport 무관, task-2553+1 §2).u   """test_owner_trigger_2553_plus1_high_fix.py — task-2553+1 F1-solo regression.

F1 = `/gemini review` comment trigger 의 endpoint/args allowlist 강화. token transport 무관 (task-2553+1 §2). clean replacement PR 의 6-file effective diff #2 (회장 §5 single authority).
"""

from __future__ import annotations

import importlib.util
import sys
from pathlib import Path

import pytest

_WS = Path(__file__).resolve().parents[2]
if str(_WS) not in sys.path:
    sys.path.insert(0, str(_WS))

_OTP = _WS / u  


def _load_otp():
    spec = importlib.util.spec_from_file_location(
        "anu_v2_owner_trigger_pat_2553p1_highfix", _OTP
    )
    mod = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(mod)
    return mod


def test_allowed_comment_body_is_exactly_gemini_review():
    """F1 invariant: 허용 comment body 는 정확히 `/gemini review`."""
    otp = _load_otp()
    assert otp.ALLOWED_COMMENT_BODY == "/gemini review"


def test_args_allowlist_rejects_foreign_endpoint():
    """F1 invariant: 다른 endpoint → ENDPOINT_NOT_ALLOWED 정적 차단."""
    otp = _load_otp()
    with pytest.raises(Exception) as ei:
        otp._assert_args_allowlist(
            ["api", "-X", "POST", "/repos/o/r/issues/1/labels",
             "-f", "body=/gemini review"],
            "o", "r", 1,
        )
    assert otp.ERR_ENDPOINT_NOT_ALLOWED in str(ei.value)


def test_args_allowlist_rejects_foreign_body():
    """F1 invariant: 다른 body → BODY_NOT_ALLOWED 정적 차단."""
    otp = _load_otp()
    with pytest.raises(Exception) as ei:
        otp._assert_args_allowlist(
            ["api", "-X", "POST", "/repos/o/r/issues/1/comments",
             "-f", "body=please merge"],
            "o", "r", 1,
        )
    assert otp.ERR_BODY_NOT_ALLOWED in str(ei.value)
r!   )owner_trigger_rels    r#   _f1_high_fix_testrm      s     
	 *, -A-	A/r"   c                      	 y)Nu  # task-2553+1 — F1-solo clean replacement (isolated-worktree evidence source)

> **Status**: clean replacement PR (F1-solo). 6-file effective diff. PR#102 원본 보존, F2/token transport 무변경, merge 0, same-branch push 0.

## F1 (단독)

`anu_v2/owner_trigger_pat.py` 의 `/gemini review` comment trigger endpoint/args allowlist 강화 (token transport 무관, task-2553+1 §2). 보존 79-test 인터페이스 결합 없는 단독 신규 회귀 `tests/regression/test_owner_trigger_2553_plus1_high_fix.py` 동봉.

## 경계

- effective diff = 정확히 6 파일 (회장 §5 single authority).
- F2 / phase3 / mqe / preserved tests 변경 0.
- evidence source = fresh origin/main 7346df82 isolated worktree (task-2553+8 refinement).
r!   r!   r"   r#   
_f1_reportro      s    	&r"   c                 F    ddj                  d t        D              z   dz   S )Nz{
  "task_id": "task-2553+1",
  "mode": "F1-solo",
  "status": "CLEAN_REPLACEMENT_PR_OPEN_PREPARED",
  "effective_diff_files": [
z,
c              3  (   K   | ]
  }d | d  yw)z    ""Nr!   .0fs     r#   	<genexpr>z"_f1_result_json.<locals>.<genexpr>
  s     5auQCq\5s   z
  ],
  "source_pr_number": 102,
  "source_branch": "task/task-2553-dev5",
  "f2_token_transport_changed": false,
  "merge_required": false,
  "same_branch_push": false,
  "evidence_source": "isolated_clean_worktree"
}
)rC   r   r!   r"   r#   _f1_result_jsonrw     s/    	(
 **595
5	6	r"   c                      	 y)Nu  RED — task-2553+1 F1-solo
test_owner_trigger_2553_plus1_high_fix.py::test_allowed_comment_body_is_exactly_gemini_review FAILED (pre-fix: allowlist invariant 미강화)
test_args_allowlist_rejects_foreign_endpoint FAILED
test_args_allowlist_rejects_foreign_body FAILED
r!   r!   r"   r#   _f1_red_logry     s    	<r"   c                      	 y)Nu6  GREEN — task-2553+1 F1-solo
test_owner_trigger_2553_plus1_high_fix.py::test_allowed_comment_body_is_exactly_gemini_review PASSED
test_args_allowlist_rejects_foreign_endpoint PASSED
test_args_allowlist_rejects_foreign_body PASSED
3 passed — F1 endpoint/args allowlist 강화 검증 (token transport 무관)
r!   r!   r"   r#   _f1_green_logr{   !  s    	[r"   c                "   t        |      }t        d   }t        | dt         d|       }t        d   ||j	                  d      sdndz   t        d   t        |      t        d   t               t        d   t               t        d	   t               t        d
   t               i}|j                         D ]:  \  }}||z  }|j                  j                  dd       |j                  |d       < t        |dddgt          t        t              S )u_  isolated worktree 안에서 task-2553+1 F1-solo 6-file delta 재현.

    ``owner_trigger_pat.py`` 는 source branch 에서 **read-only 추출**
    (``git show <SOURCE_BRANCH>:...`` — PR#102 원본 mutation 0). 나머지
    5 파일은 결정적 합성 (회장 §5 single authority, 9-R.6 신규 생성).
    반환 = stage 된 파일 목록.
    r   show:
                T)parentsexist_okzutf-8)encodingr`   z-fz--)r   r   rK   r   endswithrm   ro   rw   ry   r{   itemsparentmkdir
write_textrO   )	rT   re   r_   otp_relotp_contentcontentsrelr;   dsts	            r#   replay_six_file_deltar   ,  s     
fBlGy&]O1WI*FGK 	!k1E1Ed1KTQST!'0!jl!o'!km!mo H ^^% /	T3h

5tg./ 	UD$++	?r"   c                   t        |      }t        | |       t        |ddd      }t        d |j	                         D              }t        |t        t        t        dt              }||d<   d|d	<   d
|d<   dt        dd  d|d<   |S )ug  isolated worktree 의 실 git diff / source-PR preservation /
    same-branch-push-0 / fresh-base / forbidden-scan 수집.

    frozen builder public ``collect_real_git_facts`` (repo_path=isolated_wt)
    로 source-PR/push/fresh-base/negative-scan 을 산출하고, effective diff
    만 isolated wt 의 실 **staged** diff (``git -C <wt> diff --cached
    --name-only``; detached HEAD == FRESH_BASE_SHA) 로 override
    (회장 §2 — isolated worktree 의 git diff 가 evidence source).
    commit 미수행 → branch ref / dangling commit / repo hook 무관,
    live workspace·타 worktree 무영향.
    diffz--cachedz--name-onlyc              3  B   K   | ]  }|j                         s|  y w)N)rE   rs   s     r#   rv   z)collect_isolated_facts.<locals>.<genexpr>`  s     K1QKs   z anu2553p8/__never_materialized__)source_branchrecorded_source_headfresh_base_sha
new_branchexpected_fileseffective_filesr   head_shaFbranch_existsz\ISOLATED git -C <isolated_wt> diff --cached --name-only (detached HEAD == fresh origin/main N   z2; task-2553+1 F1-solo 6-file delta replay, staged)
diff_basis)
r   r   rK   sortedrQ   r   r   r   r   r   )rT   re   r_   diff_outr   
base_factss         r#   collect_isolated_factsr   O  s     
fB)R(B
M:HK(;(;(=KKO (
#1%5 J %4J !Jz"'J//=bq/A.B C;	< |
 r"   c                    t        |       j                         D ci c]  \  }}|dk7  s|| }}}t        |      }d|ddt        t        t
        d|d<   |S c c}}w )u7  9-R.1: deriver 와 동일 ``canonical_evidence_sha256`` (``_provenance``
    제외) + ``source_workspace_type=isolated_clean_worktree`` 추가.

    canonical sha 는 ``_provenance`` 를 제외하므로 deriver/binding 재해시
    체인 무영향 (source_workspace_type 은 runner precondition 전용).
    _provenancez&anu_v3.pre_authorized_contract_deriverTr   )
derived_byevidence_bundle_sha256recomputed_all_gate_booleansderiver_versionsource_workspace_typeevidence_source_moduleevidence_source_version)dictr   r
   r   r   r   )bundlekvoutshas        r#   _stamp_isolated_provenancer   z  sk     !L..0
GDAqA4F1a4
GC
G
#C
(C>"%(,"!?"(#1C J Hs
   AAztask-2553+1)task_idcleanupc           	     \   t        |       }t        |      }d}	 t        |      }t        ||      }t	        |||d      }t        |      }i dt        dt        dt        dt               dt        d	t        d
t        |      d|dt        |d         d|d   dddddddddt        d|d|du|d<   ||r|t        ||       S S S # t         $ r:}dt        t        t               d| gdddcY d}~|r|t        ||       S S S d}~ww xY w# |r|t        ||       w w w xY w)up  fresh origin/main isolated worktree → 6-file delta replay →
    isolated git evidence → ``source_workspace_type=isolated_clean_worktree``
    provenance 번들. live workspace 무영향.

    실패/구조적 HOLD → ``{"status": "HOLD_FOR_CHAIR", ...}`` (실 write 0,
    예외 누출 0). 정상 → deriver/gate/binding 통과 가능한 evidence bundle.
    NF)r   rT   factsstampschemamodulemodule_versionts_utcr   r   isolated_worktree_pathisolated_worktree_removedisolated_effective_filesr   isolated_head_shar   live_workspace_referenced	git_callsz0absolute git -C <isolated_wt> + _sanitized_env())destructive_ops_on_live_or_other_worktreecleanup_policyz git worktree remove --force onlyclean_replacement_branch'clean_replacement_branch_checked_out_at(would_mutate_live_branch_on_real_pr_open_isolated_worktree_evidenceHOLD_FOR_CHAIRu#   isolated worktree evidence 실패: T)statusr   r   r   hold_reasonsreal_write_performedreport_to_chair)r   rZ   rg   r   r   r   r   r   r   r(   r   r   r>   rO   r   rd   	Exception)	rT   r   r   repoconflict_wtre   r   r   r3   s	            r#   build_isolated_evidence_bundler     s    	?D-d3KF,3)$/&tV4&	
 ,F31
.1
f1
 n1
 hj	1

 $%C1
 n1
 %c&k1
 (1
 'U3D-E(F1
  z!21
 (1
 K1
 81
 @1
  '(D!1
" 6{#1
$ 79%1
,-*  v)$T62 *7  	
&.jB1#FG$)#
 	
 v)$T62 *7	
 v)$T62 *7s0   B$C 	DD7D8D DD D+)r   r   r   r   r   r   r4   rZ   rg   rd   r   r   r   )returnr>   )r   zdict[str, str])
rG   
str | PathrH   r>   r6   boolr7   intr   r>   )rT   r   rL   r>   r   z
str | None)rT   r   r[   r>   r   r   )rT   r   re   r   r   None)rl   r>   r   r>   )rT   r   re   r   r   z	list[str])rT   r   re   r   r   dict[str, Any])r   zMapping[str, Any]r   r   )rT   r   r   r>   r   r   r   r   )1r    
__future__r   r/   r@   rb   r   r   pathlibr   typingr   r   r	   &anu_v3.pre_authorized_contract_deriverr
   -anu_v3.pre_authorized_evidence_bundle_builderr   r   r   r   r   r   r   r   __annotations__r   r   r   r   r   r   r   r(   r4   rK   rZ   rg   rd   rm   ro   rw   ry   r{   r   r   r   r   __all__r!   r"   r#   <module>r      s  > # 	   '  & & L   @
 ?$
 $ .G 
 F)9 J 9 %A	! @'M * MYI YE$ 	  	
 	@ /  	> #  
	066#-6	63l(&#-F%%#-%%V0 !	<3<3 <3 	<3
 <3~r"   