feat(scripts): 놓친 8건을 코드로 막거나 검토로 보낸다
먼저 쟀다. 경고를 일괄 차단으로 올리면 채택된 편집 9건이 막힌다 — V-004 에서 고친 바로 그 9건이다. 그래서 경고는 검토로 보내고 차단은 검토가 판정할 때 일어나게 뒀다. 코드로 막은 것 셋. code[] 리비전 — check_evidence 는 리비전이 있는지만 보고 인용한 코드가 그 리비전에서 왔는지는 안 본다. 이 배치에서 실제로 났고 사람이 손으로 잡았다. 한글 수사와 그 경계 — 「다섯 개 → 여섯 개」는 결정적이다. 앵커 검사기의 오탐을 0 으로 만드는 데 시간의 절반이 갔다. 처음 판이 저장소 전체에서 401건을 냈고 전부 오탐이었다. code[] 는 한 모양이 아니다 — 심볼, 축약 경로, 줄 범위, 호스트 절대 경로, 설정 키가 섞여 있다. 축약 경로를 「없다」로 세면 있는 코드를 없다고 하는 것이고 그게 채택된 편집 아홉 건을 막았던 실패와 같은 모양이다. 판정할 수 있는 것만 판정하고 못 보는 것은 세어서 낸다. 검토로 보낸 것 다섯. D2 는 아무 계수도 안 움직이던 자리였다 — 수치도 인용도 없이 산문만 더하면 보호 구간 비교에 잡힐 것이 없다. 1인칭 표지가 늘어난 것만 보고 그 문장을 짚어 준다. 판정이 아니라 라우팅이다. 만들다 버그를 찾았다. 경고가 인용하는 문장이 파일 첫 문단에서 한 글자씩 깎이고 있었다. rfind 가 -1 을 낼 때 +2 를 해서 1 이 됐다. 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
230ad874ca
commit
845ee89054
@@ -41,6 +41,13 @@ EXTRACTORS: dict[str, re.Pattern[str]] = {
|
||||
# `500행`·`5개다` 처럼 조사나 명사가 붙은 자리에서 보호가 통째로 풀린다. 한국어에서
|
||||
# 숫자는 거의 항상 뭔가가 바로 붙으므로, 보호가 가장 필요한 자리에서 가장 안 걸렸다.
|
||||
# 버전 문자열(`1.1.0`)은 `.` 이 막아 여전히 토큰이 안 나온다 — 그것은 인라인 코드로 견준다
|
||||
# 한글 수사. 아라비아 숫자만 보면 「다섯 개 → 여섯 개」가 안 보인다. 실제로 이 배치에서
|
||||
# 한 번 놓쳤고 사람이 잡았다. 세는 말(개·건·장·줄…)이 뒤따르는 자리만 본다 —
|
||||
# 「하나」·「둘」은 「하나뿐」·「둘 다」처럼 수가 아닌 쓰임이 많다
|
||||
"한글수사": re.compile(
|
||||
r"(?<![가-힣])((?:한|두|세|네|다섯|여섯|일곱|여덟|아홉|열|열한|열두|열세|열네|열다섯|"
|
||||
r"열여섯|열일곱|열여덟|열아홉|스무|스물한|스물두|서른|마흔|쉰)"
|
||||
r"\s*(?:개|건·|건|장|줄|번|가지|편|곳|명|쪽|회|배|단계|자리))(?![가-힣])"),
|
||||
"수치": re.compile(
|
||||
r"(?<![0-9A-Za-z_.\-])(\d[\d,]*(?:\.\d+)?(?:\s?%|ms|s|MB|GB|KB|B|건|장|개|줄|분|초|회)?)"
|
||||
r"(?![0-9A-Za-z_.\-])"),
|
||||
@@ -64,6 +71,21 @@ HEDGES = (
|
||||
"하지 않았다", "않은 것", "못 한 것",
|
||||
)
|
||||
|
||||
# 자료에 없는 1인칭 경험·선택 이유. **늘어난 것만** 본다.
|
||||
#
|
||||
# 이것은 판정이 아니라 **라우팅**이다. 「처음에는 …라고 믿었다」가 자료에 있으면 정상이고
|
||||
# 없으면 지어낸 것인데, 있는지 없는지는 상류 자료를 읽어야 안다. 검사기가 가릴 수 없다.
|
||||
# 그래서 error 가 아니라 warning 이고, 무엇을 보라고 문장까지 짚어 준다.
|
||||
#
|
||||
# 보호 구간 비교로는 원리적으로 안 보이던 자리다 — 수치도 인용도 없이 산문만 더하면
|
||||
# 어느 계수도 안 움직인다. 이 배치에서 그 사례가 관문 여덟을 전부 통과했다.
|
||||
VOICE_MARKS = (
|
||||
"처음에는", "처음엔", "믿었다", "믿었는데", "고민", "고민 끝에", "놀랍", "당황",
|
||||
"돌이켜", "그제서야", "그제야", "보고서야", "뒤늦게", "뼈아", "아찔",
|
||||
"팀에서는", "팀은", "동료", "우리는", "나는", "내가", "직접 겪", "경험상",
|
||||
"시행착오", "삽질", "헤맸", "덕분에", "다행히", "결국 내",
|
||||
)
|
||||
|
||||
# `## 이름` 절. 통째로 사라진 절은 그 자체를 낸다 — 안에 보호 구간이 없으면
|
||||
# 다른 어떤 계수도 안 움직인다. 「확인하지 못한 것」 절을 지우는 편집이 그 모양이다
|
||||
SECTION = re.compile(r"^##\s+(.+?)\s*$", re.M)
|
||||
@@ -85,7 +107,10 @@ def _sentence_of(text: str, needle: str) -> str:
|
||||
i = text.find(needle)
|
||||
if i < 0:
|
||||
return ""
|
||||
start = max(text.rfind("\n\n", 0, i) + 2, 0)
|
||||
# rfind 가 -1 을 낼 때 +2 를 하면 1 이 되어 첫 글자를 잘라먹는다.
|
||||
# 파일 첫 문단의 경고가 전부 한 글자씩 깎여 나갔다
|
||||
at = text.rfind("\n\n", 0, i)
|
||||
start = 0 if at < 0 else at + 2
|
||||
stop = text.find("\n\n", i)
|
||||
chunk = text[start:stop if stop > 0 else len(text)]
|
||||
return re.sub(r"\s+", " ", chunk).strip()[:200]
|
||||
@@ -120,6 +145,17 @@ def compare(before: str, after: str) -> dict:
|
||||
"note": "절이 통째로 없어졌다. 덜어 낸 것인지 한계를 지운 것인지는 "
|
||||
"근거를 읽어야 안다"})
|
||||
|
||||
# 자료에 없는 1인칭이 들어왔나. 늘어난 것만 본다 — 지우는 것은 이 규범에서 안전한 쪽이다
|
||||
for mark in VOICE_MARKS:
|
||||
gained = after.count(mark) - before.count(mark)
|
||||
if gained > 0:
|
||||
warnings.append({
|
||||
"kind": "1인칭", "change": "새로생김", "count": gained, "value": mark,
|
||||
"sentence": _sentence_of(after, mark),
|
||||
"note": "자료에 이 사람의 행동·판단이 남아 있는지 상류에서 확인한다. "
|
||||
"없으면 지어낸 것이고, 있으면 정상이다 — 검사기가 가릴 수 없다",
|
||||
})
|
||||
|
||||
hb, ha = _hedges(before), _hedges(after)
|
||||
hedge = []
|
||||
for w in sorted(hb - ha):
|
||||
|
||||
Reference in New Issue
Block a user