docs(audit): 10차 세션 — 병렬 에이전트 코퍼스 감사, 부정확성 7건 수정

9차 세션의 구조 변경(luau-test 재편/bind-system-plan 분할/question.md
트림)이 남긴 반영 누락을 6개 병렬 에이전트로 찾아 즉시 수정:

- luau-test 재편 후 깨진 flat 경로 참조 9곳을 파일명+실제 폴더로 정정
- bind-system-plan.md 분할 후 자기참조/외부참조 깨짐 8곳 정정
- ref-plan.md에 0-Z 배너가 안 옮겨와 옛 재디스패치 모델을 무배너로
  서술 중이던 것 발견 — 배너 추가, 반영 대상 6개→7개로 갱신
- "8차 세션"으로 잘못 표기된 9차 세션 작업 17곳(git 커밋 타임스탬프로
  교차검증) 정정
- question.md 트림 중 빠진 열린 질문(State<State<T>> 평탄화) 복원,
  트림 후 깨진 참조 2곳 정정
- ROADMAP.md M0 섹션에 0-Y/0-Z 게이트 표시 누락 보강

doc-check.py ERROR 0 유지. 새로 연 설계 질문 없음 — 전부 기존 서술
정합성 문제.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y6hzeUi5QdLPEk69B6cXFa
This commit is contained in:
qwreey 2026-08-13 18:32:53 +09:00
parent 540c142969
commit aaefa08c1c
Signed by: qwreey
GPG key ID: D28DB79297A214BD
25 changed files with 223 additions and 51 deletions

View file

