docs(audit): 프로젝트 전체 재감사 — 분할 후 잔여 stale 참조 14곳 정정

69466ab/32e9db0 이후 5개 영역 병렬 에이전트 감사 + 직접 트레이싱으로
프로젝트 전체를 다시 훑음. 핵심 설계(하강 diff 재디스패치, Tag
참조카운트, nameClaims, Slot-in-Slot 해체)는 재트레이싱해도 버그
없음 — 전부 같은 클래스의 문서 참조 잔여 오류였음: `bind-system-plan.md`
2단계 분할(14차 세션) 이후에도 그 파일을 계속 가리키는 느슨한 산문
인용(따옴표 절 제목이 아니라 "~가 말하는"/"~ 참고" 식이라 doc-check.py
정규식이 못 잡는 형태).

- `ref-plan.md`/`ui-shorthand-plan.md`(2곳)/`tween-plan.md`/
  `slot-plan.md`(3곳) — `dispatch-core-plan.md`로 정정.
- `module-lifecycle-plan.md:21` — 32e9db0가 같은 파일 114/128행은
  고쳤지만 21행만 놓쳤던 것.
- `ROADMAP.md`(2곳)/`luau-test/README.md` — `None`/`recompute` 절
  참조 정정.
- `luau-test/done/02`/`03` 스파이크 주석 — 02는 `ref-plan.md`(9차
  세션에 이미 옮겨간 절이었음, bind-system-plan.md였던 적 없음),
  03은 `dispatch-core-plan.md`로 정정.
- `audit/luau-test-first-run-2026-08-13.md` — "no-op 점유 마커"를
  여전히 유효한 수정 근거처럼 서술하던 부분에 정정 각주 추가(점유
  체크 자체는 하강 diff 재설계로 폐지됨, `chains:SetStrong` 순서
  버그만 지금도 유효).

doc-check.py ERROR 0 유지, WARN 101 그대로(전부 판단 필요한 기존
느슨한 인용, 이번 라운드가 새로 만든 건 없음). 5개 에이전트 전원
"설계 결함 0건" 보고로 수렴 판단.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
qwreey 2026-08-14 00:45:03 +09:00
parent 32e9db0773
commit 10cd31be2c
Signed by: qwreey
GPG key ID: D28DB79297A214BD
10 changed files with 28 additions and 14 deletions

View file

@ -62,6 +62,20 @@
나중에 UI를 덮어쓰는** 증상까지 간다는 게 실측으로 확인됨. 수정 나중에 UI를 덮어쓰는** 증상까지 간다는 게 실측으로 확인됨. 수정
(`SetStrong` hoist + no-op 점유 마커)이 옳았다는 결정적 근거. (`SetStrong` hoist + no-op 점유 마커)이 옳았다는 결정적 근거.
> **[2026-08-14 리뷰에서 정정] "no-op 점유 마커" 부분은 이후 폐기된
> 옛 모델 서술입니다.** `chains:SetStrong``process` 호출 *전에* 끝내야
> 한다는 순서 버그(위 표, `retractor` 유실/`STALE` 재현)는 하강 diff
> 재설계(2026-08-13 열네 번째 세션, `base/dispatch-core-plan.md`)에서도
> 그대로 유효 — 지금 `Dispatch.process`도 여전히 `chains:GetStrong`/
> `SetStrong`으로 list를 먼저 확보한 뒤에 `h.process`를 부름. 다만 "점유
> 마커"(같은 인덱스가 이미 점유돼 있으면 즉시 error)는 그 뒤 **Dispatch의
> 점유 체크 자체가 폐지**되며(`dispatch-core-plan.md` "Dispatch 체인" 절)
> 없어진 옛 개념 — 지금은 `list[index] = { handler = h, retractor = NOOP }`
> placeholder만 미리 박아두는 것으로 대체됨(같은 "호출 전 자리 확보"
> 목적, 에러를 내는 메커니즘은 아님). 이 스파이크는 `rewrite-required/`
> 이동돼 있고(`luau-test/STATUS.md`), 재작성 시 이 절의 "정상(수정본)"
> 시나리오도 새 모델(placeholder, 점유 에러 없음)로 갱신할 것.
## `07` — 스파이크를 보강해야 실제 검증이 됐음 ## `07` — 스파이크를 보강해야 실제 검증이 됐음
원래 3번 섹션이 "강하게 붙잡아둔 10개가 살아있는가"라는 **sanity check만** 원래 3번 섹션이 "강하게 붙잡아둔 10개가 살아있는가"라는 **sanity check만**

