fix(base): 코퍼스 전체 stale 마커/모순 감사 및 무효화된 인라인 서사 archive 이전

이미 해소된 결정이 미해결로 표시되거나 문서 간 모순되던 항목 7개 파일
수정, 뒤집힌/무효화된 설계 서술이 정정 표시만 붙은 채 본문에 남아있던
곳을 기존 archive 컨벤션대로 이전(quad2-try 리서치, Observer cleanup
계약, keyed collection state method, debug channel ReplicatedStorage).
CLAUDE.md에 세션 로그 반영.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYfAz3BsaTrmMM8hnzn6mj
This commit is contained in:
qwreey 2026-08-09 16:36:59 +09:00
parent 23c2a8ae46
commit 911ab559ea
Signed by: qwreey
GPG key ID: D28DB79297A214BD
15 changed files with 330 additions and 122 deletions

View file

@ -74,6 +74,10 @@
| `modifier-apply-mutable-rejected.md` | **[기각됨, 2026-08-08 신설]** `Modifier.Apply`/setter를 mutable로 바꾸는 방안(및 "Apply 경계에서만 clone" 절충안) — 둘 다 형제 서브트리 오염 방지가 clone 비용 절감보다 우선이라 기각 |
| `tag-hash-key-model-reversed.md` | [역전됨] 구 `Tag` 모델(해시 파트 boolean 키, 태그 개수만큼 키 갱신) — 2026-08-08 세 번째 세션에서 array-part 값 객체(`Tag(...)`, `:Added`/`:Removed`/`:Contains`/`:Apply`/`Merged`) 모델로 완전히 대체됨 |
| `agent-mistake.md` | **[에이전트 실수, 2026-08-07 신설]** 설계 반전이 아니라 에이전트가 문서 작성 중 개념을 혼동했다가 같은 세션 안에서 스스로 정정한 사례 모음(`canExecute`/`isHandlable` 혼동, `isSource` 불필요 오판) — CLAUDE.md 세션 로그의 중복 서술을 여기로 옮기고 포인터만 남김 |
| `quad2-try-research-findings-rejected.md` | **[기각됨, 2026-08-09 코퍼스 정리 신설]** quad2-try 이전 시도 리서치 전문(OOP 상속/커스텀 파서/Slot 스텁/`Pipe` copy-on-write 4가지 죽은 접근 + `:With` 이름 방증) — `base/bind-system-plan.md`에 남아있던 인라인 전체 서술을 이전, 결론 한 줄 포인터만 본문에 남김 |
| `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` 태그로 대체 |
## 참고

View file

@ -5,9 +5,14 @@ Store 전달(`props.Theme: Store<Theme>`처럼 컴포넌트가 필요한 걸 nam
parameter로 명시적으로 요구) + 오버라이드가 필요한 지점에서
`Store({...부모값, 변경필드=새값})`을 한 번 명시적으로 만들어 그 지점부터
평소처럼 prop으로 넘기는 것 — 새 primitive 없이 이미 있는 Modifier의
"merge, 나중 게 이김" 패턴 재사용. 이 파일은 더 이상 능동적으로 참고할
필요 없음(구현에 안 씀) — "왜 Context가 없는가"가 `quadnomicon`(프레임워크
설계자용 심화 콘텐츠) 소재로 가치 있어서 사유를 통째로 보존해둔 것.
"merge, 나중 게 이김" 패턴 재사용. **base/ 포인터**: named parameter로
경계를 넘기는 일반 패턴은 `base/component-composition-plan.md` "1. Named
parameter로 경계를 넘김" 절, merge 패턴 자체는 `base/modifier-plan.md`
2번 절 — 이 결정 자체가 새 primitive를 만들지 "않기로" 한 것이라 전용
base/ 절이 따로 없고 기존 두 절의 재사용으로 충분함이 이 파일의 결론.
이 파일은 더 이상 능동적으로 참고할 필요 없음(구현에 안 씀) — "왜 Context가
없는가"가 `quadnomicon`(프레임워크 설계자용 심화 콘텐츠) 소재로 가치 있어서
사유를 통째로 보존해둔 것.
## 무엇을 검토했었나

View file

@ -0,0 +1,22 @@
# [기각됨] quad-debug 채널을 `ReplicatedStorage`에 자동 생성하는 방식
**기각 일시**: 2026-08-06 세션. **현재 유효한 설계**: `research/
debug-tooling-plan.md` "데이터 채널" 절 — Bindable을 quad 모듈 자신의
Instance 트리 안(quad가 이미 설치돼 있는 위치 그대로)에 두고
`CollectionService` 태그로 노출, 플러그인은 `GetTagged(tag)`로 찾음
(`GetDescendants()` 전체 순회 불필요). 이 파일은 더 이상 능동적으로 참고할
필요 없음(구현에 안 씀) — 사유를 짧게 보존해둔 것.
## 무엇을 검토했었나
quad-debug-roblox가 초기화 시 `ReplicatedStorage` 밑에 잘 알려진 이름으로
Bindable을 만들어 노출하는 방식.
## 기각 이유
개발자가 의도하지 않은 Instance를 게임 트리에 주입하는, 부작용이 큰 행위라
기각(사용자 정정). `ReplicatedStorage`는 개발자 자신의 게임 트리이지
quad가 마음대로 채워도 되는 공간이 아님 — quad 모듈 자신의 Instance 트리
안에 두면 이 문제 자체가 없고, `CollectionService` 태그를 쓰면 플러그인이
quad가 어디 설치됐는지 몰라도 바로 찾을 수 있어 `ReplicatedStorage`에 둬야
할 이유도 애초에 없었음.

View file