@ -16,8 +16,8 @@
| `archive/` | 완료 + 사용자가 실사용/실기기로 직접 검증까지 마침 (구현 대상). **[2026-08-06 확장]** 완전히 뒤집힌 설계 결정을 원문+역전 이유+diff와 함께 보존하는 용도로도 사용(제목 `[역전됨]` — 한 번 확정했다가 뒤집힌 것) — 더 이상 능동적으로 참고 안 해도 되지만(토큰 낭비 방지 위해 `base/`/`research/`에서 뺌) `quadnomicon` 소재로는 나중에 쓸 수 있음. **[2026-08-07 확장]** 후보였다가 채택 안 된 것(확정한 적 없이 검토 후 기각)도 같은 방식으로 보존, 제목은 구분을 위해 `[기각됨]``[역전됨]`과 의미가 다르므로 혼동하지 말 것. **[2026-08-07 세 번째 확장]** 설계 반전/기각과 별개로, 에이전트가 문서 작성 중 스스로 낸 개념 혼동을 정정한 이력은 `[에이전트 실수]` 태그로 `agent-mistake.md` 하나에 모음(CLAUDE.md 세션 로그 중복 방지) | | `archive/` | 완료 + 사용자가 실사용/실기기로 직접 검증까지 마침 (구현 대상). **[2026-08-06 확장]** 완전히 뒤집힌 설계 결정을 원문+역전 이유+diff와 함께 보존하는 용도로도 사용(제목 `[역전됨]` — 한 번 확정했다가 뒤집힌 것) — 더 이상 능동적으로 참고 안 해도 되지만(토큰 낭비 방지 위해 `base/`/`research/`에서 뺌) `quadnomicon` 소재로는 나중에 쓸 수 있음. **[2026-08-07 확장]** 후보였다가 채택 안 된 것(확정한 적 없이 검토 후 기각)도 같은 방식으로 보존, 제목은 구분을 위해 `[기각됨]``[역전됨]`과 의미가 다르므로 혼동하지 말 것. **[2026-08-07 세 번째 확장]** 설계 반전/기각과 별개로, 에이전트가 문서 작성 중 스스로 낸 개념 혼동을 정정한 이력은 `[에이전트 실수]` 태그로 `agent-mistake.md` 하나에 모음(CLAUDE.md 세션 로그 중복 방지) |
| `feedback/` | 실사용 피드백을 정리한 긴 로그 — 지금은 비어있음(구현 시작 전) | | `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` | | `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` 캐비엇을 지우고 이 문서는 근거로 남김. **현재 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는 미확인) | | `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 트릭 부분 확인 — `10`의 A 섹션 앞부분만, A-1/A-2/B/C는 미확인) |
| `tools/` | **[2026-08-13 여덟 번째 세션 신설]** 코퍼스 기계 점검 — `doc-check.py`가 깨진 파일/절 참조, README 색인 누락, 날짜 없는 시한부 주장("아직 안 돌려봄" 등), 미반영 ⚠️ 배너를 한 번에 훑음. **중대 변경 후 커밋 전에 돌릴 것**(`python3 .claude/tools/doc-check.py`) — 수동 감사에서 나온 발견의 대부분이 이 종류였고, 실제로 문서를 쪼개다 잘못 옮긴 참조를 이게 잡아냄. ERROR는 고치고 WARN은 판단 대상 | | `tools/` | **[2026-08-13 아홉 번째 세션 신설]** 코퍼스 기계 점검 — `doc-check.py`가 깨진 파일/절 참조, README 색인 누락, 날짜 없는 시한부 주장("아직 안 돌려봄" 등), 미반영 ⚠️ 배너를 한 번에 훑음. **중대 변경 후 커밋 전에 돌릴 것**(`python3 .claude/tools/doc-check.py`) — 수동 감사에서 나온 발견의 대부분이 이 종류였고, 실제로 문서를 쪼개다 잘못 옮긴 참조를 이게 잡아냄. ERROR는 고치고 WARN은 판단 대상 |
| `session/` | **[2026-08-11 신설]** 세션별 상세 로그 원문(시행착오·정정 전 서술 포함, `quadnomicon` 개발로그 소재용) — 루트 `CLAUDE.md`가 3196줄까지 불어나 성능 저하를 유발해서 분리함. 파일명 `YYYY-MM-DD-NN-slug.md`, CLAUDE.md의 "세션 히스토리" 절에서 각 항목이 여기로 링크. 항상 읽을 필요 없음 — 결정의 논의 과정이 궁금할 때만 | | `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`) — 읽기 전용 리서치 소스, 여기 내용을 옮기지 말고 항상 원본 그대로 유지 | | `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`) — 읽기 전용 리서치 소스, 여기 내용을 옮기지 말고 항상 원본 그대로 유지 |
@ -45,9 +45,9 @@
| `attribute-plan.md` | **[2026-08-07 여덟 번째 세션 신설]** 단일 키 `[AttributeKey<T> "Name"]`(구 `Attribute<T>`) — `SetAttribute(name, nil)`이 네이티브 지우기라 `None` 센티널과 가장 깔끔하게 맞아떨어짐. **[2026-08-11 아홉 번째 세션]** 여러 Store를 한 번에 attribute로 묶는 그룹 `Attribute(...)` 프리미티브 신설(`Tag`와 동형 array-part 값 객체, `Merged`로 헤테로지니어스 Store 합성), 이름 충돌 방지로 단일 키를 `AttributeKey`로 리네임(잠정). **[같은 세션 후속]** `AttributeKey(name)`이 이름별 weak 캐시로 동등성 보장하도록 확정되며, 그룹 Handler는 자기 완결형 재구현 대신 메모이즈된 키로 기존 단일 키 경로에 재귀 위임하는 걸로 개정(중복 구현 제거). **[2026-08-12 열 번째 세션]** 그룹/직접 쓰기가 같은 이름을 동시에 관리하는 충돌을 막기 위해 그룹은 공개 캐시 대신 `rawNew(name)` 전용 키+소유권 `Relate`로 전환. **[열한 번째 세션]** `retract`가 store 재발행마다 항상 불린다는 정정에 맞춰 `AttributeKeyHandler.retract`를 손봄(이 시점엔 `v==nil` 가드 버전 — 아래 열여섯 번째 세션에서 최종 재정정됨), 그룹의 "남아있는 이름" 위임도 매번 `retractUnder`를 먼저 부르도록 정정(체인 누수 방지). **[2026-08-12 열여섯 번째 세션, 최종 재정정]** `retract`는 완전 no-op으로 굳어짐(`SetAttribute`는 오직 `process(inst,k,nil)`에서만) — Attribute는 명시적 `None`/`nil`로만 지워지고, 그룹 diff나 컴포넌트 언마운트로 이름이 조용히 사라져도 값은 자동으로 안 지워짐(`Ref`의 "Destroy 무관, 정리는 명시적으로" 철학과 통일), 단 사라진 이름의 *구독*은 끊어 자원 누수는 막음 — 위 "v==nil 가드" 버전은 이걸로 폐기. **[2026-08-13 세션, 전면 재정정]** `rawNew`+`owners` 수동 레지스트리 방식이 "그룹이 이름을 놓았다 다시 포함하면 자기 자신과 충돌"하는 실제 버그로 확인됨 — `AttributeGroupKeyHandler`라는 `isHandlable` 없는 순수 체크포인트 핸들러를 `Dispatch.processAs`로 명시 push하고 `Dispatch.retractSelfAndUnder`로 통째 철거하는 방식으로 전면 재설계, 소유권 충돌 감지도 별도 레지스트리 없이 기존 재진입 가드가 대신 잡아줌(`bind-system-plan.md` 참고). `AttributeKeyHandler`는 다시 완전 무상태로 단순화됨. **[2026-08-13 세션, 다섯 번째, 전면 재설계 — 체크포인트조차 불필요해짐]** `Dispatch`가 인덱스 기반으로 재설계되며 `AttributeGroupKeyHandler`/`processAs`/`retractSelfAndUnder`를 전부 걷어냄 — 그룹이 그냥 공개 `AttributeKey(name)`으로 항상 인덱스 1부터 `Dispatch.process`/`retractFrom`을 직접 부르면 끝(점유 체크 자체가 소유권 충돌 감지), `groupState` Relate도 필요 없어짐(반환 클로저가 이름 집합을 직접 캡처) — 중간 버전은 `archive/checkpoint-handler-pattern-reversed.md`. **[2026-08-13 감사, 정정]** 그런데 그 의사코드가 `process` 안에서 이름마다 `retractFrom(...,1,...)`을 먼저 부르고 있어 **인덱스 1이 무조건 비워지는 바람에 점유 체크가 전혀 작동하지 않았음**(그룹↔그룹 사이에서 조용한 last-write-wins가 그대로 남아 있었음) — `process``Dispatch.process`만 부르고 철거는 반환 클로저가 자기가 등록한 이름 전부에 대해 하도록 정정. 그룹 Handler 시그니처가 계약과 안 맞던 것(`process(inst,index,v)` 3-인자)도 같이 수정 | | `attribute-plan.md` | **[2026-08-07 여덟 번째 세션 신설]** 단일 키 `[AttributeKey<T> "Name"]`(구 `Attribute<T>`) — `SetAttribute(name, nil)`이 네이티브 지우기라 `None` 센티널과 가장 깔끔하게 맞아떨어짐. **[2026-08-11 아홉 번째 세션]** 여러 Store를 한 번에 attribute로 묶는 그룹 `Attribute(...)` 프리미티브 신설(`Tag`와 동형 array-part 값 객체, `Merged`로 헤테로지니어스 Store 합성), 이름 충돌 방지로 단일 키를 `AttributeKey`로 리네임(잠정). **[같은 세션 후속]** `AttributeKey(name)`이 이름별 weak 캐시로 동등성 보장하도록 확정되며, 그룹 Handler는 자기 완결형 재구현 대신 메모이즈된 키로 기존 단일 키 경로에 재귀 위임하는 걸로 개정(중복 구현 제거). **[2026-08-12 열 번째 세션]** 그룹/직접 쓰기가 같은 이름을 동시에 관리하는 충돌을 막기 위해 그룹은 공개 캐시 대신 `rawNew(name)` 전용 키+소유권 `Relate`로 전환. **[열한 번째 세션]** `retract`가 store 재발행마다 항상 불린다는 정정에 맞춰 `AttributeKeyHandler.retract`를 손봄(이 시점엔 `v==nil` 가드 버전 — 아래 열여섯 번째 세션에서 최종 재정정됨), 그룹의 "남아있는 이름" 위임도 매번 `retractUnder`를 먼저 부르도록 정정(체인 누수 방지). **[2026-08-12 열여섯 번째 세션, 최종 재정정]** `retract`는 완전 no-op으로 굳어짐(`SetAttribute`는 오직 `process(inst,k,nil)`에서만) — Attribute는 명시적 `None`/`nil`로만 지워지고, 그룹 diff나 컴포넌트 언마운트로 이름이 조용히 사라져도 값은 자동으로 안 지워짐(`Ref`의 "Destroy 무관, 정리는 명시적으로" 철학과 통일), 단 사라진 이름의 *구독*은 끊어 자원 누수는 막음 — 위 "v==nil 가드" 버전은 이걸로 폐기. **[2026-08-13 세션, 전면 재정정]** `rawNew`+`owners` 수동 레지스트리 방식이 "그룹이 이름을 놓았다 다시 포함하면 자기 자신과 충돌"하는 실제 버그로 확인됨 — `AttributeGroupKeyHandler`라는 `isHandlable` 없는 순수 체크포인트 핸들러를 `Dispatch.processAs`로 명시 push하고 `Dispatch.retractSelfAndUnder`로 통째 철거하는 방식으로 전면 재설계, 소유권 충돌 감지도 별도 레지스트리 없이 기존 재진입 가드가 대신 잡아줌(`bind-system-plan.md` 참고). `AttributeKeyHandler`는 다시 완전 무상태로 단순화됨. **[2026-08-13 세션, 다섯 번째, 전면 재설계 — 체크포인트조차 불필요해짐]** `Dispatch`가 인덱스 기반으로 재설계되며 `AttributeGroupKeyHandler`/`processAs`/`retractSelfAndUnder`를 전부 걷어냄 — 그룹이 그냥 공개 `AttributeKey(name)`으로 항상 인덱스 1부터 `Dispatch.process`/`retractFrom`을 직접 부르면 끝(점유 체크 자체가 소유권 충돌 감지), `groupState` Relate도 필요 없어짐(반환 클로저가 이름 집합을 직접 캡처) — 중간 버전은 `archive/checkpoint-handler-pattern-reversed.md`. **[2026-08-13 감사, 정정]** 그런데 그 의사코드가 `process` 안에서 이름마다 `retractFrom(...,1,...)`을 먼저 부르고 있어 **인덱스 1이 무조건 비워지는 바람에 점유 체크가 전혀 작동하지 않았음**(그룹↔그룹 사이에서 조용한 last-write-wins가 그대로 남아 있었음) — `process``Dispatch.process`만 부르고 철거는 반환 클로저가 자기가 등록한 이름 전부에 대해 하도록 정정. 그룹 Handler 시그니처가 계약과 안 맞던 것(`process(inst,index,v)` 3-인자)도 같이 수정 |
| `onchange-plan.md` | **[2026-08-10 세션 신설]** `OnChange(name)``GetPropertyChangedSignal` 바인딩 전용 DI 키, `Attribute`와 달리 제네릭 타입 파라미터 없음(콜백 타입은 인라인 명시, 이벤트 바인딩과 같은 급 트레이드오프). 전부 quad-roblox(`Handlers/OnChange.luau`), `State<function>`은 기존 이벤트 store-bind 메커니즘 재사용. **[2026-08-11 아홉 번째 세션 후속]** `AttributeKey`와 동일한 이름별 weak 캐시로 `OnChange(a) == OnChange(a)` 동등성 보장 | | `onchange-plan.md` | **[2026-08-10 세션 신설]** `OnChange(name)``GetPropertyChangedSignal` 바인딩 전용 DI 키, `Attribute`와 달리 제네릭 타입 파라미터 없음(콜백 타입은 인라인 명시, 이벤트 바인딩과 같은 급 트레이드오프). 전부 quad-roblox(`Handlers/OnChange.luau`), `State<function>`은 기존 이벤트 store-bind 메커니즘 재사용. **[2026-08-11 아홉 번째 세션 후속]** `AttributeKey`와 동일한 이름별 weak 캐시로 `OnChange(a) == OnChange(a)` 동등성 보장 |
| `relate-plan.md` | **[2026-08-08 신설]** `Relate``inst`를 weak 키로 하는 범용 릴레이션 프리미티브(`SetWeak`/`GetWeak`/`SetStrong`/`GetStrong`, 비싱글톤 생성자). 구 `base.perInstanceState(inst)` placeholder를 대체·정식 승격, `lifecycle-pattern.md``bindLifetime`/`canExecute`가 그 위에 얹힘. **[2026-08-12 열세/열네 번째 세션]** 서로 다른 두 `Relate`가 서로의 키를 상대방 값으로 강하게 붙잡는 상호 순환 패턴 경고 신설 — Luau에 ephemeron 테이블이 없어(공식 확인, luau.org/compatibility) 이런 순환은 실제로 GC가 안 됨, `Slot``kSlotMap`/`slotOwner`가 실제 사례이자 수정 사례 | | `relate-plan.md` | **[2026-08-08 신설]** `Relate``inst`를 weak 키로 하는 범용 릴레이션 프리미티브(`SetWeak`/`GetWeak`/`SetStrong`/`GetStrong`, 비싱글톤 생성자). 구 `base.perInstanceState(inst)` placeholder를 대체·정식 승격, `lifecycle-pattern.md``bindLifetime`/`canExecute`가 그 위에 얹힘. **[2026-08-12 열세/열네 번째 세션]** 서로 다른 두 `Relate`가 서로의 키를 상대방 값으로 강하게 붙잡는 상호 순환 패턴 경고 신설 — Luau에 ephemeron 테이블이 없어(공식 확인, luau.org/compatibility) 이런 순환은 실제로 GC가 안 됨, `Slot``kSlotMap`/`slotOwner`가 실제 사례이자 수정 사례 |
| `ref-plan.md` | **[2026-08-13 여덟 번째 세션, `bind-system-plan.md`에서 분리]** `Ref`/`PreRef` — 지연 없는 확정 값 박스. 용도 재정의(leaf 노드를 담는 용도로도, leaf 노드에 바인딩하는 용도로도), `.Value`+`:Set`/`:Callback`/`:Wait`(전부 self 반환), `Ref`의 retract가 `TagHandler`와 같은 `Relate` diff 패턴이라는 것, 이중 바인딩 금지(`canBound`), `PreRef` 호이스팅 pre-pass와 1회용 `_fired` 가드. **분리는 순수 이동 — 결정은 하나도 안 바뀜** | | `ref-plan.md` | **[2026-08-13 아홉 번째 세션, `bind-system-plan.md`에서 분리]** `Ref`/`PreRef` — 지연 없는 확정 값 박스. 용도 재정의(leaf 노드를 담는 용도로도, leaf 노드에 바인딩하는 용도로도), `.Value`+`:Set`/`:Callback`/`:Wait`(전부 self 반환), `Ref`의 retract가 `TagHandler`와 같은 `Relate` diff 패턴이라는 것, 이중 바인딩 금지(`canBound`), `PreRef` 호이스팅 pre-pass와 1회용 `_fired` 가드. **분리는 순수 이동 — 결정은 하나도 안 바뀜** |
| `event-plan.md` | **[2026-08-13 여덟 번째 세션, `bind-system-plan.md`에서 분리 — 사용자가 직접 지목]** 이벤트 바인딩 — 핸들러가 self(Instance)를 **안** 받는다는 확정(Ref가 이미 커버, 이중 쓰기 경로 방지), 이벤트도 store-bind 가능하며 `false`를 넣으면 disconnect. 이벤트 *네이밍* 관례는 인스턴스 생성과 한 절에 섞여 있어 `bind-system-plan.md`에 남음, `GetPropertyChangedSignal``onchange-plan.md`. **분리는 순수 이동** | | `event-plan.md` | **[2026-08-13 아홉 번째 세션, `bind-system-plan.md`에서 분리 — 사용자가 직접 지목]** 이벤트 바인딩 — 핸들러가 self(Instance)를 **안** 받는다는 확정(Ref가 이미 커버, 이중 쓰기 경로 방지), 이벤트도 store-bind 가능하며 `false`를 넣으면 disconnect. 이벤트 *네이밍* 관례는 인스턴스 생성과 한 절에 섞여 있어 `bind-system-plan.md`에 남음, `GetPropertyChangedSignal``onchange-plan.md`. **분리는 순수 이동** |
| `brand-plan.md` | **[2026-08-13 여덟 번째 세션, `bind-system-plan.md`에서 분리]** `Brand` — 런타임 nominal 타입 판별 통합 메커니즘(`Brand.set`/`Brand.get`), `isState`를 10종 branded 타입 전부로 일반화. 동작/구현은 확정, **이름 `Brand` 자체만 용어 정리 대기**(`question.md` 1번). **분리는 순수 이동** | | `brand-plan.md` | **[2026-08-13 아홉 번째 세션, `bind-system-plan.md`에서 분리]** `Brand` — 런타임 nominal 타입 판별 통합 메커니즘(`Brand.set`/`Brand.get`), `isState`를 10종 branded 타입 전부로 일반화. 동작/구현은 확정, **이름 `Brand` 자체만 용어 정리 대기**(`question.md` 1번). **분리는 순수 이동** |
| `tween-plan.md` | **[2026-08-12 세션, `research/`에서 승격]** 값-레벨 `Tween<T>` 래퍼(PropertyHandler가 소비, 구 특수 bind key 모델은 `archive/tween-special-bind-key-reversed.md`). 3-상태 릴레이션 슬롯(`{Tween,Value}\|true\|nil`), `T'=T\|Tween<T>` 타입 치환. 옵션 값 모양은 `Info: TweenInfo?` 우선+편의 필드 폴백, override는 `Tween.Cancel`(기본)/`Tween.Finish` 2값. `Animate(info)``Tween` opts를 `T\|State<T>`로 받아 `:Apply`로 꽂는 sugar. 자연완료 시 per-instance 북키핑은 정리 안 해도 됨으로 확정(목표값 도달 상태라 부작용 없음, Completed 이벤트 구독 장치는 오버엔지니어링으로 판단). `initValue`는 사용자가 직접 처리(에이전트 범위 제외) | | `tween-plan.md` | **[2026-08-12 세션, `research/`에서 승격]** 값-레벨 `Tween<T>` 래퍼(PropertyHandler가 소비, 구 특수 bind key 모델은 `archive/tween-special-bind-key-reversed.md`). 3-상태 릴레이션 슬롯(`{Tween,Value}\|true\|nil`), `T'=T\|Tween<T>` 타입 치환. 옵션 값 모양은 `Info: TweenInfo?` 우선+편의 필드 폴백, override는 `Tween.Cancel`(기본)/`Tween.Finish` 2값. `Animate(info)``Tween` opts를 `T\|State<T>`로 받아 `:Apply`로 꽂는 sugar. 자연완료 시 per-instance 북키핑은 정리 안 해도 됨으로 확정(목표값 도달 상태라 부작용 없음, Completed 이벤트 구독 장치는 오버엔지니어링으로 판단). `initValue`는 사용자가 직접 처리(에이전트 범위 제외) |
## `reference/` — 온디맨드 참고 자료 (2026-08-07 신설) ## `reference/` — 온디맨드 참고 자료 (2026-08-07 신설)
@ -93,7 +93,7 @@
| `onchange-per-property-codegen-rejected.md` | **[기각됨, 2026-08-10 신설]** `OnChange.PropertyName` 프로퍼티별 정적 코드 생성 — Attribute의 정적 지름길과 달리 (클래스 수 × 프로퍼티 수) 규모로 폭발해 기각, `OnChange(name)` 단일 팩토리로 대체 | | `onchange-per-property-codegen-rejected.md` | **[기각됨, 2026-08-10 신설]** `OnChange.PropertyName` 프로퍼티별 정적 코드 생성 — Attribute의 정적 지름길과 달리 (클래스 수 × 프로퍼티 수) 규모로 폭발해 기각, `OnChange(name)` 단일 팩토리로 대체 |
| `retract-always-fires-reversed.md` | **[역전됨, 2026-08-12 열한 번째 세션 신설]** "핸들러 타입이 안 바뀌면 retract 없이 process가 diff" — 실제로는 `retract`가 store 재발행마다 항상 불림(핸들러 타입 무관). `Tag`/`Ref`/`Slot`/`Attribute` 전부 이 오류 위에서 설계돼 있었음이 드러나 한 세션에 전부 정정 | | `retract-always-fires-reversed.md` | **[역전됨, 2026-08-12 열한 번째 세션 신설]** "핸들러 타입이 안 바뀌면 retract 없이 process가 diff" — 실제로는 `retract`가 store 재발행마다 항상 불림(핸들러 타입 무관). `Tag`/`Ref`/`Slot`/`Attribute` 전부 이 오류 위에서 설계돼 있었음이 드러나 한 세션에 전부 정정 |
| `slot-discard-no-portal-reversed.md` | **[역전됨, 2026-08-13 일곱 번째 세션 신설]** Slot의 **"retract = 폐기, 옮기지 않음"(2026-08-04 확정) + "portal은 오버엔지니어링이라 안 함"** — 여섯 번째 세션에 `State<Slot>` 교체가 파괴에서 **언마운트**로 뒤집히며 portal이 별도 기능이 아니라 그 귀결이 됨(`state<Frame>`와 동일한 시맨틱). `base/slot-plan.md`에 히스토리로 남아 있던 세 덩어리(확정 문단 + `State<Slot?>` 왕복 분석 + 포탈 검토와 숙제 셋)를 원문 그대로 이전, 숙제 셋이 각각 어떻게 결말났는지도 정리 | | `slot-discard-no-portal-reversed.md` | **[역전됨, 2026-08-13 일곱 번째 세션 신설]** Slot의 **"retract = 폐기, 옮기지 않음"(2026-08-04 확정) + "portal은 오버엔지니어링이라 안 함"** — 여섯 번째 세션에 `State<Slot>` 교체가 파괴에서 **언마운트**로 뒤집히며 portal이 별도 기능이 아니라 그 귀결이 됨(`state<Frame>`와 동일한 시맨틱). `base/slot-plan.md`에 히스토리로 남아 있던 세 덩어리(확정 문단 + `State<Slot?>` 왕복 분석 + 포탈 검토와 숙제 셋)를 원문 그대로 이전, 숙제 셋이 각각 어떻게 결말났는지도 정리 |
| `question-resolved.md` | **[해소 아카이브, 2026-08-13 여덟 번째 세션 신설]** `question.md`에서 걷어낸 **결정 완료** 항목 전부(당시 32개 `[해소됨]` 마커) — 추가 프리미티브 필요성 라운드, 구현 착수 직전 감사 요약, 확정된 용어들(`State`/`Relate`/`List`/`canBound`/`Ref`/`PreRef`/`Peek`/`isState`/`None`/`Handler`), 포탈·`State<Slot?>` 왕복 해소 등. 분리 직전 전문을 그대로 보존. **`question.md`는 이제 사용자가 답해야 할 것만 담음** — 항목이 해소되면 여기로 옮길 것 | | `question-resolved.md` | **[해소 아카이브, 2026-08-13 아홉 번째 세션 신설]** `question.md`에서 걷어낸 **결정 완료** 항목 전부(당시 32개 `[해소됨]` 마커) — 추가 프리미티브 필요성 라운드, 구현 착수 직전 감사 요약, 확정된 용어들(`State`/`Relate`/`List`/`canBound`/`Ref`/`PreRef`/`Peek`/`isState`/`None`/`Handler`), 포탈·`State<Slot?>` 왕복 해소 등. 분리 직전 전문을 그대로 보존. **`question.md`는 이제 사용자가 답해야 할 것만 담음** — 항목이 해소되면 여기로 옮길 것 |
| `checkpoint-handler-pattern-reversed.md` | **[역전됨, 2026-08-13 다섯 번째 세션 신설]** `AttributeGroupHandler`의 이름 소유권 충돌을 고치려고 만든 `Dispatch.processAs`/`Dispatch.retractSelfAndUnder` 체크포인트 핸들러 패턴(같은 날 네 번째 세션 신설) — `chains`를 핸들러 identity가 아니라 재귀 깊이 인덱스로 추적하는 더 근본적인 재설계로 대체되며 같은 날 바로 불필요해짐. `State<State<T>>`도 이 재설계로 UB에서 정상 지원 대상으로 바뀜 | | `checkpoint-handler-pattern-reversed.md` | **[역전됨, 2026-08-13 다섯 번째 세션 신설]** `AttributeGroupHandler`의 이름 소유권 충돌을 고치려고 만든 `Dispatch.processAs`/`Dispatch.retractSelfAndUnder` 체크포인트 핸들러 패턴(같은 날 네 번째 세션 신설) — `chains`를 핸들러 identity가 아니라 재귀 깊이 인덱스로 추적하는 더 근본적인 재설계로 대체되며 같은 날 바로 불필요해짐. `State<State<T>>`도 이 재설계로 UB에서 정상 지원 대상으로 바뀜 |
## 참고 ## 참고

View file

@ -1,6 +1,6 @@
# [해소 아카이브] `question.md`에서 걷어낸 결정 완료 항목 # [해소 아카이브] `question.md`에서 걷어낸 결정 완료 항목
**분리 시점**: 2026-08-13 여덟 번째 세션. 사용자 지적 — **분리 시점**: 2026-08-13 아홉 번째 세션. 사용자 지적 —
> "question.md 또한, 사람이 봐야하는 문서인데 해결된 것이 많아서 필터해서 > "question.md 또한, 사람이 봐야하는 문서인데 해결된 것이 많아서 필터해서
> 필요한 부분만 읽어보기 힘듦. archive 되어야할 요소는 archive 에 > 필요한 부분만 읽어보기 힘듦. archive 되어야할 요소는 archive 에

View file

@ -1,6 +1,6 @@
# gcconn 트릭 — 부분 실측 검증 결과 # gcconn 트릭 — 부분 실측 검증 결과
**상태**: 부분 확인(2026-08-13). `luau-test/10-roblox-studio-checks.server.luau`의 **상태**: 부분 확인(2026-08-13). `10-roblox-studio-checks.server.luau`(`luau-test/not-run/`)
공식 스크립트가 아니라 사용자가 별도로 작성한 저수준 검증 스크립트로 공식 스크립트가 아니라 사용자가 별도로 작성한 저수준 검증 스크립트로
확인됨 — `bindLifetime`/`canBound`/`unbindLifetime` 자체의 이중 바인딩 확인됨 — `bindLifetime`/`canBound`/`unbindLifetime` 자체의 이중 바인딩
로직(A-1/A-2)과 Part B/C는 여전히 미검증. **아래 "아직 확인 안 된 것"이 로직(A-1/A-2)과 Part B/C는 여전히 미검증. **아래 "아직 확인 안 된 것"이
@ -32,7 +32,7 @@ Studio에서 실행된 사용자 자작 스크립트(공식 `10` 파일이 아
트릭과 동일한 신호를 구독, 콜백 클로저가 `target`을 업밸류로 캡처. 트릭과 동일한 신호를 구독, 콜백 클로저가 `target`을 업밸류로 캡처.
- Connection을 변수로 안 잡는 경우(Test 1)와 잡는 경우(Test 2) 둘 다 확인. - Connection을 변수로 안 잡는 경우(Test 1)와 잡는 경우(Test 2) 둘 다 확인.
- `triggerGC()`로 GC 완료를 간접 관찰(기법 상세는 - `triggerGC()`로 GC 완료를 간접 관찰(기법 상세는
`luau-test/gc-trigger-helper.server.luau`). `gc-trigger-helper.server.luau`, `luau-test/not-run/`).
## 확인된 것 ## 확인된 것
@ -54,8 +54,8 @@ Studio에서 실행된 사용자 자작 스크립트(공식 `10` 파일이 아
- **A-1/A-2 (`canBound` 이중 바인딩 게이트, unbind 후 재바인딩 허용)** - **A-1/A-2 (`canBound` 이중 바인딩 게이트, unbind 후 재바인딩 허용)**
`bindLifetime`/`canBound`/`unbindLifetime`/`Subscribed` 로직 자체는 `bindLifetime`/`canBound`/`unbindLifetime`/`Subscribed` 로직 자체는
이 스크립트에 없음(순수 GC/Connection 메커니즘만 테스트함). `luau-test/ 이 스크립트에 없음(순수 GC/Connection 메커니즘만 테스트함). `luau-test/README.md`가
README.md`가 명시한 두 번째 "심각한 발견" 트리거 조건(A-2 실패 시 명시한 두 번째 "심각한 발견" 트리거 조건(A-2 실패 시
`canBound`/`unbindLifetime` 설계 재검토)은 미해소 — 공식 `10` 파일을 `canBound`/`unbindLifetime` 설계 재검토)은 미해소 — 공식 `10` 파일을
그대로 실행해서 확인해야 함. 그대로 실행해서 확인해야 함.
- **Part B (Attribute의 Instance 참조 타입 지원)**, **Part C - **Part B (Attribute의 Instance 참조 타입 지원)**, **Part C
@ -80,7 +80,7 @@ Studio에서 실행된 사용자 자작 스크립트(공식 `10` 파일이 아
결론은 이번에 반증됨** — weak-value 테이블에 canary를 넣어두고 결론은 이번에 반증됨** — weak-value 테이블에 canary를 넣어두고
할당 압력(`table.create` 반복)을 걸며 `task.wait`로 기다리면, incremental 할당 압력(`table.create` 반복)을 걸며 `task.wait`로 기다리면, incremental
GC가 실제로 완료되는 시점을 간접 관찰 가능함(사용자가 이번 검증에서 GC가 실제로 완료되는 시점을 간접 관찰 가능함(사용자가 이번 검증에서
확인). `07`의 docstring과 이 기법 자체는 `luau-test/gc-trigger-helper.server.luau`로 확인). `07`의 docstring과 이 기법 자체는 `gc-trigger-helper.server.luau`(`luau-test/not-run/`)
분리해뒀음 — Studio 기반 스파이크(`10` 등)가 GC 완료를 기다려야 할 때 분리해뒀음 — Studio 기반 스파이크(`10` 등)가 GC 완료를 기다려야 할 때
그 파일의 `waitForGC`를 그대로 복붙하면 됨. 그 파일의 `waitForGC`를 그대로 복붙하면 됨.

View file

@ -49,7 +49,7 @@ quad는 이제 "스크립트"가 아니라 **라이브러리**다. DOMless Roblo
복잡도가 너무 올라간다고 판단 — 당장은 `CollectionService` 그대로 사용. **대신 복잡도가 너무 올라간다고 판단 — 당장은 `CollectionService` 그대로 사용. **대신
Ref가 도입됨** — 단 Ref의 용도는 "id로 조회"가 아니라 "외부에서 이미 Ref가 도입됨** — 단 Ref의 용도는 "id로 조회"가 아니라 "외부에서 이미
관리되고 있는 instance를 quad로 점진적으로 마이그레이션/래핑하기 위해 관리되고 있는 instance를 quad로 점진적으로 마이그레이션/래핑하기 위해
직접 참조를 얻는 것"(`base/bind-system-plan.md`의 Ref 절 참고) — 둘을 직접 참조를 얻는 것"(`base/ref-plan.md`의 Ref 절 참고) — 둘을
혼동하지 말 것. 혼동하지 말 것.
- **2026-08-04 6차: 네임스페이싱 충돌을 심각하게 안 보는 이유 확정.** - **2026-08-04 6차: 네임스페이싱 충돌을 심각하게 안 보는 이유 확정.**
충돌을 피해야 하는 단위는 보통 컴포넌트 단위로 나오고, 그 경우는 Ref로 충돌을 피해야 하는 단위는 보통 컴포넌트 단위로 나오고, 그 경우는 Ref로

View file

@ -1,6 +1,6 @@
# Bind 시스템 — pluggable key/value 핸들러 (base로 승격됨) # Bind 시스템 — pluggable key/value 핸들러 (base로 승격됨)
> **📄 [2026-08-13 여덟 번째 세션] 이 문서는 분할 중입니다 — 1단계 완료.** > **📄 [2026-08-13 아홉 번째 세션] 이 문서는 분할 중입니다 — 1단계 완료.**
> 2989줄까지 불어나 사람이 검토할 수 없고 한 곳의 실수가 미치는 범위가 > 2989줄까지 불어나 사람이 검토할 수 없고 한 곳의 실수가 미치는 범위가
> 너무 크다는 사용자 지적으로 쪼개는 중. **1단계로 분리된 것(내용/결정은 > 너무 크다는 사용자 지적으로 쪼개는 중. **1단계로 분리된 것(내용/결정은
> 하나도 안 바뀜, 순수 이동)**: > 하나도 안 바뀜, 순수 이동)**:
@ -164,8 +164,8 @@ retract 클로저를 반환하는 1-메소드 계약으로 합쳐짐 — 이 절
- 모든 핸들러는 대상 **Instance를 직접, 항상** 받는다. quad는 "인스턴스를 생성하고 - 모든 핸들러는 대상 **Instance를 직접, 항상** 받는다. quad는 "인스턴스를 생성하고
그 인스턴스를 처리하는" 라이브러리다 — 다른 라이브러리가 만든 값(예: Store)을 그 인스턴스를 처리하는" 라이브러리다 — 다른 라이브러리가 만든 값(예: Store)을
그 인스턴스에 적용하도록 돕는 역할에 가깝다. 그래서 핸들러가 "나중에 생길 그 인스턴스에 적용하도록 돕는 역할에 가깝다. 그래서 핸들러가 "나중에 생길
대상"을 비동기로 기다릴 필요 자체가 없음(아래 Ref 절 참고 — Ref는 다른 이유로 대상"을 비동기로 기다릴 필요 자체가 없음(`ref-plan.md`의 Ref 절 참고 — Ref는
존재). 다른 이유로 존재).
- **보강(2026-08-04)**: `inst`가 항상 살아있는 엔진 객체(Roblox Instance)일 - **보강(2026-08-04)**: `inst`가 항상 살아있는 엔진 객체(Roblox Instance)일
필요는 없음 — 특정 백엔드에서 실제 엔진 객체 생성/바인딩 비용이 비싸면 필요는 없음 — 특정 백엔드에서 실제 엔진 객체 생성/바인딩 비용이 비싸면
(예: 웹 DOM) 중간 표현으로 평범한 테이블을 만들고 나중에 그 테이블을 (예: 웹 DOM) 중간 표현으로 평범한 테이블을 만들고 나중에 그 테이블을
@ -329,8 +329,8 @@ retract 클로저를 반환하는 1-메소드 계약으로 합쳐짐 — 이 절
키(프로퍼티/이벤트)를 다른 의미로 취급해야 하니 구분 비용이 이미 드는 키(프로퍼티/이벤트)를 다른 의미로 취급해야 하니 구분 비용이 이미 드는
참에 순서까지 명시적으로 고정하는 게 거의 공짜. **결과적으로 배열 참에 순서까지 명시적으로 고정하는 게 거의 공짜. **결과적으로 배열
슬롯에 놓인 어떤 값(Ref 포함)이든 모든 프로퍼티/이벤트 세팅보다 항상 슬롯에 놓인 어떤 값(Ref 포함)이든 모든 프로퍼티/이벤트 세팅보다 항상
먼저 처리된다는 게 base 자체의 보장**이 됨 — 아래 "Ref 일반화" 절 뒤에 먼저 처리된다는 게 base 자체의 보장**이 됨 — `ref-plan.md`의 "Ref 일반화" 절
이어지는 "PreRef" 절이 이 보장 위에서 성립. **M0 스파이크에서 실제 뒤에 이어지는 "PreRef" 절이 이 보장 위에서 성립. **M0 스파이크에서 실제
Luau로 이 순회 동작 자체를 검증할 것**(지금까지 추론/관찰만으로 확정된 Luau로 이 순회 동작 자체를 검증할 것**(지금까지 추론/관찰만으로 확정된
항목 — `research/pre-implementation-audit.md`가 짚은 "실제 Luau로 항목 — `research/pre-implementation-audit.md`가 짚은 "실제 Luau로
부딪혀본 적 없는 것" 범주와 같은 급이라 신중하게 다룸). 부딪혀본 적 없는 것" 범주와 같은 급이라 신중하게 다룸).
@ -860,7 +860,7 @@ lazy 생성.
`nil`을 넣으면 (1) 그 자리가 "안 채워짐"과 구별이 안 되고 (2) 배열이 `nil`을 넣으면 (1) 그 자리가 "안 채워짐"과 구별이 안 되고 (2) 배열이
구멍 나면서 순수 array 취급이 깨져 접근 비용이 올라감(해시 파트로 밀림) 구멍 나면서 순수 array 취급이 깨져 접근 비용이 올라감(해시 파트로 밀림)
`None`은 실재하는 값이라 자리를 "채워짐"으로 유지시켜줌, PreRef `None`은 실재하는 값이라 자리를 "채워짐"으로 유지시켜줌, PreRef
pre-pass 소진 슬롯에 이미 적용된 것과 같은 원칙( "PreRef" 절의 pre-pass 소진 슬롯에 이미 적용된 것과 같은 원칙(`ref-plan.md`의 "PreRef" 절의
"왜 `None`이 아니라 `nil`인가" 참고 — **단, 그 절에서 최종적으로 `nil` "왜 `None`이 아니라 `nil`인가" 참고 — **단, 그 절에서 최종적으로 `nil`
되돌아간 건 Ref 콜백/대기자 배열 한정**이고 `sourceList`/PreRef 되돌아간 건 Ref 콜백/대기자 배열 한정**이고 `sourceList`/PreRef
pre-pass처럼 순서가 실제로 중요하거나 "채워짐 여부"를 엄밀히 구별해야 pre-pass처럼 순서가 실제로 중요하거나 "채워짐 여부"를 엄밀히 구별해야
@ -1098,8 +1098,8 @@ end
- **반환하는 클로저가 할 일은 `unbindLifetime(inst, observer)` 호출뿐 — - **반환하는 클로저가 할 일은 `unbindLifetime(inst, observer)` 호출뿐 —
위임 대상까지 수동으로 안 쫓아가도 됨.** `Dispatch.retractFrom`이 자기 위임 대상까지 수동으로 안 쫓아가도 됨.** `Dispatch.retractFrom`이 자기
밑에 위임된 걸 알아서 정리해주므로(위 "Dispatch 체인" 절), 이 밑에 위임된 걸 알아서 정리해주므로(위 "Dispatch 체인" 절), 이
클로저는 정확히 자기 자신의 자원(Observer)만 정리하면 끝 — 이게 클로저는 정확히 자기 자신의 자원(Observer)만 정리하면 끝 — 이게
"이벤트도 store-bind 가능" 절에서 이미 "엔지니어링 비용이 낮다"고 `event-plan.md` "이벤트도 store-bind 가능" 절에서 이미 "엔지니어링 비용이 낮다"고
서술한 것과 같은 이유(새 디스패치 메커니즘 없이 기존 계약만 구현). 서술한 것과 같은 이유(새 디스패치 메커니즘 없이 기존 계약만 구현).
**[2026-08-13 다섯 번째 세션] 별도 `Relate`가 더 이상 필요 없음** — **[2026-08-13 다섯 번째 세션] 별도 `Relate`가 더 이상 필요 없음** —
`observer``process` 안의 로컬 변수를 반환 클로저가 upvalue로 그대로 `observer``process` 안의 로컬 변수를 반환 클로저가 upvalue로 그대로
@ -1146,14 +1146,14 @@ ref 타입처럼 생각하는 게 맞는 거 같음 — 그걸 처리하는 플
의미가 "조용히 UB"도 "즉시 실패"도 아니라 "그냥 정상적으로 동작함"으로 의미가 "조용히 UB"도 "즉시 실패"도 아니라 "그냥 정상적으로 동작함"으로
다시 한번 바뀜. 다시 한번 바뀜.
## Ref / PreRef — 전용 문서로 분리됨 (2026-08-13 여덟 번째 세션) ## Ref / PreRef — 전용 문서로 분리됨 (2026-08-13 아홉 번째 세션)
`Ref`/`PreRef`(용도 재정의, `.Value`/`:Set`/`:Callback`/`:Wait` API, `Ref`/`PreRef`(용도 재정의, `.Value`/`:Set`/`:Callback`/`:Wait` API,
`Ref`의 retract, 이중 바인딩 금지, PreRef 호이스팅/1회용 가드)는 `Ref`의 retract, 이중 바인딩 금지, PreRef 호이스팅/1회용 가드)는
**`base/ref-plan.md`로 분리**됨 — 이 문서가 3000줄에 육박해 분할한 **`base/ref-plan.md`로 분리**됨 — 이 문서가 3000줄에 육박해 분할한
1단계. 내용/결정은 안 바뀜. 1단계. 내용/결정은 안 바뀜.
## 이벤트 바인딩 — 전용 문서로 분리됨 (2026-08-13 여덟 번째 세션) ## 이벤트 바인딩 — 전용 문서로 분리됨 (2026-08-13 아홉 번째 세션)
"이벤트 핸들러는 self(Instance)를 받지 않는다"와 "이벤트도 store-bind "이벤트 핸들러는 self(Instance)를 받지 않는다"와 "이벤트도 store-bind
가능 — `false`로 disconnect" 두 절은 **`base/event-plan.md`로 분리**됨 가능 — `false`로 disconnect" 두 절은 **`base/event-plan.md`로 분리**됨
@ -2243,7 +2243,7 @@ vs `[BooleanAttribute "name"]`)뿐 아니라 `None`/`process`/`retract` 동작
동형)가 추가되며, 단일 키 생성자는 이름 충돌 방지로 `AttributeKey<<T>>` 동형)가 추가되며, 단일 키 생성자는 이름 충돌 방지로 `AttributeKey<<T>>`
리네임됨. 리네임됨.
## `Brand` — 전용 문서로 분리됨 (2026-08-13 여덟 번째 세션) ## `Brand` — 전용 문서로 분리됨 (2026-08-13 아홉 번째 세션)
런타임 nominal 타입 판별 통합 메커니즘(`Brand.set`/`Brand.get`, `isState` 런타임 nominal 타입 판별 통합 메커니즘(`Brand.set`/`Brand.get`, `isState`
10종 branded 타입 전부로 일반화)은 **`base/brand-plan.md`로 분리**됨 — 10종 branded 타입 전부로 일반화)은 **`base/brand-plan.md`로 분리**됨 —

View file

@ -1,6 +1,6 @@
# `Brand` — 런타임 nominal 타입 판별 통합 메커니즘 # `Brand` — 런타임 nominal 타입 판별 통합 메커니즘
> **[2026-08-13 여덟 번째 세션] `bind-system-plan.md`에서 분리됨.** > **[2026-08-13 아홉 번째 세션] `bind-system-plan.md`에서 분리됨.**
> 자기 완결적인 유틸이라 디스패치 코어와 같은 파일에 있을 이유가 없었음. > 자기 완결적인 유틸이라 디스패치 코어와 같은 파일에 있을 이유가 없었음.
> **내용은 옮기기만 했고 결정은 하나도 안 바뀜.** > **내용은 옮기기만 했고 결정은 하나도 안 바뀜.**

View file

@ -303,7 +303,7 @@ caller가 named parameter 하나에 여러 modifier를 몰아넣고 싶을 때
내부는 항상 이미 합쳐진 단일 값만 받으므로 컴포넌트 저작자가 배열 처리를 내부는 항상 이미 합쳐진 단일 값만 받으므로 컴포넌트 저작자가 배열 처리를
신경 쓸 필요 없음. Ref는 필드 충돌 개념이 없어 이 문제 자체가 없음(여러 신경 쓸 필요 없음. Ref는 필드 충돌 개념이 없어 이 문제 자체가 없음(여러
Ref를 받으면 그냥 전부 실행하면 됨 — Ref 콜백 리스트는 애초에 여러 등록을 Ref를 받으면 그냥 전부 실행하면 됨 — Ref 콜백 리스트는 애초에 여러 등록을
누적하도록 설계돼 있음, `bind-system-plan.md`의 Ref 콜백/대기자 절) — 별도 누적하도록 설계돼 있음, `ref-plan.md`의 Ref 콜백/대기자 절) — 별도
결합 유틸 불필요. **정확한 동작(baked 값 교체 경고, 순서 의존성, `Apply`와의 결합 유틸 불필요. **정확한 동작(baked 값 교체 경고, 순서 의존성, `Apply`와의
역할 구분, `:Peek`/`isState`)은 `base/modifier-plan.md` 9번 절이 최종 역할 구분, `:Peek`/`isState`)은 `base/modifier-plan.md` 9번 절이 최종
소스** — `Merge`로 전부 대체해 `Apply`만 강제하는 방안도 이번에 검토했으나, 소스** — `Merge`로 전부 대체해 `Apply`만 강제하는 방안도 이번에 검토했으나,

View file

@ -1,6 +1,6 @@
# 이벤트 바인딩 — self 미전달, `false`로 disconnect # 이벤트 바인딩 — self 미전달, `false`로 disconnect
> **[2026-08-13 여덟 번째 세션] `bind-system-plan.md`에서 분리됨.** > **[2026-08-13 아홉 번째 세션] `bind-system-plan.md`에서 분리됨.**
> 사용자가 "이벤트 연결은 다른 base 문서가 되어야 할 듯"이라고 직접 > 사용자가 "이벤트 연결은 다른 base 문서가 되어야 할 듯"이라고 직접
> 지목한 부분. **내용은 옮기기만 했고 결정은 하나도 안 바뀜.** > 지목한 부분. **내용은 옮기기만 했고 결정은 하나도 안 바뀜.**

View file

@ -189,7 +189,7 @@ function bindLifetime(inst, value)
local _ = gchold -- 발화 안 함, 클로저 생존이 곧 gchold 생존 local _ = gchold -- 발화 안 함, 클로저 생존이 곧 gchold 생존
-- 2026-08-13 부분 실측 확인(미발화 + Destroy 시 Connected 즉시 -- 2026-08-13 부분 실측 확인(미발화 + Destroy 시 Connected 즉시
-- 전환) — audit/gcconn-trick-verification.md. canBound 이중 -- 전환) — audit/gcconn-trick-verification.md. canBound 이중
-- 바인딩 게이트(A-1/A-2)는 아직 공식 luau-test/10으로 미확인. -- 바인딩 게이트(A-1/A-2)는 아직 공식 10(`luau-test/not-run/`)으로 미확인.
end) end)
relate:SetStrong(inst, GCCONN, gcconn) relate:SetStrong(inst, GCCONN, gcconn)
end end

View file

@ -221,7 +221,7 @@ self 최상위가 아니라 **내부 저장소**에 써야 함 — 역시 아래
> 패턴**이라 실사용에서 바로 터지는 경로임. 해법: 필드 데이터를 유일 테이블 > 패턴**이라 실사용에서 바로 터지는 경로임. 해법: 필드 데이터를 유일 테이블
> identity 키(`FieldsKey` 등)로 분리된 내부 테이블에 담고, `table.clone` > identity 키(`FieldsKey` 등)로 분리된 내부 테이블에 담고, `table.clone`
> 얕은 복사라 setter 안에서 그 내부 테이블도 따로 `table.clone` 할 것. > 얕은 복사라 setter 안에서 그 내부 테이블도 따로 `table.clone` 할 것.
> `luau-test/17`이 이 구조를 실측 검증함(원래 스파이크가 리터럴 키 방식으로 > `17`(`luau-test/done/`)이 이 구조를 실측 검증함(원래 스파이크가 리터럴 키 방식으로
> 짜여 있다가 바로 이 이유로 크래시했고, 그 과정에서 이 문서의 "데이터를 > 짜여 있다가 바로 이 이유로 크래시했고, 그 과정에서 이 문서의 "데이터를
> 테이블에 직접 두고"라는 표현이 두 가지로 읽힌다는 게 드러남). 즉 `:FontSize`/ > 테이블에 직접 두고"라는 표현이 두 가지로 읽힌다는 게 드러남). 즉 `:FontSize`/
`:Round`/앞으로 생길 어떤 필드 이름이든 전부 이 **하나의 제네릭 `__index` `:Round`/앞으로 생길 어떤 필드 이름이든 전부 이 **하나의 제네릭 `__index`
@ -537,7 +537,7 @@ Modifier 값을 변수/모듈 상수로 만들어 재사용하는, 기존에도
### 9-2. `Overridden`가 서로 다른(그러나 상하위 관계인) Modifier 타입을 섞는 경우 — ### 9-2. `Overridden`가 서로 다른(그러나 상하위 관계인) Modifier 타입을 섞는 경우 —
타입 시그니처 미확정 (2026-08-07 다섯 번째 세션 후속) 타입 시그니처 미확정 (2026-08-07 다섯 번째 세션 후속)
**[해소됨, 2026-08-13 첫 실측 라운드]** `luau-test/09-type-modifier-overridden-subtype.luau`로 **[해소됨, 2026-08-13 첫 실측 라운드]** `09-type-modifier-overridden-subtype.luau`(`luau-test/done/`)
실측 완료 — 아래에서 우려한 지점이 그대로 재현됨(`Overridden`의 리턴 실측 완료 — 아래에서 우려한 지점이 그대로 재현됨(`Overridden`의 리턴
타입 불일치로 서브타입이 깨짐), fallback(`any`)은 정상 작동 확인. 타입 불일치로 서브타입이 깨짐), fallback(`any`)은 정상 작동 확인.
"실 Luau 테스트 필요"는 더 이상 유효하지 않음 — 아래는 그 결론에 이른 "실 Luau 테스트 필요"는 더 이상 유효하지 않음 — 아래는 그 결론에 이른

View file

@ -1,6 +1,6 @@
# Ref / PreRef — 지연 없는 확정 값 박스 # Ref / PreRef — 지연 없는 확정 값 박스
> **[2026-08-13 여덟 번째 세션] `bind-system-plan.md`에서 분리됨.** 그 > **[2026-08-13 아홉 번째 세션] `bind-system-plan.md`에서 분리됨.** 그
> 문서가 2989줄까지 불어나 사람이 검토하기 어렵고 한 곳의 실수가 미치는 > 문서가 2989줄까지 불어나 사람이 검토하기 어렵고 한 곳의 실수가 미치는
> 범위가 너무 커진다는 사용자 지적에 따른 1단계 분할. **내용은 옮기기만 > 범위가 너무 커진다는 사용자 지적에 따른 1단계 분할. **내용은 옮기기만
> 했고 결정은 하나도 안 바뀜.** > 했고 결정은 하나도 안 바뀜.**
@ -210,6 +210,17 @@ tween-plan.md`도 이에 맞춰 갱신됨). Ref의 진짜 용도는 다름:
### `Ref`의 retract — `State<Ref>` 재바인드 시 이전 Ref에 `nil` (2026-08-12 여덟 번째 세션, `TagHandler`와 같은 메커니즘 재사용) ### `Ref`의 retract — `State<Ref>` 재바인드 시 이전 Ref에 `nil` (2026-08-12 여덟 번째 세션, `TagHandler`와 같은 메커니즘 재사용)
> **⚠️ [2026-08-13 감사 보강] 이 절의 "이전 `process`가 반환한 클로저가
> `hintValue`로 먼저 불려 언바인딩하고, 그 다음 `process`가 바인딩"이라는
> 서술은 곧 교체될 예정 — 아직 반영 안 됨. `research/dispatch-redispatch-diff-plan.md`
> 폐기 대상으로 지목한 바로 그 **선행 `retractFrom` 호출** 패턴이다 — 이 문서가
> `bind-system-plan.md`에서 분리(2026-08-13 아홉 번째 세션, 순수 이동)될
> 때 원문이 그대로 옮겨오면서 `bind-system-plan.md`/`tag-plan.md`/
> `slot-plan.md`/`attribute-plan.md`가 이미 달고 있는 것과 같은 0-Z
> 배너가 같이 안 옮겨졌음. `question.md` **0-Z**(Attribute 이름 소유권)
> 해소 시 "하강 diff" 모델대로 이 절도 같이 재작성할 것 — **여기 적힌
> 대로 구현하면 옛 모델로 짜게 됨.**
**배경**: `Ref`는 이미 "일반 프로퍼티/Modifier 필드/Store 값 어디든 자유롭게 **배경**: `Ref`는 이미 "일반 프로퍼티/Modifier 필드/Store 값 어디든 자유롭게
들어감"(아래 "동적 경로 가드" 절)이 확정돼 있어 — `State<Ref>`가 실제로 들어감"(아래 "동적 경로 가드" 절)이 확정돼 있어 — `State<Ref>`가 실제로
가능하고, 그러면 Store 값이 `refA`에서 `refB`로 바뀌는 경우가 생김. 이때 가능하고, 그러면 Store 값이 `refA`에서 `refB`로 바뀌는 경우가 생김. 이때

