
    j,3                       d Z ddlmZ ddlZddlZddlZddlmZmZ ddlm	Z	 ddl
mZ  ej                  d       ej                  d	       ej                  d
       ej                  d       ej                  d      gZ ej                  d       ej                  d       ej                  d      gZdZdZdZdZddZdddZ	 d	 	 	 	 	 ddZddZ	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 d dZy)!uO  v3.6 Runtime Harness — finish-task preflight helper (P1-B §12).

chair_authorization_id=CHAIR-AUTH-TASK-2706-V36-FINISH-TASK-PROFILE-LAYER-P1B-260529

Implements finish-task entry preflight: task_mode classification,
lock_sha extraction, dirty audit, and full profile assembly.

Called before finish-task.sh executes. If this layer is inactive or errors,
existing finish-task.sh behavior is preserved without modification.

Public API
----------
- ``run_preflight(task_md_path, task_id, lock_sha_hint)``
  Returns a full profile dict (§11 schema) or a minimal safe-fail dict.

- ``classify_dirty_workspace(git_status_output)``
  Returns (dirty_count, dirty_classification).

- ``extract_lock_sha(task_md_text, task_timers_path)``
  Returns (lock_sha, fallback_reason).

Safe-fail: never raises. All exceptions return UNKNOWN/ESCALATE profile.
    )annotationsN)datetimetimezone)Optional   )SCHEMA_VERSIONz\.log$z\.pid$z\.lock$z__pycache__/z\.pyc$zmemory/(events|reports)/task-\dzscripts/harness/ztests/harness/i  2      c                   	 | r| j                         sy| j                         j                         D cg c]  }|j                         s| }}t        |      }|dk(  ryg }|D ]v  }|j                         j                  dd      }t        |      dk(  s2|d   j                  d      d   j                         j                  d      }|j	                  |       x t        d	 |D              }t        d
 |D              }	|t        k\  r|dfS |	t        t        |dz        k\  r	|dk  r|dfS |t        t        |dz        k\  r|dfS |t        k\  r	|	dkD  r|dfS |dk  r|dfS |dfS c c}w # t        $ r Y yw xY w)u&  Classify the dirty workspace state from git status --porcelain output.

    Args:
        git_status_output: Output of 'git status --porcelain'.

    Returns:
        (dirty_count, dirty_classification)

    Classification logic:
        - 0 lines → CLEAN
        - Mostly daemon files (logs/pid/pyc) → DAEMON_RECURRENCE
        - Mostly memory/events/ task lineage → INHERITED_DIRTY
        - Large number (500+) of diverse files → EXTERNAL_DIRTY
        - Task's own expected changes → OWN_DIRTY
        - Otherwise → UNKNOWN_DIRTY
    )r   CLEANr   Nr      z -> "c              3  R   K   | ]  t        fd t        D              rd   yw)c              3  @   K   | ]  }|j                          y wNsearch.0patps     H/home/jay/workspace/scripts/harness/v36/finish_task_profile_preflight.py	<genexpr>z5classify_dirty_workspace.<locals>.<genexpr>.<genexpr>a   s     =S3::a==   r   N)any_DAEMON_PATTERNSr   r   s    @r   r   z+classify_dirty_workspace.<locals>.<genexpr>_   s(      
=,<== 
   $'c              3  R   K   | ]  t        fd t        D              rd   yw)c              3  @   K   | ]  }|j                          y wr   r   r   s     r   r   z5classify_dirty_workspace.<locals>.<genexpr>.<genexpr>e   s     @S3::a=@r   r   N)r   _INHERITED_PATTERNSr   s    @r   r   z+classify_dirty_workspace.<locals>.<genexpr>c   s(      
