
    jwu                       U d Z ddlm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mZmZmZ ddlmZmZmZmZmZmZmZmZ ddlmZmZmZmZ  ej:                  e      Zd	Z d
e!d<   dZ"d
e!d<    ejF                  d      Z$de!d<    ejF                  d       ejF                  d       ejF                  d       ejF                  d       ejF                  d       ejF                  d       ejF                  d       ejF                  d       ejF                  d       ejF                  d      f
Z%de!d<   dZ&de!d<    ed !       G d" d#             Z' G d$ d%e(      Z) G d& d'e(      Z* G d( d)e+      Z, G d* d+e(      Z-d=d,Z.d>d-Z/d?d@d.Z0 ejF                  d/      Z1de!d0<   d1Z2de!d2<   d3Z3d
e!d4<   dd5dAd6Z4dBd7Z5 G d8 d9      Z6	 	 	 	 	 	 	 	 	 	 	 	 dCd:Z7	 	 	 	 	 	 	 	 	 	 	 	 	 	 dDd;Z8d@d<Z9y)Eu  anu_v2.owner_trigger_only — OWNER_TRIGGER_ONLY_CAPABILITY core module.

회장 §명시 14장 1:1 박제 (2026-05-11 KST, OWNER_TRIGGER_ONLY_CAPABILITY).
task-2554+1 회장 §명시 (2026-05-12 KST) HIGH race condition fix 반영.
task-2554+2 회장 §명시 (2026-05-12 KST) §1 1:1 완성:
  - ``RESULT_PENDING`` import + sentinel 활용.
  - ``http_post`` 호출 직전 ``txn.record(PENDING)`` 으로 영구 기록 — process crash 후
    다음 runner 가 DEDUPED 판정 (crash-safety fail-closed).
task-2563 회장 §명시 (2026-05-13 KST) FUC-3 hardening 반영:
  - ``logger.exception(...)`` 을 http_post 실패 경로 ``txn.record(FAILED)`` 호출 전에 추가.
  - secret masking 강제: token / Authorization / api_key 등은 ``_redact_diagnostics`` 로 redact.
  - audit fail-closed 속성 유지 (token_value_logged=False, token_hash_prefix 보존).

본 모듈 범위:
  - public action **1 종**: ``POST_GEMINI_REVIEW_TRIGGER_COMMENT``
  - comment body 상수 (외부 입력 X): ``COMMENT_BODY = "/gemini review"``
  - 단일 허용 endpoint: ``POST /repos/{owner}/{repo}/issues/{pr_number}/comments``
  - 금지 11 endpoint hard-block (``PermissionError`` raise)
  - 전용 token 1 종: ``OWNER_GEMINI_TRIGGER_TOKEN`` (다른 token fallback 0)
  - merge path 와 완전 분리: ``BOT_GITHUB_TOKEN`` 본 모듈 어디서도 사용 0
  - dedupe atomic (audit JSONL + fcntl flock + sidecar lock transaction)
  - token redaction guard (audit 에 raw value 미출력)
  - decision JSON v1 검증 fail-closed
  - result enum: POSTED / DEDUPED / FAILED / PENDING

result 흐름 (task-2554+2 §1):
  1. txn.check_dedupe (POSTED + PENDING)
  2. (DEDUPED 인 경우 audit DEDUPED + return)
  3. token presence + hash_prefix 계산
  4. **txn.record(PENDING)** — http_post 직전 영구 기록 (crash-safety)
  5. http_post 호출
  6. 성공: txn.record(POSTED) / 실패: txn.record(FAILED)

one-way isolation: anu_v2/ 외부 import 금지.
    )annotationsN)	dataclass)Path)AnyCallableFinal)AuditRedactionErrorDedupeViolationOwnerTriggerAuditRESULT_DEDUPEDRESULT_FAILEDRESULT_PENDINGRESULT_POSTEDtoken_hash_prefix)ALLOWED_ACTIONALLOWED_COMMENT_BODYDecisionInvalidErrorvalidate_decisionz/gemini reviewz
