Files
Agentic-AI-Template/Writing/Gemini/scripts/validate_docs.py
T
2026-04-28 01:30:16 +09:00

197 lines
5.1 KiB
Python

#!/usr/bin/env python3
"""
Basic validation for the Markdown document harness template.
This check is intentionally lightweight: it verifies that the template files
exist and keep the sections that later Harness steps depend on.
"""
import json
from pathlib import Path
import sys
try:
import tomllib
except ModuleNotFoundError: # pragma: no cover - Python < 3.11 compatibility
tomllib = None
ROOT = Path(__file__).resolve().parent.parent
REQUIRED_FILES = [
"README.md",
"AGENTS.md",
"docs/PRD.md",
"docs/ResearchNote.md",
"docs/DraftFeedback.md",
"docs/FinalFeedback.md",
"docs/ARCHITECTURE.md",
"docs/ADR.md",
"docs/UI_GUIDE.md",
".agents/skills/document-harness/SKILL.md",
".agents/skills/document-harness/references/phase-templates.md",
".agents/skills/document-review/SKILL.md",
".codex/config.toml",
".codex/hooks.json",
".codex/hooks/pre_tool_guard.py",
".codex/hooks/stop_validate.py",
".codex/agents/doc_researcher.toml",
".codex/agents/doc_drafter.toml",
".codex/agents/doc_reviewer.toml",
".codex/agents/evidence_checker.toml",
]
REQUIRED_DIRS = [
"docs",
"scripts",
".agents",
".agents/skills",
".agents/skills/document-harness",
".agents/skills/document-review",
".codex",
".codex/hooks",
".codex/agents",
]
REQUIRED_SECTIONS = {
"README.md": [
"## 핵심 아이디어",
"## Codex 구성",
"## 빠른 시작",
"## 자동 실행 방식",
"## 피드백 게이트",
"## 검증",
],
"docs/PRD.md": [
"## 문서 목적",
"## 대상 독자",
"## 최종 산출물",
"## 문서 개요",
"## 중요 키워드",
"## 핵심 질문",
"## 범위",
"## 톤과 스타일",
"## 조사 요구사항",
"## 사용자 피드백 방식",
],
"docs/ResearchNote.md": [
"## 조사 범위",
"## 조사 일시",
"## 검색어",
"## 핵심 결론",
"## 출처 목록",
"## 쟁점과 상반된 주장",
"## 확인 필요",
],
"AGENTS.md": [
"## 목적",
"## Codex 구성",
"## 기본 산출물",
"## 문서 작성 규칙",
"## Codex 작업 규칙",
"## 권장 워크플로우",
"## 명령어",
],
".agents/skills/document-harness/SKILL.md": [
"# Document Harness Skill",
"## Operating Rules",
"## Staged Workflow",
"## Validation",
],
".agents/skills/document-review/SKILL.md": [
"# Document Review Skill",
"## Read First",
"## Review Checklist",
"## Output Format",
],
}
REQUIRED_JSON_FILES = [
".codex/hooks.json",
]
REQUIRED_TOML_FILES = [
".codex/config.toml",
".codex/agents/doc_researcher.toml",
".codex/agents/doc_drafter.toml",
".codex/agents/doc_reviewer.toml",
".codex/agents/evidence_checker.toml",
]
def first_nonempty_line(path: Path) -> str:
for line in path.read_text(encoding="utf-8").splitlines():
if line.strip():
return line.strip()
return ""
def markdown_file_has_valid_start(path: Path) -> bool:
first = first_nonempty_line(path)
if first.startswith("# "):
return True
if first == "---" and path.name == "SKILL.md":
return True
return False
def main() -> int:
errors: list[str] = []
for rel in REQUIRED_DIRS:
path = ROOT / rel
if not path.is_dir():
errors.append(f"missing directory: {rel}")
for rel in REQUIRED_FILES:
path = ROOT / rel
if not path.is_file():
errors.append(f"missing file: {rel}")
continue
if path.suffix == ".md":
if not markdown_file_has_valid_start(path):
errors.append(f"markdown file must start with a level-1 heading or Skill frontmatter: {rel}")
for rel, sections in REQUIRED_SECTIONS.items():
path = ROOT / rel
if not path.is_file():
continue
text = path.read_text(encoding="utf-8")
for section in sections:
if section not in text:
errors.append(f"missing section in {rel}: {section}")
for rel in REQUIRED_JSON_FILES:
path = ROOT / rel
if not path.is_file():
continue
try:
json.loads(path.read_text(encoding="utf-8"))
except json.JSONDecodeError as exc:
errors.append(f"invalid JSON in {rel}: {exc}")
if tomllib is not None:
for rel in REQUIRED_TOML_FILES:
path = ROOT / rel
if not path.is_file():
continue
try:
tomllib.loads(path.read_text(encoding="utf-8"))
except tomllib.TOMLDecodeError as exc:
errors.append(f"invalid TOML in {rel}: {exc}")
if errors:
print("Document harness validation failed:")
for error in errors:
print(f"- {error}")
return 1
print("Document harness validation passed.")
return 0
if __name__ == "__main__":
sys.exit(main())