View file

@ -18,7 +18,7 @@ Slot과 맞물려서 잘 생각해서 구현해야 하는 부분. **기울어진
"Handler로 base를 받는" 게 아니라) — 이유: 여긴 가상돔이 없어서, base "Handler로 base를 받는" 게 아니라) — 이유: 여긴 가상돔이 없어서, base
쪽이 "누가 실제로 그려주는지" 모르는 채로 있다가 Roblox Handler를 주입받는 쪽이 "누가 실제로 그려주는지" 모르는 채로 있다가 Roblox Handler를 주입받는
모양이 더 자연스러워 보임. (이름 자체는 이후 "Handler"로 확정 — 모양이 더 자연스러워 보임. (이름 자체는 이후 "Handler"로 확정 —
`base/bind-system-plan.md`의 핸들러 계약 절 참고, 이 문서는 여전히 `base/dispatch-core-plan.md`의 핸들러 계약 절 참고, 이 문서는 여전히
초안 당시 표현인 "프로바이더"로 쓰여 있던 걸 정정.) 초안 당시 표현인 "프로바이더"로 쓰여 있던 걸 정정.)
## pluggable 플러그 초기화는 누구 몫인가 ## pluggable 플러그 초기화는 누구 몫인가

View file

@ -6,7 +6,7 @@
> 했고 결정은 하나도 안 바뀜.** > 했고 결정은 하나도 안 바뀜.**
**상태**: base — 확정. `Dispatch`/`Brand`와의 관계는 **상태**: base — 확정. `Dispatch`/`Brand`와의 관계는
`base/bind-system-plan.md`(디스패치 코어)와 `base/brand-plan.md` 참고. `base/dispatch-core-plan.md`(디스패치 코어)와 `base/brand-plan.md` 참고.
## Ref — 도입 확정, 단 용도는 재정의됨 ## Ref — 도입 확정, 단 용도는 재정의됨

View file

