From 91fd7b88a8e7483746e3e7032a9bfdbb4bb39419 Mon Sep 17 00:00:00 2001 From: qwreey Date: Thu, 13 Aug 2026 17:20:45 +0900 Subject: [PATCH] =?UTF-8?q?docs(audit):=20=EC=BD=94=ED=8D=BC=EC=8A=A4=205?= =?UTF-8?q?=EC=B0=A8=20=EA=B0=90=EC=82=AC=20=E2=80=94=200-Y/0-Z=20?= =?UTF-8?q?=ED=8F=AC=EC=9D=B8=ED=84=B0=204=EA=B3=B3=20+=20=ED=8C=8C?= =?UTF-8?q?=EA=B4=B4=20=EC=9A=A9=EC=96=B4=20=EC=9E=94=EC=A1=B4=205?= =?UTF-8?q?=EA=B3=B3=20=EC=A0=84=EC=88=98=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 4차 감사(반복되던 두 패턴을 grep으로 전수 스윕)에서 발견한 것 정리: - architecture.md: 모든 세션이 "먼저 읽으라"는 진입점 파일인데 소스 트리의 chains/retractFrom 서술에 0-Z 포인터가 전혀 없었음 — 최상단 배너 추가(가장 중요한 gap, question.md 0-A가 경고하던 바로 그 함정) - tween-plan.md/operator-sugar-plan.md/effect-plan.md: self-lazy-핸들 계약(0-Y)에 의존하는 실행 예시/메커니즘 서술에 포인터가 전혀 없었음 — 3곳 추가 - CLAUDE.md: "지금 할 일" 0번(0-Y/0-Z가 최우선 게이트)과 1번("M0 착수 전 남은 유일한 게이트는 luau-test뿐")이 정면 모순 — 0-Y/0-Z 발견 이전에 쓰인 문장이 안 갱신된 stale, 정정 - slot-plan.md 3곳/additional-primitives-plan.md/ dispatch-redispatch-diff-plan.md: reconcile/Slot:List/재귀 재emit 가드 설명이 여전히 "파괴"를 현재형으로 서술 — 2026-08-13 여섯 번째 세션의 언마운트 전환 반영해 정정(지난 세 라운드가 놓친 인스턴스들, 전수 grep으로 확인) Co-Authored-By: Claude Sonnet 5 --- .claude/base/architecture.md | 7 +++++++ .claude/base/effect-plan.md | 5 ++++- .claude/base/slot-plan.md | 20 +++++++++++-------- .claude/base/tween-plan.md | 3 +++ .../research/additional-primitives-plan.md | 4 +++- .../research/dispatch-redispatch-diff-plan.md | 4 +++- .claude/research/operator-sugar-plan.md | 6 ++++++ CLAUDE.md | 6 +++++- 8 files changed, 43 insertions(+), 12 deletions(-) diff --git a/.claude/base/architecture.md b/.claude/base/architecture.md index d023c37..6d3bbbb 100644 --- a/.claude/base/architecture.md +++ b/.claude/base/architecture.md @@ -1,5 +1,12 @@ # quad-v2 전체 아키텍처 (현재 상태 요약) +> **⚠️ [2026-08-13 4차 감사에서 발견] 이 문서는 세션 시작 시 가장 먼저 +> 읽는 진입점인데, 아래 소스 트리의 `chains`/`retractFrom`(Dispatch/init.luau +> 항목)과 `retractFrom`에 재귀 위임(Attribute.luau 항목) 서술은 현행 +> (교체 예정) 재-dispatch 모델을 전제로 쓰여 있음 — `question.md` **0-Z** +> 해소 전엔 그대로 구현하면 옛 모델로 짜게 됨. 상세는 +> `base/bind-system-plan.md` 최상단 배너, `research/dispatch-redispatch-diff-plan.md`. + **상태**: base — 횡단 결정의 최종 상태 요약. 특정 기능 plan이 아니라 프로젝트 전체에 걸친 결정이라 완료 개념 없음. 근거가 된 원본 브레인스토밍은 `.claude/initreq/raw-userinput.md`(안 옮기고 그대로 둠 — 이 문서들로 나누기 전의 diff --git a/.claude/base/effect-plan.md b/.claude/base/effect-plan.md index 753b59f..c5059f2 100644 --- a/.claude/base/effect-plan.md +++ b/.claude/base/effect-plan.md @@ -32,7 +32,10 @@ Effect(fn, state?) -> EffectHandle **`state` 지정 시(2026-08-07 여섯 번째 세션 확정)**: Effect는 내부적으로 `state:Observer(...)`를 감싸는 걸로 구현 — `fn`은 포지셔널 인자로 `state`를 받고(`fn(state)`, `:Compute`의 `fn(self)` 포지셔널-self 패턴 재사용, -모듈화 목적 — 클로저 캡처 없이 `fn`을 독립적으로 정의/재사용 가능), +모듈화 목적 — 클로저 캡처 없이 `fn`을 독립적으로 정의/재사용 가능 +— **[2026-08-13 4차 감사] 이 `fn(state)`가 lazy `State` 핸들을 받는다는 +전제 자체가 `question.md` 0-Y로 열려 있음, `Effect`는 0-Y가 명시적으로 +지목하는 영향 대상 중 하나**), Observer가 이제 등록 즉시 1회 실행되므로(아래 Observer 절 참고) 그 첫 실행이 "설치"를 겸함. 이후 `state`가 무효화될 때마다 **직전 `fn` 호출이 리턴한 cleanup을 먼저 호출한 뒤 `fn`을 재호출**, 그리고 Effect가 바인드된 diff --git a/.claude/base/slot-plan.md b/.claude/base/slot-plan.md index 4a96b45..e571f65 100644 --- a/.claude/base/slot-plan.md +++ b/.claude/base/slot-plan.md @@ -468,11 +468,12 @@ nested `Slot`을 파괴 후 재사용하는 경로가 정확히 이 케이스). 상호 강참조" 패턴과 다른 모양 — GC 문제 없음. - **같은 바인딩이면 완전히 무시하는 게 이 자리에선 효율 문제가 아니라 - 정합성 문제** — Slot은 아래 "확정" 절대로 "폐기, 옮기지 않음"(portal - 없음)이 이미 정책으로 확정돼 있어서, 이 no-op 가드가 없으면 **재귀 재 - emit이 있을 때마다 마운트된 서브트리 전체가 파괴됐다 다시 만들어짐** - (자식들이 들고 있던 스크롤 위치/포커스/애니메이션 상태 전부 유실) — - Tag의 `Contains` 힌트가 막으려던 "깜빡임" 문제보다 훨씬 파괴적인 버전. + 정합성 문제** — 이 no-op 가드가 없으면 **[정정, 2026-08-13 4차 감사] + 재귀 재emit이 있을 때마다 마운트된 서브트리 전체가 언마운트됐다 다시 + 마운트됨**(작성 당시엔 아직 destroy 모델이었음, 위 "확정" 절 언마운트 + 전환 참고 — 물리 트리 이탈+재마운트라 여전히 자식들이 들고 있던 스크롤 + 위치/포커스/애니메이션 상태는 유실됨, 파괴가 아니라는 것만 정정) — + Tag의 `Contains` 힌트가 막으려던 "깜빡임" 문제보다 훨씬 파급이 큰 버전. `store.key:Set(sameSlotAgain)`처럼 사용자가 실수로 같은 객체를 다시 emit하거나, 상위 `:Compute`가 재계산됐는데 결과가 우연히 같은 Slot 레퍼런스인 경우 등이 실제로 이 경로를 탈 수 있음. `retract`가 `old ~= v` @@ -704,7 +705,8 @@ splice 의미)와 `Swap`(O(1), 순수 페어 교환)을 공개 CRUD에 추가 Fusion `ForPairs`/`ForKeys`/`ForValues`, Vide `indexes()`/`values()`, React `key` prop에 대응하는 프리미티브 — 데이터 배열을 정체성(key) 기준으로 -diff해서 변경분만 생성/갱신/파괴한다. **독립 타입이 아니라 `Slot`의 +diff해서 변경분만 생성/갱신/언마운트한다(**[정정, 2026-08-13 4차 감사]** +원래 "파괴"였으나 언마운트 전환 반영). **독립 타입이 아니라 `Slot`의 콜론 메소드**로 확정(아래 "왜 자유 함수/새 타입이 아닌가" 참고) — 자기 자신을 변경하고 자신을 반환, `Ref():Callback(fn)`류의 기존 체이닝 패턴과 동일: @@ -1146,8 +1148,10 @@ raw `i`를 그대로 위치 인자로 썼는데, 앞쪽 item이 filter로 마운 방식, `data`가 참조를 유지한 채 뮤테이션+`Emit()`되는 경로도 지원해야 하는 이상 최소 한 번은 훑어야 하는 게 불가피함. - **`updateFn`을 매번 부르는 게 비싼 게 아닌 이유** — 흔한 경로(`prev` - 그대로 반환)는 함수 호출 하나뿐, 실제 Instance 생성/파괴가 있는 건 - key가 새로 나타나거나/사라지거나/filter로 구조가 바뀌는 경우뿐. + 그대로 반환)는 함수 호출 하나뿐, 실제 Instance 생성/**[정정, + 2026-08-13 4차 감사] 언마운트**(파괴 아님 — 위 "구현" 절 `rawUnmount` + 참고)가 있는 건 key가 새로 나타나거나/사라지거나/filter로 구조가 + 바뀌는 경우뿐. 200개 중 값만 갱신되는 사이클엔 200번의 값싼 함수 호출이 있을 뿐, 200번의 재구성이 있는 게 아님. - **`mounted`/`userdata`를 정리하는 루프가 `mounted`가 아니라 이전 diff --git a/.claude/base/tween-plan.md b/.claude/base/tween-plan.md index 8189ed1..43af00d 100644 --- a/.claude/base/tween-plan.md +++ b/.claude/base/tween-plan.md @@ -219,6 +219,9 @@ end -- Animate(info)는 factory(self) -> State를 반환 — :Apply 전용 -- (2026-08-12 세션 후속 논의로 :Compute 직결에서 정정됨, 아래 -- "왜 `:Apply`로 정정됐는가" 절 참고) +-- [2026-08-13 4차 감사] 아래 selfH:Get()은 :Compute의 self-lazy-핸들 +-- 계약에 의존 — 그 계약 자체가 question.md 0-Y로 열려 있음. 0-Y가 +-- raw 값 쪽으로 결론나면 이 구현도 selfH:Get() -> selfH로 바뀜. local function Animate(info) return function(self) return self:Compute(function(selfH) diff --git a/.claude/research/additional-primitives-plan.md b/.claude/research/additional-primitives-plan.md index 939586c..c2af450 100644 --- a/.claude/research/additional-primitives-plan.md +++ b/.claude/research/additional-primitives-plan.md @@ -47,7 +47,9 @@ Vide/v1/artworks 소스 근거 조사, Context 구현 난이도 판정) + 그 React `key` prop, Vue `v-for :key`, Solid ``, Fusion `ForPairs`/ `ForKeys`/`ForValues`, Vide `indexes()`/`values()`에 대응하는 프리미티브 — -데이터 배열을 정체성(key) 기준으로 diff해서 변경분만 생성/갱신/파괴한다. +데이터 배열을 정체성(key) 기준으로 diff해서 변경분만 생성/갱신/언마운트한다 +(**[정정, 2026-08-13 4차 감사]** 원래 "파괴"였으나 2026-08-13 여섯 번째 +세션의 언마운트 전환 반영 — 최신 소스는 `base/slot-plan.md`). **최종 확정 형태는 자유 함수도 새 타입도 아니라 `Slot`의 콜론 메소드** (`Slot():List(data, updateFn, keyFn?) -> Slot`) — 상세 시그니처/구현 의사코드/왜 자유 함수·새 타입이 아닌지/`Move` 기반 리오더/`userdata` 기반 diff --git a/.claude/research/dispatch-redispatch-diff-plan.md b/.claude/research/dispatch-redispatch-diff-plan.md index 1a8f283..3cd92a7 100644 --- a/.claude/research/dispatch-redispatch-diff-plan.md +++ b/.claude/research/dispatch-redispatch-diff-plan.md @@ -47,7 +47,9 @@ Attribute는 클로저가 no-op이라 무해하지만, `TagHandler`였다면 자기 타입일 때만 동작 — 위 경로들에선 소리 없이 전량 정리로 퇴화함. **정확성은 유지되지만(그래서 지금까지 안 드러남) 깜빡임/재생성 방지라는 존재 이유가 무너짐.** 특히 `Slot`은 "가드가 없으면 마운트된 서브트리 -전체가 파괴됐다 재생성"이라 파급이 큼. +전체가 **[정정, 2026-08-13 4차 감사]** 언마운트됐다 재마운트"(작성 당시 +아직 destroy 모델 — 파괴가 아니라는 것만 정정, 파급이 크다는 결론은 그대로)라 +파급이 큼. ## 2. 채택 모델 — 재디스패치는 "철거 후 재구축"이 아니라 "하강 diff" diff --git a/.claude/research/operator-sugar-plan.md b/.claude/research/operator-sugar-plan.md index a4f8916..fd1bcfe 100644 --- a/.claude/research/operator-sugar-plan.md +++ b/.claude/research/operator-sugar-plan.md @@ -7,6 +7,12 @@ 함수를 지우고 고쳐도 안전 — 우선순위가 낮은 이유. **지금 이 문서를 쓰는 목적은 구현 착수가 아니라 설계/네이밍 논의를 미리 남겨두는 것뿐.** +> **⚠️ [2026-08-13 4차 감사에서 발견]** 아래 모든 `Operator.*` 예시가 +> `h:Get()`(self-lazy-핸들 계약)에 의존 — 그 계약 자체가 `question.md` +> **0-Y**로 열려 있음(`Effect`/`Observer`/`Animate`/`Operator`가 전부 +> 같은 계약 공유 대상으로 명시됨). 우선순위가 어차피 최하위라 지금 +> 당장 막는 건 없지만, 나중에 착수 시점엔 0-Y 결론부터 확인할 것. + ## 동기 `:Compute`/`:Apply`는 `fn(self, ...)` 람다를 요구하는데, `not self:Get()`처럼 diff --git a/CLAUDE.md b/CLAUDE.md index 279f888..64a5573 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -147,7 +147,11 @@ modifier/Ref의 컴포넌트 경계 통과 방식) 논의도 2026-08-04 세션 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개 전원 완료** — M0 착수 전 남은 유일한 게이트는: + 해소되어 **11개 전원 완료** — 이 항목(우선순위1) 기준 남은 유일한 + 게이트는 아래였음(**[2026-08-13 4차 감사 정정] 위 0번 항목의 0-Y/0-Z가 + 이후 같은 날 발견돼 실제로는 게이트가 하나 더 있음 — "유일한"은 그 + 발견 전 서술이 안 갱신된 stale, 0-Y/0-Z가 최우선으로 먼저 해소돼야 + 함**): - **`.claude/luau-test/`(2026-08-09 신설, 2026-08-13 기준 20개 — 2026-08-12 열일곱 번째 세션에 16/17 추가, 2026-08-13에 세션 8~21이 만든 커버리지 갭을 메우는 18/19/20 추가) 스파이크 결과** —