
    6j#                     6   d Z ddlZddlZddlZddl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  ed      Z ed      Z ed      ZdZd	Zd
ZdZdZd Zd Zd Zd Zd Zd Zd Zd Zd Zd Z e!dk(  r ejD                   e               yy# e$ r dZY _w xY w)aS  
aria-deadline-reconcile.py  (KAR-684)

Deterministic deadline surfacing. Polling -> Interrupts.

Once a day this reads the source-of-truth (Linear due-dates <=7d + optional
deadlines.yaml) and rewrites the `## This Week` block in HEARTBEAT.md WHOLESALE
between two markers. Idempotent, self-correcting. The script OWNS that block;
the agent only ever reads it.

No LLM involved. Date arithmetic can't hallucinate "already handled" the way an
LLM polling loop can -- for deadlines, boring + deterministic is the feature.

FAIL-SAFE: if the markers are missing/clobbered, the script ABORTS and pings
Telegram rather than overwriting the block with garbage. Backup + atomic write.

Env (loaded by systemd EnvironmentFile chain):
  LINEAR_API_KEY      - read Linear issues
  TELEGRAM_BOT_TOKEN  - failure ping
  TELEGRAM_CHAT_ID    - failure ping target
    N)Path)ZoneInfozEurope/Berlinz/root/aria/brain/HEARTBEAT.mdz/root/aria/state/deadlines.yamlz<!-- DEADLINES:START -->z<!-- DEADLINES:END -->      zhttps://api.linear.app/graphqlc                      t         r1t        j                  j                  t               j                         S t        j                  j	                         S )N)TZdatetimenowdatetoday     aria-deadline-reconcile.pyr   r   1   s8    	  $$R(--//==  r   c                 $    t        d|  d       y )Nz[deadline-reconcile] T)flush)print)msgs    r   logr   7   s    	!#
't4r   c                 &   t         j                  j                  d      xs t         j                  j                  d      }t         j                  j                  d      }|r|st        d       y	 t	        j
                  || d      j                         }t        j                  j                  d| d|d	d
i      }t        j                  j                  |d      j                          y# t        $ r}t        d|        Y d}~yd}~ww xY w)z(Best-effort failure alert. Never raises.TELEGRAM_BOT_TOKENTG_TOKENTELEGRAM_CHAT_IDz,telegram_ping skipped: token/chat_id missingN)chat_idtextzhttps://api.telegram.org/botz/sendMessageContent-Typeapplication/jsondataheaders   timeoutztelegram_ping failed: )osenvirongetr   jsondumpsencodeurllibrequestRequesturlopenread	Exception)r   tokenr   r   reqes         r   telegram_pingr2   ;   s    JJNN/0NBJJNN:4NEjjnn/0G:;	*zzgt<=DDFnn$$*5'>#%78 % 

 	sB/446 *$QC())*s   1A=C/ /	D8DDc           
         t         j                  j                  d      }|st        d       g S d}t	        j
                  |d| j                         id      j                         }t        j                  j                  t        ||dd      }	 t        j                  j                  |d	
      j                         }t	        j                  |      }d|v rt%        d|d          g }|j                  di       j                  di       j                  dg       D ]t  }	|	j                  d      s	 t&        j(                  j+                  |	d   dd       }
|j/                  |
|	d   j1                         |	d   |	j                  dd      d       v |S # t        j                  j                   t"        f$ r}t%        d|       d}~ww xY w# t,        $ r Y w xY w)z>Linear issues with dueDate <= horizon, not completed/canceled.LINEAR_API_KEYz0LINEAR_API_KEY missing -> skipping Linear sourcea  
    query($due: TimelessDateOrDuration!) {
      issues(first: 50, filter: {
        dueDate: { lte: $due },
        state: { type: { nin: ["completed", "canceled"] } }
      }) {
        nodes { identifier title dueDate url state { name } }
      }
    }
    due)query	variablesr   )Authorizationr   r      r!   zLinear request failed: NerrorszLinear GraphQL errors: r   issuesnodesdueDate
   title
