From 1221512341c1104b3a288039f1cb42f334acacf1 Mon Sep 17 00:00:00 2001 From: qwreey Date: Thu, 13 Aug 2026 17:53:00 +0900 Subject: [PATCH] =?UTF-8?q?fix(dispatch,docs):=207=EC=B0=A8=20=EA=B0=90?= =?UTF-8?q?=EC=82=AC=204=EB=9D=BC=EC=9A=B4=EB=93=9C=20=E2=80=94=20retractF?= =?UTF-8?q?rom=EC=9D=98=20nil=20=EA=B0=80=EB=93=9C=EA=B0=80=20=EA=B3=84?= =?UTF-8?q?=EC=95=BD=20=EC=9C=84=EB=B0=98=EC=9D=84=20=EC=A1=B0=EC=9A=A9?= =?UTF-8?q?=ED=9E=88=20=EC=82=BC=ED=82=A4=EB=8D=98=20=EA=B2=83,=20CLAUDE.m?= =?UTF-8?q?d=20=EB=AC=B8=EC=84=9C=20=EC=A7=80=EB=8F=84=20=EB=88=84?= =?UTF-8?q?=EB=9D=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 실제 의사코드 결함 1건(문서 정합성이 아니라 로직): - `Dispatch.retractFrom`이 `if retractor then ... end`로 감싸고 있어서, 핸들러가 계약을 어기고 `nil`을 반환해도 조용히 넘어감. 그런데 문서 두 곳(핸들러 계약 절, Handler 작성 체크리스트 7번)은 "생략하면 `attempt to call a nil value`로 크래시"라고 서술 — 그 가드 때문에 크래시조차 안 나는 게 실제였음. 더 나쁜 건 실제 결과: `list[index] = nil`로 **배열에 구멍**이 나서 (a) `#list`가 Lua 명세상 미정의가 되어 retractFrom의 순회 시작점이 어긋나고, (b) 그 자리가 비어 보여 `Dispatch.process`의 점유 체크를 통과 → **소유권 충돌 감지가 조용히 꺼짐**(0-Z가 다루는 바로 그 안전망). 가드에 걸리는 유일한 경우가 계약 위반뿐이므로(정상 경로는 process가 NOOP 마커를 먼저 박음) 프로젝트의 "즉시 error" 원칙대로 error로 전환, 서술 두 곳도 실제 실패 모드로 정정. CLAUDE.md "계획 문서 구조"에 `luau-test/`와 `audit/`가 통째로 누락돼 있던 것 추가 — 항상 로드되는 진입점인데, 지금 스파이크 상태의 소스 (`luau-test/STATUS.md`)와 0-Y의 근거(`audit/`)로 가는 길이 이 지도에 없었음. README의 `audit/` 행에도 파일 2개 명시(gcconn-trick-verification.md는 README 어디에도 등재돼 있지 않았음). 색인 완전성 전수 확인: base 17 / research 10 / archive 17 / reference 3 전부 README 등재됨(누락은 위 audit 1건뿐이었고 이번에 해소). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Y6hzeUi5QdLPEk69B6cXFa --- .claude/README.md | 2 +- .claude/base/bind-system-plan.md | 29 +++++++++++++++++++++++------ CLAUDE.md | 11 +++++++++++ 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/.claude/README.md b/.claude/README.md index f0e0ae3..5a89269 100644 --- a/.claude/README.md +++ b/.claude/README.md @@ -16,7 +16,7 @@ | `archive/` | 완료 + 사용자가 실사용/실기기로 직접 검증까지 마침 (구현 대상). **[2026-08-06 확장]** 완전히 뒤집힌 설계 결정을 원문+역전 이유+diff와 함께 보존하는 용도로도 사용(제목 `[역전됨]` — 한 번 확정했다가 뒤집힌 것) — 더 이상 능동적으로 참고 안 해도 되지만(토큰 낭비 방지 위해 `base/`/`research/`에서 뺌) `quadnomicon` 소재로는 나중에 쓸 수 있음. **[2026-08-07 확장]** 후보였다가 채택 안 된 것(확정한 적 없이 검토 후 기각)도 같은 방식으로 보존, 제목은 구분을 위해 `[기각됨]` — `[역전됨]`과 의미가 다르므로 혼동하지 말 것. **[2026-08-07 세 번째 확장]** 설계 반전/기각과 별개로, 에이전트가 문서 작성 중 스스로 낸 개념 혼동을 정정한 이력은 `[에이전트 실수]` 태그로 `agent-mistake.md` 하나에 모음(CLAUDE.md 세션 로그 중복 방지) | | `feedback/` | 실사용 피드백을 정리한 긴 로그 — 지금은 비어있음(구현 시작 전) | | `luau-test/` | **[2026-08-09 신설]** `base/` 확정 사항 중 "추론만으로 확정하고 실제 Luau로 부딪혀본 적 없는 것"(M0 스파이크 대상)을 `luau`/`luau-analyze`/`luau-lsp`/Roblox Studio로 사용자가 직접 돌려볼 독립 실행 스크립트 모음. **[2026-08-13 여섯 번째 세션, 첫 실측]** `luau`/`luau-analyze` 바이너리가 생겨 처음으로 실제 실행 — **런타임 12개 전원 통과**, 타입 쪽에서 `:Compute(fn)` lazy 핸들 계약이 Luau 추론과 충돌하는 게 드러남(`question.md` 0-Y). **`STATUS.md`가 상태의 소스**(pass / 사람 결정 필요 / 스파이크 깨짐 / 미실행 분류 — 사람이 먼저 볼 것만 위에), `luau-test/README.md`는 각 파일의 검증 의도·배경, 실행 결과 상세는 `audit/luau-test-first-run-2026-08-13.md` | -| `audit/` | **[2026-08-13 신설]** `luau-test/` 등 스파이크를 실제로 돌려본 뒤 "무엇이 확인됐고 무엇이 아직 안 됐는지"를 기록하는 곳 — 스크립트/계획 자체가 아니라 **실측 결과**만 다룸. base/luau-test와 달리 부분 확인(일부만 통과)도 있는 그대로 기록, 완전히 해소되면 관련 `base/`/`luau-test/README.md` 캐비엇을 지우고 이 문서는 근거로 남김 | +| `audit/` | **[2026-08-13 신설]** `luau-test/` 등 스파이크를 실제로 돌려본 뒤 "무엇이 확인됐고 무엇이 아직 안 됐는지"를 기록하는 곳 — 스크립트/계획 자체가 아니라 **실측 결과**만 다룸. base/luau-test와 달리 부분 확인(일부만 통과)도 있는 그대로 기록, 완전히 해소되면 관련 `base/`/`luau-test/README.md` 캐비엇을 지우고 이 문서는 근거로 남김. **현재 2개**: `luau-test-first-run-2026-08-13.md`(첫 실측 라운드 전체 — 런타임 12개 통과, `question.md` 0-Y의 1차 근거), `gcconn-trick-verification.md`(사용자가 Studio에서 직접 돌린 gcconn 트릭 부분 확인 — `luau-test/10`의 A 섹션 앞부분만, A-1/A-2/B/C는 미확인) | | `session/` | **[2026-08-11 신설]** 세션별 상세 로그 원문(시행착오·정정 전 서술 포함, `quadnomicon` 개발로그 소재용) — 루트 `CLAUDE.md`가 3196줄까지 불어나 성능 저하를 유발해서 분리함. 파일명 `YYYY-MM-DD-NN-slug.md`, CLAUDE.md의 "세션 히스토리" 절에서 각 항목이 여기로 링크. 항상 읽을 필요 없음 — 결정의 논의 과정이 궁금할 때만 | | `initreq/` | 프로젝트 착수 시 클론해둔 참고 레포(quad v1, fusion, vide, rbvm, tbox, code-docker) + PA님 실 코드(`artworks/`, 4차 라운드 교차검증 근거) + 원본 요청(`req.md`, `raw-userinput.md`) + `quad2-try`(이전에 시도했다 폐기한 v2 재작성 시도 — 리서치 완료, 결론은 `base/bind-system-plan.md`) — 읽기 전용 리서치 소스, 여기 내용을 옮기지 말고 항상 원본 그대로 유지 | diff --git a/.claude/base/bind-system-plan.md b/.claude/base/bind-system-plan.md index 58b628e..c1f5e7b 100644 --- a/.claude/base/bind-system-plan.md +++ b/.claude/base/bind-system-plan.md @@ -70,9 +70,15 @@ v1의 `ProcessQuadProperty`(`.claude/initreq/quad/src/class.lua:134-214`)는 계기·근거는 아래 "Dispatch 체인" 절). **반환값 생략 불가 — 정리할 게 없는 핸들러도 항상 `function() end`(no-op) 형태로 반환할 것** — `Dispatch.process`(아래 절)가 이 반환값을 `chains`에 저장해뒀다가 - 나중에 정확히 이 클로저 하나만 호출해서 정리하므로, 반환을 - 생략하면(`nil`을 돌려주면) 나중에 `attempt to call a nil value`로 - 크래시(예전 "retract 필드 생략 불가" 규칙과 같은 이유, 자리만 옮겨옴). + 나중에 정확히 이 클로저 하나만 호출해서 정리하므로(예전 "retract 필드 + 생략 불가" 규칙과 같은 이유, 자리만 옮겨옴). **[정정, 2026-08-13 7차 + 감사] 생략했을 때 실제로 벌어지는 일**: `list[index] = nil`이 되어 + **배열에 구멍이 뚫림** — `#list`가 Lua 명세상 정의되지 않게 되고 + (`retractFrom`의 순회 시작점이 어긋남), 그 자리가 비어 보이므로 + `Dispatch.process`의 점유 체크도 통과해버려 **소유권 충돌 감지가 조용히 + 꺼짐**. 옛 서술("`attempt to call a nil value`로 크래시")은 부정확했음 — + `retractFrom`이 `if retractor then` 가드로 넘기고 있어서 크래시조차 안 + 났음. 그 가드를 즉시 error로 바꿔 계약 위반이 실제로 드러나게 함. **핸들러가 직접 자기 자신의 하위 위임(재귀 `Dispatch.process`로 만든 것들)까지 클로저 안에서 다시 정리할 필요는 없음** — `Dispatch. retractFrom`의 순회 구조 자체가 항상 깊은 인덱스부터 먼저 정리하고 @@ -531,9 +537,14 @@ function Dispatch.retractFrom(inst, k, index, v) if not list then return end for i = #list, index, -1 do local retractor = list[i] - if retractor then - retractor(if i == index then v else nil) + -- [정정, 2026-08-13 7차 감사] 예전엔 `if retractor then ... end` 가드였으나, + -- 그 가드에 걸리는 경우는 **핸들러가 계약을 어기고 nil을 반환한 것뿐**이고 + -- (정상 경로는 process가 항상 NOOP 마커를 먼저 박음) 조용히 넘기면 이미 + -- 배열에 구멍이 난 뒤라 `#list`도 점유 체크도 같이 망가짐 — 즉시 error가 맞음. + if retractor == nil then + error("Dispatch: 인덱스 " .. i .. "에 retractor가 없음 — 핸들러가 반환을 생략했음") end + retractor(if i == index then v else nil) list[i] = nil end end @@ -720,7 +731,13 @@ end `index`라고 이름 붙였다가 시그니처 자체가 계약과 어긋난 전례가 있음. **7. 반환 생략 금지.** 정리할 게 없어도 `function() end`. `nil`을 -반환하면 `retractFrom`이 `attempt to call a nil value`로 크래시. +반환하면 `list[index]`에 **구멍이 뚫려** `#list`가 정의되지 않게 되고 +(순회 시작점이 어긋남) 그 자리가 비어 보여 **점유 체크까지 조용히 +통과** — 즉 소유권 충돌 감지가 꺼짐. **[정정, 2026-08-13 7차 감사]** +예전엔 이 항목이 "`attempt to call a nil value`로 크래시"라고 적혀 +있었으나 `retractFrom`이 `if retractor then`으로 넘기고 있어 크래시조차 +안 나는 게 실제였음 — 지금은 `retractFrom`이 즉시 error를 냄(위 +"Dispatch 체인" 절 의사코드). ### Length/Offset — 여러 Slot이 형제로 섞일 때 순서 보장 (2026-08-09 여섯 번째 세션) diff --git a/CLAUDE.md b/CLAUDE.md index 17c0588..3979e1e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,6 +52,17 @@ modifier/Ref의 컴포넌트 경계 통과 방식) 논의도 2026-08-04 세션 `.claude/README.md`가 소스, 여기서 개수 반복 안 함(과거에 "두 개뿐"이라 적어놨다가 새 문서 추가될 때마다 안 갱신되는 패턴이 반복돼서 아예 안 세기로 함). +- `.claude/luau-test/` — **[2026-08-09 신설]** "추론만으로 확정하고 실제 + Luau로 부딪혀본 적 없는 것"을 미리 검증하는 독립 실행 스파이크 20개 + (`luau <파일>` / `luau-analyze <파일>`). **상태의 소스는 `STATUS.md`** + (pass / 사람 결정 필요 / 스파이크 깨짐 / 미실행), 각 파일이 뭘 왜 + 검증하는지는 `README.md`. 2026-08-13에 첫 실측 완료 — 런타임 12개 전원 + 통과, 남은 건 Studio 전용 `10`과 코드가 깨진 `13`/`15`/`16`. +- `.claude/audit/` — **[2026-08-13 신설]** 스파이크를 실제로 돌린 **실측 + 결과**만 기록(계획/스크립트 아님). 부분 확인도 있는 그대로 남김 — + `luau-test-first-run-2026-08-13.md`(첫 실측 라운드 전체, 0-Y의 근거), + `gcconn-trick-verification.md`(사용자가 Studio에서 직접 돌린 gcconn 트릭 + 부분 확인). - `.claude/qa-request/`, `.claude/feedback/` — 구현 시작되면 쓰기 시작함, 지금은 비어있음. `.claude/archive/`는 원래 같은 취급이었으나 2026-08-06 세 번째 세션부터 **완전히 뒤집힌 설계 결정을 원문+역전