feat(scripts): 핵심이 측정을 주장하는데 근거 목록이 비었는지 본다
후보를 둘 버렸다. 처음에 「핵심의 수치가 근거에 없으면」으로 만들었는데 사례에 아라비아 숫자가 하나도 없다 — 「응답시간의 p99 가 절반이 됐다」. 275건에 돌려 오탐 0 · 검출 0 이었다. 두 번째는 비교 낱말만 봤다. 275건에서 둘이 걸렸고 둘 다 오탐이었다 — 「계약의 절반은 라우트 레지스트리에서 유도한다」·「가드는 절반만 존재합니다」. 한국어에서 「절반」은 측정이 아닌 쓰임이 흔하다. 그래서 성능을 재는 명사와 비교하는 말이 함께 나올 때만 측정 주장으로 본다. 그 주장을 하면서 evidence 가 비어 있으면 핵심에 근거가 없는 모양이다. 근거를 대면 통과한다 — 막는 것은 주장이 아니라 근거 없는 주장이다. 경고를 만드는 것으로 끝내지 않고 review-package 의 관문에 넣었다. 관문이 exit≠0 이면 studio-save 의 approved() 가 그 묶음을 거절한다. 만드는 것과 막는 것은 둘 다 있어야 한 쌍이다. 저장소 실제 기록 275건에 하나도 안 걸린다. 문제·결론에 수치를 적는 건 정상 기록이 늘 하는 일이라 여기가 과잉 차단이 가장 나기 쉬운 자리다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4vKjQo9KKBBokzxqXLCfk
This commit is contained in:
co-authored by
Claude Opus 5
parent
e3230ce5ed
commit
ad055fb3b9
@@ -0,0 +1,129 @@
|
||||
"""글의 핵심이 측정을 주장하는데 근거 목록이 비었는지 보는 검사기.
|
||||
|
||||
후보를 둘 버리고 셋째가 맞았다. 그 과정을 회귀로 남긴다 — 다음 사람이 되돌리면 잡힌다.
|
||||
"""
|
||||
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()
|
||||
Reference in New Issue
Block a user