diff --git a/.claude/README.md b/.claude/README.md index 4565277..1e058f9 100644 --- a/.claude/README.md +++ b/.claude/README.md @@ -43,6 +43,7 @@ | `attribute-plan.md` | **[2026-08-07 여덟 번째 세션 신설]** 단일 키 `[AttributeKey "Name"]`(구 `Attribute`) — `SetAttribute(name, nil)`이 네이티브 지우기라 `None` 센티널과 가장 깔끔하게 맞아떨어짐, `retract` 불필요. **[2026-08-11 아홉 번째 세션]** 여러 Store를 한 번에 attribute로 묶는 그룹 `Attribute(...)` 프리미티브 신설(`Tag`와 동형 array-part 값 객체, `Merged`로 헤테로지니어스 Store 합성), 이름 충돌 방지로 단일 키를 `AttributeKey`로 리네임(잠정). **[같은 세션 후속]** `AttributeKey(name)`이 이름별 weak 캐시로 동등성 보장하도록 확정되며, 그룹 Handler는 자기 완결형 재구현 대신 메모이즈된 키로 기존 단일 키 경로에 재귀 위임하는 걸로 개정(중복 구현 제거) | | `onchange-plan.md` | **[2026-08-10 세션 신설]** `OnChange(name)` — `GetPropertyChangedSignal` 바인딩 전용 DI 키, `Attribute`와 달리 제네릭 타입 파라미터 없음(콜백 타입은 인라인 명시, 이벤트 바인딩과 같은 급 트레이드오프). 전부 quad-roblox(`Handlers/OnChange.luau`), `State`은 기존 이벤트 store-bind 메커니즘 재사용. **[2026-08-11 아홉 번째 세션 후속]** `AttributeKey`와 동일한 이름별 weak 캐시로 `OnChange(a) == OnChange(a)` 동등성 보장 | | `relate-plan.md` | **[2026-08-08 신설]** `Relate` — `inst`를 weak 키로 하는 범용 릴레이션 프리미티브(`SetWeak`/`GetWeak`/`SetStrong`/`GetStrong`, 비싱글톤 생성자). 구 `base.perInstanceState(inst)` placeholder를 대체·정식 승격, `lifecycle-pattern.md`의 `bindLifetime`/`canExecute`가 그 위에 얹힘 | +| `tween-plan.md` | **[2026-08-12 세션, `research/`에서 승격]** 값-레벨 `Tween` 래퍼(PropertyHandler가 소비, 구 특수 bind key 모델은 `archive/tween-special-bind-key-reversed.md`). 3-상태 릴레이션 슬롯(`{Tween,Value}\|true\|nil`), `T'=T\|Tween` 타입 치환. 옵션 값 모양은 `Info: TweenInfo?` 우선+편의 필드 폴백, override는 `Tween.Cancel`(기본)/`Tween.Finish` 2값. `Animate(info)`는 `Tween` opts를 `T\|State`로 받아 `:Apply`로 꽂는 sugar. 자연완료 시 per-instance 북키핑은 정리 안 해도 됨으로 확정(목표값 도달 상태라 부작용 없음, Completed 이벤트 구독 장치는 오버엔지니어링으로 판단). `initValue`는 사용자가 직접 처리(에이전트 범위 제외) | ## `reference/` — 온디맨드 참고 자료 (2026-08-07 신설) @@ -56,7 +57,6 @@ | 문서 | 내용 | 우선순위 | |---|---|---| -| `tween-plan.md` | **[2026-08-12 세션에서 구조+옵션 값 모양+override 정책+`Animate` 콤비네이터까지 전부 확정]** 값-레벨 `Tween` 래퍼(PropertyHandler가 소비), 구 특수 bind key 모델은 `archive/tween-special-bind-key-reversed.md`로 이전. 3-상태 릴레이션 슬롯(`{Tween,Value}\|true\|nil`), `T'=T\|Tween` 타입 치환. 옵션 값 모양은 `Info: TweenInfo?` 우선+편의 필드(`Time`/`Style`/...) 폴백, override는 `Tween.Cancel`(기본)/`Tween.Finish` 2값. `Animate(info)`는 `Tween` opts(`Value` 제외)를 `T\|State`로 받아 `factory(self)->State`를 반환하는 sugar로 확정(구 `useTween` 스케치 대체), 호출 경로는 같은 세션 후속 논의로 `:Compute`→`:Apply` 정정(재사용 가능한 콤비네이터 정합성 문제, `operator-sugar-plan.md`와 같은 근거). `initValue`는 사용자가 직접 처리(에이전트 범위 제외), 남은 건 자연완료 북키핑 하나뿐 | 하 — 사실상 다 닫힘 | | `existing-instance-bind-plan.md` | 이미 생성된 인스턴스 재바인드 — 착수 안 하되 "미지원" 확정도 안 함, 열린 가능성 유지 | 하 — v2 초기 스코프 제외 | | `debug-tooling-plan.md` | 실물 Instance→코드 위치 역추적 Studio 플러그인(`quad-debug`) — 채널 실현 가능성(BindableEvent/Function 크로스 컨텍스트)까지 실측 검증 완료, 세부 API 이름·구현만 남음 | 하 — 사용자가 "quad 개발 완료 전엔 착수 못 함"으로 직접 후순위 지정, base 설계 시 훅 확장 지점만 고려 | | `documentation-plan.md` | 문서 사이트 구조(초심자/api/심화/`quadnomicon` 4축, 백엔드별 트랙 분리) + UI 네이밍 컨벤션·Store 부작용 패턴·권장 이벤트 핸들링 3개 세부 문서 뼈대 | 하 — 착수 시점 미정, 구조/스코프만 합의된 상태 | @@ -83,7 +83,7 @@ | `observer-cleanup-contract-rejected.md` | **[기각됨, 2026-08-09 코퍼스 정리 신설]** `Observer` 자체에 React `useEffect`식 cleanup 반환 계약을 추가하는 안 — 클로저로 이미 충분해 기각, `Effect`가 opt-in 상위 계층으로 이 패턴을 제공 | | `keyed-collection-state-method-rejected.md` | **[기각됨, 2026-08-09 코퍼스 정리 신설]** 키 기반 동적 컬렉션 재조정 프리미티브를 `state:Keyed(...)` State 메소드로 두려던 초안 — Source 미사용 컴포넌트가 접근 못 한다는 반례로 철회, 현재는 자유 함수로 확정 | | `debug-channel-replicatedstorage-rejected.md` | **[기각됨, 2026-08-09 코퍼스 정리 신설]** quad-debug 채널을 `ReplicatedStorage`에 자동 생성하던 초안 — 게임 트리 오염 부작용으로 기각, quad 모듈 자신의 트리+`CollectionService` 태그로 대체 | -| `tween-special-bind-key-reversed.md` | **[역전됨, 2026-08-10 신설]** 구 Tween 모델(`[Tween(key,tweenData...)] = storeValue` 특수 bind key, 우선순위 최상위 Dispatch 핸들러) — 값-레벨 `Tween` 래퍼 모델로 완전히 대체됨(`research/tween-plan.md`) | +| `tween-special-bind-key-reversed.md` | **[역전됨, 2026-08-10 신설]** 구 Tween 모델(`[Tween(key,tweenData...)] = storeValue` 특수 bind key, 우선순위 최상위 Dispatch 핸들러) — 값-레벨 `Tween` 래퍼 모델로 완전히 대체됨(`base/tween-plan.md`) | | `onchange-per-property-codegen-rejected.md` | **[기각됨, 2026-08-10 신설]** `OnChange.PropertyName` 프로퍼티별 정적 코드 생성 — Attribute의 정적 지름길과 달리 (클래스 수 × 프로퍼티 수) 규모로 폭발해 기각, `OnChange(name)` 단일 팩토리로 대체 | ## 참고 diff --git a/.claude/archive/tween-special-bind-key-reversed.md b/.claude/archive/tween-special-bind-key-reversed.md index 82775ab..ea26206 100644 --- a/.claude/archive/tween-special-bind-key-reversed.md +++ b/.claude/archive/tween-special-bind-key-reversed.md @@ -2,7 +2,7 @@ **역전 일시**: 2026-08-10. **원 확정 일시**: 2026-08-04(로드맵 인수인계 라운드 전후, "확정된 방향: 트윈을 Store/반응 그래프 밖에 둔다" 최초 작성). -**현재 유효한 설계**: `research/tween-plan.md`(전면 재작성됨)가 최종 소스. +**현재 유효한 설계**: `base/tween-plan.md`(전면 재작성됨)가 최종 소스. 이 파일은 능동적으로 참고할 필요 없음(구현에 안 씀) — 왜 "Tween이 곧 범용 store-bind 핸들러"였던 모델에서 "Tween은 PropertyHandler가 소비하는 값-레벨 래퍼"로 넘어갔는지가 `quadnomicon` 소재로 가치 있어서 사유·원문을 diff --git a/.claude/base/architecture.md b/.claude/base/architecture.md index 84313a2..76a8c44 100644 --- a/.claude/base/architecture.md +++ b/.claude/base/architecture.md @@ -143,7 +143,7 @@ quad/ │ ├── Modifier.luau # flatten-before-dispatch, immutable 체이닝, 제네릭 `__index` 필드 setter 합성 + `:Apply`/`:Peek`/`Overridden`(`base/modifier-plan.md`) │ ├── Tag.luau # 값 타입+immutable clone 체이닝(`Tag(...)`/`:Added`/`:Removed`/`:Contains`/`:Apply`/`Merged`), CollectionService 글루는 quad-roblox Handlers/Tag.luau(`base/tag-plan.md`, 2026-08-08 세 번째 세션) │ ├── Attribute.luau # 그룹 값 타입+API(`Attribute(store1, store2, ...)`/`Merged`, `Tag`와 동형) — `SetAttribute` 글루는 quad-roblox Handlers/Attribute.luau(`base/attribute-plan.md`, 2026-08-11 아홉 번째 세션). 단일 키(`AttributeKey<>`)는 값 타입 레이어 없이 quad-roblox 단독 소속(아래) -│ ├── Tween.luau # 값 타입만(`Tween(opts)` 팩토리, `isTween`/`TweenTag`) — 엔진 무관, 독립 Dispatch 핸들러 아님. 실제 애니메이션 처리는 quad-roblox Handlers/Property.luau 내부 분기(`research/tween-plan.md`, 2026-08-10 세션 재설계) +│ ├── Tween.luau # 값 타입만(`Tween(opts)` 팩토리, `isTween`/`TweenTag`) — 엔진 무관, 독립 Dispatch 핸들러 아님. 실제 애니메이션 처리는 quad-roblox Handlers/Property.luau 내부 분기(`base/tween-plan.md`, 2026-08-10 세션 재설계) │ ├── Effect.luau # `Effect(fn, state?)` — state 없으면 설치1회+leaf사망시 정리, 있으면 State.Observer를 조합해 재실행(`base/effect-plan.md`) │ ├── Dispatch/ │ │ ├── init.luau # process/retract 엔진, isHandlable 우선순위 스캔, `chains`(inst,k별 핸들러 체인)+`retractUnder`(`bind-system-plan.md` "Dispatch 체인" 절, 2026-08-08 세 번째 세션) @@ -170,7 +170,7 @@ quad/ │ ├── Tag.luau # CollectionService 글루만(process/retract) — 값 타입/API는 quad-base Tag.luau(`base/tag-plan.md`) │ ├── Slot.luau # base Slot 재조정 로직의 실제 적용/해제(Instance Parent 조작) │ └── InstanceChild.luau # k:number, v:Instance — 중첩 인스턴스 자식(예: Frame { Frame {} }) - ├── Animate.luau # `Animate(info)` 편의 콤비네이터 — `factory(self)->State`, `:Apply`로 붙임(내부는 `:Compute`/`Tween{...}` 조합), base 프리미티브 아님(`research/tween-plan.md`) + ├── Animate.luau # `Animate(info)` 편의 콤비네이터 — `factory(self)->State`, `:Apply`로 붙임(내부는 `:Compute`/`Tween{...}` 조합), base 프리미티브 아님(`base/tween-plan.md`) ├── DI/ │ └── init.luau # 제네릭 생성자 + ~25개 정적 필드(UIInstances) └── init.luau diff --git a/.claude/base/attribute-plan.md b/.claude/base/attribute-plan.md index 9a621c9..7427a85 100644 --- a/.claude/base/attribute-plan.md +++ b/.claude/base/attribute-plan.md @@ -114,7 +114,7 @@ end 같은 `AttributeKeyHandler`가 이 키를 계속 담당(핸들러 *타입*이 안 바뀜). `retract`가 의미 있는 유일한 패턴("매치되는 핸들러 타입 자체가 바뀜", `Tag(...)`↔`nil`이 실사례 — 2026-08-10 세션부터 Tween은 더 이상 이 - 패턴의 예시가 아님, `research/tween-plan.md`)에 해당 안 함 — + 패턴의 예시가 아님, `base/tween-plan.md`)에 해당 안 함 — `bind-system-plan.md` "확정된 디스패치 모델" 절이 한때 Attribute도 retract 필요 예시로 들었던 걸 여기서 바로잡음. - store-bind 가능(일반 프로퍼티와 동일하게 취급, `Store`/`State` diff --git a/.claude/base/bind-system-plan.md b/.claude/base/bind-system-plan.md index 3e37427..7d75ec9 100644 --- a/.claude/base/bind-system-plan.md +++ b/.claude/base/bind-system-plan.md @@ -132,7 +132,7 @@ src/schema/union.luau:48-68`) — 에러 메시지는 즉시 문자열로 만들 PropertyHandler가 소비하는 값-레벨 래퍼(`Tween`)로 재설계되며 매치되는 핸들러가 항상 PropertyHandler 하나뿐이 되어 이 케이스 자체가 사라짐 — 트윈 취소/전환은 이제 PropertyHandler 내부의 - 3-상태 릴레이션 슬롯으로 처리(`research/tween-plan.md`, `archive/ + 3-상태 릴레이션 슬롯으로 처리(`base/tween-plan.md`, `archive/ tween-special-bind-key-reversed.md`). - store bind가 새 값으로 넘어갈 때 이전 핸들러의 `retract`를 호출해주면 됨 — **정확한 전파 메커니즘은 아래 "Dispatch 체인" 절 참고**(재귀 @@ -324,7 +324,7 @@ NoneHandler.process(inst, k, v) = process(inst, k, nil) -- 재귀 재호출 등록됨 — base 기본 핸들러와 backend 핸들러가 별도 경로로 안 갈리고 전부 하나의 우선순위 스캔을 공유. **[정정, 2026-08-10 세션]** Tween은 더 이상 별도로 등록되는 핸들러가 아님 — Property 핸들러 내부에서 - 소비되는 값-레벨 래퍼로 재설계됨(`research/tween-plan.md`). + 소비되는 값-레벨 래퍼로 재설계됨(`base/tween-plan.md`). - **모듈 재생성(`New()`)과의 관계 — 새 설계 불필요, 이미 있는 선례로 자연히 풀림.** v1처럼 `require`를 감싸 `Init(QuadId?)`로 격리 인스턴스를 만드는 방식은 안 씀(위 "확정된 것" 절 — id 기반 조회 자체가 Ref로 대체되며 @@ -2008,7 +2008,7 @@ Modifier처럼 플래튼하지 않는가"는 설계 근거를 알고 싶은 사 **[2026-08-12 세션 감사에서 확인] `:Compute` 콜백 인자에 `:Get()`을 빠뜨리는 실수가 반복되기 쉬움 — 실제로 `.claude/` 문서 예시 코드 4곳(`tag-plan.md`, -`slot-plan.md` 2곳, `research/tween-plan.md`)에서 발견·수정됨.** `fn(self, +`slot-plan.md` 2곳, `base/tween-plan.md`)에서 발견·수정됨.** `fn(self, ...)`의 모든 인자가 raw 값이 아니라 lazy State 핸들이라는 원칙(바로 위 절)을 사람도 에이전트도 코드 작성 중에 잊기 쉬운 지점 — `:Compute`/`:With` 콜백 안에서 인자를 비교(`==`)/연산(`+`)/테이블에 담기 전에 항상 `:Get()`부터 @@ -2272,7 +2272,7 @@ T|State|nil`가 돌려주는 raw union을 사용자 코드가 분기하려면 `Source`/`Slot`/`None`까지, Handler 구현(`isHandlable`에서 "이 값이 Store인가/Tag인가" 판별, 또는 PropertyHandler의 `process` 내부에서 "이 값이 Tween인가" 판별 — 2026-08-10 세션부터 `isTween`은 `isHandlable`이 -아니라 값-레벨 분기에서만 쓰임, `research/tween-plan.md` 참고)과 사용자 +아니라 값-레벨 분기에서만 쓰임, `base/tween-plan.md` 참고)과 사용자 코드 양쪽에서 반복적으로 필요해질 수단이라 `isState` 하나만 만들고 끝내지 않고 전체를 일관된 메커니즘으로 통합(component-composition-plan.md 4번 절이 이미 "`isSource`류 판별자로 diff --git a/.claude/base/modifier-plan.md b/.claude/base/modifier-plan.md index 718b321..cd667ff 100644 --- a/.claude/base/modifier-plan.md +++ b/.claude/base/modifier-plan.md @@ -322,7 +322,7 @@ predicate(`Brand` 절)를 State/Source 쪽에도 적용해 **런타임에 직접 왜 Modifier만 막고 나머지는 다 허용하는지의 핵심 근거. **[정정, 2026-08-10 세션] `Tween`은 이 그룹에서 빠짐** — Tween이 독립 Dispatch 핸들러(`process`/`retract`를 가진 dispatch 참가자)에서 PropertyHandler가 - 소비하는 값-레벨 래퍼로 재설계되며(`research/tween-plan.md`), `Tween`는 + 소비하는 값-레벨 래퍼로 재설계되며(`base/tween-plan.md`), `Tween`는 이제 `process`/`retract`가 없는 순수 raw 데이터 값 — `None`과 같은 분류. State/Source에 `Tween`가 담기는 것 자체는 여전히 문제없이 허용되지만(위 타입 대수 절 참고), 그 이유는 "재귀 dispatch 참가자라서"가 @@ -550,7 +550,7 @@ Source도 같이 잡아줌 — **[2026-08-07 여덟 번째 세션 정정] `isSou ### 10. `Tween`와의 타입 합성 — `T' = T | Tween` 치환만으로 해결 (2026-08-10 세션) -`research/tween-plan.md`가 값-레벨 `Tween` 래퍼로 재설계되며, 프로퍼티류 +`base/tween-plan.md`가 값-레벨 `Tween` 래퍼로 재설계되며, 프로퍼티류 Modifier 필드 setter가 트윈 값도 받을 수 있어야 하는지가 자연히 따라오는 질문이었음 — **답은 "이미 있는 `T | State` 필드 타입 모양에 새 케이스를 추가할 필요가 없다"** — 위 4번 절이 확정한 필드 타입 모양(리터럴 `T` 또는 diff --git a/.claude/research/tween-plan.md b/.claude/base/tween-plan.md similarity index 92% rename from .claude/research/tween-plan.md rename to .claude/base/tween-plan.md index 642f466..97aade1 100644 --- a/.claude/research/tween-plan.md +++ b/.claude/base/tween-plan.md @@ -1,4 +1,4 @@ -# Tween / 애니메이션 플러깅 (구조+옵션 값 모양+`Animate` 확정 — 자연완료 북키핑만 남음) +# Tween / 애니메이션 플러깅 (전부 확정 — `base/`로 승격) **상태**: research — **2026-08-10 세션에서 구조 전체가 재설계됨.** 기존 "`v`가 Store인 아무 `k`나 잡는 우선순위 최상위 Dispatch 핸들러" 모델은 @@ -14,8 +14,10 @@ tweenData...)] = storeValue`)은 `archive/tween-special-bind-key-reversed.md`로 시그니처까지 전부 확정됨**(아래 "확정: `Tween{...}` 최종 모양"/"`Animate` 콤비네이터" 절), **같은 날 후속 논의에서 `Animate`의 호출 경로가 `:Compute` 직결 → `:Apply`로 정정됨**(아래 "왜 `:Apply`로 정정됐는가" 절, -`research/operator-sugar-plan.md`와 같은 근거) — 남은 건 자연 완료 -(Completed) 시 북키핑 정리 여부뿐. +`research/operator-sugar-plan.md`와 같은 근거). **같은 날 다섯 번째 후속 +논의에서 마지막 남은 질문(자연완료 시 북키핑 정리 여부)도 "정리 안 해도 +됨"으로 확정**(아래 "열린 질문" 절) — 이걸로 열린 설계 질문이 없어져 +`research/`에서 `base/`로 승격. `initValue`는 사용자가 필요해지면 직접 처리하기로 확정(에이전트 작업 범위에서 제외, 아래 해당 절 참고). 원본: `.claude/initreq/raw-userinput.md` "트윈은 어떻게 할 것이냐" / "스토어 값은 @@ -444,14 +446,32 @@ Tween(opts: { 받으므로(다른 모든 핸들러와 동일) 이 문제 자체가 성립하지 않음. 절 자체는 과거 기록으로만 남김, 실행할 내용 없음. -## 열린 질문 (`.claude/question.md`에도 취합) +## 열린 질문 — 전부 해소됨 **2026-08-12 세션에서 옵션 값 모양/override 정책 이름/릴레이션 슬롯 저장 -모양/`Animate` 콤비네이터 시그니처까지 전부 확정됨** — 아래 하나만 남음: +모양/`Animate` 콤비네이터 시그니처까지 전부 확정됨**, 마지막 남았던 아래 +질문도 같은 날 다섯 번째 후속 논의로 확정됨 — 이제 열린 설계 질문 없음. -- 자연 완료(Completed) 시 per-instance 북키핑 정리 여부(3-상태 슬롯을 - `true`로 되돌리는 시점) — `research/pre-implementation-audit.md` 2-10번 - 참고, M11 착수 시 확정. +### 자연 완료(Completed) 시 per-instance 북키핑 — 정리 안 해도 됨 (확정) + +`research/pre-implementation-audit.md` 2-10번이 제기했던 질문. **결론: +Completed 이벤트를 구독해 3-상태 릴레이션 슬롯을 `true`로 되돌리는 등의 +정리 로직은 만들지 않는다.** + +**근거**: 이 정리를 하고 싶어지는 동기는 "인스턴스 초기 생성 시 프로퍼티가 +유저가 원치 않는 값(예: `Position`이 기본 `UDim2.new(0,0,0,0)`)일 수 있어, +거기서 트윈이 시작되면 툭 튀어 보인다"는 문제인데 — 이건 이미 **"첫 세팅" +분기**(위 "3-상태 저장" 절의 `prev == nil` 케이스, 애니메이션 없이 즉시 +세팅)가 처리하는 문제지, 자연완료와는 무관하다. 자연완료 상태는 반대로 +**유저가 원한 목표값에 정확히 도달한 상태**이므로, 그 상태를 나타내는 +북키핑(`{Tween, Value}`)을 안 지우고 남겨둬도 다음에 이 `(inst,k)`가 다시 +process될 때 위 "3-상태 저장"의 `prev`가 `{Tween, Value}` 테이블 분기를 +타는 것뿐 — override 정책(`Cancel`/`Finish`)이 정확히 이 케이스를 위해 +이미 정의돼 있어 별다른 부작용이 없다. 게다가 `Value`는 항상 lerp 가능한 +프리미티브(number/UDim/Vector 등, 테이블 aliasing 걱정이 있는 타입이 +아님)라 참조를 계속 들고 있어도 메모리/정합성 문제가 없다. 이 상태에서 +굳이 Completed 이벤트를 구독해 슬롯을 되돌리는 별도 장치를 추가하는 건 +실질적 이득 없이 복잡도만 늘리는 오버엔지니어링으로 판단. `initValue`(진입 애니메이션)는 별도 취급 — 위 해당 절 참고, **에이전트 작업 범위에서 제외, 필요해지면 사용자가 직접 처리**하기로 확정(질문 diff --git a/.claude/question.md b/.claude/question.md index d8828a1..1113a52 100644 --- a/.claude/question.md +++ b/.claude/question.md @@ -293,7 +293,7 @@ context-rejected.md`. 아래는 그중 **아직 실제로 열려있는 것만** | Batch(lexical) 기각, Context(+레이어드 Store) 기각 | `archive/batch-rejected.md`, `archive/context-rejected.md` | | Fusion/Vide 비교 리서치(주의: 일부 서술은 이후 라운드에서 뒤집힘, 문서 내 정정 표시 참고) | `reference/comparison-fusion-vide.md` | | v1 내부 동작 스냅샷 | `reference/quad-v1-architecture.md` | -| 트윈 — 값-레벨 `Tween` 래퍼(2026-08-10)+옵션 값 모양·override 정책·`Animate` 콤비네이터(2026-08-12) 전부 확정, 자연완료 북키핑 하나만 남음 | `research/tween-plan.md` | +| 트윈 — 값-레벨 `Tween` 래퍼(2026-08-10)+옵션 값 모양·override 정책·`Animate` 콤비네이터·자연완료 북키핑(2026-08-12) 전부 확정, `base/`로 승격 | `base/tween-plan.md` | | quad2-try(폐기된 이전 시도) 리서치 — OOP 상속/커스텀 파서/Slot 스텁/`Pipe` COW 전부 죽은 접근으로 확인, 반복 조사 금지 | `base/bind-system-plan.md` | --- diff --git a/.claude/research/documentation-content-map.md b/.claude/research/documentation-content-map.md index 0a7fd65..993f022 100644 --- a/.claude/research/documentation-content-map.md +++ b/.claude/research/documentation-content-map.md @@ -36,7 +36,7 @@ 10. **생명주기** — GC 위임(수동 정리 불필요), Destroy 이후 대상 재사용 금지 (`lifecycle-pattern.md`) 11. **Ref 기초** — 외부 관리 Instance 참조/마이그레이션용, `Ref(default):Callback(fn)`을 children 배열 숫자 슬롯에 직접 놓기 + 배열 위치로 자식 전/후 표현, "프로퍼티보다도 먼저" 필요할 때만 `PreRef`(2026-08-07 세 번째 세션, `phase` 옵션 폐기) (`architecture.md`, `bind-system-plan.md`) 12. **파생값 최소 예시** — `:With(...)` + `:Compute(fn)` 기본형 (`bind-system-plan.md`, `store-semantics.md`) -13. **Tween 기초** — `[Tween(key, ...)] = storeValue`, 취소 시 현재 보간값에서 자연스럽게 이어짐 (`research/tween-plan.md`) +13. **Tween 기초** — `[Tween(key, ...)] = storeValue`, 취소 시 현재 보간값에서 자연스럽게 이어짐 (`base/tween-plan.md`) 14. **UI 숏핸드(quad-roblox 한정)** — `UICorner`/`UIPadding`/`UIPaddingOffset`/`UIScale` 인라인 키 (`base/ui-shorthand-plan.md`) --- @@ -132,7 +132,7 @@ v1 폐기 API/버그/구조 결함 전부 v2 설계를 정당화하는 내부 7. 왜 컴포넌트 경계는 named parameter인가(Compose/Fusion/Vide/v1 수렴) — `component-composition-plan.md` 8. 왜 "다중 루트 반환" 개념을 없앴는가 — `component-composition-plan.md` 9. 왜 Slot은 단일 마운트 소유권을 강제하는가(v1/Fusion/Vide 대비) — `slot-plan.md`, `comparison-fusion-vide.md` -10. 왜 Tween은 반응 그래프 밖에 있는가 — `research/tween-plan.md` +10. 왜 Tween은 반응 그래프 밖에 있는가 — `base/tween-plan.md` 11. 왜 `:Emit()`은 Source 전용이고 파생 State엔 없는가(호출부는 `source:Emit()`, 2026-08-06 후속 세션에서 `Store:Emit(key)`→이 형태로 정리) — `store-semantics.md` 12. 독립 프리미티브 vs 파생 데이터 — 생성자 모양을 결정하는 원칙 — `store-semantics.md` 14. 왜 컴포넌트는 전역 store를 직접 참조하면 안 되는가(이식성) — `purity-and-effects-plan.md` @@ -248,7 +248,7 @@ additional-primitives-plan.md`의 "문서화 백로그" 절이 원자료)**: `setOffsetSource`(Length/Offset)로 2026-08-09 여섯 번째 세션에 확정, `bind-system-plan.md` "Length/Offset" 절 참고. - Tween 오버라이드/삭제후재시작/끝점이동 세부 옵션 키 이름, 트윈 옵션 값 - 모양(TweenInfo vs 편의 필드) (`research/tween-plan.md`) — 아직 열림. + 모양(TweenInfo vs 편의 필드) (`base/tween-plan.md`) — 아직 열림. - **[해소됨]** `Attribute` 제네릭 vs 타입별 정적 생성자 — 2026-08-09 열한 번째 세션에 "둘 다 채택"으로 확정, `base/attribute-plan.md` 참고. - provider/processor 네이밍 — **[해소됨]** `Handler`로 이미 오래전 확정 diff --git a/.claude/research/operator-sugar-plan.md b/.claude/research/operator-sugar-plan.md index fe25672..3df8ce3 100644 --- a/.claude/research/operator-sugar-plan.md +++ b/.claude/research/operator-sugar-plan.md @@ -149,7 +149,7 @@ introspection 로직을 추가하는 거라 라이브러리 복잡도가 늘어 `quad-base` — Store/State 계층 위에서만 동작하는 순수 함수라 엔진 종속 없음(`quad-roblox` 아님). `Animate`가 `Tween`과 함께 어디에 배치됐는지와 -같은 결로 맞추면 됨(`research/tween-plan.md` 참고, 단 `Animate` 자체는 +같은 결로 맞추면 됨(`base/tween-plan.md` 참고, 단 `Animate` 자체는 `Tween`이 quad-roblox 개념(`PropertyHandler`)에 연결되므로 quad-roblox 배치 — Operator 슈가는 그런 엔진 종속이 없다는 점이 다름). diff --git a/.claude/research/pre-implementation-audit.md b/.claude/research/pre-implementation-audit.md index f23b8e0..df369c1 100644 --- a/.claude/research/pre-implementation-audit.md +++ b/.claude/research/pre-implementation-audit.md @@ -44,7 +44,7 @@ M0 착수 전에 확인**. 우선순위 2는 지금 결정해두면 싼데 안 바뀌어, "일반 store-bind와 Tween-store-bind가 같은 핸들러인지"라는 질문 자체가 성립하지 않게 됨 — 범용 State/Source 언랩은 `Dispatch/ StoreBind.luau` 하나뿐이고 Tween은 그 아래에서 나온 `realv`를 -PropertyHandler가 직접 판별. 상세는 `research/tween-plan.md`(전면 +PropertyHandler가 직접 판별. 상세는 `base/tween-plan.md`(전면 재작성), 구 모델은 `archive/tween-special-bind-key-reversed.md`. 아래는 원래 발견 당시 기록. @@ -488,7 +488,14 @@ Modifier를 합친다"는 시나리오가 `Overridden`의 가장 그럴듯한 ### 2-10. Tween 자연완료(Completed) 시 per-instance 북키핑 정리 여부가 명세 안 됨 -**위치**: `research/tween-plan.md` "`retract`(구 cleanup)로 확정된 +**[해소됨, 2026-08-12 세션]** 결론은 "정리 안 해도 됨" — 자연완료 상태는 +유저가 원한 목표값에 도달한 상태라 남은 북키핑이 부작용을 안 일으키고 +(`Value`가 항상 lerp 가능한 프리미티브), Completed 이벤트 구독 같은 별도 +정리 장치는 오버엔지니어링으로 판단해 안 만들기로 확정. 상세 근거는 +`base/tween-plan.md`(승격됨) "자연 완료(Completed) 시 per-instance +북키핑 — 정리 안 해도 됨" 절. 아래는 원래 발견 당시 기록. + +**위치**: `base/tween-plan.md` "`retract`(구 cleanup)로 확정된 오버라이드 시맨틱" 절. **문제**: "새 값이 들어와 갈아치울 때"의 `retract` 동작(4가지 옵션, 기본값 diff --git a/.claude/session/2026-08-12-06-tween-completed-bookkeeping-promoted.md b/.claude/session/2026-08-12-06-tween-completed-bookkeeping-promoted.md new file mode 100644 index 0000000..4fbe302 --- /dev/null +++ b/.claude/session/2026-08-12-06-tween-completed-bookkeeping-promoted.md @@ -0,0 +1,52 @@ +# 2026-08-12 세션 — Tween 자연완료 북키핑 확정, `tween-plan.md`를 `base/`로 승격 + +`research/tween-plan.md`에 마지막으로 남아있던 열린 질문 — 자연 완료 +(Completed) 시 per-instance weak-keyed 저장소(3-상태 릴레이션 슬롯)에 +남은 이전 Tween 참조를 정리해야 하는가(`research/pre-implementation-audit.md` +2-10번) — 를 사용자가 직접 결론냄. + +## 결론: 정리 안 해도 됨 + +사용자 설명 그대로 요약: + +- 이 정리를 하고 싶어지는 동기는 "인스턴스 초기 생성 시 프로퍼티가 유저가 + 원치 않는 값(예: `Position`이 기본 `UDim2.new(0,0,0,0)`)일 수 있어, 거기서 + 트윈이 시작되면 툭 튀어 오른다"는 문제다. +- 그런데 이건 이미 "3-상태 저장"의 **첫 세팅 분기**(`prev == nil` → + 애니메이션 없이 즉시 세팅)가 처리하는 문제고, 자연완료와는 무관하다. +- **자연완료 상태는 반대로 유저가 원한 목표값에 정확히 도달한 상태** — + 그 상태를 나타내는 북키핑을 안 지우고 남겨둬도, 다음 process 때 기존 + override 정책(`Cancel`/`Finish`)이 정확히 이 케이스를 위해 이미 정의돼 + 있어 부작용이 없다. +- `Value`는 항상 lerp 가능한 프리미티브(number/UDim/Vector 등, 테이블 + aliasing 걱정이 있는 타입이 아님)라 참조를 계속 들고 있어도 메모리/정합성 + 문제가 없다. +- 이 상태에서 Completed 이벤트를 구독해 슬롯을 되돌리는 별도 장치를 + 추가하는 건 실질적 이득 없이 복잡도만 늘리는 오버엔지니어링. + +## `base/`로 승격 + +이걸로 `research/tween-plan.md`에 남은 열린 설계 질문이 없어짐 — 사용자 +제안대로 `base/tween-plan.md`로 승격(`git mv`). + +**반영된 파일**: +- `base/tween-plan.md`(구 `research/tween-plan.md`) — 헤더/열린 질문 + 절 갱신, "자연 완료(Completed) 시 per-instance 북키핑 — 정리 안 해도 + 됨 (확정)" 절 신설. +- `research/pre-implementation-audit.md` 2-10번 — `[해소됨]` 표시 추가. +- `.claude/README.md` — `research/` 표에서 `tween-plan.md` 행 제거, + `base/` 표에 추가. `archive/tween-special-bind-key-reversed.md` 행의 + 경로도 `base/tween-plan.md`로 갱신. +- `.claude/question.md` — 트윈 요약 줄 경로를 `base/tween-plan.md`로 갱신. +- `CLAUDE.md` — "지금 할 일"/`research/` 목록에서 `tween-plan.md` 제거. +- 라이브 크로스레퍼런스(`research/tween-plan.md` → `base/tween-plan.md`) + 일괄 갱신: `base/architecture.md`(2곳), `base/attribute-plan.md`, + `base/modifier-plan.md`(2곳), `base/bind-system-plan.md`(4곳), + `research/pre-implementation-audit.md`, `research/operator-sugar-plan.md`, + `research/documentation-content-map.md`, + `archive/tween-special-bind-key-reversed.md`. **`session/` 안의 과거 + 기록은 의도적으로 안 건드림** — 그 시점엔 실제로 `research/`였으므로 + 원문 그대로 정확함. + +이걸로 Tween 관련 설계는 전부 확정 — `ROADMAP.md` M11 착수 시 `base/ +tween-plan.md`가 유일한 소스. diff --git a/CLAUDE.md b/CLAUDE.md index 01147b3..fe07f0a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -42,13 +42,13 @@ modifier/Ref의 컴포넌트 경계 통과 방식) 논의도 2026-08-04 세션 base 문서가 근거로 인용하는 온디맨드 참고 자료(v1 내부 동작 스냅샷, Fusion/Vide 비교 리서치) — 항상 읽을 필요는 없고 인용될 때만 열어볼 것. - `.claude/research/` — 아직 착수 전, 사용자와 상의 필요한 설계 논의. - `tween-plan.md`/`existing-instance-bind-plan.md`/`debug-tooling-plan.md`/ + `existing-instance-bind-plan.md`/`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 세션에서 사실상 다 닫힘, 남은 - 건 자연완료 북키핑 정도로 더 이상 급하지 않음). 최신 목록·우선순위는 + — 전부 후순위(`tween-plan.md`는 2026-08-12 세션에 마지막 열린 항목까지 + 전부 해소돼 `base/`로 승격, 더 이상 여기 없음). 최신 목록·우선순위는 `.claude/README.md`가 소스, 여기서 개수 반복 안 함(과거에 "두 개뿐"이라 적어놨다가 새 문서 추가될 때마다 안 갱신되는 패턴이 반복돼서 아예 안 세기로 함). @@ -489,3 +489,13 @@ bind-system-plan.md`) `local addTax = Sum(a,b)`처럼 만든 값을 `:Compute` `Relate`로 클로저의 operand 목록 추적) 아이디어도 나왔으나 실사용 사례 나오면 재검토로 보류. `base/architecture.md`의 stale `Animate` 2-인자 시그니처 코멘트도 이 김에 수정. + +**2026-08-12 여섯 번째 세션 — Tween 자연완료 북키핑 확정, `tween-plan.md` +`base/`로 승격** (`session/2026-08-12-06-tween-completed-bookkeeping-promoted.md`) +`tween-plan.md`의 마지막 열린 질문(자연완료 시 per-instance 북키핑 정리 +여부)을 사용자가 확정 — 정리 안 해도 됨(자연완료는 유저가 원한 목표값에 +도달한 상태라 남은 참조가 부작용 없음, `Value`가 항상 lerp 가능한 +프리미티브라 메모리 문제도 없음, 별도 Completed 이벤트 정리 장치는 +오버엔지니어링). 이걸로 열린 설계 질문이 없어져 `research/tween-plan.md`를 +`base/tween-plan.md`로 승격, 라이브 크로스레퍼런스 전부 갱신(session/ +과거 기록은 원문 보존을 위해 그대로 둠).