View file

@ -121,7 +121,7 @@ Handler 계약이 "`process`가 자기 retract 클로저를 반환"으로 바뀌
- **정리 조건을 실제 정리와 묶을 것.** `Relate` 엔트리를 지우는 코드가 - **정리 조건을 실제 정리와 묶을 것.** `Relate` 엔트리를 지우는 코드가
"실제로 물러날 때"라는 조건 **밖**에 있으면, spurious 재발행에서 "실제로 물러날 때"라는 조건 **밖**에 있으면, spurious 재발행에서
기록만 날아가 dedup이 조용히 무력화됨 — `RefLeafHandler`가 실제로 이 기록만 날아가 dedup이 조용히 무력화됨 — `RefLeafHandler`가 실제로 이
버그를 냈음(`bind-system-plan.md` "`Ref`의 retract" 절). 버그를 냈음(`ref-plan.md` "`Ref`의 retract" 절).
- **weak하다고 "언젠간 알아서 사라진다"에 기대지 말 것.** 값이 `SetWeak` - **weak하다고 "언젠간 알아서 사라진다"에 기대지 말 것.** 값이 `SetWeak`
이어도 **언제 사라지는지는 GC 타이밍**이라, 그 전에 같은 키를 다시 이어도 **언제 사라지는지는 GC 타이밍**이라, 그 전에 같은 키를 다시
쓰려는 코드가 비결정적으로 실패함 — `Slot``destroySlotTree`가 자식 쓰려는 코드가 비결정적으로 실패함 — `Slot``destroySlotTree`가 자식

