+
    <j/                        R t ^ RIt^ RIt^ RIt^ RIHtHt ^ RIHt ^ RI	H
t
 ^ RIt^ RIt^ RIHtHt ]P                   ! ]4      t]! ]4      P)                  4       P*                  P*                  t]R,          t]R,          t ! R R	]4      t ! R
 R]4      t ! R R]4      tR R ltR R ltR R lt]! 4       s ]R,          t!\@        PE                  RR4      t#\@        PE                  RR4      t$R t%]&! 0 Rkm4      t'. RlOt(]&! 0 Rmm4      t)]&! 0 Rnm4      t*RRRRRRR RR!RRRR"R/t+Rot,R#t-]P\                  ! R$]P^                  4      t0Rpt1]! R%R&7       ! R' R(4      4       t2 ! R) R*]34      t4 ! R+ R,]4      t5R- R. lt6R/ R0 lt7R1 R2 lt8R3 R4 lt9R5 R6 lt:R7 R8 lt;]! R%R&7       ! R9 R:4      4       t<R; R< lt=R= R> lt>Rqt?R? R@ lt@RA RB ltARC RD ltBRE tC]BtD]CtERrRF RG lltFRrRH RI lltGRrRJ RK lltHRrRL RM lltIRrRN RO lltJRrRP RQ lltK. RRNRSNRTNRUNRVNRWNRXNRYNRZNR[NRNRNR	NR\NR]NR^NR_NR`NR:NRaNRbNRcNRdNR(NR*NR,NReNRfNRgNRhNRiNRjNtLR# )su(  
paths.py — Single source of truth for engine and project paths.

v3 layout (post layout-v3 + ref-taxonomy migration):

    projects/{slug}/
    ├── assets/{char,loc,prop}/{subject}/{look}/
    │   ├── {subject}_{kind}.{ext}          ← hero ref
    │   └── pool/{kind}/                    ← pool refs
    │       └── 4k/                         ← upscaled pool refs
    ├── prep/ep_{NNN}/
    ├── renders/ep_{NNN}/
    ├── scripting/{bible,episodes,development,compiled}/
    ├── _pipeline/{state,shot_plans,annotations,sessions,audio,tests,archive,visual}/
    ├── _history/{snapshots,archives,migration,debug}/
    └── project_config.json

v2 layout (deprecated, migrated by scripts/migrate_v3_layout.py):

    projects/{slug}/
    ├── assets/{identity,turn,expr,loc,prop,scene}/{subject}/
    ├── sequences/ep_{NNN}/
    ├── renders/ep_{NNN}/
    ├── state/{visual,manifests,bundles}/
    ├── bible/
    ├── episodes/
    ├── development/
    ├── _history/{snapshots,archives,migration,debug}/
    └── project_config.json

The legacy v1 layout (output/{refs,frames,previs,video,bundles,manifests}/)
is DESTROYED by scripts/migrate_v2_layout.py. No v1 fallback paths exist
in the codebase.
N)	dataclassfieldPath)Optional)ref_filenamesubject_stemconfigzpipeline_config.jsonc                       ] tR t^?tRtRtR# )ProjectsRootUnresolvablezRaised when neither RECOIL_PROJECTS_ROOT nor pipeline_config.json
provides a usable projects root directory. Law 4: fail loudly. N__name__
__module____qualname____firstlineno____doc____static_attributes__r       ڇ/Users/joeturnerlin/Code/recoil-sessions/Joes-MacBook-Pro/adhoc--claude--atlas-redesign--20260625T050330Z-1fd50d09/recoil/core/paths.pyr   r   ?   s    Fr   r   c                       ] tR t^DtRtRtR# )LayoutFrozenErrorzRaised when v2-only path properties are accessed on a project with
layout_freeze == 'ad-hoc'. The project predates v2 layout and must be
finished against its pre-migration git commit.r   Nr   r   r   r   r   r   D   s    6r   r   c                       ] tR t^JtRtRtR# )DeprecatedPathAPIErrorzRaised by the deprecation shims for project_refs_dir() and
project_output_dir(). Migrate callers to ProjectPaths.

See: consultations/recoil/project-paths-refactor-v2-2026-05-26/SYNTHESIS.md
r   Nr   r   r   r   r   r   J   s    r   r   c                $    V ^8  d   QhR\         /#    returndict)formats   "r   __annotate__r!   R   s     = =t =r   c                  `    \         P                  4       '       g   / # ^RIHp  V ! \         R4      # )   validate_and_loadpipeline_config)CONFIG_PATHexistsconfig_schemar%   r$   s    r   _load_pipeline_configr*   R   s'    	0[*;<<r   c                0    V ^8  d   QhR\         R\         /# )r   rootr   r   )r    s   "r   r!   r!   Y   s     $ $D $T $r   c                    V R ,          pVP                  4       '       d   V #  V P                  4       ;'       dQ    \        ;QJ d*    R V P                  4        4       F  '       g   K   RM	  RM! R V P                  4        4       4      pV'       d@   Rp VP                  R4       Rp\        P                  RT V'       d	   R4       V # R4       V # \        V  R24      h  \         d    Rp Ldi ; i  \         d     LWi ; i)	z.recoil-data-rootc              3      "   T FW  pVP                  4       '       g   K  VP                  P                  R 4      '       d   K>  VR,          P                  4       x  KY  	  R# 5i).project_config.jsonN)is_dirname
startswithr(   ).0ps   & r   	<genexpr>$_assert_data_root.<locals>.<genexpr>c   sK      )
#xxz 1"#&&"3"3C"8 1Q&&..00#s   A!A!A!TFzrecoil-data-root
u   _assert_data_root: .recoil-data-root sentinel was missing but %s holds real project data (a project_config.json is present) — %s (likely Dropbox Smart-Sync eviction of the 0-byte marker). (REC-101)zrecreated the sentinelz5could NOT recreate it (read-only?); proceeding anywayu    resolved but .recoil-data-root sentinel missing and no project data present — Dropbox paused or Smart-Sync stub. Halting (Law 4).)	r(   r1   anyiterdirOSError
write_text_LOGwarningr   )r,   sentinelhas_datacreateds   &   r   _assert_data_rootrA   Y   s   ))H;;= 
 
