docs: 코퍼스 전체 정합성 감사 반영, agent-mistake.md 신설

여러 세션에 걸쳐 쌓인 stale 참조/자기모순을 서브에이전트 병렬 감사로
찾아내 전부 수정:

- bind-system-plan.md: CreatedRef {phase=...} 옵션이 폐기 이후에도 두
  곳에 방치돼 있던 것을 archive 포인터로 정리
- question.md: Ref 이름 재검토 대상 여부 자기모순 해소, framework-
  comparison-findings.md/v1-compat-plan.md §8 누락 항목 보강
- UICorner 숏핸드 개명(구 Modifier.Rounded(8))을 modifier-plan.md/
  store-semantics.md/ui-shorthand-plan.md/documentation-content-map.md/
  pre-implementation-audit.md 5곳에 전파
- canExecute(handle) 시그니처 정정을 bind-system-plan.md/
  store-semantics.md 예시 호출부에 전파
- architecture.md/ROADMAP.md/CLAUDE.md의 stale 문구·누락 참조 정정
- store-semantics.md 제목을 "State는 Source 위의 캐시 레이어"로 정정
  (Store 아님 — 사용자 확인)

archive/agent-mistake.md 신설 — 설계 반전/기각과 구분되는 세 번째
카테고리로, 에이전트가 문서 작성 중 스스로 낸 개념 혼동을 같은 세션
안에서 정정한 사례(canExecute/isHandlable 혼동, isSource 오판) 전용.
CLAUDE.md 세션 로그의 중복 서술을 옮기고 포인터만 남김.

slot-plan.md의 CRUD 의미론 갭은 사용자 요청으로 이번 라운드에서 보류.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
qwreey 2026-08-07 19:34:40 +09:00
parent d947abf17a
commit 98bd46af09
Signed by: qwreey
GPG key ID: D28DB79297A214BD
13 changed files with 104 additions and 47 deletions

View file

@ -13,7 +13,7 @@
| `reference/` | **[2026-08-07 신설]** 결정 자체가 아니라 다른 문서가 근거로 인용하는 온디맨드 참고 자료(v1 스냅샷, 프레임워크 비교 리서치) — "완료" 개념 없는 건 `base/`와 같지만, 항상 읽을 필요는 없고 해당 문서가 인용될 때만 열어보면 됨. `quadnomicon` 소재 후보가 많음 | | `reference/` | **[2026-08-07 신설]** 결정 자체가 아니라 다른 문서가 근거로 인용하는 온디맨드 참고 자료(v1 스냅샷, 프레임워크 비교 리서치) — "완료" 개념 없는 건 `base/`와 같지만, 항상 읽을 필요는 없고 해당 문서가 인용될 때만 열어보면 됨. `quadnomicon` 소재 후보가 많음 |
| `research/` | 아직 착수 전, 사용자와 스코프/설계를 더 상의해야 함 | | `research/` | 아직 착수 전, 사용자와 스코프/설계를 더 상의해야 함 |
| `qa-request/` | 구현 완료(코드/에이전트 검증까지 끝남) + 사용자 본인의 실기기(Roblox Studio) QA만 남음 — 지금은 구현 자체가 시작 전이라 비어있음 | | `qa-request/` | 구현 완료(코드/에이전트 검증까지 끝남) + 사용자 본인의 실기기(Roblox Studio) QA만 남음 — 지금은 구현 자체가 시작 전이라 비어있음 |
| `archive/` | 완료 + 사용자가 실사용/실기기로 직접 검증까지 마침 (구현 대상). **[2026-08-06 확장]** 완전히 뒤집힌 설계 결정을 원문+역전 이유+diff와 함께 보존하는 용도로도 사용(제목 `[역전됨]` — 한 번 확정했다가 뒤집힌 것) — 더 이상 능동적으로 참고 안 해도 되지만(토큰 낭비 방지 위해 `base/`/`research/`에서 뺌) `quadnomicon` 소재로는 나중에 쓸 수 있음. **[2026-08-07 확장]** 후보였다가 채택 안 된 것(확정한 적 없이 검토 후 기각)도 같은 방식으로 보존, 제목은 구분을 위해 `[기각됨]``[역전됨]`과 의미가 다르므로 혼동하지 말 것 | | `archive/` | 완료 + 사용자가 실사용/실기기로 직접 검증까지 마침 (구현 대상). **[2026-08-06 확장]** 완전히 뒤집힌 설계 결정을 원문+역전 이유+diff와 함께 보존하는 용도로도 사용(제목 `[역전됨]` — 한 번 확정했다가 뒤집힌 것) — 더 이상 능동적으로 참고 안 해도 되지만(토큰 낭비 방지 위해 `base/`/`research/`에서 뺌) `quadnomicon` 소재로는 나중에 쓸 수 있음. **[2026-08-07 확장]** 후보였다가 채택 안 된 것(확정한 적 없이 검토 후 기각)도 같은 방식으로 보존, 제목은 구분을 위해 `[기각됨]``[역전됨]`과 의미가 다르므로 혼동하지 말 것. **[2026-08-07 세 번째 확장]** 설계 반전/기각과 별개로, 에이전트가 문서 작성 중 스스로 낸 개념 혼동을 정정한 이력은 `[에이전트 실수]` 태그로 `agent-mistake.md` 하나에 모음(CLAUDE.md 세션 로그 중복 방지) |
| `feedback/` | 실사용 피드백을 정리한 긴 로그 — 지금은 비어있음(구현 시작 전) | | `feedback/` | 실사용 피드백을 정리한 긴 로그 — 지금은 비어있음(구현 시작 전) |
| `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`) — 읽기 전용 리서치 소스, 여기 내용을 옮기지 말고 항상 원본 그대로 유지 |
@ -70,6 +70,7 @@
| `ui-shorthand-roundsize-dropped.md` | **[기각됨, 2026-08-07 신설]** v1 `RoundSize`(이미지 9-slice 라운드 트릭) — 네이티브 `UICorner`로 대체되어 포팅 불필요. 이 판단이 한 차례 "Corner/PaddingAll/Scale 숏핸드 전체가 불필요하다"로 과잉일반화됐다가 정정된 이력 포함 | | `ui-shorthand-roundsize-dropped.md` | **[기각됨, 2026-08-07 신설]** v1 `RoundSize`(이미지 9-slice 라운드 트릭) — 네이티브 `UICorner`로 대체되어 포팅 불필요. 이 판단이 한 차례 "Corner/PaddingAll/Scale 숏핸드 전체가 불필요하다"로 과잉일반화됐다가 정정된 이력 포함 |
| `batch-rejected.md` | **[기각됨, 2026-08-07 신설]** lexical `Batch(fn)` — 코루틴 yield 위에서 구조적으로 위험해 기각, 값 기반 `Blocker`(`base/blocker-plan.md`)로 대체 | | `batch-rejected.md` | **[기각됨, 2026-08-07 신설]** lexical `Batch(fn)` — 코루틴 yield 위에서 구조적으로 위험해 기각, 값 기반 `Blocker`(`base/blocker-plan.md`)로 대체 |
| `context-rejected.md` | **[기각됨, 2026-08-07 신설]** `Context`(트리 하위 암묵 전파) + 대안이던 레이어드 Store 둘 다 기각 — 명시적 타입 강제 Store 전달로 충분하다는 판단 | | `context-rejected.md` | **[기각됨, 2026-08-07 신설]** `Context`(트리 하위 암묵 전파) + 대안이던 레이어드 Store 둘 다 기각 — 명시적 타입 강제 Store 전달로 충분하다는 판단 |
| `agent-mistake.md` | **[에이전트 실수, 2026-08-07 신설]** 설계 반전이 아니라 에이전트가 문서 작성 중 개념을 혼동했다가 같은 세션 안에서 스스로 정정한 사례 모음(`canExecute`/`isHandlable` 혼동, `isSource` 불필요 오판) — CLAUDE.md 세션 로그의 중복 서술을 여기로 옮기고 포인터만 남김 |
## 참고 ## 참고

