docs: CLAUDE.md 4분할(매 세션 로드 1537→354줄), 워크플로 가짜 초록불 수정

## CLAUDE.md 분할
1537줄이라 (a) 사람이 검토 불가, (b) 공식 권장치(파일당 200줄) 7.7배
초과로 지침 준수도 자체가 저하, (c) 긴 파일 편집 시 에이전트 실수 증가.

  CLAUDE.md (39줄, 진입점)
  ├─ @.claude/conventions.md      언어/모델 관례 + 작업 방식
  ├─ @.claude/project-context.md  프로젝트 설명 + 문서 구조
  └─ @.claude/todos.md            지금 할 일
     .claude/session-summary.md   ← import 안 함(의도적, 온디맨드)

세션 히스토리 1231줄(전체의 80%)은 그 문서 스스로 "항상 읽을 필요 없음,
base/가 소스"라고 명시해온 색인이라 @import에서 뺐음. 내용 유실 없음
(1537→1647줄, 추가 헤더만큼 증가).

주의: @import는 컨텍스트를 줄이지 않음(전부 로드됨). 분할이 사는 건
사람 검토성 + 편집 정확도 + 파일 단위 자동생성 가능성.
CLAUDE.md 계열의 블록 HTML 주석은 주입 전 제거되므로 지시는 본문에 쓸 것.

## 워크플로 가짜 초록불 수정
첫 실측에서 감사 에이전트 6개 전원 실패했는데 converged:true가 나왔음
(전멸하면 fresh가 비어 "깨끗한 라운드"와 구분 불가). 감사 도구 최악의
실패 모드라 (1) 전멸이면 throw, (2) 반영 에이전트 실패 시 그 발견을
seen에서 빼 다음 라운드가 재시도하도록 수정.

## 부수
- 분할로 깨진 상호참조 20여 곳 정정(병렬 에이전트 3개).
  ref-plan.md:541의 사전 존재 오류(→ pre-implementation-audit 1-5)도 정정.
- doc-check.py: 새 파일 4개를 OURS에 등록(안 하면 깨진 참조가 WARN으로만
  잡힘), is_history()로 session-summary.md를 archive/와 같이 면제.
- doc-include-plan.md: 목적지가 통째로 생성되는 파일이 되면서 양방향
  마커 설계의 절반(목적지 마커)이 불필요해져 단방향 생성으로 단순화.

doc-check.py ERROR 0 유지.

미해결: session-summary.md 자동생성 미착수(91개 세션 파일 마커 삽입 필요),
orphan 인용 3건(modifier-plan.md:536, v1-compat-plan.md:50,
pre-implementation-audit.md:434 — 분할 이전부터 존재).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HvEKn9f67kkx2nGLG8PsP
This commit is contained in:
qwreey 2026-08-16 01:35:02 +09:00
parent a1c0e44258
commit 8aeec7644f
Signed by: qwreey
GPG key ID: D28DB79297A214BD
24 changed files with 2035 additions and 1578 deletions

View file

@ -1,9 +1,21 @@
# .claude/ — quad-v2 계획/설계 문서 색인 # .claude/ — quad-v2 계획/설계 문서 색인
이 레포 전체가 quad-v2(재작성) 프로젝트이므로, webmanager류 서브프로젝트 구분 없이 이 레포 전체가 quad-v2(재작성) 프로젝트이므로, webmanager류 서브프로젝트 구분 없이
`.claude/` 바로 아래에 전부 있음. **루트 `CLAUDE.md`가 현재 상태+TODO 색인의 `.claude/` 바로 아래에 전부 있음. **루트 `CLAUDE.md`가 진입점** — 먼저 그걸 보고,
최종 소스** — 먼저 그걸 보고, 특정 결정의 자세한 근거/논의가 필요할 때만 아래 특정 결정의 자세한 근거/논의가 필요할 때만 아래 개별 문서를 열어볼 것.
개별 문서를 열어볼 것.
**[2026-08-16 재구조화]** `CLAUDE.md`가 1537줄까지 불어나 사람이 검토할 수
없고 지침 준수도도 떨어져서, 주제별로 쪼개고 `@import`로 다시 합침. 지금
`CLAUDE.md`는 39줄짜리 진입점이고 실제 내용은 아래 세 파일에 있음(전부 매
세션 자동 로드됨):
| 파일 | 무엇이 들어있나 |
|---|---|
| `conventions.md` | 언어/모델 관례 + **작업 방식**(핸드오버 체크리스트, `doc-check.py`, `quad-doc-auditor`, SAFETY 준수 등 에이전트가 따라야 할 절차 전부) |
| `project-context.md` | 이 프로젝트가 뭔지 + 계획 문서 구조(폴더별 성격 요약 — 상세 색인은 이 README가 소스) |
| `todos.md` | 지금 할 일(우선순위순). 가장 자주 바뀜 |
| `session-summary.md` | 세션별 2~4줄 요약 색인. **`@import` 안 됨(의도적)** — 1231줄을 매 세션 올릴 이유가 없어 온디맨드로 둠, 선행 맥락이 필요할 때 grep해서 열 것. 자동생성 전환 예정(`research/doc-include-plan.md`) |
| `question.md` | 사용자가 답해야 할 열린 질문(우선순위순) |
## 폴더 기준 ## 폴더 기준

View file

