From 98bd46af0932d1980681163090444e108387387b Mon Sep 17 00:00:00 2001 From: qwreey Date: Fri, 7 Aug 2026 19:34:40 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20=EC=BD=94=ED=8D=BC=EC=8A=A4=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=EC=A0=95=ED=95=A9=EC=84=B1=20=EA=B0=90=EC=82=AC=20?= =?UTF-8?q?=EB=B0=98=EC=98=81,=20agent-mistake.md=20=EC=8B=A0=EC=84=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 여러 세션에 걸쳐 쌓인 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 --- .claude/README.md | 3 +- .claude/archive/agent-mistake.md | 39 +++++++++++++++++++ .claude/base/architecture.md | 4 +- .claude/base/bind-system-plan.md | 17 ++++---- .claude/base/modifier-plan.md | 6 +-- .claude/base/store-semantics.md | 6 +-- .claude/base/ui-shorthand-plan.md | 2 +- .claude/question.md | 14 ++++++- .claude/research/documentation-content-map.md | 2 +- .claude/research/pre-implementation-audit.md | 20 ++++++---- .claude/research/tween-plan.md | 2 +- CLAUDE.md | 26 +++++-------- ROADMAP.md | 10 +++-- 13 files changed, 104 insertions(+), 47 deletions(-) create mode 100644 .claude/archive/agent-mistake.md diff --git a/.claude/README.md b/.claude/README.md index a4cb785..30dd6d5 100644 --- a/.claude/README.md +++ b/.claude/README.md @@ -13,7 +13,7 @@ | `reference/` | **[2026-08-07 신설]** 결정 자체가 아니라 다른 문서가 근거로 인용하는 온디맨드 참고 자료(v1 스냅샷, 프레임워크 비교 리서치) — "완료" 개념 없는 건 `base/`와 같지만, 항상 읽을 필요는 없고 해당 문서가 인용될 때만 열어보면 됨. `quadnomicon` 소재 후보가 많음 | | `research/` | 아직 착수 전, 사용자와 스코프/설계를 더 상의해야 함 | | `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/` | 실사용 피드백을 정리한 긴 로그 — 지금은 비어있음(구현 시작 전) | | `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 숏핸드 전체가 불필요하다"로 과잉일반화됐다가 정정된 이력 포함 | | `batch-rejected.md` | **[기각됨, 2026-08-07 신설]** lexical `Batch(fn)` — 코루틴 yield 위에서 구조적으로 위험해 기각, 값 기반 `Blocker`(`base/blocker-plan.md`)로 대체 | | `context-rejected.md` | **[기각됨, 2026-08-07 신설]** `Context`(트리 하위 암묵 전파) + 대안이던 레이어드 Store 둘 다 기각 — 명시적 타입 강제 Store 전달로 충분하다는 판단 | +| `agent-mistake.md` | **[에이전트 실수, 2026-08-07 신설]** 설계 반전이 아니라 에이전트가 문서 작성 중 개념을 혼동했다가 같은 세션 안에서 스스로 정정한 사례 모음(`canExecute`/`isHandlable` 혼동, `isSource` 불필요 오판) — CLAUDE.md 세션 로그의 중복 서술을 여기로 옮기고 포인터만 남김 | ## 참고 diff --git a/.claude/archive/agent-mistake.md b/.claude/archive/agent-mistake.md new file mode 100644 index 0000000..735299d --- /dev/null +++ b/.claude/archive/agent-mistake.md @@ -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`가 둘 다 존재, 전자는 집합 멤버십, 후자는 단순 항등)이 +최종 소스. diff --git a/.claude/base/architecture.md b/.claude/base/architecture.md index b7d2471..fdfbad8 100644 --- a/.claude/base/architecture.md +++ b/.claude/base/architecture.md @@ -159,8 +159,8 @@ quad/ **남은 것**: Slot 코어 로직의 정확한 API(`research`→`base` 승격된 `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) diff --git a/.claude/base/bind-system-plan.md b/.claude/base/bind-system-plan.md index a3594b4..5808152 100644 --- a/.claude/base/bind-system-plan.md +++ b/.claude/base/bind-system-plan.md @@ -398,9 +398,11 @@ ref 타입처럼 생각하는 게 맞는 거 같음 — 그걸 처리하는 플 같은 문제를 겪으므로 이미 널리 받아들여진 UX, quad가 새로 감수하는 트레이드오프 아님. - **`CreatedRef`와의 관계**: 둘은 상충하지 않음 — 이 절의 Ref가 범용 - 프리미티브, `CreatedRef(fn, {phase=...})`는 그 위에 얹힌 "children - 배열에 넣으면 dispatch가 자동으로 채워주는" 특수 편의 패턴(quad가 - 만든 instance에 한정된 경우). + 프리미티브, `CreatedRef(fn)`는 그 위에 얹힌 "children 배열에 넣으면 + dispatch가 자동으로 채워주는" 특수 편의 패턴(quad가 만든 instance에 + 한정된 경우). 정확한 타이밍 보장은 옵션 값이 아니라 위치 기반 + `PreRef` + 타입으로 표현됨 — 아래 "`phase` 옵션 폐기 → 위치로 표현, `PreRef` 신설" + 절이 최신. - **해소됨 — 반복 재설정 가능(one-shot 아님), 사용자 확정.** React에서도 자식이 재생성되는 경우 같은 방식(ref가 다시 채워짐)을 씀 — 예: 마우스 호버/무브 시 `current` 확인 후 라벨 위치를 결정하는 라벨 컨테이너 @@ -1132,7 +1134,7 @@ Modifier처럼 플래튼하지 않는가"는 설계 근거를 알고 싶은 사 - `base/store-semantics.md`에 있던 "`isInit=false`면 허용, `isInit=true`+ 생존확인 거짓이면 불허" 분기 초안은 폐기. state-invalidate 리스너 클로저도 `base/lifecycle-pattern.md`의 "생명 바인드 유틸"(canExecute - predicate)로 등록하면, 발화 시 `canExecute()` 하나만 확인하고 거짓이면 + predicate)로 등록하면, 발화 시 `canExecute(handle)` 하나만 확인하고 거짓이면 그냥 no-op — `isInit` 분기라는 별도 개념 자체가 불필요(사용자 확정: "canExecute 하나로 통일"). @@ -1467,9 +1469,10 @@ State ... end`처럼 런타임 검증 뒤 명시적 캐스팅을 붙이는 모듈 이름** — 방향은 전부 확정, 이름만 구현 단계에서 남음(`On` 모듈은 이벤트 바인딩이 PA님 방식으로 바뀌며 아예 불필요해짐 — 위 "인스턴스 생성 / 이벤트 네이밍" 절 참고). -- **`CreatedRef`(가칭)의 정확한 함수/옵션 이름** — children 배열에 아이템으로 - 넣는다는 방향과 생성/마운트 두 시점 모두 지원한다는 것은 확정, 정확한 API - 이름만 남음. +- **`CreatedRef`(가칭)의 정확한 함수 이름** — children 배열에 아이템으로 + 넣는다는 방향, 그리고 타이밍은 옵션 값이 아니라 위치 + `PreRef` 타입으로 + 표현한다는 것까지 확정(위 "`phase` 옵션 폐기 → 위치로 표현, `PreRef` + 신설" 절), 정확한 API 이름만 남음. - **매 `process()` 호출마다 우선순위 스캔 비용** — 실제 구현/벤치마크 단계에서 확인 필요(디자인 자체는 확정됐으므로 더 이상 사용자 확인 대상 아님, 구현 검증 대상). diff --git a/.claude/base/modifier-plan.md b/.claude/base/modifier-plan.md index 03ddfba..1a2a7df 100644 --- a/.claude/base/modifier-plan.md +++ b/.claude/base/modifier-plan.md @@ -208,14 +208,14 @@ Modifier에는 없음). "누가 modifier에 타입을 붙여주냐"는 새 문제가 아니라, Store/인스턴스 생성에 이미 적용한 "정적으로 알려진 건 dot-access, 동적인 건 문자열 폴백" 프로젝트 전역 관습(`base/bind-system-plan.md` "타입 추론 문제" 절)을 그대로 적용하면 -됨 — `Modifier.Rounded(8)`/`Modifier.FontSize(...)`처럼 DI 쪽 "제네릭 -생성자 함수 하나 + 자주 쓰는 것만 정적 필드로 미리 바인딩" 패턴 재사용. +됨 — `mod:UICorner(8)`/`mod:FontSize(...)`처럼 DI 쪽 "제네릭 생성자 함수 +하나 + 자주 쓰는 것만 정적 필드로 미리 바인딩" 패턴 재사용. (주의: 이벤트는 이 관습의 유일한 예외라 인용 대상에서 제외 — 이벤트 바인딩은 PA님 방식인 문자열 키 + 런타임 리플렉션으로 감, `base/bind-system-plan.md` "이벤트 바인딩 정정" 절 참고. Modifier는 이벤트가 아니라 Store/인스턴스 생성과 같은 카테고리라 dot-access 관습이 그대로 적용됨.) -`Modifier.Rounded(8)`가 실제로 어떻게 UICorner 자식을 만들어 붙이는지(v1의 +`mod:UICorner(8)`가 실제로 어떻게 UICorner 자식을 만들어 붙이는지(v1의 `Corner` 특수 프로퍼티 선례, 핸들러 배치 소견)는 `base/ui-shorthand-plan.md` 참고 — 이 문서는 Modifier 값 자체의 동작만 다루므로 분리. diff --git a/.claude/base/store-semantics.md b/.claude/base/store-semantics.md index 392ef06..9d41fa0 100644 --- a/.claude/base/store-semantics.md +++ b/.claude/base/store-semantics.md @@ -1,4 +1,4 @@ -# Store 의미론 — 부작용 허용, State는 Store 위의 조합 가능한 캐시 레이어 +# Store 의미론 — 부작용 허용, State는 Source 위의 조합 가능한 캐시 레이어 **상태**: base — 전부 확정. State/Source 온톨로지는 2026-08-04 검증 라운드에서 새로 열려 같은 세션 2~4차 라운드에 걸쳐 확정까지 마침 — 최신 @@ -31,7 +31,7 @@ purity-and-effects-plan.md`와 연결됨). 어떻게 되는가 — 별도 메커니즘을 새로 만들 필요 없이, `base/ lifecycle-pattern.md`의 "생명 바인드 유틸"(canExecute predicate)을 state- invalidate 리스너 클로저 등록에도 그대로 재사용하면 됨: 발화 시 -`canExecute()` 하나만 확인, 거짓이면 no-op. 한때 검토했던 `isInit=false`면 +`canExecute(handle)` 하나만 확인, 거짓이면 no-op. 한때 검토했던 `isInit=false`면 허용/`isInit=true`+생존확인 거짓이면 불허 분기 초안은 폐기 — `canExecute` 하나로 통일(사용자 확정). 상세는 `base/bind-system-plan.md`의 "Store/State/Source 온톨로지" 절 참고. @@ -73,7 +73,7 @@ pull-recompute)·`:Compute` 인자 규칙·State 쓰기 금지·`Source` 독립 - **독립 존재 가능한 프리미티브** — Source, Ref, Store, Modifier. 다른 무언가 없이 그 자체로 `Type(args)` 팩토리 함수로 만들어짐(`Source(default)`/ - `Ref(default)`/`Store({defaults})`/`Modifier.Rounded(8)`, 위 "생성자 + `Ref(default)`/`Store({defaults})`/`mod:UICorner(8)`, 위 "생성자 스타일 확정" 참고). - **원천에 종속된 파생 데이터** — State, Observer. 자기 혼자 존재할 수 없고 항상 특정 원천(Source/다른 State)에 의존 — 그래서 이 둘은 자유 diff --git a/.claude/base/ui-shorthand-plan.md b/.claude/base/ui-shorthand-plan.md index b394394..cc71210 100644 --- a/.claude/base/ui-shorthand-plan.md +++ b/.claude/base/ui-shorthand-plan.md @@ -56,7 +56,7 @@ Roblox Instance 이름과 맞춘 `UICorner`/`UIPadding`(+`UIPaddingOffset`)/ "이름 붙은 자식을 찾거나 만들고 프로퍼티 세팅"을 `process(inst, k, v)`에 구현 — v1의 하드코딩 if/elseif 대신 정식 핸들러 계약(`isHandlable`/ `priority`/`process`/`retract`)을 따르는 것만 다름. `modifier-plan.md`가 -이미 예시로 든 `Modifier.Rounded(8)`은 이 특수 키를 flatten해서 props에 +이미 예시로 든 `mod:UICorner(8)`은 이 특수 키를 flatten해서 props에 꽂아넣는 사탕 문법일 뿐, 실제 처리는 이 Handler가 함 — Modifier를 안 거치고 `Frame { UICorner = 8 }`처럼 순수 인라인 키로 직접 써도(v1처럼) 동일하게 작동함, `architecture.md`의 `[Attribute "Name"]`류 특수 키와 같은 층위. diff --git a/.claude/question.md b/.claude/question.md index 267a593..2b9d6f1 100644 --- a/.claude/question.md +++ b/.claude/question.md @@ -111,8 +111,9 @@ context-rejected.md`. 아래는 그중 **아직 실제로 열려있는 것만** - **이미 지나간 사례로 참고**: `register`(v1) → `State`(v2) 리네임은 "모호함"은 풀었지만 "다른 뜻으로 이미 쓰이는 단어"라는 새 문제를 만든 셈 — 이번 정리에서 같은 패턴을 조심할 것. -- `Store`/`Source`/`Modifier`/`Ref`/`process`/`retract`/`isHandlable`은 - 업계 선례와 잘 맞거나 이미 신중하게 결정된 이름들이라 특별한 문제 없음. +- `Store`/`Source`/`Modifier`/`process`/`retract`/`isHandlable`은 업계 + 선례와 잘 맞거나 이미 신중하게 결정된 이름들이라 특별한 문제 없음 + (`Ref`는 여기 포함 안 됨 — 위 3순위 목록에 이미 재검토 대상으로 있음). ### 2. 구현 착수 직전 감사 결과 (2026-08-06 신설, M0 착수 전 확인 권장) @@ -195,6 +196,15 @@ context-rejected.md`. 아래는 그중 **아직 실제로 열려있는 것만** 모두 기술적 근거와 안전 규칙까지 정리됐으나(문서 7번), **Slot이 foreign Instance를 어떻게 다루는지만 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 스파이크 때 같이 검증할지 나중 최적화 + 패스로 미룰지 — 아직 사용자 판단 전. ## 참고: 지금까지 확정된 것 (요약) diff --git a/.claude/research/documentation-content-map.md b/.claude/research/documentation-content-map.md index f62f006..44b6109 100644 --- a/.claude/research/documentation-content-map.md +++ b/.claude/research/documentation-content-map.md @@ -86,7 +86,7 @@ v1 폐기 API/버그/구조 결함 전부 v2 설계를 정당화하는 내부 ### modifier-plan.md / slot-plan.md - 초심자: 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<>(key)` + `isState`(→심화: `Get`과 이름을 다르게 한 이유) +- api: Setter가 리터럴/변환 함수 둘 다 받음(→심화: getter 없는 이유) / 필드가 State일 수 있는 4가지 조합 표(→심화: 반응성 유지/끊김 이유) / `mod:UICorner(8)` dot-access 생성자 관습 / Slot은 인스턴스당 여럿 가능 / 중첩 인스턴스 자식 처리 / retract 시 slot 내용 폐기(→심화: portal 없는 이유) / `:Apply(factory)` 기본 체이닝 관용구(→심화: 언제 `Apply` vs `Override`인지 성능 기준) / `:Peek<>(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 비용 절감보다 우선) - 열린 질문(문서화 보류): 여러 Slot이 형제로 섞일 때 순서 보장 — 미확정 - skip: 세션 날짜/확정 이력, 문서 승격/정정 안내 diff --git a/.claude/research/pre-implementation-audit.md b/.claude/research/pre-implementation-audit.md index 5d546a2..fd7dcc8 100644 --- a/.claude/research/pre-implementation-audit.md +++ b/.claude/research/pre-implementation-audit.md @@ -210,7 +210,12 @@ element별 weak-set) — throw 조건을 "Slot 핸들러의 `process`가 같은 ### 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 생존 확인)"`. **문제**: `base/lifecycle-pattern.md`("생명 바인드 유틸"을 State-invalidate @@ -403,7 +408,7 @@ Modifier를 합친다"는 시나리오가 `Override`의 가장 그럴듯한 실 관습 재사용" 절, `.claude/question.md` 1번. **문제**: Modifier의 런타임 체이닝 엔진은 quad-base 소유가 맞지만, 클래스별 -정적 타입 안전성(`Modifier.Rounded(8)`가 `FrameModifier` 타입으로 추론되는 +정적 타입 안전성(`mod:UICorner(8)`가 `FrameModifier` 타입으로 추론되는 것)은 "DI 쪽 '제네릭 생성자 함수 하나 + 자주 쓰는 것만 정적 필드' 패턴 재사용"이라 문서 스스로 밝히듯 quad-roblox의 DI 타입 생성 계층(M5)에 강하게 결합돼 있다. 그런데 `ROADMAP.md` M7 체크리스트(flatten-before-dispatch, @@ -561,12 +566,13 @@ Handler"라고만 서술해, 사실상 3개의 거의 동일한 형태(리터럴 ## 다음 액션 제안 -- **M0 착수 전**: 1-5(props.Modifier/Ref nil-hole)를 M0 스파이크 코드에 - 반영, 1-10(`store.key` 레코드 필드 타이핑)을 M0로 앞당기는 것 검토, - 1-11(Modifier `__index` 트릭)도 비용이 낮으니 M0 후보로 포함 검토. +- **M0 착수 전**: 1-5(props.Modifier/Ref nil-hole)는 `ROADMAP.md` M0에 + 반영 완료. 1-10(`store.key` 레코드 필드 타이핑)을 M0로 앞당기는 것 검토, + 1-11(Modifier `__index` 트릭)도 비용이 낮으니 M0 후보로 포함 검토는 계속 + 열려있음. - **M2(Dispatch) 착수 전**: 1-2, 1-3, 1-4를 한 번에 확정(전부 base dispatch 엔진의 에러/상태관리 규칙이라 같이 결정하는 게 효율적). -- **M2/M3 착수 전**: 1-6(canExecute 실제 구현) 실측, 1-9(LifetimeHandle - 마일스톤 재배치). +- **M2/M3 착수 전**: 1-6(canExecute 실제 구현) 실측(1-9는 반영 완료, 위 + 항목 참고). - **나머지**: 해당 마일스톤 착수 시점에 이 문서를 다시 열어 관련 항목만 확인하면 됨 — 지금 전부 결정할 필요는 없음. diff --git a/.claude/research/tween-plan.md b/.claude/research/tween-plan.md index 1b949a0..c1fb808 100644 --- a/.claude/research/tween-plan.md +++ b/.claude/research/tween-plan.md @@ -1,4 +1,4 @@ -# Tween / 애니메이션 플러깅 (기본값 확정, 옵션 키 이름만 남음) +# Tween / 애니메이션 플러깅 (기본값 확정, 옵션 키 이름·값 모양만 남음) **상태**: research — 방향은 뚜렷하게 잡혀 있고(라이브러리가 트윈을 직접 구현하지 않는다), `retract` 순서/오버라이드 기본값(Cancel)도 확정됨. 남은 건 diff --git a/CLAUDE.md b/CLAUDE.md index 7224758..78f41d7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -45,10 +45,11 @@ modifier/Ref의 컴포넌트 경계 통과 방식) 논의도 2026-08-04 세션 `tween-plan.md`/`existing-instance-bind-plan.md`/`debug-tooling-plan.md`/ `documentation-plan.md`/`documentation-content-map.md`/ `framework-comparison-findings.md`/`additional-primitives-plan.md`(키 기반 - 동적 컬렉션 재조정만 남음) — 전부 후순위(급한 건 `tween-plan.md` - 세부 옵션 정도). 최신 목록·우선순위는 `.claude/README.md`가 소스, 여기서 - 개수 반복 안 함(과거에 "두 개뿐"이라 적어놨다가 새 문서 추가될 때마다 - 안 갱신되는 패턴이 반복돼서 아예 안 세기로 함). + 동적 컬렉션 재조정만 남음)/`pre-implementation-audit.md`/`v1-compat-plan.md` + — 전부 후순위(급한 건 `tween-plan.md` 세부 옵션 정도). 최신 목록·우선순위는 + `.claude/README.md`가 소스, 여기서 개수 반복 안 함(과거에 "두 개뿐"이라 + 적어놨다가 새 문서 추가될 때마다 안 갱신되는 패턴이 반복돼서 아예 안 + 세기로 함). - `.claude/qa-request/`, `.claude/feedback/` — 구현 시작되면 쓰기 시작함, 지금은 비어있음. `.claude/archive/`는 원래 같은 취급이었으나 2026-08-06 세 번째 세션부터 **완전히 뒤집힌 설계 결정을 원문+역전 @@ -1054,12 +1055,9 @@ Tag/Attribute 전용 문서 신설.** None 논의를 파고들다 디스패치 `base/bind-system-plan.md`/`base/lifecycle-pattern.md`/`base/tag-plan.md` (신규)/`base/attribute-plan.md`(신규)/`ROADMAP.md`에 반영 완료: -- **제 실수 정정 — `canExecute`와 `isHandlable`은 다른 개념.** - `isHandlable(k,v)`는 KV 매치 predicate(핸들러 계약 4종 중 하나), - `canExecute`는 특정 바인딩 하나가 "지금 살아있어 실행돼도 되는가"만 - 보는 별개의 라이프타임 게이트(`lifecycle-pattern.md`) — `NoneHandler`가 - 구현해야 하는 건 `isHandlable`이지 `canExecute`가 아님, 앞서 잘못 쓴 - 문장을 고침. +- **제 실수 정정 — `canExecute`와 `isHandlable`은 다른 개념** (전체 경위는 + `archive/agent-mistake.md` 1번으로 옮김) — 결론만: `NoneHandler`가 + 구현해야 하는 건 `isHandlable`이지 `canExecute`가 아님. - **`Dispatch.getHandler`/`Dispatch.process`/`Dispatch.addHandler`/ `Dispatch.drive`로 이름 공식화.** 원래 "확정된 디스패치 모델" 절은 "스캔+실행"과 "매치된 핸들러 자신의 처리"를 둘 다 그냥 `process`라고 @@ -1136,12 +1134,8 @@ Tag/Attribute 전용 문서 신설.** None 논의를 파고들다 디스패치 introspection 창구(quad-debug 용도) 역할까지 겸하도록 `None`을 특수 분기로 앞단에서 걸러줌 — `isNone`이 그 분기의 실제 구현체. - **정정 — `isSource`는 별도로 필요함, 다섯 번째 세션의 "불필요" 서술을 - 뒤집음.** 그땐 "State면 충분한 용도"만 봤지만 `Source`는 State보다 - 진짜 더 많은 능력(`:Set`/`:Emit`)을 가진 서브타입이라 "쓰기도 되는 - 원천인가"를 알아야 하는 코드엔 `isState`만으론 부족 — `isSource` 별도 - 제공, `isState`는 여전히 `{State,Source}` 둘 다 통과. `component- - composition-plan.md` 4번 절이 애초에 `isSource`가 존재한다고 가정해둔 - 것과도 이걸로 정합됨(그동안 두 문서가 서로 모순돼 있었음, 이번에 발견). + 뒤집음** (전체 경위는 `archive/agent-mistake.md` 2번으로 옮김) — 결론만: + `isSource`를 별도 제공, `isState`는 여전히 `{State,Source}` 둘 다 통과. - **Luau 타입 narrowing은 자동으로 안 됨 — 사용자가 직접 확인, 명시적 `::` 캐스팅 필요.** `isX(v)`가 참이어도 Luau가 TypeScript의 `x is T` 같은 사용자 정의 타입 가드를 지원 안 해서 `v`의 정적 타입을 자동으로 diff --git a/ROADMAP.md b/ROADMAP.md index 5921179..7ad5505 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -4,8 +4,9 @@ quad-v2 구현 단계 실행 계획. 설계 근거/아키텍처 자체는 여기 `.claude/base/`가 소스, 여긴 **순서와 진행 상황**만. 마일스톤 시작할 때 체크박스를 세분화해서 늘려도 되고, 끝나면 체크만 하면 됨 — 살아있는 문서. -**2026-08-04 세션에 준비만 해둔 상태 — 아직 M0도 시작 안 함.** 다음 세션은 -바로 M0부터. +**2026-08-04 세션에 준비만 해둔 상태로 신설, 이후 여러 세션에 걸쳐 설계가 +확정될 때마다 각 마일스톤 체크박스가 계속 갱신돼왔음 — 그래도 아직 M0 +자체는 시작 안 함.** 다음 세션은 바로 M0부터. ## M0 — 스켈레톤 + 기술검증 (스파이크, "진짜" 마일스톤 아님) @@ -32,7 +33,10 @@ Luau 코드로 부딪혀본 적 없는 세 가지**를 던지는 코드로 검 폐기 → 위치로 표현, `PreRef` 신설" 절) - [ ] `props.Modifier`/`props.Ref` named-parameter로 받는 컴포넌트 하나 작성, `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/` 갱신