
    i-h                     `   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mZ dZdZ	g dZ
g dZe
g dz   Zh d	Zd
Zg dZg dZd ZdefdZdefdZdedededz  fdZdefdZdee   defdZdee   defdZdee   defdZdedefdZdee   defdZd edefd!Zd" Z d# Z!e"d$k(  r e!        yy)%u  
Phase 4 Validation Script - Marketing System Upgrade
작성자: 프레이야 (프론트엔드)
대상: 마케팅 스킬 업그레이드 검증 5개 항목

Usage:
  python3 validate_marketing_upgrade.py --target all|evals|context|routing|integrity
    [--check-files file1,file2,...]
    [--context-check skill1,skill2,...]
    [--routing-check skill1,skill2,...]
    [--backup-dir /path/to/backup/]
    N)Pathz/home/jay/.claude/skillsz6/home/jay/workspace/backups/marketing-upgrade-20260326)zad-creativecopywritingzcontent-strategyz	seo-auditzai-seozsocial-contentzpaid-adszanalytics-tracking)blog-dominancegeo-optimizerthread-architect	naver-seozcopywriting-promptzchurn-preventionzlaunch-strategyzmarketing-ideas)r   r   r   r   >   idfilesprompt
assertionsexpected_output   )u	   라우팅z	defers tozdefer tozshould deferzcross-referencezRecognizes this aszReferences or defersznot ad creative generationznot creative generationznot an SEO auditznot page copywritingzshould make clear thatzthe right skill forzsee theu   범위를 벗어zoutside content-strategy scopezoutside.*scopeu   → paid-adsu   → ad-creativeu   → email-sequenceu   → seo-auditu   → schema-markupu   → thread-architectu   → geo-optimizeru   → analytics-trackingu   → copywritingu   → blog-dominanceu
   → revopsu   → cold-emailu   → sales-enablement)zCore PrincipleszOutput Formatu   핵심 원칙u   출력 형식c                  .    	 ddl } | S # t        $ r Y yw xY w)u0   PyYAML 임포트 시도, 실패 시 None 반환.r   N)yamlImportError)r   s    </home/jay/workspace/teams/dev2/validate_marketing_upgrade.pytry_import_yamlr   h   s!     s    	contentc                 x    t        j                  d| t         j                        }|sy|j                  d      dfS )uy   
    SKILL.md에서 첫 번째 YAML frontmatter 블록(--- ... ---)을 추출.
    반환: (raw_yaml_str, error_msg)
    z^---\s*\n(.*?)\n---\s*(\n|$))Nu?   YAML frontmatter 블록(--- ... ---)을 찾을 수 없습니다   N)rematchDOTALLgroup)r   ms     r   extract_yaml_frontmatterr   r   s4    
 	0'299EAV771:t    yaml_strc                    t               }|r	 |j                  |       }|i }|dfS i }| j                         }d}|t	        |      k  r||   }t        j                  d|      }|r|j                  d      }|j                  d      j                         }	|	dv rtg }
|dz  }|t	        |      k  rJ||   dk(  s
||   d   d	v r(|
j                  ||   j                                |dz  }nn|t	        |      k  rJd
j                  |
      ||<   t        j                  dd|	      }	|	||<   |dz  }|t	        |      k  r|dfS # t        $ r}dd| fcY d}~S d}~ww xY w)u   
    YAML 파싱. PyYAML 우선, 없으면 정규식 기반 단순 파서 폴백.
    반환: (dict_or_None, error_msg_or_None)
    Nu   yaml.safe_load 실패: r   z"^([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)r      )|z|->z>- ) 	
z^["\']|["\']$)r   	safe_load	Exception
splitlineslenr   r   r   stripappendjoinsub)r   yaml_moddataelinesiliner   keyvalblock_liness              r   parse_yaml_safer8   }   s   
  H	7%%h/D|: D!E	A
c%j.QxHH:DA''!*C''!*""$C,, Q#e*nQx2~q!)C#**58>>+;<Q #e*n !IIk2S	 ff-r37S		Q/ c%j.0 :A  	721#666	7s   E 	EEEEsection_namereturnc                     dt        j                  |       d}t        j                  || t         j                        }|r|j	                  d      j                         S y)uk   
    ## {section_name} 헤딩에서 다음 ## 헤딩까지의 텍스트를 반환.
    없으면 None.
    z(## z\s*\n.*?)(?=\n## |\Z)r   N)r   escapesearchr   r   r+   )r   r9   patternr   s       r   extract_sectionr?      sP    
 bii-..CDG
		'7BII.Awwqz!!r   c                  ^   g } g }g }t         D ]  }t        j                  j                  t        |dd      }t        j                  j                  |      s| j                  d| d|        a	 t        |d      5 }t        j                  |      }ddd       g }d
vr|j                  d       |j                  d      }	t        |	t              s9|j                  d       | j                  d| ddj                  |      z          t        |	      t         k  r%|j                  dt        |	       dt          d       g }
t#        |	      D ]  \  }}t        |t$              s|
j                  d| d       ,t&        t)        |j+                               z
  }|sO|
j                  d| d|j                  dd       dt-        |               |
r#|j                  ddj                  |
      z          d}|	D ]f  }t        |t$              st/        |j                  dd            t/        |j                  dd            z   t1        fdt2        D              sdd} n |s|j                  d| d        |r)| j                  d| dd!j                  |      z          |j                  |        | rd"}d#dj                  |        g}n1|rd$}d%dj                  |       g}nd&}d'dj                  |       g}|r#|j                  d(dj                  |              d)|d!j                  |      d*S # 1 sw Y   xY w# t        j                  $ r"}| j                  d| d|        Y d}~pd}~wt        $ r"}| j                  d| d	|        Y d}~d}~ww xY w)+uB   V1: 8개 마케팅 스킬의 evals/evals.json 스키마를 검증.evalsz
evals.json[u   ] evals.json 파일 없음: utf-8encodingNu   ] JSON 파싱 오류:    ] 파일 읽기 오류: 
skill_nameu   skill_name 필드 없음u.   evals 필드가 배열이 아님 또는 없음] , u   eval 수 부족: u   개 (최소 u   개 필요)zeval[u   ]: dict 아님z](id=r	   ?u   ): 필드 누락 u   필드 누락 - ; Fr   r#   r   c              3   &   K   | ]  }|v  
 y w)N ).0kwtexts     r   	<genexpr>z&verify_evals_schema.<locals>.<genexpr>   s     9"2:9s   Tuu   ] 경계 테스트(라우팅) eval 없음 (expected_output 또는 assertions에 '라우팅'/'defers to'/'→' 없음) | FAILu   FAIL 스킬: WARNu   PASS 스킬: PASSu   모든 스킬 통과: WARN: V1_evals_schemaverifierstatusdetails)EVAL_TARGET_SKILLSospathr-   SKILLS_BASEexistsr,   openjsonloadJSONDecodeErrorOSErrorget
isinstancelistr*   EVAL_MIN_COUNT	enumeratedictEVAL_REQUIRED_FIELDSsetkeyssortedstranyROUTING_KEYWORDS)issueswarningspassedskill	eval_pathfr0   r1   skill_errorsrA   missing_fields_evalsidxevmissinghas_routing_evalrZ   detail_partsrP   s                    @r   verify_evals_schemar      s   FHF# C!GGLLeWlK	 ww~~i(MMAeW$@LM	i'2 $ayy|$  t# :; !%& PQMMAeWB-$))L*AAB u:&"3CJ<|NK[[f gh  " ' 	tGCb$'$++eC5,GH*S^;G$++eC5bffT#>N=OO`aghoap`q,rs	t   2TYY?S5T TU ! 	Bb$'rvv/45BFF<QS<T8UUD9(899#' 	  OOE7 ^ _
 MMAeWB-%**\*BBCMM% GC!L '		&(9':;<	'		&(9':;<061B0CDEfTYYx%8$9:; &::l+ S$ $## 	MMAeW$:1#>? 	MMAeW$<QC@A	s<   0M=MMM	MN,!M>>N,
N''N,check_filesc                    | sddddS g }g }| D ]g  }|j                         }t        j                  j                  |      s|j	                  | d       H	 t        |d      5 }|j                         }ddd       t              \  }}|r|j	                  | d
|        |J t        |      \  }	}
|
s|	|j	                  | d|
        d|	vr|j	                  | d       |	d   }|r t        |t              r&|j                         s|j	                  | d       |j	                  t        j                  j                  t        j                  j                  |            xs |       j |r0d}ddj                  |       }|r:|ddj                  |       z  }n"d}dt        |       ddj                  |       }d||dS # 1 sw Y   `xY w# t        $ r!}|j	                  | d	|        Y d}~d}~ww xY w)uC   V2: 지정된 SKILL.md 파일의 YAML frontmatter 유효성 검증.V2_yaml_validrT   u@   --check-files 옵션이 제공되지 않아 검증 대상 없음rX   u   : 파일 없음rC   rD   Nu   : 읽기 오류 - z: u   : YAML 파싱 실패 - descriptionu   : description 필드 없음u%   : description 필드가 비어 있음rS   u   실패: rK       | 통과: rI   rU   u   모든 파일 통과 (u   개): )r+   r]   r^   r`   r,   ra   readre   r   r8   rg   rp   basenamedirnamer-   r*   )r   rs   ru   fpathrx   r   r1   r   errr0   	parse_errdescrZ   r[   s                 r   verify_yaml_frontmatterr   '  s*   'Y
 	
 FF $Iww~~e$MMUG?34	eg. #!&&(# 19#MMUG2cU+, ###)(3iMMUG#:9+FG $MMUG#>?@'
