docs(base): 코퍼스 전체 정합성 감사 반영, 기각된 대안 archive 이관

병렬 서브에이전트 4개로 base/reference/research/archive 전체를 재감사해
발견한 14건 수정 — canExecute(inst,value) 시그니처 통일, Dispatch.process가
Dispatch 체인/retractUnder와 모순되던 서술 정정, "프로바이더"→Handler 잔재
정리, Overridden 오타, :Peek 반환 타입에 None 누락, Peek/isState 미정
표기 해소 누락, slot CRUD 의미론 갭 미표기, Relate 개명 반영 누락,
pre-implementation-audit.md 자기모순(이미 해소된 1-2 재언급, 옛 UI
숏핸드 이름), documentation-content-map.md 신구 이름 자기모순 및
TagService/CollectionService 재발, README.md archive 색인 누락 행,
agent-mistake.md 카테고리 태그 누락.

modifier-plan.md 9-1번 절에 인라인으로 남아있던 기각된 Apply-mutable
대안 두 개의 전체 경위를 archive/modifier-apply-mutable-rejected.md로
이관하고 본문은 결론+포인터로 압축 — quadnomicon 개발로그 소재 확보,
컨텍스트 비대화 방지.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
qwreey 2026-08-08 11:38:29 +09:00
parent 959a519533
commit 4f3badf414
Signed by: qwreey
GPG key ID: D28DB79297A214BD
14 changed files with 104 additions and 56 deletions

View file