@ -490,7 +490,7 @@ nested `Slot`을 파괴 후 재사용하는 경로가 정확히 이 케이스).
체크로 이걸 막고, `process``old == slotValue` 체크로 대칭적으로 막음 체크로 이걸 막고, `process``old == slotValue` 체크로 대칭적으로 막음
— 둘 다 스킵돼야 완전한 no-op. — 둘 다 스킵돼야 완전한 no-op.
- Slot 핸들러 자신이 감시 중인 값(배열/스토어)이 바뀔 때 child를 갱신하는 - Slot 핸들러 자신이 감시 중인 값(배열/스토어)이 바뀔 때 child를 갱신하는
추적(구독)도 `base/bind-system-plan.md`가 말하는 "process 함수가 다른 값 추적(구독)도 `base/dispatch-core-plan.md`가 말하는 "process 함수가 다른 값
변경을 추적해도 됨" 범위에 속하고, `retract` 시점엔 그 추적만 풀면 됨 — 변경을 추적해도 됨" 범위에 속하고, `retract` 시점엔 그 추적만 풀면 됨 —
Destroy 시점엔 `retract`가 호출되지 않는다는 원칙(`base/lifecycle-pattern.md`)도 Destroy 시점엔 `retract`가 호출되지 않는다는 원칙(`base/lifecycle-pattern.md`)도
동일하게 적용. 동일하게 적용.
@ -1440,7 +1440,7 @@ end
``` ```
`recompute`가 owner가 Slot이면 그 `.Length`에도 합계를 반영하도록 `recompute`가 owner가 Slot이면 그 `.Length`에도 합계를 반영하도록
확장됐으므로(`base/bind-system-plan.md` 참고) — `Slot.Length`는 더 확장됐으므로(`base/dispatch-core-plan.md` 참고) — `Slot.Length`는 더
이상 raw 개수가 아니라 **"요소별 기여도의 합"**(plain=1, nested 이상 raw 개수가 아니라 **"요소별 기여도의 합"**(plain=1, nested
Slot=그 `.Length`)이 됨. plain 요소만 있는 흔한 경우엔 항상 합==개수라 Slot=그 `.Length`)이 됨. plain 요소만 있는 흔한 경우엔 항상 합==개수라
체감 차이 없음. 체감 차이 없음.
@ -1558,7 +1558,7 @@ GC가 전부 한 번에 정리하니 손 안 대도 됨 — 이 구분은 새
죽는 경우)를 만나서 드러난 것뿐. 죽는 경우)를 만나서 드러난 것뿐.
- **Length 변경은 정확히 offset 변경으로만 전파됨, 별도 채널 없음** - **Length 변경은 정확히 offset 변경으로만 전파됨, 별도 채널 없음**
수정된 `recompute`(`base/bind-system-plan.md` 참고)를 보면 `:Set()` 수정된 `recompute`(`base/dispatch-core-plan.md` 참고)를 보면 `:Set()`
호출되는 대상은 (a) 뒤 형제들의 offset, (b) owner가 Slot이면 그 호출되는 대상은 (a) 뒤 형제들의 offset, (b) owner가 Slot이면 그
`.Length` 딱 둘뿐. Length 값 자체는 읽히기만 함(`:Get()`/Observer `.Length` 딱 둘뿐. Length 값 자체는 읽히기만 함(`:Get()`/Observer
트리거) — (b)로 올라간 `.Length`도 한 단계 위에서는 그냥 또 다른 트리거) — (b)로 올라간 `.Length`도 한 단계 위에서는 그냥 또 다른

View file

