docs: 감사 루프 재설계(워크플로 폐기) + 정의 파일은 커밋된 HEAD에서 읽힘으로 재정정

사용자 결정 반영:
- 감사 루프를 Workflow에서 "메인이 quad-doc-auditor를 병렬 호출 → 메인이
  일괄 수정 → 반복"으로 재설계. .claude/workflows/quad-handover-audit.js 삭제,
  절차 소스는 conventions.md "작업 방식". 폐기 근거 셋 — 토큰 과다, 파일별
  픽스 에이전트가 또 부정확한 서술을 생산, 서브에이전트는 사용자에게 못 물음.
- 패스 수는 최소 2에서 변경 규모에 따라 증가. 감사자 모델은 sonnet 유지
  (haiku 배제).
- 출처 없던 원칙 "드문 오용/가상 미래 요구까지 방어·최적화하려고 구조를
  복잡하게 만들지 않는다"를 conventions.md "설계 원칙" 절로 명문화(선택지 a).
  modifier-plan.md 인용을 그쪽으로 재조준, question.md 항목은 archive로 이전.
- 신설 관례: 사용자 발언을 근거로 인용할 때 결론만 적지 말고 논거까지 남길 것
  (논거 원문은 session/에, 라이브 문서는 결론+짧은 논거+포인터).

 재정정 — 직전 커밋의 "정의 파일은 세션 시작 시점 스냅샷" 결론은 틀렸음.
정의는 워킹트리가 아니라 **커밋된 HEAD**에서 읽힌다(감사 패스가 받은 지시문이
세션 도중 만든 HEAD 커밋의 blob과 바이트 단위로 동일, git rev-parse로 독립
확인). 규칙이 "재시작"에서 "고쳤으면 커밋 후 실행"으로 싸짐. 이 정정으로
오래 미확정이던 (d)도 해소 — memory: project가 Write/Edit을 딸려온다는 진단이
맞았고, "빼도 그대로"로 보였던 건 제거가 아직 커밋 안 됐던 탓. 남은 미해결은
tools: 필드 미반영뿐.

첫 감사 라운드(새 절차) 반영: 자기 메모리 2건의 stale 서술,
documentation-content-map.md "943줄, 최대 문서"(실측 203줄, 최대는 slot-plan
1970줄), README.md 패스 수 하드코딩. 직전 커밋의 미재감사 6건은 회귀 없음으로
확인해 todos.md ⚠️ 블록 닫음.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
qwreey 2026-08-16 03:32:09 +09:00
parent 134379632f
commit 07c77e5a5a
Signed by: qwreey
GPG key ID: D28DB79297A214BD
11 changed files with 307 additions and 278 deletions

View file

@ -30,9 +30,8 @@
| `luau-test/` | **[2026-08-09 신설]** `base/` 확정 사항 중 "추론만으로 확정하고 실제 Luau로 부딪혀본 적 없는 것"(M0 스파이크 대상)을 `luau`/`luau-analyze`/`luau-lsp`/Roblox Studio로 사용자가 직접 돌려볼 독립 실행 스크립트 모음. **[2026-08-13 여섯 번째 세션, 첫 실측]** `luau`/`luau-analyze` 바이너리가 생겨 처음으로 실제 실행 — **런타임 12개 전원 통과**, 타입 쪽에서 `:Compute(fn)` lazy 핸들 계약이 Luau 추론과 충돌하는 게 드러남(당시 `question.md` 0-Y). **[2026-08-13 열세 번째 세션]** 그 0-Y가 해소되며 `review-required/`**비었음** — 계약은 유지 확정, 남은 건 Luau 자체 한계라 `base/typing-limits.md`가 담당. **`STATUS.md`가 상태의 소스**(pass / 사람 결정 필요 / 스파이크 깨짐 / 미실행 분류 — 사람이 먼저 볼 것만 위에), `luau-test/README.md`는 각 파일의 검증 의도·배경, 실행 결과 상세는 `audit/luau-test-first-run-2026-08-13.md` |
| `audit/` | **[2026-08-13 신설]** `luau-test/` 등 스파이크를 실제로 돌려본 뒤 "무엇이 확인됐고 무엇이 아직 안 됐는지"를 기록하는 곳 — 스크립트/계획 자체가 아니라 **실측 결과**만 다룸. base/luau-test와 달리 부분 확인(일부만 통과)도 있는 그대로 기록, 완전히 해소되면 관련 `base/`/`luau-test/README.md` 캐비엇을 지우고 이 문서는 근거로 남김. **개수는 폴더가 소스**(여기서 세지 않음): `luau-test-first-run-2026-08-13.md`(첫 실측 라운드 전체 — 런타임 12개 통과, 구 `question.md` 0-Y의 1차 근거. **단 이 문서의 "콜백이 raw 값을 받으면 완전 클린" 판정은 아래 `type-recursion-issue/`가 뒤집었음**), `gcconn-trick-verification.md`(사용자가 Studio에서 직접 돌린 gcconn 트릭 부분 확인 — `10`의 A 섹션 앞부분만. **[2026-08-14 다섯 번째 세션, 열한 번째 세션에 `canBound` 재도입 반영해 재갱신]** 실측된 사실 자체는 그대로 유효하고 `value` 단독 1-인자 재정정으로 오히려 더 중요해졌음 — 이중 바인딩 게이트(`canBound`)/emit 게이팅(`canExecute`)/재바인딩 허용/`value` 쪽 복사 gcconn 판정/Instance userdata 동일성/B/C가 미확인), **`type-recursion-issue/`**(**[2026-08-13 열세 번째 세션 신설]** 0-Y 재실측 전체 — `REPORT.md` + `spikes/`(개수는 폴더가 소스). 다른 audit 기록과 달리 **스크립트를 같이 둠**: 이 건의 근거가 "여러 formulation을 서로 대조한 것"이라 개별 파일을 직접 돌려야 판정이 재현되기 때문. 결론은 `base/typing-limits.md`로 승격됨), `fallback-xpcall-verification.md`(**[2026-08-14 신설]** `base/fallback-plan.md``Traceback` 메커니즘 전부 확인 — 클로저 업밸류 배선/중첩 스택 캡처/`err: any`/`error(msg)` 위치 접두 10개 검증 전부 통과. 스크립트 1개뿐이라 재현용으로 같이 둠: `fallback-xpcall-spike.luau`), **`type-recursive-issue-with-typeof/`**(**[2026-08-15 신설]** 사용자가 발견한 `typeof(named fn)` 간접참조가 0-Y(재귀 제네릭 반환 leak)를 실제로 우회하는지 실측 — `REPORT.md` + `spikes/`. 결론: 인라인 대신 이름 붙은 함수 + `typeof`로 선언하면 LHS 명시 없이도 다운스트림이 안전해짐(체이닝 50단·타입 변경·중첩 self 호출까지 확인), `typing-limits.md` §1 ③으로 승격. 부수적으로 `setmetatable` 확장 시도에서 quad와 무관한 Luau 0.733 솔버 버그(모순 진단 두 개 동시 발생) 발견, 채택 안 함. `luau-test/16`(type function으로 `Store<T>` 레코드 필드 합성) 복구도 이 조사 중 완료 — API 버전 드리프트였을 뿐 설계 문제 아니었음, `typing-limits.md` §5 승격), **`type-recursive-issue-try-callback/`**(**[2026-08-15 신설]** 콜백 파라미터 무주석 추론을 뚫을 방법이 정말 없는지 type function/메타테이블/오버로드/제네릭 디폴트 등 전방위로 재시도 — `REPORT.md` + `spikes/`(개수는 폴더가 소스 — 최초 라운드 + `/code-review high`가 이중 꺾쇠 명시적 제네릭 인스턴스화를 안 시도했음을 지적해 추가된 후속 조사 라운드로 구성). 결론: quad의 `state:Compute(fn)` 단일 호출 모양을 유지한 채로는 여전히 안 됨. 발견 셋 — (1) 근본 원인이 재귀 자기참조가 아니라 "제네릭 콜백 인자 전반에 컨텍스트 타입 전파가 안 됨"이라는 게 더 정확함(재귀 없는 최소 사례로도 재현), (2) `T`를 명시 중간 변수로 먼저 고정하거나 재사용 가능한 monomorphize 헬퍼를 거치면 실제로 추론이 살아나지만 둘 다 단일 콜론 호출을 2단계 체인으로 바꿔야만 해서 §0 대전제로 채택 안 함, (3) 이중 꺾쇠 명시 인스턴스화(`Compute<<T,U>>(fn)`)는 leaf 호출에선 sound하게 성립하지만(spurious 진단 원인도 규명 — read-only/read-write 가변성 불일치) 매 호출 T/U 전부 명시 필요 + 중첩 self 호출 여전히 실패라 순손해로 채택 안 함) |
| `tools/` | **[2026-08-13 아홉 번째 세션 신설]** 코퍼스 기계 점검 — `doc-check.py`가 깨진 파일/절 참조, README 색인 누락, 날짜 없는 시한부 주장("아직 안 돌려봄" 등), 미반영 ⚠️ 배너를 한 번에 훑음. **중대 변경 후 커밋 전에 돌릴 것**(`python3 .claude/tools/doc-check.py`) — 수동 감사에서 나온 발견의 대부분이 이 종류였고, 실제로 문서를 쪼개다 잘못 옮긴 참조를 이게 잡아냄. ERROR는 고치고 WARN은 판단 대상 |
| `agents/` | **[2026-08-16 신설]** 프로젝트 서브에이전트 정의(`.claude/agents/*.md`, Claude Code 표준 위치). 현재 `quad-doc-auditor.md` 하나 — `doc-check.py`가 못 잡는 의미론적 stale/모순(본문 문장이 뒤집힌 결정을 여전히 서술, 개수/목록 이중 소스 드리프트 등)을 신선한 맥락에서 찾는 읽기 전용 감사자. 중대 변경 커밋 전에 위임하는 게 기본(`.claude/conventions.md` "작업 방식" 참고). `tools/`의 기계 점검과 짝을 이루는 의미론적 점검 계층 |
| `agents/` | **[2026-08-16 신설]** 프로젝트 서브에이전트 정의(`.claude/agents/*.md`, Claude Code 표준 위치). 현재 `quad-doc-auditor.md` 하나 — `doc-check.py`가 못 잡는 의미론적 stale/모순(본문 문장이 뒤집힌 결정을 여전히 서술, 개수/목록 이중 소스 드리프트 등)을 신선한 맥락에서 찾는 읽기 전용 감사자. 중대 변경 커밋 전에 위임하는 게 기본**여러 개를 병렬로 돌리고 수정은 메인 세션이 일괄로 하는 루프**(패스 수는 여기 안 적음 — 소스는 conventions.md)이며 절차는 `.claude/conventions.md` "작업 방식" 절이 소스(**[2026-08-16]** 이 루프를 담던 `workflows/quad-handover-audit.js`는 토큰 과다·픽스 에이전트발 부정확 서술·사용자 질의 불가 때문에 폐기됨). `tools/`의 기계 점검과 짝을 이루는 의미론적 점검 계층 |
| `agent-memory/` | **[2026-08-16 신설]** 서브에이전트가 라운드를 넘겨 유지하는 영속 메모리(`agent-memory/<에이전트 이름>/MEMORY.md`가 색인). 지금은 `quad-doc-auditor/` 하나 — 코퍼스 구조, 반복되는 실패 패턴 등을 기억해 감사 라운드마다 처음부터 파악하지 않게 함. **사람이 손으로 채우는 문서가 아니라 에이전트가 스스로 쓰는 것**이지만, `.gitignore` 대상이 아니라 커밋하면 코퍼스 일부가 되고 `doc-check.py` 검사 대상에도 들어감(감사 대상이기도 하다는 뜻 — 여기 적힌 주장도 stale해질 수 있음). **[2026-08-16 기준] 아직 커밋 안 됨(untracked)** — 추적할지 `.gitignore`로 뺄지는 사용자 판단 |
| `workflows/` | **[2026-08-16 신설]** Claude Code Workflow 정의. 현재 `quad-handover-audit.js` 하나 — `quad-doc-auditor` 단일 패스는 비결정적이라 매번 다 잡는다는 보장이 없어서, 라운드마다 병렬로 여러 번 감사+파일별 반영을 새 발견이 없는 라운드가 연속으로 나올 때까지 반복해 수렴시키는 다회·병렬 감사 루프(라운드당 패스 수·수렴 조건·최대 라운드는 튜닝 대상 상수라 여기 숫자를 적지 않음 — 소스는 `quad-handover-audit.js` 상단). "핸드오버 준비하고 커밋해" 류 요청 시 자동으로 먼저 돌림(`.claude/conventions.md` "작업 방식" 참고), 실제 `git commit`은 워크플로 밖 메인 세션이 함 |
| `session/` | **[2026-08-11 신설]** 세션별 상세 로그 원문(시행착오·정정 전 서술 포함, `quadnomicon` 개발로그 소재용) — 루트 `CLAUDE.md`가 3196줄까지 불어나 성능 저하를 유발해서 분리함. 파일명 `YYYY-MM-DD-NN-slug.md`, `.claude/session-summary.md`의 각 항목이 여기로 링크. 항상 읽을 필요 없음 — 결정의 논의 과정이 궁금할 때만 |
| `initreq/` | 프로젝트 착수 시 클론해둔 참고 레포(quad v1, fusion, vide, rbvm, tbox, code-docker) + PA님 실 코드(`artworks/`, 4차 라운드 교차검증 근거) + 원본 요청(`req.md`, `raw-userinput.md`) + `quad2-try`(이전에 시도했다 폐기한 v2 재작성 시도 — 리서치 완료, 결론은 `base/bind-system-plan.md`) — 읽기 전용 리서치 소스, 여기 내용을 옮기지 말고 항상 원본 그대로 유지 |

