
    'iwZ                       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mZ ddlmZ ddlZdZeej&                  vrej&                  j)                  de       ddlmZmZmZmZ g dZd	dddd
ddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d_dZd`dZ G d d      Z G d d      Z G d d      Z G d d      Z  G d d      Z! G d d      Z" G d d      Z# G d d      Z$ G d d       Z% G d! d"      Z& G d# d$      Z' G d% d&      Z(d' Z)d( Z*d) Z+d* Z,d+ Z-d, Z.d- Z/d. Z0d/ Z1d0 Z2d1 Z3e4d2k(  riddl5Z5g d3 e       jl                  fd4 e       jn                  fd5 e       jp                  fd6 e       jr                  fd7 e       jt                  fd8 e       jv                  fd9 e       jx                  fd: e        jz                  fd; e        j|                  fd< e!       j~                  fd= e!       j                  fd> e"       j                  fd? e"       j                  fd@ e#       j                  fdA e$       j                  fdB e%       j                  fdC e%       j                  fdD e&       j                  fdE e&       j                  fdF e'       j                  fdG e'       j                  fdH e(       j                  fdI e(       j                  fdJ e(       j                  fdK e(       j                  fdLe)fdMe*fdNe+fdOe,fdPe-fdQe.fdRe/fdSe0fdTe1fdUe2fdVe3fZOdZP eQeO      ZReOD ]  \  ZSZT	  eT         eUdWeS        ePdXz  ZP  eUd[eP d\eR d]       ePeRk(  r	 eUd^       y ej                  dX       yy# eV$ r)ZW eUdYeS dZeW         e5j                          Y dZW[WzdZW[Www xY w)au:  
tests/track_b/test_story_9_07.py

Story 9.07: EpochReportGenerator — Epoch Summary Report

Black Box Tests (BB):
    BB1  Generated markdown contains "# Genesis Nightly Epoch"
    BB2  File written to correct path under report_dir
    BB3  PR URL included in markdown when result.pr_url is not None
    BB4  PR URL NOT in markdown when result.pr_url is None
    BB5  Status COMPLETED when all 7 phases present
    BB6  Status PARTIAL when some (but not 0) phases present
    BB7  Status FAILED when phases_completed is empty

White Box Tests (WB):
    WB1  No LLM calls during report generation (pure Python — inspected via mock)
    WB2  File path follows epoch_{epoch_id}.md convention
    WB3  Shadow Arena pass rate included when pr_url AND shadow_pass_rate set
    WB4  Shadow Arena pass rate NOT included when pr_url is None
    WB5  EpochResult / EpochReport are proper dataclasses

Additional Tests:
    T01  Axioms list rendered as Markdown bullet items
    T02  tier1_updates count appears in report
    T03  week_summary prose appears in report
    T04  Empty axioms renders graceful placeholder text
    T05  core.epoch __init__ exports EpochReportGenerator, EpochReport, EpochResult, REPORT_DIR
    T06  generate() creates report_dir if it does not exist
    T07  EpochReport.epoch_id matches input EpochResult.epoch_id
    )annotationsN)Optional)patchz/mnt/e/genesis-systemEpochReportGeneratorEpochReportEpochResult
REPORT_DIR)lock_acquireconversation_aggregateaxiom_distilltier1_updateshadow_arenapr_openlock_release
2026_02_25   zEGenesis ran 12 sessions. Three failures resolved via axiom evolution.epoch_idphasesaxiomspr_urltier1_updatesweek_summaryshadow_pass_ratec           	     D    t        | ||nt        dd ||ng ||||      S )z0Build a test EpochResult with sensible defaults.N)r   phases_completedr   r   r   r   r   )r	   ALL_7_PHASESr   s          6/mnt/e/genesis-system/tests/track_b/test_story_9_07.py_make_resultr    M   s8     #)#5<?+v#!)     c                 F    t        j                         } t        |       | fS )z8Return (generator, tmpdir) using a fresh temp directory.
report_dir)tempfilemkdtempr   )tmpdirs    r   _generator_in_tmpdirr(   c   s     F62F::r!   c                      e Zd ZdZd Zd Zy)TestBB1_HeaderPresentz;BB1: Generated markdown contains '# Genesis Nightly Epoch'.c                   t               \  }}t               }|j                  |      }d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      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}}y )	Nz# Genesis Nightly Epochinz8%(py1)s in %(py5)s
{%(py5)s = %(py3)s.markdown_content
}reportpy1py3py5z?Report header '# Genesis Nightly Epoch' is missing from content
>assert %(py7)spy7r(   r    generatemarkdown_content
@pytest_ar_call_reprcompare	_saferepr@py_builtinslocals_should_repr_global_name_format_assertmsgAssertionError_format_explanation
selfgen_resultr/   @py_assert0@py_assert4@py_assert2@py_format6@py_format8s
             r   test_header_in_contentz,TestBB1_HeaderPresent.test_header_in_contentq   s    %'Qf%( 	
F,C,C 	
(,CC 	
 	
(,C 	
 	
 		 ) 	
 	
	6	
 	
  -3 	
 	
 		 -3 	
 	
 		 -D 	
 	
  N	
 	
 	
 	
 	
 	
r!   c                   t               \  }}t               }|j                  |      }d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            d x}x}}y )Nz
## Status:r,   r.   r/   r0   assert %(py7)sr5   r(   r    r7   r8   r9   r:   r;   r<   r=   r>   r@   rA   rB   s
             r   test_status_line_presentz.TestBB1_HeaderPresent.test_status_line_presentz   s    %'Qf%6v666|66666|6666|666666v666v66666666666r!   N)__name__
__module____qualname____doc__rL   rP    r!   r   r*   r*   n   s    E
7r!   r*   c                  "    e Zd ZdZd Zd Zd Zy) TestBB2_FileWrittenToCorrectPathz3BB2: File written to correct path under report_dir.c           	        t               \  }}t        d      }|j                  |      }t        j                  }|j
                  }|j                  } ||      }|s!t        j                  d|j                         dz   dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      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}}y )Nr   r   zReport file not found at z
>assert %(py9)s
{%(py9)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.path
}.isfile
}(%(py7)s
{%(py7)s = %(py5)s.file_path
})
}osr/   )py0py2py4r3   r5   py9)r(   r    r7   rZ   pathisfile	file_pathr9   r?   r<   r=   r>   r;   r@   rA   )
rC   rD   r'   rF   r/   @py_assert1@py_assert3@py_assert6@py_assert8@py_format10s
             r   test_file_exists_after_generatez@TestBB2_FileWrittenToCorrectPath.test_file_exists_after_generate   sT   *,V|4f%ww 	
w~~ 	
f.. 	
~./ 	
/ 	
  ((8(8'9:	
 	
	6	
 	
   	
 	
 		  	
 	
 		  	
 	
 		  	
 	
	6	
 	
  % 	
 	
 		 % 	
 	
 		 / 	
 	
 		 0 	
 	
 	
 	
 	
 	
r!   c                   t               \  }}t        d      }|j                  |      }|j                  }|j                  } ||      }|st        j                  d|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}}y )	Nr   rY   zReport file z is outside report_dir zm
>assert %(py7)s
{%(py7)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.file_path
}.startswith
}(%(py5)s)
}r/   r'   r[   r\   r]   r3   r5   )r(   r    r7   ra   
startswithr9   r?   r<   r=   r>   r;   r@   rA   )	rC   rD   r'   rF   r/   rb   rc   rd   rK   s	            r   test_file_is_inside_report_dirz?TestBB2_FileWrittenToCorrectPath.test_file_is_inside_report_dir   s?   *,V|4f% 	
** 	
*62 	
2 	
  6++..EfZP	
 	
	6	
 	
   	
 	
 		  	
 	
 		   	
 	
 		 + 	
 	
	6	
 	
  ,2 	
 	
 		 ,2 	
 	
 		 3 	
 	
 	
 	
 	
 	
r!   c                   t               \  }}t        d      }|j                  |      }t        |j                  d      5 }|j                         }d d 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t        j                  |      d	z  }	t        j                  d
      dz   d|	iz  }
t        t        j                  |
            d x}}y # 1 sw Y   xY w)Nr   rY   utf-8encoding==)z8%(py0)s == %(py4)s
{%(py4)s = %(py2)s.markdown_content
}on_diskr/   r[   r\   r]   z=Content read from disk does not match report.markdown_content
>assert %(py6)spy6)r(   r    r7   openra   readr8   r9   r:   r<   r=   r>   r;   r?   r@   rA   )rC   rD   r'   rF   r/   fhrr   rc   rb   @py_format5@py_format7s              r   *test_file_content_matches_markdown_contentzKTestBB2_FileWrittenToCorrectPath.test_file_content_matches_markdown_content   s4   *,V|4f%&""W5 	 ggiG	  !11 	
w11 	
 	
w1 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
  ! 	
 	
 		 ! 	
 	
 		 2 	
 	
  L	
 	
 	
 	
 	
	  	 s   E##E-N)rQ   rR   rS   rT   rg   rk   r{   rU   r!   r   rW   rW      s    =



r!   rW   c                      e Zd ZdZd Zd Zy)TestBB3_PRUrlIncludedWhenSetz@BB3: PR URL included in markdown when result.pr_url is not None.c                p   t               \  }}d}t        |      }|j                  |      }|j                  }||v }|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t	        j                  |      dz  }t	        j                  d| d	      d
z   d|iz  }	t        t	        j                  |	            d x}}y )N'https://github.com/genesis/repo/pull/42r   r,   z8%(py0)s in %(py4)s
{%(py4)s = %(py2)s.markdown_content
}prr/   rs   zPR URL 'z' not found in markdown contentrt   ru   r(   r    r7   r8   r9   r:   r<   r=   r>   r;   r?   r@   rA   )
rC   rD   rE   r   rF   r/   rc   rb   ry   rz   s
             r   test_pr_url_in_contentz3TestBB3_PRUrlIncludedWhenSet.test_pr_url_in_content   s   %'Q6R(f%,, 	
r,, 	
 	
r, 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
   	
 	
 		  	
 	
 		 - 	
 	
  rd9:	
 	
 	
 	
 	
r!   c                   t               \  }}d}t        |      }|j                  |      }d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }	dd	|	iz  }
t        t	        j                  |
            d x}x}}y )
Nr   r   ## Pull Requestr,   r.   r/   r0   rN   r5   rO   )rC   rD   rE   r   rF   r/   rG   rH   rI   rJ   rK   s              r   test_pr_section_header_presentz;TestBB3_PRUrlIncludedWhenSet.test_pr_section_header_present   s    %'Q6R(f% ;F$;$;; $;;;;; $;;;; ;;;;;;F;;;F;;;$;;;;;;;;r!   N)rQ   rR   rS   rT   r   r   rU   r!   r   r}   r}      s    J
<r!   r}   c                      e Zd ZdZd Zd Zy)TestBB4_PRUrlAbsentWhenNonez7BB4: PR URL NOT in markdown when result.pr_url is None.c                t   t               \  }}t        d       }|j                  |      }d}|j                  }||v}|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            d x}x}}d	}|j                  }||v}|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            d x}x}}y )
Nr   z
github.comnot inz<%(py1)s not in %(py5)s
{%(py5)s = %(py3)s.markdown_content
}r/   r0   rN   r5   r   rO   rB   s
             r   #test_no_pr_url_in_content_when_nonez?TestBB4_PRUrlAbsentWhenNone.test_no_pr_url_in_content_when_none   s   %'QT*f%:6#:#::|#:::::|#::::|::::::6:::6:::#:::::::: ?(?(?? (????? (???? ????????????(????????r!   c                   t               \  }}t        d       }|j                  |      }d}|j                  }||v}|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            d x}x}}y )	Nr   z	PR openedr   r   r/   r0   rN   r5   rO   rB   s
             r   #test_no_pr_section_when_pr_url_nonez?TestBB4_PRUrlAbsentWhenNone.test_no_pr_section_when_pr_url_none   s    %'QT*f%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99999r!   N)rQ   rR   rS   rT   r   r   rU   r!   r   r   r      s    A@:r!   r   c                      e Zd ZdZd Zd Zy)TestBB5_StatusCompletedz3BB5: Status is COMPLETED when all 7 phases present.c                   t               \  }}t        t        d d        }|j                  |      }d}|j                  }||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}x}}y )	Nr   ## Status: COMPLETEDr,   r.   r/   r0   rN   r5   r(   r    r   r7   r8   r9   r:   r;   r<   r=   r>   r@   rA   rB   s
             r    test_completed_with_all_7_phasesz8TestBB5_StatusCompleted.test_completed_with_all_7_phases   s    %'Q\!_5f%%@)@)@@%)@@@@@%)@@@@%@@@@@@@@@@@@)@@@@@@@@r!   c                   t               \  }}t        t        dgz         }|j                  |      }d}|j                  }||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd	|iz  }	t        t        j                  |	            d
x}x}}y
)u'   More than 7 phases → still COMPLETED.bonus_phaser   r   r,   r.   r/   r0   rN   r5   Nr   rB   s
             r   &test_completed_with_more_than_7_phasesz>TestBB5_StatusCompleted.test_completed_with_more_than_7_phases   s    %'Q\]O%CDf%%@)@)@@%)@@@@@%)@@@@%@@@@@@@@@@@@)@@@@@@@@r!   N)rQ   rR   rS   rT   r   r   rU   r!   r   r   r      s    =AAr!   r   c                      e Zd ZdZd Zd Zy)TestBB6_StatusPartialz<BB6: Status is PARTIAL when some (but not 0) phases present.c                   t               \  }}t        t        d d       }|j                  |      }d}|j                  }||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd	|iz  }	t        t        j                  |	            d x}x}}y )
N   r   ## Status: PARTIALr,   r.   r/   r0   rN   r5   r   rB   s
             r   test_partial_with_3_phasesz0TestBB6_StatusPartial.test_partial_with_3_phases   s    %'Q\"1%56f%#>v'>'>>#'>>>>>#'>>>>#>>>>>>v>>>v>>>'>>>>>>>>r!   c                   t               \  }}t        dg      }|j                  |      }d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd	|iz  }	t        t	        j                  |	            d x}x}}y )
Nr   r   r   r,   r.   r/   r0   rN   r5   rO   rB   s
             r   test_partial_with_1_phasez/TestBB6_StatusPartial.test_partial_with_1_phase   s    %'Qn%56f%#>v'>'>>#'>>>>>#'>>>>#>>>>>>v>>>v>>>'>>>>>>>>r!   N)rQ   rR   rS   rT   r   r   rU   r!   r   r   r      s    F??r!   r   c                      e Zd ZdZd Zy)TestBB7_StatusFailedz5BB7: Status is FAILED when phases_completed is empty.c                   t               \  }}t        g       }|j                  |      }d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            d x}x}}y )	Nr   z## Status: FAILEDr,   r.   r/   r0   rN   r5   rO   rB   s
             r   test_failed_with_no_phasesz/TestBB7_StatusFailed.test_failed_with_no_phases   s    %'QR(f%"=f&=&=="&====="&===="======f===f===&========r!   N)rQ   rR   rS   rT   r   rU   r!   r   r   r      s
    ?>r!   r   c                      e Zd ZdZd Zd Zy)TestWB1_NoPurePythonOnlyz9WB1: No LLM calls during report generation (pure Python).c                   t               \  }}t               }t        dt              5 }|j	                  |      }|j
                  D cg c]J  }t        |j                        dk\  r0t        |j                  d   t              rd|j                  d   v r|L }}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}
}	ddd       yc c}w # 1 sw Y   yxY w)z@Patch common LLM entry points to confirm they are never invoked.zbuiltins.open)wraps   r   .md>=z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} >= %(py6)slenwrite_callsr[   r1   r2   ru   z%Expected at least one file write callz
>assert %(py8)spy8N)r(   r    r   rv   r7   call_args_listr   args
isinstancestrr9   r:   r<   r=   r>   r;   r?   r@   rA   )rC   rD   rE   rF   	mock_openr/   cr   rI   @py_assert5rH   rz   @py_format9s                r   test_no_gemini_client_calledz5TestWB1_NoPurePythonOnly.test_no_gemini_client_called  sM   %'Q ?$/ 	R9\\&)F %33qvv;!#
166!9c(BuPQPVPVWXPYGY K  {#QqQ#q(QQQ#qQQQQQQ3QQQ3QQQQQQ{QQQ{QQQ#QQQqQQQ*QQQQQQQQ	R 	R	R 	Rs    G	AG
D)G
GGc                Z   ddl mc m} ddl}|j                  }t        |d      5 }|j                         }dd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}}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}|}|sd}|j                   }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  }|
j#                  |       |st        j                  dfdf      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                        t        j                        t        j                  |      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}x}x}x}}y# 1 sw Y   jxY w)zCopenai and google.generativeai must NOT be imported by this module.r   Nrm   rn   openair   )z%(py1)s not in %(py3)ssourcer1   r2   z0openai import found in epoch_report_generator.pyz
>assert %(py5)sr3   generativeaiz=google.generativeai import found in epoch_report_generator.pygemini_clientimport)z%(py3)s not in %(py5)s)r2   r3   z%(py7)sr5   )zV%(py10)s not in %(py18)s
{%(py18)s = %(py14)s
{%(py14)s = %(py12)s.split
}(%(py16)s)
})py10py12py14py16py18z%(py20)spy20r   zUnexpected gemini_client importz
>assert %(py23)spy23)!core.epoch.epoch_report_generatorepochepoch_report_generatorsys__file__rv   rw   r9   r:   r;   r<   r=   r>   r?   r@   rA   splitappend_format_boolop)rC   modr   source_pathrx   r   rG   rI   @py_format4rJ   rb   rH   @py_assert9@py_assert13@py_assert15@py_assert17@py_assert11rK   @py_format19@py_format21@py_format22@py_format24s                         r   $test_generate_does_not_import_openaiz=TestWB1_NoPurePythonOnly.test_generate_does_not_import_openai  s   77ll+0 	BWWYF	Yxv%YYYxvYYYxYYYYYYvYYYvYYYY'YYYYYYYl~V+lll~Vlll~llllllVlllVllll-lllllll	
 	
f, 	
 	
v|| 	
T\ 	
|T\G] 	
G]0] 	
 	
 	
f 	
 	
 		  	
 	
	6	
 	
  '- 	
 	
 		 '- 	
 	
 	
	6	
		
 	
G] 	
 	
 		 1@ 	
 	
	6	
 	
  HN 	
 	
 		 HN 	
 	
 		 HT 	
 	
 		 U] 	
 	
 		 H^ 	
 	
 	
	6	
		
 	
 	
  .	
 	
 	
 	
 	
 	
 	
		 	s   N  N*N)rQ   rR   rS   rT   r   r   rU   r!   r   r   r     s    CR
r!   r   c                      e Zd ZdZd Zd Zy)TestWB2_FilePathConventionz6WB2: File path follows epoch_{epoch_id}.md convention.c                   t               \  }}d}t        |      }|j                  |      }d| d}t        j                  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                  |      rt        j                  |      ndd	z  }	t        j                  d
| d| d      dz   d|	iz  }
t        t        j                  |
            d }y )Nr   rY   epoch_r   rp   )z%(py0)s == %(py2)sactual_filenameexpected_filenamer[   r\   zExpected filename '', got ''z
>assert %(py4)sr]   )r(   r    r7   rZ   r_   basenamera   r9   r:   r<   r=   r>   r;   r?   r@   rA   )rC   rD   r'   r   rF   r/   r   r   rb   @py_format3ry   s              r    test_filename_matches_conventionz;TestWB2_FilePathConvention.test_filename_matches_convention&  s   *,Vx0f%$XJc2''**6+;+;<"33 	
 	
"3 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
  #4 	
 	
 		 #4 	
 	
  ""3!4H_<MQO	
 	
 	
 	
 	
r!   c           
        t               \  }}d}t        |      }|j                  |      }d| d}t        j                  }|j
                  }|j                  }	 ||	      }
|
|k(  }|s_t        j                  d|fd|
|f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      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	d
z  }dd|iz  }t        t        j                  |            d x}x}x}	x}
}y )N2026_03_01_nightlyrY   r   r   rp   )z%(py9)s
{%(py9)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.path
}.basename
}(%(py7)s
{%(py7)s = %(py5)s.file_path
})
} == %(py11)srZ   r/   r   )r[   r\   r]   r3   r5   r^   py11zassert %(py13)spy13)r(   r    r7   rZ   r_   r   ra   r9   r:   r<   r=   r>   r;   r@   rA   )rC   rD   r'   r   rF   r/   r   rb   rc   rd   re   @py_assert10@py_format12@py_format14s                 r    test_custom_epoch_id_in_filenamez;TestWB2_FilePathConvention.test_custom_epoch_id_in_filename3  s$   *,V'x0f%$XJc2wwFwF 0 0F 01F15FFFFF15FFFFFFFrFFFrFFFwFFFFFFFFFFFFFFF 0FFF1FFFFFF5FFFF5FFFFFFFFFr!   N)rQ   rR   rS   rT   r   r   rU   r!   r   r   r   #  s    @
Gr!   r   c                      e Zd ZdZd Zd Zy)TestWB3_ShadowArenaRateIncludedzJWB3: Shadow Arena pass rate included when pr_url AND shadow_pass_rate set.c                .   t               \  }}t        dd      }|j                  |      }d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }t	        j                  d	|j                         d
z   d|iz  }	t        t	        j                  |	            d x}x}}y )N&https://github.com/genesis/repo/pull/1g      ?r   r   z87.5%r,   r.   r/   r0   z"Expected '87.5%' in content, got:
r4   r5   r6   rB   s
             r    test_shadow_pass_rate_in_contentz@TestWB3_ShadowArenaRateIncluded.test_shadow_pass_rate_in_content@  s    %'Q;"
 f% 	
&11 	
w11 	
 	
w1 	
 	
 		  	
 	
	6	
 	
  ! 	
 	
 		 ! 	
 	
 		 2 	
 	
  2&2I2I1JK	
 	
 	
 	
 	
 	
r!   c                   t               \  }}t        dd      }|j                  |      }d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }d	d
|iz  }	t        t	        j                  |	            d x}x}}y )Nr   g      ?r   Shadow Arena pass rater,   r.   r/   r0   rN   r5   rO   rB   s
             r   #test_shadow_pass_rate_label_presentzCTestWB3_ShadowArenaRateIncluded.test_shadow_pass_rate_label_presentL  s    %'Q; 
 f%'B6+B+BB'+BBBBB'+BBBB'BBBBBB6BBB6BBB+BBBBBBBBr!   N)rQ   rR   rS   rT   r   r   rU   r!   r   r   r   =  s    T

Cr!   r   c                      e Zd ZdZd Zd Zy)%TestWB4_ShadowArenaRateAbsentWhenNoPRz=WB4: Shadow Arena pass rate NOT included when pr_url is None.c                   t               \  }}t        d d      }|j                  |      }d}|j                  }||v}|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd	|iz  }	t        t	        j                  |	            d x}x}}y )
N?r   zShadow Arenar   r   r/   r0   rN   r5   rO   rB   s
             r   test_no_shadow_rate_when_no_przDTestWB4_ShadowArenaRateAbsentWhenNoPR.test_no_shadow_rate_when_no_prZ  s    %'QTC@f%<V%<%<<~%<<<<<~%<<<<~<<<<<<V<<<V<<<%<<<<<<<<r!   c                   t               \  }}t        dd       }|j                  |      }d}|j                  }||v}|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd	|iz  }	t        t	        j                  |	            d x}x}}y )
Nr   r   r   r   r   r/   r0   rN   r5   rO   rB   s
             r   .test_no_shadow_rate_when_shadow_pass_rate_nonezTTestWB4_ShadowArenaRateAbsentWhenNoPR.test_no_shadow_rate_when_shadow_pass_rate_nonea  s    %'Q;!
 f%'Fv/F/FF'/FFFFF'/FFFF'FFFFFFvFFFvFFF/FFFFFFFFr!   N)rQ   rR   rS   rT   r  r  rU   r!   r   r   r   W  s    G=Gr!   r   c                  (    e Zd ZdZd Zd Zd Zd Zy)TestWB5_DataclassesCorrectzLWB5: EpochResult and EpochReport are proper dataclasses with correct fields.c                   t         j                  } |t              }|sddt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            d x}}y )NNassert %(py5)s
{%(py5)s = %(py2)s
{%(py2)s = %(py0)s.is_dataclass
}(%(py3)s)
}dataclassesr	   r[   r\   r2   r3   )
r  is_dataclassr	   r<   r=   r9   r>   r;   r@   rA   rC   rb   rH   rJ   s       r   test_epoch_result_is_dataclassz9TestWB5_DataclassesCorrect.test_epoch_result_is_dataclasso      ''4'44444444{444{444'4444444444444444444r!   c                   t         j                  } |t              }|sddt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            d x}}y )Nr  r  r   r	  )
r  r
  r   r<   r=   r9   r>   r;   r@   rA   r  s       r   test_epoch_report_is_dataclassz9TestWB5_DataclassesCorrect.test_epoch_report_is_dataclassr  r  r!   c                ~   t        j                  t              D ch c]  }|j                   }}h d}|j                  } ||      }|st        j                  d||z
         dz   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t        j                  |      dz  }t        t        j                  |            d x}}y c c}w )N>   r   r   r   r   r   r   r   Missing fields: L
>assert %(py5)s
{%(py5)s = %(py2)s
{%(py2)s = %(py0)s.issubset
}(%(py3)s)
}requiredfield_namesr	  )r  fieldsr	   nameissubsetr9   r?   r<   r=   r>   r;   r@   rA   rC   fr  r  rb   rH   rJ   s          r   test_epoch_result_fieldsz3TestWB5_DataclassesCorrect.test_epoch_result_fieldsu  s    '2'9'9+'FG!qvvGG
   Z -Z-ZZ1A([BXAY/ZZZZZZZxZZZxZZZ ZZZZZZZZZZZZ-ZZZZZZ H   D:c                ~   t        j                  t              D ch c]  }|j                   }}h d}|j                  } ||      }|st        j                  d||z
         dz   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t        j                  |      dz  }t        t        j                  |            d x}}y c c}w )N>   r   ra   r8   r  r  r  r  r	  )r  r  r   r  r  r9   r?   r<   r=   r>   r;   r@   rA   r  s          r   test_epoch_report_fieldsz3TestWB5_DataclassesCorrect.test_epoch_report_fields  s    '2'9'9+'FG!qvvGG@  Z -Z-ZZ1A([BXAY/ZZZZZZZxZZZxZZZ ZZZZZZZZZZZZ-ZZZZZZ Hr  N)rQ   rR   rS   rT   r  r  r  r  rU   r!   r   r  r  l  s    V55[[r!   r  c                 D	   t               \  } }ddddddddg}t        |      }| j                  |      }d}|j                  }||v }|st	        j
                  d	|fd
||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            dx}x}}d}|j                  }||v }|st	        j
                  d	|fd
||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            dx}x}}d}|j                  }||v }|st	        j
                  d	|fd
||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            dx}x}}d}|j                  }||v }|st	        j
                  d	|fd
||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            dx}x}}|j                  j                         D 
cg c]  }
|
j                  d      s|
 }}
t        |      }d}||k\  }|st	        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  }dd|iz  }t        t	        j                  |            dx}x}}yc c}
w )z3T01: Axioms list rendered as Markdown bullet items.epoch_2026_02_25_001zRead before buildr   idcontent
confidenceepoch_2026_02_25_002zVerify infra firstg333333?r   r,   r.   r/   r0   rN   r5   Nz	- `epoch_   r   r   r   bullet_linesr   zassert %(py8)sr   )r(   r    r7   r8   r9   r:   r;   r<   r=   r>   r@   rA   
splitlinesrj   r   )rD   rE   r   rF   r/   rG   rH   rI   rJ   rK   liner'  r   rz   r   s                  r   (test_t01_axioms_rendered_as_bullet_itemsr*    s   !#FC%2EUXY%2FVZ[F (F\\&!F!<V%<%<<!%<<<<<!%<<<<!<<<<<<V<<<V<<<%<<<<<<<<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99999!<V%<%<<!%<<<<<!%<<<<!<<<<<<V<<<V<<<%<<<<<<<<:6#:#::#:::::#::::::::::6:::6:::#::::::::  00;;=Q\A]L  |!!!!!!!!!!!!3!!!3!!!!!!|!!!|!!!!!!!!!!!!!s   /RRc                    t               \  } }t        d      }| j                  |      }d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd	|iz  }t        t	        j                  |            d
x}x}}y
)z+T02: tier1_updates count appears in report.   )r   17r,   r.   r/   r0   rN   r5   NrO   	rD   rE   rF   r/   rG   rH   rI   rJ   rK   s	            r    test_t02_tier1_updates_in_reportr/    s    !#FC+F\\&!F*6***4*****4****4******6***6***********r!   c                 <   t               \  } }d}t        |      }| j                  |      }|j                  }||v }|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t	        j                  |      dz  }dd	|iz  }t        t	        j                  |            d
x}}y
)z*T03: week_summary prose appears in report.z9Genesis executed 20 sessions. All axioms above threshold.)r   r,   r   summary_textr/   rs   zassert %(py6)sru   N)r(   r    r7   r8   r9   r:   r<   r=   r>   r;   r@   rA   )	rD   rE   r1  rF   r/   rc   rb   ry   rz   s	            r   test_t03_week_summary_in_reportr2    s    !#FCNL|4F\\&!F!222<22222<2222222<222<2222226222622222222222r!   c                    t               \  } }t        g       }| j                  |      }d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }t        t	        j                  |            d	x}x}}y	)
z9T04: Empty axioms list renders graceful placeholder text.r%  zNo axioms distilledr,   r.   r/   r0   rN   r5   NrO   r.  s	            r   )test_t04_empty_axioms_renders_placeholderr4    s    !#FC$F\\&!F ;F$;$;; $;;;;; $;;;; ;;;;;;F;;;F;;;$;;;;;;;;r!   c                    ddl m} m}m}m} | t        u }|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  }dd	|iz  }t        t        j                  |            d
}|t        u }|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  }dd	|iz  }t        t        j                  |            d
}|t        u }|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  }dd	|iz  }t        t        j                  |            d
}t        |t              }|sd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
}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
)z\T05: core.epoch __init__ exports EpochReportGenerator, EpochReport, EpochResult, REPORT_DIR.r   r   )is)z%(py0)s is %(py2)sERGr   r   zassert %(py4)sr]   NERr   ERsltr	   z5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}r   RDr   )r[   r1   r\   r]   observabilityr,   )z%(py1)s in %(py3)sr   zassert %(py5)sr3   )
core.epochr   r   r	   r
   r9   r:   r<   r=   r>   r;   r@   rA   r   r   )r7  r8  r9  r:  rb   r   ry   rc   rG   rI   r   rJ   s               r   test_t05_package_init_exportsr=    s+     &&&&&3&&&&&&&3&&&3&&&&&&&&&&&&&&&&&&222K5K55KKb#::bb## ?b    ?b   ?      b   b       r!   c                 J   t        j                         } t        j                  j	                  | d      }t        j                  }|j
                  } ||      }| }|sddt        j                         v st        j                  t              rt        j                  t              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}}t        |      }t               }|j                  |       t        j                  }|j                   } ||      }|s t        j"                  d      d	z   dt        j                         v st        j                  t              rt        j                  t              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}}y)