View file

@ -188,7 +188,7 @@ State를 만족하도록 만들고, RefSource라는 별도 타입은 폐기**하
반환**하는 것으로 자연히 갱신됨(위 "타입 추론 문제" 절과 연동). 반환**하는 것으로 자연히 갱신됨(위 "타입 추론 문제" 절과 연동).
**[해소됨, 2026-08-13 첫 실측 라운드]** 핵심 질문(Source가 State를 구조적으로 **[해소됨, 2026-08-13 첫 실측 라운드]** 핵심 질문(Source가 State를 구조적으로
만족하는 제네릭 메소드 체이닝)은 `luau-test/08-type-source-satisfies-state.luau`로 만족하는 제네릭 메소드 체이닝)은 `08-type-source-satisfies-state.luau`(`luau-test/review-required/`)
실측 통과 확인됨 — 아래 우려대로 "두 제네릭 타입 별칭이 서로를 참조하는 실측 통과 확인됨 — 아래 우려대로 "두 제네릭 타입 별칭이 서로를 참조하는
상호 재귀"는 실제로 위험했지만, 그 아래 제안한 단방향 의존(`State`가 상호 재귀"는 실제로 위험했지만, 그 아래 제안한 단방향 의존(`State`가
`Source`를 참조 안 함) 회피책이 그대로 맞아떨어짐. **다만 좁은 잔여 `Source`를 참조 안 함) 회피책이 그대로 맞아떨어짐. **다만 좁은 잔여