Final[str]COMMENT_BODYOWNER_GEMINI_TRIGGER_TOKENTOKEN_ENV_NAMEz(^/repos/[^/]+/[^/]+/issues/\d+/comments$zFinal[re.Pattern[str]]_ALLOWED_PATH_REz/pulls/\d+/merge\bz/pulls/\d+/reviews\bz/git/refs\bz/contents\bz/issues/\d+(?:\?|$)z	/labels\bz/branches\bz$/actions/(?:runs|workflows)/.*/rerunz3/check-runs/.*/rerequest|/check-suites/.*/rerequestz/pulls/\d+\?|/pulls/\d+$z"Final[tuple[re.Pattern[str], ...]]FORBIDDEN_ENDPOINT_PATTERNS)BOT_GITHUB_TOKENGH_TOKENGITHUB_TOKEN	OWNER_PAT	PAT_TOKENzFinal[tuple[str, ...]]FORBIDDEN_TOKEN_ENV_NAMEST)frozenc                  p    e Zd ZU dZded<   ded<   ded<   ded<   ded<   ded	<   d
ed<   ded<   dZded<   y)TriggerResultuP   ``trigger_gemini_review`` 결과 객체. token / authorization header 미포함.strstatusintprheadactioncomment_bodyendpointbooltoken_presentr   N
str | None
error_code)__name__
__module____qualname____doc____annotations__r.        ;/home/jay/workspace/scripts/../anu_v2/owner_trigger_only.pyr"   r"   j   s:    ZKG
IKM!J
!r5   r"   c                      e Zd ZdZy)ForbiddenEndpointErroru9   금지 11 endpoint 호출 시도 (회장 §7 hard-block).Nr/   r0   r1   r2   r4   r5   r6   r8   r8   |   s    Cr5   r8   c                      e Zd ZdZy)TokenBoundaryViolationu-   다른 token 사용 시도 (회장 §6 fail).Nr9   r4   r5   r6   r;   r;      s    7r5   r;   c                      e Zd ZdZy)CommentBodyViolationuK   COMMENT_BODY 외부 변조 / 외부 입력 사용 시도 (회장 §4 fail).Nr9   r4   r5   r6   r=   r=      s    Ur5   r=   c                      e Zd ZdZy)MergePathViolationuG   merge_queue_executor merge path 와의 분리 위반 (회장 §9 fail).Nr9   r4   r5   r6   r?   r?      s    Qr5   r?   c                    t        | t              r| rd| v rt        d      t        |t              r|rd|v rt        d      t        |t              rt        |t              s|dk  rt        d      d|  d| d| dS )	N/z,owner must be a non-empty string without '/'z+repo must be a non-empty string without '/'r   z pr_number must be a positive intz/repos/z/issues/z	/comments)
isinstancer#   
ValueErrorr%   r+   )ownerrepo	pr_numbers      r6   _build_post_comment_pathrG      s    eS!#,GHHdC tFGGi%It)D	UV;<<UG1TF(9+Y??r5   c                6   t        | t              r| j                         dk7  rt        d|       t        |t              st        d      t        D ]!  }|j                  |      st        d|       t        j                  |      st        d|      y)z+static + dynamic endpoint hard-block guard.POSTzforbidden method: zpath must be stringzforbidden endpoint path: z8path not in allow-list (POST issues/{n}/comments only): N)rB   r#   upperr8   r   searchr   match)methodpathpats      r6   assert_endpoint_allowedrP      s    fc"flln&>$'9&%DEEdC $%:;;* O::d(+DTH)MNNO !!$'$HQ
 	
 (r5   c                T    | yt         D ]  }|| v st        d| dt         d       y)u   token 경계 검증 (회장 §6).

    명시적으로 ``env`` dict 에 다른 token env 이름이 키로 전달되면 fail.
    Nzforbidden token env injected: u!    — owner trigger path must use  only)r   r;   r   )env	forbiddens     r6   assert_token_boundaryrU      sI    
 {. 	(0 <*+52 r5   z5(?i)(token|authorization|api[_-]?key|secret|password)_REDACT_KEY_RE)Bearer ghp_github_pat_ghu_ghs_ghr__REDACT_VALUE_SENTINELSz
<redacted>_REDACTED_PLACEHOLDER_depthc                  dk\  rt         S | t        | t        t        t        f      r| S t        | t
              r7| j                         }t        D ]  }|j                         |v st         c S  | S t        | t              rYi }| j                         D ]B  \  }}t        |      }t        j                  |      r
