# 2026-08-16 세 번째 세션 — 표기 컨벤션으로 doc-check 정규식 줄이기 전 세션(`session/2026-08-16-02-audit-tooling-verification-and-first-real-run.md`) 인수인계를 받고 시작. 설계는 안 건드림, 문서 툴링만. ## 사용자 문제 제기 > 현행 md 툴링 py 상 날짜 적는거 형식 같은것도, 강제화 하게 하는게 맞다 생각이 > 드는듯. doc-check.py 자체가 regex 를 여럿 사용해 정적으로, 결정론적이게 > 분석하는데, 형식이 많이 왔다 갔다 하면 문제가 커져. 여러 regex 를 추가해가며 > 하는것도 좋지만, '예상 가능 범위' 를 어느정도 컨벤션으로 정하는게 좋다 > 생각하는데(가장 비용이 쌈), 어떻게 봄? ## 실측 — 날짜는 이미 문제가 아니었다 전수 조사: ``` 20NN-NN-NN 형태 1864건 / 1864건 (100%) 다른 구분자(. 또는 /) 0건 비-ASCII 공백 혼입 0건 대괄호 마커 383건 ``` **강제 장치가 없는데 100% 균일**. 사용자 직감("싼 컨벤션이 통한다")을 반증하는 게 아니라 오히려 뒷받침하는 데이터라, 이걸 근거로 삼되 드리프트가 실제로 있는 곳으로 방향을 돌렸다. `doc-check.py` 당시 출력: 라이브 문서 88개, ERROR 0, **WARN 86건** — 그중 **절 참조 불일치가 78건(91%)**. 날짜 없는 주장은 3건뿐. > **재검증 시 주의**: 이 78은 **실제 작업 트리** 기준이다. `git archive HEAD` > 같은 걸로 격리 체크아웃해 돌리면 72가 나오는데, 그건 아카이브에 > gitignore된 `.claude/initreq/`가 안 들어가서 `raw-userinput.md`를 가리키는 > 인용들이 "절 참조 불일치"가 아니라 "깨진 파일 참조"로 분류되기 때문이다 > (커밋 전 4라운드 감사가 실제로 이 차이에 걸렸다). 숫자를 대조할 땐 트리를 > 갈아엎지 말고 `git show HEAD:<경로>`로 파일 단위 비교를 쓸 것. ## 프레이밍 교정 — 컨벤션은 정규식의 대안이 아니다 사용자가 "regex를 늘릴까 / 컨벤션을 정할까"의 양자택일로 물었지만, 실제 관계는 **컨벤션이 정규식을 열거형에서 앵커형으로 바꿔 작게 만드는 것**이다. `DATED`가 "±3줄 안에 아무 날짜나 있으면 통과"라는 느슨한 판정을 하는 이유가 정확히 앵커할 표준 마커가 없어서다. 동시에 컨벤션이 못 고치는 영역도 명확히 했다 — 검사는 두 종류다. - **(A) 이스케이프 해치형**: 도구가 위험 패턴을 잡고 사람이 표준 마커로 잠재움(`REF` 절 인용, `DATED`, `OURS`). 컨벤션이 곧바로 이득. - **(B) 탐지형**: 도구가 위험한 문장을 먼저 찾아야 함(`TEMPORAL`의 6개 한국어 표현, `'아직 반영 안 됨'` 리터럴). **컨벤션이 원리적으로 못 고침** — "시한부 주장에 날짜를 붙여라"를 정해도 정규식은 *날짜를 안 붙인 문장*을 못 찾는다. 이 잔여가 `quad-doc-auditor`의 몫. ## 우선순위 제안과 사용자 결정 1. 절 인용 규약 + ERROR 승격 (WARN의 91%) 2. 세션 지칭 표기 (grep을 깨뜨림) 3. 날짜 마커 라벨 어휘 닫기 사용자 결정: **1+2 채택, 3 기각.** 3에 대한 논거가 날카로웠음 — > 3. 의 경우 사실상 에이전트가 순서 섞였을 때 최신의, 옳은 요소 선택에 도움이 > 되는 정보에 가깝지 이게 warn 을 만들지는 못할듯. 즉 라벨은 **기계 검사 대상이 아니라 읽는 쪽(에이전트)의 판단 재료**다. 어휘를 닫아도 검사가 하나도 안 생기므로 비용만 든다. 3에 딸려 있던 `DATED` 앵커 좁히기만은 유일하게 WARN을 만드는 항목이라 성격이 다르지만, 지금 그 위음성이 아프다는 증거가 없어 같이 보류. ## 핵심 발견 — 78건은 대부분 "코퍼스가 지저분한 것"이 아니라 "검사기가 못 읽는 것"이었다 `dispatch-core-plan.md`는 `#` 헤딩이 11개뿐인데 다른 문서들이 인용하는 "우선순위 동률/매치 실패 처리", "핸들러 내부 상태 저장", "props 순회 순서"는 전부 실재한다 — **`**볼드**`로 시작하는 줄**로. `headings()`가 `#` 줄만 수집하니 이게 통째로 안 보였다. 그런데 볼드를 통째로 절 제목으로 인정하면 검사가 장식이 된다: | 파일 | `#` 헤딩 | 볼드(전체) | 볼드(줄머리) | |---|---|---|---| | `dispatch-core-plan.md` | 11 | 264 | 130 | | `slot-plan.md` | 40 | 331 | 162 | | `architecture.md` | 9 | 60 | 23 | 세 규칙을 실측 대조: ``` A 느슨 볼드 + 부분문자열 해소 30 B 줄머리 볼드 + 부분문자열 해소 32 C 줄머리 볼드 + 앞부분일치 해소 26 → 선두 장식(⚠️) 정규화 추가 후 30 ``` **C가 A와 해소 건수는 같으면서 매칭 표면만 좁다.** C 채택. `#` 헤딩은 부분문자열, 볼드 절은 앞부분일치라는 **비대칭**이 핵심 — 헤딩은 명시적 절 마커라 오인 위험이 없고, 볼드는 강조와 구분이 안 되니 더 엄격해야 한다. 부수로 `1. **…**`(번호 리스트 볼드)도 절로 인정하도록 넓힘 — `conventions.md`의 핸드오버 체크리스트가 정확히 그 형태고 다른 문서가 그걸 절로 인용한다. ## `initreq/` 면제 `raw-userinput.md`는 `.claude/initreq/`에 있는 읽기 전용 원본 요청이고, 스스로 "진짜 생각의 흐름 대로 작성되어있으며 정리가 없음"이라 밝힌 문서다. 여길 가리키는 인용은 절 제목이 아니라 원문 문장을 따오는 것이라 헤딩 대조가 애초에 무의미 → 폴더 범주로 면제. (`is_history()`를 넓히는 건 오답이었음 — 그건 *인용하는 쪽* 문서를 면제하는 스위치인데 여기선 *인용 대상*이 문제였다.) ## 결과 ``` 절 참조 WARN 78 → 42 (검사기 수정) → 23 (인용 19건 교정) → 15 (인용 8건 추가 교정) → 0 (추적 후 15건 교정) 그리고 WARN → ERROR 승격 ``` 교정한 인용은 대부분 "실제 절이 명확히 존재하는데 의역돼 있던 것". 대표적으로 `architecture.md`의 `복사(clone) 구현 지양`을 "복사 구현 지양"으로, `이벤트 핸들러는 self(Instance)를 받지 않는다`를 "self를 받지 않는다"로 줄여 쓴 것들. `operator-sugar-plan.md`는 `tween-plan.md`가 `research/`→`base/`로 승격된 걸 안 따라와서 경로도 같이 고침. 큰따옴표가 든 절 제목은 인용 문법(`"..."`)을 깨므로 앞부분만 인용하는 규칙이 여기서 나왔다(`pluggable 디스패치 엔진 자체도 "인터페이스"로 base가 소유`). ## 마지막 15건 — 서브에이전트 3개에 병렬 위임 "그 절이 어디로 갔는가"를 따져야 해서 읽을 양이 많았고, 메인 컨텍스트를 지키려고 대상 파일별로 3등분해 위임했다(읽기 전용, 발견만 리포트). **총평: 설계 서술이 유실된 건은 0건.** 대부분 "애초에 절이 아닌 것을 절로 인용해온 것"이었다 — 코드 주석 한 줄(`클로저 생존이 곧 gchold 생존`), 본문 산문(`데이터를 테이블에 직접 두고`), 앞으로 쓸 문서의 주제명 (`왜 quad를 쓰는가`), 그냥 의역(`bind는 유일 슬롯`). 부수로 드러난 것 — `onchange-plan.md`가 9차 분할 때 **일부러 안 옮긴** 절을 `event-plan.md`로 잘못 가리키고 있었고(실제 위치는 `bind-system-plan.md`), `brand-plan.md`는 2026-08-07에 이미 이행된 정정을 아직 "정정 대상"이라 부르고 있었다. ## 검사기 보완 — 조용한 통과 구멍 둘 에이전트들이 검사기 자체의 결함을 짚었다. 1. **인용이 60자를 넘으면 `REF`가 매칭 자체를 안 해 검사에서 조용히 빠져나갔다.** 위양성보다 나쁜 종류라 상한을 160자로 넓힘. 그러자 3건이 새로 드러났는데 전부 **줄바꿈 때문에 원문에 없는 공백이 인용에 끼어든** 케이스여서, 비교를 공백 무시로 바꿔 순증 작업량 0으로 닫음. 2. **선두 상태/날짜 태그**(`**[2026-08-16 신설]** 제목…`)가 볼드 절 제목의 앞부분을 차지해 어떤 제목형 인용도 매칭 불가였다. 태그는 시간이 지나면 바뀌는 메타 표시라 제목의 일부가 아니므로 양쪽에서 벗겨내고 비교. 이건 ERROR 승격 직후 **게이트가 내가 방금 쓴 인용 두 개를 잡아서** 발견됐다. ## 인용을 짧게 자르다 문장을 깬 것들 — 프로즈 패스에서 회수 정규식이 통과해도 의미는 깨질 수 있다. 커밋 전 diff를 "인용 매칭"이 아니라 "문장 읽기" 모드로 다시 훑어 회수한 것들: - **`ROADMAP.md`의 blockquote를 통째로 깨뜨렸었다** — 인용이 `> ` 줄바꿈에 걸쳐 있어 마커가 인용문 안에 들어가 있었고, 그걸 지우면서 인용 블록이 무너졌다. 규약에 함정으로 명시. - 절 제목을 명사구 자리에 그대로 끼워 **문법이 깨진 문장 5건** (`"pluggable 디스패치 엔진 자체도"는 원칙에…`). `"…" 절이 세운 원칙` 형태로 교정하고 이것도 규약에 넣음. - 축약하다 주장이 빠진 인용 1건(`README.md`의 "빈 자리 아님" 결론). ## 1라운드 감사 — 규칙 자체의 정밀도 구멍 두 번째 감사자가 더 중요한 걸 짚었다 — `attribute-plan.md`와 `dispatch-core-plan.md`가 인용한 `architecture.md`의 `**pluggable 디스패치 엔진 자체도 …**`는 **절이 아니다.** 한 문단이 줄바꿈되면서 강조 스팬이 우연히 줄머리에 걸린 자리이고, 진짜 절은 두 줄 위의 `**패키지 경계**`다. 같은 원문을 인용하는 `slot-plan.md:209`는 이미 올바르게 `"패키지 경계"`를 쓰고 있어서, **같은 diff 안에 옳은 인용과 우연한 인용이 공존**하고 있었다. 즉 "줄머리 볼드"라는 규칙만으로는 절과 랩된 강조를 못 가른다. 진짜 절 제목은 **빈 줄 다음에 오거나 리스트 항목 머리**라는 성질이 있어서 그걸 조건으로 추가했다. 실측 결과 **정확히 그 가짜 인용 2건만 깨지고 나머지 46건은 그대로 통과**했고, 볼드 후보 풀은 `dispatch-core-plan.md` 기준 131→104로 좁아졌다. 위양성 0으로 정밀도만 올린 셈. 교훈: 이 규칙은 "사람이 절이라고 인식하는 것"을 근사하는 휴리스틱이라, 근사가 어긋나는 자리는 실제 인용을 감사해서만 드러난다. 검사기가 통과시켰다고 그게 절인 건 아니다. ## 1라운드 감사 — 이 세션 자신의 실수 `pre-implementation-audit.md`의 "아직 안 고침" 절에 있는 `State` 비일관성 항목이 해소된 것으로 보여 `**[해소됨 — 2026-08-16 확인]**` 마커를 달았는데, **바로 다음 문단에 2026-08-09 세션이 단 `**[완전 해소, 2026-08-09 세션]**`이 이미 있었다.** 중복일 뿐 아니라 해소 시점을 일주일 뒤로 잘못 읽히게 만들어 되돌렸다. 서브에이전트(추적 담당)가 "후임 절이 옛 서술을 뒤집었다"는 근거를 정확히 줬지만 **그 항목이 이미 해소 표시를 달고 있다는 사실은 안 봤고, 메인 세션도 그 문단 아래를 안 읽었다** — 근거가 맞아도 "이미 처리돼 있지 않은가"를 따로 확인해야 한다는 교훈. 이 항목이 해소 표시를 달고도 "아직 안 고침" 헤더 아래 남아 있는 헤더-내용 불일치는 이 세션 이전부터 있던 별개 부채라 임의로 재배치하지 않고 사용자에게 물었고, **"이미 고침" 절로 옮기라는 결정**을 받아 반영했다. 그 헤더 아래엔 이제 진짜 미해소인 `Destroying` 훅 건만 남는다. 감사 세 라운드가 전부 이 자리를 지목했는데, 세 번 다 "고칠지 말지가 판단 문제"라 서브에이전트는 결론을 못 냈다 — 메인 세션만 사용자에게 물을 수 있다는 게 이 절차의 핵심인 이유다. ## 2라운드 감사 — "규칙을 세운 세션이 그 규칙을 어긴다" 1라운드 발견을 반영한 뒤 2라운드를 돌렸더니 또 실질 발견 2건이 나왔다. 둘 다 **이번 세션이 새로 생산한 산문**의 문제였다. 1. **`base/brand-plan.md`에 이번에 새로 쓴 문장이 "세션은 파일 ID로 지칭할 것" 규약을 어겼다** — 같은 커밋에서 만든 규약을 같은 커밋의 새 문장이 위반. `2026-08-07 여덟 번째 세션` → 세션 파일 ID로 교정했고, 이번에 건드린 다른 두 줄(`README.md`의 `tools/` 행, `lifecycle-pattern.md`)도 규약의 "만지는 김에 같이 고친다" 조항대로 처리했다. 2. **`research/framework-comparison-findings.md`의 수정이 "고침"이 아니라 "검사망 회피"였다** — `"왜 quad를 쓰는가"` 인용을 문법만 비껴가게 고쳐서 검사 대상에서 통째로 빠졌는데, 감사자가 `REF` 정규식으로 `group(3)`이 `None`이 되는 걸 직접 확인해 잡았다. 같은 diff의 다른 두 회피 (`CLAUDE.md`의 "세션 히스토리", `옛 Modifier는 핸들러 계층을 모름`)는 **과거형으로 참인 사실**을 서술하니 정당하지만, 이건 대상 문서에 근거가 없는 **미래형 주장**이었다는 게 판정 근거. 실제 절(`초심자 (getting-started) core loop`/`quadnomicon`)을 가리키도록 고쳤다. 부수로, `conventions.md`에 박아둔 세션 서수 분포 숫자(971/68/8)가 재현되지 않았다 — 내 집계는 `session/`까지 포함한 전체 grep이었고 감사자는 `doc-check.py`가 훑는 88개 라이브 문서 기준이었다. **집계 범위를 안 적은 숫자를 매 세션 로드되는 파일에 박은 것 자체가 이번에 세운 "개수는 소스 하나만" 원칙 위반**이라 정성적 서술로 바꿨다. 같은 이유로 날짜 균일 수치도 `conventions.md`에서 빼고 이 파일을 가리키게 했다. **교훈**: 규약을 세우는 커밋이 그 규약의 첫 위반자가 된다. 규약을 쓴 세션은 자기 diff를 그 규약으로 한 번 되짚어야 하고, 그걸 자기 self-audit 으로는 잘 못 한다(이번에도 둘 다 서브에이전트가 잡았다). ## 3라운드 감사 — 수정이 만든 새 결함, 그리고 사각지대 하나 2라운드 수정을 반영하고 3라운드를 돌렸더니 **내가 2라운드에 만든 마크다운 버그**가 나왔다. `"cleanup"` 용어 인용을 규약대로 고치려고 볼드 스팬을 쪼개다 **짝 없는 `**`를 남겼다** — `lifecycle-pattern.md`의 `**` 개수가 144(짝수)→145(홀수)로 바뀐 걸 감사자가 교차 확인해 잡았다. 렌더러에 따라 그 뒤 리스트의 볼드 페어링까지 밀려 깨질 수 있는 실제 결함이었다. 복구한 뒤 변경 파일 전체에 같은 검사를 기계적으로 돌려, 내 변경이 짝을 뒤집은 파일은 그것 하나뿐임을 확인했다(다른 홀수 파일들은 HEAD에서도 이미 홀수 — 코드블록 안의 `**` 때문). **구조적 사각지대 하나가 같이 드러났다**: `- **[해소됨, …]** 실제 제목 — …` 처럼 **볼드가 상태 태그에서 닫히고 진짜 제목이 볼드 밖 평문**이면 (`archive/question-resolved.md`가 이 형태) 검사기는 `[해소됨, …]`만 절 제목으로 본다. 절 참조가 ERROR로 승격된 뒤라, 이걸 모르고 규약대로 큰따옴표 인용을 하면 **실재하는 서술인데도 커밋이 막힌다.** 규약에 캐비엇으로 명시하고 "새로 쓸 땐 태그와 제목을 한 볼드 안에" 지침을 넣었다. 게다가 이 세션이 신설한 "세션은 파일 ID로 지칭" 관례가 `**[태그, 세션ID]**` 형태의 리드를 늘리는 방향이라 재생산될 구조였다는 지적까지 붙었다. 보류한 것 하나 — `#` 헤딩 검사가 **부분문자열**이라 `"확정"` 같은 짧은 인용은 같은 파일의 무관한 헤딩에 걸려 통과한다(`slot-plan.md`엔 "확정"이 든 헤딩이 6개). 다만 감사가 **실제 오매칭 사례를 하나도 못 찾았고**, 사용자와 같이 `conventions.md`의 "드문 오용이나 가상의 미래 요구까지 방어/최적화하려고 구조를 복잡하게 만들지 않는다" 원칙에 따라 지금은 손대지 않기로 했다. ## 감사자가 `git stash`를 쓴다 — 실동에서 드러난 위험 `.pyc` 추적 해제(`git rm --cached`)가 두 번 되돌아갔다. 원인은 감사자들이 HEAD와 대조하려고 `git stash`를 건 것 — **커밋 안 된 작업 트리에서** 그걸 하면 메인 세션의 스테이지가 풀리고, pop이 실패하면 세션 작업을 통째로 날릴 수 있다. 이번엔 유실이 없음을 확인했고, `.claude/agents/quad-doc-auditor.md`에 작업 트리를 바꾸는 git 명령 금지와 대안(`git show HEAD:<경로>` 등)을 규약으로 넣었다. "읽기 전용"을 도구 목록으로 못 거는 이상(`tools:` 필드가 그대로 반영되지 않는 기존 미해결 항목) 이런 규약이 유일한 방어선이다.