@ -71,6 +71,8 @@
| `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 전달로 충분하다는 판단 |
| `modifier-apply-mutable-rejected.md` | **[기각됨, 2026-08-08 신설]** `Modifier.Apply`/setter를 mutable로 바꾸는 방안(및 "Apply 경계에서만 clone" 절충안) — 둘 다 형제 서브트리 오염 방지가 clone 비용 절감보다 우선이라 기각 |
| `tag-hash-key-model-reversed.md` | [역전됨] 구 `Tag` 모델(해시 파트 boolean 키, 태그 개수만큼 키 갱신) — 2026-08-08 세 번째 세션에서 array-part 값 객체(`Tag(...)`, `:Added`/`:Removed`/`:Contains`/`:Apply`/`Merged`) 모델로 완전히 대체됨 |
| `agent-mistake.md` | **[에이전트 실수, 2026-08-07 신설]** 설계 반전이 아니라 에이전트가 문서 작성 중 개념을 혼동했다가 같은 세션 안에서 스스로 정정한 사례 모음(`canExecute`/`isHandlable` 혼동, `isSource` 불필요 오판) — CLAUDE.md 세션 로그의 중복 서술을 여기로 옮기고 포인터만 남김 | | `agent-mistake.md` | **[에이전트 실수, 2026-08-07 신설]** 설계 반전이 아니라 에이전트가 문서 작성 중 개념을 혼동했다가 같은 세션 안에서 스스로 정정한 사례 모음(`canExecute`/`isHandlable` 혼동, `isSource` 불필요 오판) — CLAUDE.md 세션 로그의 중복 서술을 여기로 옮기고 포인터만 남김 |
## 참고 ## 참고

View file

@ -1,4 +1,4 @@
# 에이전트 실수 기록 # [에이전트 실수] 에이전트 실수 기록
CLAUDE.md 세션 로그 안에 흩어져 있던 "에이전트가 같은 세션 안에서 스스로 CLAUDE.md 세션 로그 안에 흩어져 있던 "에이전트가 같은 세션 안에서 스스로
정정한 실수" 서술을 여기로 모음 — 최종 결론은 이미 각 `base/` 문서에 정정한 실수" 서술을 여기로 모음 — 최종 결론은 이미 각 `base/` 문서에

View file

@ -0,0 +1,46 @@
# [기각됨] Modifier `Apply`/setter를 mutable로 바꾸는 방안 (전체·절충안 둘 다)
**상태**: 후보였다가 채택 안 됨(확정한 적 없이 검토 후 기각) — `base/
modifier-plan.md` 9-1번 절에서 이 판단의 결론(판단 기준 자체는 "동질적/
이질적"이 아니라 "계산 의존성 유무")만 남기고 아래 전체 경위는 이 문서로
옮김. `batch-rejected.md`/`context-rejected.md`와 같은 카테고리 —
`quadnomicon` 소재 후보.
## 배경
2026-08-07 다섯 번째 세션 후속. `Apply` 체이닝이 호출마다 clone을 만들기
때문에, 항목 수천 개짜리 리스트 UI처럼 무거운 Modifier를 대량으로
재생성하는 상황에서 이 clone 비용이 누적되는 게 아닌지 사용자가 우려 —
대안으로 (a) `Apply`/setter를 아예 mutable로 바꾸는 방안, (b) `Overridden`
"여러 값을 합칠 특수 상황"이 아니라 "성능 최적화 수단"으로 승격하는 방안을
검토했음(이 문서는 (a)와 그 절충안만 다룸 — (b)는 기각되지 않고 "계산
의존성 유무" 판단 기준으로 정리되어 `modifier-plan.md` 본문에 그대로 남음).
## (a) `Apply`를 mutable로 바꾸는 방안 — 기각
3번 절에서 immutable+clone을 확정한 이유가 정확히 "같은 modifier
레퍼런스를 공유하는 형제 서브트리가 mutate로 오염되는 것"을 막기
위해서였음 — 이건 특정 세션 판단이 아니라 2026-08-04부터 계속 지켜온
하드 제약. `Apply`/setter가 mutable이면 여러 컴포넌트가 참조하는 공유
테마 상수 하나에 어느 한쪽이 체이닝만 해도 다른 쪽까지 같이 바뀌는
클래스의 버그가 그대로 돌아옴 — clone 비용 절감이 이 안전성보다
우선순위가 높다고 볼 근거가 없어 기각. (단, `table.clone`은 Luau native
shallow-copy라 Modifier 필드 수(한 자리~여남은 개) 기준 개별 clone 비용
자체는 이미 3번 절에서 무시 가능하다고 판단됨 — 이번에 새로 문제 삼는 건
"한 번 비용의 크기"가 아니라 "체인 길이 × 인스턴스 수로 누적되는 clone
*횟수*"라는 별개 축.)
## (a-1) 절충안 — "`Apply` 진입 시 한 번만 clone하고 그 안에서는 mutable로" — 검토했으나 기각
clone 횟수를 체인 길이만큼이 아니라 `Apply` 호출당 1번으로 줄이자는
아이디어(`Apply` 경계에서만 복사, 내부 setter들은 그 복사본을 그대로
mutate).
**기각 이유**: 이렇게 해도 버그 클래스 자체가 안 없어짐 — `Apply`
거치지 않고 setter를 직접 호출하는 흔한 경로(`mod:FontSize(...)`처럼
체이닝 자체가 아니라 단발 호출)는 여전히 mutable이라, 공유 레퍼런스에
대고 단발 setter 하나만 불러도(예: 서브트리 어딘가에서 폰트 두께만 살짝
바꾸는 경우) 그대로 오염됨 — "`Apply` 안에서는 안전, 밖에서는 안 안전"처럼
**어디서 터지느냐만 달라질 뿐 문제 자체는 그대로 남는 비일관적인
절충**이라 실익이 없음. 전부 clone하는 지금 방식이 버그 클래스를 균일하게
없애는 유일한 방법 — 확정 유지.

View file

@ -35,7 +35,7 @@ quad는 이제 "스크립트"가 아니라 **라이브러리**다. DOMless Roblo
5. **id 기반 전역 조회 폐지, Tag 시스템으로 대체.** v1의 `Store.GetObject(id)`/ 5. **id 기반 전역 조회 폐지, Tag 시스템으로 대체.** v1의 `Store.GetObject(id)`/
`Frame "id" {}`류는 더 이상 없음 — "id 매핑이 비현실적"이라는 게 이유. `Frame "id" {}`류는 더 이상 없음 — "id 매핑이 비현실적"이라는 게 이유.
네임스페이싱 문제는 있지만 별도 네임스페이스 개념을 추가하면 라이브러리 네임스페이싱 문제는 있지만 별도 네임스페이스 개념을 추가하면 라이브러리
복잡도가 너무 올라간다고 판단 — 당장은 TagService 그대로 사용. **대신 복잡도가 너무 올라간다고 판단 — 당장은 `CollectionService` 그대로 사용. **대신
Ref가 도입됨** — 단 Ref의 용도는 "id로 조회"가 아니라 "외부에서 이미 Ref가 도입됨** — 단 Ref의 용도는 "id로 조회"가 아니라 "외부에서 이미
관리되고 있는 instance를 quad로 점진적으로 마이그레이션/래핑하기 위해 관리되고 있는 instance를 quad로 점진적으로 마이그레이션/래핑하기 위해
직접 참조를 얻는 것"(`base/bind-system-plan.md`의 Ref 절 참고) — 둘을 직접 참조를 얻는 것"(`base/bind-system-plan.md`의 Ref 절 참고) — 둘을

View file

@ -220,10 +220,13 @@ NoneHandler.process(inst, k, v) = process(inst, k, nil) -- 재귀 재호출
- `Dispatch.getHandler(inst,k,v): Handler?` — 순수 스캔(`handler.isHandlable(inst,k,v)`+ - `Dispatch.getHandler(inst,k,v): Handler?` — 순수 스캔(`handler.isHandlable(inst,k,v)`+
`priority`), 부작용 없음. `priority`), 부작용 없음.
- `Dispatch.process(inst,k,v)` — 오케스트레이터: `getHandler` 호출 → - `Dispatch.process(inst,k,v)` — 오케스트레이터: `getHandler` 호출 →
이전에 이 키를 담당하던 핸들러와 다르면 이전 핸들러의 `retract` 호출 → 매치된 핸들러를 `(inst,k)` 체인 꼬리에 push → 그 핸들러의 `.process`
새로 매치된 핸들러의 `.process` 호출. **재귀 재디스패치(Tween/일반 호출. **"이전 핸들러와 다르면 retract"라는 diff는 `Dispatch.process`
store-bind/`NoneHandler`)는 전부 이 `Dispatch.process`를 다시 부르는 자신의 일이 아님** — 재귀/래핑 핸들러(Tween/일반 store-bind/
것** — 원래 있던 재귀 관례 그대로, 새로 바뀐 것 없음. `NoneHandler`)가 재-dispatch 전에 스스로 `Dispatch.retractUnder(inst,
k, self, newV)`를 먼저 불러 자기 밑을 정리하는 책임을 짐(정확한
메커니즘·기각된 대안은 아래 "Dispatch 체인" 절 참고 — 전역 소유자
슬롯 하나로 diff하는 안은 래핑 핸들러에서 깨져서 기각됨).
- `Dispatch.addHandler(handler: Handler)` — 핸들러를 우선순위 레지스트리에 - `Dispatch.addHandler(handler: Handler)` — 핸들러를 우선순위 레지스트리에
등록. `Dispatch.process`/`getHandler`와 마찬가지로 base엔 인터페이스만 등록. `Dispatch.process`/`getHandler`와 마찬가지로 base엔 인터페이스만
있고, quad-roblox의 concrete Handler들(PropertyHandler/EventHandler/ 있고, quad-roblox의 concrete Handler들(PropertyHandler/EventHandler/
@ -1448,7 +1451,8 @@ 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(handle)` 하나만 확인하고 거짓이면 predicate)로 등록하면, 발화 시 `canExecute(inst, value)`(2026-08-08 세션
최종 시그니처) 하나만 확인하고 거짓이면
그냥 no-op — `isInit` 분기라는 별도 개념 자체가 불필요(사용자 확정: 그냥 no-op — `isInit` 분기라는 별도 개념 자체가 불필요(사용자 확정:
"canExecute 하나로 통일"). "canExecute 하나로 통일").