z8T06: generate() creates report_dir if it does not exist.epoch_reports_newzfassert not %(py7)s
{%(py7)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.path
}.exists
}(%(py5)s)
}rZ   
new_subdirri   Nr#   z(report_dir was not created by generate()zc
>assert %(py7)s
{%(py7)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.path
}.isdir
}(%(py5)s)
})r%   r&   rZ   r_   joinexistsr<   r=   r9   r>   r;   r@   rA   r   r    r7   isdirr?   )
r'   r@  rb   rc   rd   re   r   rD   rF   rK   s
             r   $test_t06_generate_creates_report_dirrD    su   Ff&9:Jww)w~~)~j)))))))))))r)))r)))w)))~))))))j)))j))))))))))
*
5C^FLL77P7==P=$P$PP&PPPPPPP2PPP2PPP7PPP=PPPPPPPPPPPP$PPPPPPr!   c                    t               \  } }d}t        |      }| j                  |      }|j                  }||k(  }|st	        j
                  d|fd||f      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	|j                   d
      dz   d|iz  }t        t	        j                  |            dx}}y)z=T07: EpochReport.epoch_id matches input EpochResult.epoch_id.2026_12_31_specialrY   rp   )z0%(py2)s
{%(py2)s = %(py0)s.epoch_id
} == %(py4)sr/   r   rs   zExpected report.epoch_id='r   r   rt   ru   N)r(   r    r7   r   r9   r:   r<   r=   r>   r;   r?   r@   rA   )	rD   rE   r   rF   r/   rb   rc   ry   rz   s	            r   &test_t07_report_epoch_id_matches_inputrG    s   !#FC#H8,F\\&!F?? ?h&   ?h                    '    '    %XJhv6GqI    r!   c                    ddl } | j                   G d d             }t               \  }} |ddd      g}t        |	      }|j	                  |      }d}|j
                  }||v }	|	st        j                  d
