`question.md` 최우선 두 건을 한 패스로 닫고, 배너를 달고 있던 7개 문서 +
인덱스 레이어 전체를 갱신했다.
0-Z (Attribute 이름 소유권):
- 그룹은 비공개 `GetKey`로 이름마다 자기 전용 키를 써서 위임 → 교차 오염
구조적 제거. 이름 소유권은 `AttributeKeyHandler`의 이름 claim이 판정
(`nameClaims` Relate, 다른 키가 같은 이름을 노리면 즉시 error).
- 권고안 (a)(그룹 안 claimant Relate)는 그룹↔직접 쓰기를 못 잡아 기각 —
두 경로가 만나는 말단 핸들러에서 공개 키는 같은 객체라 소유자 구분 불가.
0-A (재디스패치 = 하강 diff):
- 래핑 핸들러의 선행 `retractFrom` 폐기, `Dispatch.process`가 슬롯의
`handler`를 먼저 비교(같으면 클로저에 새 값 전달 후 재process, 다르면
그 자리부터 전량 철거).
- 귀결: `retractFrom`이 3-인자로 축소(힌트를 외부에서 만들어 넣을 자리
소멸), `isX(hintValue)` 가드 규칙 폐지, 깊은 체인 힌트 유실 캐비엇 삭제,
Dispatch의 점유 체크 폐지.
- 9차 세션이 미뤄둔 2단계 분할을 같이 수행 — 디스패치 코어를
`base/dispatch-core-plan.md`로 분리하며 재작성(bind-system-plan은
2263→1219줄). 옛 모델은 archive/dispatch-hintvalue-model-reversed.md.
패키지 재배치 (사용자 제기):
- Tag/Attribute의 부기 알고리즘 전체를 quad-base로, 백엔드는
`addTag`/`removeTag(inst,{string})`/`setAttribute(inst,name,v)` 3개 op만
주입(웹 className/data-* 대응). 엔진 고유 타입 패밀리만 백엔드.
- `HANDLER_PRIORITY_FALLBACK` 신설 — base 제공 핸들러의 밴드, 백엔드가
평범한 우선순위로 덮어쓰면 언제나 이김.
부수: 스파이크 04/19가 옛 모델을 검증 중이라 rewrite-required로 이동,
question.md 최우선 칸 비움, 새 소소 항목 2건 등록(Merged 이름 중복,
`hintValue` 이름 재검토). doc-check ERROR 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KckSawrsJSJmDBcSojJPxZ
9.3 KiB
스파이크 상태판 — 폴더가 곧 상태
마지막 갱신: 2026-08-13 열네 번째 세션(하강 diff 재디스패치 확정으로
04/19가 옛 모델을 검증하고 있어rewrite-required/로 이동). 첫 실측은 여섯 번째 세션 — 상세 결과는.claude/audit/luau-test-first-run-2026-08-13.md. 실행법:luau <파일>(런타임) /luau-analyze <파일>(타입 전용).
[2026-08-13 열세 번째 세션] review-required/가 비었습니다 — 마지막
한 건이던 08이 해소돼 done/으로 갔습니다. 지금 남은 건 에이전트가
처리할 일(rewrite-required/)과 Studio가 필요한 일(not-run/)뿐입니다.
| 폴더 | 뜻 | 개수 | 누가 처리 |
|---|---|---|---|
review-required/ |
설계가 걸림 — 사람 결정 필요 | 0 | ⭐ 사용자 |
rewrite-required/ |
스파이크가 낡음(코드가 깨졌거나, 설계가 바뀌어 옛 모델을 검증 중) | 5 | 에이전트 |
not-run/ |
이 환경에서 못 돌림(Studio 전용) | 1(+헬퍼 1) | 사용자 or MCP 연결 후 에이전트 |
done/ |
통과 or 판정 끝, 더 할 일 없음 | 14 | — |
폴더를 옮기는 게 곧 상태 갱신 — 스파이크를 고치거나 돌렸으면 파일을
해당 폴더로 git mv하고 아래 표의 줄도 같이 옮길 것. 파일별 "무엇을 왜
검증하는가"는 README.md가 담당(이 파일은 상태만).
⭐ review-required/ — 사람 결정 필요 (0건, 비어 있음)
[2026-08-13 열세 번째 세션] 마지막 한 건이 해소됐습니다.
08-type-source-satisfies-state.luau가 남겨뒀던 잔여 케이스(State<T>가
자기 자신을 다른 타입 인자로 재귀 참조하면 막힘)는 Luau의 현 한계로
확정되어 quad가 설계로 풀 대상이 아님이 정해졌고(구 question.md
0-Y 해소), 스파이크는 done/으로 이동했습니다. 당시 검토됐던 "구울 때
인라이닝" 방향은 채택 안 함.
- 지금 유효한 규약:
base/typing-limits.md - 실측 근거 전문(스파이크 44개 포함):
audit/type-recursion-issue/
15도 같은 계약을 다루지만 스파이크 자체가 파싱 실패라
rewrite-required/에 그대로 둠 — 재작성 대상이지 사람 결정 대상이
아님(계약 자체는 위에서 이미 확정됨).
🟠 rewrite-required/ — 스파이크가 낡음 (5건)
[2026-08-13 열네 번째 세션] 앞의 두 건은 "코드가 깨진" 게 아니라 "설계가
바뀐" 경우 — question.md 0-A/0-Z 확정으로 재디스패치가 하강 diff가
되면서, 이 둘이 검증하던 전제(선행 retractFrom + 4-인자 힌트 + 인덱스
점유 체크)가 더 이상 설계가 아님. 통과 상태로 done/에 두면 옛 모델을
"검증됨"으로 오독하게 되므로 옮김. 새 정본은
base/dispatch-core-plan.md/base/attribute-plan.md.
| 파일 | 상태 | 무엇을 고쳐야 하나 |
|---|---|---|
04-dispatch-chain-retractFrom.luau |
옛 모델 기준으로는 ✅ 통과였음 | (1) chains 슬롯이 {handler, retractor}가 되고 Dispatch.process가 핸들러를 먼저 비교하는 하강 diff로 재작성, (2) retractFrom은 3-인자(힌트 인자 없음), (3) "힌트가 target 인덱스에만 간다"를 검증하던 부분은 정반대로 뒤집힘 — 이제 각 레벨이 자기 값을 받는지를 검증해야 함. 살릴 것: chains:SetStrong 순서 음성 대조군(그 버그는 새 모델에서도 그대로 유효) |
19-ownership-refcount-relate-patterns.luau |
A/C ✅ 유효, B 섹션이 낡음 | B가 검증하던 "공개 AttributeKey(name) + 인덱스 1 점유 체크"가 폐기됨 — 그룹 전용 키 + AttributeKeyHandler의 이름 claim으로 재작성하고, 음성 대조군도 "두 그룹이 같은 이름 → 즉시 error", "그룹↔직접 쓰기 → 즉시 error"로 바꿀 것(0-Z 확정 내용). A/C는 손댈 것 없음 |
13-type-ref-preref-subtype.luau |
타입 A섹션 ✅ 통과 / 런타임 B섹션 실행 불가 | B가 A의 더미 스텁(fakePreRef = nil)에 막혀 도달 못 함 — 두 섹션을 파일로 분리 |
15-type-compute-trailing-deps-typepack.luau |
파싱 실패(SyntaxError) | 음성 대조군의 타입 표기가 TypeError가 아니라 SyntaxError로 걸려 파일 전체가 아무것도 검증 못 함 — 대조군을 별도 파일/블록으로 격리 |
16-type-store-key-typefunction.luau |
❌ 실패 | types.newfunction 시그니처가 설치된 버전의 실제 API와 안 맞음 — 실제 API 재확인 후 재시도 |
⚪ not-run/ — 이 환경에서 못 돌림
| 파일 | 이유 |
|---|---|
10-roblox-studio-checks.server.luau |
Studio 전용(luau CLI로 못 돌림). A 섹션 앞부분만 사용자 자작 스크립트로 실측 — audit/gcconn-trick-verification.md. A-1/A-2(canBound 게이트)/B/C는 여전히 미확인 |
gc-trigger-helper.server.luau |
스파이크가 아니라 헬퍼 — Studio에 collectgarbage()가 없어서 GC를 강제 트리거하는 기법. 10을 돌릴 때 같이 씀 |
✅ done/ — 통과 or 판정 끝 (14건)
런타임 12개 전원 통과(crash 0 / FAIL 0) — [열네 번째 세션] 그중
04/19는 검증 대상 설계가 바뀌어 위 rewrite-required/로 이동했고,
아래 표엔 남은 10개만 있음(실측 당시 통과였다는 사실 자체는 유효):
| 파일 | 확인된 것 |
|---|---|
01-two-pass-array-hash-order |
배열 파트 전체 → 해시 파트 순. Dispatch.drive 두 패스 계약과 PreRef 호이스팅의 전제 |
02-none-sentinel-vs-nil-holes |
nil 소진 시 #t 50→49로 무너짐 / None은 항상 50. 반대로 Ref 콜백 배열은 None 쓰면 죽은 슬롯 1000개 잔존 — 두 배열의 규칙이 서로 반대여야 함이 정량 확인 |
03-recursive-store-bind-dispatch |
StoreBind 재귀 재-dispatch, None→nil 흐름, 무한재귀 없이 종료 |
05-store-state-diamond-propagation |
다이아몬드에서 재계산 정확히 1회, invalidate 2번째는 즉시 중단 |
06-component-boundary-nil-hole-props |
or None 없으면 앞쪽 nil-hole로 슬롯 소실, 관용구 쓰면 항상 보존 |
07-relate-weak-table-gc |
연쇄 GC 확정(아래 별도 절) — GC-native 아키텍처의 핵심 전제 |
11-modifier-illegal-value-error |
Modifier 필드/Source에 핸들러 계층 값 넣으면 즉시 error — 16개 케이스 전원 |
17-modifier-index-tableclone-chaining |
제네릭 __index + table.clone 체이닝, 메타테이블 참조 공유, 형제 분기 무오염 |
18-relate-mutual-cycle-gc |
두 Relate 상호 순환은 실제로 GC 안 됨(아래 별도 절) |
20-slot-splice-index-arithmetic |
Splice 산술 11개 경계 케이스 전부 참조 구현과 일치 |
타입 스파이크 중 판정이 끝나 더 할 일 없는 것:
| 파일 | 판정 |
|---|---|
08-type-source-satisfies-state |
✅ 핵심 질문(Source⊇State 구조적 서브타이핑) 통과. 잔여 케이스(자기 이름을 다른 인자로 재귀 참조)는 [2026-08-13 13차 세션] Luau 현 한계로 확정 — quad가 풀 대상 아님, base/typing-limits.md 1번 |
09-type-modifier-overridden-subtype |
✅ 통과 — 문서가 우려한 FrameModifier↔GuiObjectModifier 서브타입 깨짐이 그대로 재현, fallback(any)은 정상 |
12-type-attribute-generic-key-narrowing |
❌지만 설계 영향 없음 — 제네릭 키 narrowing이 안 되는 건 attribute-plan.md가 이미 fallback으로 예비해둔 결과(타입 패밀리가 유일하게 믿을 경로) |
14-type-nilable-default-overload |
⚠️ 부분 — 의도한 오용은 막지만 정상 nilable 사용례까지 막아 현 스케치로는 채택 불가. 설계 결정은 아직 필요 없음(대안이 이미 UB 경고로 존재)이라 review-required가 아님 |
특별히 중요한 통과 3건
04 — 직전 감사가 찾은 버그가 음성 대조군으로 재현됨(파일은 지금
rewrite-required/에 있음 — 아래 관측 자체는 새 모델에서도 유효)
| 관측 지점 | 정상(수정본) | 대조군(버그) |
|---|---|---|
| 최초 마운트 후 체인 깊이 | 3 | 1 |
| 재발행 후 옛 store 구독 | 0 (끊김) | 1 (안 끊김) |
| 죽은 store를 건드리면 | 값 유지 | STALE로 덮어써짐 |
chains:SetStrong을 handler.process 뒤에 두면 하위 retractor가 통째로
유실되고, 결국 버려진 store가 나중에 UI를 덮어쓰는 데까지 감.
07 — GC-native 아키텍처의 핵심 전제
inst 5개만 살린 상태 → 살아남은 payload 5 / 엔트리 5 (기대치 일치)
모든 참조를 놓은 뒤 → 살아남은 payload 0 / 엔트리 0 (기대치 일치)
bindLifetime으로 매달아둔 자원이 inst와 함께 연쇄 소멸함이 확인됨.
(이 스파이크는 원래 sanity check만 하고 있어서 이번에 보강한 것 — 파일이
스스로 적어둔 "weak table 엔트리를 셀 방법 없음"이라는 전제가 틀렸음.)
18 — relate-plan.md의 상호 순환 경고
상호 강참조 순환: inst=true, value=true (GC 못 풂)
한쪽을 weak-value로 낮춤: inst=false, value=false (풀림)
추측이 아니라 실제로 GC가 안 됨 — Slot의 두-Relate 수정이 필수
조치였음이 입증.