#!/usr/bin/env python3 """Tech Log 파이프라인이 함께 쓰는 어휘와 보고 형식. `tech-log-tree.json` 이 프로젝트의 분해 계약이자 색인이고 정본이다. 만드는 쪽 (`build-tech-log-tree.py`)과 검사하는 쪽(`verify-tech-log-tree.py`)이 같은 값을 쓰도록 여기 모은다. """ from __future__ import annotations import collections import hashlib import json import os KINDS = ["case", "concept", "reference", "question", "decision"] READINESS = ["READY", "OPEN", "NEEDS_EVIDENCE", "NEEDS_DECISION", "BLOCKED"] DISPOSITIONS = ["PROMOTE", "MERGE_INTO", "KEEP_IN_SSOT", "NEEDS_EVIDENCE", "NEEDS_DECISION", "BLOCKED"] def sha256_of(path: str) -> str | None: if not os.path.exists(path): return None return hashlib.sha256(open(path, "rb").read()).hexdigest() def load_index(path: str) -> dict | None: """`tech-log-tree.json` 을 읽는다. 없으면 None.""" if not os.path.exists(path): return None return json.load(open(path, encoding="utf-8")) def nodes(index: dict): """(주제 slug, 종류, 노드) 를 차례로 낸다.""" for slug, topic in (index.get("topics") or {}).items(): for kind, items in (topic.get("kinds") or {}).items(): for node in items: yield slug, kind, node class Report: """검사기가 규칙별로 모아 내는 결과. 한 규칙에 수백 건이 걸리는 것이 정상이라 개별 줄이 아니라 규칙으로 센다. error 는 계약 위반이고 warn 은 편집 판단이 필요한 자리다. """ def __init__(self, project: str) -> None: self.project = project self.errors: dict[str, list[str]] = collections.defaultdict(list) self.warns: dict[str, list[str]] = collections.defaultdict(list) self.facts: dict[str, object] = {} def error(self, rule: str, detail: str = "") -> None: self.errors[rule].append(detail) def warn(self, rule: str, detail: str = "") -> None: self.warns[rule].append(detail) @property def error_count(self) -> int: return sum(len(v) for v in self.errors.values()) @property def warn_count(self) -> int: return sum(len(v) for v in self.warns.values())