View file

@ -1,6 +1,6 @@
---
name: quad-doc-auditor
description: quad 프로젝트의 `.claude/` 설계 문서 코퍼스(base/research/reference/archive, README.md, question.md, conventions.md, project-context.md, todos.md, 루트 CLAUDE.md/ROADMAP.md/HUMAN_TODO.md)에서 `doc-check.py`가 못 잡는 의미론적 stale/모순을 찾는다. 설계 결정이 뒤집히거나 확정되는 등 이 코퍼스에 중대한 변경이 있은 뒤, 특히 그런 변경을 커밋하기 전에 사용. 읽기 전용 — 문제를 리포트만 하고 직접 고치지 않는다.
description: quad 프로젝트의 `.claude/` 설계 문서 코퍼스(base/research/reference/archive, README.md, question.md, conventions.md, project-context.md, todos.md, agent-memory/, 루트 CLAUDE.md/ROADMAP.md/HUMAN_TODO.md)에서 `doc-check.py`가 못 잡는 의미론적 stale/모순을 찾는다. 설계 결정이 뒤집히거나 확정되는 등 이 코퍼스에 중대한 변경이 있은 뒤, 특히 그런 변경을 커밋하기 전에 사용. 읽기 전용 — 문제를 리포트만 하고 직접 고치지 않는다.
tools: Read, Grep, Glob, Bash
model: sonnet
---
@ -17,34 +17,34 @@ model: sonnet
너를 호출한 세션이 한다. 이 규칙은 도구 유무가 아니라 **행동 규약**이다 —
어떤 이유로 쓰기 도구가 주어지더라도 문서를 직접 고치지 마라.
> **[2026-08-16] 정의를 고친 그 세션에서 그대로 돌리면 옛 정의로 돈다 —
> 워크플로는 `scriptPath`로, 에이전트 정의는 재시작으로 피할 것.** 이 배너는
> 같은 날 감사 라운드들이 남긴 긴 가설 서술을 검증된 것만 남겨 압축한
> 것이다(경위 원문은
> `.claude/session/2026-08-16-01-subagent-audit-and-claudemd-split.md`).
> 확인된 것:
> - **이름으로 부르는 워크플로(`Workflow({name})`)는 세션 시작 시점
> 스냅샷을 쓴다 — 1차 증거 있음.** `quad-handover-audit`을 이름으로 돌렸을
> 때 실제로 실행된 스크립트가 세션 시작 시점 상태와 **바이트 단위로
> 동일**했고, 같은 세션에서 그 파일에 가한 편집은 전혀 반영되지 않았다.
> - **반면 `Workflow({scriptPath})`는 디스크에서 실시간으로 읽는다 — 1차
> 증거 있음.** 세션 시작 후 **새로 만든** 스크립트가 정상 실행됐고, 그
> 파일을 고쳐 다시 부르니 고친 내용이 반영됐다. 즉 **워크플로 쪽 해법은
> 세션 재시작이 아니라 `scriptPath`를 넘기는 것**이다.
> - **에이전트 정의도 stale하게 로드된 정황이 있으나 근거가 약하다**
> 감사 에이전트들이 "받은 지시문에 그 세션에 추가된 배너가 없었다"고
> 보고했을 뿐 자기 보고다(아래 Grep/Glob 항목과 같은 등급의 근거).
> 에이전트 정의엔 `scriptPath` 같은 우회가 없으니, 확인 전까지는
> **정의를 고쳤으면 재시작 뒤에 감사를 돌리는** 보수적 쪽을 따를 것.
> - **`model: sonnet`은 반영된다** — 서브에이전트 트랜스크립트에서
> `claude-sonnet-5` 확인(자기 보고가 아니라 기록 기준).
> - **Write/Edit이 주어지는 원인은 미확정.** `memory: project`를 뺐지만
> 재시작 뒤 재확인은 아직 안 했다. 그래서 위 행동 규약이 유일한 보루다.
> - **"Grep/Glob이 안 주어졌다"는 두 소스가 어긋나는 미해결 불일치다** —
> 호출하는 세션이 보는 에이전트 등록 목록엔 Grep/Glob이 **포함돼 있는데**,
> 실행된 에이전트는 자기 도구를 Read/Bash/Write/Edit으로 보고했다. 어느
> 한쪽을 실측으로 확정하지 말고, (d)와 함께 재시작 직후 깨끗한 실행에서
> 같이 볼 것.
> **⭐ [2026-08-16, 재정정] 정의 파일은 워킹트리가 아니라 *커밋된 HEAD*에서
> 읽힌다 — 고쳤으면 커밋한 뒤에 감사를 돌릴 것(재시작 불필요).** 이 배너는
> 같은 날 감사 라운드들이 남긴 긴 가설 서술을, 그 뒤 실측으로 확인된 것만
> 남겨 압축한 것이다(경위는
> `.claude/session/2026-08-16-02-audit-tooling-verification-and-first-real-run.md`).
> - **"세션 시작 시점 스냅샷"이라던 앞선 결론은 틀렸다.** 한 감사 패스가
> 자기가 받은 지시문이 blob `92b9484`, 즉 **그 시점 HEAD 커밋의 버전과
> 바이트 단위로 같다**고 보고했고, 메인 세션이 `git rev-parse`로 독립
> 확인했다 — 그 HEAD는 세션 시작 시점이 아니라 **세션 도중에 만든
> 커밋**이었다. 즉 정의는 커밋될 때마다 갱신되고, **커밋 안 된 워킹트리
> 편집만 안 보인다**. 앞선 워크플로 관측(실행 스크립트가 세션 시작
> 상태와 동일)도 그때 HEAD가 곧 세션 시작 상태였을 뿐이라 이 설명과
> 모순되지 않는다.
> - **`memory: project`가 Write/Edit을 딸려온다는 진단은 이제 지지된다.**
> 그 옵션이 살아있던 정의로 돈 감사자들은 Write/Edit을 받고 실제로
> 메모리 파일을 썼고(파일 mtime 확인), 옵션이 빠진 정의가 커밋된 뒤
> 돈 감사자는 Write/Edit이 없었고 메모리 쓰기도 없었다. 한때 "옵션을
> 뺐는데도 그대로 주어진다"며 반증된 것처럼 보였던 건 **그 제거가 아직
> 커밋 안 돼서 반영이 안 됐던 것**이다.
> - **`model: sonnet`은 반영된다** — 서브에이전트 트랜스크립트의 최상위
> `message.model`이 전부 `claude-sonnet-5`(자기 보고가 아니라 기록 기준).
> ⚠️ 확인할 때 `"model"` 문자열만 grep하면 안 된다 —
> `message.usage.iterations[].model``claude-opus-5`가 섞여 들어와
> 오독을 부른다.
> - **아직 안 풀린 것: `tools:` 필드가 그대로 반영되지는 않는다.**
> frontmatter에 적힌 Grep/Glob이 실제로는 안 주어지고, 적지 않은
> `advisor`가 주어진 라운드가 있었다. 그래서 "파일을 고치지 않는다"는
> 위 규칙은 도구 유무가 아니라 **행동 규약**으로 지키는 것이다.
## 절차
@ -97,5 +97,12 @@ model: sonnet
고치면 되는지 한 문장. **확실**(다른 문장과 직접 모순되거나 doc-check.py급
확신)과 **의심**(사람 판단 필요, 애매한 경우)으로 나눠라.
**[2026-08-16 추가] "사용자 판단 필요"를 별도로 표시해라.** 설계 판단이
섞였거나(어느 쪽이 맞는지가 취향/방향 문제), 출처가 불분명한 인용이거나,
고치는 방법이 둘 이상인데 고르는 근거가 문서에 없는 발견은 `의심`에 묻지
말고 **`사용자 판단`**으로 따로 빼라. 너를 부른 메인 세션은 사용자에게
직접 물을 수 있고, 그게 지금 감사 루프의 핵심 설계다 — 네가 애매한 걸
그냥 "이렇게 고치면 됨"으로 넘기면 그 강점이 죽는다.
발견이 없으면 "발견 없음"이라고 그대로 보고해라. 그럴듯해 보이려고 사소한
문체 지적을 지어내지 마라 — 이 감사의 가치는 정확도지 발견 개수가 아니다.