|	fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}	}d}|j
                  }||v }	|	st        j                  d
|	fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}	}y)zOAxiom dataclass objects (with .id / .content / .confidence attrs) are rendered.r   Nc                  ,    e Zd ZU ded<   ded<   ded<   y)8test_axiom_dataclass_objects_rendered.<locals>.FakeAxiomr   r!  r"  floatr#  N)rQ   rR   rS   __annotations__rU   r!   r   	FakeAxiomrJ    s    r!   rM  	epoch_001zWatch the logsgp=
ף?r   r%  r,   r.   r/   r0   rN   r5   )r  	dataclassr(   r    r7   r8   r9   r:   r;   r<   r=   r>   r@   rA   )dcrM  rD   rE   r   rF   r/   rG   rH   rI   rJ   rK   s               r   %test_axiom_dataclass_objects_renderedrQ    s?   \\  
 "#FC;0@TRSF(F\\&!F1&111;11111;1111;111111&111&111111111116v666666666666666666v666v66666666666r!   c                    t               \  } }g d}t        |      }| j                  |      }|D ]  }|j                  }||v }|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t	        j                  |      dz  }t	        j                  d| d	      d
z   d|iz  }	t        t	        j                  |	            dx}} y)z2Each completed phase name appears in the markdown.)r   r   r   r   r,   r   phaser/   rs   zPhase 'z' not found in report contentrt   ru   Nr   )
rD   rE   r   rF   r/   rS  rc   rb   ry   rz   s
             r   test_phases_listed_in_markdownrT    s   !#FC9F(F\\&!F 