View file

@ -4,7 +4,7 @@
> README는 각 파일이 **무엇을 왜 검증하는지**(의도·배경)만 담고, 상태는 > README는 각 파일이 **무엇을 왜 검증하는지**(의도·배경)만 담고, 상태는
> **폴더 구조 + STATUS.md**가 소스. > **폴더 구조 + STATUS.md**가 소스.
**[2026-08-13 여덟 번째 세션] 폴더가 곧 상태** — 파일이 평평하게 21개 **[2026-08-13 아홉 번째 세션] 폴더가 곧 상태** — 파일이 평평하게 21개
쌓여 있어 사람이 "지금 내가 볼 게 뭔지" 못 고르겠다는 사용자 지적으로 쌓여 있어 사람이 "지금 내가 볼 게 뭔지" 못 고르겠다는 사용자 지적으로
재편: 재편:

View file

@ -1,6 +1,6 @@
# 스파이크 상태판 — **폴더가 곧 상태** # 스파이크 상태판 — **폴더가 곧 상태**
> 마지막 갱신: 2026-08-13 여덟 번째 세션(폴더 재편). > 마지막 갱신: 2026-08-13 아홉 번째 세션(폴더 재편).
> 첫 실측은 여섯 번째 세션 — 상세 결과는 `.claude/audit/luau-test-first-run-2026-08-13.md`. > 첫 실측은 여섯 번째 세션 — 상세 결과는 `.claude/audit/luau-test-first-run-2026-08-13.md`.
> 실행법: `luau <파일>` (런타임) / `luau-analyze <파일>` (타입 전용). > 실행법: `luau <파일>` (런타임) / `luau-analyze <파일>` (타입 전용).

