- H-143 Rerun 꼬리: 실행 중 사망(wasAlive and not canExecute)이면 cleanup 즉시 소진 (처음 쓴 not canExecute 판정은 생성자 최초 설치를 죽여 감사 2라운드가 정정) - H-144 재구독 꼬리(Refresh 먼저) + 진입점은 EffectHandle 자기 것 (b) (Observer 함수 배정은 콜론 위임으로 꼬리 2회 — 감사 4라운드, luau 재현) → conventions.md 설계 원칙 신설: 하나의 무언가가 두 일을 하지 않는가 - H-145 bk.indexOfElement weak-key / H-146 루트 .Parent는 사용자 몫, Mount 없음 - 감사 1→1→1→1→1→1→1→0, /code-review high 10건 중 7 반영 - H-147~H-149는 qa-request/pre-implementation-handtrace-round10.md §4로 (배치 회신) - round10 지시서(-brief.md) 신설, 광범위 탐사 예정 Co-authored-by: qwreey <me@qwreey.moe> Claude-Session: https://claude.ai/code/session_01546hjsYNLSMZdHdPyTZaGb
7.7 KiB
구현 전 손 트레이싱 10라운드 — 발견 보고 + 배치 결정 문항지
이 파일이 무엇인가: [2026-08-28 신설] 9라운드 후속(
H-143H-146) 반영 뒤/code-review high가 낸 판단 대기 셋(H-147H-149)을 씨앗으로, 신선한 탐사자가 M2~M8 전 표면을 광범위하게 손 트레이싱·실측한 결과를 여기 이어 쓴다(H-150~). 지시서는-round10-brief.md. 사용자는 §4 표를 위에서 아래로 읽고 갈래만 회신한다(배치). 결정이 나면-round10-followup.md를 새로 만들고base/에 반영한다 — 이 파일은 발견 당시의 기록이라 각 항목의 "갈래"는 선택 전 목록이니 반영 뒤엔 그대로 믿지 말 것.상태: [2026-08-28 기준] 탐사 진행 중 / 회신 대기.
H-147H-149는H-143H-146반영분에/code-review high가 낸 10건 중 새 메커니즘·기존 결정 변경이라 문항으로 올린 셋(나머지 일곱은 반영 —-round9-followup.md의 마지막 code-review 절).
요약 표
| 번호 | 심각도 | 한 줄 | 주 대상 | 성격 | 실측 |
|---|---|---|---|---|---|
H-147 |
🟡 | Rerun 꼬리 wasAlive가 루프 머리 _consumeCleanup() 뒤에 잡히고 공개 Rerun()에 게이트가 없어 진입 시점에 이미 죽은 핸들(직전 cleanup이 self:Unsubscribe() / 해제 뒤 타이머의 self:Rerun())은 else 분기 → 고아 cleanup + _installed = true |
effect-plan.md Rerun |
/code-review 발견, 처방은 규칙 또는 새 플래그 |
— |
H-148 |
🟡 | H-146의 "Parent 거부는 전용 문구"가 새 메커니즘 — isHandlable 거부는 Dispatch.process의 일반 매치 실패 문구로 떨어지고 그 자리에 특수 분기는 두지 않기로 확정돼 있다. 사용자 회신에 문구 언급 없음(에이전트 선택이었음) |
bind-system-plan.md H-142 항목, ROADMAP.md M5 |
/code-review 발견, 처방은 새 핸들러 또는 철회 |
— |
H-149 |
🟡 | Observer:Subscribe가 self:WeakSubscribe()로 위임하므로 거기 붙인 error(…, 2)가 o:Subscribe() 경로에선 사용자 호출부가 아니라 Observer:Subscribe 본문을 가리킴(H-104 level 계약 위반). EffectHandle은 (b)로 인라인해 문제 없음 |
lifecycle-pattern.md Observer 네 진입점 |
/code-review 발견, 처방은 기존 결정("위임하므로 게이트 한 번") 변경 |
— |
상세
H-147 🟡 — 이미 죽은 핸들에서 Rerun()이 불리면 (H-143 잔여)
effect-plan.md Rerun: 루프 머리 _consumeCleanup() → local wasAlive = canExecute(self) → fn → if wasAlive and not canExecute(self). "실행 중에
죽은" 건 잡지만 "진입 시점에 이미 죽은" 건 못 잡는다:
- 직전 cleanup이
self:Unsubscribe()를 부름 → 루프 머리 소진 안에서 죽음 →wasAlive거짓 →fn실행 → else → 저장 +_installed = true. 고아 cleanup, 이후 재Subscribe의resubscribeTail이_installed참을 보고 재설치 생략. fn이task.delay(1, function() self:Rerun() end)를 걸고 사용자가 그 전에Unsubscribe(). 생성자 최초Rerun("한 번도 산 적 없음")과 구분할 상태가 없다. 갈래: (a) "죽은 핸들에서의Rerun()은 사용자 책임(UB)"으로 문서화 — 새 상태 없음; (1)은 "cleanup 안에서 자기 해제"를 지원 목록에서 빼는 것, (2)는Unsubscribe가 도는 cleanup이 그 예약을 취소해야 하는 것(그게 cleanup의 일). 기존 "error 시 UB / 수렴 책임은fn" 계약과 같은 결 / (b)_everAlive플래그 하나(첫canExecute참 시점에 세움) →Rerun진입에서everAlive and not canExecute → return. 필드 하나, 생성자 케이스도 가림 / (c)wasAlive를_consumeCleanup()앞에서 잡기 — (1)만 닫히고 (2)는 그대로. 권고 (a) — 두 시나리오 다 "죽은 뒤에도 자기를 부르는 코드"고,Unsubscribe가 cleanup을 도는 이유가 바로 그런 예약을 거두라는 것.
H-148 🟡 — H-146의 "전용 에러 문구"는 새 메커니즘이었다
PropertyHandler.isHandlable이 "Parent"를 거부하면 나오는 건 Dispatch.process의
일반 매치 실패 문구("no handler … check provider")이고, 그 자리에 특수
분기는 두지 않기로 확정돼 있다(dispatch-core-plan.md의 매치 실패 절). 전용
문구를 내려면 새 자리가 필요하다. 사용자 H-146 회신엔 문구 언급이 없었고
에이전트가 "배선 세부"라며 붙였다 — conventions.md 2026-08-27 규칙 위반.
갈래: (a) 전용 문구 철회 — 일반 매치 실패 error 그대로, 오해는 사용자
문서("Parent는 props에 못 쓴다, 루트는 밖에서")로 흡수. 새 것 0 / (b) 가드
핸들러 — k == "Parent"에 매치해 process에서 전용 문구로 error. Observer/
Effect의 "동적 경로 가드"(effect-plan.md)와 같은 모양이라 새 종류는 아니나
핸들러 하나 + 숏핸드보다 위 우선순위 필요 / (c) Dispatch.process 매치 실패
문구에 키 이름을 싣기(일반 개선이지만 별도 결정).
권고 (a) — 원래 회신의 취지(새 API 없음, 최종 사용자 몫)와 같은 결. M5에서
실제로 밟아 헷갈리면 (b)를 그때 얹는 게 싸다.
H-149 🟡 — Observer:Subscribe의 위임 때문에 level 2가 quad 내부 줄을 가리킨다
2026-08-27에 Observer:WeakSubscribe의 error에 , 2를 붙였는데(H-104
계약), Subscribe가 self:WeakSubscribe()로 위임하므로 o:Subscribe() 경로의
level 2는 Observer:Subscribe 본문을 가리킨다. 가장 흔한 오용(leaf 바인딩된
Observer에 o:Subscribe())이 quad 내부 줄을 블레임한다. EffectHandle 쪽은 (b)로
게이트를 인라인해 문제 없음.
갈래: (a) Observer도 Subscribe에 게이트·등록 세 줄을 인라인 — Effect와
같은 모양, level 2 정확. "게이트는 한 번만 돈다 — Subscribe가 WeakSubscribe에
위임"이라는 기존 문장이 "각자 한 번"으로 바뀜. 중복 세 줄은 같은 타입 안이라
dot 호출 로컬 헬퍼로 빼도 됨(가상 디스패치 없음) / (b) 내부 헬퍼
weakSubscribe(self, level)로 위임하고 Subscribe는 level 3 — 코퍼스에
level 3 선례 없음 / (c) 그대로 두고 Subscribe 경로만 블레임이 한 단계 안쪽임을
문서화.
권고 (a) — "본문을 섞지 않는다"(2026-08-27 원칙)와 결이 같다.
§4 ⭐ 사용자 결정이 필요한 것 (배치 회신용)
| 문항 | 무엇 | 선택지 | 권고 |
|---|---|---|---|
H-147 |
죽은 핸들에서 Rerun() |
(a) UB로 문서화(cleanup 안 자기 해제는 지원 목록 밖, 타이머 취소는 cleanup의 일) / (b) _everAlive 플래그 + 진입 게이트 / (c) wasAlive를 소진 앞에서 |
(a) — 새 상태 없이 기존 UB 계약과 같은 결 |
H-148 |
Parent 거부 문구 |
(a) 전용 문구 철회, 일반 매치 실패 그대로 / (b) Parent 가드 핸들러(동적 경로 가드형) / (c) 일반 문구에 키 이름 |
(a) — 회신 취지(새 API 없음) 그대로, 필요하면 M5에서 (b) |
H-149 |
Observer Subscribe 위임과 level 2 |
(a) Subscribe에 게이트·등록 인라인(Effect와 동형) / (b) level 3 위임 / (c) 문서화만 |
(a) — 본문 안 섞기 원칙과 동형 |
(탐사자의 문항은 아래에 이어 붙는다.)
§5 이상 없다고 확인한 것
(탐사자가 채운다.)
§6 남은 의심 / 못 본 것
(탐사자가 채운다.)
§7 레인 C 실행 기록
(탐사자가 채운다 — audit/handtrace-round10-reference-impl/.)