SS )
\\^)
SSS )
\\^)
 &
 	 45G 	S (/$	
  I	
 
"& O 	P %    		s4   C  C  C  (#C  C3  C0/C03D Dc                $    V ^8  d   QhR\         /# r   r   )r    s   "r   r!   r!      s      t r   c                 ~   \         P                  P                  RR4      P                  4       p V '       do   \	        V 4      P                  4       P                  4       pVP                  4       '       d   VP                  4       '       g   \        RV : R24      h\        V4      # \        4       P                  R4      pV'       do   \	        V4      P                  4       P                  4       pVP                  4       '       d   VP                  4       '       g   \        RV: R24      h\        V4      # \        R4      h)	zReturn the canonical projects root directory.

Resolution order:
  1. RECOIL_PROJECTS_ROOT env var
  2. pipeline_config.json["projects_root"]
  3. RAISE ProjectsRootUnresolvable
RECOIL_PROJECTS_ROOT zRECOIL_PROJECTS_ROOT=z! does not resolve to a directory.projects_rootz#pipeline_config.json projects_root=z does not resolve.zoCannot resolve projects root: RECOIL_PROJECTS_ROOT not set and pipeline_config.json has no 'projects_root' key.)osenvirongetstripr   
expanduserresolver(   r1   r   rA   get_pipeline_config)overrider,   raws      r   rF   rF      s     zz~~4b9??AHH~((*224{{}}DKKMM*'|3TU  !&&


#
#O
4C
Cy##%--/{{}}DKKMM*5cW<NO  !&&
"	; r   pipelinedefault_project	leviathanvisual_state_namespacevisualc                    \        \        P                  4      p \        \        4      p\        \        4      p\	        \
        P                  4      ^8  dW   \
        P                  ^ ,          V 8X  d;   \
        P                  ^,          V8X  d   \
        P                  ^,          V8X  d   R# \
        P                   Uu. uF  q3WV39  g   K  VNK  	  up\
        P                  R&   \
        P                  P                  ^ V4       \
        P                  P                  ^ V4       \
        P                  P                  ^ V 4       R# u upi )zCAdd RECOIL_ROOT.parent, RECOIL_ROOT, and PIPELINE_ROOT to sys.path.N:NNN)strRECOIL_ROOTparentPIPELINE_ROOTlensyspathinsert)
parent_str
recoil_strpipeline_strr5   s       r   ensure_pipeline_importablera      s    [''(J[!J}%LCHHHHQK:%HHQK:%HHQK<'!hhZhJj3Y*Y11hZCHHQKHHOOA|$HHOOAz"HHOOAz" [s   >
E	E	identitylocpropchar	character
characterslocation	locationspropsi   z_v(\d+)$T)frozenc                   0   a  ] tR t^t o RtV 3R ltRtV tR# )ResolvedRefz*Result of a successful resolve_ref() call.c                   < V ^8  d   Qh/ S[ ;R&   S[;R&   S[;R&   S[;R&   S[S[,          ;R&   S[S[,          ;R&   S[;R&   # )r   r\   clssubjectkindvariantversionintegrity_ok)r   rV   r   intbool)r    __classdict__s   "r   r!   ResolvedRef.__annotate__   sd      J  
H	 
 L  I  c]  c]   r   r   N)r   r   r   r   r   __annotate_func__r   __classdictcell__rw   s   @r   rm   rm      s     4  r   rm   c                       ] tR t^tRtRtR# )RefNotFoundErrorzQRaised when resolve_ref() finds no candidate matching (cls,subject,kind,variant).r   Nr   r   r   r   r}   r}      s    [r   r}   c                       ] tR t^tRtRtR# )BrokenRefErrorzCRaised when a candidate file exists but fails integrity validation.r   Nr   r   r   r   r   r      s    Mr   r   c                0    V ^8  d   QhR\         R\         /# )r   rp   r   rV   )r    s   "r   r!   r!      s     5 5 5 5r   c                    V '       g   \        R4      hV P                  4       P                  RR4      P                  4       # )z=Canonicalize a subject id: lowercase, hyphens to underscores.zsubject must be non-empty-_)
ValueErrorlowerreplacerJ   )rp   s   &r   _normalize_subjectr      s3    455==?""3,2244r   c                0    V ^8  d   QhR\         R\         /# r   ro   r   r   )r    s   "r   r!   r!     s      # # r   c           
         V \         9   d   V # \        P                  V 4      pVf1   \        RV : R\	        \         4       R\	        \        4       R24      h\
        P                  RW4       V# )z?Collapse class aliases to canonical. Logs warning on alias use.Invalid asset class: . Must be one of z (or alias of: ).zDresolve_ref: class alias %r -> %r. Migrate caller to canonical name.)VALID_ASSET_CLASSES_CLASS_ALIASESrI   r   sortedr<   r=   )ro   	canonicals   & r   _normalize_classr     sy    
!!
""3'I#C7*;)*+?n%&b*
 	

 	LLN r   c                0    V ^8  d   QhR\         R\        /# )r   stemr   )rV   tuple)r    s   "r   r!   r!     s     . . . .r   c                    \         P                  V 4      pV'       g   V R3# V RVP                  4        \        VP	                  ^4      4      3# )zJStrip trailing _vNN from a stem. Returns (base_stem, version_int_or_None).N)_VERSION_REsearchstartru   group)r   ms   & r   _parse_versionr     sC    4 ATz!'')c!''!*o--r   c                ^    V ^8  d   QhR\         R\         R\        \         ,          R\        /# )r   rp   rq   rr   r   )rV   r   list)r    s   "r   r!   r!     s2     