@,?@@ 
r   EXTERNAL_DIRTYg?   INHERITED_DIRTYg      ?DAEMON_RECURRENCE   	OWN_DIRTYUNKNOWN_DIRTY)r   r)   )strip
splitlineslensplitappendsum_EXTERNAL_THRESHOLD_LARGEmax_INHERITED_THRESHOLD_DAEMON_THRESHOLD_EXTERNAL_THRESHOLD_MEDIUM	Exception)
git_status_outputllinescountpathslinepartspathdaemon_countinherited_counts
             r   classify_dirty_workspacer@   ;   s   "3" (9(?(?(A-335@@BPqaggiPPE
A:  	#DJJL&&tQ/E5zQQx~~f-b1779??DT"	#  

 
  

 
 --*** c"6DDQT+++ 30%#+>>--- ..?Q3F+++ B;+%%o%%Y Q\  "!"sX   E-  E- E(E(E- "5E- A7E- E- /E- 	E- E- $E- (E- -	E98E9c                    	 g d}| xs  t         j                  j                  dd      }t        j                  ||ddd      }|j
                  dk(  r|j                  S y# t        $ r Y yw xY w)	z-Run git status --porcelain and return output.)gitstatusz--porcelain	WORKSPACEz/home/jay/workspaceT   )cwdcapture_outputtexttimeoutr   N)osenvironget
subprocessrun
returncodestdoutr5   )workspace_pathcmdrF   results       r   _run_git_statusrT      sr    .R

{<Q R
 !==  s   AA   	A,+A,c                   	 t        j                  dt         j                        t        j                  dt         j                        t        j                  dt         j                        t        j                  dt         j                        g}|D ].  }|j                  | xs d      }|s|j	                  d      dfc S  |rt
        j                  j                  |      r	 dd	l}t        |d
d      5 }|j                  |      }d	d	d	       t        t              rl|j                         D ]Y  \  }}	t        |	t              s|	j                  d      xs |	j                  d      }
|
s>t        j                  d|
      sU|
dfc S  yy# 1 sw Y   xY w# t         $ r Y yw xY w# t         $ r}d	d| fcY d	}~S d	}~ww xY w)a1  Extract lock_sha from task md text or task-timers.json.

    Args:
        task_md_text: Full text content of the task md.
        task_timers_path: Optional path to task-timers.json.

    Returns:
        (lock_sha, fallback_reason)

    Safe-fail: never raises; returns (None, reason) on any error.
    zlock_sha[:\s]+([0-9a-f]{40})z&task_md_sha_before[:\s]+([0-9a-f]{40})z snapshot_sha[:\s]+([0-9a-f]{40})z dispatched.*sha.*?([0-9a-f]{40}) r   extracted_from_task_mdr   Nrutf-8)encodinglock_shatask_md_sha_beforez^[0-9a-f]{40}$extracted_from_task_timers)N$no_lock_sha_found_main_HEAD_fallbackzlock_sha_extraction_exception: )recompile
IGNORECASEr   grouprJ   r=   existsjsonopenload
isinstancedictitemsrL   matchr5   )task_md_texttask_timers_pathsha_patternsr   mrd   ftimers_
timer_datashaexcs               r   extract_lock_sharu      s    = JJ6FJJ@"--PJJ:BMMJJJ:BMMJ	
   	<C

<-2.Awwqz#;;;	< /? @*C'B *a!YYq\F*fd+)/ I:%j$7",.."<"d
Oc@dC"rxx0A3'G'*,H'H H	I <* *   <	  =6se<<<=sx   B*F* -F* "F* %F 7F	>F &F /F F F FF 	F'$F* &F''F* *	G3F?9G?Gc                    	 | rt         j                  j                  |       sdd|  fS t        | ddd      5 }|j	                         dfcddd       S # 1 sw Y   yxY w# t
        $ r}dd| fcY d}~S d}~ww xY w)z3Read task md file. Returns (content, error_reason).Nztask_md_not_found: rX   rY   replace)rZ   errorsztask_md_read_error: )rJ   r=   rc   re   readr5   )task_md_pathro   rt   s      r   _read_task_mdr{      s    2277>>,#?.|n===,giH 	"A668T>	" 	" 	" 2+C51112s?   'A! A! A	A! AA! A! !	A;*A60A;6A;c                    ddl m} |D ci c]	  }|dd|d }}t        | xs dd|ddd|dd	| d
|ddd|ddS c c}w )z5Build a minimal ESCALATE profile for safe-fail cases.r   )	GATE_KEYSzN/Apreflight_safe_fail)rS   evidence	rationaleUNKNOWNsignalpriority_applied
confidenceESCALATEzpreflight_safe_fail: Nr)   TESCALATE_CHAIR=CHAIR-AUTH-TASK-2706-V36-FINISH-TASK-PROFILE-LAYER-P1B-260529schema_versiontask_id	task_mode!task_mode_classification_evidencegatesoverall_resultoverall_rationalelock_sha_usedlock_sha_fallback_reasondirty_workspace_classificationchair_decision_requiredanu_action_recommendedtschair_authorization_id_linkage)finish_task_profile_schemar}   r   )r   reasonr   r}   gkr   s         r   _build_safe_escalate_profiler      s     6  	u*?fUUE 
 )'i )#.

 $4VH=$**9#'"2*i% 	s   ;c                   t        j                  t        j                        j	                         }	 	 ddlm} ddlm}m	}	 d}| rt        |       \  }}|rd}n|xs d}|s9|r7t        j                  d|      }|r|j                  d      j!                         }|xs d} |||       }|j#                  d	d      }|r|}d
}nt%        ||      \  }}t'        |      }t)        |xs d      \  }}||}n|||d} |||      } |	|      \  }}|dk(  rd}d}n|dk(  rd}d}n|dk(  rd}d}nd}d}t*        |||j#                  dd      |j#                  dd      |j#                  dd      d|||||||||dd}|S # t        $ r}
t        |xs dd|
 |      cY d}
