검사: 볼 것이 없어서 통과한 것을 「문제 없음」이라고 쓰지 않는다

R4 · R13 · R6 · R3. 네 가지가 같은 자리를 본다 — 검사기가 대상을 못 찾았을 때 무엇을
내는가.

세 상태를 가른다 (CLAUDE.md 「검사」 절의 표).

  봤고 괜찮다              exit 0   문제 없음 · error 0
  대상이 성립하지 않는다   exit 2   대상이 성립하지 않는다 — <이유>
  볼 것이 아직 없다        exit 0   기록 0건 — 아직 쓴 기록이 없다

R4 — 없는 프로젝트를 주면 여섯 중 넷이 초록을 냈다. `verify-tech-log-tree.py` 는 그것을
「프로젝트 1 · error 0 · PASS」로 셌다 — 오타 한 번이면 검사를 다 돈 것처럼 보인다.
판정은 `techlog.check_targets()` 하나로 모은다. 여섯 곳에 같은 규칙을 따로 쓰면 다음에
하나만 어긋난다. `check_evidence.mjs` 는 이미 exit 2 라 문구만 맞춘다.

R13 — 프로젝트 이름 쪽만 고치면 `--file` 로 오타를 내는 순간 다시 조용히 0건이 된다.
`techlog.check_files()` 로 같은 자리에 둔다. `preview-figure.py` 는 인자가 아예 없을 때
exit 1 을 냈는데 그것도 「대상이 성립하지 않는다」다.

R6 — 두 자리를 함께 고쳐야 했다.
  (a) `verify_projects()` 가 `docs/*/tech-log-studio` 만 훑어 계약 없는 프로젝트가
      목록에서 사라졌다. 기준을 `final/document.md` 로 바꾼다 — SSOT 가 있으면 대상이다.
  (b) `verify-tech-log-tree.py:194` 가 「분해 계약 없음」을 warn 으로 냈다. CLAUDE.md 는
      「계약 미채택도 error 다 — 경고로 두면 옛 스키마로 남아 있는 한 검사를 피한다」고
      적어 두었는데, 경고로 두었더니 실제로 그렇게 됐다.

R3 — `verify-pipeline.py` 가 `check-figure-text.py` 와 `check_evidence.mjs --repo` 를
프로젝트마다 돌린다(`OUTPUT CHECKS`). 게시 전에 돌리라고 적어 둔 검사인데 전체 훑기가
부르지 않아 결함이 있는 채로 PASS 로 보고됐다. `check-required-content.py` 자리는 주석으로
남겨 둔다 — 그 파일이 들어온 뒤에 더한다.

회귀 `scripts/tests/test_no_target.py` 7건 (92 → 99). 「실재하는 경로는 통과한다」 대조를
함께 넣는다 — 무조건 거절로 성공률을 올리는 것이 R-003 이 든 실패다. 판정을 일부러
되돌려 FAILED 가 나는 것을 확인한 뒤 복구했다.

알려진 부채는 그대로 둔다. `verify-pipeline.py` 는 exit 1 이다 — 미준수 런 1건과
`OUTPUT CHECKS` 5건, 그리고 `ca-tmpl` 의 계약 없음이 이제 `TECH LOG TREES` 에서도 보인다.
같은 사실이고 두 번 세지 않는다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wp9jNbePAmWc5jQwCYhK9v
This commit is contained in:
DongHyeonka
2026-09-10 11:07:24 +09:00
co-authored by Claude Opus 5
parent c2742a66cc
commit e00c1a2b76
10 changed files with 342 additions and 9 deletions
+50
View File
@@ -14,6 +14,56 @@ import os
# ── 검사할 것이 없을 때 관문이 무엇을 내야 하는가 ────────────────────────────
# CLAUDE.md 「검사」 절의 표. 「볼 것이 없어서 통과」를 「문제 없음」이라고 쓰지 않는다.
#
# 봤고 괜찮다 exit 0 문제 없음 · error 0
# 대상이 성립하지 않는다 exit 2 대상이 성립하지 않는다 — <이유>
# 볼 것이 아직 없다 exit 0 기록 0건 — 계약의 글감 N개가 아직 안 쓰였다
#
# 가운데는 error 로 센다. 아래는 error 가 아니다 — 아직 안 쓴 것은 결함이 아니다.
# 다만 초록으로 보이면 안 된다.
NO_TARGET_EXIT = 2
def project_root(project: str, root: str) -> str:
return os.path.join(root, "docs", project)
def missing_target(project: str, why: str) -> int:
"""대상이 성립하지 않는다. 규범 문구를 찍고 2 를 돌려준다."""
import sys as _sys
print(f"대상이 성립하지 않는다 — {project}: {why}", file=_sys.stderr)
return NO_TARGET_EXIT
def check_files(paths) -> int | None:
"""`--file` 로 직접 준 경로가 성립하는지 본다. 하나라도 없으면 2, 전부 있으면 None.
프로젝트 이름으로 부르는 쪽만 고치면 `--file` 로 오타를 내는 순간 다시 조용히
0건이 된다. 같은 규칙을 여기 함께 둔다.
"""
for path in paths:
if not os.path.isfile(path):
return missing_target(path, "그런 파일이 없다")
return None
def check_targets(projects, root: str, needs: str = "") -> int | None:
"""지정한 프로젝트들이 성립하는지 본다. 하나라도 아니면 2, 전부 성립하면 None.
`needs` 를 주면 그 하위 경로까지 있어야 성립으로 본다
(예: `tech-log-studio` — 계약이 없는 프로젝트를 걸러 낸다).
"""
for project in projects:
base = project_root(project, root)
if not os.path.isdir(base):
return missing_target(project, "docs 아래 그런 프로젝트가 없다")
if needs and not os.path.exists(os.path.join(base, needs)):
return missing_target(project, f"{needs} 가 없다")
return None
KINDS = ["case", "concept", "reference", "question", "decision"]
READINESS = ["READY", "OPEN", "NEEDS_EVIDENCE", "NEEDS_DECISION", "BLOCKED"]
DISPOSITIONS = ["PROMOTE", "MERGE_INTO", "KEEP_IN_SSOT",