pipeline: make tech-log-tree.json the one decomposition contract and enforce it
리뷰 두 건을 반영했다. 계약 - tech-log-tree.json 하나가 분해 계약이자 색인이다. 사람이 읽는 트리·Node Specification· 후보 대장은 없어졌고, 문서에 남아 있던 그 개념을 걷어냈다 - candidateScope — 후보를 찾는 SSOT 범위. 접어 넣은 제2부·제3부는 근거이지 후보가 아니다 - sourceRepository — 분석한 저장소의 경로·리비전·판단 근거. 리비전을 모르면 null 로 두고 지어내지 않는다. 갈래가 여럿이면 revisions - 검사기: 계약 미채택·PENDING·PROMOTE↔글감 양방향·candidateScope·sourceRepository 를 error/warn 으로 센다. 옛 스키마도 검사를 피하지 못한다. 테스트 22 → 31 기록 쓰기 - 템플릿 5종에 source·sourceRevision·topicName, Question 에 닫는 조건, 본문 없는 종류에서 assets 제거. 고정 절 개수 삭제 - check_evidence.mjs — 인용한 코드가 SSOT 에 있는지, 앵커가 SSOT 를 가리키는지, 제목이 계약과 같은지, 리비전이 저장소에 있는지. 게시된 기록에서 SSOT 와 다른 URL 을 잡았다 문체 - 문체 규칙의 정본을 ai-tells.md 로. explaining.md 의 질문체 제목·절 끝 대조 반복·그림 예고 규칙을 삭제해 충돌을 없앴다. 첫 절 「설명 뒤에 평가를 붙이지 않는다」에 지우는 사례 네 유형 - voice 스킬의 「독자 쪽을 본다」를 자료에 오독 기록이 있을 때로 좁히고, 평가만 더한 예시를 교체 - check_prose: 안내 문장을 요구하던 경고 제거, 문장이 끝나지 않은 채 문단이 끝나는 조각 검사 추가 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
73026cada6
commit
9d2a3725c5
@@ -0,0 +1,68 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user