From 1f4f21c75a1280ce0e46394ccc5bed1c77b20d4b Mon Sep 17 00:00:00 2001 From: qwreey Date: Thu, 13 Aug 2026 22:19:59 +0900 Subject: [PATCH] =?UTF-8?q?docs(question):=200-W=20=EC=8B=A0=EC=84=A4=20?= =?UTF-8?q?=E2=80=94=20=EA=B0=99=EC=9D=80=20Ref=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=EC=9D=98=20=EC=9D=B4=EC=A4=91=20=EB=B0=B0=EC=B9=98=EA=B0=80=20?= =?UTF-8?q?=EB=AC=B4=EB=B0=A9=EB=B9=84=EC=9D=B8=20=EA=B0=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0-Z(Attribute 이름 소유권) 확인 중 사용자가 "Ref에도 같은 문제가 있냐"고 물어 손 트레이싱한 결과, 있고 막는 장치가 전혀 없음을 확인. 메커니즘은 0-Z와 반대 방향이라 별개 항목으로 분리 — Attribute는 두 소유자가 메모이즈된 키 때문에 한 자리로 수렴, Ref는 한 객체가 두 자리로 발산. RefLeafHandler의 relate가 (inst,k)별로만 있어 "이 Ref가 이미 다른 자리에 있다"를 원천적으로 못 봄. 증상: 두 번째 바인딩이 조용히 첫 번째를 덮고, 첫 번째 자리가 retract될 때 r:Set(nil)로 두 번째의 정당한 값까지 지움. Slot(claimOwner)/PreRef(_fired)는 정확히 이 클래스를 error로 막고 Tag는 겹침이 의도된 동작인데 Ref만 비어 있음 — 스파이크 19도 Tag/Attribute/Slot만 커버하고 Ref는 없음. 하강 diff 모델이 만든 회귀가 아니라 원래부터 있던 갭이라 0-Z와 독립이고 M0를 막지 않음 — "결정 대기" 절에 배치. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01KckSawrsJSJmDBcSojJPxZ --- .claude/question.md | 47 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/.claude/question.md b/.claude/question.md index 2fad4a7..3481505 100644 --- a/.claude/question.md +++ b/.claude/question.md @@ -69,6 +69,53 @@ Attribute가 직접 해야 함.** ## 결정 대기 — M0는 안 막음 +### 0-W. 같은 `Ref` 객체가 두 자리에 놓이는 걸 막을 것인가 (2026-08-13 열세 번째 세션 신설, 0-Z 확인 중 발견) + +**0-Z(Attribute)를 보다가 "Ref에도 같은 문제가 있냐"는 사용자 질문에서 +나온 것 — 있고, 막는 장치가 전혀 없음.** 단 메커니즘은 0-Z와 **반대 +방향**이라 별개 항목으로 분리: Attribute는 *두 소유자 → 한 자리*(이름별 +메모이즈된 키라 수렴), Ref는 *한 객체 → 두 자리*(발산). + +**손 트레이싱**(`base/ref-plan.md`의 `RefLeafHandler` 의사코드에 대입, +`Frame1 { Ref = r }` / `Frame2 { Ref = r }`): + +1. `process(inst1,"Ref",r)` → `relate[inst1]["Ref"]`가 nil → `r:Set(inst1)` +2. `process(inst2,"Ref",r)` → `relate[inst2]["Ref"]`도 nil(**다른 키**) → + `r:Set(inst2)` — inst1 바인딩이 **조용히 유실, 에러 없음** +3. inst1 자리가 retract → `hintValue(nil) ~= v(r)` → **`r:Set(nil)`** — + inst2가 정당하게 들고 있던 값을 지움(교차 오염) + +`relate`가 `(inst,k)`별로만 있어 "이 Ref가 이미 다른 자리에 있다"를 +원천적으로 못 봄. + +**형제 프리미티브 대조 — Ref만 비어 있음**: + +| | 공유 자원 | 방어 | 상태 | +|---|---|---|---| +| `Slot` | element | `claimOwner`/`claimOwnerAt` → 즉시 error(`Slot{a,a}`/`Frame{slot,slot}`) | 막힘 | +| `PreRef` | 자기 자신 | `_fired` → 재사용 시 error | 막힘 | +| `Tag` | 태그 이름 | 위치별 참조 카운트 — 겹침이 **의도된 동작**(합집합) | 설계상 정상 | +| `Attribute` | 이름 | 없음 | **0-Z** | +| **`Ref`** | 자기 자신 | **없음** | **이 항목** | + +특히 걸리는 두 가지: +- **`PreRef`는 정확히 이 재사용을 error로 막고**, 문서가 "`Slot:List`의 + `updateFn`처럼 반복 호출되는 자리에선 매번 새 `PreRef()`를 만들라"는 + 관용구까지 명시해뒀음 — **일반 `Ref`는 같은 자리에서 같은 실수를 해도 + 아무도 안 막음**(비대칭). +- 스파이크 `19`가 Tag/Attribute/Slot 소유권은 음성 대조군까지 넣어 + 검증하는데 **`Ref`만 커버가 없음**. + +**0-Z와의 관계 — 독립**: 이건 하강 diff 모델이 만든 회귀가 **아니라 +원래부터 있던 갭**(점유 체크는 같은 `(inst,k,index)`만 봤지, 서로 다른 +자리를 가로지르는 건 원래 안 봤음). 0-Z를 어떻게 정하든 별도 결정이고, +0-Z와 달리 **M0를 막지 않음** — 다만 사용자가 소유권 설계를 스케치할 때 +같이 보는 게 자연스러움. + +**선택지**: (a) `Slot`/`PreRef`와 같이 즉시 error(일관성 높음, `Relate` +하나로 Ref→현재 자리 추적), (b) UB로 두고 문서화만(현상 유지 — +단 증상이 "조용한 값 소실"이라 다른 UB보다 나쁨), (c) 마지막 쓰기 승리를 +정식 동작으로 인정(비권장, `Ref`의 "확정된 값 박스" 의미와 충돌). ### 0-A. `hintValue` 폐기 → process 하강 중 핸들러 비교 (2026-08-13 여섯 번째 세션, **Attribute 건 외 확정**)