quad/.claude/question.md
qwreey-agent-selene 2348ea8058
design: PopOnly 가칭을 Detach로 리네임 확정 + 공개 표면 위치 확정
`:List` reconcile의 비파괴 반환 sentinel 이름을 사용자와 후보 검토(Bench/
Stash/Hold/Detach 등) 끝에 Detach로 확정 — 이미 있는 Extract(호출자 직접
호출, 명령형 소유권 이관)와 동사가 겹쳐도 "화면에서만 떼고 관리 주체는
reconcile"이라는 의미가 자연스럽게 구분됨. 공개 표면 위치도 같이 확정 —
Slot이 함수라 Slot.Detach로 못 붙이므로, None sentinel의 선례(공개 표면은
패키지 최상위 export, 정의는 관련 로직 옆)를 그대로 따름.

base/slot-plan.md 전량 반영(가칭 표기 제거, 이름/배치 두 결정 불릿 신설),
question.md/todos.md/ROADMAP.md/archive/question-resolved.md/README.md
인덱스 갱신, session/2026-08-19-02-*.md로 논의 원문 남김. 키 소멸 시 홀드
중이던 요소 처분 문제는 이름과 무관한 별개 항목으로 여전히 미결.

quad-doc-auditor 1라운드(무발견) 거쳐 doc-check.py ERROR 0 확인 후 커밋.

Co-authored-by: qwreey <me@qwreey.moe>
2026-08-19 01:46:18 +09:00

21 KiB

확인/결정 필요 목록

이 문서는 사용자가 답해야 할 것만 담습니다. 이미 결정이 끝난 항목은 2026-08-13 아홉 번째 세션에 archive/question-resolved.md로 옮겼음 — "해결된 게 너무 많아 필요한 부분만 읽기 어렵다"는 사용자 지적에 따른 것. 결정 내용은 안 바뀌었고 읽는 자리만 옮겼으니, 어떤 항목이 왜 그렇게 정해졌는지 되짚고 싶으면 그 문서를 볼 것(지금 유효한 설계 자체는 항상 base/가 소스).

항목을 해소하면: 여기서 지우고 archive/question-resolved.md에 근거와 함께 옮길 것 — 다시 쌓이면 같은 문제가 반복됨.


최우선 — 없음 (2026-08-14 열한 번째 세션 기준)

M0 착수를 막던 항목이 전부 해소됐습니다. 0-Y(:Compute(fn)의 lazy 핸들 계약)는 열세 번째 세션에, 0-Z(Attribute 이름 소유권)와 0-A(재디스패치 하강 diff)는 열네 번째 세션에, 0-W(Ref 이중 배치 방지)는 2026-08-14 열한 번째 세션에 확정·base/ 반영 완료 — 그래서 이 문서엔 이제 "결정 대기" 절 자체가 없음(비어서 헤딩째로 삭제). 해소 전 원문과 결론은 archive/question-resolved.md, 뒤집힌 옛 재디스패치 모델은 archive/dispatch-hintvalue-model-reversed.md.

M0 착수 전 읽을 것: base/typing-limits.md(0-Y가 남긴 구현 규약), base/dispatch-core-plan.md(0-A/0-Z가 반영된 디스패치 코어 — 열네 번째 세션에 bind-system-plan.md에서 분리 신설).

1. 용어 정리 — 아직 안 정해진 것만 (사용자 요청, 진행 중)