View file

@ -0,0 +1,39 @@
# 에이전트 실수 기록
CLAUDE.md 세션 로그 안에 흩어져 있던 "에이전트가 같은 세션 안에서 스스로
정정한 실수" 서술을 여기로 모음 — 최종 결론은 이미 각 `base/` 문서에
정확히 반영돼 있어서 CLAUDE.md에 전체 문단을 남겨둘 필요는 없지만(중복),
같은 실수를 반복하지 않기 위한 기록 자체는 남겨둘 가치가 있음. 다른 archive
문서(`*-reversed.md`/`*-rejected.md`)와 달리 이건 "설계 결정의 반전"이
아니라 "에이전트가 문서를 쓰다가 실제로 개념을 혼동했던 사례" 전용.
## 1. `canExecute``isHandlable`을 같은 개념으로 혼동 (2026-08-07 여덟 번째 세션)
**실수**: `NoneHandler`(값을 `None`에서 `nil`로 바꿔 재디스패치하는 base
내장 핸들러)를 설계하며 그 매치 조건을 `canExecute`로 잘못 서술함.
**정정**: 둘은 완전히 다른 계층 — `isHandlable(k,v)`는 KV 매치
predicate(핸들러가 이 키/값을 담당하는지 판단, 핸들러 계약 4종 중 하나),
`canExecute(handle)`는 특정 바인딩 하나가 "지금 살아있어 실행돼도
되는가"만 보는 별개의 라이프타임 게이트(`base/lifecycle-pattern.md`).
`NoneHandler`가 구현해야 하는 건 `isHandlable`이지 `canExecute`가 아님.
**현재 유효한 설계**: `base/bind-system-plan.md``None` 센티널 절과
"매치 predicate는 `isHandlable`" 절이 최종 소스.
## 2. `isSource`가 불필요하다고 잘못 판단 (다섯 번째 세션 → 여덟 번째 세션에서 정정)
**실수**: 2026-08-07 다섯 번째 세션에서 `isState`/`isSource` predicate를
설계하며 "State면 충분한 용도만 있으니 `isSource`는 따로 안 만들어도
된다"고 서술. 이때 `base/component-composition-plan.md` 4번 절은 이미
`isSource`가 존재한다고 가정하고 쓰여 있었는데, 그 모순을 그때는 못
찾아냄.
**정정**: `Source``State`보다 실제로 더 많은 능력(`:Set`/`:Emit`)을
가진 서브타입이라, "쓰기도 되는 원천인가"를 알아야 하는 코드는
`isState`만으론 부족함 — `isSource`를 별도로 제공해야 함. `isState`
여전히 `{State, Source}` 둘 다 통과시킴(상위집합 판별 유지).
**현재 유효한 설계**: `base/bind-system-plan.md``Brand`
(`isState`/`isSource`가 둘 다 존재, 전자는 집합 멤버십, 후자는 단순 항등)이
최종 소스.

View file

@ -159,8 +159,8 @@ quad/
**남은 것**: Slot 코어 로직의 정확한 API(`research`→`base` 승격된 **남은 것**: Slot 코어 로직의 정확한 API(`research`→`base` 승격된
`slot-plan.md` 참고)와 각 파일의 정확한 함수/타입 이름은 구현 단계에서. `slot-plan.md` 참고)와 각 파일의 정확한 함수/타입 이름은 구현 단계에서.
Tween/purity/existing-instance-bind는 여전히 `research/`에 남아있고 이 Tween/existing-instance-bind는 여전히 `research/`에 남아있고 이 구조 확정을
구조 확정을 막지 않음. 막지 않음(`purity-and-effects-plan.md`는 이미 `base/`로 승격 완료).
## 테스트 전략: quad-base용 최소 mock (2026-08-04) ## 테스트 전략: quad-base용 최소 mock (2026-08-04)