4-djjlMMUG#HIJbgg&&rwwu'=>G%HI$IL TYYv./0TYYv%6$788G*3v;-vdii>O=PQ $ Q# # 	MMUG#5aS9:	s0   G!%G6G!G	G!!	H*HHcontext_skillsc                    g }g }| D ]  }|j                         }t        j                  j                  t        |d      }t        j                  j                  |      s|j                  d| d|        o	 t        |d      5 }|j                         }ddd       dvr|j                  d| d	       |j                  |        |r0d
}ddj                  |       }	|r.|	ddj                  |       z  }	nd}ddj                  |       }	d||	dS # 1 sw Y   xY w# t        $ r"}|j                  d| d|        Y d}~Cd}~ww xY w)uI   V3: 지정 스킬의 SKILL.md에 product-marketing-context 참조 확인.SKILL.mdrB   u   ] SKILL.md 없음: rC   rD   N   ] 읽기 오류: zproduct-marketing-contextu.   ] 'product-marketing-context' 문자열 없음rS   u   참조 누락: rK   r   rI   rU   u:   모든 스킬에 product-marketing-context 참조 존재: V3_context_refrX   )
r+   r]   r^   r-   r_   r`   r,   ra   r   re   )
r   rs   ru   rv   
skill_pathrx   r   r1   rZ   r[   s
             r   verify_context_referencer   n  sa   FF !WW\\+ujA
ww~~j)MMAeW$7
|DE	j73 #q&&(# 'g5MMAeW$RSTMM% %!( #DIIf$5#67TYYv%6$788GNtyyY_O`Nab % )# # 	MMAeW$5aS9:	s0   7D$DD$D!	D$$	E-E