~
S d}
~
ww xY w# t,        $ r}t        |xs dd| |      cY d}~S d}~ww xY w)ud  Run the finish-task preflight for a given task.

    Args:
        task_md_path: Path to the task md file.
        task_id: Task ID string (e.g. "task-2706").
        lock_sha_hint: Optional pre-known lock_sha.
        workspace_path: Workspace root for git status.
        task_timers_path: Optional path to task-timers.json.
        gate_inputs_override: Optional dict to override gate inputs
                              (used in tests/replay scenarios).

    Returns:
        Full §11 profile dict. Never raises — always returns something.

    Safe-fail: any exception → UNKNOWN/ESCALATE profile.
    r   )classify_task_mode)evaluate_gatescompute_overallr   zimport_error: NrV   z\*\*task_id\*\*:\s*(task-\S+)r   provided_as_hint)r[   dirty_countdirty_classificationPASSPROCEEDFWARNCAVEAT_PROCEEDFAILBLOCKTr   r   r   r   r   r   r   zpreflight_exception: )r   nowr   utc	isoformatfinish_task_profile_classifierr   finish_task_profile_judger   r   ImportErrorr   r{   r_   r   rb   r*   rL   ru   rT   r@   r   r5   )rz   r   lock_sha_hintrQ   rl   gate_inputs_overrider   r   r   r   imp_errrk   contentread_errrn   classificationr   r[   lock_sha_fallbackgit_status_outr   r   gate_inputsgates_resultr   r   
anu_actionchair_requiredprofilert   s                                 r   run_preflightr      sX   0 
hll	#	-	-	/Bk
	JR  -l ;GX!&}" <		:LIA''!***,&Y ,L,G"&&{I>	 $H 2*:.+'H'
 )8,D b-
))
  +.K %*(<K &i= -<L,I)) V#"J"Nv%)J"Nv% J!N)J!N -"(,,Xr:$2$6$67I9$U,00yI2
 ",!2%(9.B'5&0.m%
*   	/$9 	* 	B  
+ y#C5)
 	

sH   E: D7F$ :	F!FF!F$ F!!F$ $	G-G GG)r6   strreturnztuple[int, str]r   )rQ   Optional[str]r   r   )rk   r   rl   r   r   ztuple[Optional[str], str])rz   r   r   z#tuple[Optional[str], Optional[str]])r   r   r   r   r   r   r   rh   )NNNNN)rz   r   r   r   r   r   rQ   r   rl   r   r   zOptional[dict]r   rh   )__doc__
__future__r   rJ   r_   rM   r   r   typingr   r   r   r`   r   r"   r0   r4   r2   r3   r@   rT   ru   r{   r   r        r   <module>r      s  . # 	 	  '  BJJyBJJyBJJzBJJBJJy  BJJ12BJJ"#BJJ !       D"N* '+/=/=#/= /=d2 	 
	H "#'$(&*+/E
E
E
 !E
 "	E

 $E
 )E
 
E
r   