t         ||<   0t        |dz         ||<   D |S t        | t              r| D cg c]  }t        |dz          c}S t        | t              rt        fd| D              S 	 t        |       }	|	j                         }t        D ]  }|j                         |v st         c S  |	S c c}w # t        $ rI 	 t!        |       j"                  }
n# t        $ r d}
Y nw xY wt$        j'                  d|
d       t         cY S w xY w)	u}  dict / list / tuple / str 입력에서 token/Authorization/api_key 등 secret 을 masking.

    회장 §6 + task-2563 FUC-3 1:1: logger.exception 으로 diagnostic 박제 시 token 원문 / Authorization
    header / api_key 등이 로그에 누출되지 않도록 보수적 마스킹.

    동작:
      - dict: 키가 ``_REDACT_KEY_RE`` 패턴과 매치되면 값을 ``"<redacted>"`` 로 교체.
        그 외 키도 값 자체에 sentinel 포함 시 redact (defense in depth).
      - list / tuple: 원소를 재귀적으로 redact (튜플은 동일 타입 보존).
      - str: ``_REDACT_VALUE_SENTINELS`` 중 하나라도 포함되면 전체 redact.
      - int / bool / float / None: 그대로 반환.
      - 그 외: ``str(data)`` 로 변환한 뒤 위 규칙 적용 (방어적).

    재귀 깊이 상한 (``_depth >= 8``) — 비정상 입력에 대한 stack overflow 방지.
          r_   c              3  >   K   | ]  }t        |d z           yw)rc   r_   N)_redact_diagnostics).0vr`   s     r6   	<genexpr>z&_redact_diagnostics.<locals>.<genexpr>   s      M1(6A:>>Ms   unknownuH   redaction str() conversion failed for type=%s — redacting fail-closed.T)exc_info)r^   rB   r+   r%   floatr#   lowerr]   dictitemsrV   rK   re   listtuple	Exceptiontyper/   loggerwarning)datar`   