@ -0,0 +1,26 @@
# [기각됨] 키 기반 동적 컬렉션 재조정 프리미티브를 `state:Keyed(...)` State 메소드로 두는 안
**기각 일시**: `research/additional-primitives-plan.md` 논의 도중(날짜 미상,
"이전 라운드"로만 기록). **현재 유효한 설계**: `research/
additional-primitives-plan.md` "폼 팩터" 절 — 이 프리미티브는 자유 함수로
두고, `data` 인자가 plain array/table이든 `State<array>`/`Source<array>`든
둘 다 받는 폴리모픽 컨벤션(quad의 leaf 프로퍼티가 이미 쓰는 "리터럴 또는
State 둘 다" 관례와 동일)을 따름. 이름 자체는 아직 미정 — 이 프리미티브의
최종 설계는 여전히 열려있는 질문이라 `question.md`/`additional-primitives-plan.md`
본문을 계속 참고할 것, 이 파일은 "왜 State 메소드가 아닌가"라는 기각
사유만 보존.
## 무엇을 검토했었나
"독립 프리미티브 vs 원천 종속 파생 데이터" 원칙(Source/Ref/Store/Modifier=
독립 프리미티브, State/Observer=원천에 종속된 파생 데이터)을 그대로 적용해,
이 재조정 프리미티브도 `state:Keyed(...)`처럼 **State의 메소드**로 두자는
제안.
## 기각 이유
Source를 안 쓰는 컴포넌트는 이 메소드 자체에 접근을 못 함 — 정적 데이터
(한 번만 렌더되고 다시는 안 바뀌는 리스트)를 키 기반으로 렌더링하고 싶을
뿐인데, 굳이 `Source(정적데이터)`로 감싸야 접근 가능하다면 불필요한 강제.
"독립 프리미티브 vs 파생 데이터" 원칙 자체가 틀린 게 아니라, 이 프리미티브가
그 분류 어디에도 깔끔히 안 맞는 케이스였다는 게 재검토 결과.

View file

@ -0,0 +1,41 @@
# [기각됨] `Observer` 자체에 React `useEffect`식 cleanup 반환 계약 추가
**기각 일시**: 2026-08-07 여섯 번째 세션. **현재 유효한 설계**: `base/
effect-plan.md` "Effect와 Observer의 관계" 절 — `Observer`의 기본 계약은
재실행 신호만 주고 cleanup은 클로저로 직접 처리, 자동 cleanup 배선이
필요하면 opt-in 상위 계층인 `Effect(fn, state?)`를 쓸 것. 이 파일은 더
이상 능동적으로 참고할 필요 없음(구현에 안 씀) — "왜 Observer 자체에
cleanup 계약을 안 넣었는가"가 `quadnomicon`(프레임워크 설계자용 심화
콘텐츠) 소재로 가치 있어서 사유를 보존해둔 것.
## 무엇을 검토했었나
React `useEffect`류 패턴 — `state:Observer(fn)``fn``nil | () -> ()`
반환하면, 다음 재실행 직전에 quad가 그 반환값을 자동으로 호출해주는 안.
## 기각 이유
클로저 업밸류로 이미 쉽게 되고 잘 작동함:
```lua
local lastConn
state:Observer(function()
if lastConn then lastConn:Disconnect() end
lastConn = ...
end)
```
**Observer 자체**가 이걸 대신 배선해줘야 할 이유가 약함 — 반환값을 잡아뒀다가
다음 실행 전에 불러주는 기능을 Observer 코어에 넣으면, 그 계약을 안 쓰는
대다수 사용처까지 복잡도가 늘어나는데 클로저로 이미 공짜로 되는 걸 다시
API 표면으로 만드는 셈.
## 왜 완전히 헛수고는 아니었나 — Effect 설계와 상충하지 않음
이 기각과 이후 확정된 `Effect(fn, state?)` 설계는 상충하지 않는다 — 그때
기각한 건 "Observer 자체에 이 복잡도를 넣지 말자"였지 "이 패턴 자체가
무용하다"가 아니었음. 자동 cleanup 배선이 필요한 사람만 opt-in으로 쓰는
별도 계층(`Effect`)으로 분리해 얹었을 뿐, `Observer`의 기본 계약(재실행
신호만, cleanup은 클로저로 직접)은 그대로 가볍게 유지됨 — `Effect`
내부적으로 `state:Observer(...)`를 조합해 이 패턴을 상위 계층에서 정확히
구현한다(`base/effect-plan.md` 참고).

View file

