+
    <j3                     n  a  R9 t"0 t R t^ RIt^ RIHt ^ RIHt ]! 0 R:m4      tR
RRR/t	R R lt
]P                  ! R4      t]! RRR7       ! R R4      4       t ! R R]4      tRR/R R lltR R lt]! RRR7       ! R R4      4       tR]! RR^^^R4      R]! RR^^^R4      R]! RR^^^R4      R]! RR ^^^R4      R]! RR!^^
^R4      R	]! R	R"^^^R#4      /t] ^ k ]! ]P+                  4       R$ R%7      tR& R' ltR( R) ltR* R+ ltR;R, R- lltR. R/ ltRsR0 R1 lt]tR2RR3RR4R/tR5 R6 lt R7 R8 lt!R# )<u  
taxonomy.py — Asset naming convention, type taxonomy, and slot mapping.

Single source of truth for the 6 canonical asset types, the naming regex,
filename parsing, and pipeline slot allocation rules.

Human-readable documentation: ASSET_TAXONOMY.md
Numeric constants (weights, slots): CONSTANTS.md § Asset Taxonomy & Pipeline Allocation

Naming format:  {subject}_{type}_{variant}_v{NN}.{ext}
  - subject:  lowercase alphanumeric + hyphens (torch, int-lower-decks, old-man)
  - type:     one of 6 enum values (identity, turn, expr, loc, prop, scene)
  - variant:  lowercase alphanumeric + hyphens (hero, front, wide, pipe-detail)
  - version:  zero-padded 2-digit integer (v01, v02, v99)
  - ext:      png | jpeg | webp  (.jpg normalized to .jpeg on ingest)
N)	dataclass)Pathidentityturnexprlocpropscenez.jpgz.jpegz.tifz.tiffc                0    V ^8  d   QhR\         R\         /#    filenamereturnstr)formats   "ړ/Users/joeturnerlin/Code/recoil-sessions/Joes-MacBook-Pro/adhoc--claude--atlas-redesign--20260625T050330Z-1fd50d09/recoil/pipeline/_lib/taxonomy.py__annotate__r   &   s      # #     c                    \         P                  4        FB  w  rV P                  4       P                  V4      '       g   K,  V R\	        V4      )  V,           u # 	  V # )z>Normalize file extension (.jpg -> .jpeg) in a filename string.N)EXTENSION_NORMALIZEitemslowerendswithlen)r   old_extnew_exts   &  r   normalize_extensionr   &   sO    /557>>$$W--Os7|m,w66 8 Or   z^(?P<subject>[a-z][a-z0-9]*(?:-[a-z0-9]+)*)_(?P<type>identity|turn|expr|loc|prop|scene)_(?P<variant>[a-z][a-z0-9]*(?:-[a-z0-9]+)*)_v(?P<version>[0-9]{2})\.(?P<ext>png|jpeg|webp)$T)frozenslotsc                   ^   a  ] tR t^?t o Rt]V 3R lR l4       tV 3R lR ltV 3R ltRt	V t
R# )	AssetIdz+Parsed representation of an asset filename.c                    < V ^8  d   QhRS[ /# )r   r   r   )r   __classdict__s   "r   r   AssetId.__annotate__I   s     [ [# [r   c           	         V P                    RV P                   RV P                   RV P                  R RV P                   2	# )z#Reconstruct the canonical filename.__v02d.subjecttypevariantversionextselfs   &r   r   AssetId.filenameH   sA     ,,q1T\\N"T\\#<NaPTPXPXzZZr   c                   < V ^8  d   QhRR/# )r   r   r!    )r   r#   s   "r   r   r$   M   s     
 
i 
r   c                    \        V P                  V P                  V P                  V P                  ^,           V P
                  R7      # )z3Return a new AssetId with version incremented by 1.r*   )r!   r+   r,   r-   r.   r/   r0   s   &r   bumpAssetId.bumpM   s7    LLLLLL1$
 	