View file

@ -747,3 +747,23 @@ context-rejected.md`. 아래는 그중 **아직 실제로 열려있는 것만**
전체 순서/우선순위는 루트 `CLAUDE.md`가 최종 소스 — 위 표는 힌트일 뿐 그쪽이
바뀌면 이 문서도 갱신할 것.
## 인용 출처 없는 원칙 — "드문 오용/가상 미래 요구까지 방어/최적화하려고 구조를 복잡하게 만들지 않는다" (2026-08-16 신설 → 같은 날 해소)
**결론: 선택지 (a) 채택 — `.claude/conventions.md`의 "설계 원칙" 절에 정식
명문화.** `base/modifier-plan.md`의 인용은 그쪽을 가리키도록 바꿨고, 대기
배너는 제거됨.
**경위**: `modifier-plan.md`의 "미검토로 남긴 것" 문단이 이 원칙을
`CLAUDE.md`의 것으로 인용해왔으나, 코퍼스 전체 grep과 `git log -S`
(CLAUDE.md 전 히스토리) 어디에도 그런 서술이 없었음 — 존재한 적 없는
출처였다. 같이 발견된 다른 두 건은 인용 대상만 틀린 것이라 실제 소스로
재조준 완료(`research/v1-compat-plan.md`→`base/component-composition-plan.md`+
`base/store-plan.md`, `research/pre-implementation-audit.md`→`ROADMAP.md`).
**사용자 논거**: "(a) 하면 될 것 같아. 그리고 추측하건데, 내가 세션 중
했던 말을 옮겨적지 않은 경우로 보이기도 함." — 즉 원칙 자체는 실제로
사용자가 구두로 세운 것인데 문서화가 누락됐다는 판단. 이 추정에서
**"사용자 발언을 인용할 때는 논거까지 남긴다"는 새 관례**가 같이 나왔음
(`conventions.md` "작업 방식" 절).

View file

@ -533,16 +533,12 @@ Modifier 값을 변수/모듈 상수로 만들어 재사용하는, 기존에도
**미검토로 남긴 것**: `Apply` 체인이 실측으로 병목이라고 확인되면 그때
"unsafe/fast-path mutable 빌더" 같은 별도 opt-in을 검토할 수 있으나, 지금은
근거 없는 선제 최적화라 설계하지 않음 — "드문 오용/가상 미래 요구까지
방어/최적화하려고 구조를 복잡하게 만들지 않는다" 원칙과 동일.
> **[2026-08-16] 위 인용의 출처가 코퍼스에 없음 — 사용자 판단 대기.**
> 원래 이 문장은 그 원칙을 `CLAUDE.md`의 것으로 인용했으나, 코퍼스 전체
> grep과 `git log -S`(CLAUDE.md 전 히스토리) 어디에도 그런 서술이 없음.
> 원칙을 `conventions.md`에 정식으로 명문화할지, 아니면 출처 없이 이
> 문서의 지역 논거로만 둘지는 사용자가 정할 것 — `question.md`
> "인용 출처 없는 원칙" 항목 참고. 이 문장이 지지하는 결론("지금은
> 설계하지 않음") 자체는 영향받지 않음.
근거 없는 선제 최적화라 설계하지 않음 — `.claude/conventions.md`
"설계 원칙" 절 "드문 오용이나 가상의 미래 요구까지 방어/최적화하려고
구조를 복잡하게 만들지 않는다"와 동일(**[2026-08-16]** 이 문장은 원래 그
원칙을 `CLAUDE.md`의 것으로 인용했는데 코퍼스 어디에도 그런 서술이 없었음
— 사용자 결정으로 `conventions.md`에 정식 명문화하고 여기 인용을 그쪽으로
돌림).
### 9-2. `Overridden`가 서로 다른(그러나 상하위 관계인) Modifier 타입을 섞는 경우 —
타입 시그니처 미확정 (2026-08-07 다섯 번째 세션 후속)

View file

@ -19,6 +19,17 @@
haiku, 일반 작업은 sonnet. 특히 소스코드를 많이 읽어야 하는 리서치는 메인
컨텍스트를 부패시키니 Agent로 위임할 것(아래 "작업 방식" 참고).
## 설계 원칙
- **⭐ 드문 오용이나 가상의 미래 요구까지 방어/최적화하려고 구조를 복잡하게
만들지 않는다.** 실제로 관측된 문제(실측된 병목, 실제로 밟은 함정)에만
구조를 쓴다. **[2026-08-16 명문화]** 이 원칙은 `base/modifier-plan.md`
`Apply` 체인 fast-path를 미루는 근거 등으로 **여러 문서가 이미 인용해왔지만
정작 어디에도 적혀 있지 않았다** — 코퍼스 전체 grep과 `git log -S`
확인됨. 사용자가 세션 중 구두로 말한 게 옮겨적히지 않았을 가능성이 크다는
사용자 본인 추정에 따라 여기 정식 관례로 승격(선택지 (a) 채택). 이 누락이
아래 "사용자 발언을 인용할 때" 관례가 생긴 계기이기도 함.
## 작업 방식
- **소스코드를 많이 읽어야 하는 리서치는 Agent(Explore)로 위임** — 메인
@ -79,26 +90,43 @@ haiku, 일반 작업은 sonnet. 특히 소스코드를 많이 읽어야 하는
짜는 대신 고정 정의로 옮긴 것. **아주 큰 변경**(설계 반전 규모)엔 이걸로
대체하지 말고 `/code-review`(diff 기반)와 사용자의 직접 diff 검토를
병행할 것 — 이 서브에이전트는 diff가 아니라 코퍼스 전체의 정합성만 봄.
- **[2026-08-16 도입] "핸드오버 준비하고 커밋해" 류 요청엔 되묻지 말고
`.claude/workflows/quad-handover-audit.js`(Workflow 이름
`quad-handover-audit`)를 먼저 돌릴 것.** 단일 `quad-doc-auditor` 패스는
비결정적이라 매번 다 잡는다는 보장이 없다는 게 사용자 지적 — 이 워크플로는
라운드마다 `quad-doc-auditor`를 병렬로 여러 번 돌리고 새 발견을 파일별로
즉시 반영한 뒤, **새 발견이 없는 라운드가 연속으로 나올 때까지** 반복해
수렴시킨다(라운드당 패스 수·수렴 조건·최대 라운드는 튜닝 대상 상수라
여기 숫자를 적지 않음 — 소스는 `.claude/workflows/quad-handover-audit.js`
상단). 사용자가 정확성을 시간보다 우선한다고
명시했으므로 기본 워크플로 크기 가이드라인(15 에이전트 이하)을 의도적으로
넘을 수 있음 — 이 워크플로 자체가 그 예외 대상. Workflow는 백그라운드로
돌고 완료 시 알림이 오므로, 호출 직후 대화를 막지 말고 진행 상황만
알린 뒤 알림을 기다릴 것. **[2026-08-16] 워크플로 스크립트를 그 세션에서
고쳤다면 이름 대신 `Workflow({scriptPath: '.claude/workflows/quad-handover-audit.js'})`
부를 것** — 이름 해석은 세션 시작 시점 스냅샷이라 편집이 안 실린 채
"돌긴 돌았다"가 되고, 감사 도구에선 그게 조용한 거짓 초록불이다(실측 근거는
`.claude/agents/quad-doc-auditor.md` 상단 배너). 알림이 오면 `python3 .claude/tools/doc-check.py`
ERROR 0을 최종 확인한 뒤 평소 커밋 절차(git status/diff 검토, 메시지 작성)로
넘어갈 것 — **실제 `git commit`은 이 워크플로 안이 아니라 항상 메인 세션이
직접 함**(커밋 전 diff 재검토는 대화형 맥락이 필요해서 워크플로에 위임 안 함).
- **⭐ [2026-08-16 재설계] "핸드오버 준비하고 커밋해" 류 요청엔 되묻지 말고
아래 감사 루프를 돌릴 것.** 단일 `quad-doc-auditor` 패스는 비결정적이라
매번 다 잡는다는 보장이 없다는 게 사용자 지적 — 그래서 병렬 다회로 돌린다.
**처음엔 이걸 Workflow(`quad-handover-audit.js`)로 만들어 픽스까지
서브에이전트에 맡겼으나, 첫 실동에서 에이전트 67개·4.6M 토큰을 쓰고도
수렴하지 못해 2026-08-16에 폐기**(원문은 `archive/`가 아니라 git 히스토리와
`session/2026-08-16-02-*.md`에 있음). 폐기 이유는 셋 — (1) 토큰이 과하게
갈림, (2) 파일별로 픽스 에이전트를 쪼개니 **그 에이전트들이 또 부정확한
서술을 새로 만들어냄**, (3) 서브에이전트는 사용자에게 물을 수가 없어서
판단이 필요한 항목이 그냥 임의 처리됨. 지금 절차:
1. **`quad-doc-auditor`를 한 메시지에서 병렬 호출**(Agent 도구 직접 호출
— Workflow 안 씀). **최소 2개, 변경 사항이 많으면 그만큼 늘릴 것**
사용자 지침(2026-08-16): "최소 2에, 변경 사항이 많아진다면 batch 수가
늘어나는게 맞다고 봄". 감사자는 sonnet이라 패스당 비용이 낮고 역할이
"빠르게 전체를 훑는 것"이라, 커버리지를 넓히는 쪽이 이득인 구조다
(사실 검증은 상위 모델인 메인이 한다). 기준은 라이브 문서 몇 개가
바뀌었나 — 두어 개 고친 정도면 2개, 설계 반전이나 문서 분할처럼
코퍼스 전반을 건드린 변경이면 3~4개.
**감사자 모델은 `sonnet` 유지 — haiku로 더 낮추지 말 것**(사용자
판단, 2026-08-16). 실제로 이 감사가 잡아온 것들(재역전된 배너, 자기
메모리의 stale 서술)은 문맥 추론이 꽤 들어간 발견이었다.
2. **두 리포트를 메인 세션이 합쳐서 검토하고, 수정도 메인이 일괄로 한다.**
픽스를 서브에이전트에 위임하지 말 것(위 (2)번 이유). 감사자는 읽기
전용이고 발견만 리포트한다.
3. **애매하면 임의로 정하지 말고 그 자리에서 사용자에게 보고할 것.**
"의심"으로 온 발견, 설계 판단이 섞인 발견, 출처가 불분명한 인용 등이
여기 해당 — 메인 세션은 사용자에게 물을 수 있다는 게 서브에이전트
대비 유일한 강점이니 그걸 쓸 것.
4. **새 발견이 없는 라운드가 연속 2번 나올 때까지 1~2를 반복**(보통
2~3라운드). **수렴 안 하면 조용히 멈추지 말고 사용자에게 보고할 것**
첫 실동이 실제로 6라운드를 소진하고도 수렴 못 했고(새 발견
28→15→16→7→11→6, 단조 감소가 아니었음), 그때 조용히 끊으면
"감사 통과"로 오독된다.
5. `python3 .claude/tools/doc-check.py`로 ERROR 0을 최종 확인한 뒤 평소
커밋 절차(git status/diff 검토, 메시지 작성)로 넘어갈 것.
**`git commit`은 항상 메인 세션이 직접 함** — 커밋 전 diff 재검토는
대화형 맥락이 필요해서 위임하지 않는다.
- **문서가 쌓이면서 모순/중복/stale 마커가 생기기 쉬움 — 주기적으로 감사할
것.** 다만 위 체크리스트+`doc-check.py`+`quad-doc-auditor`가 자리잡으면
이 감사는 "기계도 서브에이전트도 못 보는 것"(설계 자체의 자기모순, 의사코드
@ -111,6 +139,20 @@ haiku, 일반 작업은 sonnet. 특히 소스코드를 많이 읽어야 하는
위험한 명령을 반복 시도하지 말 것. 그런 상황이면 MCP 없이 할 수 있는 작업만
하거나 대기. 연결 방법은 `HUMAN_TODO.md` 1번 항목 참고(사용자가 Studio에서
베타 기능을 켜줘야 함).
- **⭐ [2026-08-16 신설] 사용자 발언을 근거로 인용할 때는 결론만 적지 말고
"사용자가 무슨 논거로 그렇게 말했는지"까지 남길 것.** 문서에 **사용자
확정**이라고만 적어두면 다음 세션이 그 결정을 재검토할 수도, 인접한
새 문제에 적용할 수도 없다 — 근거가 없으면 그건 권위지 논거가 아니다.
- **어디에 적나**: 논거 원문·대화 흐름은 `.claude/session/`의 그 세션
파일에, 라이브 문서(`base/`/`research/`)는 결론 + 짧은 논거 + 그
세션 파일 포인터를 적는다. 좋은 본보기는
`base/component-composition-plan.md` §1의
`**사용자 확정**("마법 안쓴다 그것도 동의함")` 패턴 — 사용자 어투
그대로 짧게 인용하면 나중에 진위와 맥락을 다 되짚을 수 있다.
- **원칙·규칙을 새로 세우는 발언이면 그 자리에서 이 문서(`conventions.md`)
나 해당 `base/` 문서에 명문화할 것.** 안 그러면 나중 문서들이 출처
없는 원칙을 인용하게 된다 — 위 "설계 원칙" 절의 첫 항목이 실제로 그렇게
2026-08-16까지 출처 없이 인용돼온 사례다.
- **`SAFETY.md` 반드시 지킬 것** — (1) GitHub 등 외부 git 호스팅에 이 레포를
push하지 말 것, 모델의 git 작업 공간은 사용자가 별도로 마련해줄 제한 계정
(예: git.qwreey.moe) 전용으로 국한됨 — 그 계정/원격이 설정되기 전까지는

View file

@ -113,23 +113,6 @@
## 3. 낮은 우선순위 — 열려 있지만 급하지 않음
- **인용 출처 없는 원칙 — "드문 오용/가상 미래 요구까지 방어/최적화하려고
구조를 복잡하게 만들지 않는다"(2026-08-16 신설)** —
`base/modifier-plan.md`의 "미검토로 남긴 것" 문단이 이 원칙을 `CLAUDE.md`
것으로 인용해왔으나, **코퍼스 전체 grep과 `git log -S`(CLAUDE.md 전
히스토리) 어디에도 그런 서술이 없음** — 즉 존재한 적 없는 출처였음(같이
발견된 다른 두 건은 인용 대상만 틀린 것이라 실제 소스로 재조준 완료:
`research/v1-compat-plan.md`→`base/component-composition-plan.md`+
`base/store-plan.md`, `research/pre-implementation-audit.md`→`ROADMAP.md` +
`question.md`(이 문서)).
선택지는 (a) 이 원칙을 `.claude/conventions.md`에 정식 명문화 —
실제로 여러 설계 결정이 이 취지로 내려졌으니 한 번 적어두면 앞으로도
인용 가능, (b) 명문화하지 않고 `modifier-plan.md`의 지역 논거로만 둠
(이 경우 그 문단의 정정 배너를 지우고 문장만 남기면 됨).
**어느 쪽이든 그 문단의 결론("지금은 fast-path 빌더를 설계하지 않음")은
영향받지 않음** — 순수하게 "이 원칙을 프로젝트 공식 관례로 승격할
것인가"만 묻는 질문.
- **`Operator` 콤비네이터 슈가 네임스페이스 이름+포함 범위(2026-08-12 신설,
같은 날 후속으로 외부 리서치 완료)** — `Sum`/`Product`/`Not`/비트연산 등
`:Compute`/`:Apply`용 슈가 함수 모음의 이름. 흔한 단어라 top-level

View file

@ -77,7 +77,7 @@ v1 폐기 API/버그/구조 결함 전부 v2 설계를 정당화하는 내부
넘어오는 기존 사용자용 마이그레이션 가이드**가 나중에 별도 문서로 계획된다면
그때만 재사용 가치 있음 — 지금 3축 어디에도 해당 없음.
### bind-system-plan.md (943줄, 최대 문서)
### bind-system-plan.md
- 초심자: Source/Store/State 기본 정의+생성자, State 읽기 전용 규칙 / dot-access가 값 읽기 1급 경로 / `:With`+`:Compute` 최소 사용법 / Ref 기본 개념(children 배열에 직접 놓기, 별도 `CreatedRef` 없음) / 이벤트 self 미채택 기본 규칙+문자열 키 / 인스턴스 생성(제네릭+정적 필드) / 라이브러리 초기화 3줄(`RobloxFactory(QuadBase)`)
- api: `state:Observer(fn)` 사용법(→심화: weak-table 내부 인덱싱) / `:Subscribe()`/`:Unsubscribe()` 시그니처(→심화: 강참조 레지스트리 구조) / Ref 일반화 표면 API(→심화: "왜 값이 아니라 콜백인가") / 이벤트 store-bind 존재+권장 안 함 가이드(→심화: 엔지니어링 비용 근거) / 핸들러 3종 계약(`isHandlable`/`priority`/`process` — `process`가 자기 retract 클로저를 반환, 2026-08-13 다섯 번째 세션에 4종에서 축소) / `AttributeKey<T>` 특수 키(2026-08-11 아홉 번째 세션에 `Attribute<T>`에서 개명, 그룹 값 `Attribute(...)`와 구분 — 확정됨)
- 심화: push-invalidate/pull-recompute 전파 모델+"관측해야 실체화된다" 원칙+`previous` 캐비엇 / **왜 State를 Modifier처럼 플래튼하지 않는가**(이미 문서화 완료, 아래 3번 참고) / Store가 Store를 못 담는 이유 / 이벤트 self 미채택 4가지 근거 / store-bind 재귀 래핑 내부 메커니즘, retract가 Destroy 시 호출 안 되는 이유 / 같은 팩토리 재호출 no-op·다른 팩토리 충돌 에러 내부 안전장치

View file

@ -1315,3 +1315,34 @@ stale")가 틀렸음**을 잡아냄(실제로는 `initreq/tbox/CLAUDE.md`를 가
서술하던 것 등을 잡음. 워크플로도 개선 — 반환값에 `findings` 추가(커밋 전
diff 리뷰 근거), `totalFindingsFixed`→`findingsSentToFix` 개명(과대계상),
반영 에이전트 `model: 'sonnet'` 명시.
**[같은 세션 후반, 사용자 결정 2건]** (1) **감사 루프 구조 재설계** — 첫
실동이 에이전트 67개·4.6M 토큰을 쓰고도 수렴 못 한 걸 보고 사용자가
"토큰이 미친듯이 갈린다 / 감사 두 개, 끝나면 한 명이 일괄처리 / 마치
code-review 처럼"으로 구조 자체를 바꾸기로 함. 폐기 근거 셋: 토큰 과다,
파일별 픽스 에이전트가 **또 부정확한 서술을 생산**하고 파일 충돌 회피에
처리 낭비, 서브에이전트는 **사용자에게 못 물어서** 판단 항목이 임의 처리됨.
메커니즘은 사용자가 Agent 직접 호출을 선택 →
`.claude/workflows/quad-handover-audit.js` **삭제**, 절차는 `conventions.md`
"작업 방식"이 소스(감사 2개 병렬 → 메인이 일괄 검토·수정 → 애매하면 즉시
사용자 보고 → 새 발견 없는 라운드 2연속까지 반복, 수렴 못 하면 보고하고
멈춤). 감사 에이전트 출력 형식에 **`사용자 판단`** 등급 추가. (2) **출처
없던 원칙을 명문화** — `modifier-plan.md`가 인용해온 "드문 오용/가상 미래
요구까지 방어/최적화하려고 구조를 복잡하게 만들지 않는다"를 (a)안으로
`conventions.md`에 "설계 원칙" 절 신설해 정식화. 사용자 추정("내가 세션 중
했던 말을 옮겨적지 않은 경우로 보이기도 함")에서 파생돼 **"사용자 발언을
인용할 때는 결론만 적지 말고 논거까지 남긴다"는 새 관례**도 같이 신설됨.
**[같은 세션, 재정정]** 위 "워크플로 정의 해석" 결론은 새 절차의 첫 감사
라운드가 더 정밀하게 갈아치웠다 — **정의 파일은 워킹트리가 아니라 커밋된
HEAD에서 읽힌다**(감사 패스가 받은 지시문이 *세션 도중 만든* HEAD 커밋의
blob과 바이트 단위로 동일, 메인이 `git rev-parse`로 독립 확인). 즉 규칙이
"재시작"에서 **"고쳤으면 커밋한 뒤 돌린다"**로 싸짐. 이 정정이 오래
미확정이던 (d)도 같이 풀었음 — **`memory: project`가 Write/Edit을
딸려온다는 진단이 맞았고**, "빼도 그대로"로 보였던 건 그 제거가 아직 커밋
안 됐던 탓. 남은 미해결은 `tools:` 필드 미반영뿐(적힌 Grep/Glob이 안
주어지고 안 적은 `advisor`가 주어짐). 첫 감사 라운드는 그 외에 자기
메모리 2개의 stale 서술(폐기된 워크플로를 살아있는 것처럼 서술),
`documentation-content-map.md`의 "943줄, 최대 문서"(실측 203줄, 최대는
`slot-plan.md` 1970줄), `README.md`의 패스 수 하드코딩을 잡았고, 직전
커밋의 미재감사 6건은 **회귀 없음**으로 확인해 `todos.md`의 ⚠️ 블록을 닫음.

View file

@ -117,3 +117,130 @@
- **`.claude/agent-memory/`** — 감사 에이전트가 스스로 쓰는 영속 메모리가
생겼고 지금 untracked. 커밋할지 `.gitignore`로 뺄지는 사용자 판단.
- **`modifier-plan.md:536` 원칙** — `question.md` 3번, 사용자 판단 대기.
## 6. [사용자 결정] 감사 루프 재설계 — 워크플로 폐기, 메인 일괄 처리
첫 실동 결과(에이전트 67개 / 4.6M 토큰 / 수렴 실패)를 보고 사용자가 구조
자체를 바꾸기로 함. **사용자 논거 그대로**:
> "토큰이 미친듯이 갈린다는거야. 차라리 감사 두개, 끝나면 한명이 일괄처리
> (보니까, 지금은 처리를 전부 분리해서 함. 이로 인해서 또 다시 부정확
> 문서가 생성될 수도 있고, 파일 겹침을 막기 위해서 너무 많은 처리를
> 수행함). 가 맞다고 생각해. 정확히는, 문제 사항을 처음 편집을 했던 최상위
> 에이전트가 받고, 검토 한 다음 처리하고 다시 2개 정도의 감사 에이전트를
> 돌리고, 마치 code-review 처럼 되는게 맞는듯."
>
> "이런 구조로 인해서 만일 유저의 판단이 필요해보이더라도, 에이전트들이
> 유저에게 입력 요구하기가 까다로움. 메인 에이전트다 하면 뭔가 애매하다
> 싶으면 바로 유저에게 보고하게 하는게 맞아보임"
정리하면 폐기 근거 셋 — (1) 토큰 과다, (2) 파일별 픽스 에이전트가 **또
부정확한 서술을 새로 생산**하고 파일 충돌 회피에만 처리를 낭비, (3)
서브에이전트는 사용자에게 못 물어서 판단이 필요한 항목이 임의 처리됨.
메커니즘은 사용자가 **Agent 직접 호출**을 골랐다(워크플로 유지안과 비교
제시 → "기계가 가장 적고 name/scriptPath 스냅샷 함정이 아예 사라짐" 쪽).
그래서 `.claude/workflows/quad-handover-audit.js`는 **삭제**됐고 절차는
`conventions.md` "작업 방식"이 소스가 됐다:
1. `quad-doc-auditor` 2개를 한 메시지에 병렬 호출
2. 메인이 두 리포트를 합쳐 검토하고 **수정도 메인이 일괄로** 수행
3. 애매하면 임의 처리 말고 **즉시 사용자에게 보고**
4. 새 발견 없는 라운드 2연속까지 반복, **수렴 못 하면 사용자에게 보고하고 멈춤**
5. `doc-check.py` ERROR 0 확인 후 커밋
감사 에이전트 정의에도 출력 형식에 **`사용자 판단`** 등급을 추가했다 —
`의심`에 묻지 말고 따로 빼라는 것(메인이 사용자에게 물을 수 있다는 게 이
구조의 핵심인데, 감사자가 애매한 걸 "이렇게 고치면 됨"으로 넘기면 그
강점이 죽는다).
## 7. [사용자 결정] 출처 없던 원칙 — (a) 명문화 + 새 인용 관례
`modifier-plan.md:536`이 인용해온 "드문 오용/가상 미래 요구까지
방어/최적화하려고 구조를 복잡하게 만들지 않는다"는 **선택지 (a) 채택**으로
`conventions.md`에 "설계 원칙" 절을 신설해 명문화했다.
**사용자 논거**: "(a) 하면 될것 같아. 그리고 추측하건데, 내가 세션 중
했던 말을 옮겨적지 않은 경우로 보이기도 함." — 즉 원칙 자체는 사용자가
실제로 세웠는데 문서화가 누락됐다는 판단.
여기서 파생된 요구로 **새 관례**를 `conventions.md`에 추가: 사용자 발언을
근거로 인용할 때 결론("사용자 확정")만 적지 말고 **무슨 논거로 그렇게
말했는지**까지 남길 것. 논거 원문은 `session/`에, 라이브 문서엔 결론 +
짧은 논거 + 세션 파일 포인터. 본보기는
`base/component-composition-plan.md` §1의 `**사용자 확정**("마법 안쓴다
그것도 동의함")` 패턴. 원칙·규칙을 세우는 발언이면 **그 자리에서**
명문화할 것 — 안 그러면 이번처럼 출처 없는 원칙을 후속 문서들이 인용하게
된다.
## 8. [실측] "감사자가 opus로 돈다"는 관찰 — 감사자가 아니라 픽스 에이전트였음
사용자가 "quad-doc-auditor가 opus로 돌고 있더라"고 지적해서 폐기된 워크플로
실행의 트랜스크립트를 전수 집계함(각 턴을 실제로 생성한 모델 = 최상위
`message.model` 기준):
| 에이전트 종류 | 모델 | 어시스턴트 턴 수 |
|---|---|---|
| `quad-doc-auditor` | `claude-sonnet-5` | 1550 |
| `general-purpose`(픽스) | `claude-opus-5` | 797 |
**감사자는 한 턴도 opus로 돈 적이 없다** — frontmatter `model: sonnet`
정상 반영된다. opus로 돈 건 **픽스 에이전트 49개**였고, 그게 4.6M 토큰의
주범이다. 픽스 쪽에 `model: 'sonnet'`을 명시하는 편집을 해뒀었지만 그
편집이 실린 스크립트가 실행되지 않았다 — 이름 호출 스냅샷 문제(§2)를
그대로 밟은 것. 지금은 픽스 에이전트 자체가 없어져 이 경로가 사라졌다.
**단, 감사자 트랜스크립트에도 `claude-opus-5` 문자열이 실제로 섞여 있다** —
위치가 `message.usage.iterations[].model`이고 감사자 18개 중 14개에 각각
딱 한 번씩만 나온다(그 메시지의 최상위 `message.model`은 전부 sonnet).
사용량 회계 필드지 생성 모델이 아니다. **모델을 확인할 때 문자열 grep만
하면 여기서 오독한다** — 반드시 `message.model`로 집계할 것. 이 세션도
처음엔 `grep -o '"model":"..."'`로 확인했었고, 그 방법이 이 함정에
그대로 노출돼 있었다.
## 9. [사용자 결정] 감사 패스 수와 감사자 모델
- **패스 수**: "최소 2에, 변경 사항이 많아진다면 batch 수가 늘어나는게
맞다고 봄" — 고정 2가 아니라 변경 규모에 따라 스케일. 사용자 논거는
구조 설명 쪽이었음: "애초에 빠르게 전체를 훑는거고, 의견에 대한
사실검증은 상위자가 하니까. 빠르게 여러패스가 훑어지는게 유리한 구조임."
- **감사자 모델**: "haiku 는 쓰지 않는편이 나아보임" — sonnet 유지.
사용자가 별도 논거를 붙이지는 않았음(추측해 적지 않는다). 이 세션이
관측한 방증만 덧붙이면, 이번 감사가 잡아온 발견들은 문맥 추론이 꽤
들어간 것들이었다.
둘 다 `conventions.md`의 감사 루프 절차에 반영됨.
## 10. [재정정] §2의 "세션 시작 스냅샷"은 틀렸다 — *커밋된 HEAD*가 맞다
새 절차의 첫 감사 라운드에서 한 패스가 **자기가 받은 지시문이 blob
`92b9484`, 즉 그 시점 HEAD 커밋의 버전과 바이트 단위로 같다**고 보고했고,
메인 세션이 `git rev-parse HEAD:.claude/agents/quad-doc-auditor.md`로 독립
확인했다. 그 HEAD는 **세션 도중에 만든 커밋**(`1343796`)이지 세션 시작
시점(`8aeec76`)이 아니다. 따라서:
- 정의 파일은 **커밋될 때마다 갱신되고, 커밋 안 된 워킹트리 편집만 안
보인다.** §2가 관측한 워크플로 사례도 그때 HEAD가 곧 세션 시작 상태였을
뿐이라 이 설명과 모순되지 않는다(그 관측이 틀린 게 아니라 일반화가 틀렸음).
- 실무 규칙이 **"재시작"에서 "커밋"으로 싸졌다.** 워크플로엔 `scriptPath`
우회가 따로 있지만, 지금은 워크플로 자체를 안 쓰므로 규칙은 하나다 —
**정의를 고쳤으면 커밋한 뒤 감사를 돌린다.**
**이 정정이 (d)도 같이 풀었다.** "`memory: project`를 뺐는데도 Write/Edit이
그대로 주어진다"며 미확정으로 남겨뒀던 건, **그 제거가 아직 커밋 안 돼서
반영이 안 됐던 것**이다. 실제로 옵션이 빠진 정의가 커밋된 뒤 돈 감사자는
Write/Edit이 없었고 `.claude/agent-memory/`에 쓰기도 없었다(파일 mtime이
전부 그 이전 시각). 진단은 처음 것이 맞았다.
**남은 미해결은 `tools:` 필드**다 — frontmatter에 적힌 Grep/Glob이 실제로는
안 주어지고, 적지 않은 `advisor`가 주어진 라운드가 있었다. 그래서 감사자의
읽기 전용은 계속 **행동 규약**으로 지킨다.
교훈 하나: 이 정정은 감사자의 *자기 보고*에서 출발했다. 앞서 이 세션은
자기 보고를 "근거가 약하다"고 깎았다가 다시 "미해결 불일치"로 올렸는데,
이번엔 그 자기 보고가 **검증 가능한 형태**(blob 해시)로 왔기 때문에
메인이 1차 근거로 승격시킬 수 있었다. 자기 보고 자체가 약한 게 아니라
**대조 가능한 형태로 오는가**가 갈림점이다.

View file

@ -127,24 +127,29 @@
**확인됨**(첫 실측 때 전원 `agentType not found`였던 건 `.claude/agents/`
세션 도중 생긴 디렉토리였기 때문, 재시작으로 해소), (c) frontmatter
`model: sonnet` 반영 — **확인됨**(서브에이전트 트랜스크립트에
`claude-sonnet-5` 기록), (d) **미해소** — 읽기 전용인데 Write/Edit이
주어지는 원인. `memory: project`를 뺐지만 그 뒤 재시작 없이 돌린
관찰뿐이라 진단 미확정이고, **다음 세션 시작 직후에 한 번 확인하면 됨**.
결론 날 때까지 읽기 전용은 도구 유무가 아니라 프롬프트의 행동 규약으로
지킨다.
`claude-sonnet-5` 기록), (d) **해소** — 읽기 전용인데 Write/Edit이
주어지던 원인은 `memory: project`가 맞았음(아래 부수 확정 참고). 다만
`tools:` 필드가 그대로 반영되지 않는 건 **여전히 미해결**이라, 읽기
전용은 도구 유무가 아니라 프롬프트의 행동 규약으로 계속 지킨다.
**부수 확정 — 워크플로는 `name`으로 부르면 세션 시작 스냅샷, `scriptPath`
부르면 디스크 실시간**(둘 다 1차 증거 있음). **워크플로 정의를 고쳤으면
재시작하지 말고 `scriptPath`로 호출할 것** — 이름으로 부르면 옛 정의로
돌면서 새 정의로 돈 것처럼 보인다. 에이전트 정의도 stale하게 로드된 정황이
있으나 그건 자기 보고뿐이라 근거 등급이 낮고, 우회 수단도 없어 재시작이
보수적 해법. 상세는 `.claude/agents/quad-doc-auditor.md` 상단 배너가 소스.
**부수 확정 — 정의 파일(에이전트·워크플로)은 워킹트리가 아니라 커밋된
HEAD에서 읽힌다.** 그래서 **정의를 고쳤으면 커밋한 뒤에 감사를 돌릴 것**
(재시작 불필요). 앞서 "세션 시작 시점 스냅샷"이라 적었던 건 틀렸음 —
감사 패스가 받은 지시문이 *세션 도중 만든* HEAD 커밋의 blob과 바이트
단위로 같다는 게 확인됐다. 이 정정으로 위 (d)도 같이 풀렸다: **`memory:
project`가 Write/Edit을 딸려온다는 진단은 지지됨**(옵션이 살아있던 정의로
돈 감사자는 Write/Edit을 받고 실제로 메모리를 썼고, 옵션이 빠진 정의가
커밋된 뒤 돈 감사자는 둘 다 없었음 — "빼도 그대로"로 보였던 건 제거가
아직 커밋 안 됐던 탓). **남은 미해결은 `tools:` 필드가 그대로 반영되지
않는다는 것**(적힌 Grep/Glob이 안 주어지고, 안 적은 `advisor`가 주어진
라운드가 있었음). 상세는 `.claude/agents/quad-doc-auditor.md` 상단 배너가
소스.
**⚠️ [2026-08-16] 코퍼스에 재감사 안 된 수정이 들어있음** — 첫 실동이
수렴하지 못하고 최대 라운드로 끊겨서, 마지막 라운드의 새 발견 6건은
반영만 되고 다시 감사되지 않았다(diff는 사람이 손으로 검토했고 핵심
주장은 1차 근거로 확인했지만 dry 라운드와 같지는 않음). **다음
`quad-handover-audit` 실동의 첫 임무가 이걸 재확인하는 것.** 겸해서
`MAX_ROUNDS`/"연속 dry 2회" 조건도 재검토 대상(새 발견이 단조 감소하지
않았음: 28→15→16→7→11→6). M0/설계 게이트와 무관.
**[2026-08-16 닫힘] 재감사 안 됐던 수정 6건은 확인 완료** — 첫 실동이
수렴 못 하고 끊겨 마지막 라운드분이 재감사 없이 커밋됐었는데, 새 절차의
첫 라운드(감사 2개 병렬)가 그 셋(spikes 개수 단일화, `slot-plan.md`
재역전 배너, `doc-check.py` docstring)을 다시 훑어 **회귀 없음**으로
확인했다. 한 패스는 구세대 트리(`8aeec76`)와 현재본의 WARN 목록을 직접
diff해서 대조했고, 그 구간에 오히려 절 참조 오류 2건이 해소된 것도
확인됨. M0/설계 게이트와 무관.

