
    c:j!                    
   d Z ddlmZ ddlZddlZddlmZ h dZdZ ee	      j                         j                  j                  Zedz  dz  d	z  ZddlZ ej                   d
      ZddZd Zd ZddZdddddddd	 ddZy)u  
gate_shadow_runner.py — v2.14 S1/S2: §31 Phase-1 gate shadow report-only wrapper.

회장 승인: ANU_V2_14_PHASE1_GATE_SHADOW_S1_S2_IMPLEMENTATION_APPROVED_ACTIVE_FALSE (2026-06-20).

원칙 (엄수):
- report-only / fail-open. 어떤 verdict 도 block/exit/raise 로 연결하지 않는다.
- dispatch 흐름·반환값·기존 부작용·운영 파이프라인 동작을 변경하지 않는다(호출자는 None 만 받는다).
- 로그에는 raw prompt / credential / owner key / secret / env / 토큰 / 민감 args 를 기록하지 않는다.
  verdict / reason / touched_axes / rollback_cost + task_id / goal_id / level 수준 최소 메타만 기록한다.
- runtime_state 로 민감값이 들어와도 그 내용은 로그에 기록하지 않는다(classify_gate 입력으로만 사용).
- log write 실패도 fail-open(무시).
- 실제 차단 0 · production write 0 · callback/wake/fire 0 · systemd/always-on 무관. ACTIVE=false.