@ -0,0 +1,90 @@
# [기각됨] quad2-try 리서치 — 죽은 접근 4가지 + Unix 파이프 영감의 최종 정리
**기각/해소 일시**: 2026-08-04(2차 라운드). **현재 유효한 설계**:
`base/bind-system-plan.md`의 "Store/State/Source 온톨로지" 절 — `state(state)`
기존 state의 결과를 받아 새 state를 만드는 조합 모델이 최종 결론, Slot은
`base/slot-plan.md`의 from-scratch 설계, `:With` 이름은 이미 확정. 이 파일은
더 이상 능동적으로 참고할 필요 없음(구현에 안 씀, "OOP 상속/커스텀 파서/Slot
스텁/Pipe copy-on-write는 확인된 죽은 접근이라 반복 조사 금지"라는 결론
한 줄만 `CLAUDE.md`/`base/bind-system-plan.md`에 포인터로 남으면 충분) —
"이전 시도에서 뭘 배웠는가"가 `quadnomicon`(프레임워크 설계자용 심화 콘텐츠)
소재로 가치 있어서 조사 과정과 근거를 통째로 보존해둔 것.
## 배경 — quad는 원래 Unix 파이프에서 영감을 받아 설계됨
quad는 원래 파이프라인/스트림 개념에서 영감을 받아 만들어짐. 이상적으로는
store에서 한 값을 추적(track)하면 "State"가 나오고, 거기에 `compute`
적용하면 또 다른 "State"가 나오는 식 — Unix의 `(cat a; cat b) | while
read ...`처럼 State끼리 자유롭게 합성/파이핑 가능한 것이 최종 목표.
`:With`의 두 번째 인자도 다른 `:Compute`의 결과물(State)을 그대로 받을 수
있어야 이상적이었음.
이 목표를 실제로 어떻게 구현할지에 두 갈래 긴장이 있었음: (1) Compute
체인이 자기 자신을 mutable하게 바꾸는 방식(엔지니어링 비용 낮지만
공유/합성이 깨짐) vs (2) 명시적 `State:fromState(state)`류 비-mutating
생성자(합성은 안전, 비용 미확정). `.claude/initreq/quad2-try/out/quad-core`
정확히 이 문제를 다뤘던 이전 재작성 시도가 있어서 그걸 조사해 답을 찾으려
했음.
## 조사 결과 — 확인된 죽은 접근, 절대 반복하지 말 것
- **OOP 상속(`Base:Extends`) 구조**가 `Source`/`State`/`Pipe`/`Store`/
`Event`/`Action`+8개 서브타입 전체에 퍼져 있었음 — 모든 서브클래스
생성자마다 `self._super._constructor(self, ...)`를 수동으로 호출해야
하고(빼먹기 쉬움, 컴파일러가 검증 안 함), private/protected는 `_` 접두사
관례일 뿐 실제 캡슐화가 전혀 없었으며, `Base:IsInstance`가 수동 유지되는
`_proto`/`_super` 연결 리스트를 순회하는 런타임 전용 타입 체크라 Luau
정적 타입 시스템이 전혀 못 봄. 사용자가 우려한 그대로 확인됨 — 상속
기반 설계 금지.
- **`--&` 커스텀 파서 시도**는 완전히 죽은 코드였음 — 6개 파일에 156줄의
주석 기반 타입/가시성 어노테이션이 있었지만, 이걸 실제로 소비할 도구
(`quad-gen`, `quad-lang`)는 둘 다 완전히 빈 디렉토리였음. 오타(`@clsas`를
`@class`로 못 고침)가 안 잡힌 채 남아있었고, 같은 주석 마커 아래 전혀 다른
Lua5.1-호환 트랜스파일러 지시어까지 섞여 있었음 — 파서가 한 번도 제대로
동작한 적 없다는 명백한 증거.
- **Slot은 이 시도에서도 사실상 빈 스텁**이었음 — `Insert`의 실제 구현부가
전부 주석 처리되어 있고, `Notify()`도 빈 함수. 심지어 구 v1(`quad-2`)의
`DEV_CHANGELOG.txt`에도 "TODO: slot 기능 구현"이 마지막까지 미완료로
남아있었음 — 가져올 게 전혀 없음, `base/slot-plan.md`의 from-scratch
설계를 그대로 진행하면 됨(재조사 불필요).
- 다른 서브패키지(`quad-roblox`/`quad-gtk`/`quad-lang`/`quad-gen`/
`quad-compat`/`quad-debug`/`quad-docs`)는 전부 파일이 0개인 빈 디렉토리
`quad-core` 밖엔 참고할 게 없음.
- `Store:Pipe`/`Store:Value` 연동이 담긴 유일한 두 예제 콜사이트
(`slot.luau:31-41`)조차 존재하지 않는 `Store:Value` 메서드를 호출하는 등
실제로 동작 검증된 적이 없는 죽은 스크래치 코드였음 — 이 프로토타입은
끝까지 실사용 검증을 통과한 적이 없음.
- **`Pipe`가 mutate-vs-`fromState` 긴장 관계에 제시했던 절충안** —
"체이닝된 `Compute`/`Add`/... 호출은 자신이 액션 리스트의 유일한
'끝(tip)'일 때만 공유 배열에 그대로 append(뮤테이션), 이미 다른 코드가
그 지점 이후로 체인을 확장해버렸다면 배열을 복사한 뒤 새 `Pipe` 객체를
반환"하는 copy-on-write 방식 — 한때는 위 (1)/(2) 긴장을 풀어보려 한
유일한 시도로서 다시 설계해볼 후보였으나, 최종적으로 폐기됨 —
`state(state)` 조합 모델이 소유권/버전 가드 없이도 같은 문제를 더
간단히 풀어서 이 절충안 자체가 불필요해짐. 원본이 갖고 있던 진짜 결함
(소유권/버전 관리 없이 경쟁 상황에 취약, 테스트/실사용 검증도 없었음)도
기록으로 남김.
## 건질 만한 것 (인체공학/아이디어만, 코드는 아님)
- **`store:Pipe(key):Compute(fn)` 같은 왼쪽에서 오른쪽으로 읽히는 파이프
문법 자체**는 목표로 유지할 가치가 있다고 판단됐음 — 실제로 이후
`:With`+`:Compute` 체이닝으로 달성됨.
- **`Depend(...)` 액션** — 계산값에는 관여하지 않고 오직 "이 소스가 바뀌면
다시 계산하라"는 추가 의존성만 등록하는 값-투명(value-transparent) no-op
액션. 작지만 깔끔한 아이디어로 기록됐으나, 이후 실제 설계에서 별도
프리미티브로 채택되지는 않음(`:With(...)` 가변인자로 같은 효과를 얻음).
- **흥미로운 발견**: 스크래치 파일(`out/asdf`)에 남아있던 더 이전 버전의
파이핑 스케치가 정확히 `Pipe(store.background):With(store.transparency,
globalStore.test):Compute(fn)` 모양이었음 — 실제 구현으로 넘어가며
`:Depend()`+포지셔널 인자로 바뀌었지만, `:With(...)` 네이밍은 이후
라운드에서 다시 요청된 것과 정확히 일치 — 우연이 아니라 원래 지향점이었던
것으로 보이며, `:With` 이름 채택에 힘을 실어준 방증.
## 결론
이 프로토타입은 사실상 죽은 시도가 맞음(확인됨) — `:With` 네이밍은
quad-v2 설계에 그대로 살아남았지만, Pipe의 copy-on-write 절충안은
2026-08-04 검증 라운드에서 폐기되고 `state(state)` 조합 모델로 대체됨.
Unix 파이프 영감이라는 원래 동기 자체는 `:With`+`:Compute` 체이닝으로
충분히 달성된 것으로 최종 판단.

View file

