
    tj                    r   d Z ddlmZ ddlZddlmZmZmZmZ ddlm	Z	 ddl
mZ ddlmZmZ edz  d	z  Zedz  d
z  Zedz  dz  Zedz  Zedz  ZdZdddZdddZdddZddZdd dZd!dZd"dZedk(  rddlmZ 	  e e             y# e$ r  e $ r!Z! ede!      Z" e#de"         ed      dZ![!ww xY w)#a  The judgement pass. Reads what the rail found and thinks about it.

THE ONE RULE: this can only ADD.

It opens findings and the register read-only and writes exactly one file,
`data/judgement/<date>.json`. There is no code path here that edits a finding,
removes a register row, or changes a bucket. The brief renders what this produces
underneath what the rules found, never instead of it.

That is deliberate and it is the whole reason the rail has no model calls in it. A
component that can score a message can also silently drop one, and the value of the
register is that a date captured in January still surfaces in March whether or not
anything was feeling clever that day. So judgement lives out here, where the worst
it can do is be wrong in an extra paragraph.

If it does not run, or returns nothing, or returns garbage, the brief is unchanged.
Failure of this file is invisible to the thing that matters.
    )annotationsN)datedatetime	timedeltatimezone)Path)Any)ROOTenv_pathdata	judgementfindingszdeadlines.jsonlz
CONTEXT.mdzCOO.md$   c                   	 | xs0 t        j                         j                         j                         } t        | j                          dz  }|j                         syt        j                  |j                  d            }t        j                  |d         }t        j                  t        j                        |z
  t        t              kD  ry|j                  dg       D cg c]5  }t!        |      j#                         st!        |      j#                         7 }}|rd|dd iS dS c c}w # t$        $ r Y yw xY w)	uE   Today's judgement, or None. Never raises — the brief must not care..jsonNutf-8encodingwritten_at_utc)hourslines   )r   now
astimezoner   JUDGEMENT_DIR	isoformatexistsjsonloads	read_textfromisoformatr   utcr   MAX_AGE_HOURSgetstrstrip	Exception)daypathvaluewrittenliner   s         /root/personal-cos/judgement.pyjudgement_forr.   (   s   7X\\^..0557#--/!2%88{{}

4>>7>;<((/?)@A<<%/)-2PP/4yy"/E[tTIZT"[[',r#6$6 \ s=   AD6 !A4D6 D6 *D1	D1$
D6 /D6 1D6 6	EEc                   t        j                         j                         j                         t	        |       z
  j                         }g }t        j                         s|S t        t        j                  d            D ]m  }|j                  |k  r|j                  d      j                         D ]8  }|j                         s	 |j                  t        j                   |             : o |S # t"        $ r Y Jw xY w)N)daysz*.jsonlr   r   )r   r   r   r   r   r   FINDINGS_DIRr   sortedglobstemr    
splitlinesr&   appendr   r   
ValueError)r0   cutoffrowsr)   r,   s        r-   _recent_findingsr:   :   s    lln'')..09$3GGRRTF!#D |((34 99vNNGN4??A 	Dzz|KK

4 01	 K " s   
$C44	D ?D c                   t         j                         sg S t        j                         j	                         j                         }i }t         j                  d      j                         D ]j  }|j                         st        j                  |      }	 t        j                  |d         }d||z
  j                  cxk  r| k  s[n ^|||d   |d   f<   l t        |j!                         d       S # t        t        f$ r Y w xY w)Nr   r   r   r   source_subjectc                    | d   S )Nr    )items    r-   <lambda>z_horizon.<locals>.<lambda>Z   s
    $v,     )key)REGISTER_PATHr   r   r   r   r   r    r5   r&   r   r   r!   KeyErrorr7   r0   r2   values)r0   todayseenr,   recorddues         r-   _horizonrJ   K   s    !	LLN%%',,.E24D'''9DDF 	Fzz|D!	$$VF^4C u""*d*?ED&.&)9":;<	F $++-%>??	 *% 		s   C66DDc                    t               } | D cg c]  }|j                  d      dk(  s| }}t               }t        j	                         rt        j                  d      nd}t        j	                         rt        j                  d      nd}dj                  d |dd	 D              xs d
}dj                  d |D              xs d}| d| d| d| dS c c}w )zEEverything the judgement pass gets to see. Read-only, assembled here.bucketACTr   r    
c              3     K   | ]T  }d |j                  d       d|j                  dd      dd  ddj                  |j                  d	g       dd
        d V yw)z- [accountz] subjectrN   NZ   z  (z, reasons   ))r$   join).0fs     r-   	<genexpr>zbuild_prompt.<locals>.<genexpr>e   sk        aeeIr!%%	2"6s";!<