View file

@ -20,7 +20,7 @@
실행: `luau 11-modifier-illegal-value-error.luau` 실행: `luau 11-modifier-illegal-value-error.luau`
]] ]]
-- ===== Brand 흉내 — 실제로는 base/bind-system-plan.md의 Brand 절이 다루는 -- ===== Brand 흉내 — 실제로는 base/brand-plan.md의 Brand 절이 다루는
-- weak-key 레지스트리 기반이지만, 이 스파이크에선 태그 필드로 단순화 ===== -- weak-key 레지스트리 기반이지만, 이 스파이크에선 태그 필드로 단순화 =====
local function tag(name) local function tag(name)
@ -305,7 +305,7 @@ end
3. B에서 Slot 같은 "Modifier가 아닌 다른 핸들러 계층 값"은 State/Source에 3. B에서 Slot 같은 "Modifier가 아닌 다른 핸들러 계층 값"은 State/Source에
여전히 자유롭게 들어갈 수 있는가(Modifier만의 예외라는 걸 재확인). 여전히 자유롭게 들어갈 수 있는가(Modifier만의 예외라는 걸 재확인).
4. 이 스파이크는 Brand/isState를 태그 필드로 단순화한 것 — 실제 구현은 4. 이 스파이크는 Brand/isState를 태그 필드로 단순화한 것 — 실제 구현은
base/bind-system-plan.md의 weak-key 레지스트리 기반 Brand를 씀, base/brand-plan.md의 weak-key 레지스트리 기반 Brand를 씀,
여기선 그 판별 로직 자체가 아니라 "체크 지점 배치가 실제로 동작하는가"만 여기선 그 판별 로직 자체가 아니라 "체크 지점 배치가 실제로 동작하는가"만
검증 대상. 검증 대상.
]] ]]

View file

@ -15,7 +15,7 @@
이제 `isHandlable = isRef(v) and not isPreRef(v)`로 **명시적으로 이제 `isHandlable = isRef(v) and not isPreRef(v)`로 **명시적으로
좁혀야만** PreRef를 잘못 삼키지 않는다는 것. 좁혀야만** PreRef를 잘못 삼키지 않는다는 것.
배경: .claude/base/bind-system-plan.md의 `Brand` 절 배경: .claude/base/brand-plan.md의 `Brand` 절
("isRef(x)는 그 위에 Brand.get(x)==RefTag를 OR로 얹은 상위 개념")와 ("isRef(x)는 그 위에 Brand.get(x)==RefTag를 OR로 얹은 상위 개념")와
"`(v=Ref)` children 배열 leaf 매치 핸들러... isRef(v) and not "`(v=Ref)` children 배열 leaf 매치 핸들러... isRef(v) and not
isPreRef(v)로 명시적으로 좁혀야 함" 부분. isPreRef(v)로 명시적으로 좁혀야 함" 부분.

View file

@ -1,7 +1,7 @@
# 확인/결정 필요 목록 # 확인/결정 필요 목록
**이 문서는 사용자가 답해야 할 것만 담습니다.** 이미 결정이 끝난 항목은 **이 문서는 사용자가 답해야 할 것만 담습니다.** 이미 결정이 끝난 항목은
2026-08-13 여덟 번째 세션에 `archive/question-resolved.md`로 옮겼음 — 2026-08-13 아홉 번째 세션에 `archive/question-resolved.md`로 옮겼음 —
"해결된 게 너무 많아 필요한 부분만 읽기 어렵다"는 사용자 지적에 따른 것. "해결된 게 너무 많아 필요한 부분만 읽기 어렵다"는 사용자 지적에 따른 것.
결정 내용은 안 바뀌었고 읽는 자리만 옮겼으니, 어떤 항목이 왜 그렇게 결정 내용은 안 바뀌었고 읽는 자리만 옮겼으니, 어떤 항목이 왜 그렇게
정해졌는지 되짚고 싶으면 그 문서를 볼 것(지금 유효한 설계 자체는 항상 정해졌는지 되짚고 싶으면 그 문서를 볼 것(지금 유효한 설계 자체는 항상
@ -241,6 +241,13 @@ Attribute가 직접 해야 함.**
엘비스 연산자류) 후보 추가 — Haskell 비교 리서치 중 나옴, 카탈로그 확정 엘비스 연산자류) 후보 추가 — Haskell 비교 리서치 중 나옴, 카탈로그 확정
규칙에 그대로 맞아 포함 근거는 있음. 상세는 `research/operator-sugar-plan.md`. 규칙에 그대로 맞아 포함 근거는 있음. 상세는 `research/operator-sugar-plan.md`.
구현 자체는 맨 마지막 우선순위(순수 슈가, 없어도 무방) — 여전함. 구현 자체는 맨 마지막 우선순위(순수 슈가, 없어도 무방) — 여전함.
- **중첩 State 평탄화 `State<State<T>>``State<T>`(2026-08-13 여섯 번째
세션 신설, 백로그)** — 인덱스 기반 `Dispatch` 재설계로 `State<State<T>>`
UB에서 정상 지원 대상이 됐지만, 깊이가 늘수록 `retractFrom`의 힌트가
더 깊은 인덱스엔 `nil`로 전달돼 `Tag`/`Ref`/`Slot` 등의 힌트 기반
최적화(깜빡임 방지)가 무력화되는 실제 기능 손실이 있음 — 값 층에서
평탄화하는 `state:Flatten()`류 콤비네이터 아이디어는 나왔으나 착수 안
함. 상세는 `research/operator-sugar-plan.md` 마지막 절.
- `research/existing-instance-bind-plan.md` — 스코프 논의만 필요, 구현 - `research/existing-instance-bind-plan.md` — 스코프 논의만 필요, 구현
착수를 막지 않음. 착수를 막지 않음.
- **`quad-debug` 세부 API 이름** — `research/debug-tooling-plan.md` 참고. - **`quad-debug` 세부 API 이름** — `research/debug-tooling-plan.md` 참고.
@ -248,7 +255,7 @@ Attribute가 직접 해야 함.**
넘는지)까지 사용자가 Studio에서 직접 실측 검증 완료 — 기술적 불확실성은 넘는지)까지 사용자가 Studio에서 직접 실측 검증 완료 — 기술적 불확실성은
다 해소됨, 남은 건 세부 API 이름뿐("이벤트 함수가 self로 instance를 다 해소됨, 남은 건 세부 API 이름뿐("이벤트 함수가 self로 instance를
읽는 게 quad 관습"이라는 언급은 2026-08-06 후속 세션에서 해소 — 읽는 게 quad 관습"이라는 언급은 2026-08-06 후속 세션에서 해소 —
채택 안 함으로 확정, `base/bind-system-plan.md` "이벤트 핸들러는 채택 안 함으로 확정, `base/event-plan.md` "이벤트 핸들러는
self(Instance)를 받지 않는다" 절 참고). 사용자가 "quad 개발 완료 전엔 self(Instance)를 받지 않는다" 절 참고). 사용자가 "quad 개발 완료 전엔
착수 못 함"으로 직접 후순위 지정한 건 여전함 — base 설계(M2 Dispatch/ 착수 못 함"으로 직접 후순위 지정한 건 여전함 — base 설계(M2 Dispatch/
M3 Source/M5 DI 생성자) 시점에 훅 확장 지점만 고려해두면 됨. M3 Source/M5 DI 생성자) 시점에 훅 확장 지점만 고려해두면 됨.

View file