@ -643,9 +643,10 @@ ref 타입처럼 생각하는 게 맞는 거 같음 — 그걸 처리하는 플
Ref는 그냥 "지금 뭐가 들어있나/누가 채워주길 기다리나"만 다루는 즉시
값 박스이고, 파생값이 필요하면 Store/State(`:With`+`:Compute`)를 쓸 것
— 둘을 섞으려 하지 말 것.
- **용어 정리 합류 대상**: Ref의 정의 자체가 "instance를 얻는 것"에서
"범용 값 박스"로 넓어졌으므로, 진행 중인 용어 정리(`question.md` 1번)
때 이름이 여전히 맞는지 같이 재검토할 것.
- **[해소됨, 2026-08-08 다섯 번째 세션]** 위 정의 확장을 감안해도 `Ref`
이름은 그대로 확정 — "지연 없는 확정된 값 박스"라는 정의가 leaf로
담기는 용도/leaf에 바인딩하는 용도 둘 다에 여전히 맞아 더 나은 대안이
없다는 결론, 용어 정리 대상에서 제외됨.
### `phase` 옵션 폐기 → 위치로 표현, `PreRef` 신설 (2026-08-07 세 번째
세션 — 이 절이 당시 쓰던 `CreatedRef(fn, ...)` 래퍼 이름 자체도 이후
@ -1268,25 +1269,11 @@ end
두 경로를 동시에 쓰고 싶으면 각각 독립된 새 `Effect(...)`/
`state:Observer(...)` 호출로 따로 만들 것"을 명시할 것.
## Unix 파이프에서 영감 받은 스트림 지향 — 원래 의도, 해소됨
**배경**: quad는 원래 이 파이프라인/스트림 개념에서 영감을 받아 만들어짐.
이상적으로는 store에서 한 값을 추적(track)하면 "State"가 나오고, 거기에
`compute`를 적용하면 또 다른 "State"가 나오는 식 — Unix의 `(cat a; cat b) | while
read ...`처럼, State끼리 자유롭게 합성/파이핑 가능한 것이 최종 목표. `:With`
두 번째 인자(`b`)도 다른 `:Compute`의 결과물(State)을 그대로 받을 수 있어야
이상적.
**해소됨(2차 라운드) — 두 갈래 방식 중 실질적으로 옵션 2 방향으로 정리됨**:
당시엔 (1) Compute 체인이 자기 자신을 mutable하게 바꾸는 방식(엔지니어링 비용
낮지만 공유/합성이 깨짐) vs (2) 명시적 `State:fromState(state)`류 비-mutating
생성자(합성은 안전, 비용 미확정) 둘로 긴장이 있었으나, 실제 확정된 모델은
아래 "Store/State/Source 온톨로지" 절의 **`state(state)`로 기존 state의
결과를 받아 새 state를 만드는 조합**임 — 매번 새 State를 만든다는 점에서
옵션 2와 같은 축(비-mutating)이고, 별도 `fromState`/`Pipe` 콤비네이터 타입
없이도 `state(state)` 하나로 충분하다는 게 최종 결론(`Pipe` 후보는 폐기).
`base/architecture.md`의 "복사 구현 지양, 팩토리 함수로 대체" 원칙과 같은
축의 해법.
**quad의 Unix 파이프 영감(원래 동기)과 `Pipe`/`fromState` 후보 검토 경위는
`archive/quad2-try-research-findings-rejected.md`로 이전됨** — 최종 결론만
남기면: 목표(State끼리 자유롭게 합성/파이핑)는 아래 "Store/State/Source
온톨로지" 절의 `state(state)` 조합 모델로 달성됨, 별도 `Pipe`/`fromState`
콤비네이터 타입은 불필요로 폐기.
## Store/State/Source 온톨로지 — 핵심 메커니즘 확정 (2026-08-04 2차 라운드)
@ -1579,63 +1566,13 @@ Observable/Observer)을 조사한 결과, 두 지점에서 기존 확정과 실
## quad2-try 리서치 결과 (완료) — 이전 시도에서 뭘 가져오고 뭘 버릴지
`.claude/initreq/quad2-try/out/quad-core`에 정확히 이 문제(Unix 파이프 영감의
State/스트림)를 다뤘던 이전 시도가 있었음. 조사 결과 요약:
**확인된 죽은 접근 — 절대 반복하지 말 것:**
- **OOP 상속(`Base:Extends`) 구조**가 `Source`/`State`/`Pipe`/`Store`/`Event`/
`Action`+8개 서브타입 전체에 퍼져 있었음 — 모든 서브클래스 생성자마다
`self._super._constructor(self, ...)`를 수동으로 호출해야 하고(빼먹기 쉬움,
컴파일러가 검증 안 함), private/protected는 `_` 접두사 관례일 뿐 실제
캡슐화가 전혀 없었으며, `Base:IsInstance`가 수동 유지되는 `_proto`/`_super`
연결 리스트를 순회하는 런타임 전용 타입 체크라 Luau 정적 타입 시스템이
전혀 못 봄. **사용자가 우려한 그대로 확인됨 — 상속 기반 설계 금지.**
- **`--&` 커스텀 파서 시도**는 완전히 죽은 코드였음 — 6개 파일에 156줄의
주석 기반 타입/가시성 어노테이션이 있었지만, 이걸 실제로 소비할 도구
(`quad-gen`, `quad-lang`)는 **둘 다 완전히 빈 디렉토리**였음. 오타(`@clsas`를
`@class`로 못 고침)가 안 잡힌 채 남아있었고, 같은 주석 마커 아래 전혀 다른
Lua5.1-호환 트랜스파일러 지시어까지 섞여 있었음 — 파서가 한 번도 제대로
동작한 적 없다는 명백한 증거. **확인대로 반복 금지.**
- **Slot은 이 시도에서도 사실상 빈 스텁**이었음 — `Insert`의 실제 구현부가
전부 주석 처리되어 있고, `Notify()`도 빈 함수. 심지어 구 v1(`quad-2`)의
`DEV_CHANGELOG.txt`에도 "TODO: slot 기능 구현"이 마지막까지 미완료로 남아있었음
**가져올 게 전혀 없음**, `base/slot-plan.md`의 from-scratch 설계를
그대로 진행하면 됨(재조사 불필요).
- 다른 서브패키지(`quad-roblox`/`quad-gtk`/`quad-lang`/`quad-gen`/`quad-compat`/
`quad-debug`/`quad-docs`)는 전부 파일이 0개인 빈 디렉토리 — `quad-core` 밖엔
참고할 게 없음.
- `Store:Pipe`/`Store:Value` 연동이 담긴 유일한 두 예제 콜사이트(`slot.luau:31-41`)조차
존재하지 않는 `Store:Value` 메서드를 호출하는 등 실제로 동작 검증된 적이
없는 죽은 스크래치 코드였음 — 이 프로토타입은 끝까지 실사용 검증을 통과한
적이 없음.
**건질 만한 것 (인체공학/아이디어만, 코드는 아님):**
- **`store:Pipe(key):Compute(fn)` 같은 왼쪽에서 오른쪽으로 읽히는 파이프
문법 자체**는 목표로 유지할 가치가 있음.
- **`Pipe`가 mutate-vs-`fromState` 긴장 관계에 제시했던 절충안** — "체이닝된
`Compute`/`Add`/... 호출은 자신이 액션 리스트의 유일한 '끝(tip)'일 때만 공유
배열에 그대로 append(뮤테이션), 이미 다른 코드가 그 지점 이후로 체인을
확장해버렸다면 배열을 복사한 뒤 새 `Pipe` 객체를 반환"하는 **copy-on-write
방식** — 한때는 이 문서의 "mutate-in-place vs `fromState`" 긴장을 풀어보려
한 유일한 시도로서 다시 설계해볼 후보였으나, **아래 "종합"에서 최종적으로
폐기됨** — `state(state)` 조합 모델이 소유권/버전 가드 없이도 같은 문제를
더 간단히 풀어서 이 절충안 자체가 불필요해짐. 원본이 갖고 있던 진짜 결함
(소유권/버전 관리 없이 경쟁 상황에 취약, 테스트/실사용 검증도 없었음)은
기록으로만 남김.
- **`Depend(...)` 액션** — 계산값에는 관여하지 않고 오직 "이 소스가 바뀌면
다시 계산하라"는 추가 의존성만 등록하는 값-투명(value-transparent) no-op
액션. 작지만 깔끔한 아이디어라 이름 그대로 채택할 만함.
- **흥미로운 발견**: 스크래치 파일(`out/asdf`)에 남아있던 더 이전 버전의
파이핑 스케치가 정확히 `Pipe(store.background):With(store.transparency,
globalStore.test):Compute(fn)` 모양이었음 — 실제 구현으로 넘어가며
`:Depend()`+포지셔널 인자로 바뀌었지만, **`:With(...)` 네이밍은 사용자가
이번 라운드에서 다시 요청한 것과 정확히 일치** — 우연이 아니라 원래
지향점이었던 것으로 보임, `:With` 이름 채택에 힘을 실어줌.
**종합**: 이 프로토타입은 사실상 죽은 시도가 맞음(확인됨) — `Depend`/`:With`
네이밍은 quad-v2 설계에 그대로 살려볼 가치가 있는 아이디어로 남지만, **Pipe의
copy-on-write 절충안은 2026-08-04 검증 라운드에서 사실상 폐기 쪽으로 재평가됨**
(위 "Store/State/Source 온톨로지" 절 참고 — State 자체가 파이핑 결합체이고
`state(state)`로 분기하는 쪽이 더 간단하다는 사용자의 최신 판단).
State/스트림)를 다뤘던 이전 시도가 있어 조사함 — **확인된 죽은 접근(OOP 상속
`Base:Extends`/`--&` 커스텀 파서/Slot 빈 스텁/`Pipe` copy-on-write 절충안)은
절대 반복 조사하지 말 것**, 상세 근거와 "건질 만한 것"(`:With` 이름의
방증 등)은 `archive/quad2-try-research-findings-rejected.md` 참고 — 이
조사의 최종 결론은 이미 아래 "Store/State/Source 온톨로지" 절의 `state(state)`
조합 모델로 대체되어 있고 Slot은 `base/slot-plan.md`의 from-scratch 설계를
그대로 쓰면 됨(재조사 불필요).
## 확정된 것 (더 이상 열린 질문 아님)

