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:
parent
134379632f
commit
07c77e5a5a
11 changed files with 307 additions and 278 deletions
|
|
@ -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`) — 읽기 전용 리서치 소스, 여기 내용을 옮기지 말고 항상 원본 그대로 유지 |
|
||||
|
||||
|
|
|
|||
|
|
@ -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 추가] "사용자 판단 필요"를 별도로 표시해라.** 설계 판단이
|
||||
섞였거나(어느 쪽이 맞는지가 취향/방향 문제), 출처가 불분명한 인용이거나,
|
||||
고치는 방법이 둘 이상인데 고르는 근거가 문서에 없는 발견은 `의심`에 묻지
|
||||
말고 **`사용자 판단`**으로 따로 빼라. 너를 부른 메인 세션은 사용자에게
|
||||
직접 물을 수 있고, 그게 지금 감사 루프의 핵심 설계다 — 네가 애매한 걸
|
||||
그냥 "이렇게 고치면 됨"으로 넘기면 그 강점이 죽는다.
|
||||
|
||||
발견이 없으면 "발견 없음"이라고 그대로 보고해라. 그럴듯해 보이려고 사소한
|
||||
문체 지적을 지어내지 마라 — 이 감사의 가치는 정확도지 발견 개수가 아니다.
|
||||
|
|
|
|||
|
|
@ -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` "작업 방식" 절).
|
||||
|
|
|
|||
|
|
@ -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 다섯 번째 세션 후속)
|
||||
|
|
|
|||
|
|
@ -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) 전용으로 국한됨 — 그 계정/원격이 설정되기 전까지는
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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·다른 팩토리 충돌 에러 내부 안전장치
|
||||
|
|
|
|||
|
|
@ -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`의 ⚠️ 블록을 닫음.
|
||||
|
|
|
|||
|
|
@ -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차 근거로 승격시킬 수 있었다. 자기 보고 자체가 약한 게 아니라
|
||||
**대조 가능한 형태로 오는가**가 갈림점이다.
|
||||
|
|
|
|||
|
|
@ -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/설계 게이트와 무관.
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
Loading…
Reference in a new issue