`.claude/pre-implementation-qa.md`(사용자가 base/ 확정 문서를 문항으로
재심사한 결과)를 실제 문서에 반영하고, 그 문서를 qa-request/로 옮기며
1라운드임을 파일명·제목에 명시(2라운드는 새 파일).
그대로 구현하면 반대로 돌던 것 2건:
- canBound의 판정 방향이 이름과 반대였음 → canBound(v) == not
isBoundAlive(v), 게이트는 전부 `if not canBound(v) then error(...)`.
canExecute와는 값이 같은 게 아니라 서로의 부정이고, 그게 오히려 이름
분리의 명분이 됨(옛 근거 "값이 항상 같다"는 폐기).
- gcconn/gchold 보관이 SetStrong으로 적혀 있었음 → SetWeak. 근거 문장까지
틀렸던 것이라 같이 교체(그대로 짰으면 두-Relate 상호 강참조 누수).
설계가 바뀐 것:
- Dispatch.drive의 None 스킵 분기 폐기 → NoneHandler는 재귀 전담,
NilHandler 신설(k=number and v==nil 말단이 setLength/setOffsetSource
등록). 깨진 전제는 "배열 파트의 None은 process를 안 탄다".
- Length/Offset 등록 책임이 "처음 매치한 Handler" → 말단 Handler.
- 이벤트 disconnect 센티널 false → None/nil.
- Ref 내부 구조를 .Callbacks 분리 + 평범한 .Value 필드로 단순화,
RefLeafHandler에 빠져 있던 type(k)=="number" 추가(leaf는 배열 전용).
- :List reconcile의 nil 리턴은 다시 파괴가 기본, 값 교체와 PopOnly(가칭)만
비파괴.
- base 소유 Fallback Handler 등록 주체를 백엔드 팩토리 → quad-base 자신으로
재역전(백엔드 미로드 시 안내 에러 경로가 안 돌았음).
- "이벤트 콜백 시그니처는 Luau가 검증 못 한다"가 거짓임이 사용자 반례로
확인 → onchange-plan.md의 파생 근거까지 교체(결론은 유지).
이름/표면: DI → D(Declarative) 확정 및 전수 반영, New 커링 + D는 전량
코드 생성, Attribute.Merged/Overridden 둘 다 제공, Quad.debug 신설,
store "key" 문자열 커링 기각(→ store:GetDynamic).
판단이 갈리던 4건(PopOnly 채택 / D-7 재역전 / NoneHandler·NilHandler 역할
분담 / 동적 키 경로)은 사용자에게 물어 확정.
커밋 전 검증: quad-doc-auditor 1패스가 1건, 사용자가 돌린
`/code-review high`가 10건을 더 잡아 전부 반영(ROADMAP이 SL-3 역전을 안
따라오던 것, 설계 갭 2건은 새 열린 질문으로 등록). doc-check.py ERROR 0.
Co-authored-by: qwreey <me@qwreey.moe>
사용자가 "확정으로 적혀 있는 것"을 전부 다시 심사하겠다고 해서,
base/ 25개 문서를 의존성 순서로 훑으며 표면 타입계약/내부 구현
메커니즘/동작 원리 세 층위로 주장을 뽑아 문항으로 확인받았다.
"아니오"가 나온 것만 .claude/pre-implementation-qa.md에 모은다.
정정은 이 커밋에 없다 — 사용자가 "어디가·어떻게·왜 틀렸고 원래 뭐가
맞는지"를 따로 회신하기로 했고, 그 전까지 해당 항목은 미해결 결함으로
두기로 했다. 에이전트가 임의로 고치면 이 QA를 한 이유가 없어진다.
결함/열린항목 20건 + 신규 요구사항 9건 + 부수 오탈자 2건.
특히 두 건은 그대로 구현하면 반대로 돈다:
- S-1 canBound 게이트 호출부 반전 (정상 첫 바인드가 전부 에러,
이중 바인드는 통과). lifecycle-pattern.md가 진원지고
source-state-plan/ref-plan이 전부 이걸 인용한다.
- RE-1 gcconn/gchold를 SetStrong으로 적은 두 곳. 그대로 짜면
같은 문서가 경고하는 두-Relate 상호 강참조 누수에 걸린다.
이 라운드에서 같이 확정된 이름/표면 결정 둘:
- N-8 DI -> D 리네임 (question.md 1순위였던 항목). 네임스페이스는 D로,
"특수 DI 키"라는 설명 표현은 "특수 키"로 단순화. 라이브 문서
19개 파일 전수 목록을 표로 넣어둠 — onchange-plan.md의 헤딩과
lifecycle-hooks-plan.md의 절 인용이 짝으로 묶여 있어 한쪽만
고치면 doc-check.py가 ERROR로 잡는다.
- N-9 New는 커링(New(name)({...})), D는 전량 코드 생성 산출물.
index<UIInstances, ClassName> 방식으로는 MouseButton1Click이
시그널 타입이 돼 콜백 시그니처가 안 나온다는 게 근거 — BS-2가
요구한 것과 같은 문제의 양면이다. 생성 범위는 GUI에 쓰이는
인스턴스 전부, 그 밖은 any로 열고 필요하면 사용자가 직접 캐스트.
인덱스 2층 갱신: .claude/README.md 색인, todos.md에 M0 착수 전
이 문서부터 읽으라는 최우선 항목(기존 0번의 "착수를 막는 결정은
없음"보다 우선한다는 것까지 명시).
doc-check.py ERROR 0 확인.
Co-authored-by: qwreey <me@qwreey.moe>