decide(base): Observer 즉시실행 확정, Effect가 Observer를 조합하도록 확정

- state:Observer(fn)는 등록 즉시 1회 실행되는 것으로 확정 — 초기화 순서
  디버깅 문제를 피하고, store-bind 프로퍼티 핸들러가 "초기값 적용"과
  "이후 변경 반영"을 같은 코드 경로로 통일할 수 있게 됨.
- Effect(fn, state?)로 확정 — state 생략 시 기존 스펙(설치 1회 + 확정
  정리) 유지, state 지정 시 내부적으로 state:Observer(...)를 조합해
  재실행 + 자동 cleanup 배선(React useEffect와 동형). 다수 의존성은
  :With(...)로 묶어서 넘김. 여전히 자유 함수(메소드 아님) — leaf 생명주기
  바인딩을 state가 소유하지 않아서.
- fn 커링 스타일을 Effect/Observer 공통 모듈화 관용구로 권장, state:Apply
  커링 조합기 아이디어는 백로그로만 기록.
- question.md 0번의 Effect/Observer 열린 질문 해소.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
qwreey 2026-08-07 16:20:54 +09:00
parent 8ce3c11213
commit 8fc6dd3b8c
Signed by: qwreey
GPG key ID: D28DB79297A214BD
5 changed files with 136 additions and 61 deletions

View file

@ -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에 붙어있다"는 걸 전제함. 근데 흔한 실사용 패턴 하나가 이

View file

@ -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번의 관련 항목도 해소됨으로 갱신 완료.

View file

@ -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` |

View file

@ -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` |

View file

@ -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 착수 우선순위 자체는 그대로.