@ 
@c 
@ 
@x} 
@ 
@r   c           	        . pV'       d   VP                  V  RV RV 24       VR9   d   VP                  V  RV R24       VP                  V  RV 24       VP                  \        \        \        V 4      V4      4      P                  4       \        4       pV Uu. uF&  qUV9   d   K  VP                  V4      '       d   K$  VNK(  	  up# u upi )z/Build the priority-ordered stem candidate list.r   _hero)Nhero)appendr   r   r   r   setadd)rp   rq   rr   stemsseenss   &&&   r   _candidate_stemsr     s    Ey$q	23. y$u-.	LLG9AdV$%	LLl<#8$?@EEF5D?u!$YA$((1+AAu???s   C*CCc                (    V ^8  d   QhR\         RR/# )r   r\   r   Nr   )r    s   "r   r!   r!   *  s     
 
4 
D 
r   c           
       a  V P                  4       P                  pT\        8  dF    T P                  4       R,          P                  RRR7      p\        R
T R\         RT  RT: 24      h T P                  R4      ;_uu_ 4       pTP                  ^4      oRRR4       \        ;QJ d#    T3R l\         4       F  '       g   K   RM	  RM! T3R l\         4       4      '       g   \        RS: RT  RT R24      hR#   \         d   p\        RT  RT R24      ThRp?ii ; i  \         d    R	p Li ; i  + '       g   i     L; i  \         d   p\        RT  RT R24      ThRp?ii ; i)zJValidate that path is a real image file. Raises BrokenRefError on failure.zCannot stat ref file: z ()N:N   Nutf-8r   )errorsz<unreadable>z'Ref file too small to be a real image (z	 bytes < z): z
  Content preview: rbzCannot read ref file: c              3   J   <"   T F  w  rSP                  V4      x  K  	  R # 5iN)r3   )r4   magicr   heads   &  r   r6   #_integrity_check.<locals>.<genexpr>>  s     En(%tu%%ns    #TFz4Ref file has no image header magic. First 12 bytes: z	
  Path: z	
  Size: z bytes)statst_sizer:   r   _MIN_IMAGE_BYTES
read_bytesdecodeopenreadr8   _IMAGE_HEADERS)r\   sizeepreviewfr   s   &    @r   _integrity_checkr   *  st   Kyy{"" 	%oo'-44WY4OG 5dV9 D6)>wkK
 	
KYYt__66":D  3EnE333EnEEEB4( KfJtfF4
 	
 F!  K5dV2aSBCJK
  	%$G	% _ K5dV2aSBCJKs^   D (D' .E D:E D$DD$'D76D7:E
	E 
E E0E++E0c          	      ^    V ^8  d   QhR\         R\        R\        R\        \        ,          /# )r   look_dirr   allow_versionedr   )r   rV   rv   r   r   )r    s   "r   r!   r!   E  s4      
  e_	r   c                   \          F+  pW V 2,          pVP                  4       '       g   K'  VR3u # 	  V'       d   Rp\          F  pV P                  V RV 24       F  p\        P	                  VP
                  4      pV'       g   K,  VP
                  RVP                  4        V8w  d   KP  \        VP                  ^4      4      pVe   W^ ,          8  g   K}  W3pK  	  K  	  Ve   V^,          V^ ,          3# R# )zNFind the best file matching stem. Returns (path, version_int_or_None) or None.Nz_v*)	_IMAGE_EXTENSIONS_RESOLVERr(   globr   r   r   r   ru   r   )	r   r   r   ext	candidatebestmatchr   vs	   &&&      r   _resolve_one_stemr   E  s     *u~-	d?" * -C!$s3%'89&&uzz2::k	*d2
O<1Aw;:D : . 7DG##r   c                     a  ] tR tRt o Rt]! RR7      t]RyV 3R lR ll4       t]V 3R lR l4       t	]
V 3R	 lR
 l4       t]
V 3R lR l4       tV 3R lR lt]
V 3R lR l4       tRyV 3R lR lltV 3R lR ltV 3R lR ltRzV 3R lR lltRzV 3R lR lltRzV 3R lR lltR{RRRR/R  lltRzV 3R! lR" llt]
V 3R# lR$ l4       tV 3R% lR& ltV 3R' lR( ltV 3R) lR* lt]
V 3R+ lR, l4       tV 3R- lR. lt]
V 3R/ lR0 l4       tV 3R1 lR2 lt]
V 3R3 lR4 l4       t]
V 3R5 lR6 l4       t ]
V 3R7 lR8 l4       t!]
V 3R9 lR: l4       t"]
V 3R; lR< l4       t#]
V 3R= lR> l4       t$]
V 3R? lR@ l4       t%]
V 3RA lRB l4       t&]
V 3RC lRD l4       t']
V 3RE lRF l4       t(]
V 3RG lRH l4       t)]
V 3RI lRJ l4       t*]
V 3RK lRL l4       t+]
V 3RM lRN l4       t,]
V 3RO lRP l4       t-]
V 3RQ lRR l4       t.]
V 3RS lRT l4       t/]
V 3RU lRV l4       t0]
V 3RW lRX l4       t1]
V 3RY lRZ l4       t2V 3R[ lR\ lt3V 3R] lR^ lt4V 3R_ lR` lt5]
V 3Ra lRb l4       t6]
V 3Rc lRd l4       t7]
V 3Re lRf l4       t8]
V 3Rg lRh l4       t9]
V 3Ri lRj l4       t:]
V 3Rk lRl l4       t;]
V 3Rm lRn l4       t<]
V 3Ro lRp l4       t=]
V 3Rq lRr l4       t>]
V 3Rs lRt l4       t?]
V 3Ru lRv l4       t@V 3Rw ltARxtBV tCR# )|ProjectPathsi`  u  Immutable path bundle for a single project under v3 layout.

Construct via:
    paths = ProjectPaths.for_project("tartarus")
    paths = ProjectPaths(project_root=Path("/abs/path/to/projects/tartarus"))