사용자 원 메모: "quad는 register라던가 좀 부정확하거나 느낌이 바로 와닿지 않던 용어들이 많음 — 전체적 용어를 보고 생각해볼래? 제안을 줘, 나도 같이 볼게." 이미 확정된 이름(State/Relate/List/canBound/Ref/ PreRef/Peek/isState/None/NoneHandler/Handler)의 근거는 archive/question-resolved.md. (canBound는 2026-08-14 다섯 번째 세션에 폐기돼 canExecute로 통합됐다가 같은 날 열한 번째 세션에 별도 진입점으로 재도입되어 여전히 이 목록에 있음 — 이중 바인딩 가드 전용이고 canExecute (emit 게이팅 전용)와는 판정 로직만 공유. 아래 3번 canExecute 항목과 base/lifecycle-pattern.md의 "canBound vs canExecute" 절, archive/canexecute-inst-arg-reversed.md 하단 addendum 참고.)

  • [해소됨, 2026-08-18] DID(Declarative) 확정 — 원문과 근거는 archive/question-resolved.md. 요지: DI가 "Dependency Injection"과 완전히 겹쳐 실제 오해 전례가 있었고, D는 Instance 전용이 아닌 declare 요소 전반으로 확장 가능하며 D.FrameModifier류 타입 프리픽스도 짧게 유지된다. 미뤄뒀던 유일한 사유(한 글자 식별자의 검색성/자기설명력)는 "문서에서 처음 나올 때 항상 D(Declarative)로 풀어쓴다"는 표기 규약으로 보완하기로 같이 확정. 코퍼스 반영 완료 — base/bind-system-plan.md의 "인스턴스 생성 / 이벤트 네이밍 인체공학" 절.
  • [해소됨, 2026-08-19] PopOnlyDetach 확정 — 원문과 근거는 archive/question-resolved.md. 요지: 이미 있는 Extract(호출자가 직접 부르는 명령형 추출)와 동사가 겹치면 헷갈리는데, Detach는 "화면(부모 계층)에서만 떼어낼 뿐 관리 주체는 여전히 reconcile"이라는 뜻이라 Extract의 "소유권을 통째로 넘긴다"와 자연스럽게 구분되고, nil(파괴)과의 대비도 더 직접적으로 드러남. 공개 표면 위치도 같이 확정 — Slot이 함수(팩토리)라 Slot.Detach처럼 붙이려면 callable-table+메타테이블이 새로 필요해서 과함, None과 같은 선례를 따라 패키지 최상위 export로. 코퍼스 반영 완료 — base/slot-plan.md의 "nil 리턴은 파괴가 기본" 절.
  • Slot(2순위): Vue의 "slot"(콘텐츠 주입 지점)과 이름은 같지만 의미가 다름(quad의 Slot은 자식 배열 재조정 프리미티브) — Vue 배경 있는 사람이 헷갈릴 수 있음.
  • canExecute(3순위, 사소함): 실제로 "이 값이 아직 살아있나" 확인인데 이름이 범용 권한 체크처럼 들림 — isAlive 쪽이 더 직접적이라는 제안이 있었으나, (2026-08-08 재검토) isAlive는 top-level isX 계열 (isState/isRef/isPreRef/isModifier/isObserver류 — 전부 타입 판별자)과 접두어가 겹쳐 "이것도 타입 체크인가" 오해를 유발할 수 있다는 점이 지적됨. canExecute는 타입이 아니라 liveness(생존 여부)를 묻는 질문이라 is보다 can 계열 접두를 유지하는 쪽이 낫다는 방향으로 사용자가 기욺 — 여전히 미확정, 다음에 can으로 시작하는 구체 대안(예: canRun)을 같이 검토할 것. [2026-08-14 다섯 번째 세션] 시그니처는 canExecute(value): boolean 1-인자로 확정(옛 (inst, value) 2-인자는 폐기, base/lifecycle-pattern.md, archive/canexecute-inst-arg-reversed.md). [2026-08-14 열한 번째 세션 정정] 당시엔 canBound가 폐기돼 canExecute 하나가 두 역할을 겸했으나, 이후 canBound가 별도 진입점으로 재도입되며 다시 갈라짐(같은 문서의 "canBound vs canExecute" 절) — 이 이름 정리 항목은 이제 canExecute(emit 게이팅 전용)에만 적용되고, canBound(이중 바인딩 가드 전용)는 별개 이름으로 유지됨. 둘 다 판정 로직은 비공개 헬퍼 하나를 공유.
  • 클로저 인자 이름 hintValue(3순위, 사소함, 2026-08-13 열네 번째 세션 신설): 하강 diff 재디스패치에서 이 인자는 더 이상 "힌트"가 아니라 nil이거나 같은 핸들러가 곧 처리할 새 값임이 계약으로 보장됨(base/dispatch-core-plan.md) — 이름이 옛 모델의 잔재라 nextValue류가 더 정확함. 코퍼스에 이미 널리 쓰인 이름이라 이번엔 안 바꾸고 대기열에만 올림(의사코드는 새로 쓰는 자리부터 nextValue를 쓰기 시작했음).
  • Brand(3순위, 사소함, 2026-08-07 여덟 번째 세션 추가): 런타임 nominal 타입 판별 통합 메커니즘(Brand.set/Brand.get, isState를 branded 타입 전부로 일반화) — brand-plan.mdBrand 절에서 동작/구현 방식은 확정, "OOP 인스턴스의 클래스명을 얻는 느낌"을 전달할 더 나은 이름이 있는지가 열린 질문(사용자가 직접 제기) — Tag는 이미 quad-roblox의 CollectionService 래퍼로 쓰여서 이름 충돌, 후보로 "type namespace"류를 사용자가 검토했으나 미확정. (2026-08-08 재확인) 사용자가 다시 짚었지만 여전히 미정.
  • Tag/Added/Removed/Merged(3순위, 사소함, 2026-08-08 세 번째 세션 array-part 값 객체 재설계 때 확정된 API 표면): base/tag-plan.md가 "열린 질문 없음, 값 모양/메커니즘/retract/패키지 배치 전부 확정, 이름 자체만 용어 정리 대상"이라고 명시해뒀으나 이 목록에 반영이 안 돼 있던 누락 — 이번에 추가. Tag는 Roblox CollectionService가 쓰는 용어와 1:1 대응이라 그 자체로는 무난해 보이지만, 위 Brand 항목(97-99행)에서 "Tag가 이미 이 뜻으로 쓰이고 있어서 충돌"이라는 이유로 Brand의 대안 이름 후보에서 제외됐다는 점은 참고할 것 — 두 이름이 같은 코퍼스 안에서 공존 가능한지도 같이 검토 대상.
  • Attribute/AttributeKey(3순위, 사소함, 2026-08-11 아홉 번째 세션 추가): 여러 Store를 한 번에 attribute로 묶는 그룹 프리미티브 (Attribute(store1, store2, ...), Tag와 동형)가 신설되면서, 기존 단일 키 생성자 Attribute<<T>>("name")를 이름 충돌 방지를 위해 AttributeKey<<T>>로 잠정 리네임함(OnChange/OnChangeKey처럼 함수 이름과 반환 타입 이름이 분리된 기존 전례와 대칭) — 해석 모호성 자체는 이미 없앴으니 급하지 않지만, 최종 이름은 여전히 이 목록의 다른 가칭들과 함께 검토 대상. base/attribute-plan.md "그룹 Attribute(...)" 절 참고.
  • 참고 — 이미 지나간 사례: register(v1) → State(v2) 리네임은 "모호함"은 풀었지만 "다른 뜻으로 이미 쓰이는 단어"라는 새 문제를 만든 셈 — 이번 정리에서 같은 패턴을 조심할 것.
  • Store/Source/Modifier/process/retract/isHandlable은 업계 선례와 잘 맞거나 이미 신중하게 결정된 이름들이라 특별한 문제 없음.

