+
    Kjr                        R t ^ RIt^ RIt^ RIHt ^ RIHt ]! ]4      P                  t	]P                  P                  ^ ]! ]	4      4       ^ RIHt ^ RIHt ]! 4       '       g.   ]! R]P"                  ! 4        R24       ]P$                  ! ^ 4       ]	R,          t]	R	,          t]	R
,          t. ROt. ROt0 RmtRR ltR t]R8X  d
   ]! 4        R# R# )ax  
Post-scan health + compliance check. Runs right after run_daily_scan.sh's
Claude invocation finishes and answers two questions that nothing previously
verified:

  1. Did the scan actually produce valid output today? (daily_run.log once
     showed a run that "started but never finished" with no alert to anyone --
     found only by manual log review.)
  2. Did it actually follow daily_scan_prompt.md's Step 1a/2 requirements
     (apply active lessons, evaluate enough candidates/sectors), or just
     silently under-comply the way Step 1b's enrichment did for two months?

Both failure modes are the same root problem: trusting a prompt was followed
without checking. This script checks.

Run manually:  python3 verify_scan.py
Cron (right after run_daily_scan.sh finishes, appended to that script itself
rather than a separate cron entry, so it can't run before the scan is done).
N)date)Path)is_trading_day)
send_alert[verify_scan] z% is not a TSX trading day -- skippingdaily_snapshotszscreen_criteria.jsonzretrospective.jsonc                     V P                  4       '       g   V# \        V 4      ;_uu_ 4       p\        P                  ! V4      uuR R R 4       #   + '       g   i     R # ; iN)existsopenjsonload)pathdefaultfs   && -/media/raid/rshare/SwingTrader/verify_scan.py	load_jsonr   7   s4    ;;==	dqyy| 
s   AA	c                  	   \         P                  ! 4       P                  4       p . p\        R V  R2,          pVP	                  4       '       g6   \        RV  R2RR7       \        RV  24       \        P                  ! ^4        \        V4      ;_uu_ 4       p\        P                  ! V4      pRRR4       \         F!  pTX9  g   K  TP                  RT R24       K#  	  XP!                  R4      ;'       g    / p\"         F   pY9  g   K  TP                  RT R24       K"  	  TP!                  R4      ;'       g    . p	T	 Uu. uF1  pTP!                  R4      \$        9  g   K  TP!                  RR4      NK3  	  p
pT
'       dK   \        RT  RRP'                  T
4       RT  R2RRR7       TP                  RRP'                  T
4       24       TP!                  R4      T 8w  d(   TP                  R TP!                  R4       R!T  R24       \)        \*        / 4      ;'       g    / pTP!                  R"/ 4      pTP!                  R#4      pTP!                  R$4      pTP!                  R%4      pTe5   Tf   TP                  R&4       MY8  d   TP                  R'T R(T R)24       TP!                  R*4      pTe]   \-        T\.        4      '       g   TP                  R+4       M4\1        T4      T8  d%   TP                  R,\1        T4       R-T R.T R)24       \)        \2        R4      pT'       dS   TP!                  R/4      '       d<   TP!                  R04      '       g%   TP                  \1        TR/,          4       R124       T'       dU   R2P'                  T4      p\        RT  R3T 2R4R7       \        R5\1        T4       R6T 24       \        P                  ! ^4       \        R7T  R8T R9\1        T;'       g    . 4       R:24       R#   + '       g   i     EL`; i  \        P                   d[   p\        RTP                   R	T R
2RR7       \        RTP                   RT 24       \        P                  ! ^4        Rp?ELRp?ii ; iu upi );swing_trade_z.jsonz'Daily scan produced no output file for zB. It may have crashed or timed out mid-run -- check daily_run.log.zSwingTrader: Scan Did Not Run)titlez)[verify_scan] FAIL -- no output file for NzToday's scan output (z) is not valid JSON: z?. The scan likely got cut off mid-write -- check daily_run.log.z"SwingTrader: Scan Output Malformedz&[verify_scan] FAIL -- invalid JSON in z: zmissing required field ""signalszsignals missing "new_buysorder_statusticker?zToday's scan (z) produced new_buys for z, z but Step 6 order execution did not reach a recognized order_status. This most likely means the Questrade MCP connector was not available to the cron job and these buy candidates were NOT placed -- check daily_snapshots/swing_trade_zB.json and place manually via place_trade_prompt.md if still valid.z/SwingTrader: New-Buy Execution Did Not Completeurgent)r   priorityz*new_buys order_status not recognized for: 	scan_datezscan_date is "z", expected "funnel_breadth!min_candidates_evaluated_per_scanmin_sectors_representedcandidates_evaluated_countz,candidates_evaluated_count was not filled inzonly evaluated z candidates, below the z minimumsectors_evaluatedz#sectors_evaluated was not filled inzonly z sector(s) evaluated (z), below the lessons_for_next_scanlessons_appliedzH active lesson(s) existed but lessons_applied is empty in today's outputz; z) ran but didn't fully comply: z"SwingTrader: Scan Compliance Issuer   z compliance issue(s): z[verify_scan] OK -- z scan valid and compliant (z candidates, z	 sectors))r   today	isoformatSNAPSHOTS_DIRr
   r   printsysexitr   r   r   JSONDecodeErrornameREQUIRED_FIELDSappendgetREQUIRED_SIGNAL_KEYSKNOWN_ORDER_STATUSESjoinr   CRITERIA_FILE
isinstancelistlenRETROSPECTIVE_FILE)r&   failures	snap_pathr   snapefieldr   keyr   bad_status_tickerscriteriafunnelmin_candidatesmin_sectorscountsectorsretrosummarys                      r   mainrH   >   sL   JJL""$EH,ugU ;;I5eW =; <1	

 	9%AB
)__99Q<D  !OO6ugQ?@ ! hhy!''RG#OO/uA67 $ {{:&,,"H&.&.55 (<< 	hh   UG#;yy+,- .+ ,1' 245 D
	
 	8CU9V8WX	
 xx%.+)>(?}UGSTUV+11rH\\*B/FZZ CDNZZ 9:KHH12E!=OOJK#OO!%(??OxX hh*+G'4((OOAB\K'OOG~%;G9MR]Q^^fg ($/E233xx)**OOu4567 8> >
 ))H%UG#B7)L6	
 	s8}o-CG9MN	  (WM#gmm"4!5Y@ A} __ #INN#33H LL L6	

 	6y~~6FbLM4sC   P P-P R
%R
P	P P R-ARR__main__)r   macro_notesr"   r#   r%   r   excluded)r   holdsexits	watchlist>   deniedfilledrejectedskipped_pyramidtimed_out_unfilledskipped_hard_guardrailskipped_soft_guardrailskipped_preview_rejectedaborted_account_ambiguousaborted_mcp_tools_unavailableaborted_reconciliation_mismatchr	   )__doc__r   r*   datetimer   pathlibr   __file__parentBASEr   insertstrr   
ntfy_alertr   r)   r&   r+   r(   r4   r8   r.   r1   r2   r   rH   __name__     r   <module>rf      s   &  
  H~ 3t9  ) !	N4::<.(M
NOHHQK--2200  C  nAb zF re   