// 	
u// 	
 	
u/ 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
   	
 	
 		  	
 	
 		 0 	
 	
  eW9:	
 	
 	
 	
 	

r!   c                    t         j                  } d} | |      }|st        j                  dt               dz   dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |       t        j                  |      t        j                  |      dz  }t        t        j                  |            dx} x}}y)uE   REPORT_DIR constant must be on /mnt/e/ (E: drive — non-negotiable).z/mnt/e/z%REPORT_DIR must be on E: drive, got: zN
>assert %(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.startswith
}(%(py4)s)
}r
   )r[   r\   r]   ru   N)
r
   rj   r9   r?   r<   r=   r>   r;   r@   rA   )rb   rc   r   rz   s       r   &test_report_dir_constant_is_on_e_driverV    s         + +   0
~>             !    "+    ,      r!   c                    t               \  } }t        d      }| j                  |      }d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd	|iz  }t        t	        j                  |            d
x}x}}y
)zFDate in header is derived from epoch_id when it has YYYY_MM_DD format.
2026_03_15rY   z
2026-03-15r,   r.   r/   r0   rN   r5   NrO   r.  s	            r   $test_epoch_date_parsed_from_epoch_idrY    s    !#FC<0F\\&!F26222<22222<2222<2222226222622222222222r!   __main__z-BB1: header '# Genesis Nightly Epoch' presentzBB1: status line presentzBB2: file exists after generatezBB2: file inside report_dirzBB2: file content matcheszBB3: PR URL in content when setzBB3: PR section header presentzBB4: no PR URL when NonezBB4: no PR section when Nonez BB5: COMPLETED with all 7 phaseszBB5: COMPLETED with 8+ phaseszBB6: PARTIAL with 3 phaseszBB6: PARTIAL with 1 phasezBB7: FAILED with no phaseszWB1: no LLM imports in modulez&WB2: filename convention epoch_{id}.mdz WB2: custom epoch_id in filenamez WB3: shadow pass rate in contentzWB3: shadow pass rate labelzWB4: no shadow rate when no PRz"WB4: no shadow rate when rate NonezWB5: EpochResult is dataclasszWB5: EpochReport is dataclasszWB5: EpochResult fieldszWB5: EpochReport fieldszT01: axioms as bullet itemszT02: tier1_updates in reportzT03: week_summary in reportzT04: empty axioms placeholderzT05: __init__ exports correctz"T06: creates report_dir if missingz"T07: report epoch_id matches inputz&EDGE: dataclass axiom objects renderedzEDGE: phase names in markdownzEDGE: REPORT_DIR on E: drivezEDGE: date parsed from epoch_idz	  [PASS] r   z	  [FAIL] z: 
/z tests passedz(ALL TESTS PASSED -- Story 9.07 (Track B))r   r   r   zOptional[list[str]]r   zOptional[list]r   zOptional[str]r   intr   r   r   zOptional[float]returnr	   )r^  z tuple[EpochReportGenerator, str])ZrT   
__future__r   builtinsr<   _pytest.assertion.rewrite	assertionrewriter9   r  rZ   r   r%   typingr   unittest.mockr   pytestGENESIS_ROOTr_   insertr   r   r   r	   r
   r   r    r(   r*   rW   r}   r   r   r   r   r   r   r   r   r  r*  r/  r2  r4  r=  rD  rG  rQ  rT  rV  rY  rQ   	tracebackrL   rP   rg   rk   r{   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r  testspassedr   totalr  fnprint	Exceptionexc	print_excexitrU   r!   r   <module>rs     s  > #     	 
     'sxxHHOOA|$  !"&! _(,   	
    & ,;7 7(
 
D< <,: :&A A&? ?$> > 
 
BG G4C C4G G*[ [B"*+3<! 
Q	7&

3 z%	8:O:Q:h:hi%	#%:%<%U%UV% 
+,L,N,n,no% 
'(H(J(i(ij	%
 
%&F&H&s&st% 
+,H,J,a,ab% 
*+G+I+h+hi% 
$%@%B%f%fg% 
()D)F)j)jk% 
,-D-F-g-gh% 
)*A*C*j*jk% 
&'<'>'Y'YZ% 
%&;&=&W&WX% 
&';'='X'XY% 
)*B*D*i*ij%  
23M3O3p3pq!%" 
,-G-I-j-jk#%$ 
,-L-N-o-op%%& 
'(G(I(m(mn'%( 
*+P+R+q+qr)%* 
./T/V  0F  0F  	G+%, 
)*D*F*e*ef-%. 
)*D*F*e*ef/%0 
#$>$@$Y$YZ1%2 
#$>$@$Y$YZ3%4 
'(PQ5%6 
()IJ7%8 
'(GH9%: 
)*ST;%< 
)*GH=%> 
./ST?%@ 
./UVA%B 
23XYC%D 
)*HIE%F 
()OPG%H 
+,PQI%EN FJE "b	"DIdV$%aKF	" 
Bvhawm
,-89s b  	"IdV2cU+,I!!	"s   NO	 OO	