diff --git a/.claude/base/module-lifecycle-plan.md b/.claude/base/module-lifecycle-plan.md index be12803..f300af7 100644 --- a/.claude/base/module-lifecycle-plan.md +++ b/.claude/base/module-lifecycle-plan.md @@ -36,8 +36,9 @@ RBVM처럼 `init namespace` 하나하나 부르는 방식은 별로(`base/lifecy > 생명주기 4종을 `InitLifetimeHandle(module)`로 **인스턴스별 필드**로 만들었으므로, > `Source`/`State`/`Observer`/`Effect`처럼 그 게이트를 부르는 모듈은 `InitXxx(module)`이 > `module`을 클로저로 쥐고 **발화 시점에 `module.canExecute(self)`로 늦게 읽는다**(백엔드가 -> `New()` 뒤에 덮어쓰므로 `Init` 시점 캡처는 스텁을 영원히 잡는다). `Ref`/`Void`/`Relate`/ -> `Brand`처럼 게이트를 안 부르는 잎 모듈만 인스턴스 간 공유된다. 소스는 +> `New()` 뒤에 덮어쓰므로 `Init` 시점 캡처는 스텁을 영원히 잡는다). 게이트를 안 부르는 +> 잎 모듈만 인스턴스 간 공유된다 — 어느 파일이 잎인지는 **각 파일의 헤더 주석이 소스** +> (여기 나열하지 않는다, 실제로 한 번 갈라졌다). 소스는 > `base/lifecycle-pattern.md` "탑레벨 평범한 함수로 확정" 절의 `H-174` 문단. 바로 위 절이 확정한 `InitRoblox(Module)` 패턴(팩토리가 모듈 테이블을 diff --git a/ROADMAP.md b/ROADMAP.md index b4a0197..d7d9920 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -397,7 +397,7 @@ Luau 코드로 부딪혀본 적 없는 세 가지**를 던지는 코드로 검 돌며 **값만 앞당기고 통지는 상류 emit을 기다린다**. 다이아몬드 중복 통지가 접히므로 스파이크 `05`도 그에 맞춰 재작성해야 한다 (`luau-test/STATUS.md`). -- [x] **[2026-08-28 완료 — 단위 2]** `Source.luau`/`State.luau`/`Store.luau` — `InitState`/`InitSource`/`InitStore` 팩토리(`H-174`), `test/spec.{epochmap,source,state,store}.luau` +- [x] **[2026-08-28 완료 — 단위 2]** `Source.luau`/`State.luau`/`Store.luau` — `State.Init`/`InitSource`/`InitStore` 팩토리(`H-174`), `test/spec.{epochmap,source,state,store}.luau` - [x] **[2026-08-28 완료 — 단위 2]** **[2026-08-28 10라운드 `H-153`]** Store 생성자의 `isSource` 순회와 `store:Of(name)`에 **예약 이름 런타임 가드**(`error(…, 2)`) — 동적 키는 타입이 못 막는다; 그림자 = store 자신(`base/store-plan.md`). diff --git a/quad-base/src/EpochMap.luau b/quad-base/src/EpochMap.luau index 13b36a7..1fb8814 100644 --- a/quad-base/src/EpochMap.luau +++ b/quad-base/src/EpochMap.luau @@ -19,8 +19,8 @@ decided at runtime with `isEpoch` (§5 "런타임 분기는 `isEpoch`로"). Keys are weak: when an `Epoch` dies its entry goes with it. Values are - plain numbers, so `relate-plan.md`'s "value must not refer back to the - key" caveat does not apply. + plain numbers, so the weak-table hazard relate-plan.md warns about (a + value that refers back to its key keeps the key alive) cannot arise here. ]] local Brand = require("./Brand") diff --git a/quad-base/src/State.luau b/quad-base/src/State.luau index 582b061..4f2dffa 100644 --- a/quad-base/src/State.luau +++ b/quad-base/src/State.luau @@ -2,7 +2,7 @@ State — derived reactive node (`:Compute` / `:With` / `:Apply`), the propagation loop, and the epoch-based recompute judgement. - Assembly (`H-174`, user-confirmed 2026-08-28): `InitState(module)` builds + Assembly (`H-174`, user-confirmed 2026-08-28): `State.Init(module)` builds one implementation table per `quad` instance and keeps `module` in its closure, so later units (`Observer`, `Gate`) can read lifetime gates as `module.canExecute(...)` AT FIRE TIME (never captured at init — the @@ -193,7 +193,7 @@ end local function implFor(module: any) local impl = implByModule[module] if impl == nil then - error("State: InitState(module) has not run for this quad instance", 1) -- invariant check; every caller pulls it in first (`H-177`) + error("State: State.Init(module) has not run for this quad instance", 1) -- invariant check; every caller pulls it in first (`H-177`) end return impl end