diff --git a/.claude/base/bind-system-plan.md b/.claude/base/bind-system-plan.md index 3629f1a..7706e07 100644 --- a/.claude/base/bind-system-plan.md +++ b/.claude/base/bind-system-plan.md @@ -584,6 +584,17 @@ Frame { 이러면 `observer`는 `Frame`이 살아있는 동안만 유지되고, `Frame`이 retract/Destroy되면 자동으로 정리됨. +- **`fn`은 등록 시점에 즉시 1회 실행된다(2026-08-07 여섯 번째 세션, + 사용자 확정 — 이전까지 미명시였던 항목).** 근거: (1) 이미 채워진 + State를 나중에 구독하면 그 값을 반영하는 연산이 아예 한 번도 안 + 일어나는 문제가 생겨 초기화 순서에 디버깅 부담이 생김. (2) 초회 + 실행을 하지 말아야 할 구체적 근거가 약함. (3) **이 결정 덕에 + Observer 하나로 "초기값 적용"과 "이후 변경 반영"을 같은 코드 경로로 + 통일할 수 있음** — 예: State→프로퍼티 store-bind 핸들러가 그냥 + `state:Observer(function() inst.SomeProp = state:Get() end)`를 걸어 + 두는 것만으로 최초 적용까지 공짜로 됨(별도의 "설치 시 1회 적용" 코드를 + 따로 안 짜도 됨). `state:Observer()`(인자 없는 "항상 관측" 유틸)도 + 이 규칙을 그대로 따름 — 호출 즉시 한 번 관측이 트리거됨. - **값을 안 실어줌 — 반드시 `Get()`을 다시 해야 함.** 기존 "emit은 무효화 신호 하나로 좁혀짐 — 값을 안 실어보내므로 저렴함" 원칙(아래 "Store/State/Source 온톨로지" 절)이 그대로 적용됨: `fn`은 "뭔가 @@ -593,6 +604,10 @@ retract/Destroy되면 자동으로 정리됨. `:With`한 값에 따라 갈리는 경우가 있어서(위 "포지셔널 인자 지양" 절의 `noprint` 예시처럼 계산 자체를 통째로 생략하고 싶을 수 있음) — `Get()` 호출 여부를 작성자가 직접 결정하게 열어둔 것. +- **`fn`을 커링 스타일로 짜는 것도 모듈화 관용구로 권장(2026-08-07 여섯 + 번째 세션)** — `state:Observer(makeLogger("x"))`처럼 팩토리가 실제 + `fn`을 만들어 반환하는 패턴, `Modifier`의 `Boldify(10)` 커링(`modifier-plan.md` + 8번)과 같은 결. `base/effect-plan.md`의 Effect도 동일하게 권장. - **base가 제공하는 것은 `isObserver`류 타입 판별자 하나** — children 배열 dispatch가 숫자 슬롯 값을 훑을 때 "이게 Observer인가"를 판별해 `CreatedRef`와 같은 방식으로 라이프사이클에 묶어주는 것 말고는 base가 @@ -615,7 +630,14 @@ retract/Destroy되면 자동으로 정리됨. 이 State가 계속 재계산되게만 강제하고 싶을 때 씀. 문서화만 확실히 하면 별문제 없음(사용자 판단). -### `:Subscribe()`/`:Unsubscribe()` — 리프에 안 붙는 "전역/독립" Observer용 (2026-08-06 후속 세션) +### 백로그(미확정) — `state:Apply(...)`: `:With`+`:Compute` 등록을 커링으로 자동화 (2026-08-07 여섯 번째 세션, 사용자 제안) + +Effect/Observer의 `fn` 커링 관용구 논의 중 나온 인접 아이디어 — `Modifier`의 +`:Apply(factory)`(팩토리 체이닝, `modifier-plan.md` 8번)와 비슷하게, 여러 +개를 커링으로 받아 알아서 `:With`/`:Compute` 등록을 대신 해주는 +`state:Apply(...)` 같은 조합기가 있으면 편리할 수 있다는 제안. **지금 결정 +필요 없음 — 백로그로만 기록.** 구체 시그니처/필요성 검증 없음, 나중에 +`:With`/`:Compute` 관용구가 실제로 자주 반복되는 게 확인되면 다시 논의. **문제**: children 배열에 넣는 자동 라이프사이클 바인딩은 Observer가 "어딘가 leaf에 붙어있다"는 걸 전제함. 근데 흔한 실사용 패턴 하나가 이 diff --git a/.claude/base/effect-plan.md b/.claude/base/effect-plan.md index e8e7627..3bf439f 100644 --- a/.claude/base/effect-plan.md +++ b/.claude/base/effect-plan.md @@ -1,60 +1,83 @@ -# Effect — leaf 죽음에 확정 정리, 재실행 개념 없음 +# Effect — 설치 + 확정 정리, `state` 있으면 Observer를 감싸 재실행도 지원 **상태**: base — `research/additional-primitives-plan.md`(다른 프레임워크 대비 갭 분석)에서 갈라져 나온 확정 프리미티브. `base/blocker-plan.md`(같은 -조사에서 나온 다른 확정 프리미티브)와는 서로 무관 — Effect는 Store/State -작업이나 Ref/PreRef와도 파생 관계가 아닌 완전히 독립된 요소라 별도 파일로 -둔다(2026-08-07 문서 정리에서 한 파일로 합쳤던 걸 다시 분리). +조사에서 나온 다른 확정 프리미티브)와는 서로 무관 — Store/State 작업이나 +Ref/PreRef와 파생 관계는 아니라 별도 파일로 둔다(2026-08-07 문서 정리에서 +한 파일로 합쳤던 걸 다시 분리). 단 `state` 인자를 받는 형태는 내부적으로 +Observer를 조합해서 만들어짐(아래 참고, 2026-08-07 여섯 번째 세션 확정) — +"Observer와 무관한 완전 독립 프리미티브"였던 이전 서술은 정정됨. -**Observer와는 별개의, 완전히 새로운 요소로 확정** — Ref/PreRef 같은 -서로 파생된 관계가 아니라 독립적으로 존재하는 primitive. Roblox엔 -`task.spawn`으로 코루틴에 반복문/타이머를 돌리는 패턴이 흔하고, Luau -테이블엔 `__gc` 같은 GC 시점 훅이 없어서 "이게 진짜 사라지는 순간"을 아는 -유일한 방법은 `Instance.Destroying`류 명시적 신호뿐 — 이런 케이스(타이머 -시작 → leaf가 죽을 때 반드시 정지)를 위한 별도 primitive로 합의됨. +**Effect와 Observer의 관계 확정(2026-08-07 여섯 번째 세션)**: 별개의 +독립 프리미티브이되, `state`를 받는 형태의 Effect는 내부적으로 Observer를 +**조합(compose)**해서 만들어짐 — Ref/PreRef처럼 브랜드 태그만 다른 재사용이 +아니라, Observer(재실행 신호) 위에 자동 cleanup 배선을 얹은 한 단계 위 +계층. **자유 함수인 이유는 여전히 유효**: `state` 없이도 성립하는 +mount/unmount 전용 유스케이스가 있고, 실제 leaf 생명주기 바인딩은 (Observer와 +마찬가지로) children 배열 위치에 거는 것이라 `state`가 그 바인딩을 소유하지 +않음 — Roblox엔 `task.spawn`으로 코루틴에 반복문/타이머를 돌리는 패턴이 +흔하고, Luau 테이블엔 `__gc` 같은 GC 시점 훅이 없어서 "이게 진짜 사라지는 +순간"을 아는 유일한 방법은 `Instance.Destroying`류 명시적 신호뿐 — 이런 +케이스(타이머 시작 → leaf가 죽을 때 반드시 정지)를 위한 별도 primitive로 +합의됨. ``` -Effect(fn) -> EffectHandle -- fn을 즉시 1회 실행, 리턴값(nil | () -> ())은 - -- 이 Effect가 바인드된 leaf가 죽을 때 정확히 1회 호출 +Effect(fn, state?) -> EffectHandle ``` -**재실행 개념이 없다** — 값 변화에 반응해 다시 도는 건 Observer(+클로저로 -직접 짠 cleanup)의 영역이고, Effect는 순수하게 "설치 + 확정 정리" 페어 -하나만 담당한다. children 배열에 leaf로 놓는 기존 Observer 바인딩 패턴을 -그대로 재사용(그 leaf가 살아있는 동안만 유효, leaf가 죽으면 정리 콜백 -호출). 비용은 leaf당 실제 Destroying 바인딩 하나(공유 weak table로 되는 -Observer보다 비쌈) — 필요할 때만 쓰는 걸로 충분. +**`state` 생략 시**: `fn()`을 즉시 1회 실행, 리턴값(`nil | () -> ()`)은 +이 Effect가 바인드된 leaf가 죽을 때 정확히 1회 호출. 재실행 없음 +(mount/unmount 전용, React `useEffect(fn, [])`와 동형). -**Observer에 cleanup 반환 계약을 추가하는 안은 기각됨** — React `useEffect`류로 -`fn`이 `nil | () -> ()`를 반환하면 다음 재실행 직전에 그걸 불러주는 안을 -검토했으나, 클로저 업밸류로 이미 쉽게 되고 잘 작동해서(`local lastConn; -state:Observer(function() if lastConn then lastConn:Disconnect() end; -lastConn = ... end)`) 프레임워크가 이걸 대신해줄 이유가 약하다는 판단 — -`state:Observer(fn)` 자체는 여전히 재실행 계약만 갖고, Effect가 별도로 -"1회 설치 + 확정 정리"를 담당하는 이 분리 구조가 유지됨. +**`state` 지정 시(2026-08-07 여섯 번째 세션 확정)**: Effect는 내부적으로 +`state:Observer(...)`를 감싸는 걸로 구현 — `fn`은 포지셔널 인자로 `state`를 +받고(`fn(state)`, `:Compute`의 `fn(self)` 포지셔널-self 패턴 재사용, +모듈화 목적 — 클로저 캡처 없이 `fn`을 독립적으로 정의/재사용 가능), +Observer가 이제 등록 즉시 1회 실행되므로(아래 Observer 절 참고) 그 첫 +실행이 "설치"를 겸함. 이후 `state`가 무효화될 때마다 **직전 `fn` 호출이 +리턴한 cleanup을 먼저 호출한 뒤 `fn`을 재호출**, 그리고 Effect가 바인드된 +leaf가 죽을 때 **마지막 cleanup을 한 번 더 호출**. 결과적으로 React +`useEffect(fn, [dep])`와 동형(설치+재실행 사이/최종 cleanup 전부 같은 +반환 계약 하나로 처리). -## ⚠️ 미해결 — Effect와 Observer의 관계, 사용자 확인 필요 +- **다수 의존성은 `:With(...)`로 먼저 하나의 State로 묶어서 넘길 것** — + React식 별도 deps 배열을 새로 만들지 않음, quad가 이미 가진 다중 의존성 + 결합 관용구(`base/bind-system-plan.md` "`:With` + `:Compute`" 절)를 + 그대로 재사용해 같은 일 하는 두 번째 경로를 안 만듦. +- **`fn`은 커링 스타일도 권장(2026-08-07 여섯 번째 세션, 사용자 제안)** — + `Effect(makeLogger("mount"), state)`처럼 팩토리 함수가 실제 `fn(state)`를 + 만들어 반환하는 패턴, `Modifier`의 `Boldify(10)` 커링 관용구(`modifier-plan.md` + 8번)와 같은 결. `state:Observer(fn)`도 동일하게 커링 스타일을 권장 대상으로 + 같이 문서화(아래 Observer 절 참고) — 모듈화가 필요하면 둘 다 이 패턴을 쓸 것. +- **재실행이 필요 없는 케이스와 혼동하지 말 것**: 값 변화와 무관하게 설치+최종 + 정리만 필요하면 `state` 없이 `Effect(fn)`을 씀 — `state`를 굳이 넘겨서 + 재실행을 유발할 필요 없음. -**임의로 결론내지 않고 열어둠(2026-08-07 문서 정리 세션)**: 위 스펙은 -`Effect(fn)`를 State에 종속되지 않는 완전한 자유 함수로 서술하지만, 아래 -두 가지가 문서상 명확히 확인되지 않음: +children 배열에 leaf로 놓는 기존 Observer 바인딩 패턴을 그대로 재사용(그 +leaf가 살아있는 동안만 유효, leaf가 죽으면 최종 정리 콜백 호출). 비용은 +leaf당 실제 Destroying 바인딩 하나(공유 weak table로 되는 Observer보다 +비쌈) — 필요할 때만 쓰는 걸로 충분. -1. **`Effect`가 실제로는 `state:Effect(fn)`처럼 State의 메소드(=Observer의 - 변형, "재실행 없음 + 확정 정리 추가"만 다른 버전)로 구현/노출되어야 - 하는 것 아닌가?** — 그렇다면 "독립 존재 가능한 프리미티브 vs 원천에 - 종속된 파생 데이터"(`base/store-semantics.md`) 분류상 Effect도 - Observer처럼 후자(자유 함수 생성자 없음, 항상 `:` 메소드)로 재분류해야 - 함. 지금 이 문서는 이전 조사(`research/additional-primitives-plan.md`)를 - 따라 자유 함수로 서술했지만, 이게 최종 확정인지는 불명확. -2. **`state:Observer(fn)`가 생성 시점에 `fn`을 즉시 1회 실행하는지가 문서 - 어디에도 명시돼 있지 않음** — `base/bind-system-plan.md`의 Observer - 절은 "값을 안 실어줌, `fn` 본문에서 `Get()`을 다시 읽어야 함"만 - 명시할 뿐 "생성 즉시 1회 호출되는지"는 다루지 않는다. Effect는 - "즉시 1회 실행"이 스펙에 명시돼 있어 이 부분만 보면 둘이 겹쳐 - 보인다. +**Observer 자체에 cleanup 반환 계약을 추가하는 안은 여전히 기각** — React +`useEffect`류로 `fn`이 `nil | () -> ()`를 반환하면 다음 재실행 직전에 그걸 +불러주는 안을 검토했으나, 클로저 업밸류로 이미 쉽게 되고 잘 작동해서(`local +lastConn; state:Observer(function() if lastConn then lastConn:Disconnect() +end; lastConn = ... end)`) **Observer 자체**가 이걸 대신해줄 이유는 여전히 +약함. **이 기각과 위 Effect 설계는 상충하지 않는다** — 그때 기각한 건 +"Observer 자체에 이 복잡도를 넣지 말자"였지 "이 패턴 자체가 무용하다"가 +아니었음. 자동 cleanup 배선이 필요한 사람만 opt-in으로 쓰는 별도 계층 +(Effect)으로 분리해 얹었을 뿐, Observer의 기본 계약(재실행 신호만, cleanup은 +클로저로 직접)은 그대로 가볍게 유지됨. -이 두 질문이 풀리면 Effect가 (a) 완전히 별개인 자유 함수 primitive로 -남는지, (b) `state:Effect(fn)`로 Observer 계열에 합류하는지가 갈린다. -**확인 전까지는 위에 적은 자유 함수 스펙을 잠정 스펙으로 두되, 구현 -착수(M3~M4 전후) 전에 반드시 사용자와 다시 확인할 것** — `.claude/ -question.md`에 같은 항목 등재됨. +## 해결됨 — Effect/Observer 관계 (2026-08-07 여섯 번째 세션, 이전 미해결 절 대체) + +**과거 미해결이었던 두 질문 모두 확정**: +1. Effect는 자유 함수로 확정(`state:Effect(fn)` 메소드 아님) — 위 "Effect와 + Observer의 관계 확정" 절 참고. `state` 인자가 있어도 실제 leaf 생명주기 + 바인딩을 `state`가 소유하지 않아서 메소드로 만들 필연성이 없었음. +2. `state:Observer(fn)`는 등록 즉시 1회 실행되는 것으로 확정(`base/ + bind-system-plan.md`의 Observer 절 참고) — 이 덕에 Effect가 `state`를 + 받을 때 Observer를 그대로 조합해 재사용할 수 있게 됨(별도 "설치 시 + 1회 실행" 로직을 Effect가 따로 만들 필요 없음). + +`.claude/question.md` 0번의 관련 항목도 해소됨으로 갱신 완료. diff --git a/.claude/question.md b/.claude/question.md index da44a8e..fb56a81 100644 --- a/.claude/question.md +++ b/.claude/question.md @@ -30,16 +30,11 @@ context-rejected.md`. 아래는 그중 **아직 실제로 열려있는 것만** 필요 — 최종 이름만 미정(아래 "용어 정리" 절에 후보 추가). **사용자가 "작업 전에 모든 정의를 마치고 싶다"고 명시** — M0 이전 완전 확정 목표. 상세는 `research/additional-primitives-plan.md`(이제 이 주제 전용). -- **Effect가 Observer의 변형(`state:Effect()`)인지, 완전히 독립된 free - function인지 — 신규, 2026-08-07 문서 정리 세션에서 발견.** `base/ - effect-plan.md`가 지금까지의 조사대로 Effect를 "재실행 없는 - 독립 free function"으로 서술해뒀지만, 사용자가 직접 `state:Effect()` - 형태(=Observer에 "확정 정리" 계약만 추가된 변형)로 기억하고 있어서 - 확인이 필요함. 관련 하위 질문: `state:Observer(fn)`가 생성 시점에 - `fn`을 즉시 1회 실행하는지도 현재 문서 어디에도 명시돼 있지 않음(Effect는 - "즉시 1회 실행"이 스펙에 있음 — 이 부분만 보면 둘이 겹쳐 보이는 이유). - **임의로 결론내지 않고 열어둠** — 구현 착수(M3~M4 전후) 전에 확인 필요, - 상세는 `base/effect-plan.md`의 "미해결" 절. +- **[해소됨, 2026-08-07 여섯 번째 세션]** Effect/Observer 관계 — Effect는 + 자유 함수로 확정(`state` 인자를 받으면 내부적으로 `state:Observer(...)`를 + 조합해 재실행+자동 cleanup 배선, React `useEffect`와 동형). `state:Observer(fn)`도 + 등록 즉시 1회 실행되는 것으로 확정. 상세는 `base/effect-plan.md`의 + "해결됨" 절과 `base/bind-system-plan.md`의 Observer 절. - Untrack/Suspense/Error Boundary/Readonly는 조사 결과 새 프리미티브 없이 기존 설계·Lua 자체 기능으로 이미 충분한 것으로 판단(`research/ additional-primitives-plan.md` "빈 자리 아닌 것" 절). @@ -206,7 +201,7 @@ context-rejected.md`. 아래는 그중 **아직 실제로 열려있는 것만** | 컴포넌트화(플레인 함수, State/Source 경계, 컴포넌트 경계 modifier/Ref는 named parameter로 전달, multi-root 개념 폐기, `Modifier.Override`) | `base/component-composition-plan.md` | | 컴포넌트 이식성(전역 store 참조 시 재사용성 문제) | `base/purity-and-effects-plan.md` | | Blocker(값 기반 emit 지연/합치기) | `base/blocker-plan.md` | -| Effect(leaf 죽음에 확정 정리 — 단 Observer와의 관계는 위 0번 열린 질문 참고) | `base/effect-plan.md` | +| Effect(설치+확정 정리, `state` 있으면 Observer 조합해 재실행도 지원 — 확정) | `base/effect-plan.md` | | UICorner/UIPadding/UIScale 인라인 편의 키 — 이름·메커니즘·store-bind 가능성까지 확정 | `base/ui-shorthand-plan.md` | | Batch(lexical) 기각, Context(+레이어드 Store) 기각 | `archive/batch-rejected.md`, `archive/context-rejected.md` | | Fusion/Vide 비교 리서치(주의: 일부 서술은 이후 라운드에서 뒤집힘, 문서 내 정정 표시 참고) | `reference/comparison-fusion-vide.md` | diff --git a/.claude/research/additional-primitives-plan.md b/.claude/research/additional-primitives-plan.md index e0a9727..554712e 100644 --- a/.claude/research/additional-primitives-plan.md +++ b/.claude/research/additional-primitives-plan.md @@ -30,7 +30,7 @@ Vide/v1/artworks 소스 근거 조사, Context 구현 난이도 판정) + 그 | 후보 | 판정 | 현재 위치 | |---|---|---| | 키 기반 동적 컬렉션 재조정 | **진짜 빈 자리, 최우선** — 아직 열려있음 | 이 문서(아래) | -| Effect(leaf 죽음에 확정 정리) | 채택, 단 Observer와의 관계는 미해결 | `base/effect-plan.md` | +| Effect(leaf 죽음에 확정 정리 + `state` 있으면 재실행) | **채택, 확정** — Observer와의 관계도 해소 | `base/effect-plan.md` | | Blocker(값 기반 emit 지연/합치기) | **채택** — Batch의 대안 | `base/blocker-plan.md` | | Batch(함수/코루틴 스코프 lexical block) | **기각** | `archive/batch-rejected.md` | | Context(트리 하위 암묵 전파) + 레이어드 Store | **기각** | `archive/context-rejected.md` | diff --git a/CLAUDE.md b/CLAUDE.md index 6f208b7..dd68667 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -851,7 +851,7 @@ setter를 단발로 직접 호출하는 흔한 경로는 여전히 mutable이라 하나 추가된 것과, 위 Effect/Observer 미해결 항목은 M3~M4 착수 전에 확인해야 함. -## 2026-08-07 여섯 번째 세션 — Ref/PreRef 메소드 API 확정, 파일 분리, Tween GC 저장 구조 확인 +## 2026-08-07 여섯 번째 세션 — Ref/PreRef 메소드 API 확정, 파일 분리, Tween GC 저장 구조 확인, Effect/Observer 관계 해소 사용자가 메모 형태로 두 가지를 던짐: (1) Tween 인스턴스를 per-instance 저장소에 담는 구조가 실제로 GC-안전한지, (2) Ref가 이제 충분히 완결된 @@ -888,5 +888,40 @@ setter를 단발로 직접 호출하는 흔한 경로는 여전히 mutable이라 용어 정리 대상" 항목과 모순 없음(이번 세션은 메소드 이름만 확정, Ref라는 타입 이름 자체는 여전히 가칭). +**같은 세션 후반 — `.claude/question.md` 0번의 마지막 미해결 항목(Effect가 +`state:Effect()`인지 자유 함수인지) 해소.** 사용자가 직접 "정해볼까" 하고 +제기해 라이브로 논의, 다음으로 확정(전부 `base/effect-plan.md`/ +`base/bind-system-plan.md`에 반영): + +- **`state:Observer(fn)`는 등록 즉시 1회 실행되는 것으로 확정** — 근거: + (1) 이미 채워진 State를 나중에 구독하면 반영 연산이 아예 한 번도 안 + 일어나는 초기화-순서 디버깅 문제, (2) 초회 실행을 안 해야 할 구체적 + 근거가 약함, (3) 이러면 Observer 하나로 "초기값 적용"과 "이후 변경 + 반영"이 같은 코드 경로로 통일됨(store-bind 프로퍼티 핸들러가 최초 + 적용용 코드를 별도로 안 짜도 됨). +- **`Effect(fn, state?) -> EffectHandle`로 확정** — `state` 생략 시 기존 + 스펙 그대로(설치 1회 + leaf 죽을 때 확정 정리, 재실행 없음). `state` + 지정 시 **내부적으로 `state:Observer(...)`를 조합** — Observer가 이제 + 즉시 1회 실행되므로 그 첫 실행이 설치를 겸하고, 이후 무효화마다 + 직전 cleanup 호출 후 `fn` 재호출, leaf 사망 시 마지막 cleanup 1회 — + React `useEffect(fn, [dep])`와 동형. 다수 의존성은 `:With(...)`로 먼저 + 하나의 State로 묶어서 넘기는 쪽으로 확정(React식 별도 deps 배열 + 안 만듦 — 같은 일 하는 두 번째 경로 방지 원칙). Effect는 여전히 + 자유 함수(메소드 아님) — `state` 없이도 성립하는 유스케이스가 있고, + 있어도 leaf 생명주기 바인딩을 `state`가 소유하지 않아서. +- **예전에 기각했던 "Observer에 cleanup 반환 계약 추가"와 안 부딪힘** — + 그때 기각한 건 "Observer 자체에 이 복잡도를 넣지 말자"였지 패턴 자체가 + 무용하다는 게 아니었음. Effect가 opt-in 상위 계층으로 이 패턴을 제공하는 + 지금 구조가 그 기각과 정확히 양립함. +- **`fn`을 커링 스타일(팩토리가 실제 fn을 만들어 반환)로 짜는 것도 Effect/ + Observer 둘 다 모듈화 관용구로 권장** — `Modifier`의 `Boldify(10)` 커링과 + 같은 결. +- **백로그로만 기록, 결정 안 함**: `state:Apply(...)`처럼 여러 개를 커링으로 + 받아 `:With`/`:Compute` 등록을 자동화하는 조합기 아이디어(사용자 제안, + `Modifier:Apply`의 State판 대응물) — `base/bind-system-plan.md`에 백로그 + 절로만 남김, 시그니처/필요성 미검증. +- 이걸로 `question.md` 0번(추가 프리미티브 논의)의 열린 항목은 "키 기반 + 동적 컬렉션 재조정" 하나만 남음. + **다음 세션이 할 일**: 안 바뀜(`ROADMAP.md` M0부터) — 이번 세션도 이미 설계된 것의 세부 마무리라 M0 착수 우선순위 자체는 그대로.