@ -154,7 +154,7 @@ StoreBind가 State/Source 레이어를 전부 풀어낸 뒤의 값(그리고 이
`inst`가 죽으면 이 슬롯(엔진 Tween 객체+`Value` 포함)도 별도 정리 로직 `inst`가 죽으면 이 슬롯(엔진 Tween 객체+`Value` 포함)도 별도 정리 로직
없이 같이 GC됨. **[정정, 2026-08-12 열한 번째 세션, 2026-08-13 다섯 번째 없이 같이 GC됨. **[정정, 2026-08-12 열한 번째 세션, 2026-08-13 다섯 번째
세션에 클로저 반환 계약으로 서술 갱신]** `process`가 반환한 클로저는 store 세션에 클로저 반환 계약으로 서술 갱신]** `process`가 반환한 클로저는 store
재발행마다 항상 불리지만(`bind-system-plan.md` 일반 retract 계약 절 재발행마다 항상 불리지만(`dispatch-core-plan.md` 일반 retract 계약 절
정정분 — "거의 안 불림"이었던 원 서술은 틀렸음), PropertyHandler의 정정분 — "거의 안 불림"이었던 원 서술은 틀렸음), PropertyHandler의
그 클로저는 몸체가 no-op이라 실질적으로 하는 일이 없음 — 아래 절 참고. 그 클로저는 몸체가 no-op이라 실질적으로 하는 일이 없음 — 아래 절 참고.

View file

@ -83,7 +83,7 @@ Modifier 타입의 메소드 목록에 끼워 넣도록 챙기면 됨, 새로
### `v``nil`인 경우 — `process`가 직접 자식 제거, 반환 클로저는 관여 안 함 (2026-08-07 여덟 번째 세션) ### `v``nil`인 경우 — `process`가 직접 자식 제거, 반환 클로저는 관여 안 함 (2026-08-07 여덟 번째 세션)
`modifier-plan.md``None` 센티널(`base/bind-system-plan.md`의 `modifier-plan.md``None` 센티널(`base/dispatch-core-plan.md`의
`NoneHandler` 재귀 재디스패치 절 참고)이 최종적으로 이 Handler의 `NoneHandler` 재귀 재디스패치 절 참고)이 최종적으로 이 Handler의
`process(inst, k, nil)`을 호출하는 구체 사례 — 이 Handler에서 "`v`가 `process(inst, k, nil)`을 호출하는 구체 사례 — 이 Handler에서 "`v`가
`nil`"은 만들어둔 `_quad_corner`류 자식이 있으면 그냥 지우는 것으로 확정. `nil`"은 만들어둔 `_quad_corner`류 자식이 있으면 그냥 지우는 것으로 확정.
@ -93,7 +93,7 @@ Modifier 타입의 메소드 목록에 끼워 넣도록 챙기면 됨, 새로
- **이건 반환 클로저가 아니라 `process` 자신의 로직****[정정, 2026-08-12 - **이건 반환 클로저가 아니라 `process` 자신의 로직****[정정, 2026-08-12
열한 번째 세션, 2026-08-13 다섯 번째 세션에 클로저 반환 계약으로 서술 열한 번째 세션, 2026-08-13 다섯 번째 세션에 클로저 반환 계약으로 서술
갱신]** 그 클로저는 store 재발행마다 항상 불리지만(핸들러 타입이 안 갱신]** 그 클로저는 store 재발행마다 항상 불리지만(핸들러 타입이 안
바뀌어도, `bind-system-plan.md` 일반 retract 계약 절 정정분 참고), 이 바뀌어도, `dispatch-core-plan.md` 일반 retract 계약 절 정정분 참고), 이
Handler는 `process(inst,k,v,index)` 자체가 `v``nil`이든 숫자든 Handler는 `process(inst,k,v,index)` 자체가 `v``nil`이든 숫자든
전부 완결적으로 처리하므로(있으면 지우거나 만들거나) 반환 클로저가 할 일이 전부 완결적으로 처리하므로(있으면 지우거나 만들거나) 반환 클로저가 할 일이
없어 `function() end`이면 충분 — 일반 프로퍼티 핸들러가 no-op 클로저를 없어 `function() end`이면 충분 — 일반 프로퍼티 핸들러가 no-op 클로저를

View file

@ -86,7 +86,7 @@ ROADMAP 항목 근거인지, 어떻게 실행하는지, 실행 후 뭘 확인해
| `17-modifier-index-tableclone-chaining.luau` | Modifier의 제네릭 `__index`+`table.clone` 체이닝 — 임의 필드 이름에 대해 즉석 setter가 만들어지는지, `table.clone`이 메타테이블을 참조로 공유해 여러 단계 clone에서도 체이닝이 안 끊기는지, 원본이 mutate 안 되는지, 형제 분기끼리 오염 안 되는지 | `modifier-plan.md` "런타임은 클래스별 코드 없이 base에 딱 하나만 있으면 됨" 절 + "`table.clone`의 정확한 동작 — 확인됨" 절(2026-08-12 열일곱 번째 세션), `pre-implementation-audit.md` 1-11 | | `17-modifier-index-tableclone-chaining.luau` | Modifier의 제네릭 `__index`+`table.clone` 체이닝 — 임의 필드 이름에 대해 즉석 setter가 만들어지는지, `table.clone`이 메타테이블을 참조로 공유해 여러 단계 clone에서도 체이닝이 안 끊기는지, 원본이 mutate 안 되는지, 형제 분기끼리 오염 안 되는지 | `modifier-plan.md` "런타임은 클래스별 코드 없이 base에 딱 하나만 있으면 됨" 절 + "`table.clone`의 정확한 동작 — 확인됨" 절(2026-08-12 열일곱 번째 세션), `pre-implementation-audit.md` 1-11 |
| `18-relate-mutual-cycle-gc.luau` | **[2026-08-13 신규]** 서로 다른 두 `Relate`가 서로의 키를 상대방의 강한 값으로 제공하는 상호 순환은 Luau에 ephemeron이 없어 GC가 못 푼다는 주장(지금까지 공식 문서 인용으로만 뒷받침됨) — 음성 대조군(순환 재현)과 양성 대조군(한쪽을 weak-value로 낮추면 풀리는지) 둘 다 실측 | `relate-plan.md` "위험한 패턴" 절(2026-08-12 열세/열네 번째 세션), `slot-plan.md``kSlotMap`/`slotOwner`/`elementOwner` 실사례 | | `18-relate-mutual-cycle-gc.luau` | **[2026-08-13 신규]** 서로 다른 두 `Relate`가 서로의 키를 상대방의 강한 값으로 제공하는 상호 순환은 Luau에 ephemeron이 없어 GC가 못 푼다는 주장(지금까지 공식 문서 인용으로만 뒷받침됨) — 음성 대조군(순환 재현)과 양성 대조군(한쪽을 weak-value로 낮추면 풀리는지) 둘 다 실측 | `relate-plan.md` "위험한 패턴" 절(2026-08-12 열세/열네 번째 세션), `slot-plan.md``kSlotMap`/`slotOwner`/`elementOwner` 실사례 |
| `19-ownership-refcount-relate-patterns.luau` | **[2026-08-13 신규, 같은 날 B/C 전면 재작성 — 지금은 현행 설계 기준]** 세 소유권/참조카운트 알고리즘 검증. **A**: Tag `tagNameMap` 참조 카운트(여러 위치가 같은 이름을 겹쳐 가져도 마지막 홀더가 빠질 때만 실제 `RemoveTag`. 옛 `kTagMap`은 클로저 캡처로 대체돼 삭제됨). **B**: Attribute 이름 소유권 — 공개 `AttributeKey(name)` 캐시 + `Dispatch.process`의 인덱스 1 **점유 체크**가 충돌을 잡는지(옛 `rawNew`+`owners` 수동 레지스트리는 폐기). **C**: Slot 소유권 — nested 엄격 `claimOwner`(같은 owner 재클레임도 error) vs top-level `claimOwnerAt(inst,k)`(정확히 같은 자리 재발행만 no-op). **셋 다 음성 대조군 포함** — 옛 로직이 `Slot{a,a}`/`Frame{slot,slot}`을 조용히 통과시키는 걸 재현. **[2026-08-13 열네 번째 세션] 0-Z가 확정되며 B 섹션이 낡음 → `rewrite-required/`** — 이제 "그룹 전용 키 + `AttributeKeyHandler`의 이름 claim"을 검증해야 함(A/C는 그대로 유효) | `tag-plan.md` "메커니즘", `attribute-plan.md` "이름 소유권", `slot-plan.md` "요소 소유권" | | `19-ownership-refcount-relate-patterns.luau` | **[2026-08-13 신규, 같은 날 B/C 전면 재작성 — 지금은 현행 설계 기준]** 세 소유권/참조카운트 알고리즘 검증. **A**: Tag `tagNameMap` 참조 카운트(여러 위치가 같은 이름을 겹쳐 가져도 마지막 홀더가 빠질 때만 실제 `RemoveTag`. 옛 `kTagMap`은 클로저 캡처로 대체돼 삭제됨). **B**: Attribute 이름 소유권 — 공개 `AttributeKey(name)` 캐시 + `Dispatch.process`의 인덱스 1 **점유 체크**가 충돌을 잡는지(옛 `rawNew`+`owners` 수동 레지스트리는 폐기). **C**: Slot 소유권 — nested 엄격 `claimOwner`(같은 owner 재클레임도 error) vs top-level `claimOwnerAt(inst,k)`(정확히 같은 자리 재발행만 no-op). **셋 다 음성 대조군 포함** — 옛 로직이 `Slot{a,a}`/`Frame{slot,slot}`을 조용히 통과시키는 걸 재현. **[2026-08-13 열네 번째 세션] 0-Z가 확정되며 B 섹션이 낡음 → `rewrite-required/`** — 이제 "그룹 전용 키 + `AttributeKeyHandler`의 이름 claim"을 검증해야 함(A/C는 그대로 유효) | `tag-plan.md` "메커니즘", `attribute-plan.md` "이름 소유권", `slot-plan.md` "요소 소유권" |
| `20-slot-splice-index-arithmetic.luau` | **[2026-08-13 신규]** `Slot:Splice(index, removeCount, ...newElements)`의 shift+recompute 1회 계산이, `Extract`/`Add` 반복으로 재현한 참조 구현과 항상 같은 결과를 내는지 — 제거/삽입 길이가 다를 때(delta 양수/음수) 뒤 요소가 밀리는 방향과 양을 헷갈리는 off-by-one 위험(이 프로젝트가 `Dispatch.recompute`에서 실제로 냈던 것과 같은 클래스의 버그)을 경계값 케이스로 검증 | `slot-plan.md` "확정" CRUD 표 + "`Splice` 신설" 절(2026-08-12 열다섯 번째 세션), `bind-system-plan.md`의 `recompute` off-by-one 수정 사례(2026-08-11 여섯 번째 세션) | | `20-slot-splice-index-arithmetic.luau` | **[2026-08-13 신규]** `Slot:Splice(index, removeCount, ...newElements)`의 shift+recompute 1회 계산이, `Extract`/`Add` 반복으로 재현한 참조 구현과 항상 같은 결과를 내는지 — 제거/삽입 길이가 다를 때(delta 양수/음수) 뒤 요소가 밀리는 방향과 양을 헷갈리는 off-by-one 위험(이 프로젝트가 `Dispatch.recompute`에서 실제로 냈던 것과 같은 클래스의 버그)을 경계값 케이스로 검증 | `slot-plan.md` "확정" CRUD 표 + "`Splice` 신설" 절(2026-08-12 열다섯 번째 세션), `dispatch-core-plan.md`의 `recompute` off-by-one 수정 사례(2026-08-11 여섯 번째 세션) |
## 공통 유틸리티 ## 공통 유틸리티

View file

@ -8,7 +8,7 @@
사용자가 직접 찾아낸 버그: None으로 소진하면 그 슬롯이 영원히 사용자가 직접 찾아낸 버그: None으로 소진하면 그 슬롯이 영원히
non-nil로 남아있어서, `:Wait()`/`:Callback()`가 반복 호출될 때마다 non-nil로 남아있어서, `:Wait()`/`:Callback()`가 반복 호출될 때마다
배열이 끝없이 길어지는(예전 소진 슬롯을 재사용 못 하는) 진짜 버그였음. 배열이 끝없이 길어지는(예전 소진 슬롯을 재사용 못 하는) 진짜 버그였음.
.claude/base/bind-system-plan.md "왜 None이 아니라 nil인가" 절(2026-08-09 .claude/base/ref-plan.md "왜 `None`이 아니라 `nil`인가" 절(2026-08-09
열한 번째 세션, 최종 정정)이 최신 소스 — 결론은 두 패턴이 서로 다른 열한 번째 세션, 최종 정정)이 최신 소스 — 결론은 두 패턴이 서로 다른
문제를 풀고 있었다는 것: 문제를 풀고 있었다는 것:
@ -164,7 +164,7 @@ end
작은 값 유지)? 작은 값 유지)?
3. Part B-2 — None+table.insert 방식은 실제로 1000까지 자라는가 — 3. Part B-2 — None+table.insert 방식은 실제로 1000까지 자라는가 —
이게 바로 사용자가 찾아낸 "무한 성장" 버그의 정량적 재현. 이 결과가 이게 바로 사용자가 찾아낸 "무한 성장" 버그의 정량적 재현. 이 결과가
기대와 다르면(예: 실제로는 안 자란다면) bind-system-plan.md의 정정 기대와 다르면(예: 실제로는 안 자란다면) ref-plan.md의 정정
근거 자체를 재검토해야 하니 반드시 알려줄 것. 근거 자체를 재검토해야 하니 반드시 알려줄 것.
4. Part B-3 — 새 등록이 소진된 빈 슬롯(index=idx2)을 실제로 재사용하는가 4. Part B-3 — 새 등록이 소진된 빈 슬롯(index=idx2)을 실제로 재사용하는가
— 이게 "table.insert 대신 선형 탐색 재사용 등록 함수"가 실제로 — 이게 "table.insert 대신 선형 탐색 재사용 등록 함수"가 실제로