이 모듈은 §31 게이트(dispatch/v2_13_gate.classify_gate)를 dispatch 경로에서 report-only 로
1회 관측하기 위한 fail-open 래퍼다. 실제 차단(fail-closed) 전환은 별도 Chair 승인(S4) 대상이다.
    )annotationsN)Path>   tsmodelevelactivereasonsourcegoal_idtask_idverdict	task_typetouched_axesrollback_costcontract_source   memorystategate_shadow_logz^[A-Za-z0-9._+-]+$c                `   	 | t        |       nd}|rd|v sd|v st        j                  |      sy|rt        |      nt        }|dz  dz  | dz  }|j                         sydd	lm}  ||j                  d
            }t        |t              sy|j                  d      xs g }|syt        |j                  d      xs g       t        |j                  d      xs g       z   }|j                  d      }	ddlm}
 |dddt        |      d| |
g       |	d|	inddddS # t        $ r Y yw xY w)u  task md allowed_resources 에서 결정론적으로 strict_template goal_contract 를 구성한다.

    회장 승인 매핑만 사용(자유 서술/추론 0):
      goal_id ← task_id (task_id 에서만)
      expected_files ← allowed_resources.paths (paths 에서만)
      forbidden_operations ← forbidden_paths + commands (결정론 합)
      budget ← ttl_hours (있을 때만; 없으면 "UNSPECIFIED")
      rollback_floor ← dispatch.v2_13_gate.rollback_floor([]) 재사용
      scope · blast_radius · allowed_capability_delta · allowed_runtime_changes · exit_criteria = "UNSPECIFIED"
      provenance = "strict_template" 고정 — chair_injected 는 절대 사용하지 않는다(Trust Root #1).

    allowed_resources / paths 부재(불완전) → None 반환(기존 INVALID_CONTRACT_PROVENANCE 관측 유지).
    어떤 예외도 None 으로 흡수(fail-open). task md 원문/task_desc 는 contract 에 넣지 않는다.
    N z../r   tasksz.mdr   )_parse_allowed_resourcesutf-8encodingpathsforbidden_pathscommands	ttl_hours)rollback_floorUNSPECIFIEDstrict_templater   scopeblast_radiusallowed_capability_deltaexpected_filesallowed_runtime_changesforbidden_operationsr"   budgetexit_criteria
provenance)str_SAFE_TASK_IDmatchr   
_REPO_ROOTexistsdispatchr   	read_text
isinstancedictgetlistdispatch.v2_13_gater"   	Exception)r   	workspacetidwsmdr   arr   	forbiddenttlr"   s              2/home/jay/workspace/dispatch/gate_shadow_runner.pybuild_strict_template_contractrD   (   s;    %1c'lrdckSCZ}7J7J37O )T)_z(]W$#c{2yy{5%bllGl&DE"d#w%2 128b9D
ASAYWY<ZZ	ff[!6")(5"5k'4$-,R0,/O{C(*+
 	
  s)   .D! 1D! #.D! D! *A6D! !	D-,D-c                .    | xs d|xs dddddddddddS )up  dispatch 시점엔 표준 goal_contract 가 없으므로 best-effort 로 구성한다.

    provenance 를 비표준 표지(dispatch_runtime_shadow)로 둬, classify_gate 가
    INVALID_CONTRACT_PROVENANCE 를 관측하게 한다 — 이는 '현재 dispatch 가 표준
    goal_contract 를 만들지 않는다'는 사실의 가시화일 뿐 차단이 아니다.
    unknownNdispatch_runtime_shadowr%    )r   r   s     rC   _shadow_contractrI   Z   s9     'i#)$(#' $/     c                F    	 t        |       d t         S # t        $ r Y yw xY w)Nr   )r/   _REASON_CAPr;   )rs    rC   _safe_reasonrN   p   s*    1vl{## s    	  c                   	 | j                         D ci c]  \  }}|t        v s|| }}}|rt        |      nt        }|j	                  dd       t
        j
                  j                         j                  d      }t        |d| dz  dd      5 }|j                  t        j                  |d	
      dz          ddd       yc c}}w # 1 sw Y   yxY w# t        $ r Y yw xY w)uG   최소 메타 1줄을 jsonl 로 append. 실패해도 fail-open(무시).T)parentsexist_okz%Y%m%dgate_shadow_z.jsonlar   r   F)ensure_ascii
N)items_ALLOWED_LOG_KEYSr   _DEFAULT_LOG_DIRmkdirdatetimenowstrftimeopenwritejsondumpsr;   )reclog_dirkvsafeddayfs           rC   _append_logri   w   s    	!$GA5F0F1GG$DM*:	t,##%..x8!SE00#H 	AAGGDJJt%84?@	A 	A	 H	A 	A sE   C C	C	A+C *C C 	C CC C 	C'&C'r4   )r   r   r   runtime_staterb   r
   contractc                f   	 ddl m} |}d}	t        |t              r|j	                  d      dk7  rd}d}	|t        |       }t        |t              rdnd}	|t        | |      }d}	t        |t              r|n|||d	}
 ||d
g |
      xs i }t        t        j                  j                         j                         j                         || | |||j	                  d      t        |j	                  d            |j	                  d      |j	                  d      |	ddd|       y# t        $ r Y yw xY w)uB  §31 게이트를 report-only 로 1회 평가하고 최소 메타만 기록한다.

    항상 None 을 반환하며, 어떤 예외도 호출자에게 전파하지 않는다(fail-open).
    호출자(dispatch / finish_task)의 흐름·반환값·부작용을 변경하지 않는다.

    source: 관측 경로 표지("dispatch" 기본 / "finish_task" 등). 로그 메타에만 기록.
    contract: optional. 명시 시 사용(단 provenance="strict_template" 만 허용 — chair_injected
            자칭은 거부). 미지정이면 task md 에서 strict_template contract 빌드를 시도하고,
            만들 수 없으면 best-effort(_shadow_contract)로 기존 INVALID_CONTRACT_PROVENANCE 관측 유지.
            ★ 실제 차단 0 — provenance 통과로 다른 verdict 가 나와도 report-only 일 뿐이다.
    r   )classify_gateexplicitr.   r$   Nexplicit_rejectedbest_effort)r
   r   r   r   r   r	   r   r   report_only_shadowF)r   r
   r   r   r   r   r   r	   r   r   r   r   r   )r:   rm   r6   r7   r8   rD   rI   ri   rZ   r[   
astimezone	isoformatrN   r;   )r   r   r   rj   rb   r
   rk   rm   cr   rsress               rC   shadow_evaluaterw      s?   &5 $a155#6:K#KA1O9.w7A3=a3F/DO9 %0A+O(=]9uD
 Ar2r*0b##'')446@@B"wwy)"3778#45GGN3 WW_5.(
 	$   s   D!D$ $	D0/D0)N)returnNone)__doc__
__future__r   r_   rZ   pathlibr   rW   rL   __file__resolveparentr2   rX   re_recompiler0   rD   rI   rN   ri   rw   rH   rJ   rC   <module>r      s   " #    
 (^##%,,33
(725FF  12/d,  $44"&Z!5&*5rJ   