Access (v3):
    paths.assets_dir                              → projects/tartarus/assets/
    paths.asset_class_dir("char")                 → projects/tartarus/assets/char/
    paths.asset_subject_dir("char", "jade")       → projects/tartarus/assets/char/jade/
    paths.asset_look_dir("char", "jade", "base")  → projects/tartarus/assets/char/jade/base/
    paths.pool_dir("char", "jade", "identity")    → .../jade/base/pool/identity/
    paths.resolve_hero("char", "jade", "identity") → .../jade/base/jade_identity.jpeg
    paths.prep_dir                                → projects/tartarus/prep/
    paths.episode_prep_dir(1)                     → projects/tartarus/prep/ep_001/
    paths.renders_dir                             → projects/tartarus/renders/
    paths.episode_renders_dir(1)                  → projects/tartarus/renders/ep_001/
    paths.pipeline_dir                            → projects/tartarus/_pipeline/
    paths.state_dir                               → projects/tartarus/_pipeline/state/
    paths.shot_plans_dir                          → projects/tartarus/_pipeline/shot_plans/
    paths.scripting_dir                           → projects/tartarus/scripting/
    paths.bible_dir                               → projects/tartarus/scripting/bible/
    paths.episodes_dir                            → projects/tartarus/scripting/episodes/
    paths.development_dir                         → projects/tartarus/scripting/development/
    paths.compiled_dir                            → projects/tartarus/scripting/compiled/
    paths.treatment_path                          → projects/tartarus/scripting/treatment.md
    paths.history_dir                             → projects/tartarus/_history/

Deprecated v2 accessors (raise DeprecatedPathAPIError):
    paths.sequences_dir    → use prep_dir
    paths.asset_kind_dir() → use asset_class_dir/asset_subject_dir/asset_look_dir

