`qa-request/pre-implementation-handtrace-round7.md`의 `H-55`~`H-106`을
사용자와 대화형으로 처리하고 `base/`에 전량 반영했다. 결정의 소스는
`-followup.md`(개수·개별 항목은 여기서 세지 않는다).
## 처분
확정 39 / 무효·소멸 4(`H-73`~`H-76`) / 기각 1(`H-77`) / 범위 축소 2 /
다른 항목으로 흡수 6.
**부수로 `question.md` 최우선 절이 비었다** — 중간 State GC는 `_hold`
불변식(하류 → 상류 강함)으로, 동적 키 표면 위치는 `store:Of<<T>>(name)`
하나로 닫혔다. **M2 착수를 막는 항목이 없다.**
## 구조가 바뀐 것
- `Ref`가 `Epoch`를 만족(`.Revision` + `EpochBrand`) — 포탈 캐치업 비대칭과
같은 `Ref` 중복 dep이 같이 닫힘
- `Weak*` 등록 표면 신설(`Ref:WeakCallback` / `Observer:WeakSubscribe`) —
Weak 쪽이 프리미티브고 강한 쪽이 "GC 킵"을 얹은 것
- `Effect`: dep 등록이 생성자 한 곳으로, 강한 주인은 `_deps` 하나,
억제는 사적 `Blocker`, `bindLifetime`은 핸들 하나에만 적용.
`:Rerun()` 정의 신설(재진입은 지연 재실행), `_installed` 신설
- 전파 루프 의사코드 확정 — 구독자 집합의 원소는 Observer **값**이고
**자식 State 노드는 `canExecute`를 안 탄다**(그대로 짜면 파생 State
아래가 전부 침묵했다)
- `rawInvalid` → `cacheTargetCount`/`cacheCurrCount` 카운터 쌍
- `recompute` 재진입 차단 + `invalidAfter` 되감기, `gatedRecompute`가
인덱스를 캡처하지 않고 토큰으로 조회
- `emit(commit) -> boolean`, `EpochMap:Peek` — 정책이 흡수 집합을
버리지도 읽지도 못하던 것을 닫음
- error 계약(`level` 이분, 메시지는 영어)과 예외 계약(`pcall`로 안 감쌈)을
`architecture.md`에 신설
## Store — 같은 날 재설계하고 철회했다
`H-75`/`H-76`으로 `WrapStore`/`ProcessStoreType`이 폐기되자 그 자리를
"`store.key`는 값, `store:Of(k)`가 프리미티브"로 채웠다가 **같은 날
철회**했다(`archive/store-value-field-redesign-withdrawn.md`). 살아남은 건
**명시적 초기화** 하나다. 최종형은 타입 인자에 `Source<T>`를 직접 쓰고
`store.key`는 평범한 레코드 필드이며 **타입 함수를 안 쓴다**.
철회 이유 중 하나가 원칙으로 승격됐다 — **"타입 함수는 타입이 못 잡는
문제를 에러로 띄우는 정도 이상으로 가지 않는다"**(`typing-limits.md` §0).
`index<>`/`keyof<>`도 Luau가 predefine한 타입 함수라 같은 함정을 갖는다.
## 툴체인
**`luau` CLI가 심볼릭 링크를 못 탄다**(디렉토리·파일 둘 다)는 것이 최소
재현으로 밝혀졌다 — pesde 워크스페이스 링크가 전부 심볼릭이라 스모크 2개가
안 돌았고 `luau-analyze`는 **조용히 통과**했다("거짓 클린"). `scripts/relink.sh`
+ `scripts/test.sh` 신설, 이제 스모크 셋 전부 PASS.
## 검증
`/code-review high` 2회(12건 + 14건)와 감사 8패스(12/6/6/4/13/3/6/0건)를
각도를 바꿔가며 돌렸고 전부 반영했다 — 마지막 패스가 무발견으로 수렴.
각 패스의 각도와 대표 발견은 `-followup.md`의 검증 절이 소스.
`doc-check.py` ERROR 0.
Co-authored-by: qwreey <me@qwreey.moe>
Claude-Session: https://claude.ai/code/session_012oLwATeQdq9TCFdENPutFG
사용자 제기 — doc-check.py가 정규식으로 결정론적 판정을 하는데 표기가 흔들리면
문제가 커지니, 정규식을 늘리기보다 "예상 가능 범위"를 컨벤션으로 좁히는 게 싸지
않냐. 실측해보니 날짜 표기는 이미 100% 균일해서 고칠 게 없었고(강제 장치 없이),
드리프트는 절 인용 쪽이었다 — WARN 86건 중 78건(91%)이 절 참조 불일치.
핵심은 그 78건이 코퍼스가 지저분한 게 아니라 **검사기가 못 읽는 것**이었다는
점이다. 이 코퍼스는 `**볼드**` 줄을 하위 절로 쓰는데 headings()가 `#`만 봤다.
## 동작 변화 (문서 정정으로만 보이지만 게이트가 바뀐다)
- 절 참조 불일치가 **WARN → ERROR**. 이제 절 인용 오류가 커밋을 막는다.
- 절 인식이 `#` 헤딩 + `**볼드**` 절로 확장. 단 볼드는 **빈 줄 다음이나
리스트 항목 머리**만 인정 — 문단이 줄바꿈되며 우연히 줄머리에 걸린 강조를
절로 오인하던 걸 커밋 전 감사가 잡아 조였다.
- 인용 길이 상한 60→160자. 60자를 넘으면 매칭 자체가 안 걸려 검사에서
**조용히** 빠져나갔음(위양성보다 나쁜 구멍).
- 비교를 공백 무시로(줄바꿈 인용 대응), 선두 장식·상태/날짜 태그 정규화,
`initreq/` 대상 인용은 절 검사 면제(읽기 전용 외부 원본).
## 규약
`conventions.md`에 "문서 표기 규약" 절 신설 — 절 인용 규약(의역 금지, 헤딩은
부분문자열/볼드는 앞부분일치, 태그로 닫히는 볼드 캐비엇, blockquote 함정),
세션은 산문 서수 말고 파일 ID로 지칭. 날짜 마커 라벨 어휘 닫기는 사용자 판단
으로 기각(기계 검사 대상이 아니라 읽는 쪽 판단 재료).
## 결과
절 참조 불일치 78 → 0. 36건은 검사기 수정으로 사라졌고(애초에 위양성), 42건은
인용을 실제 절 제목으로 손으로 고쳤다. 마지막 15건은 서브에이전트 3개에 병렬
위임해 추적 — **설계 서술이 유실된 건은 0건**, 대부분 코드 주석·본문 산문·
주제명처럼 애초에 절이 아닌 걸 절로 인용해온 것이었다.
부수로 드러나 같이 고친 것: onchange-plan이 9차 분할 때 일부러 안 옮긴 절을
잘못된 파일로 가리키던 것, brand-plan이 이미 이행된 정정을 "정정 대상"이라
부르던 것, ROADMAP의 blockquote가 인용 줄바꿈 때문에 깨져 있던 것,
pre-implementation-audit의 해소된 항목이 "아직 안 고침" 절에 남아 있던 것
(사용자 결정으로 "이미 고침"으로 이동).
커밋 전 감사 4라운드(에이전트 8개)를 돌렸고, 발견 추이는 2→2→1→0이다.
매 라운드 발견이 "직전 라운드 수정이 만든 새 결함"이었던 게 특징 — 규약을
세우는 커밋이 그 규약의 첫 위반자가 된다는 걸 실측으로 확인했다. 상세는
.claude/session/2026-08-16-03-doc-check-section-convention.md.
부수: __pycache__를 .gitignore에 추가하고 추적 해제(32e9db0에 실수로 딸려
들어가 있었음). quad-doc-auditor에 작업 트리를 바꾸는 git 명령 금지 규약 추가
— 감사자가 git stash를 걸어 메인 세션 스테이지가 반복적으로 풀렸다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011zk7XHkSfiBfdPLZUQHdZf
전 세션이 "재시작해야만 확인 가능"으로 남긴 3건을 전부 닫음:
- @import 3개(conventions/project-context/todos) 실제 로드 확인
- quad-doc-auditor 레지스트리 등록 확인(전 세션 전멸은 .claude/agents/가
세션 도중 생긴 디렉토리였던 탓)
- frontmatter model: sonnet 반영 확인(트랜스크립트에 claude-sonnet-5 기록,
워크플로에 opts.model 명시 불필요)
정의 파일은 세션 시작 시점 스냅샷으로 고정된다는 것을 1차 증거로 확정 —
quad-handover-audit이 실제 실행한 스크립트가 세션 시작 시점 상태와 바이트
단위로 동일했고 같은 세션의 편집은 반영 0. 에이전트 정의도 동일.
정의를 고쳤으면 재시작 뒤에 감사를 돌릴 것(안 그러면 거짓 초록불).
전 세션 감사가 남긴 긴 가설 배너(80줄)를 검증된 것만 남겨 압축.
quad-handover-audit 첫 실동: 에이전트 67개/6라운드, 수렴 실패
(새 발견 28→15→16→7→11→6, 라운드5에서 되레 증가). MAX_ROUNDS와
"연속 dry 2회" 조건 재검토 필요 — 결과 자체는 위 스냅샷 문제로 옛
스크립트가 돈 것이라 재시작 후 재실동 대상.
감사가 잡은 것 반영: slot-plan.md 정정 배너가 그 뒤 재역전(retract=언마운트)을
놓치고 있던 것, "spikes 44개"(실제 48개) 류 하드코딩 개수의 단일 소스화,
doc-check.py docstring이 검사 심각도를 실제 코드와 다르게 서술하던 것 등.
인용 출처 3건 재분류 — 2건은 인용 대상만 틀린 것이라 실제 소스로 재조준
(v1-compat-plan.md→component-composition-plan.md+store-plan.md,
pre-implementation-audit.md→ROADMAP.md). 진짜 출처가 없는 1건
(modifier-plan.md:536)만 question.md 3번으로 올려 사용자 판단 대기.
워크플로 개선: 반환값에 findings 추가(커밋 전 diff 리뷰 근거),
totalFindingsFixed→findingsSentToFix 개명(과대계상), 반영 에이전트 sonnet 명시.
.claude/agent-memory/는 의도적으로 커밋 제외(추적 여부는 사용자 판단).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
## CLAUDE.md 분할
1537줄이라 (a) 사람이 검토 불가, (b) 공식 권장치(파일당 200줄) 7.7배
초과로 지침 준수도 자체가 저하, (c) 긴 파일 편집 시 에이전트 실수 증가.
CLAUDE.md (39줄, 진입점)
├─ @.claude/conventions.md 언어/모델 관례 + 작업 방식
├─ @.claude/project-context.md 프로젝트 설명 + 문서 구조
└─ @.claude/todos.md 지금 할 일
.claude/session-summary.md ← import 안 함(의도적, 온디맨드)
세션 히스토리 1231줄(전체의 80%)은 그 문서 스스로 "항상 읽을 필요 없음,
base/가 소스"라고 명시해온 색인이라 @import에서 뺐음. 내용 유실 없음
(1537→1647줄, 추가 헤더만큼 증가).
주의: @import는 컨텍스트를 줄이지 않음(전부 로드됨). 분할이 사는 건
사람 검토성 + 편집 정확도 + 파일 단위 자동생성 가능성.
CLAUDE.md 계열의 블록 HTML 주석은 주입 전 제거되므로 지시는 본문에 쓸 것.
## 워크플로 가짜 초록불 수정
첫 실측에서 감사 에이전트 6개 전원 실패했는데 converged:true가 나왔음
(전멸하면 fresh가 비어 "깨끗한 라운드"와 구분 불가). 감사 도구 최악의
실패 모드라 (1) 전멸이면 throw, (2) 반영 에이전트 실패 시 그 발견을
seen에서 빼 다음 라운드가 재시도하도록 수정.
## 부수
- 분할로 깨진 상호참조 20여 곳 정정(병렬 에이전트 3개).
ref-plan.md:541의 사전 존재 오류(→ pre-implementation-audit 1-5)도 정정.
- doc-check.py: 새 파일 4개를 OURS에 등록(안 하면 깨진 참조가 WARN으로만
잡힘), is_history()로 session-summary.md를 archive/와 같이 면제.
- doc-include-plan.md: 목적지가 통째로 생성되는 파일이 되면서 양방향
마커 설계의 절반(목적지 마커)이 불필요해져 단방향 생성으로 단순화.
doc-check.py ERROR 0 유지.
미해결: session-summary.md 자동생성 미착수(91개 세션 파일 마커 삽입 필요),
orphan 인용 3건(modifier-plan.md:536, v1-compat-plan.md:50,
pre-implementation-audit.md:434 — 분할 이전부터 존재).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HvEKn9f67kkx2nGLG8PsP
type-recursive-issue-try-callback/ 신설(spikes 35개) — type function/
메타테이블/제네릭/이중 꺾쇠 명시 인스턴스화까지 전방위로 재시도했으나
quad의 state:Compute(fn) 단일 호출 모양을 유지한 채로는 여전히 안 됨,
근본 원인은 재귀 특유가 아니라 "제네릭 콜백 인자엔 컨텍스트 타입
전파가 안 됨"이라는 더 일반적 Luau 한계임을 확인. typing-limits.md
§1/§7에 반영, 원칙 자체는 안 바뀜.
이어진 /code-review 2회전이 이 작업과 luau-test/16 복구 세션
전반에서 정합성 문제 다수 발견 — 스파이크 결과 표 stale(16번 실패로
잔존), 폴더 개수 off-by-one 여러 건, type-recursion-issue/REPORT.md의
실제 오류(self가 제네릭이면 자유 함수로 빼도 여전히 실패한다는 사실이
"통과"로 잘못 서술됨), 옛 솔버 캐비엇 누락, 체크리스트 항목 간 모순,
이 세션 자체의 히스토리 누락 등 — 전부 수정. doc-check.py ERROR 0 유지.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HvEKn9f67kkx2nGLG8PsP
워크트리(worktree-debounce-throttle-plan)에서 네 라운드로 다듬은 결과를
메인의 3단계 분할 구조에 맞춰 필요한 변경만 이식.
## 신설: research/debounce-throttle-plan.md
- Blocker가 이미 쓰는 게이티드 노드의 릴리스 트리거만 타이머로 바꾼 것.
공개 Blocker API엔 "상류 신호 도착" 통지가 없어 그 위엔 못 얹음 →
M3에서 게이트를 공용 Gate로 뺄 것.
- 두 도구의 차이는 "신호가 창 타이머를 리셋하는가" 한 비트뿐.
공개 생성자 2개 + 내부 구현 1개(초안이 옮겨온 lodash식 maxWait 공식엔
trailing 통과 직후 이중 발화 버그가 있었음).
- quad-base + 주입 op 2개: setTimeout(func, delay) -> Timeout /
clearTimeout. Roblox는 task.delay/task.cancel로 배선(인자 순서 반대).
os.clock()은 Luau 표준 라이브러리라 주입 대상 아님(diff 전용).
Timeout = { __type_timeout: true, _native: any }.
## 역전: emit은 자기 invalid 상태와 무관하게 항상 전파된다
source-state-plan.md의 "이미 invalid였다면 그 아래로 더 전파하지 않는다"가
확정된 Observer 계약(fn이 :Get()을 안 불러도 됨)과 정면 충돌 — 액면대로면
:Get() 안 하는 Observer는 한 번 울고 영구 침묵. architecture.md가 같은
다이아몬드 문제를 pull-recompute로 설명하는 것과도 어긋나 있었음.
정정 모델: invalid는 캐시 낡음 표시일 뿐, 중복 재계산은 pull-recompute+
캐시가 막고 중복 통지는 안 접음(접으려면 Blocker 같은 명시적 게이트).
- source-state-plan.md: 전파 규칙 재작성, "다이아몬드 의존성은 무엇이
푸는가" 절 신설, Observer 절 상호 참조. 플래튼 기각/:With 빌더 기각
근거를 캐시 공유로 재작성(두 결론 유지, 근거 강도는 상승)
- architecture.md, blocker-plan.md(전파를 지연시키는 유일한 요소로 위치
명문화), comparison-fusion-vide.md, framework-comparison-findings.md
- ROADMAP M0 체크리스트: 확인할 것이 정반대가 됨
- luau-test 05 → rewrite-required/(옛 모델을 통과 상태로 검증 중이었음),
STATUS.md 개수 동기화(rewrite 6→7, done 14→13)
- audit: 05 행 정정 + "12개 전원 통과"를 액면대로 읽지 말라는 경고
- archive/invalidate-dedup-propagation-reversed.md 신설
doc-check: ERROR 0 / WARN 84(작업 전 85).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
세 건을 한 커밋에 처리:
1. ui-shorthand-plan.md — Tween 지원 확정. 숏핸드가 자식 프로퍼티를 직접
대입하지 않고 Dispatch.process(child, prop, ..., 1)로 위임하면 Tween이
공짜로 따라옴(해석 코드는 PropertyHandler 하나에만 남음). "process 중
inst를 바꾸는 건 키를 바꾸는 것과 같은 층위라 UB 아님"을
dispatch-core-plan.md에 일반 규칙으로 명문화. wrap을 Tween<T>.Value에만
적용되도록 들어올리는 헬퍼가 새로 필요한 유일한 부품. ROADMAP M10에
통째로 빠져 있던 UI 숏핸드 항목도 보강.
2. existing-instance-bind — 기각, research/ → archive/. 사유: Length/Offset
등 quad가 만든 트리를 전제한 부기를 바깥에서 밀고 당기는 버그 표면이
치명적으로 넓어짐. "열려 있음"을 전제로 쓰인 본문 7곳도 같이 정정
(architecture.md의 "아직 미정" 절은 유일 항목이었어서 절 자체를 갱신).
3. bind-system-plan.md 3단계 분할 + store-semantics.md 흡수(순수 이동):
- base/store-plan.md 신설 — Store = 이름 붙은 Source 모음
- base/source-state-plan.md 신설 — 반응형 코어(Source⊇State, 전파 모델,
:With/:Compute/:Apply/previous, Observer, 구독·생명주기 게이트)
- bind-system-plan.md 1238→203줄(인스턴스 생성·이벤트 네이밍 + 색인)
- store-semantics.md 삭제
참조 40여 곳 스윕. doc-check.py ERROR 0, WARN 101→84.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
69466ab/32e9db0 이후 5개 영역 병렬 에이전트 감사 + 직접 트레이싱으로
프로젝트 전체를 다시 훑음. 핵심 설계(하강 diff 재디스패치, Tag
참조카운트, nameClaims, Slot-in-Slot 해체)는 재트레이싱해도 버그
없음 — 전부 같은 클래스의 문서 참조 잔여 오류였음: `bind-system-plan.md`
2단계 분할(14차 세션) 이후에도 그 파일을 계속 가리키는 느슨한 산문
인용(따옴표 절 제목이 아니라 "~가 말하는"/"~ 참고" 식이라 doc-check.py
정규식이 못 잡는 형태).
- `ref-plan.md`/`ui-shorthand-plan.md`(2곳)/`tween-plan.md`/
`slot-plan.md`(3곳) — `dispatch-core-plan.md`로 정정.
- `module-lifecycle-plan.md:21` — 32e9db0가 같은 파일 114/128행은
고쳤지만 21행만 놓쳤던 것.
- `ROADMAP.md`(2곳)/`luau-test/README.md` — `None`/`recompute` 절
참조 정정.
- `luau-test/done/02`/`03` 스파이크 주석 — 02는 `ref-plan.md`(9차
세션에 이미 옮겨간 절이었음, bind-system-plan.md였던 적 없음),
03은 `dispatch-core-plan.md`로 정정.
- `audit/luau-test-first-run-2026-08-13.md` — "no-op 점유 마커"를
여전히 유효한 수정 근거처럼 서술하던 부분에 정정 각주 추가(점유
체크 자체는 하강 diff 재설계로 폐지됨, `chains:SetStrong` 순서
버그만 지금도 유효).
doc-check.py ERROR 0 유지, WARN 101 그대로(전부 판단 필요한 기존
느슨한 인용, 이번 라운드가 새로 만든 건 없음). 5개 에이전트 전원
"설계 결함 0건" 보고로 수렴 판단.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
44개 스파이크로 question.md 0-Y를 재실측한 결과, 여섯 번째 세션의
"콜백이 raw 값을 받으면 완전 클린" 판정이 틀렸음이 드러남 — 그건 진단
0건만 확인한 것이었고, luau-analyze --annotate로 열어보니 반환 타입이
Unifiable<Error>로 조용히 새고 있었음(틀린 대입도 안 잡힘).
진짜 원인은 콜백 계약이 아니라 Compute가 State<U>(자기 이름을 다른 타입
인자로 감싼 타입)를 반환한다는 것 자체 — RFC relax-recursive-type-restriction이
Promise<T>.andThen으로 예시 든 바로 그 패턴. 사용자 확정: quad가 타입을
비틀 일이 아니라 상위 Luau의 현 한계이고, RFC/이슈 수혜를 받을 때 해결될
일이라 당장 할 수 있는 바 없음.
- base/typing-limits.md 신설 — 흩어져 있던 타입 한계 5건 통합, 대전제
"Luau 한계를 우회하려 타입/API를 비틀지 않는다", 새 API 설계 체크리스트
- audit/type-recursion-issue/ 신설 — REPORT.md + spikes 44개(audit 폴더에
스크립트를 같이 둔 첫 예외, 판정 재현에 개별 실행이 필요해서)
- 0-Y 해소 전파: question.md(최우선 2건→1건) / archive / base 5개 /
research 2개 / 인덱스 4개 / luau-test(08을 done/으로, review-required 비움)
- audit/luau-test-first-run-2026-08-13.md: 판정이 뒤집힌 당사자라 배너뿐
아니라 본문 표·문단·결론까지 전수 수정
- HUMAN_TODO 6번 신설: luau-lsp 기본이 옛 솔버라 CLI와 진단이 다름
교훈: luau-analyze 진단 0건은 타입 해소를 뜻하지 않음 — 타입 스파이크는
--annotate로 실제 추론 타입을 확인하고 음성 대조군을 같이 둘 것.
doc-check.py ERROR 0 유지(WARN 59건, 변경 전과 동일).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KckSawrsJSJmDBcSojJPxZ
luau/luau-analyze 바이너리가 사용 가능해져, 2026-08-09에 스파이크를 만들기
시작한 이래 처음으로 실제로 돌림(CLAUDE.md가 "M0 착수 전 남은 유일한
게이트"로 꼽아온 항목). 전문: .claude/audit/luau-test-first-run-2026-08-13.md
## 설계 검증 결과 — 런타임은 전부 성립
- 04가 직전 커밋의 감사에서 찾은 버그를 음성 대조군으로 재현: chains:SetStrong을
process 뒤에 두면 체인 깊이가 3 대신 1로 무너지고, 죽은 store가 나중에 UI를
덮어씀(STALE). 감사→수정 사이클이 실측으로 닫힘.
- 07은 보강해야 실제 검증이 됐음. 3번 섹션이 sanity check만 하고 있었고 헤더가
내세운 연쇄 GC 주장은 미검증이었음. 파일이 스스로 적어둔 "weak table 엔트리를
셀 표준 API가 없다"는 전제도 틀렸음(outer가 __mode="k"라 GC 후 pairs에서
사라짐) — _countEntries + weak-value canary로 4번 섹션 신설, GC-native
아키텍처의 핵심 전제(연쇄 GC)가 실측 확정됨.
- 18이 relate-plan.md의 상호 순환 경고를 실증 — 추측이 아니라 실제로 GC 안 됨.
- 01/02/03/05/06/20도 전부 통과.
## 타입 — 진짜 설계 이슈 1건 (question.md 0-Y 신설)
:Compute(fn)의 lazy 핸들 계약이 Luau 양방향 추론과 충돌.
`state:Compute(function(s) return s:Get() * 2 end)`가 타입 에러를 냄.
최소 재현으로 원인 확정: read/self 표기 조정으로는 안 풀리고, 콜백이 raw 값을
받으면 완전 클린. Effect/Observer/Animate/Operator 등 같은 계약을 공유하는
API 전부에 걸림 — M0 착수 전 결정 필요.
## 문서 결함 발견 — modifier-plan.md
"데이터를 테이블에 직접 두고"가 "self 최상위 리터럴 키"로 읽힐 여지가 있었는데,
그렇게 하면 __index가 rawget 성공 시 안 불려 같은 필드 재호출이 죽음. 그 재호출
패턴이 문서 3·4절의 대표 용례라 실사용에서 즉시 터지는 경로였음 — 경고 문단 추가.
## 스파이크 수정
- 17: self 최상위 키 저장 → 내부 저장소 구조로 재작성(크래시 해소)
- 11: 브랜드 판별 크래시로 "다른 Modifier" 케이스가 엉뚱하게 통과하던 것 수정
- 19: B/C를 폐기 설계(rawNew+owners, 3분기 claimOwner)에서 현행 설계로 재작성,
음성 대조군으로 옛 로직이 Slot{a,a}/Frame{slot,slot}을 통과시킴을 재현
- 07: 위 참고
남은 것은 13/15/16의 스파이크 격리·API 재확인(설계 문제 아님).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>