View file

@ -34,7 +34,8 @@ v1의 `Class.Extend()`(Init/Render/AfterRender/Getter/Setter/UpdateTriggers,
자동 연결과 동일) — quad.qwreey.kr 튜토리얼 `11_extend/` 문서 원문 확인.
이 두 역할이 v2 온톨로지에서는 이미 갈라져 있음: (1)은 확정된 **Ref**가
대체, (2)는 이번 논의에서 다루는 Source 양방향 프록시가 대체.
대체, (2)는 아래 "4. Source 직접 전달" 절이 대체(폐기된 `StoreSource`
프록시와는 다른 개념 — 혼동 방지용으로 명명을 맞춤).
## 수렴된 결론
@ -274,8 +275,9 @@ caller가 named parameter 하나에 여러 modifier를 몰아넣고 싶을 때
같아"). `MyComp { Modifier = Modifier.Overridden(theme, override) }` → 컴포넌트
내부는 항상 이미 합쳐진 단일 값만 받으므로 컴포넌트 저작자가 배열 처리를
신경 쓸 필요 없음. Ref는 필드 충돌 개념이 없어 이 문제 자체가 없음(여러
Ref를 받으면 그냥 전부 실행하면 됨, `modifier-plan.md` §4-2) — 별도 결합
유틸 불필요. **정확한 동작(baked 값 교체 경고, 순서 의존성, `Apply`와의
Ref를 받으면 그냥 전부 실행하면 됨 — Ref 콜백 리스트는 애초에 여러 등록을
누적하도록 설계돼 있음, `bind-system-plan.md`의 Ref 콜백/대기자 절) — 별도
결합 유틸 불필요. **정확한 동작(baked 값 교체 경고, 순서 의존성, `Apply`와의
역할 구분, `:Peek`/`isState`)은 `base/modifier-plan.md` 9번 절이 최종
소스** — `Merge`로 전부 대체해 `Apply`만 강제하는 방안도 이번에 검토했으나,
이 3번 절에서 확정한 실사용 니즈(단일 named parameter 슬롯에 독립적으로

View file

@ -59,15 +59,12 @@ leaf당 실제 Destroying 바인딩 하나(공유 weak table로 되는 Observer
비쌈) — 필요할 때만 쓰는 걸로 충분.
**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은
클로저로 직접)은 그대로 가볍게 유지됨.
`useEffect`식으로 `fn`의 반환값을 자동으로 배선해주는 안을 검토했으나,
클로저 업밸류로 이미 충분해 채택 안 함. 이 기각은 위 Effect 설계와
상충하지 않음(그때 기각한 건 "Observer 자체에 이 복잡도를 넣지 말자"였지
패턴 자체의 무용함이 아니었고, `Effect`가 opt-in 상위 계층으로 정확히
이 패턴을 제공함) — 상세 경위는 `archive/observer-cleanup-contract-rejected.md`
참고.
## `EffectHandle:Subscribe()`/`:Unsubscribe()` — leaf 없이 쓰는 독립 Effect (2026-08-07 일곱 번째 세션)

View file

