refactor: 문서 개선 중
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user