r   c                V   < V ^8  d   Qh/ S[ ;R&   S[ ;R&   S[ ;R&   S[;R&   S[ ;R&   # )r   r+   r,   r-   r.   r/   r   int)r   r#   s   "r   r   r$   ?   sA      L  I	 
 L  L  
H r   r4   N)__name__
__module____qualname____firstlineno____doc__propertyr   r6   __annotate_func____static_attributes____classdictcell__r#   s   @r   r!   r!   ?   s/     5 [ [
 
  r   r!   c                       ] tR t^\tRtRtR# )AssetNameErrorzGRaised when a filename does not conform to the asset naming convention.r4   N)r;   r<   r=   r>   r?   rB   r4   r   r   rF   rF   \   s    Qr   rF   	normalizec                <    V ^8  d   QhR\         R\        R\        /# )r   r   rG   r   )r   boolr!   )r   s   "r   r   r   a   s!     & &3 &d &g &r   c          	     J   RV 9   g   RV 9   d   \        RV : 24      hV'       d   \        V 4      p \        P                  V 4      pVf    \        RV : R\	        \
        4       24      h\        VR,          VR,          VR,          \        VR	,          4      VR
,          R7      # )a  Parse an asset filename into its constituent segments.

Args:
    filename: The filename to parse (no directory components).
        Example: "torch_identity_hero_v01.png"
    normalize: If True, normalize extensions (.jpg -> .jpeg) before
        matching. If False, non-canonical extensions will fail to match.

Returns:
    An AssetId dataclass with subject, type, variant, version, ext.

Raises:
    AssetNameError: If the filename does not match the asset convention.
/\z&Expected a bare filename, got a path: z*Filename does not match asset convention: zH. Expected: {subject}_{type}_{variant}_v{NN}.{ext} where type is one of r+   r,   r-   r.   r/   r*   )rF   r   ASSET_FILENAME_REmatchsortedVALID_TYPESr!   r:   )r   rG   rN   s   &$ r   parse_asset_filenamerQ   a   s     h$(*4XLA
 	

 &x0##H-E}8 E$$*;$7#8:
 	
 i 6]i E)$%%L r   c                0    V ^8  d   QhR\         R\        /# r   )r   rI   )r   s   "r   r   r      s      c d r   c                B     \        V 4       R#   \         d     R# i ; i)z@Return True if the filename matches the asset naming convention.TF)rQ   rF   )r   s   &r   is_valid_asset_filenamerT      s$    X& s    c                   0   a  ] tR t^t o RtV 3R ltRtV tR# )SlotSpecz.Pipeline slot specification for an asset type.c                b   < V ^8  d   Qh/ S[ ;R&   S[ ;R&   S[;R&   S[;R&   S[;R&   S[;R&   # )r   
asset_typeslot
weight_min
weight_maxorderauto_eligible)r   r:   rI   )r   r#   s   "r   r   SlotSpec.__annotate__   sN      O  I	 
 O  O  J   r   r4   N)r;   r<   r=   r>   r?   rA   rB   rC   rD   s   @r   rV   rV      s     8  r   rV   environment
expression	structurer+   vibeFc                     V P                   # )N)r\   )ss   &r   <lambda>re      s    QWWr   )keyc                0    V ^8  d   QhR\         R\        /# )r   rX   r   r9   )r   s   "r   r   r      s     4 4s 4s 4r   c                    \         P                  V 4      pVf   ^# VP                  VP                  ,           ^,          # )z@Return the default weight for an asset type (midpoint of range).)SLOT_MAPgetrZ   r[   )rX   specs   & r   default_weightrl      s0    <<
#D|OOdoo-!33r   c                R    V ^8  d   QhR\         R\        R\        \         ,          /# )r   rX   weightr   )r   r:   list)r   s   "r   r   r      s%      C  c r   c                   \         P                  V 4      pVf   RV : 2.# . pWP                  8  d$   VP                  V  RV RVP                   24       WP                  8  d$   VP                  V  RV RVP                   24       V# )zIReturn warnings if weight is outside the recommended range for this type.Unknown asset type: z weight z is below recommended minimum z is above recommended maximum )ri   rj   rZ   appendr[   )rX   rn   rk   warningss   &&  r   validate_ref_weightrt      s    <<
#D|&zn566Hl(6(*HHYZ	
 l(6(*HHYZ	
 Or   c                R    V ^8  d   QhR\         R\        R\        \         ,          /# )r   rX   autor   )r   rI   ro   )r   s   "r   r   r      s%      # T d3i r   c                    \         P                  V 4      pVf   RV : 2.# V'       d   VP                  '       g   V  R2.# . # )zBReturn errors if auto is True on a non-auto-eligible type (scene).rq   z4 refs must not be auto-resolved (auto must be false))ri   rj   r]   )rX   rv   rk   s   && r   validate_ref_autorx      sI    <<
#D|&zn566D&&&,RSTTIr   c                `    V ^8  d   QhR\         R\         R\         R\        R\         R\         /# )r   r+   rX   r-   r.   r/   r   r9   )r   s   "r   r   r      sD        	
 
 	r   c           	         V\         9  d    \        RV: R\        \         4       24      hV  RV RV RVR RV 2	p\        VRR7       V# )	ziBuild a canonical asset filename from components.

Raises AssetNameError if the result would be invalid.
zInvalid asset type: z. Must be one of r&   r'   r(   r)   F)rG   )rP   rF   rO   rQ   )r+   rX   r-   r.   r/   r   s   &&&&& r   build_asset_filenamer{      sa     $3J>ARSYZeSfRghii!J<q	GC=#GHU3Or   c          
      T    V ^8  d   QhR\         R\        R\        R\        R\        /# )r   	directoryr+   rX   r-   r   )r   r   r:   )r   s   "r   r   r      s0     
 
D 
3 
C 
# 
RU 
r   c                2   ^ pV P                  4        Fg  p \        VP                  4      pVP                  V8X  d>   VP                  V8X  d+   VP
                  V8X  d   \        WFP                  4      pKc  Ke  Kg  Ki  	  V^,           #   \         d     K  i ; i)zTScan a directory for existing versions and return the next available version number.)	iterdirrQ   namer+   r,   r-   maxr.   rF   )r}   r+   rX   r-   max_verpaids   &&&&   r   next_versionr      s    G 	&qvv.C{{g%#((j*@S[[T[E[g{{3 F\*@% ! Q;  		s   ABBBc                0    V ^8  d   QhR\         R\         /# )r   asset_idr   r   )r   s   "r   r   r      s     " "s "s "r   c                   \         f   ^ RIpVP                  R4      s V P                  4       pR
 Fb  pVP	                  V4      '       g   K  VP                  RR4      P                  RR4      P                  RR4      pWB\        V4      R ,           p M	  VP                  RR4      pVP                  RR4      p\         P                  RV4      pR	V9   d   VP                  R	R4      pK  VP                  R4      # )a  Universal slugify for characters, locations, and props.

Preserves INT/EXT prefix (semantically meaningful for lighting).
Strips non-alphanumeric except underscores.

Examples:
    'SADIE'              -> 'sadie'
    'INT. Dusty's Bar'  -> 'int_dustys_bar'
    'EXT. City Street'   -> 'ext_city_street'
    'INT/EXT. Alley'     -> 'int_ext_alley'
    'int_sadie_apartment'-> 'int_sadie_apartment'  (passthrough)
Nz
[^a-z0-9_]rK   r&   z. r)   ' __)z	int/ext. zint. zext. )	_SLUGIFY_RErecompiler   
startswithreplacer   substrip)r   _reslugprefixreplacements   &    r   slugify_asset_idr      s     kk-0>>D1??6"" ..c2::4EMMcSVWKc&kl!33D	 2 <<S!D<<R D??3%D
$,||D#&::c?r   
characters	locationspropsc                0    V ^8  d   QhR\         R\         /# )r   legacyr   r   )r   s   "r   r   r   9  s        r   c           
         V \         9   d   \         V ,          # V \        9   d   V # \        RV : R\        \         4       R\        \        4       R24      h)u   Translate a v1 plural-English entity type to its v2 singular kind.

