design: QA 4라운드 3차 회신 반영 + attachSlot 분해 논의 자료 준비
확인받은 것 반영: - Owned 설치 플래그 확정 — "누가 요소를 만들었는가"는 사이클마다 달라지는 게 아니라 설치 시점 속성이라는 사용자 판단. Detach(사이클 판단)와 직교하는 축이므로 반환값 계열에 unowned 센티널을 더 만들지 않는다. destroySlotTree/ dispose도 이 플래그를 봐야 해서 클로저가 아닌 Slot 필드여야 함. - props 순회를 일반화 for 한 번으로 정정 — flattened는 항상 평범한 Luau 테이블(__pairs/__ipairs를 갈아끼운 ud가 들어올 경로가 없음)이라 옛 근거 "다른 백엔드가 Lua 테이블이 아닌 자료구조로"는 inst엔 해당해도 flattened엔 해당하지 않았다. 계약(배열 먼저)은 그대로, 구현만 1회 순회로. 스파이크 01은 두 루프 버전이라 재작성 필요로 STATUS에 표시. attachSlot 분해는 research/slot-attach-decomposition.md로 준비: setLength를 flush 앞/뒤 어디에 둘지가 안 풀린 이유가 자리 선택이 아니라 "한 함수가 책임 일곱을 지고 있어서"라는 사용자 진단에 따라, 책임 목록과 순서 제약(전부 RC-1/RC-3/RC-4 등 실제로 밟은 버그가 출처)을 모으고 C6(길이 최종값은 flush 뒤에야 정해짐)와 C7(부기가 물리보다 먼저)이 단일 함수로는 동시 만족 불가능함을 보인 뒤 분해 후보 넷을 대조. 확정은 아무것도 안 함. Co-authored-by: qwreey <me@qwreey.moe>
This commit is contained in:
parent
f36bdcbe5d
commit
1e6cb0111d
8 changed files with 380 additions and 16 deletions
|
|
@ -90,6 +90,7 @@
|
|||
| `framework-comparison-findings.md` | quad vs Fusion/Vide/react-lua 정직한 비교(실 소스 근거) — quad 강점, 못 고치는 트레이드오프(의도된 설계) 정리. **[2026-08-12 열여덟 번째 세션]** "고칠 만한 것" 절에 남아있던 마지막 두 항목(use-after-destroy 검증 안전망 부재, `:With` 동적 의존성 미지원)도 사용자가 "고칠 필요 없음"으로 최종 판단해 3번 절(못 고치는 트레이드오프)로 이전 — 2번 절은 이제 해소된 항목만 남음 | 하 — 배경 자료, 더 이상 사용자 판단 대기 상태 아님 |
|
||||
| `additional-primitives-plan.md` | **[2026-08-09 세 번째 세션, 전부 해소]** 마지막으로 남아있던 키 기반 동적 컬렉션 재조정도 `Slot:List(...)`로 확정되어 `base/slot-plan.md`로 승격 — 이 문서엔 새로 열린 설계 질문 없음, "빈 자리 아닌 것"/"문서화 백로그"/조사 소스 목록만 배경 자료로 유지 | 하 — 배경 리서치 기록용, 열린 결정 없음 |
|
||||
| `pre-implementation-audit.md` | M0 착수 직전 크리티컬 감사(2026-08-06 신설) — `base/` 전체를 모호성/지연결정리스크/단순화후보 세 렌즈로 재검토, 11개 우선순위1 + 11개 우선순위2 + 2개 단순화후보. **[2026-08-12 열일곱 번째 세션]** 우선순위1 11개 전원 해소 — 남은 건 `.claude/luau-test/` 스파이크 실측 확인뿐 | 상 — 설계는 전부 해소, `.claude/luau-test/` 스파이크 실측만 남음 |
|
||||
| `slot-attach-decomposition.md` | **[2026-08-21 신설]** `attachSlot`의 책임 분해 **논의 준비 자료**(확정 아님). QA 4라운드 `F-4-3`에서 `Dispatch.setLength`를 flush 앞/뒤 어디에 둘지가 갈렸는데, 사용자가 그건 자리 선택 문제가 아니라 *"attachSlot 의 기능이 너무 다양해진게 문제"* 라고 짚어 확장 논의로 넘어간 것. 지금 `attachSlot`이 지고 있는 책임 일곱(부모 등록 offset/length, `:List` 실체화, 마운트 상태 전이, 배치 게이팅, 자식 배치, 재귀)과 순서 제약 일곱(각각 `RC-1`/`RC-3`/`RC-4` 등 실제로 밟은 버그 출처까지)을 모아두고, **C6(길이 최종값은 flush 뒤에야 정해짐)와 C7(부기가 물리보다 먼저)이 단일 함수로는 동시 만족 불가능**함을 보인 뒤 분해 후보 넷(현행 유지 / `prepare`+`mount` 2단 / 3단 / 문서만)을 대조. 정본은 여전히 `base/slot-plan.md` | 중 — M2/M3는 안 막지만 **M6(`:List`) 구현 전엔 정해두는 게 나음**. 선행으로 `qa-request/pre-implementation-qa-round4-followup.md`의 `F-3`(`Detach` 보관 위치 + `KeyGone`)이 먼저 닫히면 요구사항이 완전해짐 |
|
||||
| `operator-sugar-plan.md` | **[2026-08-12 신설]** `Sum`/`Product`/`Not`/비트연산 등 `:Compute`/`:Apply`용 연산자 콤비네이터 슈가 — 메커니즘은 이미 확정된 계약(`Animate`와 동형 패턴) 재사용이라 확정, 네임스페이스 이름만 미정. **[2026-08-12 열아홉 번째 세션]** 서브 에이전트 외부 리서치로 다른 리액티브 라이브러리 선례와 대조 — `Operator`가 가장 강한 선례(Python `operator` 모듈), `Clamp`/`Min`/`Max`가 추가 후보로 부상, 비트연산·비교연산자·`Sub`/`Div`는 선례 전무로 드랍 후보, Debounce/Throttle은 `Blocker`와 다른 시간 기반 메커니즘이라 별도 질문으로 분리(**[2026-08-19]** 그 질문은 `base/debounce-throttle-plan.md`로 전부 해소·승격 완료), `Filtered`의 Slot 안/밖 구분 판단이 ReactiveUI/SolidJS 선례로 뒷받침됨 — 최종 이름 결정은 여전히 사용자 몫. **[2026-08-13 세션, 두 번째]** Haskell 비교 리서치 중 `Alternative`(nil 대체값, coalesce류) 후보 신설 — 카탈로그 확정 규칙에 그대로 맞음, 이전엔 없던 게 확인됨 | 하 — 구현은 맨 마지막(순수 슈가, 없어도 무방, 함수 간 의존 없음), 사용자가 직접 후순위 지정 **[2026-08-13 여섯 번째 세션]** `State<State<T>|T>` → `State<T>` 평탄화 항목 신설(백로그) — `State<State<T>>`가 정상 동작하게 됐지만 `retractFrom`의 힌트가 직속 1단계에만 가서 깊은 중첩에선 깜빡임 방지가 꺼진다는 게 구체적 동기, 사용자 판단으로 "UB는 아니지만 원치 않는 방향". `Operator.*`가 아니라 `state:Flatten()` 메소드로 제공하는 게 맞아 보이며, **반환 노드가 동적 의존성을 갖는다는 난점**(quad가 의도적으로 비지원하기로 한 바로 그것)이 확정 전 최대 쟁점 |
|
||||
| `quad-recursive-acronym.md` | **[2026-08-14 신설]** GNU/WINE류로 `Quad`를 재귀 약어화하는 카피 브레인스토밍 — 설계 결정도 착수 게이팅도 아니고 나중에 README.md 헤딩 등에 쓸 캐치프레이즈 후보 모음. 자학 개그 방향(기각)과 지연평가/재귀·커링/펑터/클로저를 자랑하는 방향(채택 후보, 미확정) 정리 | 하 — 카피 소재, 설계 상의 필요 없음. 사용자가 최종 문구 고르면 반영 |
|
||||
| `v1-compat-plan.md` | v1 하위호환(compat) 레이어 — `quad-roblox-v1-compat` 패키지, v2→v1 단방향 브리지(`state:Observer()`+v1 프로퍼티 재대입), v2-in-v1/v1-in-v2 두 임베딩 방향의 기술 규칙까지 확정. quad2-try의 `quad-compat`은 빈 폴더로 실제 시도된 적 없었음을 확인 | 하 — Slot이 foreign Instance를 어떻게 다루는지만 Slot 코어 구현 시점까지 미결 |
|
||||
|
|
|
|||
|
|
@ -384,15 +384,40 @@ retract 클로저를 반환하는 1-메소드 계약으로 합쳐짐 — 이 절
|
|||
테이블을 `pairs`/제네릭 `for`로 순회하면 실제로 배열 파트가 해시 파트보다
|
||||
먼저 나옴(`for i, v in {a=1, 2, b=3} do print(i,v) end` → `1 2`, `a 1`,
|
||||
`b 3` 순서 — 사용자가 직접 확인). 이 관찰된 동작에 그냥 얹혀가지 않고,
|
||||
**base 드라이버가 명시적으로 두 패스로 나눠 돌기로 계약화**한다 — 숫자
|
||||
키(children)를 먼저 index 순서대로 처리하고, 그 다음 나머지 키를 처리.
|
||||
이유: (1) 다른 백엔드(`quad-web` 등)가 병합된 props를 Lua 테이블이 아닌
|
||||
다른 자료구조로 표현할 수도 있어서 "Lua 테이블의 우연한 내부 동작"에
|
||||
기대면 이식성이 깨짐, (2) 어차피 숫자 키(children/Ref)와 문자열
|
||||
키(프로퍼티/이벤트)를 다른 의미로 취급해야 하니 구분 비용이 이미 드는
|
||||
참에 순서까지 명시적으로 고정하는 게 거의 공짜. **결과적으로 배열
|
||||
슬롯에 놓인 어떤 값이든 모든 프로퍼티/이벤트 세팅보다 항상
|
||||
먼저 처리된다는 게 base 자체의 보장**이 됨.
|
||||
**base 드라이버가 이 순서를 계약으로 보장**한다 — 배열 슬롯에 놓인 어떤
|
||||
값이든 모든 프로퍼티/이벤트 세팅보다 항상 먼저 처리된다.
|
||||
|
||||
**⚠️ [구현 방식 정정, 2026-08-21 구현 전 QA 4라운드 `F-4-1`] "계약으로
|
||||
보장한다"와 "루프를 두 번 돈다"는 다른 얘기다 — 실제 구현은 일반화 `for`
|
||||
**한 번**이다.** 옛 서술은 "명시적으로 두 패스로 나눠 돌기로 계약화"라고
|
||||
적어 **구현까지 2회 순회로 못박은 것처럼** 읽혔고, 실제로 M0 스파이크
|
||||
`01`도 숫자 `for` + 일반화 `for` 두 루프로 짜여 있었다. 사용자 판정으로
|
||||
단일 순회로 정정: *"ipairs, pairs 를 따로 사용하게 되는게 아닌 단순
|
||||
일반화 for 로써 얻어지는게 맞는 상태라면, 맞는 구현이다 … `__pairs`/
|
||||
`__ipairs` 직접 구현체를 담은 ud 등을 받는 `flattened` 는 없고, luau
|
||||
테이블만 사용하는게 맞음."*
|
||||
- **`flattened`는 항상 평범한 Luau 테이블이다** — props는 사용자가 쓴
|
||||
Lua 테이블 리터럴에서 오고, `flatten`도 그걸 제자리에서 뮤테이션할 뿐
|
||||
(`base/modifier-plan.md`의 "flatten의 정확한 형태" 절). 메타테이블로
|
||||
순회를 갈아끼운 userdata 같은 게 들어올 경로가 **없다.**
|
||||
- **그래서 일반화 `for k, v in flattened do`가 배열 → 해시 순서를 그대로
|
||||
준다** — 두 층위는 `type(k) == "number"`로 가르면 된다. 순회 1회 절약.
|
||||
- **옛 근거 (1)은 과했다** — "다른 백엔드가 props를 Lua 테이블이 아닌
|
||||
자료구조로 표현할 수도"는 **`inst`에는 해당해도 `flattened`에는 해당하지
|
||||
않는다**(백엔드가 뭐든 사용자는 Luau로 props를 쓴다). 근거 (2)(숫자/문자열
|
||||
키를 어차피 다르게 취급해야 함)는 그대로 유효하고, 그건 **한 루프 안의
|
||||
분기**로 충분하다.
|
||||
- **계약 자체는 안 바뀐다** — "배열 파트 전체가 해시 파트보다 먼저"는 여전히
|
||||
base가 보장하는 것이고, 백엔드가 자기 드라이버를 짜더라도 지켜야 한다.
|
||||
바뀐 건 quad-base 자신의 구현이 그 보장을 **몇 번의 순회로 얻는가**뿐.
|
||||
- **`nil`-hole 위험은 어느 방식이든 동일** — 구멍이 생기면 숫자 키 일부가
|
||||
해시 파트로 밀려 순서가 섞이는데, `#flattened`를 쓰던 옛 방식도 똑같이
|
||||
깨진다. 방어는 그대로 `02`/`06` 스파이크의 nil-hole 규율(`None` 관용구)에
|
||||
맡긴다.
|
||||
- **⚠️ 스파이크 `01` 재작성 필요** — `luau-test/done/01-two-pass-array-hash-order.luau`가
|
||||
두 루프 버전이라 지금 계약의 구현과 안 맞는다. 단일 일반화 `for` 버전으로
|
||||
재작성하면서 **"배열 파트 전체가 해시보다 먼저 + 배열 안에서는 index
|
||||
순서"** 를 그대로 확인할 것.
|
||||
**[정정, 2026-08-18 구현 전 QA] `PreRef`/`PostRef`는 이 보장 위에서
|
||||
성립하는 게 아니다** — 옛 서술은 `ref-plan.md`의 "PreRef" 절이 "이 보장
|
||||
위에서 성립"한다고 적었는데, 실제로는 **두 패스 순회보다 더 위의 별도
|
||||
|
|
|
|||
|
|
@ -1320,6 +1320,57 @@ return Detach, { old = prev, source = ... }
|
|||
지금은 `:List` reconcile 한 곳에서만 쓰이지만 `None`도 처음엔 그렇게
|
||||
시작해 이후 재사용됐으므로 최상위에 두는 게 자연스럽다. 정확한 파일
|
||||
배치는 M6 구현 시점에 확정.
|
||||
### ⭐ 소유권은 설치 시점에 정해진다 — `Owned` 옵션 (2026-08-21 구현 전 QA 4라운드 확정)
|
||||
|
||||
위 표("`nil` → 파괴")는 **`:List`가 그 요소를 만든 경우**를 전제한다. 그런데
|
||||
`Slot:Add(state)` 라 sugar(`:Single` + 기본 identity `updateFn`, 아래 "반응형
|
||||
raw 요소" 절)로 들어온 요소는 **사용자가 `state`에 담아 넘긴 것**이라 Slot이
|
||||
죽이면 안 된다 — `state<Frame>` 교체가 이전 값을 파괴하지 않는다는 확정
|
||||
의미론(아래 "`State<Slot>` 교체는 파괴가 아니라 언마운트" 절)과 정면으로
|
||||
부딪히기 때문. 두 답을 다 만족시키는 축이 **"누가 그 요소를 만들었는가"**이고,
|
||||
그건 사이클마다 달라지는 게 아니라 **설치 시점에 고정되는 속성**이다
|
||||
(**사용자 확정, 2026-08-21**: *"unowned 로 나오는 경우가 state<Frame> 등을
|
||||
주는 경우이므로 설치 시점이다에 동의함"*).
|
||||
|
||||
```lua
|
||||
Slot:List(data, updateFn, keyFn?, opts?) -- opts.Owned: boolean? (기본 true)
|
||||
Slot:Single(state, updateFn?, opts?)
|
||||
```
|
||||
|
||||
| | `Owned = true`(기본) | `Owned = false` |
|
||||
|---|---|---|
|
||||
| 누가 요소를 만드나 | `updateFn`(= `:List` 소유) | 사용자(`state`에 담아 넘김) |
|
||||
| `updateFn`이 새 값 반환 | 밀려난 `prev` **파괴** | **언마운트만** |
|
||||
| `updateFn`이 `nil`/`None` 반환 | **파괴** | **언마운트만** |
|
||||
| 키가 데이터에서 사라짐 | **파괴** | **언마운트만** |
|
||||
| owner가 죽음(`destroySlotTree`/`dispose`) | **파괴**(재귀) | **언마운트만** |
|
||||
| `Slot:Add(state)` sugar | — | **이걸로 설치됨** |
|
||||
|
||||
- **`Detach`와는 직교하는 축이다** — `Detach`는 "지금은 안 쓰지만 **내 것**"
|
||||
(사이클마다 달라지는 판단), `Owned = false`는 "**애초에 내 것이 아님**"
|
||||
(설치 시점에 고정). 그래서 반환값 계열에 "unowned replace" 센티널을 하나 더
|
||||
만들지 않는다 — 만들면 두 의미가 한 이름에 섞인다(사용자 지적: *"의미론이
|
||||
분화한다"*).
|
||||
- **`destroySlotTree`/`dispose`도 이 플래그를 봐야 한다** — `Owned = false`인
|
||||
Slot을 파괴할 땐 자기 요소를 죽이지 않고 언마운트만 한다. 그래서 플래그는
|
||||
클로저 업밸류가 아니라 **Slot 필드**(`slot._owned`)여야 파괴 walk가 읽을 수
|
||||
있다.
|
||||
- **수동 CRUD와 안 부딪힌다** — 이 플래그는 `:List`/`:Single` 설치 시에만
|
||||
생기고, 그 Slot은 `_listed`라 수동 CRUD가 이미 막혀 있다(위 "`_crudUsed` ↔
|
||||
`_listed` 대칭").
|
||||
- **혼합은 표현 못 함** — 한 리스트 안에 "내가 만든 것"과 "사용자 것"이 섞이는
|
||||
경우는 이 플래그로 못 가른다. 실사용 사례가 안 보여 지금은 안 다루고,
|
||||
필요해지면 `Detach` + 수동 관리로 우회할 수 있다.
|
||||
- **⚠️ 이름은 `Owned`로 잠정** — `elementOwner`/`claimOwner`/`releaseOwner`와
|
||||
같은 뿌리라 골랐다. 다른 가칭들과 함께 용어 정리 대기열(`question.md` 1번).
|
||||
|
||||
**⚠️ 아직 안 닫힌 짝 항목** — `Detach`로 홀드된 요소를 **어디에 보관하고
|
||||
언제 파괴하는지**(`slot._detached` 필드 + owner 죽을 때 `Effect`로 정리)와
|
||||
`KeyGone` 센티널은 별개로 확인 대기 중이다.
|
||||
`qa-request/pre-implementation-qa-round4-followup.md`의 `F-3` 절이 소스 —
|
||||
**그게 닫히기 전엔 이 절의 "파괴" 칸을 구현하지 말 것**(무엇을 파괴 대상으로
|
||||
훑을지가 거기서 정해짐).
|
||||
|
||||
- **`Slot`의 다른 비파괴 API와의 관계**: `Extract`/`ExtractAll`/`Splice`가
|
||||
이미 비파괴 추출을 제공하지만(위 "CRUD API 확정" 절) 그건 **호출자가
|
||||
직접 부르는 명령형 경로**다. `Detach`는 같은 일을 **reconcile 안에서
|
||||
|
|
@ -1516,6 +1567,16 @@ nil/None 금지)는 그대로.
|
|||
|
||||
### 재귀 메커니즘 — 새 프리미티브 없이 `Dispatch.setLength`/`setOffsetSource`를 Slot 자신 키로 재사용
|
||||
|
||||
> **🔭 [2026-08-21] `attachSlot`의 책임 분해가 확장 논의 대기 중** — 아래
|
||||
> 의사코드가 지금 정본이고 그대로 유효하지만, 이 함수 하나가 지고 있는 책임이
|
||||
> 일곱 개(부모 등록 offset/length, `:List` 실체화, 마운트 상태 전이, 배치
|
||||
> 게이팅, 자식 배치, 재귀)라는 사용자 지적이 있었다 — *"attachSlot 의 기능이
|
||||
> 너무 다양해진게 문제같음"*. 특히 **"부모에게 알리는 길이의 최종값은 flush가
|
||||
> 끝나야 정해진다"와 "부기가 물리 조작보다 먼저"가 단일 함수로는 동시에
|
||||
> 만족되지 않는다.** 책임 목록·순서 제약의 출처·분해 후보는
|
||||
> `research/slot-attach-decomposition.md`에 정리해뒀다. **M2/M3를 막지는
|
||||
> 않지만 M6(`:List`) 구현 전엔 결론이 나 있어야 한다.**
|
||||
|
||||
**✅ [해결, 2026-08-18 구현 전 QA 2라운드 후속]** 아래가 재사용하는
|
||||
`Dispatch.setLength`/`setOffsetSource`/`recompute`가 배치 등록 중 크래시할
|
||||
수 있던 문제(`RC-1`)는 해결됨 — `base/dispatch-core-plan.md`의
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@
|
|||
|
||||
| 파일 | 확인된 것 |
|
||||
|---|---|
|
||||
| `01-two-pass-array-hash-order` | 배열 파트 전체 → 해시 파트 순. `Dispatch.drive` 두 패스 계약과 `PreRef` 호이스팅의 전제 |
|
||||
| `01-two-pass-array-hash-order` | 배열 파트 전체 → 해시 파트 순. `Dispatch.drive`의 순서 계약과 `PreRef` 호이스팅의 전제. **⚠️ [2026-08-21 재작성 필요]** 이 스파이크는 숫자 `for` + 일반화 `for` **두 루프** 버전인데, 구현이 **단일 일반화 `for`**로 정정됨(`base/dispatch-core-plan.md`의 "props 순회 순서" 절, QA 4라운드 `F-4-1`) — 검증 대상(순서 계약)은 그대로이고 스파이크 코드만 그 형태로 다시 쓸 것 |
|
||||
| `02-none-sentinel-vs-nil-holes` | `nil` 소진 시 `#t` 50→49로 무너짐 / `None`은 항상 50. 반대로 Ref 콜백 배열은 `None` 쓰면 죽은 슬롯 1000개 잔존 — **두 배열의 규칙이 서로 반대여야 함**이 정량 확인 |
|
||||
| `03-recursive-store-bind-dispatch` | StoreBind 재귀 재-dispatch, `None`→`nil` 흐름, 무한재귀 없이 종료 |
|
||||
| `05-store-state-diamond-propagation` | **[2026-08-19 재작성]** emit은 자기 invalid 상태와 무관하게 항상 전파(다이아몬드 두 경로 모두 끝까지 도달), 재계산은 `:Get()` 시점 캐시로 1회만, `:Get()`을 안 부르는 Observer는 source 변경마다 경로 수만큼(2) 계속 발화 — 옛(역전된) 모델이면 2번째 변경부터 침묵해야 하는데 안 그럼을 확인 |
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
# 구현 전 QA **4라운드 followup** — 회신 처리 결과 + 재질문
|
||||
|
||||
**상태**: **[2026-08-21] 2차 처리 완료 — 아래 F절이 최신.**
|
||||
**상태**: **[2026-08-21] 3차 처리 완료 — 아래 G절이 최신.**
|
||||
`F-4`는 전부 닫혔고(`F-4-3`은 `research/slot-attach-decomposition.md`로 넘어감),
|
||||
**남은 건 `F-3`의 (1)~(3)과 "+"뿐**(요약은 `G-3`).
|
||||
|
||||
**[2026-08-21] 2차 처리 — 아래 F절.**
|
||||
B절은 전부 확인됐고 C절 결정도 대부분 반영됐다. **지금 열려 있는 건 F절의
|
||||
`F-3`(`KeyGone`/`Owned` 설계 제안에 대한 확인)과 `F-4`(새로 발견한 불일치
|
||||
2건 + `setLength` 위치 재질문)뿐이다.** A~E절은 그 처리 과정의 기록.
|
||||
|
|
@ -993,3 +997,75 @@ if input[key] ~= nil then continue end -- "이미 있으면 건너뛴다" =
|
|||
|
||||
**안 고친 것**: `C-1`/`C-2`(F-3 동의 대기), `C-5`(F-4-3 판단 대기),
|
||||
`B-7`의 base 표현(`ownerKey` vs "owner" — F-3/F-4가 정리되면 같이).
|
||||
|
||||
---
|
||||
|
||||
# G. 3차 회신 처리 (2026-08-21)
|
||||
|
||||
## G-1. 반영 완료
|
||||
|
||||
| 항목 | 회신 | 반영 |
|
||||
|---|---|---|
|
||||
| `F-3` (4) | **확인** — *"unowned 로 나오는 경우가 state\<Frame\> 등을 주는 경우이므로 설치 시점이다에 동의함"* | `slot-plan.md`에 **"소유권은 설치 시점에 정해진다 — `Owned` 옵션"** 절 신설. `Owned=true`(기본)/`false` 대조표, `Detach`와 직교하는 축이라는 것, `destroySlotTree`/`dispose`도 이 플래그를 봐야 하므로 클로저가 아닌 **Slot 필드**(`slot._owned`)여야 한다는 것까지 |
|
||||
| `F-4-1` | **맞음** — *"`__pairs`/`__ipairs` 직접 구현체를 담은 ud 등을 받는 flattened 는 없고, luau 테이블만 사용하는게 맞음"* | `dispatch-core-plan.md`의 "props 순회 순서" 절을 **"계약은 순서 보장, 구현은 일반화 `for` 한 번"**으로 정정. 옛 근거 (1)("다른 백엔드가 props를 Lua 테이블이 아닌 자료구조로")이 **`inst`엔 해당해도 `flattened`엔 해당 안 됨**을 명시. 스파이크 `01`은 두 루프 버전이라 **재작성 필요**로 `STATUS.md`에 표시 |
|
||||
| `F-4-2` | **확인** | 이미 반영돼 있던 역순 정정 유지(`modifier-plan.md`) |
|
||||
|
||||
## G-2. `F-4-3` → 확장 논의 자료 준비 완료
|
||||
|
||||
**회신**: *"리스트 액티베이션을 먼저 하고 length 를 얻어 밀고 attachSlot 되는게
|
||||
맞을지도. attachSlot 의 기능이 너무 다양해진게 문제같음. 이 부분에 있어서는
|
||||
확장 논의를 하게 준비해두자."*
|
||||
|
||||
→ **`research/slot-attach-decomposition.md` 신설.** `setLength`를 어느 줄에
|
||||
둘지 고르는 문제가 아니라 분해 문제라는 진단에 동의하고, 논의가 바로 시작될 수
|
||||
있게 재료만 모아뒀다(**아무것도 확정 안 함**, `base/slot-plan.md`가 여전히 정본):
|
||||
|
||||
- **책임 일곱(R1~R7)** — 부모 등록(offset)/`:List` 실체화/마운트 상태 전이/
|
||||
부모 등록(length)/배치 게이팅/자식 배치/재귀. 서로 다른 축 넷이 섞여 있음.
|
||||
- **순서 제약 일곱(C1~C7)과 그 출처** — 전부 실제로 밟은 버그에서 나온
|
||||
것이라(`RC-1`/`RC-3`/`RC-4`, 해제 순서 계약, `C-7` 일반 계약) 분해안이
|
||||
하나라도 깨면 그 버그가 되돌아온다는 걸 표로.
|
||||
- **⭐ C6 ↔ C7 충돌이 근본** — "부모에게 알리는 길이의 최종값은 flush가
|
||||
끝나야 정해짐"(C6)과 "부기가 물리보다 먼저"(C7)는 **단일 함수 안에서 R4의
|
||||
자리가 하나뿐이라 동시 만족이 불가능**하다. 그래서 `F-4-3`이 자리 선택으로는
|
||||
안 풀렸던 것.
|
||||
- **분해 후보 넷** — (A) 현행 유지 / **(B) `prepare`+`mount` 2단**(회신의
|
||||
"액티베이션 먼저 → length 얻어 밀고 → attach"를 구조화한 것, R6를 부기와
|
||||
물리로 쪼개면 **C6·C7을 둘 다 만족**) / (C) 3단(+`Dispatch.drive`까지 같은
|
||||
모양으로 수렴) / (D) 문서만.
|
||||
- **같이 정해야 하는 것 6가지** — `activateList`의 Observer `bindLifetime`이
|
||||
어느 단계인지, 얇은 `attachSlot` 래퍼를 남길지, `Dispatch.drive`도 맞출지,
|
||||
prepare만 하고 mount 안 한 중간 상태 처리, `_mounted` 소비처가 새 정의로도
|
||||
맞는지, 그리고 **`Detach` 정리용 `Effect`를 어디에 설치할지**.
|
||||
- **순서 권고**: 마지막 항목 때문에 **`F-3`이 먼저 닫히는 게 낫다** — 그
|
||||
결정이 이 분해의 요구사항을 하나 더 얹는다.
|
||||
|
||||
## G-3. 아직 확인 안 된 것 — `F-3`의 나머지
|
||||
|
||||
**`F-3`은 (4)번만 확인을 받았다.** (1)~(3)과 "+"(dispose 재귀)는 아직
|
||||
답이 없어서 **base에 아무것도 안 넣었다.** 요지만 다시 줄이면:
|
||||
|
||||
1. **detached 요소가 영구 누수인 이유** — gcconn 트릭 때문에 quad-제작
|
||||
Instance는 자기 시그널 커넥션이 자기를 살린다("참조를 놓는 것만으로는
|
||||
회수되지 않고 반드시 `Destroy`로 회수된다", `lifecycle-pattern.md`) →
|
||||
**GC 폴백이 아예 없다.** 명시적 정리 경로가 필수.
|
||||
2. **그래서 detached는 `userdata`가 아니라 `slot._detached` 필드가 들고
|
||||
있어야 한다** — `userdata`는 `:List`에게 opaque라 뭘 죽여야 할지 모르고,
|
||||
소유권이 Slot에 남아야 남이 못 가져가며, `destroySlotTree`의 walk가
|
||||
닿아야 한다. 부수 이득으로 **다음 사이클에 `prev`로 그대로 돌려줄 수
|
||||
있어 `ud` 홀드가 불필요**해진다.
|
||||
3. **owner 죽음 처리는 `Effect`**(제안하신 그대로), 단 **소유 층위는
|
||||
`activateList`가 아니라 `attachSlot`/`unmountSlotTree` 쌍** — Slot이 다른
|
||||
`physicalTarget`에 재마운트되면 Effect도 옮겨야 하고, 안 그러면 옛 target이
|
||||
죽을 때 **살아있는 Slot의 detached를 파괴**한다.
|
||||
4. **`KeyGone` 후 "다시 안 묻기"는 자동 성립** — 소멸 루프가 `keyIndex`만
|
||||
도니까 사라진 키는 다음 사이클 대상이 아니다. 남은 미결은 **`KeyGone`에
|
||||
`prev`를 그대로 반환하면?**(→ `error` 추천)과 **`index` 인자**(→ `0` 추천).
|
||||
5. **"+" `dispose` 재귀** — 중첩 Slot 재귀는 **이미 된다**. 안 되는 건
|
||||
`_detached`(walk가 안 닿음)와 `userdata` 안의 quad-제작 Instance. 후자는
|
||||
`SL-38`의 "GC만으로 정리되는 값만" 제약에 **"quad Instance는 GC로 안
|
||||
죽는다"를 예시로 추가**해야 한다(지금은 `:Subscribe()` Observer만 예시라
|
||||
Instance는 안전해 보인다).
|
||||
|
||||
→ **여기 동의가 나오면 `C-1`/`C-2`/`SL-45`/"+"가 한 번에 닫히고, 그때
|
||||
`base/` 반영과 `attachSlot` 분해 논의를 이어서 하면 된다.**
|
||||
|
|
|
|||
|
|
@ -175,6 +175,14 @@
|
|||
2026-08-18 `/code-review high`] M6(`:List`가 있는 마일스톤) 착수 전
|
||||
필요** — M8(`Ref`) 아님, `base/slot-plan.md`의 "`nil` 리턴은 파괴가
|
||||
기본" 절.
|
||||
- **[신설, 2026-08-21 구현 전 QA 4라운드] `attachSlot` 책임 분해** — 한
|
||||
함수가 부모 등록(offset/length) / `:List` 실체화 / 마운트 상태 전이 / 배치
|
||||
게이팅 / 자식 배치 / 재귀를 다 지고 있어서, **"부모에게 알리는 길이의
|
||||
최종값은 flush가 끝나야 정해진다"와 "부기가 물리 조작보다 먼저"가 동시에
|
||||
만족되지 않는다**(지금은 후자를 지키고 전자를 포기 — 부모 `recompute`가
|
||||
1회 헛돎). 사용자 판단으로 확장 논의 대기 — 책임 목록/순서 제약 출처/분해
|
||||
후보 넷은 `research/slot-attach-decomposition.md`. **M6(`:List`) 착수 전
|
||||
필요**, 선행으로 아래 `Detach` 보관 위치가 먼저 닫히는 게 나음.
|
||||
- **[신설, 2026-08-18 구현 전 QA] 그룹 `Attribute`의 위치별 claim 설계** —
|
||||
같은 그룹 객체를 두 위치에 놓는 경우(`Frame { a, a }`)를 잡으려면 위치별
|
||||
claim 레지스트리가 하나 필요하다는 **방향은 확정**됐고(`Ref`처럼
|
||||
|
|
|
|||
191
.claude/research/slot-attach-decomposition.md
Normal file
191
.claude/research/slot-attach-decomposition.md
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
# `attachSlot` 책임 분해 — 확장 논의 준비 자료
|
||||
|
||||
**상태**: research — **논의 전 준비 자료.** 아무것도 확정하지 않았고, 다음
|
||||
논의가 바로 시작될 수 있게 **지금 무엇이 얽혀 있는지**를 한 곳에 모은 것.
|
||||
|
||||
**왜 생겼나**: 2026-08-21 구현 전 QA 4라운드 `F-4-3`에서 `Dispatch.setLength`를
|
||||
flush 루프 앞에 둘지 뒤에 둘지가 갈렸는데, 사용자가 그 자리를 고르는 문제가
|
||||
아니라고 짚었다 — *"리스트 액티베이션을 먼저 하고 length 를 얻어 밀고
|
||||
attachSlot 되는게 맞을지도. **attachSlot 의 기능이 너무 다양해진게
|
||||
문제같음.** 이 부분에 있어서는 확장 논의를 하게 준비해두자."*
|
||||
|
||||
정본은 여전히 `base/slot-plan.md`의 "재귀 메커니즘" 절 —
|
||||
**이 문서가 그 확정을 대체하지 않는다.**
|
||||
|
||||
---
|
||||
|
||||
## 1. `attachSlot`이 지금 하는 일 — 책임 일곱 개
|
||||
|
||||
`base/slot-plan.md`의 의사코드를 책임 단위로 쪼개면 이렇다(코드는 그 문서가
|
||||
소스, 여기선 라벨만 붙임):
|
||||
|
||||
| 라벨 | 하는 일 | 층위 |
|
||||
|---|---|---|
|
||||
| **R1** | `offsetSource` 생성 → `Dispatch.setOffsetSource(ownerKey, position, ...)` → `slot.Offset` 공개 | **부모에 대한 자기 등록** |
|
||||
| **R2** | `slot._listed`면 `activateList` — `:List` reconcile이 `_elements`를 채움(물리 마운트 없음) | **내용 실체화** |
|
||||
| **R3** | `slot._mounted = true`, `slot._mountedInst = physicalTarget` | **자기 상태 전이** |
|
||||
| **R4** | `Dispatch.setLength(ownerKey, position, slot.Length)` | **부모에 대한 자기 등록** |
|
||||
| **R5** | `getBlocker(slot):On()` … `:OffWithoutEmit()` + 마지막 `recompute` | **배치 게이팅** |
|
||||
| **R6** | flush 루프 — 각 요소의 부기 등록(`setOffsetSource`/`setLength`) **+ 물리 마운트(`Parent` 대입)** | **자식 배치** |
|
||||
| **R7** | 중첩 Slot 요소에 대해 `attachSlot` 재귀 | **재귀** |
|
||||
|
||||
**서로 다른 축이 넷 섞여 있다** — (a) 부모에게 나를 알리는 일(R1/R4),
|
||||
(b) 내 내용을 만드는 일(R2), (c) 내 자식을 실제로 붙이는 일(R6), (d) 상태
|
||||
플래그와 배치 게이팅(R3/R5). 사용자가 "기능이 너무 다양해졌다"고 한 게 이것.
|
||||
|
||||
**호출부는 셋**:
|
||||
- `SlotHandler.process`(최상위) — `bindLifetime` 후 `attachSlot(slotValue, inst, inst, k)`
|
||||
- R7(중첩) — flush 루프 안에서 재귀
|
||||
- `rawAdd`(런타임 단건) — 이미 마운트된 Slot에 나중에 nested Slot을 `Add`할 때
|
||||
|
||||
---
|
||||
|
||||
## 2. 순서 제약과 그 출처 — 왜 지금 모양이 됐나
|
||||
|
||||
**이 제약들은 전부 실제로 밟은 버그에서 나왔다.** 분해안을 평가할 때 하나라도
|
||||
깨면 그 버그가 되돌아온다.
|
||||
|
||||
| # | 제약 | 왜 | 출처 |
|
||||
|---|---|---|---|
|
||||
| **C1** | `slot.Offset`(R1)이 `activateList`(R2)보다 **먼저** | `:List`의 `updateFn`이 `offset`을 인자로 받는 계약 | `slot-plan.md` `:List` 파라미터 |
|
||||
| **C2** | `activateList`(R2)는 `_mounted == false`인 상태에서 돌아야 함 → **R2가 R3보다 먼저** | 아니면 reconcile의 `rawAdd`가 매 항목마다 즉시 물리 마운트 + `setLength`를 태워 (a) Blocker 없이 `recompute`가 돌고 (b) nested Slot에 `attachSlot`이 **두 번** 불림 | `RC-3`/`RC-4`(QA 3라운드) |
|
||||
| **C3** | `attachSlot`이 **반환될 때는** `_mounted == true`여야 함 | 런타임 `rawAdd`가 이 플래그로 "지금 붙일까 `_elements`에만 넣을까"를 가름 | `slot-plan.md` `rawAdd` |
|
||||
| **C4** | `setOffsetSource`가 `setLength`보다 **먼저** | `setLength` 끝의 `gatedRecompute`가 죽는 중인 Source에 `:Set`을 날림 | `dispatch-core-plan.md` 해제 순서 계약 |
|
||||
| **C5** | R5의 Blocker가 R6 **전체**를 감쌈 | 없으면 등록마다 `recompute`가 돌아 O(N²) | `RC-1` 해결(QA 2라운드) |
|
||||
| **C6** | R4가 넘기는 `slot.Length`의 **최종값**은 R5/R7이 끝나야 정해짐 | 중첩 Slot 요소의 `.Length`는 그 요소의 `attachSlot`이 돌아야 확정됨 | 구조적 |
|
||||
| **C7** | 부기 갱신이 물리 트리 조작보다 **먼저** | 백엔드가 "내가 물리적으로 밀어낼 때 부기는 이미 정확하다"를 전제할 수 있어야 함 | `dispatch-core-plan.md` 일반 계약(QA 4라운드 `C-7`) |
|
||||
|
||||
### ⭐ C6와 C7이 정면으로 부딪힌다 — 이게 `F-4-3`의 근본
|
||||
|
||||
- **C7을 지키려면** R4(부모에게 내 길이 알리기)가 R6(자식 물리 마운트)보다
|
||||
먼저여야 한다.
|
||||
- **C6를 지키려면** R4는 R6/R7이 끝난 **뒤**여야 최종값을 알 수 있다.
|
||||
|
||||
**지금은 C7을 지키고 C6를 포기했다** — R4가 `slot.Length`(값이 아직 `0`인
|
||||
State **객체**)를 넘기고, flush가 끝나 `recompute`가 실제 값을 넣으면 부모가
|
||||
Observer로 다시 반응해 스스로 교정한다. 대가는 **배치 밖 재마운트에서 부모
|
||||
`recompute`가 2회 도는 것**(뒤에 형제가 있으면 그 offset들이 두 번 `Set`됨).
|
||||
|
||||
**단일 함수로는 둘 다 만족할 수 없다** — 한 함수 안에서 R4의 자리가 하나뿐이기
|
||||
때문. 그래서 이건 "어느 줄에 놓을까"가 아니라 **분해 문제**다.
|
||||
|
||||
---
|
||||
|
||||
## 3. 분해 후보
|
||||
|
||||
### (A) 현행 유지 — 단일 `attachSlot`
|
||||
|
||||
- C7 지킴, C6 포기(자기 교정 1회).
|
||||
- **비용**: 배치 밖 재마운트마다 부모 `recompute` 1회 낭비. 크래시도 영구
|
||||
오류도 아님(`SL-58`에 이미 "손대지 않기로" 기록됨).
|
||||
- **문제**: 사용자가 지적한 "기능이 너무 다양함"은 그대로 남는다.
|
||||
|
||||
### (B) 2단 분리 — `prepare` / `mount` ⭐ 사용자 제안에 가장 가까움
|
||||
|
||||
*"리스트 액티베이션을 먼저 하고 length 를 얻어 밀고 attachSlot 되는게 맞을지도"*
|
||||
를 그대로 구조화하면 이 모양이 된다. **핵심은 R6를 부기(R6a)와 물리(R6b)로
|
||||
쪼개는 것.**
|
||||
|
||||
```lua
|
||||
-- 개념 스케치. 이름/시그니처 전부 가칭
|
||||
local function prepareSlot(slot, physicalTarget, ownerKey, position)
|
||||
-- R1
|
||||
local offsetSource = Source(0)
|
||||
Dispatch.setOffsetSource(ownerKey, position, offsetSource)
|
||||
slot.Offset = offsetSource
|
||||
|
||||
-- R2 (여전히 _mounted == false — C2)
|
||||
if slot._listed then activateList(slot, physicalTarget) end
|
||||
|
||||
-- R5 + R6a + R7-prepare : 부기만, 물리 마운트 없음
|
||||
local blocker = getBlocker(slot)
|
||||
blocker:On()
|
||||
for i, element in ipairs(slot._elements) do
|
||||
if isSlot(element) then
|
||||
prepareSlot(element, physicalTarget, slot, i) -- 재귀 → element.Length 확정
|
||||
Dispatch.setLength(slot, i, element.Length)
|
||||
else
|
||||
Dispatch.setOffsetSource(slot, i, None)
|
||||
Dispatch.setLength(slot, i, 1)
|
||||
end
|
||||
end
|
||||
blocker:OffWithoutEmit()
|
||||
recompute(slot, bk) -- 여기서 slot.Length가 **최종값**으로 확정 (C6 만족)
|
||||
end
|
||||
|
||||
local function mountSlot(slot, physicalTarget)
|
||||
slot._mounted = true -- R3 (C3)
|
||||
slot._mountedInst = physicalTarget
|
||||
for i, element in ipairs(slot._elements) do
|
||||
if isSlot(element) then mountSlot(element, physicalTarget) -- R7-mount
|
||||
else element.Parent = physicalTarget end -- R6b
|
||||
end
|
||||
end
|
||||
|
||||
-- 호출부(최상위)
|
||||
prepareSlot(slotValue, inst, inst, k)
|
||||
Dispatch.setLength(inst, k, slotValue.Length) -- R4 — 이제 최종값 (C6 + C7 동시 만족)
|
||||
mountSlot(slotValue, inst)
|
||||
```
|
||||
|
||||
- **C6와 C7을 둘 다 만족한다** — 부모에게 넘기는 길이가 처음부터 최종값이고,
|
||||
그 등록이 어떤 `Parent` 대입보다도 먼저다.
|
||||
- **각 함수의 일이 하나로 좁아진다** — `prepareSlot` = "부기를 정확하게
|
||||
만든다", `mountSlot` = "그 부기대로 트리에 붙인다".
|
||||
- **`_mounted`의 의미가 정직해진다** — 지금은 "`activateList`는 지났고 flush는
|
||||
아직"이라는 어정쩡한 중간 시점인데, 분리하면 문자 그대로 "mount 단계를
|
||||
지났는가"가 된다.
|
||||
- **비용**: `_elements` 순회가 2회로 늘고, 호출부가 **셋 다** 두 함수를 순서대로
|
||||
불러야 한다(빠뜨리면 half-attached 상태).
|
||||
|
||||
### (C) 3단 분리 — `register` / `activate` / `mount`
|
||||
|
||||
R1(부모 등록)까지 따로 떼는 안. `Dispatch.drive`의 최상위 배열 파트도 같은
|
||||
3단으로 맞추면 "배치 등록 → 실체화 → 마운트"라는 하나의 모양이 코퍼스 전체에
|
||||
반복된다.
|
||||
|
||||
- **이득**: `Dispatch.drive`와 `attachSlot`이 지금 서로 비슷한데 미묘하게
|
||||
다른 구조(전자는 Blocker + 두 패스, 후자는 Blocker + flush)인 걸 하나로
|
||||
수렴시킬 수 있음.
|
||||
- **비용**: 단계가 하나 더 늘고, 호출부가 셋 → 셋 × 3단이 됨. (B)의 이득
|
||||
대부분을 (B)만으로 이미 얻으므로 **추가 이득이 뭔지가 논의 대상**.
|
||||
|
||||
### (D) 최소 변경 — 분리 없이 문서만
|
||||
|
||||
`attachSlot` 안을 R1~R7 주석 블록으로 명시하고 각 제약(C1~C7)을 그 자리에
|
||||
달아둠. 코드는 그대로.
|
||||
|
||||
- **이득**: 위험 0. **비용**: 근본 문제(C6/C7 충돌, 책임 과다)는 안 풀림.
|
||||
|
||||
---
|
||||
|
||||
## 4. 어떤 분해든 같이 정해야 하는 것
|
||||
|
||||
1. **`activateList`의 `data:Observer(fn)` `bindLifetime`은 어느 단계인가.**
|
||||
지금은 `activateList` 안에서 `bindLifetime(inst, observer)`를 부르는데,
|
||||
(B)에서 그건 prepare 단계다 — 아직 아무것도 물리적으로 안 붙은 시점에
|
||||
`physicalTarget`에 생명주기를 묶는 게 맞는지.
|
||||
2. **호출부를 감싸는 얇은 `attachSlot`을 남길지.** 남기면 `SlotHandler.process`/
|
||||
`rawAdd`가 지금처럼 한 줄로 끝나고 "한쪽만 부르는" 오용도 막힌다. 대신
|
||||
"결국 다시 한 함수"라 분해의 이득이 반쯤 희석된다.
|
||||
3. **`Dispatch.drive`도 같은 모양으로 맞출지**(후보 (C)와 직결).
|
||||
4. **prepare만 하고 mount 안 한 중간 상태를 어떻게 다룰지** — 방어할지, UB로
|
||||
둘지. 코퍼스 기조상 UB + 문서화가 자연스러워 보이지만 확인 필요.
|
||||
5. **`_mounted` 소비처가 새 정의로도 맞는지** — (a) `:List()`가 마운트 이후에
|
||||
불릴 때 즉시 `activateList`하는 분기, (b) `rawAdd`의 "붙일까 말까" 분기.
|
||||
(B)에서는 `_mounted`가 mount 단계에서 켜지므로, prepare와 mount 사이에
|
||||
`:List()`가 불리는 경로가 있는지 따져야 한다.
|
||||
6. **`Detach` 정리용 `Effect`(QA 4라운드 `F-3`)를 어디에 설치할지** — 그건
|
||||
`physicalTarget`에 묶이므로 mount 단계가 자연스럽다. **`F-3`이 먼저
|
||||
닫히는 게 순서상 낫다** — 그 결정이 이 분해의 요구사항을 하나 더 얹는다.
|
||||
|
||||
---
|
||||
|
||||
## 5. 지금 상태 요약
|
||||
|
||||
- **확정된 건 없다.** `base/slot-plan.md`의 단일 `attachSlot`이 여전히 정본.
|
||||
- **급하지 않다** — (A)로 두어도 동작은 맞고(자기 교정), M2/M3 착수를 막지
|
||||
않는다. 다만 **M6(`:List`) 구현 전에는 정해두는 게 낫다** — 그 시점에
|
||||
`activateList`/`rawAdd`/`attachSlot`을 실제로 짜기 때문.
|
||||
- **선행 항목**: 위 4-6번대로 `F-3`(`Detach` 보관 위치 + `KeyGone`)이 먼저
|
||||
닫히면 이 논의의 요구사항이 완전해진다.
|
||||
|
|
@ -8,16 +8,18 @@
|
|||
00. **⭐⭐ [2026-08-18 신설] 구현 전 QA — 1·2·3라운드는 전부 `base/`에 반영
|
||||
완료, [2026-08-19 신설] 4라운드는 문항지 작성만 끝나고 사용자 회신 대기 중.**
|
||||
|
||||
**4라운드 — [2026-08-21] 회신 2차 처리 완료, F절만 열려 있음.** 문항지는
|
||||
**4라운드 — [2026-08-21] 회신 3차 처리 완료, `F-3` 일부만 열려 있음.** 문항지는
|
||||
`.claude/qa-request/pre-implementation-qa-round4.md`, 사용자 회신 원문은
|
||||
`-response.md`, 처리 결과·재질문·판단 대기 항목은 **`-followup.md`가
|
||||
소스**(여기서 목록을 세지 않음). 회신 중 판단이 명확했던 것은 그 자리에서
|
||||
`base/`에 반영했다. **B절(설명 보강 재질문)은 2차 회신으로 전부 확인됐고,
|
||||
C절 결정도 대부분 반영 완료** — 지금 열려 있는 건 followup **F절**뿐이다:
|
||||
`F-3`(`KeyGone` 홀드 + `Owned` 설치 플래그 설계 제안에 대한 확인 —
|
||||
여기 동의가 나오면 `C-1`/`C-2`/`SL-45`/`dispose` 재귀가 한 번에 닫힘),
|
||||
`F-4`(단일 일반화 `for` 전환 여부 / flatten 반복 방향 / `setLength` 위치).
|
||||
아래는 그 회신 전 서술:
|
||||
`F-3`의 **(1)~(3)과 "+"**(detached 요소를 `slot._detached` 필드가 들고
|
||||
owner 죽을 때 `Effect`로 정리 / `KeyGone` 세부 / `dispose` 재귀) — 요약은
|
||||
followup `G-3`. `F-3`의 `Owned` 설치 플래그와 `F-4` 셋은 전부 닫혔고,
|
||||
`F-4-3`(`setLength` 위치)은 **`attachSlot` 책임 분해**라는 더 큰 논의로
|
||||
넘어가 `research/slot-attach-decomposition.md`에 준비 자료를 만들어뒀다
|
||||
(M6 착수 전 필요). 아래는 그 회신 전 서술:
|
||||
|
||||
**(원 서술) 4라운드 문항지 작성 경위.** 사용자 요청("모든 확정 부분에 있어서 예가 되어야하는 질문들을
|
||||
계속 … 표면적 타입계약부터, 실제 내부 구현 계획과 동작 원리 등")으로
|
||||
|
|
|
|||
Loading…
Reference in a new issue