@ -251,6 +251,11 @@ Dispatch.process(inst, k, realv, index + 1) -- retractFrom 선행 호출 없
M6의 "`SlotHandler.process`는 claim 실패 시에도 파괴적 클로저를 M6의 "`SlotHandler.process`는 claim 실패 시에도 파괴적 클로저를
반환해야 함")이 선행 `retractFrom` 전제로 쓰여 있음 — base 4개를 반환해야 함")이 선행 `retractFrom` 전제로 쓰여 있음 — base 4개를
옮길 때 같이 갱신하고 배너를 걷을 것. 옮길 때 같이 갱신하고 배너를 걷을 것.
- **[2026-08-13 감사에서 추가]** `ref-plan.md``bind-system-plan.md`
1단계 분할(2026-08-13 아홉 번째 세션, 순수 이동) 때 "`Ref`의 retract"
절이 옮겨오면서 선행 `retractFrom` 호출 서술도 그대로 딸려왔음. 분할
당시 이 문서(section 6, 당시 아직 안 쓰임)가 없어 배너가 안 붙었던
것 — 반영 시 나머지 6개와 같이 재작성하고 배너 걷을 것.
- **[2026-08-13 8차 감사에서 추가] 같은 패스에서 `bind-system-plan.md` - **[2026-08-13 8차 감사에서 추가] 같은 패스에서 `bind-system-plan.md`
2단계 분할도 할 것** — 이 문서가 지시하는 재작성 범위(핸들러 계약 / 2단계 분할도 할 것** — 이 문서가 지시하는 재작성 범위(핸들러 계약 /
@ -259,6 +264,6 @@ Dispatch.process(inst, k, realv, index + 1) -- retractFrom 선행 호출 없
전면 재작성하므로, **재작성하면서 새 파일로 옮기면 인바운드 참조 전면 재작성하므로, **재작성하면서 새 파일로 옮기면 인바운드 참조
(~37곳)를 한 번만 고치면 됨.** 따로 하면 같은 곳을 두 번 만짐. (~37곳)를 한 번만 고치면 됨.** 따로 하면 같은 곳을 두 번 만짐.
**요약**: 배너를 달고 있는 파일 = 반영 대상. 위 6개(`bind-system-plan`/ **요약**: 배너를 달고 있는 파일 = 반영 대상. 위 7개(`bind-system-plan`/
`tag-plan`/`slot-plan`/`attribute-plan`/`architecture`/`ROADMAP`)가 `tag-plan`/`slot-plan`/`attribute-plan`/`architecture`/`ROADMAP`/
전부이고, 반영이 끝나면 각 파일의 ⚠️ 배너도 같이 제거할 것. `ref-plan`)가 전부이고, 반영이 끝나면 각 파일의 ⚠️ 배너도 같이 제거할 것.

View file

@ -357,7 +357,7 @@ M7의 전제가 Luau 공식 동작대로 성립함. 별도로, 프로퍼티에 A
### 2-1. Source가 State를 만족하는 제네릭 검증이 실패했을 때의 대안(Plan B)이 전혀 없음 ### 2-1. Source가 State를 만족하는 제네릭 검증이 실패했을 때의 대안(Plan B)이 전혀 없음
**[해소됨, 2026-08-13 첫 실측 라운드]** 검증 자체는 **[해소됨, 2026-08-13 첫 실측 라운드]** 검증 자체는
`luau-test/08-type-source-satisfies-state.luau`로 실행돼 핵심 질문(Source가 `08-type-source-satisfies-state.luau`(`luau-test/review-required/`)로 실행돼 핵심 질문(Source가
State를 구조적으로 만족)이 통과했음 — 아래 "검증이 실패했을 때"라는 State를 구조적으로 만족)이 통과했음 — 아래 "검증이 실패했을 때"라는
전제 자체가 (핵심 케이스에 한해) 더 이상 미래형이 아님. 다만 통과와 전제 자체가 (핵심 케이스에 한해) 더 이상 미래형이 아님. 다만 통과와
별개로 좁은 잔여 케이스(`State<T>`가 자기 자신을 다른 타입 인자로 별개로 좁은 잔여 케이스(`State<T>`가 자기 자신을 다른 타입 인자로

View file

@ -17,8 +17,8 @@
compat.lua나 어댑터 코드는 전혀 없고, README/커밋 메시지에도 "왜 포기했는지" compat.lua나 어댑터 코드는 전혀 없고, README/커밋 메시지에도 "왜 포기했는지"
단서가 없음 — 애초에 착수된 적이 없다는 뜻. 단서가 없음 — 애초에 착수된 적이 없다는 뜻.
`question.md:110`이 "OOP 상속/커스텀 파서/Slot 스텁/`Pipe` COW는 확인된 `archive/question-resolved.md`가 "OOP 상속/커스텀 파서/Slot 스텁/`Pipe` COW는
죽은 접근"이라고 명시한 목록에 compat은 **포함돼 있지 않음**(정확한 서술). 확인된 죽은 접근"이라고 명시한 목록에 compat은 **포함돼 있지 않음**(정확한 서술).
즉 CLAUDE.md의 "반복 조사 금지"는 compat에는 적용되지 않는다 — 이 문서를 즉 CLAUDE.md의 "반복 조사 금지"는 compat에는 적용되지 않는다 — 이 문서를
쓰는 게 규칙 위반이 아님. 쓰는 게 규칙 위반이 아님.
@ -242,8 +242,9 @@ v2 트리 안에 과거 v1 컴포넌트를 리프로 박아넣는 것, (B) 기
"직접 Destroy 금지, Unmount 경유" 규칙을 Dispatch 엔진의 어느 지점에 "직접 Destroy 금지, Unmount 경유" 규칙을 Dispatch 엔진의 어느 지점에
훅으로 강제할지도 Slot 실제 구현 시점 확인 필요. 훅으로 강제할지도 Slot 실제 구현 시점 확인 필요.
- **결론: 지금 결정 불가.** M0 이후 Slot 코어 로직 구현 라운드 - **결론: 지금 결정 불가.** M0 이후 Slot 코어 로직 구현 라운드
(`question.md`의 "여러 Slot이 형제로 섞일 때 순서 보장" 항목과 같은 (`archive/question-resolved.md`의 "여러 Slot이 형제로 섞일 때 순서 보장"
시점)에서 이 두 가지를 실제 구현과 함께 재확인해야 함. 항목과 같은 시점 — 이미 해소돼 아카이브로 옮겨졌으나 실제 구현 시점의
참고 자료로는 여전히 유효)에서 이 두 가지를 실제 구현과 함께 재확인해야 함.
## 8. 남은 확인 사항 (추가 리서치 후보, 지금 결정 불필요) ## 8. 남은 확인 사항 (추가 리서치 후보, 지금 결정 불필요)

View file

@ -0,0 +1,125 @@
# 2026-08-13 열 번째 세션 — 병렬 에이전트 코퍼스 감사, 실제 부정확성 7건 발견·수정
## 배경
사용자가 "세션 기록들을 전부 읽어보며 문서 전체에 문제가 되는 부분이
있는지" 요청 — 단, 미확정 항목(0-Y/0-Z 등)은 문제로 세지 말고 **문서가
실제로 부정확한 것만** 짚어달라는 조건. 직전 세션(아홉 번째)이 `luau-test/`
상태별 폴더 재편, `bind-system-plan.md` 1단계 분할, `question.md` 트림,
`doc-check.py` 신설을 한 번에 처리한 큰 구조 변경 세션이었기 때문에, 그
변경들의 반영 누락이 남아있을 가능성이 가장 높은 지점으로 판단.
## 방법
1. 먼저 `python3 .claude/tools/doc-check.py`로 기계가 잡는 것부터 확인 —
ERROR 0, WARN 60건은 전부 기존에 허용 범위로 확인된 것(패러프레이즈
인용, 이미 날짜/범위가 명시된 완결 주장)이라 새 이슈 없음.
2. 기계가 못 잡는 의미론적 문제를 6개 병렬 Explore 에이전트로 분담
(CLAUDE.md "작업 방식"의 병렬 Agent 원칙 그대로):
- A: `bind-system-plan.md` 분할(9차 세션) 정합성
- B: `luau-test/` 폴더 재편(9차 세션) 정합성
- C: `question.md` 트림(9차 세션) 정합성
- D: 배너 달린 base 4종(`bind-system-plan`/`tag`/`slot`/`attribute`)
정밀 감사 — 배너 범위 밖 서술이 다른 최근 결정과 모순되는지
- E: 나머지 base 16개 문서 전수 — retract-always-fires/
`State<State<T>>`/인덱스 기반 chains/Slot 언마운트 전환/and·or
금지 등 최근 결정 위반 여부
- F: 루트 문서(`ROADMAP.md`/`HUMAN_TODO.md`/`.claude/README.md`)와
`archive/` 정합성, 0-Y/0-Z 게이트 표시 여부
3. 각 결과를 실시간으로 검토하며 발견 즉시 직접 수정(에이전트에 위임
안 함 — CLAUDE.md "중대 변경 핸드오버 체크리스트"의 "그 자리에서
닫을 것" 원칙).
## 발견 및 수정
**1. `luau-test/` 재편 후 깨진 flat 경로 참조** — `research/
pre-implementation-audit.md`, `base/store-semantics.md`,
`base/modifier-plan.md`(2곳), `base/lifecycle-pattern.md`,
`.claude/README.md`, `audit/gcconn-trick-verification.md`(4곳)가 옛
`luau-test/08-...`/`/09-...`/`/10`/`/17`/`/gc-trigger-helper...` 경로를
그대로 참조 — 실제 파일은 `done`/`review-required`/`not-run` 하위로
이동해 전부 깨진 링크였음. 파일명+실제 소속 폴더 표기로 정정(경로
대신 파일명 참조라는 9차 세션 원칙 준수).
**2. `bind-system-plan.md` 분할 후 참조 정합성 깨짐**:
- 분할된 문서 **자기 자신** 안의 "아래 Ref 절"/"위 PreRef 절"/"위
이벤트 절" 같은 위치 참조 4곳(167/332/863/1102줄)이 실제로 이동된
절을 못 따라감 — "순수 이동"이 텍스트 상호참조까지는 보장 못 한다는
사례.
- 외부 문서(`architecture.md`/`relate-plan.md`/
`component-composition-plan.md`/`question.md`)와 luau-test 파일 2개가
여전히 `bind-system-plan.md`의 Ref/Brand 절을 가리킴 — 전부
`ref-plan.md`/`event-plan.md`/`brand-plan.md`로 정정.
- **가장 중요한 발견**: `ref-plan.md`의 "`Ref`의 retract" 절이 옛
재디스패치 모델(선행 `retractFrom` 호출)을 그대로 서술하는데, 분할
때 다른 4개 base 문서가 이미 달고 있던 0-Z ⚠️ 배너가 안 옮겨와 있었음
— 이 상태로 구현하면 옛 모델로 짜일 위험. `research/
dispatch-redispatch-diff-plan.md` 6절 검토로 실제 대상 메커니즘이
맞는지 교차검증 후(단순 키워드 매치가 아니라 "선행 호출 후 process"
패턴이 정확히 일치함을 확인) 같은 형식의 배너 추가, 반영 대상을
6개→**7개**로 `dispatch-redispatch-diff-plan.md`/`CLAUDE.md` 양쪽 갱신.
**3. 세션 번호 오기(8차→9차) 대량 발견** — `bind-system-plan.md` 분할/
`luau-test/` 재편/`question.md` 트림/`tools/` 신설이 실제로는 전부
**9차 세션**(`session/2026-08-13-09-structure-and-guardrails.md`) 작업인데,
git 커밋 타임스탬프로 교차검증한 결과 총 17곳(`.claude/README.md` 4곳,
`question.md`, `archive/question-resolved.md`, `luau-test/STATUS.md`,
`luau-test/README.md`, `base/ref-plan.md`/`event-plan.md`/
`brand-plan.md`/`bind-system-plan.md`의 분할 배너 6곳, `doc-check.py`
자기 설명 2곳)가 "8차 세션"으로 잘못 표기돼 있었음(같은 날 2026-08-07의
진짜 8차 세션과 혼동된 것으로 추정) — 전부 정정.
**4. `question.md` 트림 중 열린 질문 하나 누락** — `State<State<T>>`
평탄화(`state:Flatten()`) 백로그 항목이 `research/
operator-sugar-plan.md`엔 있는데 트림된 `question.md`에서 빠져 있었음 —
3번 절(낮은 우선순위)에 복원.
**5. 트림 후 깨진 참조** — `research/v1-compat-plan.md`가 이미
`archive/question-resolved.md`로 옮겨진 "여러 Slot이 형제로 섞일 때
순서 보장" 항목을 옛 `question.md:110` 경로로 가리키고 있었음(그
줄번호도 트림 후 빈 줄이 됨) — 2곳 정정.
**6. `ROADMAP.md`의 M0 섹션에 0-Y/0-Z 게이트 표시 누락** — M2/M4/M6/M10엔
"0-Z 먼저 해소할 것" 배너가 있는데, 정작 M0 자체가 두 결정에 막혀
있다는 사실이 서두/M0 섹션 어디에도 안 적혀 있었음(`HUMAN_TODO.md`/
`CLAUDE.md`/`question.md`는 이미 정확히 서술 중이었음 — `ROADMAP.md`
누락) — M0 섹션 시작 부분에 게이트 배너 추가.
## 문제 없음으로 확인된 것
배너 달린 4개 base 문서의 배너 **범위 자체**는 정확 — 배너 밖 서술
(retract 항상 호출, `State<State<T>>` 정상 지원, 인덱스 기반 `chains`,
Slot 언마운트 전환, `and`/`or` 금지)은 전부 최신 결정과 일치, 모호해서
독자를 오도할 소지 없음. 나머지 base 16개 문서, `HUMAN_TODO.md`,
`luau-test/STATUS.md`의 상태 표는 실측과 정합. `question.md`/
`question-resolved.md` 사이 항목 재분류 오류나 번호 충돌 없음.
## 교훈
- **"순수 이동"이라고 선언해도 상호참조 검증은 별도로 필요** —
bind-system-plan.md 분할이 "내용은 안 바꿈"을 지켰어도, 그 내용이
참조하던 방향(위/아래, 같은 파일 안 절 이름)과 그 내용을 참조하던
외부 문서 양쪽 다 별도로 grep 전수 스윕이 필요했음. 이번엔 doc-check.py가
없던 시절 방식(agent가 직접 정독)으로 잡았지만, doc-check.py의 절
참조 검사가 왜 이 6곳을 못 잡았는지도 짚어둘 것 — "Ref 절"처럼 헤딩
전체가 아니라 **파일명이 함께 안 적힌** 참조는 정규식이 "그 파일
안에 있다"고 가정하고 검사하므로, 파일명 없는 방향 참조(아래/위)
자체가 애초에 이 검사의 사각지대. 파일을 쪼갤 땐 grep으로 "아래/위
OO 절"류 방향 참조부터 훑는 걸 체크리스트에 추가할 가치 있음.
- **배너가 파일 단위로 붙어있으면, 그 파일에서 분할된 새 파일에
배너 대상 내용이 섞여 있는지 별도 확인이 필요** — `ref-plan.md`
사례처럼, 분할 시점에 배너 자체를 "옮길지 말지"를 판단할 근거
문서(`dispatch-redispatch-diff-plan.md`)가 그 새 파일을 아직 몰랐던
경우 특히 위험.
- **날짜/세션 번호는 한 번 잘못 적히면 복붙되며 퍼진다** — 이번
17곳도 대부분 서로를 참고하며 같은 오기를 반복한 것으로 보임(README
색인이 base 파일 배너를 그대로 요약하는 식). 세션 번호는 가능하면
파일명(`session/YYYY-MM-DD-NN-slug.md`)에서 기계적으로 뽑아 쓰는 게
안전.
## 반영 상태
base/README/question.md/archive/ROADMAP/CLAUDE.md/tools/ 전부 이 세션
안에서 즉시 반영 완료 — 24개 파일 수정, `doc-check.py` ERROR 0 유지
확인. 새로 연 설계 질문 없음(전부 기존 서술 정합성 문제), 0-Y/0-Z 상태
불변.

View file

@ -14,7 +14,7 @@
검사 항목 검사 항목
1. [ERROR] 깨진 파일 참조 라이브 문서가 가리키는 .md/.luau가 실제로 없음 1. [ERROR] 깨진 파일 참조 라이브 문서가 가리키는 .md/.luau가 실제로 없음
2. [ERROR] 깨진 참조 `foo.md` "절 제목" 파일에 없음 2. [ERROR] 깨진 참조 `foo.md` "절 제목" 파일에 없음
(문서를 쪼개거나 헤딩을 고칠 가장 깨지는 여덟 번째 세션에 (문서를 쪼개거나 헤딩을 고칠 가장 깨지는 아홉 번째 세션에
bind-system-plan.md를 분할하며 20곳이 여기 걸렸음) bind-system-plan.md를 분할하며 20곳이 여기 걸렸음)
3. [ERROR] 색인 누락 base/research/archive/reference 파일이 README에 없음 3. [ERROR] 색인 누락 base/research/archive/reference 파일이 README에 없음
4. [WARN] 날짜 없는 시한부 주장 "아직 안 돌려봄", "열린 질문 없음" 4. [WARN] 날짜 없는 시한부 주장 "아직 안 돌려봄", "열린 질문 없음"

View file

@ -183,9 +183,11 @@ modifier/Ref의 컴포넌트 경계 통과 방식) 논의도 2026-08-04 세션
새 모델은 `research/dispatch-redispatch-diff-plan.md`에만 있음 — 새 모델은 `research/dispatch-redispatch-diff-plan.md`에만 있음 —
base만 읽고 구현하면 옛 모델로 짜게 됨. 0-Z가 정해지면 그 문서 6절의 base만 읽고 구현하면 옛 모델로 짜게 됨. 0-Z가 정해지면 그 문서 6절의
파일별 반영 목록대로 **한 번에** 옮길 것 — 대상은 ⚠️ 배너를 달고 파일별 반영 목록대로 **한 번에** 옮길 것 — 대상은 ⚠️ 배너를 달고
있는 **6개**(`bind-system-plan.md`/`tag-plan.md`/`slot-plan.md`/ 있는 **7개**(`bind-system-plan.md`/`tag-plan.md`/`slot-plan.md`/
`attribute-plan.md` + **`architecture.md`/`ROADMAP.md`** — 뒤 둘은 `attribute-plan.md` + **`architecture.md`/`ROADMAP.md`** — 뒤 둘은
2026-08-13 7차 감사에서 6절 목록에 빠져 있던 걸 발견해 추가), 2026-08-13 7차 감사에서 6절 목록에 빠져 있던 걸 발견해 추가 +
**`ref-plan.md`** — 9차 세션 분할 때 "`Ref`의 retract" 절이 배너
없이 옮겨간 걸 이번 감사에서 발견해 추가),
반영 후 각 배너도 같이 제거. 반영 후 각 배너도 같이 제거.
- 반대로 **`base/slot-plan.md`의 "언마운트/`dispose`/해제 순서"는 이미 - 반대로 **`base/slot-plan.md`의 "언마운트/`dispose`/해제 순서"는 이미
확정 반영됨**(재디스패치 모델과 독립적인 결정이라 먼저 들어감). 확정 반영됨**(재디스패치 모델과 독립적인 결정이라 먼저 들어감).
@ -1020,3 +1022,17 @@ Slot 언마운트 전환 미반영 6곳. 뒤집힌 "폐기, 옮기지 않음 + p
누락, 날짜 없는 시한부 주장, 미반영 배너). **같은 세션에 실효 증명**: 분할 누락, 날짜 없는 시한부 주장, 미반영 배너). **같은 세션에 실효 증명**: 분할
중 "이중 바인딩 금지" 절 참조 4곳을 잘못 옮긴 걸 스크립트가 잡아내 되돌림. 중 "이중 바인딩 금지" 절 참조 4곳을 잘못 옮긴 걸 스크립트가 잡아내 되돌림.
CLAUDE.md "작업 방식"에 중대 변경 핸드오버 체크리스트 6단계도 명문화. CLAUDE.md "작업 방식"에 중대 변경 핸드오버 체크리스트 6단계도 명문화.
**2026-08-13 열 번째 세션 — 병렬 에이전트 코퍼스 감사, 실제 부정확성 7건**
(`session/2026-08-13-10-corpus-audit-parallel-agents.md`)
사용자 요청으로 세션 기록 대조 전수 감사(미확정 항목은 문제로 안 셈,
`doc-check.py` 선실행 후 기계가 못 잡는 것만 6개 병렬 Explore 에이전트로
분담). 아홉 번째 세션의 구조 변경(폴더 재편/분할/트림) 반영 누락이
대부분: luau-test 재편 후 깨진 flat 경로 참조 9곳, `bind-system-plan.md`
분할 후 자기참조/외부참조 깨짐 8곳, **`ref-plan.md`에 0-Z 배너가 안
옮겨와 옛 재디스패치 모델을 무배너로 서술 중이던 것**(반영 대상
6개→7개로 정정), "8차 세션"으로 잘못 표기된 9차 세션 작업 17곳(git
커밋 타임스탬프로 교차검증), `question.md` 트림 중 빠진 열린 질문 1건,
트림 후 깨진 참조 2곳, `ROADMAP.md` M0 섹션의 0-Y/0-Z 게이트 표시 누락.
발견 즉시 24개 파일에 직접 반영, `doc-check.py` ERROR 0 유지 확인. 새로
연 설계 질문 없음.

View file

@ -8,6 +8,13 @@ quad-v2 구현 단계 실행 계획. 설계 근거/아키텍처 자체는 여기
확정될 때마다 각 마일스톤 체크박스가 계속 갱신돼왔음 — 그래도 아직 M0 확정될 때마다 각 마일스톤 체크박스가 계속 갱신돼왔음 — 그래도 아직 M0
자체는 시작 안 함.** 다음 세션은 바로 M0부터. 자체는 시작 안 함.** 다음 세션은 바로 M0부터.
> **⚠️ M0 착수 자체가 두 결정으로 막혀 있음 — `.claude/question.md` 0-Y
> (`:Compute(fn)` lazy 핸들 계약과 Luau 타입 추론 충돌)/0-Z(Attribute
> 이름 소유권). 둘 다 "M0 착수 전에 정할 것"으로 명시돼 있음 —
> `HUMAN_TODO.md` 4번, `CLAUDE.md` "지금 할 일" 0번 항목 참고. 아래
> M0 체크리스트 자체는 이 두 결정과 무관하게 유효하지만, 순서상 이
> 둘이 먼저.**
## M0 — 스켈레톤 + 기술검증 (스파이크, "진짜" 마일스톤 아님) ## M0 — 스켈레톤 + 기술검증 (스파이크, "진짜" 마일스톤 아님)
최종 소스 트리를 그대로 만들기 전에, 지금까지 **추론만으로 확정하고 실제 최종 소스 트리를 그대로 만들기 전에, 지금까지 **추론만으로 확정하고 실제