identifierurl r   r?   refrA   )r#   r$   r%   r   r&   r'   	isoformatr(   r)   r*   r+   
LINEAR_URLr,   r-   loadserrorURLErrorTimeoutErrorRuntimeErrorr	   r   fromisoformat
ValueErrorappendstrip)horizon_datekeyr6   bodyr0   resppayloadr1   outnds              r   fetch_linearrX   N   s   
**..)
*C>?		E ::UL<R<R<T4UVW^^`D
..
 
 "%7IJ ! C:~~%%c2%6;;=**T" 74WX5F4GHII
C[[$((26::7BG uuY	++AiL",=>A 	

wZ%%'\?55#	
 	 J% LL!!<0 :4QC899:  		s+   AF 4%G#G:GG	GGc            	         t         j                         sg S t         j                  d      } g }	 ddl}|j	                  |       xs g }|D ]  }t        |t              s|j                         D ]^  \  }}	 t        j                  j                  t        |      dd       }|j                  |t        |      j                         ddd       `  |S # t        $ r Y pw xY w# t        $ r Y nw xY w| j!                         D ]  }|j                         j#                  d	      j                         }	|	r|	j%                  d
      sd|	vrH|	j'                  d      \  }
}}	 t        j                  j                  |
j                         dd       }n# t        $ r Y w xY w|j                  ||j                         ddd        |S )zOptional non-Linear deadlines. Tries PyYAML, falls back to a minimal
    line parser. Accepted line form (no PyYAML needed):
        - 2026-06-10: Steuererklaerung abgeben
    utf-8encodingr   Nr>   manuellrB   rC   -#:)DEADLINES_YAMLexists	read_textyaml	safe_load
isinstancedictitemsr	   r   rL   strrM   rN   rO   ImportError
splitlineslstrip
startswith	partition)r   rh   rd   r   entrykvrW   rawlinedatepart_r?   s                r   parse_deadlines_yamlrv   {   s   
   "	##W#5DE~~d#)r 	dE%&!KKM dDAq!$MM77AsD LL!c!fllnY_a!bcd	d  & ! !     	Wyy{!!#&,,.ts+s$!^^C0!U	++HNN,<Sb,ABA 		a%++-	RTUV	W LsM   .C# C# 6+C!2C# 	C C# C  C# #	C/.C/ 0F	FFc                 N    | dk  rdt        |        dS | dk(  ry| dk(  ryd|  dS )	Nr   zUEBERFAELLIG (zd)HEUTE   MORGENzin rW   )abs)
delta_dayss    r   humanizer}      s@    A~J033QQAr   c           
         t         dd|j                          dt         ddg}| s|j                  d       n| j	                  d        | D ]g  }|d	   |z
  j
                  }t        |      }|d
   r|d
   dk7  r	d|d
    dnd}|j                  d|d	   j                          d| d|d    |        i |j                  d       |j                  t               dj                  |      S )NuI   ## This Week — Deadlines (auto, Cron-owned via aria-deadline-reconcile)z	> Stand: u    · Horizont u2    Tage · Quelle: Linear due-dates + deadlines.yamlrB   u-   - _Keine Deadlines in den nächsten 7 Tagen._c                     | d   S )Nr   r   )xs    r   <lambda>zrender_block.<locals>.<lambda>   s
    6 r   )rQ   r   rD   r]   z ()z- **u   ** · u    — r?   
)	MARKER_STARTrE   HORIZON_DAYSrN   sortdaysr}   
MARKER_ENDjoin)rh   	ref_todaylinesitdeltatagrD   s          r   render_blockr      s   X,,./}\NJ|}E DE

