quad/.claude/base
qwreey 6216f12f37
decide(attribute): group Attribute(...) primitive, AttributeKey rename, weak-cache identity
Adds Attribute(store1, store2, ...) as a Tag-shaped array-part value
object that projects one or more Stores' named Source fields onto
native Roblox attributes in one binding, with .Merged for combining
heterogeneous Stores. Rejected: a bare [Attribute] = Store{...} hash
slot (only one slot per instance, can't merge heterogeneous stores)
and making Attribute a Store subtype (would let Store<T>'s T be a
Store again, colliding with the existing "handler-layer values can't
live in Source" rule). Verified the value-slot aggregation in .Merged
doesn't reopen the earlier "layered Store" rejection (archive/
context-rejected.md) since it's an explicit one-time author-time
composition, not an implicit read-time parent-chain fallback.

Renamed the existing single-key constructor Attribute<<T>>(name) to
AttributeKey<<T>>(name) to remove the name collision with the new
group primitive (provisional per existing OnChange/OnChangeKey
precedent; final bikeshed still queued in question.md).

Follow-up: AttributeKey(name) now memoizes through a name-keyed weak
table, guaranteeing AttributeKey(a) == AttributeKey(a) while any
strong reference (e.g. a live Dispatch chain entry) keeps it alive.
This let the group Attribute handler drop its originally-planned
self-contained SetAttribute/subscription logic and instead recurse
into the existing single-key AttributeKey dispatch path per field,
reusing its None/retract/store-bind handling instead of duplicating
it — the group handler's own state shrinks to just a prior-name-set
diff for deciding which keys to retractUnder. The same memoization
was applied to OnChangeKey for the same reason (pure name-to-key
mapping, no other varying state), confirmed safe even once
State<function> callbacks are involved.

Reflected across base/attribute-plan.md, base/onchange-plan.md,
base/architecture.md (source tree + Brand isX list), ROADMAP.md M2/M10,
question.md, README.md, and the luau-test type-narrowing spike.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 18:22:54 +09:00
..
architecture.md decide(attribute): group Attribute(...) primitive, AttributeKey rename, weak-cache identity 2026-08-11 18:22:54 +09:00
attribute-plan.md decide(attribute): group Attribute(...) primitive, AttributeKey rename, weak-cache identity 2026-08-11 18:22:54 +09:00
bind-system-plan.md decide(attribute): group Attribute(...) primitive, AttributeKey rename, weak-cache identity 2026-08-11 18:22:54 +09:00
blocker-plan.md docs: Blocker/Effect를 base/additional-primitives.md에서 별개 파일로 재분리 2026-08-07 14:45:57 +09:00
component-composition-plan.md fix(base): 중간검토(질문 모드)에서 발견된 설계 결함 다수 수정 2026-08-09 23:37:46 +09:00
effect-plan.md decide(bind-system): Compute(fn, ...) trailing-args sugar 확정 2026-08-11 10:29:35 +09:00
lifecycle-pattern.md decide(base): Slot 형제 순서 보장(Length/Offset), bindLifetime/unbindLifetime과 canBound 이중 바인딩 게이트 통합 2026-08-09 20:58:56 +09:00
modifier-plan.md decide(tween): 값-레벨 Tween<T> 래퍼로 재설계, pre-implementation-audit 1-1 해소 2026-08-10 00:53:26 +09:00
module-lifecycle-plan.md docs(base): 코퍼스 전체 정합성 감사 반영, 기각된 대안 archive 이관 2026-08-08 11:38:29 +09:00
onchange-plan.md decide(attribute): group Attribute(...) primitive, AttributeKey rename, weak-cache identity 2026-08-11 18:22:54 +09:00
purity-and-effects-plan.md 6차 라운드 + 컴포넌트화/Modifier 논의, 문서 코퍼스 전체 정리 결과 반영 2026-08-04 15:49:28 +09:00
relate-plan.md decide(base): Relate 프리미티브 신설, bindLifetime/canExecute 확정, retract 필수화 2026-08-08 00:54:48 +09:00
slot-plan.md decide(slot): reactive raw elements via Slot:Single sugar, :List index skip fix 2026-08-11 14:25:19 +09:00
store-semantics.md decide(base): canBound 이름 확정, Compute previous 방어/스코핑 명확화, Modifier 핸들러값·State<Modifier> UB→error 전환 2026-08-09 16:00:19 +09:00
tag-plan.md fix(base): 중간검토(질문 모드)에서 발견된 설계 결함 다수 수정 2026-08-09 23:37:46 +09:00
ui-shorthand-plan.md decide(attribute): group Attribute(...) primitive, AttributeKey rename, weak-cache identity 2026-08-11 18:22:54 +09:00