View file

@ -1,181 +0,0 @@
export const meta = {
name: 'quad-handover-audit',
description: '커밋 전 .claude/ 코퍼스 정합성을 quad-doc-auditor 병렬·다회 감사로 수렴시킴',
whenToUse: '사용자가 "핸드오버 준비하고 커밋해" 류로 요청했을 때, 커밋 전에 자동으로 돌릴 것. 단일 감사 패스는 비결정적이라 놓치는 게 있을 수 있으므로, 라운드마다 독립된 감사를 병렬로 여러 번 돌리고, 새 발견이 없는 라운드가 연속으로 나올 때까지 반복해 수렴시킨다. 시간보다 정확성을 우선하는 사용자 요청에 따라 기본 워크플로 크기 가이드라인(15 에이전트 이하)을 의도적으로 초과할 수 있음.',
phases: [
{ title: 'Audit', detail: 'quad-doc-auditor 서브에이전트를 라운드당 병렬로 여러 번' },
{ title: 'Fix', detail: '라운드에서 나온 새 발견을 파일별로 반영' },
],
}
// 실측 편의를 위해 조정 가능한 상수. 과거 세션의 수동 감사가 보통
// 4~6라운드 안에 수렴했음(예: 8→7→11→9→4→0, 9→2→3→2→0→0) — MAX_ROUNDS는
// 그보다 여유를 두되 무한루프는 막는 안전판.
const PASSES_PER_ROUND = 3
const DRY_ROUNDS_TO_CONVERGE = 2
const MAX_ROUNDS = 6
const FINDINGS_SCHEMA = {
type: 'object',
properties: {
findings: {
type: 'array',
items: {
type: 'object',
properties: {
file: { type: 'string', description: '레포 루트 기준 상대 경로' },
line: { type: 'number', description: '해당 줄 번호(모르면 0)' },
issue: { type: 'string', description: '무슨 문장이 무엇과 모순/stale인지 한 문장' },
fix: { type: 'string', description: '어떻게 고치면 되는지 한 문장' },
confidence: { type: 'string', enum: ['확실', '의심'] },
},
required: ['file', 'issue', 'fix', 'confidence'],
},
},
},
required: ['findings'],
}
const AUDIT_PROMPT =
'핸드오버 준비 감사 라운드다. 너의 정해진 절차대로 .claude/ 코퍼스를 ' +
'처음부터 독립적으로 감사해라. 다른 병렬 패스가 이미 뭘 찾았는지는 ' +
'모른다 — 그걸 의식하지 말고 빠짐없이 훑어라.'
function keyOf(f) {
return `${f.file}:${f.line || 0}:${(f.issue || '').slice(0, 40)}`
}
function fixPrompt(file, items) {
const lines = items
.map((f) => `- (줄 ${f.line || '?'}, ${f.confidence}) ${f.issue} → 제안: ${f.fix}`)
.join('\n')
return (
`아래는 quad-doc-auditor가 "${file}"에서 찾은 stale/모순 서술이다. ` +
`이 파일을 읽고 .claude/conventions.md의 관례(한국어 서술, 최소 수정, 뒤집힌 결정은 ` +
`archive/로 이전+포인터, 날짜 없는 시한부 주장엔 날짜 붙이기)에 맞춰 직접 ` +
`고쳐라. "의심"으로 표시된 항목은 실제로 문제인지 먼저 확인하고, 문제가 ` +
`아니면 건드리지 말고 넘어가라(억지로 고치지 말 것).\n\n${lines}`
)
}
const seen = new Set()
const roundLog = []
// 실제 발견 내용 자체를 누적한다 — 카운트만 돌려주면 호출한 세션이
// 커밋 전 diff 리뷰(관례상 필수)를 할 때 "이 수정이 왜 들어갔는지"를
// 알 길이 없어서 journal.jsonl을 뒤져야 한다(2026-08-16 지적).
const appliedFindings = []
let dry = 0
let round = 0
while (dry < DRY_ROUNDS_TO_CONVERGE && round < MAX_ROUNDS) {
round++
phase('Audit')
log(`라운드 ${round} — quad-doc-auditor 병렬 ${PASSES_PER_ROUND}`)
const passes = await parallel(
Array.from({ length: PASSES_PER_ROUND }, (_, i) => () =>
agent(AUDIT_PROMPT, {
label: `audit-r${round}-${i}`,
phase: 'Audit',
agentType: 'quad-doc-auditor',
schema: FINDINGS_SCHEMA,
})
)
)
// ⚠️ 가짜 초록불 방지 — 2026-08-16 첫 실측에서 실제로 당한 것.
// 감사 패스가 전부 에러로 죽으면(예: agentType 미등록) fresh가 비어
// "깨끗한 라운드"와 구분이 안 되고, 그대로 converged:true가 나와서
// **아무것도 감사 안 하고 통과 도장을 찍는다**. 감사 도구의 최악
// 실패 모드라 살아남은 패스 수를 명시적으로 확인한다.
const alive = passes.filter(Boolean)
if (alive.length === 0) {
throw new Error(
`라운드 ${round}: 감사 패스 ${PASSES_PER_ROUND}개가 전부 실패 — ` +
`감사가 실제로 수행되지 않았으므로 수렴 판정을 낼 수 없음. ` +
`(quad-doc-auditor가 등록됐는지 확인: .claude/agents/ 를 만든 직후라면 ` +
`Claude Code 재시작 필요)`
)
}
if (alive.length < PASSES_PER_ROUND) {
log(
`⚠️ 라운드 ${round}: 감사 패스 ${PASSES_PER_ROUND}개 중 ` +
`${PASSES_PER_ROUND - alive.length}개 실패 — 커버리지가 그만큼 얕음`
)
}
const all = alive.flatMap((p) => p.findings || [])
const fresh = all.filter((f) => !seen.has(keyOf(f)))
if (!fresh.length) {
dry++
log(`라운드 ${round}: 새 발견 없음 (연속 dry ${dry}/${DRY_ROUNDS_TO_CONVERGE})`)
roundLog.push({ round, fresh: 0, dry })
continue
}
dry = 0
fresh.forEach((f) => seen.add(keyOf(f)))
log(`라운드 ${round}: 새 발견 ${fresh.length}건 — 파일별로 반영 시작`)
const byFile = {}
for (const f of fresh) {
;(byFile[f.file] ||= []).push(f)
}
phase('Fix')
const fileEntries = Object.entries(byFile)
const fixed = await parallel(
fileEntries.map(([file, items]) => () =>
agent(fixPrompt(file, items), {
label: `fix:${file}`,
phase: 'Fix',
agentType: 'general-purpose',
// 토큰 관례(.claude/conventions.md "일반 작업은 sonnet") — 감사
// 패스는 frontmatter로 이미 sonnet, 반영 쪽만 메인 모델(Opus)을
// 상속하고 있어서 맞춤.
model: 'sonnet',
})
)
)
// 반영 에이전트가 죽으면 그 발견들을 `seen`에서 빼둔다 — 안 그러면
// 다음 라운드 감사가 같은 문제를 다시 찾아와도 dedup에 걸려 조용히
// 사라지고, 안 고쳐진 채로 수렴 판정이 난다(위와 같은 클래스의 버그).
const failedFiles = []
fixed.forEach((r, i) => {
const [file, items] = fileEntries[i]
if (r === null) {
failedFiles.push(file)
items.forEach((f) => seen.delete(keyOf(f)))
} else {
items.forEach((f) => appliedFindings.push({ round, ...f }))
}
})
if (failedFiles.length) {
log(`⚠️ 라운드 ${round}: 반영 실패 ${failedFiles.length}건 — ${failedFiles.join(', ')} (다음 라운드에서 재시도)`)
}
roundLog.push({
round,
fresh: fresh.length,
files: fileEntries.map(([f]) => f),
fixFailed: failedFiles,
})
}
const converged = dry >= DRY_ROUNDS_TO_CONVERGE
if (!converged) {
log(`${MAX_ROUNDS}라운드 안에 수렴 못 함 — 남은 문제는 사람이 볼 것`)
}
return {
converged,
rounds: round,
// ⚠️ "반영 에이전트에게 넘긴 발견"의 수이지 "실제로 고쳐진" 수가 아니다
// — "의심"으로 표시된 항목은 반영 에이전트가 확인 후 건드리지 않고
// 넘어갈 수 있다. 실제 반영 여부는 항상 `git diff`로 확인할 것.
findingsSentToFix: appliedFindings.length,
findings: appliedFindings,
roundLog,
}