Files
document-haness/scripts/tests/test_core_support.py
T
DongHyeonkaandClaude Opus 5 87d70e7c80 feat(check-preservation): 편집이 핵심 칸에 측정 주장을 새로 더했는지 본다
check-core-support 는 근거 목록이 비었는데 측정을 주장하는 것을 본다. 근거가 차 있는
기록에 그 근거가 지지하지 않는 결론을 더하는 편집은 그 규칙 밖이다 — 출처가 있다는 것과
그 출처가 그 주장을 지지한다는 것은 다르다.

「측정 주장이 있으면 경고」로 가지 않았다. 그건 저장소의 정상 기록 다수에 걸리고, 모든
기록에 걸리는 경고는 어느 기록에 대해서도 아무 말을 하지 않는다. 편집이 핵심 칸에 측정
주장을 새로 더했는지만 본다 — 적용 범위 검출과 같은 모양이고 더해진 것을 본다.

판정은 check-core-support 의 목록을 그대로 불러 쓴다. 두 곳에 두면 갈린다.

판정하지 않는다. 근거가 이 주장을 지지하는지는 근거를 읽어야 알고, 그것은 검토의 몫이다.

채택 편집 100쌍에 한 번도 안 걸린다. 그 쌍들은 문제·결론 칸이 없는 조각이라 대상이
아니고, 회귀에 그것도 넣었다.

곁들여 check-core-support 에 --file 을 더했다. 다른 검사기들이 이미 받은 것이다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4vKjQo9KKBBokzxqXLCfk
2026-09-10 17:40:02 +09:00

154 lines
5.3 KiB
Python

"""글의 핵심이 측정을 주장하는데 근거 목록이 비었는지 보는 검사기.
후보를 둘 버리고 셋째가 맞았다. 그 과정을 회귀로 남긴다 — 다음 사람이 되돌리면 잡힌다.
"""
import importlib.util
import os
import subprocess
import tempfile
import unittest
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
TOOL = os.path.join(ROOT, "scripts", "check-core-support.py")
_spec = importlib.util.spec_from_file_location("check_core_support", TOOL)
cs = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(cs)
HEAD = """---
kind: CASE
slug: x
title: x
sourceRevision: 0000000000000000000000000000000000000000
{evidence}---
# x
요약 문단이다.
## 관계
- **다른 기록**
왜 관계인지.
## 문제
{problem}
## 결론
{conclusion}
## 검증 환경
python 3.12.3
## 재현 조건
1. 돌린다.
## 본문
<!-- body:start -->
## x
본문이다.
<!-- body:end -->
"""
def _record(tmp, conclusion, problem="느렸다.", evidence=False):
ev = "evidence:\n - ../../../final/evidence/raw/x.txt\n" if evidence else ""
path = os.path.join(tmp, "r.md")
open(path, "w", encoding="utf-8").write(
HEAD.format(evidence=ev, problem=problem, conclusion=conclusion))
return path
def _errors(path):
rep = cs.techlog.Report("t")
cs.check_record(path, rep)
return rep
class CoreSupportTest(unittest.TestCase):
CLAIM = "프로젝션 전환으로 운영 피드 응답시간의 p99 가 절반이 됐다."
def setUp(self):
self.tmp = tempfile.mkdtemp()
def test_the_case_is_caught(self):
"""`B2-core-unsupported` 의 문장 그대로다. 아라비아 숫자가 하나도 없다."""
rep = _errors(_record(self.tmp, self.CLAIM))
self.assertEqual(1, rep.error_count)
self.assertIn("핵심이 측정을 주장하는데 근거 목록이 비었다", " / ".join(rep.errors))
def test_the_same_claim_with_evidence_passes(self):
"""근거를 대면 통과한다. 막는 것은 주장이 아니라 **근거 없는 주장**이다."""
self.assertEqual(0, _errors(_record(self.tmp, self.CLAIM, evidence=True)).error_count)
def test_a_comparison_word_alone_is_not_a_measurement(self):
"""「절반」은 한국어에서 측정이 아닌 쓰임이 흔하다.
저장소의 기록 275건에 비교 낱말만으로 걸었더니 둘이 걸렸고 **둘 다 이 모양**이었다.
성능을 재는 명사와 함께 나올 때만 측정 주장으로 본다.
"""
for text in ("서빙 계약의 절반은 라우트 레지스트리에서 유도하고 있었다.",
"사람이 기억해서 돌리는 가드는 절반만 존재합니다.",
"두 배로 늘어난 파일을 나눠 담았다."):
with self.subTest(text=text):
self.assertEqual(0, _errors(_record(self.tmp, text)).error_count)
def test_a_performance_noun_alone_is_not_a_claim(self):
"""재는 이름만 나오고 견주는 말이 없으면 측정 주장이 아니다."""
self.assertEqual(
0, _errors(_record(self.tmp, "응답 시간을 함께 적어 둔다.")).error_count)
def test_kinds_other_than_case_are_not_checked(self):
"""`문제`·`결론` 을 가진 종류는 CASE 뿐이다."""
path = _record(self.tmp, self.CLAIM)
text = open(path, encoding="utf-8").read().replace("kind: CASE", "kind: CONCEPT")
open(path, "w", encoding="utf-8").write(text)
self.assertEqual(0, _errors(path).error_count)
def test_every_record_in_this_repository_passes(self):
"""대조군. `문제`·`결론` 에 수치를 적는 건 정상 기록이 늘 하는 일이라
여기가 과잉 차단이 가장 나기 쉬운 자리다."""
p = subprocess.run(["python3", TOOL, "--samples", "1"], cwd=ROOT,
capture_output=True, text=True)
self.assertEqual(0, p.returncode, p.stdout + p.stderr)
def test_a_missing_project_is_not_reported_as_clean(self):
p = subprocess.run(["python3", TOOL, "nosuchxyz"], cwd=ROOT,
capture_output=True, text=True)
self.assertEqual(2, p.returncode)
if __name__ == "__main__":
unittest.main()
class FileModeTest(unittest.TestCase):
"""`--file` 로 기록을 직접 준다. 다른 검사기들이 R13 에서 받은 것과 맞춘다."""
def _cli(self, *args):
return subprocess.run(["python3", TOOL, *args], cwd=ROOT,
capture_output=True, text=True)
def setUp(self):
self.tmp = tempfile.mkdtemp()
def test_a_record_given_directly_is_checked(self):
bad = _record(self.tmp, "운영 피드 응답시간의 p99 가 절반이 됐다.")
self.assertEqual(1, self._cli("--file", bad).returncode)
def test_a_record_with_evidence_passes_in_file_mode(self):
ok = _record(self.tmp, "운영 피드 응답시간의 p99 가 절반이 됐다.", evidence=True)
self.assertEqual(0, self._cli("--file", ok).returncode)
def test_a_missing_file_is_not_reported_as_clean(self):
p = self._cli("--file", os.path.join(self.tmp, "nope.md"))
self.assertEqual(2, p.returncode)
self.assertIn("대상이 성립하지 않는다", p.stderr)