Erouting_skillsc           
      z   g }g }g }t               }t        j                  j                  t              rJt        j
                  t              D ].  }|j                         s|j                  |j                         0 t        j                  d      }| D ]i  }|j                         }t        j                  j                  t        |d      }t        j                  j                  |      s|j                  d| d       n	 t        |d      5 }	|	j!                         }
ddd       t%        
      \  }}|r|n|
}|j'                  |      }|s|j                  |       g }|D ]'  }t)        |      d	k  r||vs|j                  |       ) |r(|j                  d| d
dj                  |              &|r1|j                  | ddj                  t        |             d       Y|j                  |       l |r0d}ddj                  |       }|r`|ddj                  |       z  }nH|r0d}ddj                  |       }|r.|ddj                  |       z  }nd}ddj                  |       }d||dS # 1 sw Y   bxY w# t"        $ r"}|j                  d| d|        Y d}~ d}~ww xY w)uV   V4: description의 → <skill-name> 패턴이 실제 존재하는 스킬인지 확인.u   →\s*([a-z][a-z0-9-]{1,})r   rB   u   ] SKILL.md 없음 (스킵)rC   rD   Nr      u(   ] 존재하지 않는 라우팅 타겟: rI   u   (→,)rS   u   깨진 라우팅: rK   r   rT   rV   rU   u   모든 라우팅 유효: V4_routing_consistencyrX   )rm   r]   r^   isdirr_   scandiris_diraddnamer   compiler+   r-   r`   r,   ra   r   re   r   findallr*   )r   rs   rt   ru   existing_skillsentryrouting_patternrv   r   rx   r   r1   r   _search_targettargetsbrokentargetrZ   r[   s                       r   verify_routing_consistencyr     s   FHF !$O	ww}}[!ZZ, 	0E||~##EJJ/	0
 jj!>?O )%WW\\+ujA
