CLAUDE.md had grown to 3196 lines of accumulated session logs, causing context bloat. Full session narratives (including trial-and-error and later-corrected reasoning — quadnomicon devlog raw material) now live as 39 individual files under .claude/session/. CLAUDE.md keeps only a short "지금 할 일" (re-synced against question.md/pre-implementation-audit.md, stale detail dropped) and a compact per-session summary+link table. No design decisions changed; base/research/question.md were already in sync with every session (verified against README.md/question.md before archiving), so no unreflected content needed migrating first.
9.2 KiB
2026-08-09 여덟 번째 세션 — .claude/base/ 전체 중간검토(질문 모드),
실제 설계 결함 다수 발견·수정
사용자가 "이 프로젝트의 계획을 중간검토합니다. 각 요소들에 대해서 함수나
클래스 등의 동작을 제가 확인 가능하게 리스팅해요... 질문 모드를 쓰면
좋겠습니다"라고 요청 — 2026-08-04 6차 라운드 때 예고해뒀던 "다음 세션
검증 패스"를 실제로 실행한 세션. 서브에이전트 6개를 병렬로 띄워
.claude/base/ 전체(15개 파일, 5296줄)를 클러스터별로 정독시켜 확정된
API/동작을 file:line 인용과 함께 그라운딩된 리스팅으로 뽑아낸 뒤, 6개
배치(Store/State/Source+Dispatch, Ref/PreRef+Brand+Length-Offset+생명주기,
Modifier, Slot, Tag/Attribute/UI shorthand+Blocker/Effect, 컴포넌트
경계+아키텍처)로 나눠 각 배치를 텍스트로 보여주고 바로 AskUserQuestion
(문제없음/문제있음)으로 확인받는 방식으로 진행 — 문제 제기된 건 그
자리에서 바로 문서에 반영(끝까지 미루지 않음). 총 24개 확인 질문 중
약 1/3에서 실제 설계 결함이 나옴 — 전부 사용자가 구체적인 반례/Luau
시맨틱스를 근거로 지적한 것이라 전부 그대로 수용, 방어하지 않고 수정.
발견·수정된 것 (파일별):
base/bind-system-plan.md(가장 많이 고침):Source(default)/Ref(default)의default생략이 "선택"이라는 서술에 "T가 nilable일 때만 안전하다"는 캐비엇 누락 — 추가.Ref는:Callback이 등록 즉시 발화해서 이 문제가 더 잘 드러남.- Dispatch 체인 절에 "
handler.process를Dispatch.process없이 직접 호출하면 UB(체인 bookkeeping이 깨져retract가 영영 안 불리거나 정합성이 무너짐)"라는 불변식이 안 적혀 있었음 — 추가. - Ref 콜백/대기자 배열의 소진 슬롯을
None에서nil로 되돌림 — 2026-08-07 열 번째 세션에 "구멍 있는 정수 키는 순회 순서가 깨진다"는 이유로None으로 바꿨던 게 이 배열엔 안 맞는 처방이었음(사용자 지적): 이 배열은 순서가 안 중요해서 일반화for가 구멍이 있어도 전부 방문하고, 오히려None을 쓰면 슬롯이 영원히 안 비어서:Wait()마다 배열이 끝없이 길어지는 새 문제가 생김 —nil로 지우고 빈 슬롯을 재사용하는 등록 함수로 바꿈. PreRef pre-pass/ Length-Offset의sourceList는 순서가 실제로 중요해서 계속None이 맞음 — 두 사례를 헷갈리지 않게 교차 참조로 명확히 구분. .Value가 평범한 hash 필드가 아니라__index로 구현돼야 하는 이유(콜백 배열과 같은 테이블에 있으면T가 함수/스레드일 때 콜백 처리 루프에 오분류될 위험) 추가.isRef/isPreRef를isState/isSource와 같은 상위-하위 합성 패턴으로 재정정 — 원래 "서로 배타적인 형제 브랜드"였는데,Source가State를 만족하듯PreRef도Ref런타임을 재사용하는 관계라 같은 방향(하위=PreRef가 상위=Ref에 포함)으로 다뤄야 일관적이라는 지적 —isPreRef가 가장 구체적인 항등,isRef는 그 위에 얹힌 상위 개념.(v=Ref)children leaf 매치 핸들러는 이제isRef(v) and not isPreRef(v)로 명시적으로 좁혀야 함.NoneHandler가k타입을 안 가리는데 왜 배열 파트None(숫자 키)에 실제로 안 걸리는지 명확화(배열 파트None은 애초에Dispatch.process를 안 타서NoneHandler가 볼 기회 자체가 없음).setLength/setOffsetSource의None페어링 대상을 "Ref/PreRef 등" 예시 목록에서 "그 배열 위치의 값 자체가None인 모든 경우"로 명시적으로 확장, 둘이 항상 짝을 맞춰야 한다는 점도 재강조.:Subscribe()가 quad 전역 GC-native 원칙의 의도적 예외(참조를 다 놓아도 GC 안 되고 계속 실행됨)라는 경고가 없었음 — 추가, 용도도 "완전히 top-level" 케이스로 좁혀 문서화.
base/modifier-plan.md: 핸들러 계층 값 → error 체크가State<Ref>류 "State/Source가 감싼 내부 값"까지는 못 잡는다는 한계 — 명시적 UB로 문서화(오버엔지니어링 방지, 실사용 위험 낮음).base/slot-plan.md(가장 큰 변경): CRUD 식별 기준을 element 레퍼런스에서 인덱스 기준으로 전환 —Remove(index)/Extract(index, newElement?)/Move(oldIndex, newIndex). 원래 "인덱스는 stale해진다"는 이유로 레퍼런스 기준을 택했는데, 실제로는 반대(호출부가Add리턴값을 안 담고 흘려버리는 경우가 흔함)가 더 큰 문제였음.ExtractAll()/Get(index)/IndexOf(element)신설 (Get은 "YAGNI"로 드롭했던 걸 재추가).Extract(index, newElement?)신설 — 교체가 필요하면 기존엔 Extract+Add 이중 O(n) 시프트가 필요했는데, 이제 O(1) 제자리 교체 가능(이전 element 반환).base/tag-plan.md:TagHandler.retract의 전체 삭제 동작이 정확히v == nil일 때만 맞다는 전제를assert로 명시(기존엔 "v를 안 봐도 됨"이라고만 서술돼 있어 조건이 암묵적이었음).base/attribute-plan.md,.claude/question.md: 타입 파라미터화(Attribute<<T>>제네릭 vsBooleanAttribute류 정적 패밀리) — "미확정"에서 "둘 다 채택"으로 확정(내부 구현 동일, 호출부 표기만 다름).=뒤 값 타입까지 narrowing되는지는 M0/M10 실측 필요(안 돼도 런타임 무관)로 명시.base/ui-shorthand-plan.md:UICorner/UIPadding/UIScale이 타입 생성 스크립트가 만드는FrameModifier류 타입의 메소드 목록에도 포함돼야 한다는 체크리스트 항목 추가(런타임과 무관한 순수 타입 생성 디테일).base/effect-plan.md:EffectHandle이 내부 Observer를 필드로 강참조한다는 것,bindLifetime/:Subscribe()둘 다state가 있으면 내부 Observer까지 cascade해야 한다는 것(안 그러면 내부 Observer의canExecute게이팅이 올바른inst를 못 봄) — 재확인 후 명시화.base/component-composition-plan.md(Length/Offset 다음으로 많이 고침):- "리프 바인딩엔 Source가 좁은 예외"라는 서술이 틀림 — 정정.
local a = Source(true); Frame { Visible = a }; a:Set(false)처럼 Source를 리프에 직접 물리는 건 이미 확정된 "Source가 State를 구조적으로 만족" 원칙이 그대로 커버하는 정상 경로였음 — "State가 일반적"이라는 서술은 Source를 못 쓴다는 뜻이 아니라 "여러 값에서 파생된 계산 결과는 State일 수밖에 없다"는 통계적 경향 서술일 뿐이라고 재정정. props.Modifier or None관용구의None근거 포인터가 Ref 콜백 배열 정정으로 깨질 뻔한 걸 교차 참조로 바로잡음(그 배열은 순서가 중요한 별개 케이스라None이 계속 맞음).Frame { Comp{} }에서Comp가Slot을 반환하는 다중 루트 우회 경로가 새 배선 없이 그대로 작동함을 재확인(값이 컴포넌트 호출로 왔든 리터럴이든 디스패치 입장에선 구분 없음).
- "리프 바인딩엔 Source가 좁은 예외"라는 서술이 틀림 — 정정.
ROADMAP.md: 위RefNone→nil/isRef·isPreRef변경사항 체크박스 동기화.
변경 없이 확인만 된 것: :With/:Compute 체이닝, None 센티널
기본 메커니즘, Length/Offset 전체, 이중 바인딩 금지/Relate/생명주기,
Modifier setter/Apply/Overridden 판단 기준, Peek/isState/None
setter 인자, Slot 요소 타입 제약/Extract portal/Length, Slot:List
시그니처(단, 캐스케이드 성능 이슈는 keyFn 명시 유도로 이미 문서화돼
있어 추가 조치 불필요), List 구독 lazy 시점, Tag 값 모양/패키지 배치,
Blocker 전체, 소스트리/네이밍 컨벤션/Handler 3분류/테스트 전략/이식성
원칙.
부수 기록: .claude/memory(세션 간 영속 기억)의 협업 스타일 메모에
이번 리뷰 진행 방식(에이전트 병렬 추출 → 배치별 텍스트+AskUserQuestion
즉시 확인 → 그 자리에서 바로 문서 반영)을 다음에 재사용할 패턴으로
기록 완료.
다음 세션이 할 일: 안 바뀜(ROADMAP.md M0부터) — 이번 세션은 설계
확정이 아니라 기존 확정 사항의 결함 수정이었지만, 결과적으로 M0 착수
전 상태가 더 탄탄해졌을 뿐 우선순위 자체는 그대로. 이 중간검토가
마지막 배치(6단계)까지 끝났는지, 사용자가 이어서 더 볼 부분이 있는지는
다음 세션 시작 시 확인.