data_lowersentinelresultkeyvaluekey_strrg   text	type_name
text_lowers    `          r6   re   re      s     {$$|z$sE(:;$ZZ\
/ 	-H~~:-,,	- $**, 	PJC#hG$$W-"7w"5eFQJ"Ow	P $CGHa#Afqj9HH$MMMM%4y J+ )>>z)(() K+ I  
%	"T
++I 	"!I	"V 	 	

 %$
%s<   0E1/E6 6	G FGF$!G#F$$!GGc                r   i }dD ]&  }t        | |d      }t        |t              s!||d<    n t        | dd      }t        |t              rN|j	                         D ci c]  \  }}t        |      j                         |! }}}dD ]  }||v s||   ||<    t        | dd      }	|	r	d|vr|	|d<   |S c c}}w )uG  http_post 예외 객체에서 GitHub diagnostic 필드를 보수적으로 수집.

    수집 대상 (모두 optional, 미존재 시 skip):
      - status (HTTP status code) — ``exc.status`` / ``exc.code``
      - x-github-request-id        — ``exc.response_headers["x-github-request-id"]``
      - x-accepted-github-permissions
      - documentation_url          — ``exc.response_headers["documentation_url"]`` or
        ``exc.documentation_url``

    반환된 dict 은 호출자가 반드시 ``_redact_diagnostics`` 로 마스킹한 뒤 logger 에 전달해야 한다.
    )r$   status_codecodeNr$   response_headers)zx-github-request-idzx-accepted-github-permissionsdocumentation_urlr   )getattrrB   r%   rm   rn   r#   rl   )
excdiagattrrz   headerskrg   lowered
header_keydoc_urls
             r6   _collect_http_diagnosticsr   	  s     D1 T4(eS!"DN	 c-t4G'4 18AA3q6<<>1$AA
 	7J
 W$#*:#6Z 	7 c.5G&d2$+ !K Bs   $B3c                  z    e Zd ZdZdd	 	 	 	 	 	 	 	 	 ddZddZddd	 	 	 	 	 	 	 	 	 	 	 	 	 ddZd Zd	 Zd
 Z	d Z
y)OwnerTriggerOnlyu3  OWNER_TRIGGER_ONLY_CAPABILITY 단일 진입점.

    public action: ``trigger_gemini_review(decision_path, owner, repo, current_head_actual)``.

    side-effect 추상화 (test injection):
      - ``http_post``: ``Callable[[method:str, path:str, body:dict, headers:dict], dict]``
      - ``token_provider``: ``Callable[[], str]`` — OWNER_GEMINI_TRIGGER_TOKEN value 반환.

    task-2554+2 §1 흐름:
      1. token boundary 검증
      2. decision JSON 검증
      3. comment_body 검증
      4. endpoint hard-block
      5. ── ATOMIC TRANSACTION ────────────────────────────────
         5a. audit.transaction() 진입 (sidecar lock LOCK_EX)
         5b. txn.check_dedupe (POSTED + PENDING)
         5c. DEDUPED 시 audit record + return
         5d. token presence + hash_prefix 계산
         5e. **txn.record(PENDING)** — http_post 호출 직전 영구 기록 (crash-safety)
         5f. http_post 호출
         5g. 성공 시 audit POSTED / 실패 시 audit FAILED
      6. lock 해제
    N)auditc                   |t        d      |t        d      t        |      j                         | _        || _        || _        ||| _        y t        | j                        | _        y )Nz#http_post callable must be injectedz(token_provider callable must be injected)NotImplementedErrorr   resolve_workspace_root
_http_post_token_providerr   _audit)selfworkspace_root	http_posttoken_providerr   s        r6   __init__zOwnerTriggerOnly.__init__K  sk     %&KLL!%&PQQ#N3;;=#-$0e6GH\H\6]r5   c                    t        |      }|j                         s| j                  |z  }|j                         st	        dd|       t        j                  |j                  d            }|S )NE_FILE_MISSINGzdecision file not found: zutf-8)encoding)r   is_absoluter   existsr   jsonloads	read_text)r   decision_pathrN   ru   s       r6   _read_decisionzOwnerTriggerOnly._read_decision^  sg    M"!''$.D{{}&'7;TUYTZ9[\\zz$..'.:;r5   )env_overrider)   c                  t        |       | j                  |      }t        ||       |t        n|}|t        k7  rt        dt        d|      t        |d         }	|d   }
t        |||	      }t        d|       t        |      }|j                  dd	      }| j                  j                         5 }	 |j                  |	|

       | j'                         }t)        |t              r|st+        t,         d      t/        |      }|j                  ||	|
t         t0        t        ||d|dd       d| ddd}dt        i}	 | j3                  d|||       	 i }d	}	 |j                  ||	|
t         tF        t        ||d|dd       t%        tF        |	|
t         t        |d|d	      cddd       S # t        $ rV |j                  ||	|
t         t"        t        ||dd	ddd       t%        t"        |	|
t         t        |dd	d	      cY cddd       S w xY w# t4        $ r}	 t7        |      }t9        |      }t:        j=                  d|	|
dd dz   |||       nN# t4        $ rB}	 t?        d|	 d|
dd  d|t@        jB                         n# t4        $ r Y nw xY wY d}~nd}~ww xY w	 |j                  ||	|
t         tD        t        ||d|ddd       i }d	}|# i }d	}w xY wd}~ww xY w# i }d	}w xY w# t        tH        f$ r  w xY w# 1 sw Y   yxY w)u  단일 public action. /gemini review 댓글 1회 작성.

        race condition: 동시 2 process 가 같은 (pr, head) 호출 시 lock 직렬화로 후속
        process 의 check_dedupe 가 항상 DedupeViolation 차단.

        crash-safety (task-2554+2 §1): http_post 호출 직전 ``txn.record(PENDING)`` 으로
        영구 기록. process crash 후 다음 runner 가 ``_has_active_trigger`` 에서 PENDING
        발견 → DedupeViolation → DEDUPED 판정 (fail-closed).
        )current_head_actualNzcomment_body must be z, got r&   current_headrI   task_id )r&   r'   FDEDUPE)r   r&   r'   r(   rx   r)   r*   r   r,   r   token_value_loggedr.   )	r$   r&   r'   r(   r)   r*   r,   r   r.   u+    not provided — owner trigger fail-closedT)r   r&   r'   r(   rx   r)   r*   r   r,   r   r   rW   zapplication/vnd.github+jsonz
2022-11-28)AuthorizationAcceptzX-GitHub-Api-Versionbodyz[owner_trigger http_post FAILED pr=%s head=%s endpoint=%s token_hash_prefix=%s diagnostic=%srb   z...z)CRITICAL: owner_trigger logger failed pr=z head_prefix=z	 log_exc=)fileHTTP_POST_FAIL)%rU   r   r   r   r   r=   r%   rG   rP   r#   getr   transactioncheck_deduper
   recordr   r   r"   r   rB   r;   r   r   r   r   rq   r   re   rs   	exceptionprintsysstderrr   r   r	   )r   r   rD   rE   r   r   r)   decisionr   pr_numr'   rN   decision_path_strtask_id_valtxntokenhash_prefixr   body_payloadr   	http_diagredacted_diaglog_excs                          r6   trigger_gemini_reviewz&OwnerTriggerOnly.trigger_gemini_reviewi  s   ( 	l+ &&}5(8KL  ,3|''&'(<'?vdXN 
 Xd^$''tV<-.ll9b1 [[$$& ]	#  F 6> ((*EeS),%&&QR  ,E2K
 JJ*  ,,$0 $%6%))4*/$ $+5'!27(4G
 #L1L9lGDn 

#.$ $"0"/(4$():)--8.3$ !$%)""-
g]	 ]	 # 

#.$ $"0"0(4$():).-/.3&.  %))!-!"'&('
 
+]	 ]	D  4
 9# >I$7	$BM$$=Ra5(#% ! 
Gx P++/8*Ig[J!$
 % 
JJ'2"($(&4&3,8(,->-11<27*:" !GE	 !GEg4l & $%89 a]	 ]	s   5L 7F0
A9L HL )K,L 0AHL HL 	K 6IK	J'JJ	J	JJ	JKJK"*KKKKK  K##K))L ,K==L  L	c                    t        d      )NzZowner_trigger_only must not perform merge. Use merge_queue_executor with BOT_GITHUB_TOKEN.)r?   r   argskwargss      r6   mergezOwnerTriggerOnly.merge5  s     >
 	
r5   c                    t        d      )Nz)approve forbidden (review submit blocked)r8   r   s      r6   approvezOwnerTriggerOnly.approve;  s    $%PQQr5   c                    t        d      )Nzclose forbiddenr   r   s      r6   closezOwnerTriggerOnly.close>  s    $%677r5   c                    t        d      )Nzreopen forbiddenr   r   s      r6   reopenzOwnerTriggerOnly.reopenA  s    $%788r5   )
r   
str | Pathr   z&Callable[[str, str, dict, dict], dict]r   zCallable[[], str]r   zOwnerTriggerAudit | NonereturnNone)r   r   r   rm   )r   r   rD   r#   rE   r#   r   r#   r   dict | Noner)   r-   r   r"   )r/   r0   r1   r2   r   r   r   r   r   r   r   r4   r5   r6   r   r   2  s    < +/^ #^ :	^
 *^ (^ 
^&$ %)#'H "H 	H
 H !H "H !H 
HX
R89r5   r   c               Z   ddl m} ddlm} t	        | t
              st        d      	 | j                  ||||      }|j                  t         t        t        t"        fvrt        S |j                  S # t        t        t        t        |f$ r  |$ r	 t        cY S t        $ r	 t        cY S w xY w)u  ExecutorScheduler → OwnerTriggerOnly 호출 어댑터 (task-2556 §6).

    회장 §6 명시 1:1: ``owner_trigger_only.trigger_gemini_review()`` 를 scheduler 가
    자동 호출. 본 wrapper 는 runner 결과를 ``merge_queue_executor.orchestrate_owner_trigger_
    for_stale_pr`` 가 기대하는 string 형식 ("POSTED" / "DEDUPED" / "FAILED") 으로 정규화.

    설계 원칙:
      - 본 함수는 token 을 직접 만지지 않는다. ``runner`` 에 이미 ``token_provider`` 가
        주입되어 있어야 한다 (token boundary §11).
      - http_post 예외는 본 함수에서 catch — TokenBoundaryViolation / ForbiddenEndpointError /
        CommentBodyViolation 는 그대로 raise (fail-closed).
      - 일반 RuntimeError 는 "FAILED" 로 정규화 (transient 가능성, FAILED marker 가 caller
        에서 생성됨).

    Args:
      runner: OwnerTriggerOnly 인스턴스 (token_provider/http_post 이미 주입).
      decision_path: owner_trigger_decision.json 경로 (Path 또는 str).
      owner: GitHub owner.
      repo: GitHub repo.
      current_head_actual: 실측 PR head SHA (40-char hex).

    Returns:
      "POSTED" | "DEDUPED" | "FAILED" | "PENDING" — orchestrate_owner_trigger_for_stale_pr
      가 사용하는 enum.

    Raises:
      TokenBoundaryViolation: token 부재.
      ForbiddenEndpointError: 금지 endpoint 호출 시도.
      CommentBodyViolation: comment body 변조.
      MergePathViolation: merge path 침범.
      DecisionInvalidError: decision schema 위반.
    r   )r   )r
   (runner must be OwnerTriggerOnly instancer   rD   rE   r   )anu_v2.owner_trigger_decisionr   anu_v2.owner_trigger_auditr
   rB   r   	TypeErrorr   r;   r8   r=   r?   r   rq   r   r$   r   r   )runnerr   rD   rE   r   _DecisionInvalidError_DedupeViolationrx   s           r6   invoke_from_schedulerr   H  s    P \Nf./BCC--' 3	 . 
" }}]NM>ZZ== #$: "46KM  	  s   A2 2%B*B*)B*c               b    t        | t              st        d      | j                  ||||      S )u  second-review 자동 호출 진입점 (task-2565 §3.5 capability 재사용).

    내부적으로 기존 trigger_gemini_review를 호출. dedupe key는 pr_number+head_sha.

    Args:
      runner: OwnerTriggerOnly 인스턴스 (token_provider/http_post 이미 주입).
      pr_number: PR 번호.
      head_sha: 현재 head SHA.
      owner: GitHub owner.
      repo: GitHub repo.
      decision_path: owner_trigger_decision.json 경로.

    Returns:
      TriggerResult (status: POSTED|DEDUPED|FAILED|PENDING).
    r   r   )rB   r   r   r   )r   rF   head_sharD   rE   r   s         r6   trigger_for_second_reviewr     s?    0 f./BCC''#$	 (  r5   c                   | t        d      t        D ]  }|| v st        d|dt         d       t        | vrt        dt         d      | j                  t              }t	        |t
              r|st        t         d      y)	uF  scheduler 진입 시 token boundary 검증 (task-2556 §11).

    scheduler 가 BOT_GITHUB_TOKEN / GH_TOKEN / OWNER_PAT 등을 들고
    owner_trigger 경로로 진입하려는 시도를 hard-block. 회장 §11 / §16 1:1.

    또한 ``OWNER_GEMINI_TRIGGER_TOKEN`` 이 env 에 명시되어 있지 않으면 fail-closed.
    Nz>scheduler env must be provided for token boundary verificationz,scheduler env contains forbidden token name u!    — owner_trigger path must use rR   zscheduler env missing u    — owner_trigger fail-closedu-    present but empty/non-string — fail-closed)r;   r   r   r   rB   r#   )rS   rT   token_values      r6   assert_scheduler_token_boundaryr     s     {$L
 	
 / 	(>ym L//=.>eE  S $$^$44RS
 	
 ''.)Kk3'{$KL
 	
 0;r5   )rD   r#   rE   r#   rF   r%   r   r#   )rM   r#   rN   r#   r   r   )N)rS   r   r   r   )ru   r   r`   r%   r   r   )r   BaseExceptionr   rm   )r   'OwnerTriggerOnly'r   r   rD   r#   rE   r#   r   r#   r   r#   )r   r   rF   r%   r   r#   rD   r#   rE   r#   r   z'str | Path'r   r"   ):r2   
__future__r   r   loggingrer   dataclassesr   pathlibr   typingr   r   r   r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   	getLoggerr/   rs   r   r3   r   compiler   r   r   r"   PermissionErrorr8   r;   rC   r=   r?   rG   rP   rU   rV   r]   r^   re   r   r   r   r   r   r4   r5   r6   <module>r      s  "H #   	 
 !  ' '	 	 	  
		8	$
 ,j +9
 9+52::/, (  BJJ$%BJJ&'BJJ~BJJ~BJJ%&BJJ|BJJ~BJJ67BJJEFBJJ*+C ? 5 1  $" " ""D_ D8_ 8V: VR R@
& *4<*& 3 /  %1 z 0 56 9x#RP9 P9lCC C 	C
 C C 	CR  	
     D
r5   