ww~~j)OOaw&@AB	j73 #q&&(# /w7!$,'!))-8MM%  	&F6{Q_,f%	& MMAeW$LTYYW]M^L_`atCHHS\,B+C1EFe$S)%V &tyy'8&9:TYYv%6$788G	499X./0TYYv%6$788G-dii.?-@A - a# # 	MMAeW$5aS9:	s0   JJ"JJ	J	J:J55J:
backup_dirc                 `   t         j                  j                  |       s	ddd|  dS g }g }g }g }	 t        j                  |       D ].  }|j	                         s|j                  |j                         0 	 |s	ddd|  dS t        |      D ]  }t         j                  j                  | |d	      }t         j                  j                  t        |d	      }	t         j                  j                  |      sjt         j                  j                  |	      s|j                  d
| d       	 t        |d      5 }
|
j                         }ddd       t        |	d      5 }
|
j                         }ddd       d}g }t        D ]  }t        |      }t        |      }|| d}|||j                  d| d       <|||j                  d| d       V|Y|\||k7  sbdt         dt         fd} ||       ||      k7  s|j                  d| d        |st#              \  }}t#              \  }}|r|rt%        |      \  }}t%        |      \  }}|rf|rdt!        |j'                  dd            }t!        |j'                  dd            }||k7  r|j                  | d       $|j                  |       7|j                  |       J|j                  |       ]|r)|j                  d
| ddj                  |      z          |j                  |        g }|r&d}|j                  ddj                  |              nd}|r#|j                  d d!j                  |              |r#|j                  d"d!j                  |              |s|j                  d#       d|dj                  |      dS # t        $ r}ddd| dcY d}~S d}~ww xY w# 1 sw Y   xY w# 1 sw Y   xY w# t        $ r"}|j                  d
| d|        Y d}~d}~ww xY w)$uA   V5: 백업본 대비 SKILL.md 핵심 섹션 변경 여부 확인.V5_core_integrityrS   u   백업 디렉토리 없음: rX   u#   백업 디렉토리 스캔 오류: NrT   u&   백업 디렉토리에 스킬 없음: r   rB   u.   ] 현재 SKILL.md 없음 (백업에는 존재)rC   rD   rF   FT'u   ' 섹션 새로 추가됨u   ' 섹션 삭제됨rP   r:   c                 L    t        j                  dd|       j                         S )Nz\s+r$   )r   r.   r+   )rP   s    r   	normalizez(verify_core_integrity.<locals>.normalize:  s    66&#t4::<<r   u   ' 내용 변경됨r   r#   u   (desc변경)rH   rR   u   변경 감지: rK   rU   u   무결성 통과: rI   u   핵심 섹션 없음(스킵): u   검증 대상 없음)r]   r^   r   r   r   r,   r   re   ro   r-   r_   r`   ra   r   INTEGRITY_SECTIONSr?   rp   r   r8   rf   )r   rs   ru   no_section_skillsbackup_skillsr   r1   rv   backup_skill_pathcurrent_skill_pathrx   backup_contentcurrent_contentskill_has_any_sectionskill_changedsection
backup_seccurrent_secr   backup_yamlr   current_yamlbackup_datacurrent_datab_descc_descr   rZ   s                               r   verify_core_integrityr     sQ   77==$+5j\B
 	
 FF M	
ZZ
+ 	1E||~$$UZZ0	1 +?
|L
 	
 & E!GGLLUJGWW\\+ujIww~~/0ww~~01MMAeW$RST	'': *a!"*(7; +q"#&&(+ !&) 	JG(AJ)/7CK!k&9$(!!k&=$$q	1J%KL'K,?$$q	1C%DE'K,C
VaHa=C =C = Z(Ik,BB!((1WI5G)HI'	J, %5nENK6GOL!|!0!=Q"1,"?a< !CDF !1!1-!DEF')00E7,1GH)007%,,U3!((/MMAeWB-%**]*CCDMM% KE!P Lodii.?-@AB061B0CDE<TYYGX=Y<Z[\23 (::l+ I  
+<QC@
 	

6* *+ + 	MMAeW$<QC@A	sk   (O	 O	 /P<O(P"O53P		O%O O% O%(O2	-P5O?	:P	P-P((P-resultsc                     | D cg c]  }|d   	 }}d|v rd}n	d|v rd}nd}g }| D ]2  }|d   j                  d      d   }|j                  | d|d           4 || d	j                  |      d
S c c}w )u;   결과 집계: overall 상태 및 summary 문자열 생성.rZ   rS   rT   rU   rY   r   r   :r$   )overallr   summary)splitr,   r-   )r   rstatusesr   summary_partscodes         r   aggregate_resultsr   t  s    %,-(-H-	8	 M 6}""3'*vQq{m456 88M* # .s   A1r   c                 (    | dk(  ry| dk(  ry| dk(  ryy)z+exit code: 0=all pass, 1=any fail, 2=error.rU   r   rT   rS   r   r    rM   )r   s    r   determine_exit_coder     s&    &	F		F	r   c                  T   t        j                  dt         j                  d      } | j                  ddg dd       | j                  d	d
d       | j                  dd
d       | j                  dd
d       | j                  dt        dt         d       | j                         S )Nu5   Phase 4 마케팅 업그레이드 검증 스크립트u  
Target 옵션:
  all        - 모든 검증 실행 (V1~V5)
  evals      - V1: evals.json 스키마 검증
  yaml       - V2: description YAML frontmatter 유효성
  context    - V3: Before Starting 참조 확인
  routing    - V4: 라우팅 패턴 일관성 검증
  integrity  - V5: 핵심 로직 변경 무결성

