refactor: 문서 개선 중

This commit is contained in:
donghyeon-ka
2026-09-21 14:30:55 +09:00
parent c93cdea150
commit 805a18f486
1497 changed files with 525837 additions and 59152 deletions
+103
View File
@@ -50,6 +50,13 @@ REQUIRED_PATHS = (
".agents/skills/writing-practitioner-guides/references/linux-systemd.md",
".agents/skills/writing-practitioner-guides/references/networking-tls.md",
".agents/skills/writing-practitioner-guides/references/datastores.md",
".agents/skills/writing-practitioner-guides/references/command-pedagogy.md",
".agents/skills/running-tech-log-pipeline/policies/command-authoring.yaml",
".agents/skills/running-tech-log-pipeline/schemas/command-plan.schema.json",
".agents/skills/running-tech-log-pipeline/schemas/command-patch-set.schema.json",
".agents/skills/running-tech-log-pipeline/contracts/command-pedagogy-planner.md",
".agents/skills/running-tech-log-pipeline/contracts/command-pedagogy-editor.md",
".agents/skills/running-tech-log-pipeline/contracts/command-pedagogy-reviewer.md",
# 역할이 나뉜 서브에이전트. 한 세션이 쓰기와 검증을 겸하면 자동 검사가 전부 통과한
# 상태로 사실 오류가 새어 나간다 — 실제로 그렇게 새어 나간 것이 이 저장소에 있었다.
#
@@ -68,6 +75,9 @@ REQUIRED_PATHS = (
".claude/agents/reader-reviewer.md",
".claude/agents/setup-runner.md",
".claude/agents/studio-validator.md",
".claude/agents/command-pedagogy-planner.md",
".claude/agents/command-pedagogy-editor.md",
".claude/agents/command-pedagogy-reviewer.md",
# 프로젝트 폴더 틀 — 끝난 프로젝트의 모양. 작업 재료는 여기 없다
"docs/_templates/README.md",
"docs/_templates/final/document.md",
@@ -91,6 +101,10 @@ REQUIRED_PATHS = (
"scripts/techlog.py",
"scripts/verify-tech-log-tree.py",
"scripts/verify-pipeline-run.py",
"scripts/command_pedagogy.py",
"scripts/check-command-pedagogy.py",
"scripts/apply-command-pedagogy-patch.py",
"scripts/validate-command-pedagogy-artifact.py",
"scripts/check-figure-overlap.py",
"scripts/verify-project-layout.py",
"scripts/fold-analysis-into-final.py",
@@ -375,6 +389,11 @@ def verify_outputs(shared_root: Path) -> list:
except (OSError, subprocess.TimeoutExpired) as exc:
rep.error(f"{name} 을 돌리지 못했다", str(exc)[:120])
continue
if name == "check_evidence --repo" and run.returncode == 3:
rep.facts[name] = "UNVERIFIABLE (exit 3)"
rep.warn(f"{name} 대조 불가",
_last_meaningful_line(run.stdout or run.stderr))
continue
rep.facts[name] = f"exit {run.returncode}"
if run.returncode != 0:
rep.error(f"{name} 이 실패했다",
@@ -383,6 +402,81 @@ def verify_outputs(shared_root: Path) -> list:
return out
def verify_command_corpus(shared_root: Path) -> list:
"""기존 SSOT/기록에도 command-pedagogy analyzer를 읽기 전용으로 적용한다.
새 run 계약만 좋아지고 기존 문서 debt가 영원히 보이지 않는 것을 막는 dogfooding lane이다.
기존 문서를 자동 수정하지 않으며, mode를 확정할 근거가 없는 문서는 reference를 기본으로
읽고 명시적인 command-mode marker가 개별 block의 mode를 덮어쓴다.
"""
analyzer = _load(shared_root, "command_pedagogy.py", "command_pedagogy_corpus")
if analyzer is None:
return []
reports = []
for project_dir in sorted((shared_root / "docs").iterdir()):
if not project_dir.is_dir() or project_dir.name.startswith(("_", ".")):
continue
documents = []
ssot = project_dir / "final" / "document.md"
if ssot.exists():
documents.append(ssot)
studio = project_dir / "tech-log-studio"
if studio.exists():
documents.extend(sorted(studio.rglob("*.md")))
if not documents:
continue
rep = _OutputReport(project_dir.name)
shell_blocks = 0
findings = 0
command_like_text = 0
unclassified = 0
for path in documents:
try:
content = path.read_text(encoding="utf-8")
except OSError as exc:
rep.warn("command corpus 문서를 읽지 못했다",
f"{path.relative_to(shared_root)}{exc}")
continue
rel = str(path.relative_to(shared_root))
result = analyzer.analyze_commands(rel, content, mode="reference")
blocks = result.get("blocks") or []
hits = result.get("findings") or []
text_hits = ((result.get("extensions") or {})
.get("command_like_text_blocks") or [])
shell_blocks += len(blocks)
findings += len(hits)
command_like_text += len(text_hits)
unclassified_blocks = [
block for block in blocks if not block.get("mode_explicit")
]
if unclassified_blocks:
unclassified += len(unclassified_blocks)
rep.warn("기존 shell block의 mode가 명시되지 않았다",
f"{rel}{len(unclassified_blocks)} block")
for finding in hits:
rep.warn(
"command-pedagogy finding",
f"{rel}{finding.get('code')}{finding.get('evidence', '')}",
)
for hit in text_hits:
evidence = str(hit.get("evidence") or "").splitlines()
preview = evidence[0][:90] if evidence else ""
rep.warn(
"command처럼 보이는 text fence를 분류해야 한다",
f"{rel}{preview}",
)
rep.facts["shell blocks"] = shell_blocks
rep.facts["findings"] = findings
rep.facts["mode 미분류"] = unclassified
rep.facts["command-like text"] = command_like_text
reports.append(rep)
return reports
def verify_runs(shared_root: Path):
"""`runs/<프로젝트>/<runId>/run.json` 이 절차를 지켰는지 본다.
@@ -449,6 +543,7 @@ def main() -> int:
layouts = [] if args.skip_projects else verify_layouts(args.shared_root)
runs = [] if args.skip_projects else verify_runs(args.shared_root)
outputs = [] if args.skip_projects else verify_outputs(args.shared_root)
command_corpus = [] if args.skip_projects else verify_command_corpus(args.shared_root)
coverage = [] if args.skip_projects else verify_run_coverage(args.shared_root)
project_errors = (sum(r.error_count for r in reports)
+ sum(r.error_count for r in layouts)
@@ -510,6 +605,14 @@ def main() -> int:
for report in outputs:
verifier_render(report, args.samples)
if command_corpus:
command_warns = sum(r.warn_count for r in command_corpus)
print()
print(f"COMMAND PEDAGOGY CORPUS: {'WARN' if command_warns else 'PASS'}"
f" — 프로젝트 {len(command_corpus)} · warn {command_warns}")
for report in command_corpus:
verifier_render(report, args.samples)
if coverage:
total = sum(int(r.facts.get("기록") or 0) for r in coverage)
cov = sum(int(r.facts.get("원장이 덮은 기록") or 0) for r in coverage)