View file

@ -1,6 +1,6 @@
--[[ --[[
검증 대상: process(inst,k,v)/retract(inst,k,v) 기반 재귀 재-dispatch 검증 대상: process(inst,k,v)/retract(inst,k,v) 기반 재귀 재-dispatch
모델(.claude/base/bind-system-plan.md "확정된 디스패치 모델" 절)이 실제 모델(.claude/base/dispatch-core-plan.md "확정된 디스패치 모델" 절)이 실제
Luau 함수 재귀로 자연스럽게 짜이는지, 우선순위 스캔(isHandlable)이 Luau 함수 재귀로 자연스럽게 짜이는지, 우선순위 스캔(isHandlable)이
기대대로 동작하는지에 대한 최소 스파이크. 기대대로 동작하는지에 대한 최소 스파이크.

View file

@ -95,7 +95,7 @@ Luau 코드로 부딪혀본 적 없는 세 가지**를 던지는 코드로 검
(아래 항목) / `Dispatch.addHandler(handler)`(레지스트리 등록, (아래 항목) / `Dispatch.addHandler(handler)`(레지스트리 등록,
quad-roblox가 팩토리 뮤테이션 시점에 호출) / `Dispatch.drive(inst, quad-roblox가 팩토리 뮤테이션 시점에 호출) / `Dispatch.drive(inst,
flattened)`(배열→해시 두 패스 순회하며 각 `(k,v)` flattened)`(배열→해시 두 패스 순회하며 각 `(k,v)`
`Dispatch.process(inst,k,v,1)` 호출 — `bind-system-plan.md`의 `None` `Dispatch.process(inst,k,v,1)` 호출 — `dispatch-core-plan.md`의 `None`
센티널 절, 2026-08-07 여덟 번째 세션에 네이밍 확정). 센티널 절, 2026-08-07 여덟 번째 세션에 네이밍 확정).
**[2026-08-13 다섯 번째 세션 전면 재설계]** "이전 담당자와 다르면 **[2026-08-13 다섯 번째 세션 전면 재설계]** "이전 담당자와 다르면
`retract`"라는 옛 diff 모델은 폐기 — 정리 책임은 전적으로 `retract`"라는 옛 diff 모델은 폐기 — 정리 책임은 전적으로
@ -509,7 +509,7 @@ Luau 코드로 부딪혀본 적 없는 세 가지**를 던지는 코드로 검
- [ ] 인라인 키/setter로 modifier 필드를 명시적으로 지우는 `None` 센티널 - [ ] 인라인 키/setter로 modifier 필드를 명시적으로 지우는 `None` 센티널
(이름 확정, `modifier-plan.md` 2-1번, `Peek` 반환 타입에 `None` 추가) + (이름 확정, `modifier-plan.md` 2-1번, `Peek` 반환 타입에 `None` 추가) +
이를 `nil`로 재디스패치하는 base 내장 `NoneHandler` 이를 `nil`로 재디스패치하는 base 내장 `NoneHandler`
(`bind-system-plan.md`의 `None` 센티널 절, M2 dispatch 엔진의 (`dispatch-core-plan.md`의 `None` 센티널 절, M2 dispatch 엔진의
"이전 매치 핸들러 추적" 항목과 함께 구현 — `StoreBind` 핸들러와 "이전 매치 핸들러 추적" 항목과 함께 구현 — `StoreBind` 핸들러와
동일한 재귀 재디스패치 패턴이라 새 메커니즘 아님) — `None` 센티널 동일한 재귀 재디스패치 패턴이라 새 메커니즘 아님) — `None` 센티널
자체는 확정 완료. **[2026-08-13 열네 번째 세션 갱신]** `NoneHandler` 자체는 확정 완료. **[2026-08-13 열네 번째 세션 갱신]** `NoneHandler`