네 번째 세션: State<State<T>> 발견 방식을 서브에이전트로 코퍼스 전체에 반복해 Tag 참조 카운트(객체 identity 키잉)/Attribute 그룹 자기충돌 버그 발견·수정. Attribute 소유권 충돌 방지를 위해 처음엔 Dispatch.processAs/ retractSelfAndUnder 체크포인트 핸들러 패턴을 신설. 다섯 번째 세션: 체크포인트 패턴이 임시방편이라는 지적에서 출발해, chains의 핸들러 객체 identity 기반 추적 자체가 State<State<T>>를 UB로 만든 근본 원인이었음을 재확인 — 재귀 깊이 인덱스로 재설계(같은 키 재귀는 index+1, 다른 키 위임은 항상 1부터), Handler 계약을 process/retract 2-메소드에서 process가 자기 retract 클로저를 반환하는 1-메소드로 축소. 이걸로 State<State<T>>가 UB에서 정상 지원 대상으로 바뀌고, 전날 만든 체크포인트 패턴 전체가 불필요해져 archive로 이전. 여러 핸들러(StoreBind/Ref/Tag/Slot/ Attribute)의 private Relate 상태 저장소도 대거 정리됨. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0132hkscPQWa34iyHrHdoRiF
6.3 KiB
6.3 KiB
2026-08-13 네 번째 세션 — 사각지대 손 트레이싱 라운드, Dispatch.processAs/retractSelfAndUnder 체크포인트 핸들러 신설
배경
직전 세션(02)에서 State<State<T>>가 UB인데 "가능하다"로 문서에 낙관적으로
적혀 있던 문제를 발견·수정한 것을 계기로, 사용자가 같은 방식(합성
시나리오를 pseudocode에 손으로 대입)으로 다른 사각지대가 더 있는지
찾아달라고 요청. 서브에이전트 4개(Tag/Attribute/Slot/Ref+교차훑기)를
병렬로 띄워 .claude/base/ 전체를 훑음.
1부 — 서브에이전트 발견 (실제 버그 3건)
- Tag 참조 카운트 붕괴:
tagNameMap의 holders가Tag객체 identity로 키잉돼 있어서, 같은 Tag 객체(immutable이라 재사용이 흔한 관례,local SELECTED = Tag("selected")류)를 두 위치에 걸면 한 위치만 retract돼도 다른 위치가 쓰는 태그가 지워지는 버그. - Attribute 그룹 자기충돌: 그룹이 이름을 놓았다(retract, 로컬 캐시
초기화) 나중에 같은 그룹이 같은 이름을 다시 포함하면(
rawNew로 새 키 생성), 전역owners레지스트리엔 옛 키가 안 지워진 채 남아있어 "이미 다른 AttributeKey가 관리 중" 에러 — 그룹이 자기 자신과 충돌. - Slot
rawAdd의 이중 State 언랩 실패:Slot:Add(State<State<T>>)류 이중 래핑에서reconcile이 부르는rawAdd가isState재검사를 안 해서 State 객체가 그대로_elements에 박힘 — [정정, 2부에서 사용자 확인] 실제로는 버그가 아니라 이미 확정된State<State<T>>UB 범위의 당연한 사례, 최초 보고가 과다 보고였음.
문제 없음으로 확인된 것: State<State<Ref>>/State<State<Tag>>(StoreBind
재진입 가드가 먼저 걸림), PreRef 재사용 가드, Slot의 owner-키 Relate ↔
chains 네임스페이스 분리, elementOwner의 다단 Slot-in-Slot GC 안전성.
2부 — 사용자의 직접 기술 리뷰, 세 번째 설계로 수렴
사용자가 서브에이전트 보고에 대해 직접 pseudocode 레벨로 반박/재설계를 제시(이 세션의 핵심):
- Tag: 위치(
k) 기준 재키잉에 동의. 추가로 "Tag는 immutable이니oldv==newv면 retract 스킵 가능" 최적화 제안 — 채택. - Attribute:
AttributeGroupHandler.process가retractUnder에self를 안 넣는 이유를 질문하다,retractUnder가process안에 있는 것 자체가 구조적으로 틀렸다고 재지적(process가 다시 안 불리는 상황이면retractUnder도 안 일어남) — 결론적으로owners/rawNew레지스트리를 통째로 버리고,isHandlable이 없는(스캔에 안 걸리는) 순수 체크포인트 핸들러AttributeGroupKeyHandler를 새로 만들어Dispatch.processAs로 명시 push하고, 나중에Dispatch.retractSelfAndUnder(신설 — 기존retractUnder가keep미만만 지우는 것과 달리target이하까지 지움)로 체크포인트+그 아래 전부를 한 번에 철거하는 설계 제안. 손 트레이싱으로 검증: 이 설계는 소유권 충돌 감지를 별도 레지스트리 없이 기존 "같은 (inst,k)에 같은 핸들러 재사용 시 error" 가드(State<State<T>>가드와 동일 메커니즘)로 공짜로 얻음 — 채택,AttributeKeyHandler는 완전 무상태로 단순화됨. - Slot:
releaseOwner의 소유권 불일치를 조용히 무시하던 걸 즉시 error로 바꿔야 한다는 지적(그 상황 자체가 이미 버그라는 판단) — 채택.bindLifetime이attachSlot의 조건 분기에 묻혀있는 게unbindLifetime이 Handler 층위(SlotHandler.retract)에 있는 것과 비대칭이라는 지적 —bindLifetime을SlotHandler.process로 이동,attachSlot은 순수 구조적 mount 로직으로 단순화. 채택. - Brand:
isXX판별 함수들이nil을 안전하게 처리하는지 서브에이전트로 확인 요청 — 결과 안전함(weak registry lookup 방식이라nil키 조회도 항상 안전하게false로 귀결, 에러 나는 경로 없음). 문서 수정 불필요, M0 스파이크 목록에isXX(nil)명시적 실측 케이스 추가는 선택 사항으로 남김(안 함).
반영 완료
base/bind-system-plan.md: 핸들러 계약에isHandlable선택적 필드로 확장(생략 시 스캔 불가), "Dispatch 체인" 절에Dispatch.processAs/Dispatch.retractSelfAndUnder신설 + 체크포인트 패턴 설명,handler.process직접 호출 UB 경고에processAs도 정식 진입점으로 포함.base/tag-plan.md: holders를 Tag 객체 identity에서 위치(k) 기준으로 재키잉,TagHandler.retract에oldv==newv조기 반환 추가.base/attribute-plan.md: "이름 소유권"/"메커니즘" 두 절 전면 재작성 —rawNew/owners제거,AttributeGroupKeyHandler체크포인트+processAs/retractSelfAndUnder페어로 교체,AttributeKeyHandler무상태화,groupState를 "이름→키 객체 맵"에서 "이름 집합"으로 단순화.base/slot-plan.md:releaseOwner불일치 시 error로 강화,bindLifetime호출을attachSlot에서SlotHandler.process로 이동..claude/README.md: 4개 파일 행에 이 세션 요약 append.
서브에이전트 조사(문서 수정 없음, 결과만)
- Relate
GetStrong/SetStrong키 누락 + const-키 비효율 패턴 코퍼스 전수조사 —attribute-plan.md의owners:GetStrong(inst)1건(이미 이 세션에서 재설계로 해소됨) 외 추가 발견 없음, 나머지 전부 정상 사용. - Brand/
isXXnil 처리 전수조사 — 안전함 확인(위 2부 참고).
남는 것
Slot:ExtractAll()/:Splice(...)의 반환값을Slot(...)생성자에 바로 넣을 수 있다는 조합 예시를 문서에 추가하면 좋겠다는 사용자 코멘트 — 버그 아님, 착수 안 함(원하면 다음 세션에).luau-test/스파이크(20개, 이 세션에서 다룬 새 메커니즘은 아직 스파이크 파일 없음 —Dispatch.processAs/retractSelfAndUnder/AttributeGroupKeyHandler재설계를 검증할 스파이크 추가 여지가 있음, 필요시 다음 세션) — 여전히 사용자가luau로 안 돌려봄, M0 착수 전 최우선 게이트 그대로.