fix: /code-review high 발견 2건 — plain-table :List 재마운트 크래시 등
quad-doc-auditor 6라운드 수렴 후 /code-review high로 diff 자체를 재검토한 결과. 감사자는 코퍼스 전체 정합성만 보고 diff 결함은 못 잡는다는 게 conventions.md가 이미 명시한 한계인데, 실제로 이번 diff 안의 결함이 나왔다. - activateList의 재마운트 분기가 bindLifetime(physicalTarget, nil)로 크래시할 수 있었음. _listObserver는 data가 reactive(State/Source)일 때만 세팅되고 plain table data(문서가 지원하는 형태)면 영원히 nil인데, 가드 없이 불렀음. 짝인 unmountSlotTree 쪽은 이미 방어돼 있었던 것과 비대칭 — 가드 추가. - "구독 시점" 절에 activateList의 옛 파라미터 이름 inst가 리네이밍 후에도 남아 있어, 그 절만 읽는 구현자가 physicalTarget 리네이밍의 취지(owner 키가 Slot일 수도 있는 문맥과의 혼동 방지)를 놓칠 위험 — 정정. qa-request/pre-implementation-qa-round4-followup.md에 I-8로 기록. doc-check.py ERROR 0. Co-authored-by: qwreey <me@qwreey.moe>
This commit is contained in:
parent
4622fbeec8
commit
d5e5e1a1b9
2 changed files with 40 additions and 7 deletions
|
|
@ -1165,7 +1165,14 @@ function activateList(self, physicalTarget)
|
|||
-- 자원이 옛 physicalTarget에 매달린 채로 남아, 그게 죽는 순간
|
||||
-- 살아있는 이 Slot의 `:List`가 조용히 반응을 멈추고(`_listObserver`)
|
||||
-- 살아있는 detached 요소가 파괴된다(`_detachCleanup`).
|
||||
bindLifetime(physicalTarget, self._listObserver)
|
||||
-- [2026-08-21 /code-review high] `_listObserver`는 `data`가 reactive
|
||||
-- (State/Source)일 때만 세팅된다(아래 `isState(data)` 분기) — plain
|
||||
-- table `data`(문서가 지원하는 형태)면 영원히 `nil`이라, 가드 없이
|
||||
-- 부르면 `bindLifetime(physicalTarget, nil)`이 `gchold[nil] = true`로
|
||||
-- 죽는다(`base/lifecycle-pattern.md`의 `bindLifetime` 계약).
|
||||
-- 언마운트 쪽(`unmountSlotTree`)은 이미 `if slot._listObserver then`로
|
||||
-- 방어돼 있었는데 이 재마운트 분기만 빠져 있었다.
|
||||
if self._listObserver then bindLifetime(physicalTarget, self._listObserver) end
|
||||
bindLifetime(physicalTarget, self._detachCleanup)
|
||||
return
|
||||
end
|
||||
|
|
@ -1602,14 +1609,17 @@ Slot:Single(state, updateFn?, opts?)
|
|||
실제 `data:Observer(fn)` 구독 + 최초 `reconcile`은 Slot 컨테이너 자신이
|
||||
마운트되는 순간(`Dispatch/Slot.luau`의 `process(inst,k,self,index)` — 위
|
||||
"`isMounted` 이중 추적 분리" 절이 이미 `self._mounted`를 세팅하는 바로 그
|
||||
지점)에 `activateList(self, inst)`가 수행. `Dispatch.setLength(inst,i,
|
||||
self.Length)`를 부르는 것과 같은 자리에서 같이 트리거되면 됨.
|
||||
지점)에 `activateList(self, physicalTarget)`가 수행(**[리네이밍,
|
||||
2026-08-21]** 2번째 인자 이름은 `inst`였으나 owner 키가 Slot일 수도 있는
|
||||
문맥과 헷갈리지 않도록 `physicalTarget`으로 통일 — 아래 실제 정의가 소스).
|
||||
`Dispatch.setLength(inst,i, self.Length)`를 부르는 것과 같은 자리에서 같이
|
||||
트리거되면 됨.
|
||||
|
||||
**`:List()`가 마운트 이후에 불리는 경우 — `self._mounted`면 즉시 활성화
|
||||
(확정)**: 마운트는 1회성 이벤트라, `:List()`가 마운트보다 늦게 호출되면
|
||||
그 이벤트를 기다리는 방식으론 영영 활성화가 안 됨 — `:List()`가
|
||||
`self._mounted`를 확인해서 이미 참이면 그 자리에서 바로
|
||||
`activateList(self, self._mountedInst)`를 호출(마운트 시점에 `inst`를
|
||||
`activateList(self, self._mountedInst)`를 호출(마운트 시점에 물리 target을
|
||||
`self._mountedInst`로 같이 저장해둠). CRUD와의 상호배타 가드(`self._listed`)와
|
||||
같은 자리에서 자연스럽게 처리됨 — 호출 순서에 대한 새 제약을 추가하지 않음.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
# 구현 전 QA **4라운드 followup** — 회신 처리 결과 + 재질문
|
||||
|
||||
**상태**: **[2026-08-21] 4차 처리로 종결 + 반영 후 감사까지 완료 — 아래
|
||||
I절이 최신.** 감사 4라운드(의사코드 트레이싱)가 **실제 크래시 3건**을 잡아
|
||||
같은 날 전부 닫았다(`I-1`~`I-3`). H절이 반영 내용, I절이 그 감사 결과다.
|
||||
**상태**: **[2026-08-21] 4차 처리 + 감사 6라운드 + `/code-review high`
|
||||
까지 전부 완료 — 아래 I절이 최신이자 마지막.** H절이 반영 내용, I절이
|
||||
그 뒤의 검증 전량(감사 트레이싱이 잡은 크래시 3건 `I-1`~`I-3`, 사용자가
|
||||
가져온 이관 `I-7`, `/code-review`가 잡은 2건 `I-8`)이다.
|
||||
`F-3`이 전량 확인됐고 `attachSlot` 분해도 확정돼 `base/`에 전부 반영됐다.
|
||||
**이 followup에 열린 질문은 남아있지 않다.** 5라운드 문항지는 만들지
|
||||
않는다(사용자 지시). 아래 A~G절은 거기까지 온 처리 과정의 기록.
|
||||
|
|
@ -1345,3 +1346,25 @@ stale하게 남기지 않고(그 필드는 top-level `claimOwnerAt` 전용),
|
|||
Slot일 수는 없다 — 그 Slot은 이미 1번째 인자 `self`다. 옆 함수들
|
||||
(`materializeSlotTree`/`mountSlotTree`/`attachSlot`)과 이름을 맞춘
|
||||
**순수 리네이밍**.
|
||||
|
||||
## I-8. `/code-review high` — 2건, 전부 반영
|
||||
|
||||
`quad-doc-auditor` 6라운드가 수렴한 뒤 사용자가 `/code-review`(diff 기반)를
|
||||
직접 돌렸다. `conventions.md`의 "`/code-review`는 감사자를 대체하지 않는다"
|
||||
항목이 예고한 대로, 감사자 각도(코퍼스 전체 의미론적 정합성)에선 안 보이던
|
||||
**diff 자체의 결함**이 나왔다 — 둘 다 이번 세션에 새로 생긴 코드다.
|
||||
|
||||
1. **`activateList`의 재마운트 분기가 `bindLifetime(physicalTarget, nil)`로
|
||||
크래시할 수 있었다.** `_listObserver`는 `data`가 reactive(State/Source)일
|
||||
때만 세팅되는데(`isState(data)` 분기), `data`가 plain table이면(문서가
|
||||
지원하는 형태) 영원히 `nil`이다. 그런데 `I-7`이 넣은 재마운트 분기는
|
||||
가드 없이 `bindLifetime(physicalTarget, self._listObserver)`를 불렀다 —
|
||||
`bindLifetime`이 `gchold[value] = true`를 하므로 `value`가 `nil`이면
|
||||
그 자리에서 죽는다. **짝인 언마운트 쪽은 이미 `if slot._listObserver
|
||||
then`으로 방어돼 있었는데 이 재마운트 분기만 빠져 있었다** — `I-7`을
|
||||
반영하며 대칭을 놓친 것. 가드 추가로 반영.
|
||||
2. **"구독 시점" 절에 `activateList`의 옛 파라미터 이름 `inst`가 리네이밍
|
||||
후에도 남아 있었다.** 이 세션이 `physicalTarget`으로 통일한 이유가
|
||||
"owner 키가 Slot일 수도 있는 문맥과 헷갈리지 않기 위해서"인데, 정작 그
|
||||
절의 산문 설명만 안 옮겨져 있으면 구현자가 이 절만 읽고 옛 이름을
|
||||
그대로 들고 갈 위험이 있었다. 두 곳 정정.
|
||||
|
|
|
|||
Loading…
Reference in a new issue