Raises ValueError on unknown legacy types — the migration script catches
and falls back to its unmatched-file classifier (see Risk #5).
zUnknown legacy type z. Expected one of z or a v2 kind in r)   )LEGACY_PLURAL_TO_KINDVALID_ASSET_KINDS
ValueErrorrO   )r   s   &r   kind_for_legacy_typer   9  s`     &&$V,,""

vj(:'(
)):6BS;T:UUV	X r   c                0    V ^8  d   QhR\         R\         /# r   r   )r   s   "r   r   r   J  s     " "s "s "r   c                    V P                  4       pRV9   g   RV9   d   R# R F  pW!9   g   K   R# 	  RV9   d   R# RpV F  pWA9   g   K   R# 	  R# )	u-  Classify a filename from a v1 characters/{subject}/ directory into a v2 kind.

The v1 layout grouped identity refs, turnarounds, and expression refs all
into characters/{subject}/. The migration script needs to split them:

  - filename contains "_turn_" or "_angle_" or matches /^{subject}_(front|profile|three_quarter|back|closeup)/  → "turn"
  - filename contains "_expr_" or matches a known emotion label (anger, fear, joy, etc.)  → "expr"
  - everything else (including hero, _identity_) → "identity"

Returns one of: "identity", "turn", "expr".
_turn__angle_r   _expr_r   r   )_front_profile_three_quarter_back_closeup)angerfearjoysadnessdisgustsurpriseneutralcontemptanticipation_calm_smile_frown_grin_scowl)r   )r   r   angleEMOTION_LABELSlabels   &    r   classify_legacy_filenamer   J  sd     NNE 5I.N> O
 5N
  >  
 r   c                b    V ^8  d   Qh/ ^ \         9   d   \        \        \        3,          ;R&   # )r   ri   )__conditional_annotations__dictr   rV   )r   s   "r   r   r      s-      B $sH}
 Cr   >   r   r   r   r   r	   r   )   png)#r   r?   r   dataclassesr   pathlibr   	frozensetrP   r   r   r   rM   r!   r   rF   rQ   rT   rV   ri   rO   values
SLOT_ORDERrl   rt   rx   r{   r   r   r   r   r   r   r   r   )r   s   @r   <module>r      s  " 
 !  LM G
G  JJ!  $d#
 
 $
8	Z 	
&T &R $d#  $ 
]AAtD
VAAtD
\AAtD
[AAtDYAAtDVAAuE!  HOO%+<=
4"&
" "X    *V ""r   