View file

@ -365,33 +365,14 @@ Modifier가 들고 있는 모든 필드는 항상 "그 시점에 이미 완전
**동기**: `Apply` 체이닝이 호출마다 clone을 만들기 때문에, 항목 수천 개짜리 **동기**: `Apply` 체이닝이 호출마다 clone을 만들기 때문에, 항목 수천 개짜리
리스트 UI처럼 무거운 Modifier를 대량으로 재생성하는 상황에서 이 clone 리스트 UI처럼 무거운 Modifier를 대량으로 재생성하는 상황에서 이 clone
비용이 누적되는 게 아닌지 사용자가 우려 — 대안으로 (a) `Apply`/setter를 비용이 누적되는 게 아닌지 사용자가 우려 — 대안으로 (a) `Apply`/setter를
아예 mutable로 바꾸는 방안, (b) `Overridden`를 "여러 값을 합칠 특수 상황"이 아예 mutable로 바꾸는 방안(과 그 절충안), (b) `Overridden`를 "여러 값을
아니라 "성능 최적화 수단"으로 승격하는 방안을 검토. 합칠 특수 상황"이 아니라 "성능 최적화 수단"으로 승격하는 방안을 검토.
**(a) `Apply`를 mutable로 바꾸는 방안 — 기각.** 3번 절에서 immutable+clone을 **(a) `Apply`/setter를 mutable로 바꾸는 방안(및 "`Apply` 경계에서만 clone"
확정한 이유가 정확히 "같은 modifier 레퍼런스를 공유하는 형제 서브트리가 절충안) — 둘 다 검토 후 기각.** 3번 절의 immutable+clone 하드 제약(형제
mutate로 오염되는 것"을 막기 위해서였음 — 이건 특정 세션 판단이 아니라 서브트리 오염 방지)이 clone 비용 절감보다 우선순위가 높다는 결론, 절충안도
2026-08-04부터 계속 지켜온 하드 제약. `Apply`/setter가 mutable이면 여러 "어디서 터지느냐만 달라질 뿐 문제 자체는 남는" 비일관적 타협이라 기각 —
컴포넌트가 참조하는 공유 테마 상수 하나에 어느 한쪽이 체이닝만 해도 다른 전체 경위·반박 논리는 `archive/modifier-apply-mutable-rejected.md` 참고.
쪽까지 같이 바뀌는 클래스의 버그가 그대로 돌아옴 — clone 비용 절감이
이 안전성보다 우선순위가 높다고 볼 근거가 없어 기각. (단, `table.clone`
Luau native shallow-copy라 Modifier 필드 수(한 자리~여남은 개) 기준
개별 clone 비용 자체는 이미 3번 절에서 무시 가능하다고 판단됨 — 이번에
새로 문제 삼는 건 "번 비용의 크기"가 아니라 "체인 길이 × 인스턴스 수로
누적되는 clone *횟수*"라는 별개 축.)
**(a-1) 절충안 — "`Apply` 진입 시 한 번만 clone하고 그 안에서는
mutable로" — 검토했으나 기각.** clone 횟수를 체인 길이만큼이 아니라
`Apply` 호출당 1번으로 줄이자는 아이디어(`Apply` 경계에서만 복사, 내부
setter들은 그 복사본을 그대로 mutate). **기각 이유**: 이렇게 해도 버그
클래스 자체가 안 없어짐 — `Apply`를 거치지 않고 setter를 직접 호출하는
흔한 경로(`mod:FontSize(...)`처럼 체이닝 자체가 아니라 단발 호출)는
여전히 mutable이라, 공유 레퍼런스에 대고 단발 setter 하나만 불러도(예:
서브트리 어딘가에서 폰트 두께만 살짝 바꾸는 경우) 그대로 오염됨 —
"`Apply` 안에서는 안전, 밖에서는 안 안전"처럼 **어디서 터지느냐만
달라질 뿐 문제 자체는 그대로 남는 비일관적인 절충**이라 실익이 없음.
전부 clone하는 지금 방식이 버그 클래스를 균일하게 없애는 유일한 방법 —
확정.
**(b) 판단 기준 — "동질적 vs 이질적 프로퍼티"가 아니라 "필드 간 계산 **(b) 판단 기준 — "동질적 vs 이질적 프로퍼티"가 아니라 "필드 간 계산
의존성 유무"로 명시.** 사용자가 처음엔 "동질적(폰트 굵기 보정처럼 연관된 의존성 유무"로 명시.** 사용자가 처음엔 "동질적(폰트 굵기 보정처럼 연관된
@ -461,7 +442,7 @@ Modifier 값을 변수/모듈 상수로 만들어 재사용하는, 기존에도
시점에 실 테스트 결과에 따라 다시 좁히는 걸 목표로 로드맵에 남김 시점에 실 테스트 결과에 따라 다시 좁히는 걸 목표로 로드맵에 남김
(`ROADMAP.md` M7). (`ROADMAP.md` M7).
**`:Peek<<T>>(key): T | State<T> | nil`** — Modifier 필드를 확정하지 **`:Peek<<T>>(key): T | State<T> | None | nil`** — Modifier 필드를 확정하지
않고 그대로 읽는 접근자. 이름을 `Get`이 아니라 `Peek`로 정한 이유: 이 않고 그대로 읽는 접근자. 이름을 `Get`이 아니라 `Peek`로 정한 이유: 이
프로젝트 전역에서 `State:Get()`은 "확정한다"(pull + recompute + 최종값 프로젝트 전역에서 `State:Get()`은 "확정한다"(pull + recompute + 최종값
반환)는 의미로 이미 자리잡았는데, Modifier의 읽기는 정반대(들고 있는 반환)는 의미로 이미 자리잡았는데, Modifier의 읽기는 정반대(들고 있는
@ -508,5 +489,6 @@ Source도 같이 잡아줌 — **[2026-08-07 여덟 번째 세션 정정] `isSou
- **[해소됨]** `Overridden` 이름 — 2026-08-08 세션에서 확정(`Add`/`Remove` - **[해소됨]** `Overridden` 이름 — 2026-08-08 세션에서 확정(`Add`/`Remove`
→`Added`/`Removed`, `Merge`→`Merged`와 같은 분사형 네이밍 컨벤션에 →`Added`/`Removed`, `Merge`→`Merged`와 같은 분사형 네이밍 컨벤션에
맞춰 불규칙동사 `override`의 정확한 과거분사를 씀, `Overrided`는 오기). 맞춰 불규칙동사 `override`의 정확한 과거분사를 씀, `Overrided`는 오기).
`Peek`/`isState` — 동작은 위 9번 절에서 확정, 정확한 이름은 다른 `Peek`/`isState` — 동작은 위 9번 절에서 확정, 이름도 2026-08-08 다섯
가칭들과 마찬가지로 `.claude/question.md`의 용어 정리 라운드까지 잠정. 번째 세션(`.claude/question.md` 용어 정리 라운드)에서 더 나은 대안 없어
현재 이름 그대로 최종 확정됨.

View file

@ -1,4 +1,4 @@
# 모듈 라이프사이클 — 프로바이더 패턴, bind/store는 누가 구현하는가 (base로 승격됨) # 모듈 라이프사이클 — Handler 패턴, bind/store는 누가 구현하는가 (base로 승격됨)
**상태**: base — "누가 store를 구현하는가"까지 포함해 전부 확정되어 **상태**: base — "누가 store를 구현하는가"까지 포함해 전부 확정되어
`research/`에서 승격됨(`base/architecture.md`의 "구현 착수: 소스 트리 구조 `research/`에서 승격됨(`base/architecture.md`의 "구현 착수: 소스 트리 구조
@ -14,10 +14,12 @@ bind는 누가 어떻게 구현" / "스토어는 누가 구현해…" 절. 확
Slot과 맞물려서 잘 생각해서 구현해야 하는 부분. **기울어진 방향**: mount가 Slot과 맞물려서 잘 생각해서 구현해야 하는 부분. **기울어진 방향**: mount가
처리하는 게 맞아 보이지만, 그러면 확장성이 있을지가 문제. 결론: **표준 구현체는 처리하는 게 맞아 보이지만, 그러면 확장성이 있을지가 문제. 결론: **표준 구현체는
인터페이스만 두고, 실제 구현은 `quad-roblox` 같은 백엔드 서브패키지가 해당 인터페이스만 두고, 실제 구현은 `quad-roblox` 같은 백엔드 서브패키지가 해당
인터페이스를 구현**. 런타임에 프로바이더로 Roblox를 주입받는 방향(반대로 인터페이스를 구현**. 런타임에 Handler로 Roblox를 주입받는 방향(반대로
"프로바이더로 base를 받는" 게 아니라) — 이유: 여긴 가상돔이 없어서, base "Handler로 base를 받는" 게 아니라) — 이유: 여긴 가상돔이 없어서, base
쪽이 "누가 실제로 그려주는지" 모르는 채로 있다가 Roblox 프로바이더를 주입받는 쪽이 "누가 실제로 그려주는지" 모르는 채로 있다가 Roblox Handler를 주입받는
모양이 더 자연스러워 보임. 모양이 더 자연스러워 보임. (이름 자체는 이후 "Handler"로 확정 —
`base/bind-system-plan.md`의 핸들러 계약 절 참고, 이 문서는 여전히
초안 당시 표현인 "프로바이더"로 쓰여 있던 걸 정정.)
## pluggable 플러그 초기화는 누구 몫인가 ## pluggable 플러그 초기화는 누구 몫인가

View file

@ -134,3 +134,10 @@ Slot은 바인딩되는 순간 그 안의 요소를 전부 own해버리는 데
- **여러 Slot이 형제로 섞일 때 순서 보장**은 아직 열려있음(위 "여러 Slot이 - **여러 Slot이 형제로 섞일 때 순서 보장**은 아직 열려있음(위 "여러 Slot이
섞일 때 순서 보장" 절 참고) — Roblox 단일 백엔드로는 급하지 않음, Slot 섞일 때 순서 보장" 절 참고) — Roblox 단일 백엔드로는 급하지 않음, Slot
코어 로직 구현 시점에 재검토. 코어 로직 구현 시점에 재검토.
- **`add`/`remove`/`clear` CRUD 의미론 자체가 아직 정의 안 됨** — 위
"개념" 절이 이 세 연산을 뮤터블 메타 배열에 지원되는 것처럼 나열만
하고 정확한 동작(예: `add`가 위치를 지정하는지, `remove`가 값 동등성
기준인지 참조 기준인지, `clear`가 재마운트 가능한 자리를 남기는지)은
정의돼 있지 않음. `research/pre-implementation-audit.md`가 이미 지적한
갭이고, 2026-08-07 아홉 번째 세션에서 사용자가 직접 다루기로 보류함 —
Slot 코어 로직 구현 착수 전 반드시 확정 필요.

View file

@ -31,7 +31,8 @@ purity-and-effects-plan.md`와 연결됨).
어떻게 되는가 — 별도 메커니즘을 새로 만들 필요 없이, `base/ 어떻게 되는가 — 별도 메커니즘을 새로 만들 필요 없이, `base/
lifecycle-pattern.md`의 "생명 바인드 유틸"(canExecute predicate)을 state- lifecycle-pattern.md`의 "생명 바인드 유틸"(canExecute predicate)을 state-
invalidate 리스너 클로저 등록에도 그대로 재사용하면 됨: 발화 시 invalidate 리스너 클로저 등록에도 그대로 재사용하면 됨: 발화 시
`canExecute(handle)` 하나만 확인, 거짓이면 no-op. 한때 검토했던 `isInit=false` `canExecute(inst, value)`(2026-08-08 세션 최종 시그니처 — `base/
lifecycle-pattern.md` 참고) 하나만 확인, 거짓이면 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 온톨로지" 절 참고.

View file

@ -22,7 +22,7 @@ tag:Contains(name): boolean -- 멤버십 확인
tag:Apply(factory): U -- factory(self) 체이닝 설탕(Modifier와 동일 패턴) tag:Apply(factory): U -- factory(self) 체이닝 설탕(Modifier와 동일 패턴)
Tag.Merged(tag1, tag2, ...): Tag -- 여러 Tag의 합집합(무손실). Modifier의 Tag.Merged(tag1, tag2, ...): Tag -- 여러 Tag의 합집합(무손실). Modifier의
Overridden(필드 단위 덮어쓰기, 손실 있음)와 Overridden(필드 단위 덮어쓰기, 손실 있음)와
다른 연산이라 이름도 다름 — Override는 다른 연산이라 이름도 다름 — Overridden은
"이미 계산된 걸 합침", Merged는 "집합을 "이미 계산된 걸 합침", Merged는 "집합을
합침" 합침"
``` ```

View file

@ -121,7 +121,7 @@ base가 범용 유틸로 제공하기로 확정한 per-instance weak-keyed 저
## 남은 열린 질문 (단순화 후보, 사소함) ## 남은 열린 질문 (단순화 후보, 사소함)
- Corner/PaddingAll/Scale 3개 거의 동일한 형태의 Handler를 각각 만들지, - UICorner/UIPadding/UIScale 3개 거의 동일한 형태의 Handler를 각각 만들지,
`{key -> {ChildClassName, ChildDefaultName, Property, wrap=fn}}` 룩업 `{key -> {ChildClassName, ChildDefaultName, Property, wrap=fn}}` 룩업
테이블로 구동되는 단일 `Handlers/InstanceShorthand.luau`로 통합할지 — 테이블로 구동되는 단일 `Handlers/InstanceShorthand.luau`로 통합할지 —
`research/pre-implementation-audit.md` 3-2번 참고, 강제 사항 아님, `research/pre-implementation-audit.md` 3-2번 참고, 강제 사항 아님,

View file

@ -45,7 +45,7 @@
### architecture.md ### architecture.md
- 초심자: DOMless 즉시 Instance 생성 모델 / 특수 바인드 키 / Ref 기본 개념 / modifier 기본 사용법(스타일링) / Store·State·Source 온톨로지 핵심 동작 / quad-base·quad-roblox 패키지 구조 존재 사실 - 초심자: DOMless 즉시 Instance 생성 모델 / 특수 바인드 키 / Ref 기본 개념 / modifier 기본 사용법(스타일링) / Store·State·Source 온톨로지 핵심 동작 / quad-base·quad-roblox 패키지 구조 존재 사실
- api: Class 함수형+`:` 체이닝 예외 규칙 / store 바인드=전체 교체 의미론 / Tag/retract(TagService 기반) / modifier 병합 우선순위 규칙(→심화: CSS cascade 회피 근거) / PropertyChangedSignal이 pluggable 핸들러로 구현 / Source·State·Store 타입 정의 - api: Class 함수형+`:` 체이닝 예외 규칙 / store 바인드=전체 교체 의미론 / Tag/retract(CollectionService 기반) / modifier 병합 우선순위 규칙(→심화: CSS cascade 회피 근거) / PropertyChangedSignal이 pluggable 핸들러로 구현 / Source·State·Store 타입 정의
- 심화: Class가 OOP 아닌 함수형인 이유 / metatable 체이닝 폐기 이유(v1 clone 문제) / id 기반 전역 조회 폐지 이유 / Style(Default) 시스템 폐기→modifier 대체 근거 / 멀티 백엔드(GTK 등) 지향 이유 / push-invalidate·pull-recompute 전파 모델 상세, 다이아몬드 의존성 해결 근거 - 심화: Class가 OOP 아닌 함수형인 이유 / metatable 체이닝 폐기 이유(v1 clone 문제) / id 기반 전역 조회 폐지 이유 / Style(Default) 시스템 폐기→modifier 대체 근거 / 멀티 백엔드(GTK 등) 지향 이유 / push-invalidate·pull-recompute 전파 모델 상세, 다이아몬드 의존성 해결 근거
- skip: Tracker 미구현, lang 모듈 분리, Signal 클래스 미구현 판단 과정, 소스 트리·모노레포 구조, 테스트 전략(mock 설계) - skip: Tracker 미구현, lang 모듈 분리, Signal 클래스 미구현 판단 과정, 소스 트리·모노레포 구조, 테스트 전략(mock 설계)
@ -92,9 +92,9 @@ v1 폐기 API/버그/구조 결함 전부 v2 설계를 정당화하는 내부
- skip: 세션 날짜/확정 이력, 문서 승격/정정 안내 - skip: 세션 날짜/확정 이력, 문서 승격/정정 안내
### store-semantics.md / tween-plan.md / ui-shorthand-plan.md ### store-semantics.md / tween-plan.md / ui-shorthand-plan.md
- 초심자: Store 생성+`myStore.key = value` 문법 / `store.key`로 State 얻기 개념 / Tween 기본 바인드 키+취소 기본 동작 / UI 숏핸드 인라인 키 기본 예시(`Frame { PaddingAllOffset = 50 }`) - 초심자: Store 생성+`myStore.key = value` 문법 / `store.key`로 State 얻기 개념 / Tween 기본 바인드 키+취소 기본 동작 / UI 숏핸드 인라인 키 기본 예시(`Frame { UIPaddingOffset = 50 }`)
- api: `:With`+`:Compute` 시그니처(→심화) / `source:Emit()` 존재+"Get() 결과 캐시 금지" 캐비엇(버그 유발 포인트라 api에도 명시 가치 있음, →심화; 2026-08-06 후속 세션에서 `Store:Emit(key)`→`source:Emit()`로 호출부 변경, `store-semantics.md` 참고) / Tween 핸들러가 Instance 직접 받음(Ref 불필요) / retract는 Destroy 시 호출 안 됨(→심화) / UI 숏핸드 키 목록 레퍼런스 표 / Modifier와 순수 인라인 키 동등성 - api: `:With`+`:Compute` 시그니처(→심화) / `source:Emit()` 존재+"Get() 결과 캐시 금지" 캐비엇(버그 유발 포인트라 api에도 명시 가치 있음, →심화; 2026-08-06 후속 세션에서 `Store:Emit(key)`→`source:Emit()`로 호출부 변경, `store-semantics.md` 참고) / Tween 핸들러가 Instance 직접 받음(Ref 불필요) / retract는 Destroy 시 호출 안 됨(→심화) / UI 숏핸드 키 목록 레퍼런스 표 / Modifier와 순수 인라인 키 동등성
- 심화: Source·Store·State·Observer 온톨로지(독립 프리미티브 vs 파생 데이터 원칙, 생성자 모양 근거) / `Emit`이 Source 전용인 이유(디버깅 그래프 무결성) / `Store<T>`의 T가 Modifier 불가인 이유 / Tween을 반응 그래프 밖 특수 bind key로 둔 이유(Fusion 반면교사) / RoundSize 포팅 불필요 vs Corner/PaddingAll/Scale 필요 이유 / "작고 opt-in 아닌 편의 기능은 코어 포함" 원칙 - 심화: Source·Store·State·Observer 온톨로지(독립 프리미티브 vs 파생 데이터 원칙, 생성자 모양 근거) / `Emit`이 Source 전용인 이유(디버깅 그래프 무결성) / `Store<T>`의 T가 Modifier 불가인 이유 / Tween을 반응 그래프 밖 특수 bind key로 둔 이유(Fusion 반면교사) / RoundSize 포팅 불필요 vs UICorner/UIPadding/UIScale 필요 이유 / "작고 opt-in 아닌 편의 기능은 코어 포함" 원칙
- 열린 질문(문서화 보류): tween-plan.md의 오버라이드/삭제후재시작/끝점이동 옵션 키 이름 미정 / ui-shorthand의 RoundSize 완전 드롭 여부 - 열린 질문(문서화 보류): tween-plan.md의 오버라이드/삭제후재시작/끝점이동 옵션 키 이름 미정 / ui-shorthand의 RoundSize 완전 드롭 여부
- skip: 세션 정정 이력, v1 소스 조사 경위 - skip: 세션 정정 이력, v1 소스 조사 경위

View file

@ -518,12 +518,12 @@ M11 착수 시.
쉬워진다든가)가 있다면 한 줄 추가하고, 없다면 "그냥 클로저 업밸류를 쉬워진다든가)가 있다면 한 줄 추가하고, 없다면 "그냥 클로저 업밸류를
쓰라"는 문서화 패턴으로 대체해 API 표면 자체를 줄이는 걸 검토. 쓰라"는 문서화 패턴으로 대체해 API 표면 자체를 줄이는 걸 검토.
### 3-2. Corner/PaddingAll/Scale 개별 Handler 대신 데이터 테이블 구동 단일 제네릭 Handler ### 3-2. UICorner/UIPadding/UIScale 개별 Handler 대신 데이터 테이블 구동 단일 제네릭 Handler
**위치**: `base/ui-shorthand-plan.md` "메커니즘 — 새 아키텍처 개념 **위치**: `base/ui-shorthand-plan.md` "메커니즘 — 새 아키텍처 개념
불필요" 절. 불필요" 절.
**문제**: 문서는 "Corner/PaddingAll/Scale 같은 특수 키를 인식하는 **문제**: 문서는 "UICorner/UIPadding/UIScale 같은 특수 키를 인식하는
Handler"라고만 서술해, 사실상 3개의 거의 동일한 형태(리터럴 값 하나 → Handler"라고만 서술해, 사실상 3개의 거의 동일한 형태(리터럴 값 하나 →
고정 이름 자식 찾기/생성 → 프로퍼티 세팅)의 Handler를 각각 만드는 고정 이름 자식 찾기/생성 → 프로퍼티 세팅)의 Handler를 각각 만드는
그림이다. 문서 자체가 "앞으로 비슷한 제안이 오면 이 선례를 따르라"고 그림이다. 문서 자체가 "앞으로 비슷한 제안이 오면 이 선례를 따르라"고
@ -531,8 +531,8 @@ Handler"라고만 서술해, 사실상 3개의 거의 동일한 형태(리터럴
선형으로 늘어나는 구조. 선형으로 늘어나는 구조.
**제안**: `{key -> {ChildClassName, ChildDefaultName, Property, wrap=fn}}` **제안**: `{key -> {ChildClassName, ChildDefaultName, Property, wrap=fn}}`
형태의 룩업 테이블 하나로 구동되는 단일 `Handlers/InstanceShorthand.luau` 형태의 룩업 테이블 하나로 구동되는 단일 `Handlers/InstanceShorthand.luau`
통합하는 안을 검토. 새 shorthand 키 추가가 "테이블에 항목 하나 추가"로 통합하는 안을 검토. 새 shorthand 키 추가가 "테이블에 항목 하나 추가"로
끝나 M10 이후 유지보수 비용이 줄어듦. 강제 사항 아님, 구현 시점에 결정할 끝나 M10 이후 유지보수 비용이 줄어듦. 강제 사항 아님, 구현 시점에 결정할
정도의 사소한 개선 후보. 정도의 사소한 개선 후보.
@ -595,8 +595,11 @@ Handler"라고만 서술해, 사실상 3개의 거의 동일한 형태(리터럴
반영 완료. 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-3, 1-4를 한 번에 확정(전부 base dispatch
dispatch 엔진의 에러/상태관리 규칙이라 같이 결정하는 게 효율적). 엔진의 에러/상태관리 규칙이라 같이 결정하는 게 효율적). 1-2는 2026-08-08
세 번째 세션에 Dispatch 체인+`retractUnder`로 이미 해소됨(위 1-2번 항목
참고) — 남은 건 M2 스파이크에서 다단 체인 케이스가 실제로 맞게
동작하는지 실측하는 것뿐.
- **M2/M3 착수 전**: 1-6(canExecute 실제 구현) 실측(1-9는 반영 완료, 위 - **M2/M3 착수 전**: 1-6(canExecute 실제 구현) 실측(1-9는 반영 완료, 위
항목 참고). 항목 참고).
- **나머지**: 해당 마일스톤 착수 시점에 이 문서를 다시 열어 관련 항목만 - **나머지**: 해당 마일스톤 착수 시점에 이 문서를 다시 열어 관련 항목만

View file

@ -69,9 +69,10 @@ Ref나 네임스페이스드 조회가 필요하지 않음. Tween의 store-bind
라이브러리가 강제하지 않고, `[Tween(key, tweenData, {onOverride=...})]`처럼 라이브러리가 강제하지 않고, `[Tween(key, tweenData, {onOverride=...})]`처럼
키 설정으로 사용자가 고를 수 있게 열어둠 — `retract(inst, k, v)`가 이전 키 설정으로 사용자가 고를 수 있게 열어둠 — `retract(inst, k, v)`가 이전
값(v)을 받으므로 여기서 선택된 동작을 구현. `retract`가 접근해야 할 "이전에 값(v)을 받으므로 여기서 선택된 동작을 구현. `retract`가 접근해야 할 "이전에
생성한 실제 Tween 객체"는 `base/bind-system-plan.md`가 말하는 base 제공 생성한 실제 Tween 객체"는 `base/relate-plan.md`가 정의하는 `Relate`(`inst`를
범용 유틸(`inst`를 키로 하는 weak-keyed per-instance 상태 저장소)에 담아두면 weak 키로 하는 범용 릴레이션 프리미티브, 옛 가칭 `base.perInstanceState`
됨. **GC 확인(2026-08-07 여섯 번째 세션, 사용자 제안 검증)**: 이 저장소는 대체)에 담아두면 됨. **GC 확인(2026-08-07 여섯 번째 세션, 사용자 제안
검증)**: 이 저장소는
`inst`로 weak-keyed된 바깥 릴레이션 안에 `k`별 안쪽 릴레이션이 중첩된 `inst`로 weak-keyed된 바깥 릴레이션 안에 `k`별 안쪽 릴레이션이 중첩된
구조라, `inst`가 죽으면 그 안에 담긴 Tween 인스턴스 릴레이션도 별도 구조라, `inst`가 죽으면 그 안에 담긴 Tween 인스턴스 릴레이션도 별도
정리 로직 없이 같이 GC됨 — `base/bind-system-plan.md`의 "핸들러 내부 정리 로직 없이 같이 GC됨 — `base/bind-system-plan.md`의 "핸들러 내부