View file

@ -398,9 +398,11 @@ ref 타입처럼 생각하는 게 맞는 거 같음 — 그걸 처리하는 플
같은 문제를 겪으므로 이미 널리 받아들여진 UX, quad가 새로 감수하는 같은 문제를 겪으므로 이미 널리 받아들여진 UX, quad가 새로 감수하는
트레이드오프 아님. 트레이드오프 아님.
- **`CreatedRef`와의 관계**: 둘은 상충하지 않음 — 이 절의 Ref가 범용 - **`CreatedRef`와의 관계**: 둘은 상충하지 않음 — 이 절의 Ref가 범용
프리미티브, `CreatedRef(fn, {phase=...})`는 그 위에 얹힌 "children 프리미티브, `CreatedRef(fn)`는 그 위에 얹힌 "children 배열에 넣으면
배열에 넣으면 dispatch가 자동으로 채워주는" 특수 편의 패턴(quad가 dispatch가 자동으로 채워주는" 특수 편의 패턴(quad가 만든 instance에
만든 instance에 한정된 경우). 한정된 경우). 정확한 타이밍 보장은 옵션 값이 아니라 위치 기반 + `PreRef`
타입으로 표현됨 — 아래 "`phase` 옵션 폐기 → 위치로 표현, `PreRef` 신설"
절이 최신.
- **해소됨 — 반복 재설정 가능(one-shot 아님), 사용자 확정.** React에서도 - **해소됨 — 반복 재설정 가능(one-shot 아님), 사용자 확정.** React에서도
자식이 재생성되는 경우 같은 방식(ref가 다시 채워짐)을 씀 — 예: 마우스 자식이 재생성되는 경우 같은 방식(ref가 다시 채워짐)을 씀 — 예: 마우스
호버/무브 시 `current` 확인 후 라벨 위치를 결정하는 라벨 컨테이너 호버/무브 시 `current` 확인 후 라벨 위치를 결정하는 라벨 컨테이너
@ -1132,7 +1134,7 @@ Modifier처럼 플래튼하지 않는가"는 설계 근거를 알고 싶은 사
- `base/store-semantics.md`에 있던 "`isInit=false`면 허용, `isInit=true`+ - `base/store-semantics.md`에 있던 "`isInit=false`면 허용, `isInit=true`+
생존확인 거짓이면 불허" 분기 초안은 폐기. state-invalidate 리스너 생존확인 거짓이면 불허" 분기 초안은 폐기. state-invalidate 리스너
클로저도 `base/lifecycle-pattern.md`의 "생명 바인드 유틸"(canExecute 클로저도 `base/lifecycle-pattern.md`의 "생명 바인드 유틸"(canExecute
predicate)로 등록하면, 발화 시 `canExecute()` 하나만 확인하고 거짓이면 predicate)로 등록하면, 발화 시 `canExecute(handle)` 하나만 확인하고 거짓이면
그냥 no-op — `isInit` 분기라는 별도 개념 자체가 불필요(사용자 확정: 그냥 no-op — `isInit` 분기라는 별도 개념 자체가 불필요(사용자 확정:
"canExecute 하나로 통일"). "canExecute 하나로 통일").
@ -1467,9 +1469,10 @@ State<any> ... end`처럼 런타임 검증 뒤 명시적 캐스팅을 붙이는
모듈 이름** — 방향은 전부 확정, 이름만 구현 단계에서 남음(`On` 모듈은 모듈 이름** — 방향은 전부 확정, 이름만 구현 단계에서 남음(`On` 모듈은
이벤트 바인딩이 PA님 방식으로 바뀌며 아예 불필요해짐 — 위 "인스턴스 생성 / 이벤트 바인딩이 PA님 방식으로 바뀌며 아예 불필요해짐 — 위 "인스턴스 생성 /
이벤트 네이밍" 절 참고). 이벤트 네이밍" 절 참고).
- **`CreatedRef`(가칭)의 정확한 함수/옵션 이름** — children 배열에 아이템으로 - **`CreatedRef`(가칭)의 정확한 함수 이름** — children 배열에 아이템으로
넣는다는 방향과 생성/마운트 두 시점 모두 지원한다는 것은 확정, 정확한 API 넣는다는 방향, 그리고 타이밍은 옵션 값이 아니라 위치 + `PreRef` 타입으로
이름만 남음. 표현한다는 것까지 확정(위 "`phase` 옵션 폐기 → 위치로 표현, `PreRef`
신설" 절), 정확한 API 이름만 남음.
- **매 `process()` 호출마다 우선순위 스캔 비용** — 실제 구현/벤치마크 단계에서 - **매 `process()` 호출마다 우선순위 스캔 비용** — 실제 구현/벤치마크 단계에서
확인 필요(디자인 자체는 확정됐으므로 더 이상 사용자 확인 대상 아님, 구현 확인 필요(디자인 자체는 확정됐으므로 더 이상 사용자 확인 대상 아님, 구현
검증 대상). 검증 대상).

View file

@ -208,14 +208,14 @@ Modifier에는 없음).
"누가 modifier에 타입을 붙여주냐"는 새 문제가 아니라, Store/인스턴스 생성에 "누가 modifier에 타입을 붙여주냐"는 새 문제가 아니라, Store/인스턴스 생성에
이미 적용한 "정적으로 알려진 건 dot-access, 동적인 건 문자열 폴백" 프로젝트 이미 적용한 "정적으로 알려진 건 dot-access, 동적인 건 문자열 폴백" 프로젝트
전역 관습(`base/bind-system-plan.md` "타입 추론 문제" 절)을 그대로 적용하면 전역 관습(`base/bind-system-plan.md` "타입 추론 문제" 절)을 그대로 적용하면
됨 — `Modifier.Rounded(8)`/`Modifier.FontSize(...)`처럼 DI 쪽 "제네릭 됨 — `mod:UICorner(8)`/`mod:FontSize(...)`처럼 DI 쪽 "제네릭 생성자 함수
생성자 함수 하나 + 자주 쓰는 것만 정적 필드로 미리 바인딩" 패턴 재사용. 하나 + 자주 쓰는 것만 정적 필드로 미리 바인딩" 패턴 재사용.
(주의: 이벤트는 이 관습의 유일한 예외라 인용 대상에서 제외 — 이벤트 바인딩은 (주의: 이벤트는 이 관습의 유일한 예외라 인용 대상에서 제외 — 이벤트 바인딩은
PA님 방식인 문자열 키 + 런타임 리플렉션으로 감, `base/bind-system-plan.md` PA님 방식인 문자열 키 + 런타임 리플렉션으로 감, `base/bind-system-plan.md`
"이벤트 바인딩 정정" 절 참고. Modifier는 이벤트가 아니라 Store/인스턴스 "이벤트 바인딩 정정" 절 참고. Modifier는 이벤트가 아니라 Store/인스턴스
생성과 같은 카테고리라 dot-access 관습이 그대로 적용됨.) 생성과 같은 카테고리라 dot-access 관습이 그대로 적용됨.)
`Modifier.Rounded(8)`가 실제로 어떻게 UICorner 자식을 만들어 붙이는지(v1의 `mod:UICorner(8)`가 실제로 어떻게 UICorner 자식을 만들어 붙이는지(v1의
`Corner` 특수 프로퍼티 선례, 핸들러 배치 소견)는 `Corner` 특수 프로퍼티 선례, 핸들러 배치 소견)는
`base/ui-shorthand-plan.md` 참고 — 이 문서는 Modifier 값 자체의 `base/ui-shorthand-plan.md` 참고 — 이 문서는 Modifier 값 자체의
동작만 다루므로 분리. 동작만 다루므로 분리.

View file

@ -1,4 +1,4 @@
# Store 의미론 — 부작용 허용, State는 Store 위의 조합 가능한 캐시 레이어 # Store 의미론 — 부작용 허용, State는 Source 위의 조합 가능한 캐시 레이어
**상태**: base — 전부 확정. State/Source 온톨로지는 2026-08-04 검증 **상태**: base — 전부 확정. State/Source 온톨로지는 2026-08-04 검증
라운드에서 새로 열려 같은 세션 2~4차 라운드에 걸쳐 확정까지 마침 — 최신 라운드에서 새로 열려 같은 세션 2~4차 라운드에 걸쳐 확정까지 마침 — 최신
@ -31,7 +31,7 @@ purity-and-effects-plan.md`와 연결됨).
어떻게 되는가 — 별도 메커니즘을 새로 만들 필요 없이, `base/ 어떻게 되는가 — 별도 메커니즘을 새로 만들 필요 없이, `base/
lifecycle-pattern.md`의 "생명 바인드 유틸"(canExecute predicate)을 state- lifecycle-pattern.md`의 "생명 바인드 유틸"(canExecute predicate)을 state-
invalidate 리스너 클로저 등록에도 그대로 재사용하면 됨: 발화 시 invalidate 리스너 클로저 등록에도 그대로 재사용하면 됨: 발화 시
`canExecute()` 하나만 확인, 거짓이면 no-op. 한때 검토했던 `isInit=false` `canExecute(handle)` 하나만 확인, 거짓이면 no-op. 한때 검토했던 `isInit=false`
허용/`isInit=true`+생존확인 거짓이면 불허 분기 초안은 폐기 — `canExecute` 허용/`isInit=true`+생존확인 거짓이면 불허 분기 초안은 폐기 — `canExecute`
하나로 통일(사용자 확정). 상세는 `base/bind-system-plan.md` 하나로 통일(사용자 확정). 상세는 `base/bind-system-plan.md`
"Store/State/Source 온톨로지" 절 참고. "Store/State/Source 온톨로지" 절 참고.
@ -73,7 +73,7 @@ pull-recompute)·`:Compute` 인자 규칙·State 쓰기 금지·`Source` 독립
- **독립 존재 가능한 프리미티브** — Source, Ref, Store, Modifier. 다른 - **독립 존재 가능한 프리미티브** — Source, Ref, Store, Modifier. 다른
무언가 없이 그 자체로 `Type(args)` 팩토리 함수로 만들어짐(`Source(default)`/ 무언가 없이 그 자체로 `Type(args)` 팩토리 함수로 만들어짐(`Source(default)`/
`Ref(default)`/`Store({defaults})`/`Modifier.Rounded(8)`, 위 "생성자 `Ref(default)`/`Store({defaults})`/`mod:UICorner(8)`, 위 "생성자
스타일 확정" 참고). 스타일 확정" 참고).
- **원천에 종속된 파생 데이터** — State, Observer. 자기 혼자 존재할 수 - **원천에 종속된 파생 데이터** — State, Observer. 자기 혼자 존재할 수
없고 항상 특정 원천(Source/다른 State)에 의존 — 그래서 이 둘은 자유 없고 항상 특정 원천(Source/다른 State)에 의존 — 그래서 이 둘은 자유

View file

@ -56,7 +56,7 @@ Roblox Instance 이름과 맞춘 `UICorner`/`UIPadding`(+`UIPaddingOffset`)/
"이름 붙은 자식을 찾거나 만들고 프로퍼티 세팅"을 `process(inst, k, v)` "이름 붙은 자식을 찾거나 만들고 프로퍼티 세팅"을 `process(inst, k, v)`
구현 — v1의 하드코딩 if/elseif 대신 정식 핸들러 계약(`isHandlable`/ 구현 — v1의 하드코딩 if/elseif 대신 정식 핸들러 계약(`isHandlable`/
`priority`/`process`/`retract`)을 따르는 것만 다름. `modifier-plan.md` `priority`/`process`/`retract`)을 따르는 것만 다름. `modifier-plan.md`
이미 예시로 든 `Modifier.Rounded(8)`은 이 특수 키를 flatten해서 props에 이미 예시로 든 `mod:UICorner(8)`은 이 특수 키를 flatten해서 props에
꽂아넣는 사탕 문법일 뿐, 실제 처리는 이 Handler가 함 — Modifier를 안 거치고 꽂아넣는 사탕 문법일 뿐, 실제 처리는 이 Handler가 함 — Modifier를 안 거치고
`Frame { UICorner = 8 }`처럼 순수 인라인 키로 직접 써도(v1처럼) 동일하게 `Frame { UICorner = 8 }`처럼 순수 인라인 키로 직접 써도(v1처럼) 동일하게
작동함, `architecture.md``[Attribute "Name"]`류 특수 키와 같은 층위. 작동함, `architecture.md``[Attribute "Name"]`류 특수 키와 같은 층위.

View file

@ -111,8 +111,9 @@ context-rejected.md`. 아래는 그중 **아직 실제로 열려있는 것만**
- **이미 지나간 사례로 참고**: `register`(v1) → `State`(v2) 리네임은 - **이미 지나간 사례로 참고**: `register`(v1) → `State`(v2) 리네임은
"모호함"은 풀었지만 "다른 뜻으로 이미 쓰이는 단어"라는 새 문제를 만든 "모호함"은 풀었지만 "다른 뜻으로 이미 쓰이는 단어"라는 새 문제를 만든
셈 — 이번 정리에서 같은 패턴을 조심할 것. 셈 — 이번 정리에서 같은 패턴을 조심할 것.
- `Store`/`Source`/`Modifier`/`Ref`/`process`/`retract`/`isHandlable`은 - `Store`/`Source`/`Modifier`/`process`/`retract`/`isHandlable`은 업계
업계 선례와 잘 맞거나 이미 신중하게 결정된 이름들이라 특별한 문제 없음. 선례와 잘 맞거나 이미 신중하게 결정된 이름들이라 특별한 문제 없음
(`Ref`는 여기 포함 안 됨 — 위 3순위 목록에 이미 재검토 대상으로 있음).
### 2. 구현 착수 직전 감사 결과 (2026-08-06 신설, M0 착수 전 확인 권장) ### 2. 구현 착수 직전 감사 결과 (2026-08-06 신설, M0 착수 전 확인 권장)
@ -195,6 +196,15 @@ context-rejected.md`. 아래는 그중 **아직 실제로 열려있는 것만**
모두 기술적 근거와 안전 규칙까지 정리됐으나(문서 7번), **Slot이 foreign 모두 기술적 근거와 안전 규칙까지 정리됐으나(문서 7번), **Slot이 foreign
Instance를 어떻게 다루는지만 Slot 코어 구현 시점까지 결정 불가로 남음** Instance를 어떻게 다루는지만 Slot 코어 구현 시점까지 결정 불가로 남음**
(위 "여러 Slot이 형제로 섞일 때 순서 보장" 항목과 같은 시점에 확인). (위 "여러 Slot이 형제로 섞일 때 순서 보장" 항목과 같은 시점에 확인).
그 외 §8의 세부 항목(v1 자기 루트의 `Destroying` 자기청소 여부,
`registerClass` 체이닝 기능 브릿징 필요성)은 문서 자체가 "지금 결정
불필요"로 표시해둠 — 위 Slot 항목과 별도로, 실제 compat 레이어 구현
시점에 `research/v1-compat-plan.md` §8을 다시 열어 확인.
- **`framework-comparison-findings.md`의 두 남은 개선 후보 반영 여부** —
`research/framework-comparison-findings.md` "다음 단계" 절. use-after-destroy
검증 안전망 부재, `:With`의 정적 의존성(동적 With 미지원) 두 가지를 실제
설계에 반영할지, 반영한다면 M0 스파이크 때 같이 검증할지 나중 최적화
패스로 미룰지 — 아직 사용자 판단 전.
## 참고: 지금까지 확정된 것 (요약) ## 참고: 지금까지 확정된 것 (요약)

View file

@ -86,7 +86,7 @@ v1 폐기 API/버그/구조 결함 전부 v2 설계를 정당화하는 내부
### modifier-plan.md / slot-plan.md ### modifier-plan.md / slot-plan.md
- 초심자: Modifier 기본 체이닝+merge 우선순위 규칙 실제 예시 / Slot 기본 개념(children 배열)+클래스가 슬롯 받는 방법(Named Slot 없음) / 마운트된 slot 재마운트 시 즉시 throw - 초심자: Modifier 기본 체이닝+merge 우선순위 규칙 실제 예시 / Slot 기본 개념(children 배열)+클래스가 슬롯 받는 방법(Named Slot 없음) / 마운트된 slot 재마운트 시 즉시 throw
- api: Setter가 리터럴/변환 함수 둘 다 받음(→심화: getter 없는 이유) / 필드가 State일 수 있는 4가지 조합 표(→심화: 반응성 유지/끊김 이유) / `Modifier.Rounded(8)` dot-access 생성자 관습 / Slot은 인스턴스당 여럿 가능 / 중첩 인스턴스 자식 처리 / retract 시 slot 내용 폐기(→심화: portal 없는 이유) / `:Apply(factory)` 기본 체이닝 관용구(→심화: 언제 `Apply` vs `Override`인지 성능 기준) / `:Peek<<T>>(key)` + `isState`(→심화: `Get`과 이름을 다르게 한 이유) - api: Setter가 리터럴/변환 함수 둘 다 받음(→심화: getter 없는 이유) / 필드가 State일 수 있는 4가지 조합 표(→심화: 반응성 유지/끊김 이유) / `mod:UICorner(8)` dot-access 생성자 관습 / Slot은 인스턴스당 여럿 가능 / 중첩 인스턴스 자식 처리 / retract 시 slot 내용 폐기(→심화: portal 없는 이유) / `:Apply(factory)` 기본 체이닝 관용구(→심화: 언제 `Apply` vs `Override`인지 성능 기준) / `:Peek<<T>>(key)` + `isState`(→심화: `Get`과 이름을 다르게 한 이유)
- 심화: 정적 merge vs 런타임 pluggable 기각 이유(CSS cascade) / immutable+clone 체이닝 이유(형제 오염 방지) / getter 미채택 이유 / `__index` 런타임 구현 통찰 / Modifier가 핸들러 계층을 모르는 이유 / base/roblox 패키지 경계(Dispatch/Slot vs Handlers/Slot) / Slot 단일 마운트 소유권이 v1/Fusion/Vide 대비 개선인 이유 / retract=폐기 확정 히스토리(portal 검토 후 기각) / **왜 `Apply`가 기본이고 `Override`는 최적화 특수 케이스인가**(계산 의존성 있는 조합 vs 독립적 재사용 가능 조각의 병합 — 2026-08-07 다섯 번째 세션, `modifier-plan.md` 9번) / 왜 `Apply`가 clone 대신 mutate하지 않는가(형제 오염 방지가 개별 clone 비용 절감보다 우선) - 심화: 정적 merge vs 런타임 pluggable 기각 이유(CSS cascade) / immutable+clone 체이닝 이유(형제 오염 방지) / getter 미채택 이유 / `__index` 런타임 구현 통찰 / Modifier가 핸들러 계층을 모르는 이유 / base/roblox 패키지 경계(Dispatch/Slot vs Handlers/Slot) / Slot 단일 마운트 소유권이 v1/Fusion/Vide 대비 개선인 이유 / retract=폐기 확정 히스토리(portal 검토 후 기각) / **왜 `Apply`가 기본이고 `Override`는 최적화 특수 케이스인가**(계산 의존성 있는 조합 vs 독립적 재사용 가능 조각의 병합 — 2026-08-07 다섯 번째 세션, `modifier-plan.md` 9번) / 왜 `Apply`가 clone 대신 mutate하지 않는가(형제 오염 방지가 개별 clone 비용 절감보다 우선)
- 열린 질문(문서화 보류): 여러 Slot이 형제로 섞일 때 순서 보장 — 미확정 - 열린 질문(문서화 보류): 여러 Slot이 형제로 섞일 때 순서 보장 — 미확정
- skip: 세션 날짜/확정 이력, 문서 승격/정정 안내 - skip: 세션 날짜/확정 이력, 문서 승격/정정 안내

View file

@ -210,7 +210,12 @@ element별 weak-set) — throw 조건을 "Slot 핸들러의 `process`가 같은
### 1-9. `LifetimeHandle` 인터페이스가 M8에 배치돼 있지만 M4/M6이 이미 그걸 필요로 함(로드맵 순서 역전) ### 1-9. `LifetimeHandle` 인터페이스가 M8에 배치돼 있지만 M4/M6이 이미 그걸 필요로 함(로드맵 순서 역전)
**위치**: `ROADMAP.md` M8 "Ref" — `"LifetimeHandle 인터페이스 + quad-roblox **[2026-08-07 세 번째 세션 갱신 — 반영 완료.]** 아래 제안대로
`LifetimeHandle.luau`/`PerInstanceState.luau` 인터페이스가 `ROADMAP.md`
M2로 이동됐고, M8은 quad-roblox 실제 구현만 담당하도록 분리됨 — 더 이상
열린 항목 아님, 아래는 원래 발견 당시 기록.
**위치(당시)**: `ROADMAP.md` M8 "Ref" — `"LifetimeHandle 인터페이스 + quad-roblox
실제 구현(Instance 생존 확인)"`. 실제 구현(Instance 생존 확인)"`.
**문제**: `base/lifecycle-pattern.md`("생명 바인드 유틸"을 State-invalidate **문제**: `base/lifecycle-pattern.md`("생명 바인드 유틸"을 State-invalidate
@ -403,7 +408,7 @@ Modifier를 합친다"는 시나리오가 `Override`의 가장 그럴듯한 실
관습 재사용" 절, `.claude/question.md` 1번. 관습 재사용" 절, `.claude/question.md` 1번.
**문제**: Modifier의 런타임 체이닝 엔진은 quad-base 소유가 맞지만, 클래스별 **문제**: Modifier의 런타임 체이닝 엔진은 quad-base 소유가 맞지만, 클래스별
정적 타입 안전성(`Modifier.Rounded(8)`가 `FrameModifier` 타입으로 추론되는 정적 타입 안전성(`mod:UICorner(8)`가 `FrameModifier` 타입으로 추론되는
것)은 "DI 쪽 '제네릭 생성자 함수 하나 + 자주 쓰는 것만 정적 필드' 패턴 것)은 "DI 쪽 '제네릭 생성자 함수 하나 + 자주 쓰는 것만 정적 필드' 패턴
재사용"이라 문서 스스로 밝히듯 quad-roblox의 DI 타입 생성 계층(M5)에 강하게 재사용"이라 문서 스스로 밝히듯 quad-roblox의 DI 타입 생성 계층(M5)에 강하게
결합돼 있다. 그런데 `ROADMAP.md` M7 체크리스트(flatten-before-dispatch, 결합돼 있다. 그런데 `ROADMAP.md` M7 체크리스트(flatten-before-dispatch,
@ -561,12 +566,13 @@ Handler"라고만 서술해, 사실상 3개의 거의 동일한 형태(리터럴
## 다음 액션 제안 ## 다음 액션 제안
- **M0 착수 전**: 1-5(props.Modifier/Ref nil-hole)를 M0 스파이크 코드에 - **M0 착수 전**: 1-5(props.Modifier/Ref nil-hole)는 `ROADMAP.md` M0에
반영, 1-10(`store.key` 레코드 필드 타이핑)을 M0로 앞당기는 것 검토, 반영 완료. 1-10(`store.key` 레코드 필드 타이핑)을 M0로 앞당기는 것 검토,
1-11(Modifier `__index` 트릭)도 비용이 낮으니 M0 후보로 포함 검토. 1-11(Modifier `__index` 트릭)도 비용이 낮으니 M0 후보로 포함 검토는 계속
열려있음.
- **M2(Dispatch) 착수 전**: 1-2, 1-3, 1-4를 한 번에 확정(전부 base - **M2(Dispatch) 착수 전**: 1-2, 1-3, 1-4를 한 번에 확정(전부 base
dispatch 엔진의 에러/상태관리 규칙이라 같이 결정하는 게 효율적). dispatch 엔진의 에러/상태관리 규칙이라 같이 결정하는 게 효율적).
- **M2/M3 착수 전**: 1-6(canExecute 실제 구현) 실측, 1-9(LifetimeHandle - **M2/M3 착수 전**: 1-6(canExecute 실제 구현) 실측(1-9는 반영 완료, 위
마일스톤 재배치). 항목 참고).
- **나머지**: 해당 마일스톤 착수 시점에 이 문서를 다시 열어 관련 항목만 - **나머지**: 해당 마일스톤 착수 시점에 이 문서를 다시 열어 관련 항목만
확인하면 됨 — 지금 전부 결정할 필요는 없음. 확인하면 됨 — 지금 전부 결정할 필요는 없음.

View file

@ -1,4 +1,4 @@
# Tween / 애니메이션 플러깅 (기본값 확정, 옵션 키 이름만 남음) # Tween / 애니메이션 플러깅 (기본값 확정, 옵션 키 이름·값 모양만 남음)
**상태**: research — 방향은 뚜렷하게 잡혀 있고(라이브러리가 트윈을 직접 **상태**: research — 방향은 뚜렷하게 잡혀 있고(라이브러리가 트윈을 직접
구현하지 않는다), `retract` 순서/오버라이드 기본값(Cancel)도 확정됨. 남은 건 구현하지 않는다), `retract` 순서/오버라이드 기본값(Cancel)도 확정됨. 남은 건

View file

@ -45,10 +45,11 @@ modifier/Ref의 컴포넌트 경계 통과 방식) 논의도 2026-08-04 세션
`tween-plan.md`/`existing-instance-bind-plan.md`/`debug-tooling-plan.md`/ `tween-plan.md`/`existing-instance-bind-plan.md`/`debug-tooling-plan.md`/
`documentation-plan.md`/`documentation-content-map.md`/ `documentation-plan.md`/`documentation-content-map.md`/
`framework-comparison-findings.md`/`additional-primitives-plan.md`(키 기반 `framework-comparison-findings.md`/`additional-primitives-plan.md`(키 기반
동적 컬렉션 재조정만 남음) — 전부 후순위(급한 건 `tween-plan.md` 동적 컬렉션 재조정만 남음)/`pre-implementation-audit.md`/`v1-compat-plan.md`
세부 옵션 정도). 최신 목록·우선순위는 `.claude/README.md`가 소스, 여기서 — 전부 후순위(급한 건 `tween-plan.md` 세부 옵션 정도). 최신 목록·우선순위는
개수 반복 안 함(과거에 "두 개뿐"이라 적어놨다가 새 문서 추가될 때마다 `.claude/README.md`가 소스, 여기서 개수 반복 안 함(과거에 "두 개뿐"이라
안 갱신되는 패턴이 반복돼서 아예 안 세기로 함). 적어놨다가 새 문서 추가될 때마다 안 갱신되는 패턴이 반복돼서 아예 안
세기로 함).
- `.claude/qa-request/`, `.claude/feedback/` — 구현 시작되면 쓰기 시작함, - `.claude/qa-request/`, `.claude/feedback/` — 구현 시작되면 쓰기 시작함,
지금은 비어있음. `.claude/archive/`는 원래 같은 취급이었으나 지금은 비어있음. `.claude/archive/`는 원래 같은 취급이었으나
2026-08-06 세 번째 세션부터 **완전히 뒤집힌 설계 결정을 원문+역전 2026-08-06 세 번째 세션부터 **완전히 뒤집힌 설계 결정을 원문+역전
@ -1054,12 +1055,9 @@ Tag/Attribute 전용 문서 신설.** None 논의를 파고들다 디스패치
`base/bind-system-plan.md`/`base/lifecycle-pattern.md`/`base/tag-plan.md` `base/bind-system-plan.md`/`base/lifecycle-pattern.md`/`base/tag-plan.md`
(신규)/`base/attribute-plan.md`(신규)/`ROADMAP.md`에 반영 완료: (신규)/`base/attribute-plan.md`(신규)/`ROADMAP.md`에 반영 완료:
- **제 실수 정정 — `canExecute``isHandlable`은 다른 개념.** - **제 실수 정정 — `canExecute`와 `isHandlable`은 다른 개념** (전체 경위는
`isHandlable(k,v)`는 KV 매치 predicate(핸들러 계약 4종 중 하나), `archive/agent-mistake.md` 1번으로 옮김) — 결론만: `NoneHandler`
`canExecute`는 특정 바인딩 하나가 "지금 살아있어 실행돼도 되는가"만 구현해야 하는 건 `isHandlable`이지 `canExecute`가 아님.
보는 별개의 라이프타임 게이트(`lifecycle-pattern.md`) — `NoneHandler`
구현해야 하는 건 `isHandlable`이지 `canExecute`가 아님, 앞서 잘못 쓴
문장을 고침.
- **`Dispatch.getHandler`/`Dispatch.process`/`Dispatch.addHandler`/ - **`Dispatch.getHandler`/`Dispatch.process`/`Dispatch.addHandler`/
`Dispatch.drive`로 이름 공식화.** 원래 "확정된 디스패치 모델" 절은 `Dispatch.drive`로 이름 공식화.** 원래 "확정된 디스패치 모델" 절은
"스캔+실행"과 "매치된 핸들러 자신의 처리"를 둘 다 그냥 `process`라고 "스캔+실행"과 "매치된 핸들러 자신의 처리"를 둘 다 그냥 `process`라고
@ -1136,12 +1134,8 @@ Tag/Attribute 전용 문서 신설.** None 논의를 파고들다 디스패치
introspection 창구(quad-debug 용도) 역할까지 겸하도록 `None`을 특수 introspection 창구(quad-debug 용도) 역할까지 겸하도록 `None`을 특수
분기로 앞단에서 걸러줌 — `isNone`이 그 분기의 실제 구현체. 분기로 앞단에서 걸러줌 — `isNone`이 그 분기의 실제 구현체.
- **정정 — `isSource`는 별도로 필요함, 다섯 번째 세션의 "불필요" 서술을 - **정정 — `isSource`는 별도로 필요함, 다섯 번째 세션의 "불필요" 서술을
뒤집음.** 그땐 "State면 충분한 용도"만 봤지만 `Source`는 State보다 뒤집음** (전체 경위는 `archive/agent-mistake.md` 2번으로 옮김) — 결론만:
진짜 더 많은 능력(`:Set`/`:Emit`)을 가진 서브타입이라 "쓰기도 되는 `isSource`를 별도 제공, `isState`는 여전히 `{State,Source}` 둘 다 통과.
원천인가"를 알아야 하는 코드엔 `isState`만으론 부족 — `isSource` 별도
제공, `isState`는 여전히 `{State,Source}` 둘 다 통과. `component-
composition-plan.md` 4번 절이 애초에 `isSource`가 존재한다고 가정해둔
것과도 이걸로 정합됨(그동안 두 문서가 서로 모순돼 있었음, 이번에 발견).
- **Luau 타입 narrowing은 자동으로 안 됨 — 사용자가 직접 확인, 명시적 - **Luau 타입 narrowing은 자동으로 안 됨 — 사용자가 직접 확인, 명시적
`::` 캐스팅 필요.** `isX(v)`가 참이어도 Luau가 TypeScript의 `x is T` `::` 캐스팅 필요.** `isX(v)`가 참이어도 Luau가 TypeScript의 `x is T`
같은 사용자 정의 타입 가드를 지원 안 해서 `v`의 정적 타입을 자동으로 같은 사용자 정의 타입 가드를 지원 안 해서 `v`의 정적 타입을 자동으로

View file

@ -4,8 +4,9 @@ quad-v2 구현 단계 실행 계획. 설계 근거/아키텍처 자체는 여기
`.claude/base/`가 소스, 여긴 **순서와 진행 상황**만. 마일스톤 시작할 때 `.claude/base/`가 소스, 여긴 **순서와 진행 상황**만. 마일스톤 시작할 때
체크박스를 세분화해서 늘려도 되고, 끝나면 체크만 하면 됨 — 살아있는 문서. 체크박스를 세분화해서 늘려도 되고, 끝나면 체크만 하면 됨 — 살아있는 문서.
**2026-08-04 세션에 준비만 해둔 상태 — 아직 M0도 시작 안 함.** 다음 세션은 **2026-08-04 세션에 준비만 해둔 상태로 신설, 이후 여러 세션에 걸쳐 설계가
바로 M0부터. 확정될 때마다 각 마일스톤 체크박스가 계속 갱신돼왔음 — 그래도 아직 M0
자체는 시작 안 함.** 다음 세션은 바로 M0부터.
## M0 — 스켈레톤 + 기술검증 (스파이크, "진짜" 마일스톤 아님) ## M0 — 스켈레톤 + 기술검증 (스파이크, "진짜" 마일스톤 아님)
@ -32,7 +33,10 @@ Luau 코드로 부딪혀본 적 없는 세 가지**를 던지는 코드로 검
폐기 → 위치로 표현, `PreRef` 신설" 절) 폐기 → 위치로 표현, `PreRef` 신설" 절)
- [ ] `props.Modifier`/`props.Ref` named-parameter로 받는 컴포넌트 하나 작성, - [ ] `props.Modifier`/`props.Ref` named-parameter로 받는 컴포넌트 하나 작성,
`export type Params = {...}`로 타입 체크되는지 확인 `export type Params = {...}`로 타입 체크되는지 확인
(`component-composition-plan.md` 최종 결론 1번) (`component-composition-plan.md` 최종 결론 1번) — **caller가 Modifier/Ref를
안 넘기는 케이스(Lua 배열 리터럴의 nil-hole 함정, `{nil, ref, child}`처럼
뒤 항목이 무시될 수 있는 경우)를 반드시 케이스에 포함**
(`research/pre-implementation-audit.md` 1-5)
- [ ] 위 과정에서 소스 트리/메커니즘 문서에 고칠 부분이 생기면 그 자리에서 - [ ] 위 과정에서 소스 트리/메커니즘 문서에 고칠 부분이 생기면 그 자리에서
`.claude/base/` 갱신 `.claude/base/` 갱신