3. 낮은 우선순위 — 열려 있지만 급하지 않음

  • [신설, 2026-08-18 구현 전 QA 3라운드] M2가 M3의 Blocker.luau에 구조적으로 의존하게 됨 — 이대로 각주만 두고 로드맵 순서를 유지할지, Blocker.luau(또는 최소 표면 On/Off/IsOn/OffWithoutEmit)를 M2로 앞당길지, M2/M3 경계 자체를 재검토할지. RC-1의 Blocker 게이팅 해법 때문에 ROADMAP.md M2의 Dispatch.setLength/setOffsetSource 체크박스가 getBlocker/:On()/:IsOn()/:OffWithoutEmit()을 호출하는데, 정작 Blocker.luau 자체는 M3 체크박스에 있다 — 로드맵 순서대로면 M2가 아직 없는 걸 참조하게 된다. 지금은 M2 체크박스에 이 사실만 각주로 남겨둔 임시 조치(가장 보수적인 선택, 마일스톤 재편은 안 함) — M2 착수 전 필요. 상세는 qa-request/pre-implementation-qa-round3.md의 "ROADMAP.md 마일스톤 정합성" 절.
  • Operator 콤비네이터 슈가 네임스페이스 이름+포함 범위(2026-08-12 신설, 같은 날 후속으로 외부 리서치 완료)Sum/Product/Not/비트연산 등 :Compute/:Apply용 슈가 함수 모음의 이름. 흔한 단어라 top-level 노출은 위험, 후보는 Operator/Op/Ops(Combinator는 코퍼스 전반에서 이미 일반명사로 쓰여서 제외) — 서브 에이전트 외부 리서치 결과 Operator가 가장 선례가 강함(Python operator 모듈)이나 최종 확정은 여전히 사용자 몫. 같은 리서치에서 포함 범위도 새로 갈렸음 — 비트/비교 연산자 그룹과 Sub/Div는 리액티브 콤비네이터로서 선례가 전혀 없어 드랍 후보로, Clamp/Min/Max는 선례가 강해 추가 후보로, Debounce/Throttle은 업계에 흔하지만 Blocker와는 다른 시간 기반 메커니즘이라 이 카탈로그가 아니라 quad-roblox 쪽 별도 프리미티브로 다룰지 판단이 필요한 별개 질문으로 분리됨. [2026-08-13 세션 신설] Alternative(nil 대체값, coalesce/??/ 엘비스 연산자류) 후보 추가 — Haskell 비교 리서치 중 나옴, 카탈로그 확정 규칙에 그대로 맞아 포함 근거는 있음. 상세는 research/operator-sugar-plan.md. 구현 자체는 맨 마지막 우선순위(순수 슈가, 없어도 무방) — 여전함.
  • [신설, 2026-08-14] Debounce/Throttle — 남은 열린 질문(개수는 research/debounce-throttle-plan.md 12절이 소스, 여기서 반복 안 함) — 사용자 요청("Blocker와 유사하게 만들어야 한다")으로 research/debounce-throttle-plan.md 신설. 네 번의 리뷰 라운드로 패키지 경계·주입 op 시그니처·Timeout 타입·동작 정식화는 전부 확정됐고, 사용자 취향/방향이 갈리는 것만 남음 — 주요 4개:
    • 의미론 — 창이 열려 있는 동안 :Get()이 최신값을 주는가 (Blocker와 동일) vs 지연된 값을 주는가(권장, VueUse/RxJS와 일치).
    • 제어 핸들state:Apply(Debounce{...})로 끝낼지, Blocker와 글자 그대로 같은 모양(Debouncer 객체 + state:Debounce(d) + d:Flush()/:Cancel())으로 갈지. "검색창에서 Enter 누르면 즉시 커밋"류가 잦다고 보면 후자가 처음부터 나음.
    • 이름 — Roblox 관용 "debounce"(재진입 방지 불리언)와 정면 충돌. 업계 표준 이름을 유지하고 문서로 경고할지, 다른 이름을 쓸지.
    • Time = 0 허용 여부 — "이번 스텝 합치기"로 유용하지만 스케줄러 타이밍에 의존하는 준-Blocker가 됨. 이 외에 사소한 것 둘도 12절에 열려있음: 공개 생성자 2개 개정안이 사용자 확인만 남은 상태, base/blocker-plan.md가 인용하는 "Get()은 라이브 레퍼런스" 문구에 한 줄 명확화가 필요(확정 문서 수정이라 승인 대기, 아직 안 건드림). 우선순위 낮음 — M0를 막지 않고 코어 계약도 안 건드림. 다만 M3에서 Blocker를 구현할 때 게이티드 노드를 공용으로 빼두는 것만은 그 시점에 해야 함(따로 하면 같은 설계를 두 번 함).
  • 중첩 State 평탄화 State<State<T>>State<T>(2026-08-13 여섯 번째 세션 신설, 백로그)[근거 축소, 열네 번째 세션] 원래 이 항목의 주 근거는 "깊은 체인에선 힌트가 nil로 전달돼 깜빡임 방지가 꺼진다"는 실제 기능 손실이었는데, 하강 diff 재디스패치로 각 레벨이 자기 값을 받게 되면서 그 손실 자체가 없어졌음(base/dispatch-core-plan.md). 남은 근거는 편의성과 Slot offset이 밀리고 당겨지는 케이스뿐이라 우선순위가 더 내려감 — state:Flatten()류 콤비네이터 아이디어는 그대로 백로그. 상세는 research/operator-sugar-plan.md 마지막 절.
  • [신설, 2026-08-18 커밋 전 /code-review high] store:GetDynamic을 콜론 메소드로 둘지, 탑레벨 함수로 둘지 — 콜론 메소드로 두면 Store의 lazy __index(없는 키를 인덱싱하면 그 자리에서 Source를 만들어 저장)와 부딪혀서, __index가 고정 메소드 테이블을 먼저 확인해야 하고 그 결과 GetDynamic이 모든 Store의 예약 키 이름이 된다(그 이름의 Source는 dot-access로 못 만듦). Store 키는 사용자 도메인 데이터 이름이라 충돌 확률이 Modifier의 예약 이름들보다 높다. 대안은 탑레벨 getDynamic(store, name) — "특정 프리미티브에 안 묶인 범용 유틸은 소문자 탑레벨"이라는 기존 네이밍 규칙에는 오히려 더 맞는다. M3/M4 착수 전 필요, base/store-plan.md의 "타입 추론 문제" 절.
  • [신설, 2026-08-18 커밋 전 /code-review high] Detach로 홀드 중이던 요소의 키가 데이터에서 사라지면 어떻게 처분하는가 — 지금 의사코드대로면 mounted[key]가 이미 nil이라 파괴 대상이 아니고, 소멸 루프가 userdata[key]까지 지워서 파괴되지도 updateFn에게 되돌려지지도 않고 참조만 끊긴다. 같은 절의 표("키가 사라지면 파괴")와도, "버릴 시점은 updateFn이 정한다"와도 어긋남. 선택지 (a) 소멸 루프가 userdataold까지 확인해 파괴, (b) 지금 동작(참조만 끊고 GC)을 정식화하고 표를 고침, (c) updateFn을 마지막으로 한 번 더 불러 처분을 물음. [정정, 2026-08-18 /code-review high] M6(:List가 있는 마일스톤) 착수 전 필요 — M8(Ref) 아님, base/slot-plan.md의 "nil 리턴은 파괴가 기본" 절.
  • [신설, 2026-08-18 구현 전 QA] 그룹 Attribute의 위치별 claim 설계 — 같은 그룹 객체를 두 위치에 놓는 경우(Frame { a, a })를 잡으려면 위치별 claim 레지스트리가 하나 필요하다는 방향은 확정됐고(Ref처럼 bindLifetime을 재사용할 수는 없음 — 그룹 값은 여러 곳에서 쓸 수 있어야 하므로), 키를 무엇으로 할지((inst, groupValue) → k인지 groupKey 단위인지)와 기존 nameClaims와의 공존 방식이 미정 — base/attribute-plan.md의 "이름 소유권" 절.
  • [신설, 2026-08-18 구현 전 QA] SetAndDispose 류 편의 콤비네이터Get()Set(new) → 옛 값 dispose의 3단계를 매번 손으로 쓰는 게 불편하다는 사용자 지적에서 나옴. source:Apply(SetAndDispose(new)) (단 이때 ApplyState가 아니라 Source를 넘겨야 함)와 source:SetAndDispose(new) 콜론 메서드 중 어느 쪽인지, 그리고 이번 범위인지 백로그인지 미정 — M3 착수 전 방향만이라도 정할 것 (state:Apply 시그니처에 영향), base/slot-plan.mddispose 절.
  • [신설, 2026-08-18 구현 전 QA] 중간 State GC 미검증State → State → State → Observer 체인에서 중간 노드를 강하게 붙잡는 주체가 문서 어디에도 없어 전파가 조용히 끊길 수 있음. 방향(상류 strong / 하류 weak)은 사용자가 지목했고, 명문화 여부 결정 + luau-test 실측이 M3 착수 전에 필요base/source-state-plan.md의 "미해결 — 중간 State가 살아남는가" 절.
  • [신설, 2026-08-14 리뷰] AttributeGroupHandler.process의 부분 실패 롤백 — 이름 순회 도중 소유권 충돌 error가 나면 그 전에 등록된 이름들이 이 사이클엔 회수되지 않음(클로저가 안 만들어짐). 피해는 그 인스턴스 수명으로 한정되고 재현도 시끄럽게 반복돼서 지금은 별도 장치 없이 문서화만 했는데(base/attribute-plan.md "메커니즘" 절), 원자적 롤백(그룹 process에만 국소적인 unwind)을 넣을지는 열어둠. 지금 결정 불필요 — M10 구현 시점에 판단.
  • [해소됨, 2026-08-18] Attribute.Merged의 이름 중복Merged(겹치면 error)와 Overridden(겹치면 뒤가 이김)을 둘 다 제공하는 것으로 확정 (제3안). 근거·파급은 base/attribute-plan.md의 "채택안 — Tag와 동형인 array-part 값 객체" 절.
  • quad-debug 세부 API 이름research/debug-tooling-plan.md 참고. 채널 실현 가능성(BindableEvent/Function이 플러그인↔Play 중 게임 경계를 넘는지)까지 사용자가 Studio에서 직접 실측 검증 완료 — 기술적 불확실성은 다 해소됨, 남은 건 세부 API 이름뿐("이벤트 함수가 self로 instance를 읽는 게 quad 관습"이라는 언급은 2026-08-06 후속 세션에서 해소 — 채택 안 함으로 확정, base/event-plan.md "이벤트 핸들러는 self(Instance)를 받지 않는다" 절 참고). 사용자가 "quad 개발 완료 전엔 착수 못 함"으로 직접 후순위 지정한 건 여전함 — base 설계(M2 Dispatch/ M3 Source/M5 D 생성자) 시점에 훅 확장 지점만 고려해두면 됨.
  • 문서화 전략(UI 네이밍 컨벤션, Store 부작용을 게임 시스템에서 쓰는 패턴)research/documentation-plan.md(뼈대만). 정식 백로그 항목으로 올릴지, 착수 시점을 언제로 볼지 사용자 판단 필요.
  • v1 하위호환(compat) 레이어 — quad-roblox-v1-compatresearch/v1-compat-plan.md(신규, 2026-08-06, 두 차례 후속 논의로 수렴). 방향 확정: v1을 그대로 병행 실행 + 경계에서만 state:Observer()(lazy 포기)로 값을 리졸브해 v1 프로퍼티에 재대입하는 브리지, v2→v1 단방향만 (양방향 불필요로 확정), 패키지명 quad-roblox-v1-compat으로 확정(소스 트리에 세 번째 패키지로 추가될 예정). v2-in-v1/v1-in-v2 두 임베딩 방향 모두 기술적 근거와 안전 규칙까지 정리됐으나(문서 7번), Slot이 foreign Instance를 어떻게 다루는지만 Slot 코어 구현 시점까지 결정 불가로 남음 (위 "여러 Slot이 형제로 섞일 때 순서 보장" 항목과 같은 시점에 확인). 그 외 §8의 세부 항목(v1 자기 루트의 Destroying 자기청소 여부, registerClass 체이닝 기능 브릿징 필요성)은 문서 자체가 "지금 결정 불필요"로 표시해둠 — 위 Slot 항목과 별도로, 실제 compat 레이어 구현 시점에 research/v1-compat-plan.md §8을 다시 열어 확인.
  • Slot이 quad 밖에서 만들어진 임의 Instance를 받을 수 있는가 (2026-08-06 추가, 아직 안 풀림) — v1 compat 등에서 넘어온 foreign Instance를 동적 배열 원소로 받을 수 있는지, retract 시 어떻게 다루는지. Slot 코어 구현(M6) 시점에 확인research/v1-compat-plan.md 7-3.

없어진 번호에 대해: 예전 "0번(추가 프리미티브)"과 "2번(구현 착수 직전 감사 결과)"은 전원 해소되어 통째로 archive/question-resolved.md로 갔음. 우선순위1 11개의 개별 상태가 궁금하면 research/pre-implementation-audit.md가 원본이자 최신.


전체 순서/우선순위는 .claude/todos.md가 최종 소스. 확정된 것들의 문서 색인은 .claude/README.mdbase/ 표(예전에 이 문서 맨 아래에 있던 요약표는 그것과 중복이라 archive로 옮김).