*
+ 	\BZ)+11E5/C')%yRY)5KBr%yk#QSCLL46
 4 4 67vcU%7}UXTYZ[		\
 
LL	LL99Ur   c                    t               j                  d      dz   t        j                  j                         j                  d      z   }| j	                  | j
                  d| z         }t        j                  | |       t        | j                  j                  | j                  dz               }|d t          D ]  }	 |j                           |S # t        $ r Y #w xY w)Nz%Y%m%dr^   z%H%M%Sz.bak.z.bak.*)r   strftimer	   r
   with_suffixsuffixshutilcopy2sortedparentglobnameBACKUP_KEEPunlinkOSError)pathtsbakbaksolds        r   backupr      s    			(	#c	)H,=,=,A,A,C,L,LX,V	VB


4;;55
6C
LLs$++""499x#789DMk\" 	JJL
 J  		s   C	C! C!c           	         | j                  d      }|j                  t              }|j                  t              }|dk(  s
|dk(  s||k  rt	        d|  d| d| d      |d | |z   ||t        t              z   d  z   }||k(  rt        d       y	t        |        t        j                  t        | j                        d
d      \  }}	 t        j                  |dd      5 }|j                  |       d d d        t        j                  |d       t        j                          dk(  rJt        j"                  | j                        }	t        j$                  ||	j&                  |	j(                         t        j*                  ||        t        j,                  j/                  |      rt        j0                  |       y# 1 sw Y   xY w# t        j,                  j/                  |      rt        j0                  |       w w xY w)NrZ   r[   z'FAIL-SAFE: markers missing/inverted in z (start=z, end=z). Refusing to write.u)   no change (idempotent) — skipping writeFz.heartbeat-z.tmp)dirprefixr   wi  r   T)rc   findr   r   rK   lenr   r   tempfilemkstempri   r   r#   fdopenwritechmodgeteuidstatchownst_uidst_gidreplacer   rb   r   )
r   	new_blockcontentsieinew_contentfdtmpfparent_stats
             r   atomic_replace_blockr      s   nngn.G	l	#B	j	!B	Rx28rBw5dV <Tt#8:
 	
 #2,*WR#j/5I5J-KKKg78
4L3t{{#3MRXYGBYYr31 	!QGGK 	!
 	e::<1''$++.KHHS+,,k.@.@A


377>>#IIcN	! 	! 77>>#IIcN s%   
G
 "F>4BG
 >GG
 
7Hc                     t         j                         s&t        dt                 t        dt          d       yt	               } | t        j                  t              z   }g }	 |t        |      z  }|t               z  }|D cg c]  }|d   |k  s| }}t        ||       }	 t        t         |      }t        d
t        |       d|        y# t        $ r,}t        t        |             t        d|        Y d }~yd }~ww xY wc c}w # t        $ r)}t        t        |             t        d       Y d }~y	d }~ww xY w)NzHEARTBEAT not found: u   ⚠️ Deadline-Reconcile: u    fehlt — kein Write.ry   )r   uM   ⚠️ Deadline-Reconcile: Linear-Fetch fehlgeschlagen, behalte alten Block.
r   u}   🛑 Deadline-Reconcile FAIL-SAFE: Marker in HEARTBEAT.md weg/kaputt. Block NICHT überschrieben. Bitte HEARTBEAT.md prüfen.   zdone: z deadline(s), changed=r   )	HEARTBEATrb   r   r2   r   r	   	timedeltar   rX   rK   ri   rv   r   r   r   )r   horizonrh   r1   r   blockchangeds          r   mainr      s9   #I;/03I;>TUVI(,,,??GEg&& 
!##E9B2f:#8R9E9	*E&y%8 &U27)<=+  CFfghfijk :
  CFF	
 s<   %C DD%D 	D"D  D	D?D::D?__main__)#__doc__r#   sysr&   r   r   r	   urllib.requestr)   urllib.errorpathlibr   zoneinfor   r   r.   r   ra   r   r   r   r   rF   r   r   r2   rX   rv   r}   r   r   r   r   __name__exitr   r   r   <module>r      s   * 
 
       !	/	"B 01	78)%
-
!5*&*Z"J&@!H zCHHTV W  	Bs   B BB