@ -125,6 +125,15 @@ context-rejected.md`. 아래는 그중 **아직 실제로 열려있는 것만**
- `Store`/`Source`/`Modifier`/`Ref`/`PreRef`/`Peek`/`isState`/`Handler`/
`None`/`NoneHandler`/`process`/`retract`/`isHandlable`은 업계 선례와
잘 맞거나 이미 신중하게 결정된 이름들이라 특별한 문제 없음.
- **`Tag`/`Added`/`Removed`/`Merged`(3순위, 사소함, 2026-08-08 세 번째
세션 array-part 값 객체 재설계 때 확정된 API 표면)**: `base/tag-plan.md`
"열린 질문 없음, 값 모양/메커니즘/retract/패키지 배치 전부 확정, 이름
자체만 용어 정리 대상"이라고 명시해뒀으나 이 목록에 반영이 안 돼 있던
누락 — 이번에 추가. `Tag`는 Roblox `CollectionService`가 쓰는 용어와
1:1 대응이라 그 자체로는 무난해 보이지만, 위 `Brand` 항목(97-99행)에서
"`Tag`가 이미 이 뜻으로 쓰이고 있어서 충돌"이라는 이유로 `Brand`
대안 이름 후보에서 제외됐다는 점은 참고할 것 — 두 이름이 같은 코퍼스
안에서 공존 가능한지도 같이 검토 대상.
### 2. 구현 착수 직전 감사 결과 (2026-08-06 신설, M0 착수 전 확인 권장)

View file

@ -12,8 +12,8 @@ context-rejected.md`. 이 문서에는 **아직 완전히 열려있는 것 하
사용자 질문: "다른 독립 프리미티브나 종속 파생 데이터는 뭐가 더 필요할 것
같나요. 이것만으로 이 프로젝트는 충분하다 생각해요?" — 지금까지 확정된
독립 프리미티브(`Source`/`State`/`Store`/`Ref`/`Observer`/`Modifier`/`Slot`/
`DI`)만으로 충분한지, 웹 프레임워크나 실제 Roblox 개발 관점에서 솔직하게
독립 프리미티브(`Source`/`Store`/`Ref`/`Modifier`/`Slot`/`DI`)+파생 데이터
(`State`/`Observer`)만으로 충분한지, 웹 프레임워크나 실제 Roblox 개발 관점에서 솔직하게
재검토해달라는 요청.
## 조사 방법
@ -74,12 +74,9 @@ key가 유지, 순서만 변경 → renderFn 재호출 없음, Slot 위치만
### 폼 팩터 — 자유 함수로 정정 (State 메소드 프레이밍 철회)
이전 라운드에서 "독립 프리미티브 vs 원천 종속 파생 데이터" 원칙을 적용해
`state:Keyed(...)`처럼 **State의 메소드**로 두자고 제안했는데, 사용자가
정확한 반례를 지적함: **Source를 안 쓰는 컴포넌트는 이 메소드 자체에
접근을 못 한다.** 정적 데이터(한 번만 렌더되고 다시는 안 바뀌는 리스트)를
키 기반으로 렌더링하고 싶을 뿐인데 굳이 `Source(정적데이터)`로 감싸야
한다면 불필요한 강제다.
`state:Keyed(...)`처럼 State의 메소드로 두려던 초안은 "Source를 안 쓰는
컴포넌트가 접근 못 함" 반례로 철회됨 — 상세 경위는
`archive/keyed-collection-state-method-rejected.md` 참고.
재검토 결과 — quad는 이미 **leaf 프로퍼티가 "리터럴 값 또는 State" 둘 다
받는 폴리모픽 컨벤션**을 갖고 있다(`BackgroundColor3 = someColor`도

View file

@ -238,18 +238,16 @@ trace 이벤트 페이로드는 처음부터 함수/클로저 없이 **순수
재검토)**: "지금 이 순간 일어난 일" 스트림은 BindableEvent, 특정
Instance 선택 시 상세 정보 요청-응답은 BindableFunction — 둘 다 실측
확인됨(위 참고).
- **`ReplicatedStorage` 자동 생성 방식은 기각 — 사용자 정정**: 처음
구상은 quad-debug-roblox가 초기화 시 `ReplicatedStorage` 밑에 잘
알려진 이름으로 Bindable을 만들어 노출하는 것이었으나, **이건
개발자가 의도하지 않은 Instance를 게임 트리에 주입하는, 부작용이
큰 행위라 기각**. 대신 Bindable을 **quad 모듈 자신의 Instance
트리 안**(quad가 이미 설치돼 있는 위치 그대로, 새 위치를 따로
안 만듦)에 두고, `CollectionService` 태그로 노출 — 플러그인은
quad가 어디 설치됐는지 몰라도 `CollectionService:GetTagged(tag)`
바로 찾음(`GetDescendants()`로 전체 트리를 훑어 필터링할 필요
없음 — 사용자가 "roblox query descendants" 관련해서 짚어준 더
저렴한 방법). 태그를 모듈 자신에 달지 Bindable 각각에 달지는
취향 차이 — **사용자 확정**("큰 차이는 없는 엔지니어링 선택").
- **`ReplicatedStorage` 자동 생성 방식은 기각** — 개발자가 의도하지
않은 Instance를 게임 트리에 주입하는 부작용 때문(상세 경위는
`archive/debug-channel-replicatedstorage-rejected.md`). 대신 Bindable을
**quad 모듈 자신의 Instance 트리 안**(quad가 이미 설치돼 있는 위치
그대로, 새 위치를 따로 안 만듦)에 두고, `CollectionService` 태그로
노출 — 플러그인은 quad가 어디 설치됐는지 몰라도
`CollectionService:GetTagged(tag)`로 바로 찾음(`GetDescendants()`로
전체 트리를 훑어 필터링할 필요 없음). 태그를 모듈 자신에 달지
Bindable 각각에 달지는 취향 차이 — **사용자 확정**("큰 차이는 없는
엔지니어링 선택").
- **공통 원칙(사용자 확정)**: 어떤 채널이든 **debug를 안 켰을 땐 CPU/메모리
영향이 사실상 없어야 함**(패시브하게 가볍게만 들고 있거나, 아예 아무것도
안 함) — 무거운 트레이스 함수 실행은 debug를 켠 다음부터만. 이건 위

View file

@ -92,7 +92,7 @@ v1 폐기 API/버그/구조 결함 전부 v2 설계를 정당화하는 내부
- skip: 세션 날짜/확정 이력, 문서 승격/정정 안내
### store-semantics.md / tween-plan.md / ui-shorthand-plan.md
- 초심자: Store 생성+`myStore.key = value` 문법 / `store.key`로 State 얻기 개념 / Tween 기본 바인드 키+취소 기본 동작 / UI 숏핸드 인라인 키 기본 예시(`Frame { UIPaddingOffset = 50 }`)
- 초심자: Store 생성+`myStore.key:Set(value)` 문법 / `store.key`로 State 얻기 개념 / Tween 기본 바인드 키+취소 기본 동작 / UI 숏핸드 인라인 키 기본 예시(`Frame { UIPaddingOffset = 50 }`)
- api: `:With`+`:Compute` 시그니처(→심화) / `source:Emit()` 존재+"Get() 결과 캐시 금지" 캐비엇(버그 유발 포인트라 api에도 명시 가치 있음, →심화; 2026-08-06 후속 세션에서 `Store:Emit(key)`→`source:Emit()`로 호출부 변경, `store-semantics.md` 참고) / Tween 핸들러가 Instance 직접 받음(Ref 불필요) / retract는 Destroy 시 호출 안 됨(→심화) / UI 숏핸드 키 목록 레퍼런스 표 / Modifier와 순수 인라인 키 동등성
- 심화: Source·Store·State·Observer 온톨로지(독립 프리미티브 vs 파생 데이터 원칙, 생성자 모양 근거) / `Emit`이 Source 전용인 이유(디버깅 그래프 무결성) / `Store<T>`의 T가 Modifier 불가인 이유 / Tween을 반응 그래프 밖 특수 bind key로 둔 이유(Fusion 반면교사) / RoundSize 포팅 불필요 vs UICorner/UIPadding/UIScale 필요 이유 / "작고 opt-in 아닌 편의 기능은 코어 포함" 원칙
- 열린 질문(문서화 보류): tween-plan.md의 오버라이드/삭제후재시작/끝점이동 옵션 키 이름 미정 / ui-shorthand의 RoundSize 완전 드롭 여부

View file

@ -1785,3 +1785,76 @@ Modifier만 예외인 건 Modifier가 애초에 dispatch 경로 자체를 안
"State에서 Slot을 뽑아내는" 키 기반 동적 컬렉션 재조정(가칭 `Keyed`
탈락, 최종 이름 미정) — `.claude/question.md` 0번 "키 기반 동적
컬렉션 재조정"이 이미 최우선 항목으로 잡혀있으니 그걸 이어서 보면 됨.
## 2026-08-09 두 번째 세션 — `.claude/` 코퍼스 전체 stale 마커 감사·수정,
무효화된 인라인 서사 archive 이전
새 설계 결정 없음, 순수 문서 정리 세션. 서브에이전트 4개를 병렬로 띄워
`.claude/` 전체(30여 개 문서 + `ROADMAP.md`/`HUMAN_TODO.md`/`SAFETY.md`/
`archive/`)를 클러스터별로 감사, "이미 해소됐는데 미해결로 표시된 것"과
"문서 간 모순"을 찾아 전부 직접 수정(커밋 전 상태 기준). 이어서 사용자
요청으로 두 번째 라운드 — 뒤집혔거나 무효화된 설계가 정정 표시만 붙은 채
본문에 전체 서술로 남아있는 곳을 찾아 기존 `archive/*-reversed.md`/
`*-rejected.md` 컨벤션대로 이전(본문엔 결론+포인터만 남김), 컨텍스트
낭비 방지 목적. 이것도 서브에이전트 3개 병렬 감사로 후보를 찾은 뒤 직접
판단해 적용.
**1차 라운드 — stale 마커/모순 수정 (7개 파일)**:
- `bind-system-plan.md`: `Ref` 이름이 "용어 정리 재검토 대상"으로 남아있던
것 — 2026-08-08 다섯 번째 세션에서 이미 확정됐는데 반영 안 됨 → 해소
표시로 정정. `component-composition-plan.md` §4-2 인용 오류(그 절은
실제로 다른 내용을 다룸 — Ref 필드 충돌 없음의 근거를 잘못 인용)와
폐기된 `StoreSource` 프록시와 혼동될 수 있는 "Source 양방향 프록시"
표현도 정정.
- `documentation-content-map.md`: 폐기된 `myStore.key = value` 대입
문법이 예시로 남아있던 것(같은 파일 바로 다음 줄은 `:Set()`으로 옳게
써서 자기모순) → 정정.
- `ROADMAP.md`: 세션 인용 오류 2건(`git blame`으로 실제 커밋 시점 확인해
정정 — M0의 Source/State 서브타입 항목은 "세 번째 세션", M2의
`LifetimeHandle` 순서 역전 항목은 "네 번째 세션"이 맞음), `Bound`/
`None` "가칭" 표기가 이미 이름 확정됐는데 안 지워진 것 2건 정정, M6에
Slot CRUD 의미론 확정 체크박스 누락돼 있던 것 추가(`pre-implementation-audit.md`
우선순위1이 이미 지적했던 갭).
- `question.md`: `Tag`/`Added`/`Removed`/`Merged`가 `tag-plan.md`에서
"여기서 추적 중"이라 주장했지만 실제로 빠져있던 것 추가.
- `archive/context-rejected.md`: 다른 archive 문서와 달리 base/ 포인터가
없던 것 보강.
- `additional-primitives-plan.md`: State/Observer를 "독립 프리미티브"로
잘못 묶은 표현 정정(확정된 분류는 Source/Store/Ref/Modifier/Slot/DI=
독립 프리미티브, State/Observer=파생 데이터, 2026-08-08 두 번째 세션
"Handler는 세 번째 카테고리" 절 참고).
**2차 라운드 — 무효화된 인라인 서사를 archive로 이전 (신규 archive 4개)**:
- `archive/quad2-try-research-findings-rejected.md``bind-system-plan.md`
60줄 넘게 남아있던 quad2-try(폐기된 이전 재작성 시도) 리서치 전문(OOP
상속/커스텀 파서/Slot 빈 스텁/`Pipe` copy-on-write 4가지 확인된 죽은
접근 + Unix 파이프 영감이라는 원래 동기 서사)을 통째로 이전 — "반복
조사 금지" 결론과 `state(state)` 조합 모델 포인터만 본문에 남김.
- `archive/observer-cleanup-contract-rejected.md``effect-plan.md`
"Observer 자체에 React `useEffect`식 cleanup 반환 계약을 추가하는 안"
기각 서술(코드 예시 포함) 이전.
- `archive/keyed-collection-state-method-rejected.md``additional-primitives-plan.md`
"키 기반 동적 컬렉션 재조정을 `state:Keyed(...)` State 메소드로 두려던"
초안 기각 서술 이전(이 프리미티브 자체는 여전히 열린 질문 — 폼 팩터
결정 부분만 이전됨).
- `archive/debug-channel-replicatedstorage-rejected.md``debug-tooling-plan.md`
"`ReplicatedStorage` 자동 생성" 초안 기각 서술 이전.
각 archive 파일은 기존 컨벤션(`[기각됨]` 제목, "현재 유효한 설계" 포인터,
`quadnomicon` 소재 메모)을 그대로 따름, `README.md`의 archive 인덱스도
4개 항목 추가로 동기화 완료.
**의도적으로 손 안 댄 것들**: `bind-system-plan.md`의 PreRef pre-pass
위치 관련 기각 서술, `lifecycle-pattern.md``canExecute` 시그니처
재정정 단락, `modifier-plan.md` 9-1(b)의 "동질적/이질적" 초안 — 전부
현재 설계를 정당화하는 근거로 너무 밀착돼 있어서, 분리하면 "왜 이렇게
안 했는지"가 같이 잘려나가 다음 에이전트가 같은 대안을 또 검토할
위험이 있다고 판단해 그대로 둠. `documentation-content-map.md`가 최근
추가된 5개 base 문서(`relate`/`blocker`/`effect`/`tag`/`attribute`-plan.md)의
초심자/api/심화 분류를 아직 안 갖고 있는 것도 실제 설계 판단(콘텐츠
분류)이 필요해 손 안 댐 — 문서 자신도 이미 "지금 당장 안 급함"이라고
인정하고 있음.
**다음 세션이 할 일**: 안 바뀜(`ROADMAP.md` M0부터, 위 "다음 세션 예고"
Slot/키 기반 컬렉션 재조정도 그대로) — 이번 세션은 순수 문서 위생
작업이라 설계 우선순위엔 영향 없음.

View file

@ -20,7 +20,7 @@ Luau 코드로 부딪혀본 적 없는 세 가지**를 던지는 코드로 검
짜보기(다이아몬드 의존성 케이스 포함 — 이미 invalid면 전파 중단되는지)
- [ ] Source가 State를 구조적으로 만족하는 제네릭 타입(`:Compute<U>(self:
Source<T>, ...) -> State<U>`류, self 타이핑 + State 참조 혼합)이
Luau 솔버에서 안전하게 추론되는지 확인(2026-08-06 후속 세션,
Luau 솔버에서 안전하게 추론되는지 확인(2026-08-06 세 번째 세션,
`base/store-semantics.md` "Source가 State를 만족함" 절 — `State<T>`
`Source`를 참조하지 않는 단방향 의존으로 두면 위험한 상호 재귀는
피할 수 있어 보이나 실제 검증 전엔 확정 아님)
@ -97,7 +97,7 @@ Luau 코드로 부딪혀본 적 없는 세 가지**를 던지는 코드로 검
quad-roblox 실 구현은 M8) — 원래 M8에만 있었으나 M4(StoreBind의
`Connected` 확인)/M6(Slot의 `canExecute`)이 이미 이 인터페이스를
전제로 서술돼 있어 로드맵 순서가 역전돼 있었음(`pre-implementation-audit.md`
우선순위1-9, `question.md` 2번 — 2026-08-07 번째 세션에 반영).
우선순위1-9, `question.md` 2번 — 2026-08-07 번째 세션에 반영).
**`canExecute``(inst, value) -> boolean`으로 재확정(2026-08-08
세션, `(handle)` 단일 인자 서술을 대체)** — Observer/Effect는 자기
`Subscribed` 상태를 먼저 확인, 그 다음 `inst`의 공유 gcconn(`Relate`로
@ -143,9 +143,10 @@ Luau 코드로 부딪혀본 적 없는 세 가지**를 던지는 코드로 검
`EffectHandle:Subscribe()`/`:Unsubscribe()`도 추가(leaf 없이 쓰는
모듈/스크립트 레벨 Effect) — `:Unsubscribe()`는 Observer와 달리
마지막 cleanup을 1회 트리거해야 함(2026-08-07 일곱 번째 세션)
- [ ] Observer/Effect 이중 바인딩 금지 — `Bound`(가칭) 플래그로 leaf 부착과
`:Subscribe()`가 동시에 걸리면 즉시 `error`(`base/bind-system-plan.md`
"이중 바인딩 금지" 절, 2026-08-07 일곱 번째 세션)
- [ ] Observer/Effect 이중 바인딩 금지 — `canBound(handle)` predicate로 leaf
부착과 `:Subscribe()`가 동시에 걸리면 즉시 `error`(`base/bind-system-plan.md`
"이중 바인딩 금지" 절, 2026-08-07 일곱 번째 세션 신설, 이름은
2026-08-09 세션에 `canBound`로 확정)
- [ ] mock 대상 테스트
## M4 — 첫 end-to-end 반응형 업데이트
@ -169,6 +170,12 @@ Luau 코드로 부딪혀본 적 없는 세 가지**를 던지는 코드로 검
- [ ] "여러 Slot이 형제로 섞일 때 순서 보장" 열린 질문 확인(`slot-plan.md`) —
Roblox 단일 백엔드로는 급하지 않으면 스킵하고 진행 가능
- [ ] **Slot의 `add`/`remove`/`clear` CRUD 의미론 확정 — 착수 전 필수.**
`research/pre-implementation-audit.md` 우선순위1이 지적한 갭, 아직
미해결(2026-08-07 아홉 번째 세션에서 "사용자가 다음 세션에서 직접
다루기로 보류"로 확인, 2026-08-09 세션 말미에도 다음 세션 예고로
다시 지목됨). "재마운트 시 즉시 throw"가 개별 element 기준인지 Slot
컨테이너 전체 기준인지도 이 논의에서 같이 확정할 것.
- [ ] base `Dispatch/Slot.luau`(추상 재조정) + quad-roblox `Handlers/Slot.luau`
(실제 Parent 조작)
@ -196,8 +203,8 @@ Luau 코드로 부딪혀본 적 없는 세 가지**를 던지는 코드로 검
`isState(x)`/`isSource(x): boolean`(`Brand` 공유 레지스트리 기반 —
`modifier-plan.md` 9번, `bind-system-plan.md``Brand` 절, M2의
`Brand.luau`에 이미 구현돼 있어야 함)
- [ ] 인라인 키/setter로 modifier 필드를 명시적으로 지우는 `None`(가칭)
센티널(`modifier-plan.md` 2-1번, `Peek` 반환 타입에 `None` 추가) +
- [ ] 인라인 키/setter로 modifier 필드를 명시적으로 지우는 `None` 센티널
(이름 확정, `modifier-plan.md` 2-1번, `Peek` 반환 타입에 `None` 추가) +
이를 `nil`로 재디스패치하는 base 내장 `NoneHandler`
(`bind-system-plan.md`의 `None` 센티널 절, M2 dispatch 엔진의
"이전 매치 핸들러 추적" 항목과 함께 구현 — Tween store-bind 핸들러와