@ -1,6 +1,6 @@
--- ---
name: quad-doc-auditor name: quad-doc-auditor
description: quad 프로젝트의 `.claude/` 설계 문서 코퍼스(base/research/reference/archive, README.md, question.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, 루트 CLAUDE.md/ROADMAP.md/HUMAN_TODO.md)에서 `doc-check.py`가 못 잡는 의미론적 stale/모순을 찾는다. 설계 결정이 뒤집히거나 확정되는 등 이 코퍼스에 중대한 변경이 있은 뒤, 특히 그런 변경을 커밋하기 전에 사용. 읽기 전용 — 문제를 리포트만 하고 직접 고치지 않는다.
tools: Read, Grep, Glob, Bash tools: Read, Grep, Glob, Bash
model: sonnet model: sonnet
memory: project memory: project
@ -34,7 +34,8 @@ memory: project
부정하는 *본문 bullet*은 안 고쳐진 경우 — 배너만 보고 넘어가지 말고 부정하는 *본문 bullet*은 안 고쳐진 경우 — 배너만 보고 넘어가지 말고
반드시 본문까지 읽어라. 반드시 본문까지 읽어라.
4. 뒤집힌 결정의 원문이 `archive/`로 옮겨지지 않고 라이브 문서(`base/`, 4. 뒤집힌 결정의 원문이 `archive/`로 옮겨지지 않고 라이브 문서(`base/`,
`research/`, `reference/`, `README.md`, `CLAUDE.md`, `ROADMAP.md`)에 `research/`, `reference/`, `README.md`, `conventions.md`,
`project-context.md`, `todos.md`, `CLAUDE.md`, `ROADMAP.md`)에
"히스토리로만 보존" 같은 말과 함께 전체 서술로 남아있는지 확인해라. "히스토리로만 보존" 같은 말과 함께 전체 서술로 남아있는지 확인해라.
앞에서부터 읽는 구현자가 그걸 "확정"으로 오인할 여지가 있으면 문제다. 앞에서부터 읽는 구현자가 그걸 "확정"으로 오인할 여지가 있으면 문제다.
5. 이번 변경으로 개수·목록·상태 서술("N개 문서", "남은 항목은 이것뿐", 5. 이번 변경으로 개수·목록·상태 서술("N개 문서", "남은 항목은 이것뿐",

View file

@ -23,7 +23,7 @@
**배경**: 2026-08-09 열두 번째 세션에 스파이크를 만들기 시작한 이래 **배경**: 2026-08-09 열두 번째 세션에 스파이크를 만들기 시작한 이래
**처음으로 `luau`/`luau-analyze` 바이너리가 사용 가능해져 실제로 돌려본 **처음으로 `luau`/`luau-analyze` 바이너리가 사용 가능해져 실제로 돌려본
결과**. 그동안 CLAUDE.md가 "M0 착수 전 남은 유일한 게이트"로 꼽아온 항목. 결과**. 그동안 `.claude/todos.md`가 "M0 착수 전 남은 유일한 게이트"로 꼽아온 항목.
사용자 요청: "지금 상황에서 문제가 생겨 프로젝트의 구조 변경이 생기면 큰 사용자 요청: "지금 상황에서 문제가 생겨 프로젝트의 구조 변경이 생기면 큰
작업인데, 이것이 더 큰 스파이크로 번지기 전에 미리 확인하고싶습니다." 작업인데, 이것이 더 큰 스파이크로 번지기 전에 미리 확인하고싶습니다."

View file

@ -299,7 +299,7 @@ CollectionService 태그 등)를 흉내낼 필요가 없고, quad-base 자체
**백로그**: 나중에 범용 렌더 결과 디버깅 도구로 키우고 싶어지면(정적 **백로그**: 나중에 범용 렌더 결과 디버깅 도구로 키우고 싶어지면(정적
스냅샷을 넘어 Tween mock 같은 동적 동작까지 포함) 그때 스코프를 넓히는 스냅샷을 넘어 Tween mock 같은 동적 동작까지 포함) 그때 스코프를 넓히는
걸로 — 지금은 quad-base 테스트 전용 최소 mock까지만(`CLAUDE.md` 백로그 걸로 — 지금은 quad-base 테스트 전용 최소 mock까지만(`.claude/todos.md` 백로그
참고). 참고).
## Store/State/Source 온톨로지 — 확정됨 (요약) ## Store/State/Source 온톨로지 — 확정됨 (요약)

View file

@ -141,6 +141,6 @@ Store/Dispatch 어디에도 안 걸림, 엔진 지식이 전혀 필요 없음. `
**형제 백로그 항목들(`quad-mock`/`quad-debug`/문서 사이트/`Operator`)과 **형제 백로그 항목들(`quad-mock`/`quad-debug`/문서 사이트/`Operator`)과
동급, 맨 뒤 — "quad 개발 상당 부분 끝난 뒤"로 사용자가 못박은 후순위** 동급, 맨 뒤 — "quad 개발 상당 부분 끝난 뒤"로 사용자가 못박은 후순위**
(`CLAUDE.md` "지금 할 일" 4번). 이 문서가 `base/`로 승격된 건 설계가 다 (`.claude/todos.md` 4번). 이 문서가 `base/`로 승격된 건 설계가 다
확정됐다는 뜻이지, 구현 착수 순서가 앞당겨졌다는 뜻이 아님 — `Operator`처럼 확정됐다는 뜻이지, 구현 착수 순서가 앞당겨졌다는 뜻이 아님 — `Operator`처럼
순수 슈가라 없어도 quad 기능상 완전함(`pcall`을 직접 쓰면 되므로). 순수 슈가라 없어도 quad 기능상 완전함(`pcall`을 직접 쓰면 되므로).

View file

@ -538,7 +538,7 @@ flatten된 값은 해시 파트(프로퍼티 키)로 존재하게 되고, Store
이 위험을 원천 회피함(검증 불필요, 애초에 구멍을 안 만드므로). 이 위험을 원천 회피함(검증 불필요, 애초에 구멍을 안 만드므로).
**여전히 M0에서 검증해야 하는 건 다른 케이스**: `props.Modifier`/ **여전히 M0에서 검증해야 하는 건 다른 케이스**: `props.Modifier`/
`props.Ref`를 caller가 안 넘겨 생기는 리터럴 `nil`-hole(`{nil, ref, `props.Ref`를 caller가 안 넘겨 생기는 리터럴 `nil`-hole(`{nil, ref,
child}`, 위 "지금 할 일" 우선순위1 항목)은 caller가 직접 쓰는 raw child}`, `research/pre-implementation-audit.md` 1-5)은 caller가 직접 쓰는 raw
Lua 리터럴이라 프레임워크가 `None`으로 대신 못 채워줌 — 이번 REPL Lua 리터럴이라 프레임워크가 `None`으로 대신 못 채워줌 — 이번 REPL
실측으로 그 케이스의 실제 위험도가 이전 서술("뒤 항목까지 무시될 수 실측으로 그 케이스의 실제 위험도가 이전 서술("뒤 항목까지 무시될 수
있음", 국소적 피해로 서술돼 있었음)보다 훨씬 큼이 드러남: 구멍이 하나만 있음", 국소적 피해로 서술돼 있었음)보다 훨씬 큼이 드러남: 구멍이 하나만

114
.claude/conventions.md Normal file
View file

@ -0,0 +1,114 @@
# 관례와 작업 방식
이 파일은 루트 `CLAUDE.md``@import` 하므로 **매 세션 컨텍스트에 로드된다**
— 늘리기 전에 "정말 매 세션 필요한가"를 따질 것. 세션마다 필요하진 않은
상세는 `.claude/` 아래 개별 문서로.
<!-- 참고: CLAUDE.md 계열 파일의 블록 HTML 주석은 컨텍스트 주입 전에 제거된다.
즉 여기 주석으로 쓴 것은 에이전트가 못 본다 — 지시는 반드시 본문에 쓸 것. -->
## 언어/모델 관례 (기존 메모, 유지)
사용자는 한국 유저임을 유의해. 사용자가 보게 될 것은 한국어로 띄워주는 게
좋아. 너가 보는 것들(코드 주석 등)은 원하는 언어여도 되는 것(가장 성능이 좋을
영어를 쓰든 그래도 됨, 예를 들어 이 문서도 영어여도 무방하지만 지금은
한국어로 유지). 사용자가 검토해야 하는 것(plan, backlog 등)은 한국어로 써.
코드 안 주석은 공식성을 유지하기 위해 굳이 한국어일 필요 없음, 영어 가능.
또 토큰 맥싱에 유의해 — 아주 작은 테스크라 충분히 작은 모델이 쓸 수 있으면
haiku, 일반 작업은 sonnet. 특히 소스코드를 많이 읽어야 하는 리서치는 메인
컨텍스트를 부패시키니 Agent로 위임할 것(아래 "작업 방식" 참고).
## 작업 방식
- **소스코드를 많이 읽어야 하는 리서치는 Agent(Explore)로 위임** — 메인
컨텍스트 보호. 이미 완료된 v1/rbvm/tbox/Fusion/Vide 리서치 결과는
`.claude/base/`에 정리되어 있으니 중복 조사하지 말고 먼저 그걸 볼 것.
- **병렬화 가능한 작업은 Agent 여러 개를 한 메시지에 동시 호출.** 서로 독립적인
파일/주제를 다루는 리서치나 구현 조사, 또는 서로 다른 문서 파일을 고치는
문서 정리 작업이 여기 해당(단, 같은 파일을 동시에 고치는 에이전트를 병렬로
띄우지 말 것 — 충돌함).
- **크리티컬한 설계 결정은 구현으로 밀어붙이지 말고 plan을 research/에 남긴 채
연기.** 사용자는 Lua/Roblox 엔진을 깊이 아는 사람 — 근거와 선택지를 문서에
정리해두면 사용자가 깨어있을 때 훑어보고 답해줄 것. `.claude/question.md`
반드시 반영.
- **작업이 끝나면(또는 방향이 바뀌면) 항상 자기 문서화** — 완료된 걸 다시
조사하게 되는 재작업을 막기 위함. `.claude/base/`로 승격, `.claude/qa-request/`
이동, 또는 문서 자체를 갱신. code-docker/webmanager의 `.claude/` 관리 방식이
좋은 예시(`.claude/initreq/code-docker/webmanager/.claude/README.md` 참고).
- **⭐ 중대 변경 핸드오버 체크리스트 — 확정된 결정을 뒤집거나 문서를
쪼갤 때 반드시 이 순서를 밟을 것.** 2026-08-13 일곱/여덟 번째 세션에
6+6라운드 수동 감사로 55건을 찾았는데, **거의 전부가 "변경한 세션이
그 자리에서 안 한 일"이 나중에 stale로 쌓인 것**이었음. 감사로 뒤늦게
줍지 말고 바꾸는 그 순간에 닫을 것:
1. **`python3 .claude/tools/doc-check.py`를 돌릴 것**(아래 항목 참고) —
ERROR 0을 유지한 채로 커밋. 이게 규율 대부분을 기계가 대신함.
2. **바꾼 주장을 부정당하는 *본문 문장*을 grep으로 전수 찾을 것**(또는
`.claude/agents/quad-doc-auditor.md`로 위임 — 아래 항목 참고, 신선한
맥락에서 도는 서브에이전트가 이 항목을 실제로 더 잘 잡아왔음).
헤더에 정정 배너만 달고 본문 bullet을 안 고치는 게 가장 잦은 실패 —
실제로 `CLAUDE.md`가 "스파이크를 아직 안 돌려봄"이라고 서술한 채
한 라운드를 통과했음. **배너를 달았으면 그 배너가 부정하는 문장을
같은 커밋에서 고쳤는지 확인.**
3. **뒤집힌 원문은 `archive/`로 옮기고 포인터만 남길 것** — 본문에
"히스토리로만 보존"이라며 두면 구현자가 앞에서부터 읽다가 그
"확정"을 그대로 믿음(`slot-plan.md`에서 실제로 발생).
4. **개수·목록·상태는 소스를 하나만 둘 것.** "20개 중 19개", "4개
문서", "남은 건 X뿐" 류는 두 곳 이상에 적는 순간 반드시 갈라짐 —
한 곳(예: `luau-test/STATUS.md`, 폴더 구조)을 소스로 하고 나머지는
가리키기만.
5. **시한부 주장엔 날짜를 붙일 것.** "아직 안 돌려봄"이 아니라
"[2026-08-09 기준] 아직 안 돌려봄" — 날짜가 있으면 다음 세션이
의심할 수 있지만, 없으면 영원히 현재형으로 읽힘.
6. **인덱스 레이어 3개를 같이 갱신**: `.claude/README.md`(색인),
`question.md`(사용자가 답할 것만), 루트 `ROADMAP.md`/`HUMAN_TODO.md`.
- **기계 점검 — `python3 .claude/tools/doc-check.py`.** 깨진 파일/절
참조, README 색인 누락, 날짜 없는 시한부 주장, 미반영 배너를 한 번에
훑음. **커밋 전에 돌리는 게 기본** — 수동 감사에서 나온 발견의 대부분이
이걸로 잡히는 종류였고, 실제로 여덟 번째 세션에 문서를 쪼개면서 잘못
옮긴 참조를 이 스크립트가 잡아냈음. ERROR는 고치고, WARN은 판단이
필요한 것(절 제목을 의역해 인용한 관례 등)이라 늘 0일 필요는 없음.
- **[2026-08-16 도입] `.claude/agents/quad-doc-auditor.md` 서브에이전트 —
위 체크리스트 2~4번(본문 문장 grep, archive 이전, 개수/목록 단일화)을
신선한 맥락에서 대신 수행.** 읽기 전용, 발견만 리포트(직접 수정 안 함).
**`.claude/base`/`.claude/research`/이 문서/`.claude/todos.md` 등 라이브
문서에 중대한 변경이 있은 뒤, 특히 커밋 전에 돌리는 게 기본** — 지난
세션들에서 "변경한 세션 자신의
self-audit은 자기가 뭘 안 건드렸는지 몰라서 놓친다"는 패턴이 반복
확인됐고(세션 히스토리 7·8·10·11차 등), 이걸 매번 즉흥적으로 프롬프트
짜는 대신 고정 정의로 옮긴 것. **아주 큰 변경**(설계 반전 규모)엔 이걸로
대체하지 말고 `/code-review`(diff 기반)와 사용자의 직접 diff 검토를
병행할 것 — 이 서브에이전트는 diff가 아니라 코퍼스 전체의 정합성만 봄.
- **[2026-08-16 도입] "핸드오버 준비하고 커밋해" 류 요청엔 되묻지 말고
`.claude/workflows/quad-handover-audit.js`(Workflow 이름
`quad-handover-audit`)를 먼저 돌릴 것.** 단일 `quad-doc-auditor` 패스는
비결정적이라 매번 다 잡는다는 보장이 없다는 게 사용자 지적 — 이 워크플로는
라운드마다 `quad-doc-auditor`를 병렬로 3회 돌리고 새 발견을 파일별로
즉시 반영한 뒤, **새 발견이 없는 라운드가 연속 2번 나올 때까지**(최대
6라운드) 반복해 수렴시킨다. 사용자가 정확성을 시간보다 우선한다고
명시했으므로 기본 워크플로 크기 가이드라인(15 에이전트 이하)을 의도적으로
넘을 수 있음 — 이 워크플로 자체가 그 예외 대상. Workflow는 백그라운드로
돌고 완료 시 알림이 오므로, 호출 직후 대화를 막지 말고 진행 상황만
알린 뒤 알림을 기다릴 것. 알림이 오면 `python3 .claude/tools/doc-check.py`
ERROR 0을 최종 확인한 뒤 평소 커밋 절차(git status/diff 검토, 메시지 작성)로
넘어갈 것 — **실제 `git commit`은 이 워크플로 안이 아니라 항상 메인 세션이
직접 함**(커밋 전 diff 재검토는 대화형 맥락이 필요해서 워크플로에 위임 안 함).
- **문서가 쌓이면서 모순/중복/stale 마커가 생기기 쉬움 — 주기적으로 감사할
것.** 다만 위 체크리스트+`doc-check.py`+`quad-doc-auditor`가 자리잡으면
이 감사는 "기계도 서브에이전트도 못 보는 것"(설계 자체의 자기모순, 의사코드
손 트레이싱)에만 집중하면 됨. 2026-08-04 세션에 실제로 전체 `.claude/`
코퍼스에서 이런 문제가 다수 발견되어 정리함(`.claude/session-summary.md` 참고) —
여러 라운드에 걸쳐 같은 문서를 계속 고치다 보면 "정정됨" 표시가 원래
문장에 안 반영되고 방치되는 패턴이 반복되니, 큰 방향 전환이 있을 때마다
관련 문서 전체를 훑어 확인할 것.
- **Roblox Studio MCP 연결 시 주의**: Studio는 잘 죽는 편 — 죽었을 때 살리려고
위험한 명령을 반복 시도하지 말 것. 그런 상황이면 MCP 없이 할 수 있는 작업만
하거나 대기. 연결 방법은 `HUMAN_TODO.md` 1번 항목 참고(사용자가 Studio에서
베타 기능을 켜줘야 함).
- **`SAFETY.md` 반드시 지킬 것** — (1) GitHub 등 외부 git 호스팅에 이 레포를
push하지 말 것, 모델의 git 작업 공간은 사용자가 별도로 마련해줄 제한 계정
(예: git.qwreey.moe) 전용으로 국한됨 — 그 계정/원격이 설정되기 전까지는
**로컬 git 커밋까지만** 하고 원격 추가/푸시는 하지 말 것. (2) Roblox Studio는
메인 계정이 아닌 별도 계정으로만 사용 — 로그인 계정 전환을 사용자가 안 해줬다면
Studio 관련 작업(MCP 연결 등)을 진행하지 말고 대기.

View file

@ -76,7 +76,7 @@ ROADMAP 항목 근거인지, 어떻게 실행하는지, 실행 후 뭘 확인해
| `07-relate-weak-table-gc.luau` | `Relate`의 lazy 서브테이블 생성 + weak-key GC가 실제로 동작하는지 | `relate-plan.md` "M2 착수 시 실측 확인" **[2026-08-13 보강]** 4번 섹션 신설 — `_countEntries()`(테스트 전용) + weak-value canary로 **"inst가 죽으면 중첩 StrongMap 안의 payload까지 연쇄 GC되는가"를 직접 검증**(원래는 sanity check만 하고 헤더의 핵심 주장은 미검증이었음). 파일이 스스로 적어둔 "weak table 엔트리를 셀 표준 API가 없다"는 전제도 틀렸음 — outer가 `__mode="k"`라 GC 후 `pairs`에서 사라짐 | | `07-relate-weak-table-gc.luau` | `Relate`의 lazy 서브테이블 생성 + weak-key GC가 실제로 동작하는지 | `relate-plan.md` "M2 착수 시 실측 확인" **[2026-08-13 보강]** 4번 섹션 신설 — `_countEntries()`(테스트 전용) + weak-value canary로 **"inst가 죽으면 중첩 StrongMap 안의 payload까지 연쇄 GC되는가"를 직접 검증**(원래는 sanity check만 하고 헤더의 핵심 주장은 미검증이었음). 파일이 스스로 적어둔 "weak table 엔트리를 셀 표준 API가 없다"는 전제도 틀렸음 — outer가 `__mode="k"`라 GC 후 `pairs`에서 사라짐 |
| `08-type-source-satisfies-state.luau` (타입체크 전용) | `Source<T>``State<T>`를 구조적으로 만족하는 제네릭 타입이 솔버에서 안전한지 | `base/source-state-plan.md` "Source가 State를 만족함", ROADMAP M0-2 | | `08-type-source-satisfies-state.luau` (타입체크 전용) | `Source<T>``State<T>`를 구조적으로 만족하는 제네릭 타입이 솔버에서 안전한지 | `base/source-state-plan.md` "Source가 State를 만족함", ROADMAP M0-2 |
| `09-type-modifier-overridden-subtype.luau` (타입체크 전용) | `FrameModifier <: GuiObjectModifier`처럼 서브타입 관계인 Modifier를 `Overridden`으로 섞을 때 타입이 통과하는지 | `modifier-plan.md` 9-2번, ROADMAP M7 | | `09-type-modifier-overridden-subtype.luau` (타입체크 전용) | `FrameModifier <: GuiObjectModifier`처럼 서브타입 관계인 Modifier를 `Overridden`으로 섞을 때 타입이 통과하는지 | `modifier-plan.md` 9-2번, ROADMAP M7 |
| `10-roblox-studio-checks.server.luau` (Studio 전용) | **[⚠️ 2026-08-14 다섯 번째 세션: A 섹션이 폐기된 모델을 검증 중 → `rewrite-required/`, 열한 번째 세션에 `canBound` 재도입으로 재작성 사유 하나 더 추가]** (A) `bindLifetime`/`unbindLifetime`/`canBound`/`canExecute`의 gcconn 트릭 + 이중 바인딩 게이트(Destroy 시 Connected 전환 포함), (B) Attribute의 Instance 참조 타입 지원, (C) CollectionService 태그/GetTagged 왕복. **A는 재작성 대상** — 파일 속 옛 `canBound`(9차 세션 정의)와 `bindLifetime``value.Subscribed = true` 세팅, 2-인자 `canExecute(inst, value)`는 전부 낡음(현재 게이트는 이중 바인딩 확인은 `canBound(v)`, emit 게이팅은 `canExecute(v)` — 둘 다 `value` 단독 1-인자로 비공개 헬퍼를 공유, gcconn/gchold는 **Instance 생성 시점**에 생성). **[2026-08-13]** A 섹션 앞부분(ClassName 신호 미발화, Destroy 시 Connected 즉시 전환)은 사용자 자작 스크립트로 부분 확인됐고 **새 모델에서도 그대로 유효**(오히려 더 중요 — `canBound`/`canExecute`가 `.Connected`를 직접 읽는 게 leaf 경로 판정의 전부), `audit/gcconn-trick-verification.md` 참고. 이중 바인딩 게이트/재바인딩 허용/B/C는 이 공식 파일로 아직 확인 안 됨 | `lifecycle-pattern.md` "`bindLifetime`/`canBound`/`canExecute`/`unbindLifetime` — 확정", `archive/canexecute-inst-arg-reversed.md`, `source-state-plan.md` "이중 바인딩 금지", CLAUDE.md 2026-08-06 세션, `debug-tooling-plan.md` | | `10-roblox-studio-checks.server.luau` (Studio 전용) | **[⚠️ 2026-08-14 다섯 번째 세션: A 섹션이 폐기된 모델을 검증 중 → `rewrite-required/`, 열한 번째 세션에 `canBound` 재도입으로 재작성 사유 하나 더 추가]** (A) `bindLifetime`/`unbindLifetime`/`canBound`/`canExecute`의 gcconn 트릭 + 이중 바인딩 게이트(Destroy 시 Connected 전환 포함), (B) Attribute의 Instance 참조 타입 지원, (C) CollectionService 태그/GetTagged 왕복. **A는 재작성 대상** — 파일 속 옛 `canBound`(9차 세션 정의)와 `bindLifetime``value.Subscribed = true` 세팅, 2-인자 `canExecute(inst, value)`는 전부 낡음(현재 게이트는 이중 바인딩 확인은 `canBound(v)`, emit 게이팅은 `canExecute(v)` — 둘 다 `value` 단독 1-인자로 비공개 헬퍼를 공유, gcconn/gchold는 **Instance 생성 시점**에 생성). **[2026-08-13]** A 섹션 앞부분(ClassName 신호 미발화, Destroy 시 Connected 즉시 전환)은 사용자 자작 스크립트로 부분 확인됐고 **새 모델에서도 그대로 유효**(오히려 더 중요 — `canBound`/`canExecute`가 `.Connected`를 직접 읽는 게 leaf 경로 판정의 전부), `audit/gcconn-trick-verification.md` 참고. 이중 바인딩 게이트/재바인딩 허용/B/C는 이 공식 파일로 아직 확인 안 됨 | `lifecycle-pattern.md` "`bindLifetime`/`canBound`/`canExecute`/`unbindLifetime` — 확정", `archive/canexecute-inst-arg-reversed.md`, `source-state-plan.md` "이중 바인딩 금지", `.claude/session-summary.md` 2026-08-06 세션, `debug-tooling-plan.md` |
| `11-modifier-illegal-value-error.luau` | Modifier 필드에 Ref/PreRef/Observer/Effect/Slot/Modifier가 들어오면 즉시 error, State/Source가 확정하는 값이 Modifier면 즉시 error(2026-08-09 세션에 "UB"에서 전환된 규칙) | `modifier-plan.md` "핸들러 계층 값 즉시 error" 절 + 7번 절 | | `11-modifier-illegal-value-error.luau` | Modifier 필드에 Ref/PreRef/Observer/Effect/Slot/Modifier가 들어오면 즉시 error, State/Source가 확정하는 값이 Modifier면 즉시 error(2026-08-09 세션에 "UB"에서 전환된 규칙) | `modifier-plan.md` "핸들러 계층 값 즉시 error" 절 + 7번 절 |
| `12-type-attribute-generic-key-narrowing.luau` (타입체크 전용) | `[AttributeKey<<T>> "name"] = value`(구 `Attribute<<T>>`)처럼 제네릭 DI 키를 쓸 때 `value`의 타입이 실제로 `T`로 좁혀지는지 — base 문서 자신이 "미검증"이라 명시한 항목 | `attribute-plan.md` "[실측 필요, M0/M10]" (2026-08-09 열한 번째 세션 신설) | | `12-type-attribute-generic-key-narrowing.luau` (타입체크 전용) | `[AttributeKey<<T>> "name"] = value`(구 `Attribute<<T>>`)처럼 제네릭 DI 키를 쓸 때 `value`의 타입이 실제로 `T`로 좁혀지는지 — base 문서 자신이 "미검증"이라 명시한 항목 | `attribute-plan.md` "[실측 필요, M0/M10]" (2026-08-09 열한 번째 세션 신설) |
| `13-type-ref-preref-subtype.luau` | (A, 타입) `PreRef<T>``Ref<T>`를 구조적으로 만족하는지, (B, 런타임) `isRef`/`isPreRef` 합성이 재정정대로 동작하는지(`isRef(preRefInstance)`가 이제 `true`) + Leaf 핸들러가 `isRef(v) and not isPreRef(v)`로 명시적으로 좁혀야 하는 이유. **[2026-08-14 아홉 번째 세션] 재작성 시 `PostRef`도 같이 커버할 것** — 같은 `Ref` 런타임 재사용 + 브랜드 태그만 다른 형제라 A/B 둘 다 그대로 확장되고, Leaf predicate도 `isRef(v) and not isPreRef(v) and not isPostRef(v)`로 늘어남 | `brand-plan.md``Brand` 절(2026-08-09 열한 번째 세션 재정정) | | `13-type-ref-preref-subtype.luau` | (A, 타입) `PreRef<T>``Ref<T>`를 구조적으로 만족하는지, (B, 런타임) `isRef`/`isPreRef` 합성이 재정정대로 동작하는지(`isRef(preRefInstance)`가 이제 `true`) + Leaf 핸들러가 `isRef(v) and not isPreRef(v)`로 명시적으로 좁혀야 하는 이유. **[2026-08-14 아홉 번째 세션] 재작성 시 `PostRef`도 같이 커버할 것** — 같은 `Ref` 런타임 재사용 + 브랜드 태그만 다른 형제라 A/B 둘 다 그대로 확장되고, Leaf predicate도 `isRef(v) and not isPreRef(v) and not isPostRef(v)`로 늘어남 | `brand-plan.md``Brand` 절(2026-08-09 열한 번째 세션 재정정) |
@ -162,7 +162,7 @@ GC가 안 풂" 주장이 지금까지 공식 문서 인용으로만 뒷받침돼
Luau로 재현해본 적이 없었던 갭 — `Slot``kSlotMap`/`slotOwner` GC 수정 Luau로 재현해본 적이 없었던 갭 — `Slot``kSlotMap`/`slotOwner` GC 수정
사례(session 13/14) 전체가 이 사례 하나에 기대고 있어 우선순위 있게 추가. 사례(session 13/14) 전체가 이 사례 하나에 기대고 있어 우선순위 있게 추가.
**9차 (2026-08-13)**: CLAUDE.md 세션 8~21(대부분 2026-08-12) 전체를 대상으로 **9차 (2026-08-13)**: `.claude/session-summary.md` 세션 8~21(대부분 2026-08-12) 전체를 대상으로
"새 메커니즘 중 실 Luau로 안 부딪혀본 게 더 있는가"를 재점검 — Ref/Slot의 "새 메커니즘 중 실 Luau로 안 부딪혀본 게 더 있는가"를 재점검 — Ref/Slot의
`Relate` diff 기반 retract(세션 8/9)는 03/04번이 이미 검증한 것과 같은 `Relate` diff 기반 retract(세션 8/9)는 03/04번이 이미 검증한 것과 같은
클래스의 재귀 dispatch/체인 로직이라 스킵. 반면 Tag 참조 카운트(세션 11/16) 클래스의 재귀 dispatch/체인 로직이라 스킵. 반면 Tag 참조 카운트(세션 11/16)

View file

@ -23,14 +23,14 @@
gchold를 배열이 아니라 value를 키로 쓰는 테이블로 바꾼 것까지 반영 gchold를 배열이 아니라 value를 키로 쓰는 테이블로 바꾼 것까지 반영
— 이전 버전의 이 스크립트는 array 기반 gchold였음, 이번에 정정). — 이전 버전의 이 스크립트는 array 기반 gchold였음, 이번에 정정).
B) Attribute가 Instance 참조 타입을 실제로 지원하는가(ObjectValue B) Attribute가 Instance 참조 타입을 실제로 지원하는가(ObjectValue
없이 Ref 용도로 쓸 수 있다는 CLAUDE.md 서술의 실측). 없이 Ref 용도로 쓸 수 있다는 .claude/session-summary.md 서술의 실측).
C) CollectionService 태그 + GetTagged 왕복이 quad-debug가 기대하는 C) CollectionService 태그 + GetTagged 왕복이 quad-debug가 기대하는
대로 동작하는가(태그 추가/제거, GetTagged로 조회). 대로 동작하는가(태그 추가/제거, GetTagged로 조회).
배경: .claude/base/lifecycle-pattern.md "bindLifetime/canExecute/ 배경: .claude/base/lifecycle-pattern.md "bindLifetime/canExecute/
unbindLifetime — 확정" 절 + "실측 필요(M0/M2)" 캐비엇, unbindLifetime — 확정" 절 + "실측 필요(M0/M2)" 캐비엇,
.claude/base/bind-system-plan.md "이중 바인딩 금지" 절(canBound), .claude/base/bind-system-plan.md "이중 바인딩 금지" 절(canBound),
CLAUDE.md 2026-08-06 세션의 Attribute Instance 참조 지원 언급, .claude/session-summary.md 2026-08-06 세션의 Attribute Instance 참조 지원 언급,
.claude/research/debug-tooling-plan.md의 CollectionService 노출 방식. .claude/research/debug-tooling-plan.md의 CollectionService 노출 방식.
실행 방법: 실행 방법:

View file

@ -0,0 +1,93 @@
# 프로젝트 컨텍스트
루트 `CLAUDE.md``@import` 하는 파일. 폴더별 **상세** 색인은
`.claude/README.md`가 소스 — 여기선 중복 서술하지 말고 가리키기만 할 것.
## 이 프로젝트가 뭔지
Roblox 엔진에서 동작하는 DOMless UI 렌더러 **quad**를 처음부터 다시 짜는
프로젝트. 목표는 개별 프로덕트가 아니라 **라이브러리**로서의 코드 퀄리티와
지속 가능성 — 빠른 이터레이션보다 정확성/설계 정합성이 우선. 작업 기간은
길게 잡음.
**지금은 설계/계획 단계이고 구현은 아직 시작 전** — 저장소 루트에 실제 소스
코드(`src/` 등)가 없음. 핵심 아키텍처(Store 책임 분리, `process`/`retract`
디스패치 모델, Store/State/Source 온톨로지, 소스 트리 구조, Modifier 메커니즘,
컴포넌트=플레인 함수, 컴포넌트 경계 modifier/Ref 전달)는 전부 `.claude/base/`
문서로 확정돼 있음 — 먼저 `.claude/base/architecture.md`를 읽을 것. 사용자가
직접 "지금 quad에서 가장 문제되는 부분"으로 지목했던 컴포넌트화(특히
modifier/Ref의 컴포넌트 경계 통과 방식) 논의도 2026-08-04 세션에서 수렴
완료(`base/component-composition-plan.md`) — 남은 핵심 설계 질문은 없고,
용어 정리(진행 중)와 실제 스캐폴딩만 남음, 아래 "지금 할 일" 참고.
이전에 시도했다 폐기한 v2 재작성 시도(`.claude/initreq/quad2-try`)도 리서치
완료 — OOP 상속/커스텀 파서/Slot 스텁/`Pipe` copy-on-write 절충안은 확인된
죽은 접근이라 반복 조사 금지(`base/bind-system-plan.md` "확정된 것" 절 참고).
## 계획 문서 구조
`.claude/README.md`가 색인. 요약:
- **[2026-08-16]** 루트 `CLAUDE.md`는 39줄짜리 진입점일 뿐이고, 실제 내용은
`.claude/conventions.md`(관례·작업 방식) / 이 문서 / `.claude/todos.md`
(지금 할 일)로 쪼개져 `@import`로 다시 합쳐짐. `.claude/session-summary.md`
(세션 요약 색인)만 **import 안 됨** — 필요할 때 직접 열 것.
- `.claude/base/` — 확정된 아키텍처/컨텍스트, plan/done 개념 없음. 먼저
`.claude/base/architecture.md`를 읽을 것.
- `.claude/reference/`**[2026-08-07 신설]** base처럼 확정된 건 아니지만
base 문서가 근거로 인용하는 온디맨드 참고 자료(v1 내부 동작 스냅샷,
Fusion/Vide 비교 리서치) — 항상 읽을 필요는 없고 인용될 때만 열어볼 것.
- `.claude/research/` — 아직 착수 전, 사용자와 상의 필요한 설계 논의.
`debug-tooling-plan.md`/
`documentation-plan.md`/`documentation-content-map.md`/
`framework-comparison-findings.md`/`additional-primitives-plan.md`(2026-08-09
세 번째 세션에 마지막 열린 항목까지 전부 해소, 이제 배경 자료용)/
`pre-implementation-audit.md`/`v1-compat-plan.md`
— 전부 후순위(`tween-plan.md`는 2026-08-12 세션에 마지막 열린 항목까지
전부 해소돼 `base/`로 승격, 이미 생성된 인스턴스 재바인드는
2026-08-14 세션에 기각돼 `archive/existing-instance-bind-rejected.md`로 이전, 더 이상 여기 없음). 최신 목록·우선순위는
`.claude/README.md`가 소스, 여기서 개수 반복 안 함(과거에 "두 개뿐"이라
적어놨다가 새 문서 추가될 때마다 안 갱신되는 패턴이 반복돼서 아예 안
세기로 함).
- `.claude/luau-test/`**[2026-08-09 신설]** "추론만으로 확정하고 실제
Luau로 부딪혀본 적 없는 것"을 미리 검증하는 독립 실행 스파이크 20개
(`luau <파일>` / `luau-analyze <파일>`). **상태의 소스는 항상 `STATUS.md`**
(pass / 사람 결정 필요 / 스파이크 깨짐 / 미실행, 폴더 구조 자체가 상태),
각 파일이 뭘 왜 검증하는지는 `README.md`. 2026-08-13에 첫 실측 완료(당시
런타임 12개 전원 통과) — 이후 여러 세션에 걸쳐 재설계로 몇 건이 추가로
`rewrite-required/`에 합류했으니 **지금 몇 개가 어디 있는지는 여기서
나열 안 하고 `STATUS.md`로 미룸**(나열하다 stale해지는 패턴이 실제로
반복됐음, 아래 "지금 할 일" 0번 참고).
- `.claude/audit/`**[2026-08-13 신설]** 스파이크를 실제로 돌린 **실측
결과** 기록(계획 아님). 부분 확인도 있는 그대로 남김 — **지금 몇 개가
있는지·각각 뭘 확인했는지는 여기서 나열 안 하고 `.claude/README.md`
`audit/` 행으로 미룸**(luau-test와 같은 이유 — 나열하다 새 폴더가
추가될 때마다 stale해지는 패턴이 실제로 반복됐음, 가장 최근엔
2026-08-15에 이 목록이 3개에서 멈춰 있는 걸 `/code-review`가 발견).
`type-recursion-issue/`만 참고로 짚으면: **[13차 세션]** 0-Y 재실측
전체 — `REPORT.md` + `spikes/` 44개, **스크립트를 같이 두는** 예외적
구성(판정이 "여러 formulation 대조"라 개별 파일을 직접 돌려야 재현됨),
결론은 `base/typing-limits.md`로 승격 — 이후 신설된 폴더들도 같은
구성 관례를 따름(`type-recursive-issue-with-typeof/`,
`type-recursive-issue-try-callback/` 등).
- `.claude/qa-request/`, `.claude/feedback/` — 구현 시작되면 쓰기 시작함,
지금은 비어있음. `.claude/archive/`는 원래 같은 취급이었으나
2026-08-06 세 번째 세션부터 **완전히 뒤집힌 설계 결정을 원문+역전
이유+diff와 함께 보존하는 용도로도 사용 시작**(구현 완료 대상만이
아님) — `archive/store-source-proxy-reversed.md`가 첫 사례, 나중
`quadnomicon` 콘텐츠 소재로 재사용 예정.
- `.claude/session/`**[2026-08-11 신설]** 세션별 상세 로그 원문(시행착오·
정정 전 서술 포함, `quadnomicon` 개발로그 소재용) — 루트 `CLAUDE.md`가 3000줄
넘게 불어나 성능 저하를 유발해서 분리함. `.claude/session-summary.md`의 각
항목이 여기로 링크. **항상 읽을 필요 없음** — 특정 결정의 논의 과정/시행착오가
궁금할 때만 열어볼 것, 지금 유효한 설계는 항상 `base/`가 소스.
- `.claude/initreq/` — 클론해둔 참고 레포(quad v1, Fusion, Vide, rbvm, tbox,
code-docker) + PA님 실 코드(`artworks/`) + 원본 요청. **읽기 전용,
`.gitignore`로 커밋 제외됨** — 내용을 다른 곳으로 옮기지 말고 항상 원본
그대로 둘 것. 리서치가 더 필요하면 이 폴더를 다시 파고들 것.
- `.claude/question.md` — 사용자가 답해야 할 질문 전체 취합(우선순위순).
- 루트 `ROADMAP.md` — 설계 단계 종료 후 실제 구현 순서(M0, M1, ... 마일스톤 +
todo 체크박스). "무엇을 확정했는가"는 `.claude/base/`가 소스, "어떤 순서로
만드는가"는 이 문서가 소스 — 헷갈리지 말 것.
- 루트 `HUMAN_TODO.md` — 사람만 할 수 있는 일(로컬 GUI 조작, 스케줄/루프
설정 등).

View file

@ -205,6 +205,6 @@
> `research/pre-implementation-audit.md`가 원본이자 최신. > `research/pre-implementation-audit.md`가 원본이자 최신.
--- ---
전체 순서/우선순위는 루트 `CLAUDE.md`가 최종 소스. 확정된 것들의 문서 전체 순서/우선순위는 `.claude/todos.md`가 최종 소스. 확정된 것들의 문서
색인은 `.claude/README.md``base/` 표(예전에 이 문서 맨 아래에 있던 색인은 `.claude/README.md``base/` 표(예전에 이 문서 맨 아래에 있던
요약표는 그것과 중복이라 archive로 옮김). 요약표는 그것과 중복이라 archive로 옮김).

View file

@ -1,7 +1,15 @@
# 문서 stale 감소용 include 도구 — `doc-include.py` (가칭) # 문서 stale 감소용 include 도구 — `doc-include.py` (가칭)
**상태**: research — 2026-08-14 세션에 아이디어 확정, **플랜만 초안, 사용자가 **상태**: research — 2026-08-14 세션에 아이디어 확정, **[2026-08-16 갱신]**
다듬을 예정(내일 처리)**. 구현 착수 전. CLAUDE.md 분할로 파일럿 설계가 **단방향 생성으로 단순화**됨(아래 "파일럿
범위" 참고). 구현 착수 전.
> **[2026-08-16] 이 문서의 원 설계에서 절반이 불필요해졌음.** 원래는 원본에
> `<!--#summary-->`, 인용처에 `<!--#include-->` 두 종류 마커를 두는 **양방향**
> 설계였는데, 같은 날 CLAUDE.md를 분할하면서 세션 히스토리가 독립 파일
> (`.claude/session-summary.md`)이 됐음 — 목적지가 "손으로 쓴 파일 속 구간"이
> 아니라 **통째로 생성되는 파일**이 되면서 목적지 마커가 필요 없어졌다. 남는
> 건 원본 쪽 마커 + 단방향 생성기뿐. 아래 본문은 이 정정을 반영해 갱신했다.
## 배경 ## 배경
@ -45,38 +53,46 @@
Python 표준 라이브러리만으로 신설. `--write`(갱신 삽입)/`--check`(어긋나면 Python 표준 라이브러리만으로 신설. `--write`(갱신 삽입)/`--check`(어긋나면
ERROR, `doc-check.py` 파이프라인에 편입) 두 모드. ERROR, `doc-check.py` 파이프라인에 편입) 두 모드.
## 파일럿 범위 — CLAUDE.md 세션 히스토리부터 ## 파일럿 범위 — `.claude/session-summary.md` 전체 생성
사용자가 지목한 이유: 세션 히스토리 항목은 서로 독립적(과거 기록이라 사용자가 지목한 이유: 세션 히스토리 항목은 서로 독립적(과거 기록이라
다른 문서가 그 문장을 인용하는 경우가 거의 없음)이라 이 도구가 버그가 다른 문서가 그 문장을 인용하는 경우가 거의 없음)이라 이 도구가 버그가
있어도 **부작용이 다른 곳으로 안 번짐** — 첫 적용 대상으로 가장 안전. 있어도 **부작용이 다른 곳으로 안 번짐** — 첫 적용 대상으로 가장 안전.
- 지금 구조: `.claude/session/YYYY-MM-DD-NN-slug.md`에 세션 원문 전체가 - **[2026-08-16 기준] 지금 구조**: `.claude/session/YYYY-MM-DD-NN-slug.md`
있고, `CLAUDE.md`의 "세션 히스토리" 절엔 사람이 손으로 압축한 2~4줄 세션 원문 전체가 있고, `.claude/session-summary.md`(CLAUDE.md 분할 전엔
요약 + 링크가 별도로 적혀있음(둘이 물리적으로 분리된 텍스트라 한쪽만 `CLAUDE.md`의 "세션 히스토리" 절이었음)에 사람이 손으로 압축한 2~4줄
갱신되면 어긋날 수 있음 — 실제로 아직 발생한 적은 없지만 구조적으로 요약 + 링크가 **별도 텍스트로** 적혀있음 — 한쪽만 갱신되면 어긋날 수
가능한 상태). 있는 구조.
- 적용 후: 각 세션 파일 안에 "이 요약이 CLAUDE.md에 들어갈 정본"이라고 - **적용 후**: 각 세션 파일 안에 "이게 이 세션의 정본 요약"이라고 표시하는
표시하는 마커 블록을 신설(**새로 요약을 쓸 필요 없음** — 지금 마커 블록을 신설(**새로 요약을 쓸 필요 없음** — 지금 `session-summary.md`
`CLAUDE.md`에 이미 있는 압축 요약을 그대로 그 블록 안으로 옮기면 됨). 이미 있는 압축 요약을 그대로 그 블록 안으로 옮기면 됨). 생성기가 세션
`CLAUDE.md` 쪽엔 대응하는 include 마커만 남기고, `doc-include.py --write` 파일들을 파일명 순으로 훑어 마커 블록을 모아 `session-summary.md`
두 마커 사이 콘텐츠를 세션 파일에서 가져와 채움. **통째로 다시 씀**. 그 시점부터 `session-summary.md`는 직접 편집 금지
대상이 되고(파일 상단에 그렇게 명시), 요약을 고치려면 세션 파일을 고침.
### 왜 목적지 마커가 필요 없어졌나
원안은 목적지(`CLAUDE.md`)가 손으로 관리되는 파일이라, 그 안의 특정
구간만 골라 갱신하려고 `<!--#include-->` 마커가 필요했음. 분할 후엔
목적지가 **파일 통째로 파생 데이터**라 "어디를 갱신할지"를 표시할 이유가
없음 — 파일 전체를 덮어쓰면 됨. 부품이 절반으로 줄고, "목적지 마커가
손실되면?" 같은 실패 모드도 같이 사라짐.
## 마커 문법 (초안 — 사용자가 확정할 것) ## 마커 문법 (초안 — 사용자가 확정할 것)
``` ```
# 세션 파일(.claude/session/2026-08-14-14-....md) 안: # 세션 파일(.claude/session/2026-08-14-14-....md) 안:
<!--#summary:2026-08-14-14--> <!--#summary-->
**2026-08-14 열네 번째 세션 — ...** (`session/2026-08-14-14-....md`) **2026-08-14 열네 번째 세션 — ...** (`session/2026-08-14-14-....md`)
... 2~4줄 압축 요약 ... ... 2~4줄 압축 요약 ...
<!--#/summary--> <!--#/summary-->
# CLAUDE.md "세션 히스토리" 절 안:
<!--#include:2026-08-14-14 from=session/2026-08-14-14-....md-->
(doc-include.py --write가 여기를 원본 summary 블록 내용으로 갱신)
<!--#/include-->
``` ```
`session-summary.md` 쪽엔 마커가 없음 — 생성기가 헤더 + 각 세션 블록을
파일명 순으로 이어 붙여 전체를 씀. id도 불필요해짐(파일명이 곧 순서이자
식별자).
## 열린 질문 (사용자가 다듬을 것) ## 열린 질문 (사용자가 다듬을 것)
1. 마커 문법 자체(위 초안 확정 여부, id 네이밍 규칙 — 날짜+세션번호로 1. 마커 문법 자체(위 초안 확정 여부, id 네이밍 규칙 — 날짜+세션번호로

View file

@ -4,7 +4,7 @@
+ 백엔드별 트랙 분리)이 확정된 뒤, 실제로 각 축에 뭘 채울지 `.claude/base/*.md` + 백엔드별 트랙 분리)이 확정된 뒤, 실제로 각 축에 뭘 채울지 `.claude/base/*.md`
전체 + 관련 `research/*.md`(tween-plan, ui-shorthand-plan)를 2026-08-06 세션에 전체 + 관련 `research/*.md`(tween-plan, ui-shorthand-plan)를 2026-08-06 세션에
6개 에이전트로 병렬 서베이해 분류함. **아직 문서를 쓰라는 뜻 아님** — 착수 6개 에이전트로 병렬 서베이해 분류함. **아직 문서를 쓰라는 뜻 아님** — 착수
시점은 여전히 구현 우선(`CLAUDE.md` "지금 할 일" 1번). 나중에 실제 문서화를 시점은 여전히 구현 우선(`.claude/todos.md` 1번). 나중에 실제 문서화를
시작할 때 이 맵을 목차/우선순위표로 쓰면 됨. 시작할 때 이 맵을 목차/우선순위표로 쓰면 됨.
> **⚠️ [2026-08-14 아홉 번째 세션 감사] 이 서베이는 2026-08-06 시점의 > **⚠️ [2026-08-14 아홉 번째 세션 감사] 이 서베이는 2026-08-06 시점의

View file

@ -51,7 +51,7 @@ Roblox 코드(Frame 만들기 등) 안에서 자연스럽게 등장시키고,
하나)으로 구조화하고, "quad 코어가 뭔지"는 심화에서만 다룸. 하나)으로 구조화하고, "quad 코어가 뭔지"는 심화에서만 다룸.
**착수 시점**: 위 1~3번 항목과 동일하게 지금 당장 설계/작성할 필요는 **착수 시점**: 위 1~3번 항목과 동일하게 지금 당장 설계/작성할 필요는
없음(`CLAUDE.md` "지금 할 일" 1번 — 구현 착수가 최우선). 이 0번 항목은 없음(`.claude/todos.md` 1번 — 구현 착수가 최우선). 이 0번 항목은
"만약 시작한다면 이런 구조로"에 대한 합의이지, 착수 신호는 아님. "만약 시작한다면 이런 구조로"에 대한 합의이지, 착수 신호는 아님.
**콘텐츠 분류 완료(2026-08-06)**: 위 3축을 실제로 뭘로 채울지 **콘텐츠 분류 완료(2026-08-06)**: 위 3축을 실제로 뭘로 채울지
@ -173,9 +173,9 @@ UICorner/UIPadding/UIScale 숏핸드가 만드는 자식)는 `_`나 `QUAD_` 같
## 다음 단계 ## 다음 단계
셋 다 지금 당장 설계할 필요는 없음(`CLAUDE.md` "지금 할 일" 1번 — 셋 다 지금 당장 설계할 필요는 없음(`.claude/todos.md` 1번 —
구현 착수가 최우선). 사용자 판단이 필요한 것: 구현 착수가 최우선). 사용자 판단이 필요한 것:
- 이걸 `.claude/question.md`/`CLAUDE.md`에 정식 백로그 항목으로 올릴지, - 이걸 `.claude/question.md`/`.claude/todos.md`에 정식 백로그 항목으로 올릴지,
아니면 이 파일 하나로 충분한지. 아니면 이 파일 하나로 충분한지.
- 착수 시점 — quad-debug처럼 "개발 상당 부분 끝난 뒤"로 볼지, 아니면 - 착수 시점 — quad-debug처럼 "개발 상당 부분 끝난 뒤"로 볼지, 아니면
M1(스캐폴딩) 즈음부터 조금씩 곁들일지(네이밍 컨벤션은 특히 초기부터 M1(스캐폴딩) 즈음부터 조금씩 곁들일지(네이밍 컨벤션은 특히 초기부터

View file

@ -16,7 +16,7 @@
3. **오버엔지니어링/단순화 후보**: 확정됐다고 적혀 있지만 목적에 비해 과한 3. **오버엔지니어링/단순화 후보**: 확정됐다고 적혀 있지만 목적에 비해 과한
추상화로 보이거나 더 간단한 대안이 있어 보이는 지점. (단, "이미 여러 추상화로 보이거나 더 간단한 대안이 있어 보이는 지점. (단, "이미 여러
라운드에 걸쳐 검증됨"이라고 문서가 스스로 못박은 결정 자체를 재론하는 라운드에 걸쳐 검증됨"이라고 문서가 스스로 못박은 결정 자체를 재론하는
건 배제 — `CLAUDE.md`의 반복 조사 금지 원칙과 같은 이유.) 건 배제 — `.claude/project-context.md`의 반복 조사 금지 원칙과 같은 이유.)
이미 `.claude/question.md`에 취합된 항목(용어 재검토, M0 스파이크 항목 이미 `.claude/question.md`에 취합된 항목(용어 재검토, M0 스파이크 항목
자체, Slot 형제 순서 보장 등)은 여기서 제외했다 — 아래는 **전부 새로 발견된 자체, Slot 형제 순서 보장 등)은 여기서 제외했다 — 아래는 **전부 새로 발견된

View file

@ -20,7 +20,7 @@ compat.lua나 어댑터 코드는 전혀 없고, README/커밋 메시지에도 "
`archive/question-resolved.md`가 "OOP 상속/커스텀 파서/Slot 스텁/`Pipe` COW는 `archive/question-resolved.md`가 "OOP 상속/커스텀 파서/Slot 스텁/`Pipe` COW는
확인된 죽은 접근"이라고 명시한 목록에 compat은 **포함돼 있지 않음**(정확한 서술). 확인된 죽은 접근"이라고 명시한 목록에 compat은 **포함돼 있지 않음**(정확한 서술).
CLAUDE.md의 "반복 조사 금지"는 compat에는 적용되지 않는다 — 이 문서를 `.claude/project-context.md`의 "반복 조사 금지"는 compat에는 적용되지 않는다 — 이 문서를
쓰는 게 규칙 위반이 아님. 쓰는 게 규칙 위반이 아님.
## 2. v1 공개 API 표면 — 두 계층으로 나뉨 ## 2. v1 공개 API 표면 — 두 계층으로 나뉨
@ -266,7 +266,7 @@ v2 트리 안에 과거 v1 컴포넌트를 리프로 박아넣는 것, (B) 기
## 착수 시점 ## 착수 시점
지금 당장 설계/착수 불필요 — `CLAUDE.md` "지금 할 일" 1번(구현 착수, 지금 당장 설계/착수 불필요 — `.claude/todos.md` 1번(구현 착수,
ROADMAP M0)이 최우선. 7-3의 Slot 관련 두 항목은 M0 이후 Slot 구현 ROADMAP M0)이 최우선. 7-3의 Slot 관련 두 항목은 M0 이후 Slot 구현
라운드에서 실제 코드와 함께 재확인해야 풀림 — 그 전까진 이 문서의 제안 라운드에서 실제 코드와 함께 재확인해야 풀림 — 그 전까진 이 문서의 제안
(7-1/7-2 규칙들)이 최선의 추정치. (7-1/7-2 규칙들)이 최선의 추정치.

1276
.claude/session-summary.md Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,258 @@
# 2026-08-16 세션 — 코퍼스 감사 서브에이전트/워크플로 신설, CLAUDE.md 4분할
## 발단
사용자 질문 두 갈래로 시작:
1. 문서 툴링(`doc-check.py`)만으로는 **'실수'가 안 잡힌다** — `/code-review`
같은 걸 핸드오버 전/커밋 전에 에이전트가 스스로 돌게 만들 수 있나?
2. 어렵거나 목적에 더 맞는 게 있다면, 프로젝트 서브에이전트를 구성하고
CLAUDE.md에서 쓰도록 명시하는 쪽이 나을지. "summary를 실제 파일 한쪽으로
옮겨서 해결"하는 방식으로는 **단순 언급으로 남는 stale**을 잡기 어렵고
모든 곳에 적용도 어려워 보인다 — 현실적으로는 에이전트가 유의하고
서브에이전트가 새 맥락에서 훑는 게 최선 아닌가.
추가로 "다른 사람들은 이런 큰 기술 계획 문서에 에이전트를 어떻게 쓰는지"
검색해서 stale/실수 전파의 현실적 완화책을 알아봐 달라는 요청.
## 조사 결과
### hook으로 code-review를 직접 부를 수는 없음
hook은 **셸 명령만** 실행한다. Claude 스킬이나 서브에이전트를 직접 호출하는
경로가 없다. 대신 실제로 쓰이는 패턴은 **PreToolUse 게이트**:
`git commit`을 매칭해 조건 미충족이면 `deny`하고, **거부 사유 문자열로
"서브에이전트를 먼저 돌려라"는 지시를 돌려주는** 것(imti.co의 "Pre-Commit
Review Gate"). 즉 hook이 직접 부르는 게 아니라, 에이전트가 부르도록
**막아서 유도**한다.
그 글이 남긴 캐비엇도 유효:
- 기준이 느슨하면 서브에이전트가 통과용 형식적 발견을 지어냄("review theater")
- 너무 엄격하면 사소한 걸로 라운드가 5번씩 돌아 게이트가 아끼려던 시간을 까먹음
- 유효성 기준을 **diff 해시**로 잡으면 포매팅만 바뀌어도 재감사가 걸림(브리틀)
**quad엔 diff 해시 기준이 안 맞는다**는 판단: 이 프로젝트의 반복 실패 패턴은
"diff 안"이 아니라 **diff 밖 파일에 남은 stale 참조**다. 기준을 잡는다면
`.claude/**/*.md` 전체 해시여야 함.
### `/code-review`도 스킬이 맞음
marketplace 플러그인의 `code-review.md`를 직접 읽어 확인. 구조는:
Haiku로 자격 검사 → 관련 CLAUDE.md 목록 수집 → **5개 병렬 Sonnet 렌즈**
(CLAUDE.md 준수 / 얕은 버그 스캔 / git blame·히스토리 / 과거 PR 코멘트 /
코드 주석 준수) → 발견마다 Haiku가 **0~100 confidence rubric**으로 채점 →
80 미만 필터링 → 코멘트. false-positive 예시 목록도 프롬프트에 명시돼 있음.
즉 사용자 추측대로 "프론트메터와 자연어로 적절히 적으면 되는" 것이고,
프로젝트 전용으로 다시 만드는 게 타당.
### 학계 자료는 새 게 별로 없었음
"LLM agent drift / stale context mitigation" 검색 결과는 대부분 이 프로젝트가
이미 경험적으로 재발견한 것("신선한 세션/맥락이 stale을 줄인다", "요약 압축",
"주기적 리셋") 수준. 실무에 추가로 얹을 만한 구체 기법은 없었음.
## 만든 것 1 — `.claude/agents/quad-doc-auditor.md`
읽기 전용(`tools: Read, Grep, Glob, Bash`), `model: sonnet`, `memory: project`.
절차를 고정으로 박음: `doc-check.py` 먼저 → 이번에 뒤집힌/확정된 주장 파악
(git diff + 대화 맥락, 없으면 최근 session 파일) → 그 키워드로 코퍼스 전체
grep해서 **부정당하는 본문 문장** 찾기 → archive 이전 여부 → 개수/목록
이중소스 → 인덱스 3레이어 동기화.
설계 판단 두 개:
- **수정 권한 없음.** 발견만 리포트하고 반영은 호출한 세션이 함. 감사와
수정을 분리하는 게 지금 워크플로우와 맞음.
- **판정은 "확실/의심" 2단계뿐.** `/code-review`의 0~100 rubric은 diff 하나가
대상일 때 얘기고, 코퍼스 정합성엔 과한 무게라고 봄. "발견 없으면 그대로
발견 없음이라고 보고하라, 그럴듯해 보이려고 지어내지 마라"를 명시.
`memory: project`를 준 이유: "6개 문서" stale, "8차 세션" 오표기처럼 이
프로젝트에서 **실제로 반복된** 실수 유형을 지금은 사람이 CLAUDE.md 프로즈로
수기 기록 중인데, 그걸 에이전트 자신의 기억으로 축적시키는 게 맞다고 판단.
## 만든 것 2 — `.claude/workflows/quad-handover-audit.js`
사용자 지적이 핵심이었음: **"subagent가 문제를 항상 찾는다고 결정론적으로
보장되는 게 아니다. 메인 에이전트가 여럿 굴려서 커밋 전에 다 잡게 할 수
있나."** 그리고 "시간은 걸려도 괜찮다, 부정확성이 문제다. 멀티패스로 여러 번
굴려야 all consistent가 되기도 한다."
그래서 단발 호출이 아니라 **수렴 루프**로 구성:
- 라운드마다 `quad-doc-auditor`**병렬 3회** 독립 실행(서로 뭘 찾았는지
모르게 — 비결정성을 커버리지로 바꾸는 방식)
- 새 발견(dedup)을 **파일별로 묶어** `general-purpose`가 즉시 반영
- **새 발견 없는 라운드가 연속 2번** 나올 때까지 반복(최대 6라운드 안전판)
- 다음 라운드 감사가 직전 라운드 수정의 검증을 겸함 → 별도 검증 단계 불필요
`git commit`은 **의도적으로 워크플로 밖**에 뒀음 — diff 재검토와 커밋 메시지
작성은 대화형 맥락이 필요.
과거 수동 감사의 발견 추이(8→7→11→9→4→0, 9→2→3→2→0→0)를 근거로 상수를
잡음(`MAX_ROUNDS = 6`).
## 첫 실측 — 실패, 그리고 더 나쁜 건 실패가 성공으로 보고된 것
커밋(`a1c0e44`) 후 바로 돌림. 결과:
```
converged: true, rounds: 2, totalFindingsFixed: 0
failures: parallel[0..2] agent type 'quad-doc-auditor' not found (×2 라운드)
```
**에이전트 6개 전원 실패했는데 워크플로는 "수렴했다"고 보고했다.**
- 1차 원인은 예상됐던 것: `.claude/agents/`**세션 도중** 생긴 디렉토리라
레지스트리에 없음(공식 문서: 감시자는 세션 시작 시 존재했던 디렉토리만 포함).
- **진짜 문제는 2차 원인**: 모든 패스가 `null`이 되면 `fresh.length === 0`
되고, 이게 "깨끗한 라운드"와 코드상 구분이 안 돼서 그대로 dry 카운트가
올라가 수렴 판정이 났음. **감사 도구가 아무것도 감사하지 않고 통과 도장을
찍는 것** — 감사 도구로서 최악의 실패 모드.
즉시 가드 두 개 추가:
1. 라운드의 감사 패스가 **전멸하면 throw**(수렴 판정 자체를 못 내게).
일부만 실패하면 "커버리지가 그만큼 얕음"을 `log`.
2. **반영 에이전트가 실패하면 그 발견들을 `seen`에서 제거** — 안 그러면 다음
라운드 감사가 같은 문제를 다시 찾아와도 dedup에 걸려 조용히 사라지고,
안 고쳐진 채로 수렴 판정이 남(같은 클래스의 두 번째 버그).
### 부수 질문 — 서브에이전트 모델이 왜 Opus로 떴나
사용자가 진행 화면에서 `audit-r1-0 Opus 5 · failed`을 보고 물음(정의엔
`model: sonnet`인데). 답: **정의를 못 찾아서**. 공식 해석 순서는
`CLAUDE_CODE_SUBAGENT_MODEL` → 호출별 `model`**정의의 frontmatter**
메인 대화 모델인데, 정의가 없으니 3번이 통째로 건너뛰어지고 4번(당시
`/model opus`로 바뀐 Opus 5)으로 떨어진 것. 워크플로는 `opts.model`을 안
넘기므로 정의가 로드되면 `sonnet`이 이겨야 정상 — **재시작 후 첫 실행에서
표시 모델을 확인해야 확정.**
## CLAUDE.md 4분할
사용자 제안: CLAUDE.md가 사람이 검토하기 힘들다. `@name.md`로 flatten되어
컨텍스트에 들어가고 캐싱되니, 세션 히스토리 / 할 일 / 작업 방식+베이스
컨텍스트로 분화하는 게 나을 듯. **"같은 것끼리는 묶는다"가 아주 중요한
지점.** 그리고 `session-summary.md`는 절대 건들지 말라고 해놓고 **아예 전부
자동생성** 되게 하는 게 나을 것 같다.
### 실측 숫자
| 섹션 | 줄 수 | 비중 |
|---|---|---|
| 세션 히스토리 | 1231 | **80%** |
| 지금 할 일 | 116 | 8% |
| 작업 방식 | 92 | 6% |
| 계획 문서 구조 | 63 | 4% |
| 이 프로젝트가 뭔지 | 21 | 1% |
| 언어/모델 관례 | 12 | 1% |
| 합계 | 1537 | |
### 공식 문서에서 확인한 것 (셋 다 중요)
1. **권장치가 파일당 200줄**이고, 초과하면 컨텍스트만 더 쓰는 게 아니라
**"준수를 줄입니다"** — 사용자가 관찰한 "파일 길어지면 실수 더 하는
경향"이 문서에도 명시된 알려진 현상. 지금 7.7배 초과.
2. **`@import`는 컨텍스트를 안 줄인다** — "가져온 파일은 여전히 로드되고
시작 시 컨텍스트 윈도우에 들어갑니다". 분할이 사는 건 사람 검토성 +
에이전트 편집 정확도 + **파일 단위 자동생성 가능성**이지 토큰이 아님.
3. **블록 HTML 주석은 컨텍스트 주입 전 제거된다** — 사람용 메모를 토큰 0으로
남길 수 있는 대신, **거기 쓴 지시는 에이전트가 못 본다.**
(3)에서 이 세션이 자기 함정을 밟음: 새 CLAUDE.md의 "새 서술을 여기 직접 쌓지
말 것"을 HTML 주석에 넣었다가, 그게 스트립된다는 걸 문서로 확인하고 본문으로
옮김. 주석엔 "이 주석은 주입 전 제거되니 사람용 메모만 넣을 것"을 남김.
부수 발견: **`.claude/rules/*.md`** 라는 별도 메커니즘이 있음 — `@` 없이 자동
로드되고 `paths:` frontmatter로 **"특정 파일 작업할 때만 로드"**가 됨. 지금은
`src/`가 없어 쓸모없지만, M0 들어가면 "Handler 작성 체크리스트"를
`src/**/Dispatch/*.luau`에만 물리는 식으로 쓸 수 있음.
### 결정 — session-summary는 import 안 함
내가 사용자 안보다 한 발 더 나가는 제안을 함: 세션 히스토리의 고유 가치는
**"어느 세션 파일을 열지 찾는 색인"**뿐이고(그 문서 스스로 "항상 읽을 필요
없음, 지금 유효한 설계는 `base/`가 소스"라고 명시해왔음), 색인은 grep으로
온디맨드 조회하면 되는 종류다 → import 안 걸면 로드가 1537 → ~325줄.
사용자 동의: *"나도 session-summary.md 는 차라리 @ 안 붙이고, 그냥 거기에
있긴 하니까 만약에 선행 맥락이 필요하다면 그 때 가서 느리게 확인하도록
두는게 맞아보임."*
이름은 `code-of-conduct.md`를 제안받았으나 **OSS에서 "기여자 행동강령"이라는
다른 뜻으로 굳어진 말**이라 피하는 게 좋겠다고 제안 → `conventions.md` 채택.
### 최종 구조
```
CLAUDE.md (39줄, 진입점)
├─ @.claude/conventions.md 108줄 언어/모델 관례 + 작업 방식
├─ @.claude/project-context.md 88줄 프로젝트 설명 + 문서 구조
└─ @.claude/todos.md 119줄 지금 할 일
.claude/session-summary.md 1241줄 ← import 안 함(온디맨드)
```
매 세션 로드: **1537 → 354줄 (77% 감소)**. `session-summary.md`는 상단에
"자동생성 전환 예정, 전환 후엔 직접 편집 금지"를 명시해둠.
## 분할이 깨뜨린 상호참조 정리
20여 곳. 파일 그룹별 병렬 에이전트 3개(`base/` / `research/` /
루트+luau-test+audit)로 정정. 매핑은 `"지금 할 일"`→`todos.md`,
`"반복 조사 금지"`→`project-context.md`, `"세션 히스토리"`→`session-summary.md`,
SAFETY 원칙→`conventions.md`.
**가장 값진 결과는 base/ 담당 에이전트가 내 전제를 반박한 것**: 내가
"`dispatch-core-plan.md:41`의 CLAUDE.md 참조는 기존 stale"이라고 지시에
적어 보냈는데, 실제로는 **줄바꿈에 걸친 `.claude/initreq/tbox/CLAUDE.md`
참조**(tbox의 type-check/constraint-check 분리 원칙 인용)로 완전히 유효한
것이었음. 같은 패턴이 `slot-plan.md:1285`, `ref-plan.md:190`에도 있음.
에이전트가 "다른 에이전트에게 같은 전제를 넘겼다면 정정이 필요하다"까지
보고함 — 지시를 그대로 믿지 않고 검증한 게 정확히 이 감사 구조가 노린 것.
같은 에이전트가 `ref-plan.md:541``위 "지금 할 일" 우선순위1 항목`
발견 — 분할과 무관한 **원래부터 잘못된 참조**였고(그 "우선순위1"은
`research/pre-implementation-audit.md`의 용어), 진단이 확실해서
`pre-implementation-audit.md` 1-5로 정정.
**추측 수정을 금지한 게 효과가 있었음.** 진짜 orphan 3건은 손대지 않고
보고만 하게 했고, 코퍼스 전체 grep으로 인용된 원칙이 어디에도 없음을 확인:
- `modifier-plan.md:536` — CLAUDE.md의 "드문 오용/가상 미래 요구까지
방어/최적화하려고 구조를 복잡하게 만들지 않는다" 원칙
- `v1-compat-plan.md:50` — CLAUDE.md에 "이 자동 위임/재렌더 매직은 v2에서
폐기하기로 확정"이라 기록됐다는 서술
- `pre-implementation-audit.md:434` — CLAUDE.md가 M2/M3/M5에서 quad-debug
훅 확장 지점을 고려하라고 명시해뒀다는 서술
셋 다 **분할 이전부터** 존재하지 않던 인용이다. 원칙을 복원할지 인용을
버릴지는 사용자 판단 영역이라 남겨둠.
## `doc-check.py` 갱신
1. **새 파일 4개를 `OURS` 정규식에 등록.** 안 하면 `.claude/todos.md`로 가는
깨진 참조가 ERROR가 아니라 WARN으로만 잡힘 — 일곱 번째 세션에
`store-semantics.md`로 실제로 당했던 사각지대와 같은 클래스.
2. **`is_history()` 헬퍼 신설** — `archive/``session-summary.md`를 묶어
절 참조/시한부 주장 검사에서 면제. 둘 다 "그때는 그렇게 적었다"가 정상인
문서라 현재형 검사를 걸면 안 꺼지는 WARN만 쌓임.
최종 ERROR 0 유지(검사 대상 78 → 82개).
## `doc-include-plan.md` 단순화
이 분할이 그 플랜의 **설계 절반을 불필요하게 만들었음.** 원안은 원본에
`<!--#summary-->`, 인용처에 `<!--#include-->` 두 종류 마커를 두는 양방향
설계였는데, 세션 히스토리가 독립 파일이 되면서 목적지가 "손으로 쓴 파일 속
구간"이 아니라 **통째로 생성되는 파일**이 됨 → 목적지 마커가 필요 없어지고,
id도 불필요(파일명이 곧 순서이자 식별자). "목적지 마커가 손실되면?" 같은
실패 모드도 같이 사라짐. 파일럿 절을 이 정정으로 갱신.
## 남은 것
- **재시작 후 검증 필요**: (a) `/memory``@import` 4개가 실제 로드되는지,
(b) `quad-doc-auditor`가 레지스트리에 잡히는지, (c) 워크플로에서 그
에이전트가 frontmatter의 `sonnet`으로 도는지(Opus로 뜨면 `opts.model`
명시가 필요).
- **`session-summary.md` 자동생성 미착수** — 91개 세션 파일에 마커를 넣고
생성기를 짜는 마이그레이션이 남음. 그 전까지는 손으로 관리 중이며 파일
상단에 그렇게 명시해둠.
- 위 orphan 인용 3건.

121
.claude/todos.md Normal file
View file

@ -0,0 +1,121 @@
# 지금 할 일 (우선순위순)
루트 `CLAUDE.md``@import` 하는 파일. **가장 자주 바뀜** — 해소된 항목은
미루지 말고 그 자리에서 지우고, 개수·목록은 여기 적지 말고 소스를 가리킬 것
(`.claude/question.md`, `luau-test/STATUS.md` 등).
0. **⭐ M0 착수를 막는 결정은 이제 없음 (2026-08-14 열한 번째 세션 기준).**
`question.md`의 최우선 항목이 **전부 비었음**`0-Y`(`:Compute` lazy
핸들 계약)는 13차 세션에, `0-Z`(Attribute 이름 소유권)와 `0-A`(재디스패치
하강 diff)는 14차 세션에, `0-B`(`dispose` 시그니처/범위)는 2026-08-14
열 번째 세션에 확정·`base/` 반영 완료. **`0-W`(같은 `Ref` 이중 배치,
M8 구현 세부만 막던 항목)도 2026-08-14 열한 번째 세션에 해소** —
선택지 (a) 채택(즉시 error), 메커니즘은 새 `Relate` 없이
`bindLifetime`/`unbindLifetime` 재사용(`base/ref-plan.md` "이중 배치
방지" 절). 부수 결정으로 **`canBound``canExecute`와 별도 진입점으로
재도입**됨(2026-08-14 다섯 번째 세션에 하나로 합쳤던 걸 부분적으로
되짚음 — "이미 묶여 있는가"(bound 문맥)와 "지금 발화해도 되는가"
(execute 문맥)는 판정 로직은 공유해도 호출부의 질문이 다르다는 사용자
지적, `base/lifecycle-pattern.md`의 "`canBound` vs `canExecute`" 절).
`question.md`엔 이제 "결정 대기" 절 자체가 없음(비어서 헤딩째로 삭제).
**M0 착수 전 반드시 읽을 것 — 이 두 개는 "결정"이 아니라 "구현 규약"이라
여전히 유효**:
- **`base/typing-limits.md`**(0-Y의 산물) — 핵심은 "파생 State를 만드는
자리마다 결과 타입을 명시 주석으로 바인딩" + 7번 설계 체크리스트.
재귀 제네릭이 자기를 다른 타입 인자로 반환하면 Luau가 타입 안전성을
**에러 없이 조용히** 잃는 상위 한계라 quad 쪽에서 우회하지 않기로
확정(RFC `relax-recursive-type-restriction` 수혜 대기, 추적
`luau-lang/luau#2380`). 실측 근거는 `audit/type-recursion-issue/`.
- **`base/dispatch-core-plan.md`**(0-A/0-Z의 산물, 14차 세션에
`bind-system-plan.md`에서 분리 신설) — 재디스패치가 "철거 후 재구축"이
아니라 **하강 diff**임, `retractFrom`은 3-인자, 클로저 인자는
`nil`이거나 같은 핸들러가 처리할 값(타입 보장), `HANDLER_PRIORITY_FALLBACK`,
"base가 소유하는 핸들러와 주입되는 엔진 op"(`addTag`/`removeTag`/
`setAttribute`). **Handler 작성 체크리스트 8개**를 새 핸들러 짜기 전에
훑을 것 — 지난 세션들에서 실제로 반복된 실수 목록임.
해소 전 원문은 `archive/question-resolved.md`(0-Y/0-Z/0-A 절), 뒤집힌 옛
재디스패치 모델 전문은 `archive/dispatch-hintvalue-model-reversed.md`.
1. **구현 시작 — 루트 `ROADMAP.md`의 M0부터.** 설계 단계는 2026-08-04 로드맵
인수인계 라운드로 종료. `research/pre-implementation-audit.md` 우선순위1은
2026-08-12 열일곱 번째 세션에 마지막 넷(1-3/1-4/1-10/1-11)까지 전부
해소되어 **11개 전원 완료**. **[14차 세션 기준] 0-Y/0-Z/0-A까지 전부
해소돼 설계 게이트는 남아있지 않음** — 착수 전 읽을 것은 위 0번의 두
문서(`typing-limits.md`/`dispatch-core-plan.md`)뿐이고, 스파이크 상태는
아래 그대로:
- **`.claude/luau-test/`(2026-08-09 신설, 2026-08-13 기준 20개) 스파이크
결과 — [2026-08-13 여섯 번째 세션에 첫 실측 완료, 대부분 닫힘].**
**상태의 소스는 항상 `.claude/luau-test/STATUS.md`**(pass / 사람 결정
필요 / 스파이크 깨짐 / 미실행, 폴더 구조 자체가 상태) — 몇 개가 지금
어느 폴더에 있는지는 여기서 나열 안 함(04/05/10/13/15/16/19가 여러
세션에 걸쳐 재설계로 `rewrite-required/`에 들고나며 이 문단의 나열이
매번 stale해지는 패턴이 반복됐음, 최근엔 8차 세션의 "emit은 항상
전파" 정정으로 `05`도 합류). 실행 결과 상세는
`.claude/audit/luau-test-first-run-2026-08-13.md`. 첫 실측 요지만
(역사적 사실 — 이후 변동은 위처럼 `STATUS.md`가 소스):
- **런타임 12개 전원 통과**(01~07/11/17/18/19/20, crash 0 / FAIL 0) —
특히 `07`이 연쇄 GC를, `18`이 두-`Relate` 상호 순환 미해제를 실측
확정해 GC-native 아키텍처의 핵심 전제가 검증됨. `04`는 같은 세션
감사가 찾은 `chains:SetStrong` 순서 버그를 음성 대조군으로 재현.
- **타입 쪽에서 하나가 걸렸었음** → 그게 구 **0-Y**, **[13차 세션]
해소**(Luau 현 한계로 확정, `base/typing-limits.md`). 나머지 타입
스파이크는 판정 완료(`08`/`09` 통과, `12`는 실패지만 문서가 이미
fallback으로 예비해둔 결과라 설계 영향 없음, `14`는 부분).
지금 M0 착수를 막는 설계 결정은 없고, 0-Y/0-A가 남긴 규약
(`base/typing-limits.md`/`base/dispatch-core-plan.md`)은 착수 전 필독.
2. **용어 정리 — 1차 제안 이후 대부분 확정, 소수만 남음.** 최신 소스는
`.claude/question.md` 1번(개수 반복 안 함, 항목 추가/해소될 때마다 여기가
stale해지는 패턴이 반복됐어서). **[2026-08-13 정정]** `State`
2026-08-12 스무 번째 세션에 현재 이름 그대로 유지로 이미 확정됐음(이
목록이 "위험도 높음, 1순위 open"으로 stale하게 남아있던 걸 발견해 수정)
— 아직 진짜로 열려있는 것만 짚으면: `DI`→`D`(1순위), `Slot`(2순위),
`canExecute`(3순위 — `isAlive`는 검토 후 기각, `can` 계열 접두 유지
방향으로 기울었으나 구체 대안 미정), `Brand`(3순위), `Tag`/`Added`/
`Removed`/`Merged`(3순위), `Attribute`/`AttributeKey`(3순위).
3. **[2026-08-14 세션에 해소]** 오래 열려 있던 "이미 생성된 인스턴스
재바인드"는 **기각**되어 `archive/existing-instance-bind-rejected.md`
이전됨 — 더 이상 상의할 스코프 항목이 아님.
4. **[백로그]** 범용 렌더 디버깅 도구 `quad-mock`(Tween mock 등 동적 동작
지원, M0 mock 테스트 하네스와는 별개), 런타임 디버깅 플러그인
`quad-debug`(Studio 플러그인, 실물 Instance→코드 위치 역추적 — 채널
실현 가능성은 실측 검증 완료, 세부 API 이름만 남음), 문서 사이트 전체
구조(초심자/api/심화/`quadnomicon` 4축 + 콘텐츠 맵), `Operator` 콤비네이터
슈가(`Sum`/`Product`/`Not`/비트연산 등 `:Compute`/`:Apply`용 — 메커니즘은
확정, 네임스페이스 이름만 미정, 구현은 순수 슈가라 맨 마지막), 컴포넌트
에러 격리 유틸 `Fallback`/`Traceback`(**[2026-08-14 세션, 설계 확정 —
`research/`에서 `base/fallback-plan.md`로 승격]** `pcall` 기반
`Fallback``xpcall`+`debug.traceback` 기반 `Traceback`으로 분리,
`err: any` 확정, 패키지·이름 전부 확정 — **설계만 끝났을 뿐 구현
우선순위는 그대로 맨 뒤**), 생명주기 훅
`OnCreated`/`OnRendered`/`OnDestroyed`(**[2026-08-14 아홉 번째 세션,
`research/`에서 `base/lifecycle-hooks-plan.md`로 승격]** 각각
`PreRef`/`PostRef`/`Effect`를 반환하는 순수 팩토리 함수 슈가 —
`OnRendered`**채택 확정**, 그게 얹히는 `PostRef` 프리미티브 자체는
슈가가 아니라 디스패치 코어라 **ROADMAP M8에서 `PreRef`와 같이 구현됨**
(백로그가 아님, `base/ref-plan.md`의 "`PostRef`" 절). 훅 슈가 셋만
후순위) — 전부
"quad 개발 상당 부분 끝난 뒤"로 사용자가 못박은 후순위. 상세는
`.claude/README.md``base/` 표(`fallback-plan.md`/
`lifecycle-hooks-plan.md`)와 `research/`
(`debug-tooling-plan.md`/`documentation-plan.md`/
`documentation-content-map.md`/`framework-comparison-findings.md`/
`operator-sugar-plan.md`).
**[2026-08-14 추가, 성격이 다름]** 시간 기반 전파 게이트
`Debounce`/`Throttle`(`research/debounce-throttle-plan.md`)도 백로그이긴
하나 위 항목들과 달리 **사용자가 직접 요청한 실제 기능 갭**이고 순수
슈가가 아님 — M0/M3를 막지는 않지만, **M3에서 `Blocker`를 구현할 때
게이티드 노드를 공용 `Gate`로 빼두는 것만은 그 시점에 해야 함**(따로
하면 같은 설계를 두 번 함). 주입 op 2개(`setTimeout`/`clearTimeout`)가
백엔드 팩토리 표면에 추가될 예정이라는 것도 M1 설계 시 인지. 설계는
네 라운드로 대부분 확정됐고 남은 열린 질문은 `question.md` 3번(개수는
거기도 반복 안 함 — 소스는 `research/debounce-throttle-plan.md` 12절).
5. 자율 작업 루프/스케줄 설정 여부는 사용자 결정 대기 중
(`HUMAN_TODO.md` 2번 항목).
6. **[신규 백로그, 2026-08-14 열네 번째 세션]** 문서 stale 감소용 include
도구 `doc-include.py`(가칭, `doc-check.py`와 짝) — `research/
doc-include-plan.md` 참고. 플랜만 초안, **사용자가 내일 다듬을
예정**. M0/설계 게이트와 무관.

View file

@ -22,6 +22,8 @@
5. [WARN] 미반영 배너를 파일 vs 반영 목록 일치 여부 5. [WARN] 미반영 배너를 파일 vs 반영 목록 일치 여부
`session/`(원문 보존), `initreq/`(읽기 전용 클론) 검사 대상에서 제외. `session/`(원문 보존), `initreq/`(읽기 전용 클론) 검사 대상에서 제외.
`archive/` `.claude/session-summary.md` 검사 대상이되 **히스토리 문서**
참조/시한부 주장 검사는 면제(`is_history()` 참고).
""" """
import os, re, sys, collections import os, re, sys, collections
@ -92,7 +94,11 @@ def resolve(target, src):
# 안 걸리면 외부 문서명(Compose 가이드라인 등)일 수 있어 WARN으로 낮춤. # 안 걸리면 외부 문서명(Compose 가이드라인 등)일 수 있어 WARN으로 낮춤.
OURS = re.compile(r'(-plan|-reversed|-rejected|-findings|-map|-audit|-verification' OURS = re.compile(r'(-plan|-reversed|-rejected|-findings|-map|-audit|-verification'
r'|^README|^STATUS|^CLAUDE|^ROADMAP|^HUMAN_TODO|^SAFETY' r'|^README|^STATUS|^CLAUDE|^ROADMAP|^HUMAN_TODO|^SAFETY'
r'|^question|^architecture|^agent-mistake)\.md$') r'|^question|^architecture|^agent-mistake'
# [2026-08-16] CLAUDE.md 분할 산물. 여기 안 넣으면 이 파일들로
# 가는 깨진 참조가 ERROR가 아니라 WARN으로만 잡힌다 — 일곱 번째
# 세션에 store-semantics.md에서 실제로 당했던 사각지대.
r'|^conventions|^project-context|^todos|^session-summary)\.md$')
def headings(path): def headings(path):
@ -121,11 +127,23 @@ def interesting(target):
return True return True
def is_history(path):
"""히스토리 문서인가 — 절 참조/시한부 주장 검사를 면제한다.
`archive/` 뒤집힌 결정의 원문 보존, `session-summary.md` 세션별
과거 기록이라 "그때는 그렇게 적었다" 정상이다. 여기에 현재형
검사를 걸면 영원히 꺼지는 WARN만 쌓인다.
[2026-08-16] CLAUDE.md 분할로 세션 히스토리가 별도 파일이 되면서 추가.
"""
r = rel(path)
return '/archive/' in r or os.path.basename(r) == 'session-summary.md'
def check_refs(docs): def check_refs(docs):
hcache = {} hcache = {}
for d in docs: for d in docs:
# archive는 히스토리라 절 참조까지 강제하지 않음(파일 존재만) # 히스토리 문서는 절 참조까지 강제하지 않음(파일 존재만)
is_archive = '/archive/' in rel(d) is_archive = is_history(d)
# 줄 단위가 아니라 **파일 전체**를 한 번에 스캔 — 파일명/절 제목이 # 줄 단위가 아니라 **파일 전체**를 한 번에 스캔 — 파일명/절 제목이
# 줄바꿈에 걸친 인용도 잡기 위함(위 REF 주석 참고). 줄 번호는 매치 # 줄바꿈에 걸친 인용도 잡기 위함(위 REF 주석 참고). 줄 번호는 매치
# 시작 오프셋으로 역산. # 시작 오프셋으로 역산.
@ -186,7 +204,7 @@ DATED = re.compile(r'20\d\d-\d\d-\d\d|\[\s*(?:해소|정정|갱신|확정|역전
def check_temporal(docs): def check_temporal(docs):
for d in docs: for d in docs:
if '/archive/' in rel(d): if is_history(d):
continue continue
lines = open(d, encoding='utf-8').read().split('\n') lines = open(d, encoding='utf-8').read().split('\n')
for i, line in enumerate(lines, 1): for i, line in enumerate(lines, 1):

View file

@ -51,7 +51,7 @@ function fixPrompt(file, items) {
.join('\n') .join('\n')
return ( return (
`아래는 quad-doc-auditor가 "${file}"에서 찾은 stale/모순 서술이다. ` + `아래는 quad-doc-auditor가 "${file}"에서 찾은 stale/모순 서술이다. ` +
`이 파일을 읽고 CLAUDE.md의 관례(한국어 서술, 최소 수정, 뒤집힌 결정은 ` + `이 파일을 읽고 .claude/conventions.md의 관례(한국어 서술, 최소 수정, 뒤집힌 결정은 ` +
`archive/로 이전+포인터, 날짜 없는 시한부 주장엔 날짜 붙이기)에 맞춰 직접 ` + `archive/로 이전+포인터, 날짜 없는 시한부 주장엔 날짜 붙이기)에 맞춰 직접 ` +
`고쳐라. "의심"으로 표시된 항목은 실제로 문제인지 먼저 확인하고, 문제가 ` + `고쳐라. "의심"으로 표시된 항목은 실제로 문제인지 먼저 확인하고, 문제가 ` +
`아니면 건드리지 말고 넘어가라(억지로 고치지 말 것).\n\n${lines}` `아니면 건드리지 말고 넘어가라(억지로 고치지 말 것).\n\n${lines}`
@ -79,7 +79,28 @@ while (dry < DRY_ROUNDS_TO_CONVERGE && round < MAX_ROUNDS) {
) )
) )
const all = passes.filter(Boolean).flatMap((p) => p.findings || []) // ⚠️ 가짜 초록불 방지 — 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))) const fresh = all.filter((f) => !seen.has(keyOf(f)))
if (!fresh.length) { if (!fresh.length) {
@ -99,8 +120,9 @@ while (dry < DRY_ROUNDS_TO_CONVERGE && round < MAX_ROUNDS) {
} }
phase('Fix') phase('Fix')
await parallel( const fileEntries = Object.entries(byFile)
Object.entries(byFile).map(([file, items]) => () => const fixed = await parallel(
fileEntries.map(([file, items]) => () =>
agent(fixPrompt(file, items), { agent(fixPrompt(file, items), {
label: `fix:${file}`, label: `fix:${file}`,
phase: 'Fix', phase: 'Fix',
@ -109,7 +131,27 @@ while (dry < DRY_ROUNDS_TO_CONVERGE && round < MAX_ROUNDS) {
) )
) )
roundLog.push({ round, fresh: fresh.length, files: Object.keys(byFile) }) // 반영 에이전트가 죽으면 그 발견들을 `seen`에서 빼둔다 — 안 그러면
// 다음 라운드 감사가 같은 문제를 다시 찾아와도 dedup에 걸려 조용히
// 사라지고, 안 고쳐진 채로 수렴 판정이 난다(위와 같은 클래스의 버그).
const failedFiles = []
fixed.forEach((r, i) => {
if (r === null) {
const [file, items] = fileEntries[i]
failedFiles.push(file)
items.forEach((f) => seen.delete(keyOf(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 const converged = dry >= DRY_ROUNDS_TO_CONVERGE

1556
CLAUDE.md

File diff suppressed because it is too large Load diff

View file

@ -22,7 +22,7 @@ Roblox가 2026-02부터 Studio에 **MCP 서버를 내장**했음 — 예전처
**주의(사용자가 이미 말한 것)**: Roblox Studio는 잘 죽는 편 — 죽었을 때 살리려고 **주의(사용자가 이미 말한 것)**: Roblox Studio는 잘 죽는 편 — 죽었을 때 살리려고
위험한 명령을 반복 시도하지 않을 것이고, 그런 날엔 MCP 없이 할 수 있는 작업만 위험한 명령을 반복 시도하지 않을 것이고, 그런 날엔 MCP 없이 할 수 있는 작업만
하거나 대기함. 이 안전 원칙은 `CLAUDE.md`에도 적어둠. 하거나 대기함. 이 안전 원칙은 `.claude/conventions.md`에도 적어둠.
**해야 할 일**: 테스트용 place 파일(빈 place 하나, 또는 `quad/test.project.json` **해야 할 일**: 테스트용 place 파일(빈 place 하나, 또는 `quad/test.project.json`
기반 rojo 싱크 대상)을 열어서 베타 기능만 켜주면 됨. 이후 MCP 서버 설정 파일 기반 rojo 싱크 대상)을 열어서 베타 기능만 켜주면 됨. 이후 MCP 서버 설정 파일

View file

@ -826,9 +826,9 @@ Luau 코드로 부딪혀본 적 없는 세 가지**를 던지는 코드로 검
대체·해소됨) — `.claude/question.md` 1번, 최종 이름 확정되는 대로 대체·해소됨) — `.claude/question.md` 1번, 최종 이름 확정되는 대로
아무 시점에나 아무 시점에나
- [ ] 각 마일스톤 완료 시 `.claude/qa-request/`/`.claude/archive/`에 기록, - [ ] 각 마일스톤 완료 시 `.claude/qa-request/`/`.claude/archive/`에 기록,
필요하면 `CLAUDE.md` "세션 히스토리"도 갱신(전체 원문은 필요하면 `.claude/session-summary.md` "세션 히스토리"도 갱신(전체 원문은
`.claude/session/`에, CLAUDE.md엔 2~4줄 요약+링크만 — 2026-08-11 `.claude/session/`에, `.claude/session-summary.md`엔 2~4줄 요약+링크만
재구조화 세션 참고) — 2026-08-11 재구조화 세션 참고)
## 백로그 (스코프 밖 — 필요성이 실제로 드러나면 그때 설계) ## 백로그 (스코프 밖 — 필요성이 실제로 드러나면 그때 설계)