diii,Ra012!	5s   AAN   z	- nothingc              3  >   K   | ]  }d |d    d|d   dd    yw)z- r   z  r<   NP   r>   )rX   hs     r-   rZ   zbuild_prompt.<locals>.<genexpr>j   s4      9:"QvYKr!,-cr234s   z#- nothing dated in the next 90 daysz

---

z&

---

## What the rules found today

z&

## Dated obligations, next 90 days

u  

---

Give at most three lines. Each must tell him something he does not already
know from the list above — a connection, a consequence, or something in
CONTEXT.md that is going quiet and has no email to remind him. If the honest
answer is that there is nothing worth saying, return an empty list. A quiet
day reported quietly is a correct answer and padding is not.

Reply as JSON only: {"lines": ["...", "..."]}
)r:   r$   rJ   PERSONA_PATHr   r    CONTEXT_PATHrW   )r   rY   acthorizonpersonacontextact_texthorizon_texts           r-   build_promptrg   ]   s   !H
;!%%/U":1
;C
;jG:F:M:M:Ol$$g$6UWG:F:M:M:Ol$$g$6UWGyy  Sb   
	 
 99 >E  /	. 
 ) ) , * 1. :	: <s
   CCc                   |xs0 t        j                         j                         j                         }t        j                  dd       t        |j                          dz  }t        j                  t        j                        j                         | d d d}|j                  d      }|j                  t        j                  |d      d	z   d
       |j                  |       |S )NT)parentsexist_okr   r   )r   r   z	.json.tmprU   )indentrO   r   r   )r   r   r   r   r   mkdirr   r   r"   with_suffix
write_textr   dumpsreplace)r   r(   r)   payload	temporarys        r-   _writers      s    

3**,113Ct4cmmo.e44D",,x||4>>@rG   -IGA6=PdKrA   c                 j   t               } | j                         sy| j                  d      j                         D ]u  }|j	                         }|r|j                  d      sd|vr+|j                  dd      \  }}|j	                         dk(  sT|j	                         se|j	                         c S  y)a+  The key from the CONFIGURED secrets file only. Never the ambient environment.

    read_env() merges os.environ, which is right for the rest of the rail. It is wrong
    here: on 2026-07-29 this picked up the ANTHROPIC_API_KEY belonging to the Claude Code
    session that was building it, and tried to bill a model against a credential Rocky
    had never provided. It 401'd and the failure was harmless, but the next one might
    have worked, which is worse.

    A credential he has not put in the secrets file is a credential this does not have.
    Nr   r   #=   ANTHROPIC_API_KEY)r   r   r    r5   r&   
startswithsplit)r)   raw_liner,   rB   r*   s        r-   _configured_api_keyr|      s     :D;;=NNGN4??A !~~ts+s$ZZQ'
U99;--%++-;;= ! rA   c            	        t               } | st        d       ydd l}t               }|j	                  d| dddddd	|d
gdd      }|j                          dj                  d |j                         j                  dg       D              j                         }|j                  d      r(|j                  dd      d   j                  dd      d   }t        j                  |      j                  dg       }t        |D cg c]  }t        |       c}      }t        d|j                    dt#        |       d       yc c}w )NuK   no ANTHROPIC_API_KEY configured — judgement pass is dark, rail unaffectedr   z%https://api.anthropic.com/v1/messagesz
2023-06-01zapplication/json)z	x-api-keyzanthropic-versionzcontent-typezclaude-sonnet-5i  user)rolecontent)model
max_tokensmessages<   )headersr   timeoutrN   c              3  @   K   | ]  }|j                  d d        yw)textrN   N)r$   )rX   blocks     r-   rZ   zmain.<locals>.<genexpr>   s       "'		&"s   r   z```rO   rw   r   zjudgement written: z (z lines))r|   printrequestsrg   postraise_for_statusrW   r   r$   r&   ry   rz   rsplitr   rs   r%   namelen)api_keyr   promptresponser   r   r,   r)   s           r-   mainr      s;   !#G[\^F}}/ !-.
 '"(V<=

   H 77 +3==?+>+>y"+M eg 	 uzz$"1%,,UA6q9JJt  "-E/3t9/0D			{"SZL
@A 0s   E__main__)record_failurezJudgement pass failedz)Judgement pass failed (rail unaffected): )N)r(   date | Nonereturnzdict[str, Any] | None)rw   )r0   intr   zlist[dict[str, Any]])rS   )r   r%   )r   z	list[str]r(   r   r   r   )r   z
str | None)r   r   )$__doc__
__future__r   r   r   r   r   r   pathlibr   typingr	   notifyr
   r   r   r1   rC   r`   r_   r#   r.   r:   rJ   rg   rs   r|   r   __name__error_reportingr   
SystemExitr'   errorsummaryr   r>   rA   r-   <module>r      s   & #  8 8   !v+f}z)v 11l"h $"@$!H0!H z.  	 
    !8%@9'CDm	s   =B 
B6B11B6