The constructor validates layout_freeze ONCE. Frozen projects (driver-beware)
raise LayoutFrozenError on any v2-only property access.
N)defaultc                4   < V ^8  d   QhRS[ S[,          RR/# )r   projectr   r   )r   rV   )r    rw   s   "r   r!   ProjectPaths.__annotate__  s      <  <(3-  <>  <r   c                   \        4       P                  RR4      pT;'       g    TpW3P                  4       8w  d9   ^ RIpVP                  ! \
        4      P                  RW3P                  4       4       VP                  4       p\        4       V,          pVP                  4       '       g+   VP                  R4      '       g   \        RV: RV R24      h\        V4      pV ! WgR	7      # )
u*  Construct from a project slug. Looks up projects_root() and reads
project_config.json for the layout_freeze flag.

R7.4: surface caller bugs early. Production project slugs are
lowercase by convention — if a caller passes "Tartarus" instead of
"tartarus" we still case-fold (preserving backward compat) but log
a warning so the mismatch is visible. We also raise if the project
directory does not exist, EXCEPT for slugs that start with "_"
(test scaffolding such as `_phase14_gate_test`) where the harness
intentionally constructs paths before mkdir.
rQ   rR   NuN   ProjectPaths.for_project: non-lowercase project name %r — case-folding to %rr   Project z does not exist at zn. If this is intentional (e.g., constructing paths before mkdir), use ProjectPaths(project_root=...) directly.project_rootlayout_freeze)rM   rI   r   logging	getLoggerr   r=   rF   r1   r3   FileNotFoundError_read_layout_freeze)ro   r   r   rO   r   projr,   freezes   &&      r   for_projectProjectPaths.for_project  s      &'++,={K  ))+h'//`YY[ yy{%{{}}T__S%9%9#4("5dV <? @ 
 %T*;;r   c                $   < V ^8  d   QhRS[ RR/# )r   r   r   r   r   )r    rw   s   "r   r!   r     s     D DT Dn Dr   c                *    \        V4      pV ! WR7      # )z-Construct from an absolute project root path.r   )r   )ro   r   r   s   && r   	from_rootProjectPaths.from_root  s     %\2CCr   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     & & &r   c                .    V P                   P                  # )z(Project slug (last directory component).)r   r2   selfs   &r   r   ProjectPaths.project  s       %%%r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r          9 9T 9r   c                (    V P                   R ,          # )r0   r   r   s   &r   project_config_path ProjectPaths.project_config_path  s      #888r   c                $   < V ^8  d   QhRS[ RR/# )r   	prop_namer   Nr   )r    rw   s   "r   r!   r     s     	 	 	 	r   c                    V P                   '       d`   V P                   P                  4       P                  R R4      P                  RR4      R8X  d   \        RV P                  : RV: R24      hR# R# )r   r    zad-hocr   z& has layout_freeze='ad-hoc'. Property zX is v2-only and not available. Finish this project against its pre-migration git commit.N)r   r   r   r   r   )r   r   s   &&r   _check_unfrozenProjectPaths._check_unfrozen  s{     $"4"4":":"<"D"DS#"N"V"VWZ\_"`dl"l#4<<* +%= )LM  #mr   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     , ,D ,r   c                J    V P                  R 4       V P                  R,          # )
assets_dirassetsr   r   r   s   &r   r   ProjectPaths.assets_dir  s!    \*  8++r   c                <   < V ^8  d   QhRS[ RS[S[ ,          RS[/# )r   rq   rp   r   )rV   r   r   )r    rw   s   "r   r!   r     s&     
 
3 
# 
$ 
r   c                    \        R4      h)zGDEPRECATED in v3. Use asset_class_dir/asset_subject_dir/asset_look_dir.zasset_kind_dir() is deprecated in v3. Use asset_class_dir/asset_subject_dir/asset_look_dir. See consultations/recoil/ref-taxonomy-and-derivation-2026-05-30/SYNTHESIS.mdr   )r   rq   rp   s   &&&r   asset_kind_dirProjectPaths.asset_kind_dir      $[
 	
r   c                &   < V ^8  d   QhRS[ RS[/# r   rV   r   )r    rw   s   "r   r!   r     s     2 23 24 2r   c                2   V\         9   d4   \         V,          V8w  d"   \         V,          p\        R V: RV: R24      hV\        9  d    \        RV: R\	        \        4       24      hV P                  R V: R24       V P                  R,          V,          # )zasset_class_dir(z$) is deprecated. Use canonical name zw. (In Phase 1-3 transition window, resolve_ref() collapses the alias with a warning; direct dir construction does not.)r   r   r   r   )r   r   r   r   r   r   r   )r   ro   r   s   && r   asset_class_dirProjectPaths.asset_class_dir  s    . ^C%8C%?&s+I("3' *&&/] 3EF  ))4SG;LVTgMhLijkk/wa89  8+c11r   c                ,   < V ^8  d   QhRS[ RS[ RS[/# r   ro   rp   r   r  )r    rw   s   "r   r!   r     s"     3 3S 33 34 3r   c                2    V P                  V4      V,          # r   )r  )r   ro   rp   s   &&&r   asset_subject_dirProjectPaths.asset_subject_dir  s    ##C(722r   c                2   < V ^8  d   QhRS[ RS[ RS[ RS[/# )r   ro   rp   lookr   r  )r    rw   s   "r   r!   r     s)     ; ;# ; ;3 ;D ;r   c                2    V P                  W4      V,          # r   )r  )r   ro   rp   r  s   &&&&r   asset_look_dirProjectPaths.asset_look_dir  s    %%c3d::r   c          
      8   < V ^8  d   QhRS[ RS[ RS[ RS[ RS[/# r   ro   rp   rq   r  r   r  )r    rw   s   "r   r!   r     s8     G GC G# GS G GQU Gr   c                v    V\         9  d   \        R V: 24      hV P                  WV4      R,          V,          # )zInvalid ref type: pool)VALID_REF_TYPESr   r  r   ro   rp   rq   r  s   &&&&&r   pool_dirProjectPaths.pool_dir  s:    &1$:;;""36?$FFr   c          
      8   < V ^8  d   QhRS[ RS[ RS[ RS[ RS[/# r  r  )r    rw   s   "r   r!   r     s1     > >s >S > >3 >TX >r   c                4    V P                  WW44      R ,          # )4k)r  r  s   &&&&&r   pool_4k_dirProjectPaths.pool_4k_dir  s    }}S46==r   r   Tvalidatec               6   \        V4      p\        V4      p	V\        9  d!   \        RV: R\	        \        4       R24      hV P                  WV4      p
V
P                  4       '       g   \        RV
 RV: RV	: RV: R2	4      h\        WV4      p. pV FN  pVP                  V4       \        WV4      pVf   K&  Vw  ppV'       d   \        V4       \        WV	W4VR	R
7      u # 	  \        RV
 RV: RV	: RV: RV: RV R\        \        4       RV 24      h)zResolve a ref image path through the canonical SSOT resolver.

Returns ResolvedRef on success.
Raises RefNotFoundError if no candidate found.
Raises BrokenRefError if candidate fails integrity check.
zInvalid ref kind: r   r/   zNo look directory: z (cls=z
, subject=z, look=r   T)r\   ro   rp   rq   rr   rs   rt   zNo ref found in z
  cls=z	 subject=z kind=z	 variant=z
  Stems tried: z
  Extensions tried: z
  Versioned glob: )r   r   r  r   r   r  r1   r}   r   r   r   r   rm   r   r   )r   ro   rp   rq   rr   r  r   r  cls_canonicalsubject_normr   r   triedr   foundr\   rs   s   &&&&&&$$         r   resolve_refProjectPaths.resolve_ref  sm    )-)'2&$TH,=f_>U=VVWX  &&}DI  "%hZ 0%(
<2B'$QRT  !W=DLL%hoFE}!MD' &lG!   xj )"%Y|.>fTH Mk "#W %##'(B#C"D E!!0 13
 	
r   c          
      8   < V ^8  d   QhRS[ RS[ RS[ RS[ RS[/# r  r  )r    rw   s   "r   r!   r      s1     ? ? ?c ? ?C ?UY ?r   c           	     B    V P                  WVRVRR7      P                  # )zJBack-compat wrapper. Returns Path (not ResolvedRef). Validates by default.r   T)rr   r  r  )r%  r\   r  s   &&&&&r   resolve_heroProjectPaths.resolve_hero   s*    dF%)D   ::>$	?r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r   (  s     * *$ *r   c                J    V P                  R 4       V P                  R,          # )prep_dirprepr   r   s   &r   r-  ProjectPaths.prep_dir'  s!    Z(  6))r   c                &   < V ^8  d   QhRS[ RS[/# r   episoder   ru   r   )r    rw   s   "r   r!   r   ,  s     3 3 3 3r   c                0    V P                   R VR 2,          # ep_03d)r-  r   r2  s   &&r   episode_prep_dirProjectPaths.episode_prep_dir,  s    }}WSM222r   c                &   < V ^8  d   QhRS[ RS[/# r1  r3  )r    rw   s   "r   r!   r   /  s     > >s >t >r   c                2    V P                  V4      R ,          # )storyboardsr9  r8  s   &&r   episode_storyboards_dir$ProjectPaths.episode_storyboards_dir/  s    $$W-==r   c                &   < V ^8  d   QhRS[ RS[/# r1  r3  )r    rw   s   "r   r!   r   2  s     < <S <T <r   c                2    V P                  V4      R ,          # )	breakdownr>  r8  s   &&r   episode_breakdown_dir"ProjectPaths.episode_breakdown_dir2  s    $$W-;;r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r   8  s     
 
t 
r   c                    \        R 4      h)zsequences_dir is deprecated in v3. Use prep_dir instead. See consultations/recoil/project-layout-v3-migration-2026-05-30/SYNTHESIS.mdr   r   s   &r   sequences_dirProjectPaths.sequences_dir7  r  r   c                &   < V ^8  d   QhRS[ RS[/# r1  r3  )r    rw   s   "r   r!   r   >  s     8 8S 8T 8r   c                0    V P                   R VR 2,          # r5  )rH  r8  s   &&r   episode_sequences_dir"ProjectPaths.episode_sequences_dir>  s    !!c'#$777r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r   D  s     - -T -r   c                J    V P                  R 4       V P                  R,          # )renders_dirrendersr   r   s   &r   rP  ProjectPaths.renders_dirC  s!    ]+  9,,r   c                &   < V ^8  d   QhRS[ RS[/# r1  r3  )r    rw   s   "r   r!   r   H  s     6 63 64 6r   c                0    V P                   R VR 2,          # r5  )rP  r8  s   &&r   episode_renders_dir ProjectPaths.episode_renders_dirH  s    C}"555r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r   N  s     / /d /r   c                (    V P                   R ,          # )	_pipeliner   r   s   &r   pipeline_dirProjectPaths.pipeline_dirM        ;..r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r   R  s     0 0 0r   c                (    V P                   R ,          # )
shot_plans)rZ  r   s   &r   shot_plans_dirProjectPaths.shot_plans_dirQ  s      <//r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r   V  s     + +4 +r   c                J    V P                  R 4       V P                  R,          # )	state_dirstate)r   rZ  r   s   &r   rd  ProjectPaths.state_dirU  s!    [)  7**r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r   [  s     * *$ *r   c                \    \        4       P                  R R4      pV P                  V,          # )rS   rT   )rM   rI   rd  )r   	namespaces   & r   visual_state_dirProjectPaths.visual_state_dirZ  s)    
 ()--.FQ	~~	))r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r   c  s     , ,t ,r   c                J    V P                  R 4       V P                  R,          # )manifests_dir	manifestsr   rd  r   s   &r   rn  ProjectPaths.manifests_dirb  s    _-~~++r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r   h  s     * *T *r   c                J    V P                  R 4       V P                  R,          # )bundles_dirbundlesrp  r   s   &r   rt  ProjectPaths.bundles_dirg  s    ]+~~	))r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r   o  s     0 04 0r   c                (    V P                   R ,          # )orchestrationrd  r   s   &r   orchestration_dirProjectPaths.orchestration_dirn  s    ~~//r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r   s  s     1 1$ 1r   c                (    V P                   R ,          # )scenes)r{  r   s   &r   orchestration_scenes_dir%ProjectPaths.orchestration_scenes_dirr  s    %%00r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r   w  s     + +d +r   c                (    V P                   R ,          # )learningrz  r   s   &r   learning_dirProjectPaths.learning_dirv  s    ~~
**r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r   {  s     . . .r   c                (    V P                   R ,          # )checkpointsrz  r   s   &r   checkpoints_dirProjectPaths.checkpoints_dirz  s    ~~--r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     * *T *r   c                (    V P                   R ,          # )backupsrz  r   s   &r   backups_dirProjectPaths.backups_dir~  s    ~~	))r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r          / /4 /r   c                (    V P                   R ,          # )plansrj  r   s   &r   	plans_dirProjectPaths.plans_dir      $$w..r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     0 0D 0r   c                (    V P                   R ,          # )passesr  r   s   &r   
passes_dirProjectPaths.passes_dir  s    $$x//r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     r   r   c                (    V P                   R ,          # )coverage_passesr  r   s   &r   coverage_passes_dir ProjectPaths.coverage_passes_dir      $$'888r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     4 4 4r   c                (    V P                   R ,          # )
derivationr  r   s   &r   derivation_dirProjectPaths.derivation_dir  s    $$|33r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     r  r   c                (    V P                   R ,          # )shotsr  r   s   &r   	shots_dirProjectPaths.shots_dir  r  r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     < <D <r   c                (    V P                   R ,          # )zcasting_state.jsonr  r   s   &r   casting_state_pathProjectPaths.casting_state_path  s    $$';;;r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     ; ;4 ;r   c                (    V P                   R ,          # )zglobal_bible.jsonr  r   s   &r   global_bible_pathProjectPaths.global_bible_path  s    $$':::r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     ? ?$ ?r   c                (    V P                   R ,          # )zepisode_look_map.jsonr  r   s   &r   episode_look_mapProjectPaths.episode_look_map  s    $$'>>>r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     9 94 9r   c                (    V P                   R ,          # )z_hashcache.jsonr  r   s   &r   	hashcacheProjectPaths.hashcache  r  r   c                &   < V ^8  d   QhRS[ RS[/# )r   	char_namer   r  )r    rw   s   "r   r!   r     s     H H# H$ Hr   c                R    V P                   R ,          V,          R,          R,          # )re   basesheetsr   )r   r  s   &&r   get_character_sheets_dir%ProjectPaths.get_character_sheets_dir  s     ')3f<xGGr   c                &   < V ^8  d   QhRS[ RS[/# )r   loc_namer   r  )r    rw   s   "r   r!   r     s     = = = =r   c                D    V P                   R ,          V,          R,          # )rc   r  r  )r   r  s   &&r   get_location_sheets_dir$ProjectPaths.get_location_sheets_dir  s    &1H<<r   c                ,   < V ^8  d   QhRS[ RS[ RS[/# r	  r  )r    rw   s   "r   r!   r     s'     F Fc FC FD Fr   c                   RRRRRRRRRRRRRRRRR	R/	P                  W4      pVP                  4       pVR8X  d   V P                  V4      R
,          # VR8X  d   V P                  V4      R
,          # V P	                  W24      R
,          # )u  The canonical composite-sheet file for an entity — the SINGLE per-kind
sheet-layout home. char -> base/sheets/, loc -> sheets/ (NO base/),
prop -> subject-root. Layout ONLY; existence/validity is the caller's job.
This is the one place that may branch on per-kind sheet layout. `cls`
accepts the same aliases the resolver's _normalize_kind does (v3 class,
v1 plural, the 'identity'->char alias) plus the singular forms.re   rg   rf   rb   rc   ri   rh   rd   rj   zsheet_v1.png)rI   r   r  r  r  )r   ro   rp   norms   &&& r   
sheet_pathProjectPaths.sheet_path  s     FL&+vzSY5+uj%FGV
 #c-	 	
 --/6>009NJJ5=//8>II%%d4~EEr   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     / /t /r   c                (    V P                   R ,          # )	scriptingr   r   s   &r   scripting_dirProjectPaths.scripting_dir  r\  r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     , ,4 ,r   c                J    V P                  R 4       V P                  R,          # )	bible_dirbibler   r  r   s   &r   r  ProjectPaths.bible_dir  s!    [)!!G++r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r          / /d /r   c                J    V P                  R 4       V P                  R,          # )episodes_direpisodesr  r   s   &r   r  ProjectPaths.episodes_dir  !    ^,!!J..r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     2 2 2r   c                J    V P                  R 4       V P                  R,          # )development_dirdevelopmentr  r   s   &r   r  ProjectPaths.development_dir  s"    ./!!M11r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     r  r   c                J    V P                  R 4       V P                  R,          # )compiled_dircompiledr  r   s   &r   r  ProjectPaths.compiled_dir  r  r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     3 3 3r   c                (    V P                   R ,          # )ztreatment.md)r  r   s   &r   treatment_pathProjectPaths.treatment_path  s    !!N22r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     . .T .r   c                (    V P                   R ,          # )_historyr   r   s   &r   history_dirProjectPaths.history_dir  s      :--r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r          . .t .r   c                (    V P                   R ,          # )	snapshotsr  r   s   &r   history_snapshots_dir"ProjectPaths.history_snapshots_dir      +--r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     - -d -r   c                (    V P                   R ,          # )archivesr  r   s   &r   history_archives_dir!ProjectPaths.history_archives_dir  s    *,,r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     r  r   c                (    V P                   R ,          # )	migrationr  r   s   &r   history_migration_dir"ProjectPaths.history_migration_dir  r  r   c                    < V ^8  d   QhRS[ /# r   r   )r    rw   s   "r   r!   r     s     * *4 *r   c                (    V P                   R ,          # )debugr  r   s   &r   history_debug_dirProjectPaths.history_debug_dir  s    '))r   c                B   < V ^8  d   Qh/ S[ ;R&   S[S[,          ;R&   # )r   r   r   r   r   rV   )r    rw   s   "r   r!   r   `  s'     L M N C=6O r   r   r   )r  )Nr  )Dr   r   r   r   r   r   r   classmethodr   r   propertyr   r   r   r   r   r  r  r  r  r  r%  r)  r-  r9  r?  rD  rH  rL  rP  rU  rZ  r`  rd  rj  rn  rt  r{  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r  r
  ry   r   rz   r{   s   @r   r   r   `  s    "J $)#6M <  <  <D D D & & 9 9	 	 , ,
 
2 23 3; ;G G
> >*
'+*
6:*
X? ? * *3 3> >< <
 
 
8 8
 - -6 6
 / / 0 0 + + * * , , * * 0 0 1 1 + + . . * * / / 0 0 9 9 4 4 / / < < ; ; ? ? 9 9H H= =F F* / / , , / / 2 2 / / 3 3
 . . . . - - . . * *a  r   r   c                F    V ^8  d   QhR\         R\        \        ,          /# )r   r   r   r  )r    s   "r   r!   r!     s     	 	d 	x} 	r   c                    V R,          pVP                  4       '       g   R#  \        P                  ! VP                  RR7      4      pVP	                  R4      #   \        P
                  \        3 d     R# i ; i)zARead project_config.json and return layout_freeze value, or None.r0   Nr   )encodingr   )is_filejsonloads	read_textrI   JSONDecodeErrorr:   )r   cfgdatas   &  r   r   r     se    
.
.C;;==zz#---9:xx((  '* s   6A A98A9c                $    V ^8  d   QhR\         /# r   r   )r    s   "r   r!   r!     s     / /d /r   c                 8    \        4       P                  R,          # )u1   Global scratch directory — outside any project._scratch)rF   rX   r   r   r   scratch_rootr    s    ?!!J..r   c                R    V ^8  d   QhR\         R\        R\        \         ,          /# )r   base_dirr   r   r   rV   r   )r    s   "r   r!   r!     s%      $ c htn r   c                    \        V4      pVP                  P                  4       \        9   d   VP                  p\         F)  pW V 2,          pVP                  4       '       g   K'  Vu # 	  R# )zFind a ref image by stem, regardless of extension.

Searches base_dir for {stem}.jpg, .jpeg, .png, .webp.
Returns the first match or None.
N)r   suffixr   _IMAGE_EXTENSIONSr   r(   )r  r   r5   r   r   s   &&   r   r%  r%    s^     	T
Axx~~,,vv u~-	 ! r   c                R    V ^8  d   QhR\         R\        R\        \         ,          /# )r   project_dirrel_pathr   r   )r    s   "r   r!   r!     s%     1 1$ 1# 1(4. 1r   c                    W,          pVP                  4       '       d   V# \        VP                  VP                  4      # )z>Resolve a relative ref path, tolerant of extension mismatches.)r(   r%  rX   r   )r%  r&  exacts   && r   resolve_ref_pathr)    s/    "E||~~u||UZZ00r   c                $    V ^8  d   QhR\         /# r   r   )r    s   "r   r!   r!   &  s      T r   c                      \         # r   )_pipeline_configr   r   r   rM   rM   &  s    r   c                      \        4       sR # r   )r*   r,  r   r   r   reload_pipeline_configr.  *  s    ,.r   c                0    V ^8  d   QhR\         R\        /# r   r   r   r  )r    s   "r   r!   r!   6  s       t r   c                    \        R4      h)a  DEPRECATED. The v1 'output/' top-level directory was destroyed in the
v2 layout refactor (2026-05-26). Use ProjectPaths instead.

Migration:
    # Old:
    out = project_output_dir(project)
    frames = out / "frames" / f"ep_{ep:03d}"
    # New:
    paths = ProjectPaths.for_project(project)
    frames = paths.episode_prep_dir(ep)        # frames moved to prep/
    # OR (for video):
    videos = paths.episode_renders_dir(ep)     # video moved to renders/
zproject_output_dir() was deleted in the v2 layout refactor. Use ProjectPaths.for_project(project) and the explicit .prep_dir / .renders_dir / .manifests_dir / .bundles_dir properties. See recoil/core/paths.py docstring for the v3 layout.r   r   s   &r   project_output_dirr3  6  s     !	L r   c                0    V ^8  d   QhR\         R\        /# r0  r  )r    s   "r   r!   r!   L  s      c T r   c                    \        R4      h)a[  DEPRECATED. 'output/refs/' was renamed to 'assets/' and re-keyed by
singular taxonomy kind in the v2 layout refactor (2026-05-26).

Migration:
    # Old:
    refs = project_refs_dir(project)
    char_dir = refs / "characters" / "jade"
    # New:
    paths = ProjectPaths.for_project(project)
    char_dir = paths.asset_subject_dir("char", "jade")
zproject_refs_dir() was deleted in the v2 layout refactor. Use ProjectPaths.for_project(project).asset_class_dir(cls) / asset_subject_dir(cls, subject) / asset_look_dir(cls, subject, look) with cls in {'char','loc','prop'}.r   r2  s   &r   project_refs_dirr6  L  s     !	- r   c                <    V ^8  d   QhR\         R\         R\        /# r   r   rp   r   r  )r    s   "r   r!   r!   `  s!       s d r   c                *    \        RV : RV: R24      h)zZDEPRECATED v2 path: refs/characters/. Use ProjectPaths.asset_subject_dir('char', subject).zLrefs/characters/ was removed in the v3 layout. Use ProjectPaths.for_project(z).asset_subject_dir('char', zI) and resolve_ref('char', subject, 'identity', 'hero') for the hero file.r   r   rp   s   &&r   refs_characters_dirr;  `  s-    
 	((/{2Nwk ZR	R r   c                <    V ^8  d   QhR\         R\         R\        /# r8  r  )r    s   "r   r!   r!   i  s!       c T r   c                *    \        RV : RV: R24      h)z$DEPRECATED v2 path: refs/locations/.zKrefs/locations/ was removed in the v3 layout. Use ProjectPaths.for_project(z).asset_subject_dir('loc', r   r   r:  s   &&r   refs_locations_dirr>  i  s)    
 	((/{2Mg[XZ	\ r   c                <    V ^8  d   QhR\         R\         R\        /# r8  r  )r    s   "r   r!   r!   q  s!      C #  r   c                *    \        RV : RV: R24      h)z DEPRECATED v2 path: refs/props/.zGrefs/props/ was removed in the v3 layout. Use ProjectPaths.for_project(z).asset_subject_dir('prop', r   r   r:  s   &&r   refs_props_dirrA  q  s)    
 	((/{2NwkY[	] r   c                <    V ^8  d   QhR\         R\        R\        /# )r   r   r2  r   )rV   ru   r   )r    s   "r   r!   r!   y  s!      s S D r   c                `    \        RV : R2V'       d   RV R2,           4      hR,           4      h)z#DEPRECATED v2 path: output/frames/.zSoutput/frames/ was removed in the v2 layout refactor. Use ProjectPaths.for_project(r   z.episode_prep_dir(z	.prep_dirr   )r   r2  s   &&r   output_frames_dirrD  y  sK    
 	((/{!	5.5y*	H  <G	H r   
CONFIG_DIRr'   DEFAULT_PROJECTrY   rW   STATE_NAMESPACEVALID_ASSET_KINDSV3_DIRECTORIESr   r  ra   
get_configrM   reload_configr.  rF   r  r%  r)  r3  r6  r;  r>  rA  rD  >   rc   exprrd   turnscenerb   )zassets/charz
assets/loczassets/propr.  rQ  zscripting/biblezscripting/episodeszscripting/developmentzscripting/compiledz_pipeline/statez_pipeline/shot_plansz_pipeline/annotationsz_pipeline/sessionsz_pipeline/audioz_pipeline/testsz_pipeline/archivez_pipeline/visualz_history/snapshotsz_history/archivesz_history/migrationz_history/debug>   rc   re   rd   >   rL  rM  closeupfullbodyrb   ))s   PNG

png)s   jpeg)s   RIFFwebp_or_riff)s   GIF87agif)s   GIF89arT  ).jpeg.png.jpg.webp)rW  rU  rV  rX  r   )Mr   r  rG   r[   dataclassesr   r   pathlibr   typingr   rer   recoil.core.ref_stemr   r   r   r   r<   __file__rL   rX   rW   rE  r'   RuntimeErrorr   r   r   r*   rA   rF   r,  rY   rI   rF  rG  ra   	frozensetrH  rI  r   r  r   r   r   compile
IGNORECASEr   r   rm   r   r}   r   r   r   r   r   r   r   r   r   r  r#  r%  r)  rM   r.  rJ  rK  r3  r6  r;  r>  rA  rD  __all__r   r   r   <module>rd     s  !F  	 
 (   	  ;"
 8n$$&--448#
11F| F
6 6\ =$N< )* j("&&'8+F"&&'?J#$ RS    78 OP &
FV   jjbmm4 @  $  \( \N\ N5$.
@
66 $P* P* P*f	/ 7  1/ !
&
,(** * 	*
 * * * * * * * * *  !*$ !%*( )** +*, -*. /*2 3*4 5*6 7*: ;*< =*@ A*B C*D E*H I*J K*L M*N O*P Q*R S*r   