예시:
  python3 validate_marketing_upgrade.py --target all
  python3 validate_marketing_upgrade.py --target yaml --check-files /path/to/SKILL.md
  python3 validate_marketing_upgrade.py --target context --context-check blog-dominance,geo-optimizer
  python3 validate_marketing_upgrade.py --target routing --routing-check ad-creative,copywriting
  python3 validate_marketing_upgrade.py --target integrity --backup-dir /path/to/backup/
        )r   formatter_classepilogz--targetT)allrA   r   contextrouting	integrityu   실행할 검증 대상)requiredchoiceshelpz--check-filesr#   u7   V2 검증 대상 SKILL.md 파일 목록 (쉼표 구분))defaultr   z--context-checkuo   V3 검증 대상 스킬 목록 (쉼표 구분, 기본: blog-dominance,geo-optimizer,thread-architect,naver-seo)z--routing-checkuO   V4 검증 대상 스킬 목록 (쉼표 구분, 기본: 전체 마케팅 스킬)z--backup-diru    V5 백업 디렉토리 (기본: r   )argparseArgumentParserRawDescriptionHelpFormatteradd_argumentBACKUP_DIR_DEFAULT
parse_args)parsers    r   r   r     s    $$K <<F( K&	   F  
 ~  
 ^  
 "/0B/C1E  
 r   c                     	 t               }  j                  rH| j                  j                  d      D cg c]#  }|j                         s|j                         % c}ng }| j                  rH| j                  j                  d      D cg c]#  }|j                         s|j                         % c}nt        }| j                  rH| j                  j                  d      D cg c]#  }|j                         s|j                         % c}nt        }| j                  }| j                  }g }		 |dv r|	j!                  t#                      |dv r|	j!                  t%        |             |dv r|	j!                  t'        |             |dv r|	j!                  t)        |             |dv r|	j!                  t+        |             t5        |	      }t/        t1        j2                  |dd             t        j                  t7        |d                y # t        $ rM}t        j                  t	        |      j                         rt        t	        |            nd       Y d }~Yd }~ww xY wc c}w c c}w c c}w # t,        $ rT}dg d	| t	        |      d
}
t/        t1        j2                  |
dd             t        j                  d       Y d }~d }~ww xY w)Nr    r   )r   rA   )r   r   )r   r   )r   r   )r   r   ERRORu   예상치 못한 오류: )r   r   r   errorF)ensure_asciiindentr   )r   
SystemExitsysexitrp   isdigitintr   r   r+   context_checkCONTEXT_CHECK_SKILLS_DEFAULTrouting_checkROUTING_CHECK_SKILLS_DEFAULTr   r   r,   r   r   r   r   r   r(   printrb   dumpsr   r   )argsr1   rx   r   sr   r   r   r   r   error_outputoutputs               r   mainr     sM   9|
 TXScScd&6&6&<&<S&AOQWWY1779OikK  !..44S9GqQWWYG)   !..44S9GqQWWYG) 
 J [[FG%%NN.01_$NN2;?@''NN3NCD''NN5nEF))NN0<= w'F 
$**V%
:; HH 	!234o  9A 0SVa889 PG
 	H4  	 21#6V	
 	djjE!DE	sU   
H I-I-I2!I2%I7;I71BI< 	I*AI%%I*<	KA	KK__main__)#__doc__r   rb   r]   r   r   pathlibr   r_   r   r\   r   r   rl   ri   rr   r   r   rp   r   r8   r?   rk   r   rh   r   r   r   r   r   r   r   r   r   __name__rM   r   r   <module>r      sc     	 	 
  )M 	 	    2 5    R  @ c ,c ,h	S 	 	d
 	"]T ]J?c ?t ?N&T#Y &4 &\MtCy MT Mj{c {d {FtDz d 4	 	 	"/d:5z zF r   