fix: /code-review high 12건 — 9건 수정, 3건은 열린 설계 항목으로 승격
O절 커밋(c58c97a) 직후 돌린 리뷰에서 12건이 나왔고 전부 유효했다.
열린 항목으로 승격(임의로 정하지 않음):
- [M2 착수 전] 게이트가 유보했다 내보내는 emit이 어느 source를 싣는가.
확정된 setup은 (emit: () -> ()) -> (() -> ())라 양쪽 다 source를 안 받는데
에포크 수신 규칙은 전부 [source] 키로 판정한다 — 그대로면 blocker:Off()가
묶어둔 배치 emit이 하류에서 규칙 3으로 삼켜져 통지가 통째로 사라진다.
5라운드 M절이 이미 짚었는데 표면 확정 때 같이 안 닫힌 것. 후보 (a) emit(nil)
전체 확인 / (b) 유보 소스마다 emit(Blocker의 "정확히 1회"가 깨짐) /
(c) GateNode 자신을 source처럼 취급 — 권고는 (c). gate-plan.md 4번.
- [M3 착수 전] 두 맵의 초기값·:With 병합·재계산 시 갱신 범위.
규칙 1이 발행 소스 항목만 건드려 다중 소스 배치에서 같은 값을 두 번
계산하고, "상류에서 복사"는 순회가 앞당긴 지연분 상속 여부가 미정이라
새 노드가 통지를 삼킬 수 있다. state-epoch-plan.md §5 7번.
이에 따라 todos.md 00번의 "M2를 막는 설계 항목 없음"도 정정.
그 자리에서 수정:
- state-epoch-plan.md: §4/§5-2의 sourceList 잔재(코퍼스에 bk.sourceList라는
무관한 동명 식별자가 있어 오독 위험), §3의 "rawInvalid가 켜져도" 정정
- gate-plan.md: 배너가 부정하는 본문 두 문장을 같이 수정
- question.md 1번: Gate 이름 항목이 열린 채였던 것 해소로 갱신
- luau-test/STATUS.md: 05 이동이 반영 안 된 개수 3곳 + 같은 파일 안의
모순 문장("05가 다시 돌아왔다")
- comparison-fusion-vide.md: 배너 바로 위 본문이 배너와 어긋나던 것
- N절/README가 가리키던 research/ 옛 경로
처리 전량은 round5-followup.md의 P절. doc-check.py ERROR 0.
Co-authored-by: qwreey <me@qwreey.moe>
Claude-Session: https://claude.ai/code/session_01TiW21rnti9SbLgF6twtn6D
This commit is contained in:
parent
c58c97a877
commit
cb838d3172
9 changed files with 157 additions and 28 deletions
|
|
@ -24,7 +24,7 @@
|
||||||
| `base/` | 결정 완료 + 프로젝트 전체에 걸치는 컨텍스트 — plan/done 개념 없음, 계속 참조되는 배경지식. **항상 읽어야 하는** 배경지식만 여기 둠(다른 문서를 이해하는 데 전제되는 것) |
|
| `base/` | 결정 완료 + 프로젝트 전체에 걸치는 컨텍스트 — plan/done 개념 없음, 계속 참조되는 배경지식. **항상 읽어야 하는** 배경지식만 여기 둠(다른 문서를 이해하는 데 전제되는 것) |
|
||||||
| `reference/` | **[2026-08-07 신설]** 결정 자체가 아니라 다른 문서가 근거로 인용하는 온디맨드 참고 자료(v1 스냅샷, 프레임워크 비교 리서치) — "완료" 개념 없는 건 `base/`와 같지만, 항상 읽을 필요는 없고 해당 문서가 인용될 때만 열어보면 됨. `quadnomicon` 소재 후보가 많음 |
|
| `reference/` | **[2026-08-07 신설]** 결정 자체가 아니라 다른 문서가 근거로 인용하는 온디맨드 참고 자료(v1 스냅샷, 프레임워크 비교 리서치) — "완료" 개념 없는 건 `base/`와 같지만, 항상 읽을 필요는 없고 해당 문서가 인용될 때만 열어보면 됨. `quadnomicon` 소재 후보가 많음 |
|
||||||
| `research/` | 아직 착수 전, 사용자와 스코프/설계를 더 상의해야 함 |
|
| `research/` | 아직 착수 전, 사용자와 스코프/설계를 더 상의해야 함 |
|
||||||
| `qa-request/` | 원래 용도는 "구현 완료(코드/에이전트 검증까지 끝남) + 사용자 본인의 실기기(Roblox Studio) QA만 남음". **[2026-08-18 확장]** 구현 전에도 **사용자 심사 라운드의 산출물**을 여기 둠 — `pre-implementation-qa-round1.md`(1라운드: `base/` 확정 문서 전체를 문항으로 재확인받아 **"아니오"가 나온 항목만** 모은 결함 목록 + 신규 요구사항(`N-n`) + 부수 오탈자. **같은 날 전부 `base/`에 반영 완료**라 지금은 "무엇이 왜 틀렸었나"의 근거 기록이고, 지금 유효한 설계는 항상 `base/`가 소스. 아직 안 닫힌 것은 `question.md` 3번과 `.claude/todos.md` 00번이 소스), `pre-implementation-qa-round2.md`(2라운드: 확정 의사코드를 실제로 손으로 실행해보는 트레이싱 — **완료**, 발견된 크래시 `RC-1`(`recompute` 트리거 모델)도 같은 날 후속 세션에서 Blocker 게이팅 설계로 해결·반영까지 끝남, `archive/question-resolved.md`에 논의 요지 정리), `pre-implementation-qa-round3.md`(3라운드: `RC-1` 해법(Blocker 게이팅)이 실제로 `attachSlot`/`recompute`에 반영된 걸 손으로 트레이싱 — **완료**, `RC-3`/`RC-4`(`activateList`가 자기 Slot의 Blocker보다 먼저 실행되는 순서 문제)와 `bk.N` 수명주기 미정을 발견했다가 같은 세션에 사용자가 최초 분석 오류를 직접 정정하며 전부 해결·`base/` 반영까지 끝남, `archive/question-resolved.md`에 논의 요지 정리. `ROADMAP.md` M2가 M3의 `Blocker.luau`에 의존하게 된 마일스톤 순서 불일치는 각주로 반영, 마일스톤 재편 여부는 열려 있음). `pre-implementation-qa-round4.md`(4라운드: 사용자 요청으로 **`base/` 확정 전체를 "예가 나와야 정상인 문항"으로 다시 뽑은 전수 문항지** — **[2026-08-21] 완료·종결**. 1~3라운드와 달리 이 파일은 **문항지 원본 그대로 남긴다** — 처리 결과 전량이 `-followup.md`에 쌓였으므로 "아니오만 남기는 재편"은 안 하기로 함(같은 정보가 두 곳에 갈라지는 걸 피함). 문항 수/문서별 분포는 그 파일 자신이 소스). `pre-implementation-qa-round4-response.md`(사용자 회신 원문 — 이 라운드는 회신을 **별도 파일**로 받았다, 1~3라운드와 다른 점), `pre-implementation-qa-round4-followup.md`(**[2026-08-20 시작, 2026-08-21 종결]** 그 회신 처리 결과 — A~H 8개 절이 4차에 걸쳐 시간순으로 쌓였고 **마지막 H절이 최신이자 소스**. B·C절의 재질문/판단 대기 항목은 F·G절을 거쳐 **H절에서 전량 닫혔다**(`Detach` 보존 주체, `KeyGone`, `Owned`, `attachSlot` 분해). **열린 질문 없음**. **[2026-08-21 정정]** 여기 적혀 있던 "5라운드 문항지는 만들지 않는다"는 뒤집혔다 — 같은 날 사용자 요청으로 5라운드를 만들었다), `pre-implementation-qa-round5.md`(**[2026-08-21 신설·처리 완료]** 5라운드: 4라운드에서 "예"로 넘어간 자리는 건너뛰고 **(1) 4라운드에 문항이 아예 없던 영역**(`project-setup-plan.md`/`quad-types-plan.md`, 그리고 **문서가 아니라 실제 커밋된 M1 코드**), **(2) 4라운드 회신 이후 새로 확정된 것**(`Detach`/`_detached`/`KeyGone`/`Owned`/`attachSlot` 분해 등), **(3) 큰 문서의 심화**(예: `debounce-throttle-plan.md`)만 묻는다. 문항 수는 그 문서 자신이 소스), `pre-implementation-qa-round5-response.md`(사용자 회신 원문 — 4라운드와 같이 별도 파일), `pre-implementation-qa-round5-followup.md`(**[2026-08-21]** 그 회신 처리 결과 — 즉시 반영분 / 재질문 / 사용자 판단 필요 / 새로 만든 research 문서 둘(`gate-plan.md`·`state-epoch-plan.md`)까지. **처리 결과의 소스는 이 파일**). 다음 라운드가 필요해지면 라운드마다 파일을 새로 만들고 이름에 라운드 번호를 넣을 것 |
|
| `qa-request/` | 원래 용도는 "구현 완료(코드/에이전트 검증까지 끝남) + 사용자 본인의 실기기(Roblox Studio) QA만 남음". **[2026-08-18 확장]** 구현 전에도 **사용자 심사 라운드의 산출물**을 여기 둠 — `pre-implementation-qa-round1.md`(1라운드: `base/` 확정 문서 전체를 문항으로 재확인받아 **"아니오"가 나온 항목만** 모은 결함 목록 + 신규 요구사항(`N-n`) + 부수 오탈자. **같은 날 전부 `base/`에 반영 완료**라 지금은 "무엇이 왜 틀렸었나"의 근거 기록이고, 지금 유효한 설계는 항상 `base/`가 소스. 아직 안 닫힌 것은 `question.md` 3번과 `.claude/todos.md` 00번이 소스), `pre-implementation-qa-round2.md`(2라운드: 확정 의사코드를 실제로 손으로 실행해보는 트레이싱 — **완료**, 발견된 크래시 `RC-1`(`recompute` 트리거 모델)도 같은 날 후속 세션에서 Blocker 게이팅 설계로 해결·반영까지 끝남, `archive/question-resolved.md`에 논의 요지 정리), `pre-implementation-qa-round3.md`(3라운드: `RC-1` 해법(Blocker 게이팅)이 실제로 `attachSlot`/`recompute`에 반영된 걸 손으로 트레이싱 — **완료**, `RC-3`/`RC-4`(`activateList`가 자기 Slot의 Blocker보다 먼저 실행되는 순서 문제)와 `bk.N` 수명주기 미정을 발견했다가 같은 세션에 사용자가 최초 분석 오류를 직접 정정하며 전부 해결·`base/` 반영까지 끝남, `archive/question-resolved.md`에 논의 요지 정리. `ROADMAP.md` M2가 M3의 `Blocker.luau`에 의존하게 된 마일스톤 순서 불일치는 각주로 반영, 마일스톤 재편 여부는 열려 있음). `pre-implementation-qa-round4.md`(4라운드: 사용자 요청으로 **`base/` 확정 전체를 "예가 나와야 정상인 문항"으로 다시 뽑은 전수 문항지** — **[2026-08-21] 완료·종결**. 1~3라운드와 달리 이 파일은 **문항지 원본 그대로 남긴다** — 처리 결과 전량이 `-followup.md`에 쌓였으므로 "아니오만 남기는 재편"은 안 하기로 함(같은 정보가 두 곳에 갈라지는 걸 피함). 문항 수/문서별 분포는 그 파일 자신이 소스). `pre-implementation-qa-round4-response.md`(사용자 회신 원문 — 이 라운드는 회신을 **별도 파일**로 받았다, 1~3라운드와 다른 점), `pre-implementation-qa-round4-followup.md`(**[2026-08-20 시작, 2026-08-21 종결]** 그 회신 처리 결과 — A~H 8개 절이 4차에 걸쳐 시간순으로 쌓였고 **마지막 H절이 최신이자 소스**. B·C절의 재질문/판단 대기 항목은 F·G절을 거쳐 **H절에서 전량 닫혔다**(`Detach` 보존 주체, `KeyGone`, `Owned`, `attachSlot` 분해). **열린 질문 없음**. **[2026-08-21 정정]** 여기 적혀 있던 "5라운드 문항지는 만들지 않는다"는 뒤집혔다 — 같은 날 사용자 요청으로 5라운드를 만들었다), `pre-implementation-qa-round5.md`(**[2026-08-21 신설·처리 완료]** 5라운드: 4라운드에서 "예"로 넘어간 자리는 건너뛰고 **(1) 4라운드에 문항이 아예 없던 영역**(`project-setup-plan.md`/`quad-types-plan.md`, 그리고 **문서가 아니라 실제 커밋된 M1 코드**), **(2) 4라운드 회신 이후 새로 확정된 것**(`Detach`/`_detached`/`KeyGone`/`Owned`/`attachSlot` 분해 등), **(3) 큰 문서의 심화**(예: `debounce-throttle-plan.md`)만 묻는다. 문항 수는 그 문서 자신이 소스), `pre-implementation-qa-round5-response.md`(사용자 회신 원문 — 4라운드와 같이 별도 파일), `pre-implementation-qa-round5-followup.md`(**[2026-08-21]** 그 회신 처리 결과 — 즉시 반영분 / 재질문 / 사용자 판단 필요 / 새로 만든 문서 둘(`gate-plan.md`·`state-epoch-plan.md` — 같은 날 확정되며 `base/`로 승격)까지. **처리 결과의 소스는 이 파일**). 다음 라운드가 필요해지면 라운드마다 파일을 새로 만들고 이름에 라운드 번호를 넣을 것 |
|
||||||
| `archive/` | 완료 + 사용자가 실사용/실기기로 직접 검증까지 마침 (구현 대상). **[2026-08-06 확장]** 완전히 뒤집힌 설계 결정을 원문+역전 이유+diff와 함께 보존하는 용도로도 사용(제목 `[역전됨]` — 한 번 확정했다가 뒤집힌 것) — 더 이상 능동적으로 참고 안 해도 되지만(토큰 낭비 방지 위해 `base/`/`research/`에서 뺌) `quadnomicon` 소재로는 나중에 쓸 수 있음. **[2026-08-07 확장]** 후보였다가 채택 안 된 것(확정한 적 없이 검토 후 기각)도 같은 방식으로 보존, 제목은 구분을 위해 `[기각됨]` — `[역전됨]`과 의미가 다르므로 혼동하지 말 것. **[2026-08-07 세 번째 확장]** 설계 반전/기각과 별개로, 에이전트가 문서 작성 중 스스로 낸 개념 혼동을 정정한 이력은 `[에이전트 실수]` 태그로 `agent-mistake.md` 하나에 모음(`.claude/session-summary.md`/`session/` 로그와의 중복 방지) |
|
| `archive/` | 완료 + 사용자가 실사용/실기기로 직접 검증까지 마침 (구현 대상). **[2026-08-06 확장]** 완전히 뒤집힌 설계 결정을 원문+역전 이유+diff와 함께 보존하는 용도로도 사용(제목 `[역전됨]` — 한 번 확정했다가 뒤집힌 것) — 더 이상 능동적으로 참고 안 해도 되지만(토큰 낭비 방지 위해 `base/`/`research/`에서 뺌) `quadnomicon` 소재로는 나중에 쓸 수 있음. **[2026-08-07 확장]** 후보였다가 채택 안 된 것(확정한 적 없이 검토 후 기각)도 같은 방식으로 보존, 제목은 구분을 위해 `[기각됨]` — `[역전됨]`과 의미가 다르므로 혼동하지 말 것. **[2026-08-07 세 번째 확장]** 설계 반전/기각과 별개로, 에이전트가 문서 작성 중 스스로 낸 개념 혼동을 정정한 이력은 `[에이전트 실수]` 태그로 `agent-mistake.md` 하나에 모음(`.claude/session-summary.md`/`session/` 로그와의 중복 방지) |
|
||||||
| `feedback/` | 실사용 피드백을 정리한 긴 로그 — **[2026-08-19 기준] 폴더 자체가 아직 없음**(M0/M1 스캐폴딩만으론 안 생기고 실제로 렌더링해보고 쓰는 단계부터, 첫 피드백이 생길 때 만들면 됨). `qa-request/`는 **[2026-08-18] 더 이상 비어 있지 않음**(구현 전 QA 1라운드 산출물이 들어감) — 여긴 아직 폴더도 없음 |
|
| `feedback/` | 실사용 피드백을 정리한 긴 로그 — **[2026-08-19 기준] 폴더 자체가 아직 없음**(M0/M1 스캐폴딩만으론 안 생기고 실제로 렌더링해보고 쓰는 단계부터, 첫 피드백이 생길 때 만들면 됨). `qa-request/`는 **[2026-08-18] 더 이상 비어 있지 않음**(구현 전 QA 1라운드 산출물이 들어감) — 여긴 아직 폴더도 없음 |
|
||||||
| `luau-test/` | **[2026-08-09 신설]** `base/` 확정 사항 중 "추론만으로 확정하고 실제 Luau로 부딪혀본 적 없는 것"(M0 스파이크 대상)을 `luau`/`luau-analyze`/`luau-lsp`/Roblox Studio로 사용자가 직접 돌려볼 독립 실행 스크립트 모음. **[2026-08-13 여섯 번째 세션, 첫 실측]** `luau`/`luau-analyze` 바이너리가 생겨 처음으로 실제 실행 — **런타임 12개 전원 통과**, 타입 쪽에서 `:Compute(fn)` lazy 핸들 계약이 Luau 추론과 충돌하는 게 드러남(당시 `question.md` 0-Y). **[2026-08-13 열세 번째 세션]** 그 0-Y가 해소되며 `review-required/`가 **비었음** — 계약은 유지 확정, 남은 건 Luau 자체 한계라 `base/typing-limits.md`가 담당. **`STATUS.md`가 상태의 소스**(pass / 사람 결정 필요 / 스파이크 깨짐 / 미실행 분류 — 사람이 먼저 볼 것만 위에), `luau-test/README.md`는 각 파일의 검증 의도·배경, 실행 결과 상세는 `audit/luau-test-first-run-2026-08-13.md` |
|
| `luau-test/` | **[2026-08-09 신설]** `base/` 확정 사항 중 "추론만으로 확정하고 실제 Luau로 부딪혀본 적 없는 것"(M0 스파이크 대상)을 `luau`/`luau-analyze`/`luau-lsp`/Roblox Studio로 사용자가 직접 돌려볼 독립 실행 스크립트 모음. **[2026-08-13 여섯 번째 세션, 첫 실측]** `luau`/`luau-analyze` 바이너리가 생겨 처음으로 실제 실행 — **런타임 12개 전원 통과**, 타입 쪽에서 `:Compute(fn)` lazy 핸들 계약이 Luau 추론과 충돌하는 게 드러남(당시 `question.md` 0-Y). **[2026-08-13 열세 번째 세션]** 그 0-Y가 해소되며 `review-required/`가 **비었음** — 계약은 유지 확정, 남은 건 Luau 자체 한계라 `base/typing-limits.md`가 담당. **`STATUS.md`가 상태의 소스**(pass / 사람 결정 필요 / 스파이크 깨짐 / 미실행 분류 — 사람이 먼저 볼 것만 위에), `luau-test/README.md`는 각 파일의 검증 의도·배경, 실행 결과 상세는 `audit/luau-test-first-run-2026-08-13.md` |
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@
|
||||||
없이 `state:Gate( (emit) -> ()->() )` 처럼 선언되고 마치 Compute 처럼
|
없이 `state:Gate( (emit) -> ()->() )` 처럼 선언되고 마치 Compute 처럼
|
||||||
GateNode(ComputeNode 처럼) 생성된다 그리고 Blocker 는 해당 내부 배선을 따른다
|
GateNode(ComputeNode 처럼) 생성된다 그리고 Blocker 는 해당 내부 배선을 따른다
|
||||||
← 동의합니다 해당 방법대로 확정하면 됩니다."* 구현은 **M2**("게이팅 먼저"
|
← 동의합니다 해당 방법대로 확정하면 됩니다."* 구현은 **M2**("게이팅 먼저"
|
||||||
결정, `ROADMAP.md`). **남은 것은 생명주기·재진입 계약과 M2 범위뿐**(아래
|
결정, `ROADMAP.md`). **남은 것은 아래 "아직 안 정한 것"** — 그중 **4번(유보된
|
||||||
"아직 안 정한 것").
|
emit이 싣는 source)은 `setup` 시그니처를 바꿀 수 있어 M2 착수 전 판단이
|
||||||
|
필요하다**(2026-08-21 `/code-review high` 발견).
|
||||||
|
|
||||||
**⚠️ 처음 방향이 한 번 바뀌었다.** 신설 당시엔 *"공용 `Gate` 프리미티브를 꺼내고
|
**⚠️ 처음 방향이 한 번 바뀌었다.** 신설 당시엔 *"공용 `Gate` 프리미티브를 꺼내고
|
||||||
`Blocker`가 그걸 컴포지션한다"*였는데, 확정된 형태는 **프리미티브를 따로 안
|
`Blocker`가 그걸 컴포지션한다"*였는데, 확정된 형태는 **프리미티브를 따로 안
|
||||||
|
|
@ -15,8 +16,9 @@ GateNode(ComputeNode 처럼) 생성된다 그리고 Blocker 는 해당 내부
|
||||||
표면을 주기도 하구요."* 아래 본문 중 "공개 프리미티브로 꺼낸다"류 서술은 그
|
표면을 주기도 하구요."* 아래 본문 중 "공개 프리미티브로 꺼낸다"류 서술은 그
|
||||||
이전 시점 표현이니 이 배너 기준으로 읽을 것.
|
이전 시점 표현이니 이 배너 기준으로 읽을 것.
|
||||||
|
|
||||||
**한 줄**: `Blocker`가 쓰는 "게이티드 State 노드"를 한 겹 일반화해서, **상류
|
**한 줄**: `Blocker`가 쓰던 "게이티드 State 노드"를 한 겹 일반화해서, **상류
|
||||||
emit을 가로채 내려보낼지 말지를 정책이 정하는** 노드를 공개 프리미티브로 꺼낸다.
|
emit을 가로채 내려보낼지 말지를 정책이 정하는** 노드를 **`state:Gate(setup)`
|
||||||
|
공개 메소드**로 낸다(탑레벨 생성자가 아니라 — 위 배너 참고).
|
||||||
`Blocker`/`Debounce`/`Throttle`이 그 위에 얹히는 서로 다른 정책이 된다.
|
`Blocker`/`Debounce`/`Throttle`이 그 위에 얹히는 서로 다른 정책이 된다.
|
||||||
|
|
||||||
## 왜 지금인가 — 두 갈래가 같은 자리를 가리켰다
|
## 왜 지금인가 — 두 갈래가 같은 자리를 가리켰다
|
||||||
|
|
@ -37,7 +39,8 @@ emit을 가로채 내려보낼지 말지를 정책이 정하는** 노드를 공
|
||||||
하고, 바깥에서 관측만 해서는 순서를 보장할 수 없다.
|
하고, 바깥에서 관측만 해서는 순서를 보장할 수 없다.
|
||||||
|
|
||||||
**공개 여부**: 사용자 판단 — *"이 API가 비공개일 이유는 없어보인다."* 즉
|
**공개 여부**: 사용자 판단 — *"이 API가 비공개일 이유는 없어보인다."* 즉
|
||||||
내부 배관이 아니라 공개 프리미티브로 낸다.
|
내부 배관이 아니라 **공개 표면**으로 낸다. **[2026-08-21]** 다만 그 표면은
|
||||||
|
탑레벨 프리미티브가 아니라 **State 메소드 `:Gate`** 다(아래 2번).
|
||||||
|
|
||||||
## 제안된 모양 (사용자 스케치 그대로)
|
## 제안된 모양 (사용자 스케치 그대로)
|
||||||
|
|
||||||
|
|
@ -113,20 +116,47 @@ end)
|
||||||
`base/state-epoch-plan.md`가 이 계약에 **의존**한다 — 그 문서 §5의 3번이
|
`base/state-epoch-plan.md`가 이 계약에 **의존**한다 — 그 문서 §5의 3번이
|
||||||
"게이트를 에포크 경계로 만드는" 대안을 기각한 이유가 정확히 이 계약을
|
"게이트를 에포크 경계로 만드는" 대안을 기각한 이유가 정확히 이 계약을
|
||||||
뒤집지 않기 위해서다.
|
뒤집지 않기 위해서다.
|
||||||
4. **생명주기.** 게이트 노드가 잡는 자원(타이머/플래그)이 언제 죽는가 —
|
4. **⭐⭐ [2026-08-21 신설 — M2 표면에 영향] 게이트가 유보했다 내보내는 emit은
|
||||||
|
어느 source를 싣는가.** 확정된 `setup`은 `(emit: () -> ()) -> (() -> ())`로
|
||||||
|
**양쪽 다 source를 안 받는다.** 그런데 `base/state-epoch-plan.md` §2의 수신
|
||||||
|
규칙 셋은 전부 `[source]` 키로 판정한다 — 게이트가 `A:Set(); Z:Set()`을
|
||||||
|
묶어뒀다가 `blocker:Off()`로 한 번에 내보내면, 그 emit이 **어떤 source도
|
||||||
|
지목하지 못한 채** 하류에 도착한다. 그러면 하류는 3번 규칙으로 **삼켜버리고
|
||||||
|
배치 통지가 통째로 사라진다.** 5라운드 M절이 이미 *"`nil` 규약만 `Gate`
|
||||||
|
설계와 같이 확정하면 된다"*고 짚어뒀는데 표면 확정 때 같이 안 닫혔다
|
||||||
|
(2026-08-21 `/code-review high` 발견).
|
||||||
|
후보 셋 — 어느 쪽이든 `setup`/`emit` 시그니처가 바뀐다:
|
||||||
|
- **(a) `emit(nil)` = 전체 확인.** 받는 쪽이 `sourceCountMap` 전체를 훑는다.
|
||||||
|
M절의 원안. 비용은 2~4칸 순회라 작지만, "판정은 O(1)"이라는 §3 서술에
|
||||||
|
예외가 하나 생긴다.
|
||||||
|
- **(b) 게이트가 유보한 source 집합을 기억했다가 해제 때 각각 emit.**
|
||||||
|
판정은 O(1) 그대로. 대신 유보된 소스가 N개면 하류가 **N번** 통지받아
|
||||||
|
`Blocker`의 "정확히 1회"가 깨진다 — `Blocker` 목적과 정면 충돌이라
|
||||||
|
그대로는 못 쓴다.
|
||||||
|
- **(c) `GateNode` 자신을 source처럼 취급.** 해제 emit이 `emit(self)`를
|
||||||
|
싣고 게이트가 자기 count를 하나 올린다. 하류는 정확히 1회 통지받고
|
||||||
|
판정도 O(1). 대신 하류의 맵에 루트 Source가 아닌 노드가 섞이므로 §2의
|
||||||
|
"루트 Source들의 에포크"라는 서술을 넓혀야 한다.
|
||||||
|
**에이전트 권고는 (c)** — `Blocker` 계약("정확히 1회")과 O(1) 판정을 둘 다
|
||||||
|
지키는 유일한 안이고, "게이트가 하류에게는 새 원천처럼 보인다"는 게 게이트의
|
||||||
|
실제 의미와도 맞는다. 단 `Get()` 계약(통지만 막음)은 그대로 유지된다 —
|
||||||
|
§5-3이 기각한 "게이트를 **에포크 경계**로 만들기"와는 다르다. 그건 하류가
|
||||||
|
루트 Source를 **못 보게** 만드는 안이고, (c)는 루트 Source를 그대로 보면서
|
||||||
|
게이트를 **하나 더** 얹는 것뿐이다.
|
||||||
|
5. **생명주기.** 게이트 노드가 잡는 자원(타이머/플래그)이 언제 죽는가 —
|
||||||
지금 설계대로면 다운스트림이 다 죽으면 GC(팩토리는 weak 추적,
|
지금 설계대로면 다운스트림이 다 죽으면 GC(팩토리는 weak 추적,
|
||||||
`debounce-throttle-plan.md` 5-4). `Gate` 자체에 `Flush`/`Cancel` 같은 표면을
|
`debounce-throttle-plan.md` 5-4). `Gate` 자체에 `Flush`/`Cancel` 같은 표면을
|
||||||
둘지, 그건 정책(Debounce)만의 것으로 둘지.
|
둘지, 그건 정책(Debounce)만의 것으로 둘지.
|
||||||
5. **재진입.** `Blocker`의 "재진입 의도적 미지원"(`blocker-plan.md`)이 `Gate`
|
6. **재진입.** `Blocker`의 "재진입 의도적 미지원"(`blocker-plan.md`)이 `Gate`
|
||||||
레벨의 계약으로 올라가는지 — 즉 `onUpstreamEmit` 안에서 같은 게이트의
|
레벨의 계약으로 올라가는지 — 즉 `onUpstreamEmit` 안에서 같은 게이트의
|
||||||
`emit()`을 재귀적으로 부르는 경우.
|
`emit()`을 재귀적으로 부르는 경우.
|
||||||
6. **⭐ 소비자가 하나 더 있다 — `Effect(fn, ...deps)`의 최초 1회 억제.**
|
7. **⭐ 소비자가 하나 더 있다 — `Effect(fn, ...deps)`의 최초 1회 억제.**
|
||||||
2026-08-21 5라운드 `C-6`에서 확정된 다중 의존성 `Effect`는, 의존성마다 구독을
|
2026-08-21 5라운드 `C-6`에서 확정된 다중 의존성 `Effect`는, 의존성마다 구독을
|
||||||
걸면 각 구독의 "등록 즉시 1회 실행"이 N번 발화하므로 **설치 구간 동안 발화를
|
걸면 각 구독의 "등록 즉시 1회 실행"이 N번 발화하므로 **설치 구간 동안 발화를
|
||||||
눌러뒀다가 마지막에 한 번만 실행**해야 한다(`base/effect-plan.md`의 그 절).
|
눌러뒀다가 마지막에 한 번만 실행**해야 한다(`base/effect-plan.md`의 그 절).
|
||||||
즉 `Gate`(또는 `Blocker`의 직접 사용)가 **"설치 구간을 감싸 최초 발화를 한
|
즉 `Gate`(또는 `Blocker`의 직접 사용)가 **"설치 구간을 감싸 최초 발화를 한
|
||||||
번으로 접는" 용례까지 커버해야** 한다 — 설계할 때 이 소비자를 같이 볼 것.
|
번으로 접는" 용례까지 커버해야** 한다 — 설계할 때 이 소비자를 같이 볼 것.
|
||||||
7. **M2 범위.** M2에 `Gate`만 넣고 `Blocker`는 M3에 그대로 둘지, 아니면
|
8. **M2 범위.** M2에 `Gate`만 넣고 `Blocker`는 M3에 그대로 둘지, 아니면
|
||||||
`Blocker`까지 같이 앞당길지. `Dispatch.drive`의 배치 등록이 실제로 쓰는 건
|
`Blocker`까지 같이 앞당길지. `Dispatch.drive`의 배치 등록이 실제로 쓰는 건
|
||||||
`blocker:On()`/`OffWithoutEmit()`/`IsOn()`이므로(배치 게이팅 절), **최소한
|
`blocker:On()`/`OffWithoutEmit()`/`IsOn()`이므로(배치 게이팅 절), **최소한
|
||||||
그 세 메서드가 도는 형태까지는 M2에 필요**하다.
|
그 세 메서드가 도는 형태까지는 M2에 필요**하다.
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,10 @@ State
|
||||||
것**이다 — 옛 근거("전파 시점에 갱신된 count가 캐시를 신선한 것으로 오인시킨다")는
|
것**이다 — 옛 근거("전파 시점에 갱신된 count가 캐시를 신선한 것으로 오인시킨다")는
|
||||||
여전히 틀렸고, 지금 근거는 **순회가 값과 통지를 비대칭으로 앞당긴다**는 것이다.
|
여전히 틀렸고, 지금 근거는 **순회가 값과 통지를 비대칭으로 앞당긴다**는 것이다.
|
||||||
|
|
||||||
|
**⚠️ [2026-08-21 `/code-review high`] 아래 희소 구현 메모는 "둘 다 상류에서
|
||||||
|
복사"와 그대로는 안 맞는다** — 아래 §5의 7번이 소스. 두 맵을 명시적으로 다
|
||||||
|
들고 시작하는 게 안전하고, 희소화는 그 규칙이 정해진 뒤 얹을 것.
|
||||||
|
|
||||||
**구현 메모 — `sourceEmitMap`은 희소 테이블로 두면 된다.** emit이 count를 안
|
**구현 메모 — `sourceEmitMap`은 희소 테이블로 두면 된다.** emit이 count를 안
|
||||||
싣고 받는 쪽이 라이브로 읽으므로, 이 테이블에 실제로 필요한 정보는 **"순회가
|
싣고 받는 쪽이 라이브로 읽으므로, 이 테이블에 실제로 필요한 정보는 **"순회가
|
||||||
앞질러 흡수해서 아직 안 던진 소스가 무엇인가"** 뿐이다. 평상시엔 비어 있고
|
앞질러 흡수해서 아직 안 던진 소스가 무엇인가"** 뿐이다. 평상시엔 비어 있고
|
||||||
|
|
@ -110,8 +114,10 @@ State
|
||||||
아직 안 왔어도 스스로 재계산**한다. 그래서 `D`는 항상 `(B_new, C_new)`를
|
아직 안 왔어도 스스로 재계산**한다. 그래서 `D`는 항상 `(B_new, C_new)`를
|
||||||
얻는다. **`Get()`이 "지금 이 순간의 일관된 값"을 반환한다는 보장이 처음으로
|
얻는다. **`Get()`이 "지금 이 순간의 일관된 값"을 반환한다는 보장이 처음으로
|
||||||
성립**한다.
|
성립**한다.
|
||||||
- **고쳐진다 — 중복 재계산.** 뒤늦게 `C` 쪽 신호가 도착해 `rawInvalid`가 켜져도,
|
- **고쳐진다 — 중복 재계산.** 뒤늦게 `C` 쪽 신호가 도착해도 `sourceCountMap`이
|
||||||
count 비교가 "이미 최신"이라 재계산이 안 일어난다.
|
"이미 최신"이라 `rawInvalid`가 켜지지 않고, 따라서 재계산도 안 일어난다
|
||||||
|
(§2의 2번/3번 규칙 — **[2026-08-21 정정]** 여기 "`rawInvalid`가 켜져도"라고
|
||||||
|
적혀 있었으나 §2 규칙상 켜지지 않는다).
|
||||||
- **⭐ [2026-08-21 확정] 중복 *통지*도 같이 접는다 — `source-state-plan.md`가
|
- **⭐ [2026-08-21 확정] 중복 *통지*도 같이 접는다 — `source-state-plan.md`가
|
||||||
"접지 않는다"고 확정해뒀던 것의 역전이다**(역전 원문은
|
"접지 않는다"고 확정해뒀던 것의 역전이다**(역전 원문은
|
||||||
`archive/always-propagate-no-dedup-superseded.md`). 처음엔 "값만
|
`archive/always-propagate-no-dedup-superseded.md`). 처음엔 "값만
|
||||||
|
|
@ -146,7 +152,7 @@ State
|
||||||
사용자 추산(*"해시 for은 이미 빠르고, Source가 … 수 자체가 적다. 2~4개에 대해
|
사용자 추산(*"해시 for은 이미 빠르고, Source가 … 수 자체가 적다. 2~4개에 대해
|
||||||
인덱싱 하는 정도"*)에 동의한다. 덧붙일 것 둘:
|
인덱싱 하는 정도"*)에 동의한다. 덧붙일 것 둘:
|
||||||
|
|
||||||
- `sourceList`의 크기는 **그 노드 상류에 있는 서로 다른 루트 Source의 수**다.
|
- 두 맵의 크기는 **그 노드 상류에 있는 서로 다른 루트 Source의 수**다.
|
||||||
체인이 길어져도 안 늘고, `:With`로 합류할 때만 는다. UI 파생값에서 이 수가
|
체인이 길어져도 안 늘고, `:With`로 합류할 때만 는다. UI 파생값에서 이 수가
|
||||||
큰 경우는 드물다.
|
큰 경우는 드물다.
|
||||||
- **[2026-08-21 정정]** 순회 조건이 뒤집혔으므로 비용 구도도 뒤집힌다 —
|
- **[2026-08-21 정정]** 순회 조건이 뒤집혔으므로 비용 구도도 뒤집힌다 —
|
||||||
|
|
@ -164,7 +170,7 @@ State
|
||||||
이전과 다른게 없다고 생각한다."* — 선언 안 한 Source를 클로저로 읽는 건
|
이전과 다른게 없다고 생각한다."* — 선언 안 한 Source를 클로저로 읽는 건
|
||||||
**지금 모델에서도 똑같이 stale**이고 이 변경이 악화시키는 게 없으므로,
|
**지금 모델에서도 똑같이 stale**이고 이 변경이 악화시키는 게 없으므로,
|
||||||
새 조항 없이 기존 "의존성은 선언한다"는 관례 그대로 둔다.
|
새 조항 없이 기존 "의존성은 선언한다"는 관례 그대로 둔다.
|
||||||
2. **동적 의존성.** 조건에 따라 다른 상류를 읽는 계산이면 `sourceList`가
|
2. **동적 의존성.** 조건에 따라 다른 상류를 읽는 계산이면 `sourceCountMap`이
|
||||||
보수적 상위집합이 된다 — 틀리진 않고 재계산이 조금 더 잦아질 뿐이다.
|
보수적 상위집합이 된다 — 틀리진 않고 재계산이 조금 더 잦아질 뿐이다.
|
||||||
그대로 감수할지 확인.
|
그대로 감수할지 확인.
|
||||||
3. **[2026-08-21 해소] 순회가 발견한 변경을 어떻게 처분하는가 — 두 테이블로
|
3. **[2026-08-21 해소] 순회가 발견한 변경을 어떻게 처분하는가 — 두 테이블로
|
||||||
|
|
@ -217,6 +223,29 @@ State
|
||||||
`base/source-state-plan.md`의 전파 모델 절은 **채택과 함께 그렇게 다시
|
`base/source-state-plan.md`의 전파 모델 절은 **채택과 함께 그렇게 다시
|
||||||
썼다.**
|
썼다.**
|
||||||
|
|
||||||
|
7. **⭐ [2026-08-21 신설, `/code-review high`] 두 맵의 초기값·병합·재계산 시
|
||||||
|
갱신 범위 — M3 착수 전 필요.** §2가 "둘 다 상류에서 복사되고 `:With`에서
|
||||||
|
합쳐진다"고만 적고 세 자리를 안 정했다:
|
||||||
|
- **(a) 재계산이 끝났을 때 `sourceCountMap`을 어디까지 갱신하나.** §2는
|
||||||
|
`rawInvalid = false`만 말한다. 그런데 규칙 1은 **발행 소스 항목만**
|
||||||
|
건드리므로, `D`가 `A`,`Z`에 의존하고 `A:Set(); Z:Set()`이 연달아 오면 —
|
||||||
|
`A`의 emit으로 재계산된 `D`의 값은 **이미 `Z`의 새 값을 포함**하는데
|
||||||
|
`sourceCountMap[Z]`는 옛 값이라, `Z`의 emit이 규칙 1에 걸려 **같은 값을
|
||||||
|
또 계산**한다. **에이전트 권고: 재계산은 자기가 실제로 읽은 상류 전부에
|
||||||
|
대해 `sourceCountMap`을 갱신한다**(맵의 뜻이 "내 값이 이 소스에 대해
|
||||||
|
최신인가"이므로 이게 직독이다). 그러면 `Z`의 emit은 규칙 2로 떨어져
|
||||||
|
**통지는 나가되 재계산은 안 한다** — 통지가 나가는 건 옳다, 하류는 아직
|
||||||
|
`Z`의 에포크를 못 봤으므로.
|
||||||
|
- **(b) 새 노드가 생길 때 두 맵의 초기값.** "상류에서 복사"를 문자 그대로
|
||||||
|
하면, 순회로 `sourceCountMap`만 앞당겨진 상류에서 파생된 새 노드가 **그
|
||||||
|
지연분까지 상속**해야 규칙 2가 성립한다. **에이전트 권고: 복사가 아니라
|
||||||
|
"첫 재계산 때 자기가 읽은 상류들의 맵을 합쳐 구성"** — 그러면 새 노드는
|
||||||
|
`sourceEmitMap == sourceCountMap`인 깨끗한 상태로 시작하고 (a)와도 맞물린다.
|
||||||
|
다만 이건 사용자 원안의 "상류에서 복사" 표현을 바꾸는 것이라 **확인 필요.**
|
||||||
|
- **(c) `:With` 병합에서 두 상류가 같은 소스에 다른 count를 들고 있을 때.**
|
||||||
|
(b)를 택하면 자동으로 "그때의 라이브 count"로 통일돼 문제가 사라진다.
|
||||||
|
(b)를 안 택하면 명시 규칙이 필요하다(더 큰 쪽? 더 작은 쪽?).
|
||||||
|
|
||||||
## 6. 곁가지 — 폴링용 sugar
|
## 6. 곁가지 — 폴링용 sugar
|
||||||
|
|
||||||
사용자 제안: *"폴링을 위해서는 Apply(Realtime()) 같은 슈거를 주면 된다.
|
사용자 제안: *"폴링을 위해서는 Apply(Realtime()) 같은 슈거를 주면 된다.
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,9 @@
|
||||||
| 폴더 | 뜻 | 개수 | 누가 처리 |
|
| 폴더 | 뜻 | 개수 | 누가 처리 |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `review-required/` | **설계가 걸림 — 사람 결정 필요** | **0** | ⭐ 사용자 |
|
| `review-required/` | **설계가 걸림 — 사람 결정 필요** | **0** | ⭐ 사용자 |
|
||||||
| `rewrite-required/` | 스파이크가 낡음(코드가 깨졌거나, 설계가 바뀌어 옛 모델을 검증 중) | 4 | 에이전트 |
|
| `rewrite-required/` | 스파이크가 낡음(코드가 깨졌거나, 설계가 바뀌어 옛 모델을 검증 중) | 6 | 에이전트 |
|
||||||
| `not-run/` | 이 환경에서 못 돌림(Studio 전용) | 0(+헬퍼 1) | 사용자 or MCP 연결 후 에이전트 |
|
| `not-run/` | 이 환경에서 못 돌림(Studio 전용) | 0(+헬퍼 1) | 사용자 or MCP 연결 후 에이전트 |
|
||||||
| `done/` | 통과 or 판정 끝, 더 할 일 없음 | 19 | — |
|
| `done/` | 통과 or 판정 끝, 더 할 일 없음 | 17 | — |
|
||||||
|
|
||||||
**폴더를 옮기는 게 곧 상태 갱신** — 스파이크를 고치거나 돌렸으면 파일을
|
**폴더를 옮기는 게 곧 상태 갱신** — 스파이크를 고치거나 돌렸으면 파일을
|
||||||
해당 폴더로 `git mv`하고 아래 표의 줄도 같이 옮길 것. 파일별 "무엇을 왜
|
해당 폴더로 `git mv`하고 아래 표의 줄도 같이 옮길 것. 파일별 "무엇을 왜
|
||||||
|
|
@ -104,13 +104,14 @@
|
||||||
|---|---|
|
|---|---|
|
||||||
| `gc-trigger-helper.server.luau` | 스파이크가 아니라 **헬퍼** — Studio에 `collectgarbage()`가 없어서 GC를 강제 트리거하는 기법. `10`을 돌릴 때 같이 씀 |
|
| `gc-trigger-helper.server.luau` | 스파이크가 아니라 **헬퍼** — Studio에 `collectgarbage()`가 없어서 GC를 강제 트리거하는 기법. `10`을 돌릴 때 같이 씀 |
|
||||||
|
|
||||||
## ✅ `done/` — 통과 or 판정 끝 (18건)
|
## ✅ `done/` — 통과 or 판정 끝 (17건)
|
||||||
|
|
||||||
**런타임 13개 전원 통과**(crash 0 / FAIL 0 — **[2026-08-21]** `01`이
|
**런타임 12개 전원 통과**(crash 0 / FAIL 0 — **[2026-08-21]** `01`과 `05`가
|
||||||
`rewrite-required/`로 나가며 14 → 13) — **[열네 번째 세션] `04`/`19`는
|
`rewrite-required/`로 나가며 14 → 12) — **[열네 번째 세션] `04`/`19`는
|
||||||
검증 대상 설계가 바뀌어 `rewrite-required/`로 이동했고, [2026-08-19]
|
검증 대상 설계가 바뀌어 `rewrite-required/`로 이동했고, [2026-08-19]
|
||||||
`05`는 현행 모델로 재작성해 다시 여기로 돌아왔고, 신규 `22`(구 `13`
|
`05`는 현행 모델로 재작성해 잠시 돌아왔다가 **[2026-08-21] 소스 에포크
|
||||||
런타임 절반, PostRef까지 확장)가 합류**:
|
채택으로 다시 나갔으며**, 신규 `22`(구 `13` 런타임 절반, PostRef까지
|
||||||
|
확장)가 합류**:
|
||||||
|
|
||||||
| 파일 | 확인된 것 |
|
| 파일 | 확인된 것 |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
|
|
||||||
|
|
@ -936,7 +936,7 @@ emit 수신 규칙 셋: (1) count가 다르면 둘 다 갱신 + `rawInvalid` +
|
||||||
내부 발생 emit이 같은 진입점을 쓰는 것) — 해법으로는 부족한데, 막는 게이트는
|
내부 발생 emit이 같은 진입점을 쓰는 것) — 해법으로는 부족한데, 막는 게이트는
|
||||||
보통 순회하는 노드 자신이 아니라 **상류**에 있어 자기 `rawEmit`을 태워도
|
보통 순회하는 노드 자신이 아니라 **상류**에 있어 자기 `rawEmit`을 태워도
|
||||||
누출이 남고, `nil` emit은 하류마다 전체 순회를 강제해 같은 문제를 연쇄시키기
|
누출이 남고, `nil` emit은 하류마다 전체 순회를 강제해 같은 문제를 연쇄시키기
|
||||||
때문. 상세는 `research/state-epoch-validation.md` §2·§5-3.
|
때문. 상세는 `base/state-epoch-plan.md` §2·§5-3(같은 날 `base/`로 승격됨).
|
||||||
|
|
||||||
**메모**: 이 분리는 M절에서 **철회했던 `seen`/`computedAt` 분리가 다른 근거로
|
**메모**: 이 분리는 M절에서 **철회했던 `seen`/`computedAt` 분리가 다른 근거로
|
||||||
되살아난 것**이다. 옛 근거는 여전히 틀렸고, 지금 근거는 **순회가 값과 통지를
|
되살아난 것**이다. 옛 근거는 여전히 틀렸고, 지금 근거는 **순회가 값과 통지를
|
||||||
|
|
@ -951,7 +951,7 @@ state 의 전파를 손대는 작업이라 with 처럼 다른 노드가 나는
|
||||||
`:Apply`** 라는 층위 구분이다. 부수로 `Debounce`/`Throttle`의 `:Apply` 관용구는
|
`:Apply`** 라는 층위 구분이다. 부수로 `Debounce`/`Throttle`의 `:Apply` 관용구는
|
||||||
그대로 유효하고(팩토리가 내부에서 `:Gate`를 부름), `Blocker` 배선은 이미 확정된
|
그대로 유효하고(팩토리가 내부에서 `:Gate`를 부름), `Blocker` 배선은 이미 확정된
|
||||||
`state:Block(blocker)` 메소드로 자동 해소되며, `__call`은 안 쓴다.
|
`state:Block(blocker)` 메소드로 자동 해소되며, `__call`은 안 쓴다.
|
||||||
`research/gate-primitive.md`의 2번이 해소로 갱신됨.
|
`base/gate-plan.md`(같은 날 `base/`로 승격)의 2번이 해소로 갱신됨.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -999,3 +999,28 @@ Blocker 는 해당 내부 배선을 따른다 ← 동의합니다 해당 방법
|
||||||
M2 각주·M3 체크박스), `README.md`, `question.md`, `todos.md`,
|
M2 각주·M3 체크박스), `README.md`, `question.md`, `todos.md`,
|
||||||
`luau-test/`(스파이크 `05`가 `rewrite-required/`로 되돌아감 — 다이아몬드
|
`luau-test/`(스파이크 `05`가 `rewrite-required/`로 되돌아감 — 다이아몬드
|
||||||
Observer가 이제 변경당 **1회**만 울어야 하므로 핵심 assert가 정반대가 됨).
|
Observer가 이제 변경당 **1회**만 울어야 하므로 핵심 assert가 정반대가 됨).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# P절 — `/code-review high` 12건 (2026-08-21, O절 커밋 직후)
|
||||||
|
|
||||||
|
사용자가 `c58c97a`(O절) 직후 `/code-review high`를 돌려 **12건**이 나왔고
|
||||||
|
**전부 유효**했다. 9건은 그 자리에서 수정, **3건은 실제로 안 정해진 설계
|
||||||
|
구멍이라 열린 항목으로 세웠다**(임의로 정하지 않음).
|
||||||
|
|
||||||
|
## P-1. 열린 항목으로 승격한 3건
|
||||||
|
|
||||||
|
| # | 무엇 | 어디로 |
|
||||||
|
|---|---|---|
|
||||||
|
| High-1 | **게이트가 유보했다 내보내는 emit이 어느 source를 싣는가.** 확정 `setup`은 `(emit: () -> ()) -> (() -> ())`라 양쪽 다 source를 안 받는데, 에포크 수신 규칙은 전부 `[source]` 키 판정 → `blocker:Off()`의 배치 emit이 하류에서 **삼켜진다**. M절이 *"`nil` 규약만 Gate 설계와 같이 확정하면 된다"*고 짚었는데 O절이 안 닫았다 | `base/gate-plan.md` 4번 + `question.md` 3번. **M2 착수 전 필요**(시그니처가 바뀜). 권고는 (c) `emit(self)` — `GateNode`를 source처럼 취급 |
|
||||||
|
| Med-2 | **재계산 후 `sourceCountMap` 갱신 범위.** 규칙 1이 발행 소스 항목만 건드리므로 다중 소스 배치에서 **같은 값을 두 번 계산**한다 | `state-epoch-plan.md` §5 7-(a). 권고: 자기가 읽은 상류 전부 갱신 → 뒤따르는 emit은 규칙 2로 "통지만" |
|
||||||
|
| Med-3 | **두 맵의 초기값·`:With` 병합.** "상류에서 복사"를 문자 그대로 하면 순회가 앞당긴 지연분 상속 여부가 안 정해져 새 노드가 통지를 삼킬 수 있고, 희소 `pending` 구현 메모와도 안 맞는다 | 같은 곳 7-(b)/(c). 권고: 복사 대신 **첫 재계산 때 구성** — 그러면 (c)도 자동 해소 |
|
||||||
|
|
||||||
|
## P-2. 그 자리에서 고친 9건
|
||||||
|
|
||||||
|
- `state-epoch-plan.md`: §4/§5-2의 `sourceList` 잔재 제거(코퍼스에 `bk.sourceList`라는 **무관한 동명 식별자**가 이미 있어 오독 위험), §3의 "뒤늦은 신호에 `rawInvalid`가 켜져도"를 §2 규칙(안 켜짐)에 맞게 정정.
|
||||||
|
- `gate-plan.md`: 배너가 부정하는 본문 두 문장("공개 프리미티브로 꺼낸다")을 같이 수정 — `conventions.md`가 최빈 실패로 지목한 패턴.
|
||||||
|
- `question.md` 1번: `Gate` 이름 항목이 "다음 세션으로 미뤄졌다"로 열린 채였음 → 해소로 갱신.
|
||||||
|
- `luau-test/STATUS.md`: `05` 이동이 반영 안 된 개수 3곳(`done/` 19→17·18건→17건, `rewrite-required/` 4→6, 런타임 13→12)과 "`05`가 다시 돌아왔다"는 같은 파일 안의 모순 문장.
|
||||||
|
- `comparison-fusion-vide.md`: 배너 바로 위 본문("신호는 두 번 도착해도")이 배너와 어긋나던 것.
|
||||||
|
- 이 파일 N절과 `README.md`가 가리키던 `research/` 옛 경로.
|
||||||
|
|
|
||||||
|
|
@ -64,8 +64,10 @@
|
||||||
`-er`를 많이 쓰는데 `Gater`는 영어로 어색하다. 에이전트 권고는 **`Gate`
|
`-er`를 많이 쓰는데 `Gater`는 영어로 어색하다. 에이전트 권고는 **`Gate`
|
||||||
그대로**(`gate`는 이미 행위자가 아니라 **장치**를 가리키는 명사라 `-er`가
|
그대로**(`gate`는 이미 행위자가 아니라 **장치**를 가리키는 명사라 `-er`가
|
||||||
불필요 — `Source`/`Ref`/`Slot`/`Tween`도 같은 계열), 대안 후보는
|
불필요 — `Source`/`Ref`/`Slot`/`Tween`도 같은 계열), 대안 후보는
|
||||||
`Valve`/`Relay`. **설계 자체가 다음 세션으로 미뤄졌으므로 이름도 그때 같이**
|
`Valve`/`Relay`. **[2026-08-21 해소] `Gate`로 확정**(탑레벨 생성자를 안
|
||||||
— 3번 절의 `Gate` 항목과 `base/gate-plan.md`가 소스.
|
만들고 `state:Gate(setup)` 메소드로 가면서 `Gater` 문제 자체가 사라짐 —
|
||||||
|
메소드 자리에서는 `:With`/`:Compute`와 나란히 자연스럽다). 노드 타입 이름은
|
||||||
|
`GateNode`. `base/gate-plan.md`의 1번이 소스.
|
||||||
- **`Slot`(2순위)**: Vue의 "slot"(콘텐츠 주입 지점)과 이름은 같지만 의미가
|
- **`Slot`(2순위)**: Vue의 "slot"(콘텐츠 주입 지점)과 이름은 같지만 의미가
|
||||||
다름(quad의 Slot은 자식 배열 재조정 프리미티브) — Vue 배경 있는 사람이
|
다름(quad의 Slot은 자식 배열 재조정 프리미티브) — Vue 배경 있는 사람이
|
||||||
헷갈릴 수 있음.
|
헷갈릴 수 있음.
|
||||||
|
|
@ -205,6 +207,25 @@
|
||||||
빈도가 다르다.** 해법은 있다 — reconcile이 `pos`처럼 **절대 offset도 러닝
|
빈도가 다르다.** 해법은 있다 — reconcile이 `pos`처럼 **절대 offset도 러닝
|
||||||
누적**으로 들고 다니면 O(n)(그게 `mountSlotTree`가 이미 하는 방식). 그렇게
|
누적**으로 들고 다니면 O(n)(그게 `mountSlotTree`가 이미 하는 방식). 그렇게
|
||||||
할지, 아니면 실측 전엔 그냥 둘지 판단 필요.
|
할지, 아니면 실측 전엔 그냥 둘지 판단 필요.
|
||||||
|
- **⭐⭐ [신설, 2026-08-21 `/code-review high`] 게이트가 유보했다 내보내는
|
||||||
|
emit은 어느 source를 싣는가 — M2 착수 전 필요.** 확정된 `setup`은
|
||||||
|
`(emit: () -> ()) -> (() -> ())`라 **양쪽 다 source를 안 받는데**,
|
||||||
|
`base/state-epoch-plan.md`의 수신 규칙은 전부 `[source]` 키로 판정한다.
|
||||||
|
그래서 `blocker:Off()`가 묶어뒀던 배치 emit이 하류에서 **삼켜져 통지가
|
||||||
|
통째로 사라진다.** 후보는 (a) `emit(nil)` = 전체 확인, (b) 유보한 소스마다
|
||||||
|
각각 emit(→ `Blocker`의 "정확히 1회"가 깨짐), (c) `GateNode` 자신을
|
||||||
|
source처럼 취급해 `emit(self)`. **에이전트 권고는 (c)** — 1회 통지와 O(1)
|
||||||
|
판정을 둘 다 지킨다. 어느 쪽이든 `setup`/`emit` 시그니처가 바뀌므로 M2
|
||||||
|
전에 정해야 한다. 상세는 `base/gate-plan.md`의 4번.
|
||||||
|
- **⭐ [신설, 2026-08-21 `/code-review high`] State 에포크 — 두 맵의 초기값·
|
||||||
|
병합·재계산 시 갱신 범위 — M3 착수 전 필요.** 세 자리가 안 정해져 있다:
|
||||||
|
(a) 재계산이 끝나면 `sourceCountMap`을 **자기가 읽은 상류 전부**에 대해
|
||||||
|
갱신할 것인가(안 그러면 다중 소스 배치에서 같은 값을 두 번 계산한다),
|
||||||
|
(b) 새 노드의 두 맵을 "상류에서 복사"할 것인가 아니면 **첫 재계산 때
|
||||||
|
구성**할 것인가(원안은 복사인데, 순회가 앞당겨둔 지연분까지 상속해야 규칙이
|
||||||
|
성립한다), (c) `:With`에서 두 상류가 같은 소스에 다른 count를 들 때의 병합
|
||||||
|
규칙. **에이전트 권고는 (a) 전부 갱신 + (b) 첫 재계산 때 구성**이고, 그러면
|
||||||
|
(c)는 자동 해소된다. 상세는 `base/state-epoch-plan.md`의 §5 7번.
|
||||||
- **[해소, 2026-08-21] 공용 게이트 노드의 이름과 표면 — `state:Gate(setup)`
|
- **[해소, 2026-08-21] 공용 게이트 노드의 이름과 표면 — `state:Gate(setup)`
|
||||||
메소드 + `GateNode`로 확정.** 탑레벨 프리미티브는 안 만들고, `Blocker`는
|
메소드 + `GateNode`로 확정.** 탑레벨 프리미티브는 안 만들고, `Blocker`는
|
||||||
`state:Block(blocker)` 안에서 그 배선을 쓴다(사용자: *"Gate 는 따로
|
`state:Block(blocker)` 안에서 그 배선을 쓴다(사용자: *"Gate 는 따로
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,8 @@ Store/Slot/Tween/bind-dispatch 설계 결정에 근거로 인용될 때만 열
|
||||||
— quad Store가 이 naive BFS 방식을 그대로 베끼면 안 되는 이유.
|
— quad Store가 이 naive BFS 방식을 그대로 베끼면 안 되는 이유.
|
||||||
**[2026-08-14 보강]** quad가 이걸 피하는 방식은 "전파를 중간에 끊는 것"이
|
**[2026-08-14 보강]** quad가 이걸 피하는 방식은 "전파를 중간에 끊는 것"이
|
||||||
아니라 **애초에 push 시점에 계산을 안 하는 것**(pull-recompute + 노드별
|
아니라 **애초에 push 시점에 계산을 안 하는 것**(pull-recompute + 노드별
|
||||||
캐시) — 신호는 두 경로로 두 번 도착해도 계산은 `:Get()` 때 한 번뿐.
|
캐시) — 신호가 두 경로로 도착해도 계산은 `:Get()` 때 한 번뿐.
|
||||||
|
(**[2026-08-21]** 이제 신호 자체도 두 번 안 온다 — 아래 갱신 참고.)
|
||||||
즉 quad도 중복 *재평가*는 안 일어남
|
즉 quad도 중복 *재평가*는 안 일어남
|
||||||
(`base/source-state-plan.md`의 "다이아몬드 의존성은 무엇이 푸는가" 절).
|
(`base/source-state-plan.md`의 "다이아몬드 의존성은 무엇이 푸는가" 절).
|
||||||
**[2026-08-21 갱신]** 여기 있던 "중복 *통지*는 접지 않는다"는 뒤집혔다 —
|
**[2026-08-21 갱신]** 여기 있던 "중복 *통지*는 접지 않는다"는 뒤집혔다 —
|
||||||
|
|
|
||||||
|
|
@ -279,3 +279,18 @@ State 에포크도 **채택**했다(*"gate 와 epoch 가 제가 만족할만한
|
||||||
`rewrite-required/`로 갔다(다이아몬드 Observer가 이제 변경당 1회만 울어야
|
`rewrite-required/`로 갔다(다이아몬드 Observer가 이제 변경당 1회만 울어야
|
||||||
해서 핵심 assert가 정반대). 처리 전량은
|
해서 핵심 assert가 정반대). 처리 전량은
|
||||||
`qa-request/pre-implementation-qa-round5-followup.md`의 O절.
|
`qa-request/pre-implementation-qa-round5-followup.md`의 O절.
|
||||||
|
|
||||||
|
## 12. `/code-review high` — 12건 전부 유효, 그중 3건은 실제 설계 구멍
|
||||||
|
|
||||||
|
O절 커밋 직후 사용자가 돌린 리뷰에서 12건이 나왔고 전부 유효했다. 특히
|
||||||
|
**게이트가 유보했다 내보내는 emit이 어느 source를 싣는가**는 M절이 이미
|
||||||
|
짚어뒀는데(*"`nil` 규약만 Gate 설계와 같이 확정하면 된다"*) 표면 확정 때
|
||||||
|
같이 안 닫힌 것으로, 그대로 두면 `blocker:Off()`의 배치 통지가 하류에서
|
||||||
|
삼켜진다 — `setup` 시그니처에 영향이 있어 **M2 착수 전 항목**으로 되돌렸다
|
||||||
|
(같은 날 "M2를 막는 항목 없음"이라 적었던 `todos.md` 00번도 정정).
|
||||||
|
|
||||||
|
교훈이 하나 더 있다: 이번에도 **감사자 각도가 아니라 diff 각도에서만 보이는
|
||||||
|
것**이 다수였다(개수 하드코딩, 배너 vs 본문, 같은 파일 안의 모순 문장,
|
||||||
|
방금 옮긴 파일을 가리키는 새 텍스트). `conventions.md`의 "`/code-review`는
|
||||||
|
감사자를 대체하지 않는다" 항목이 다시 확인된 셈. 전량은
|
||||||
|
`qa-request/pre-implementation-qa-round5-followup.md`의 P절.
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,15 @@
|
||||||
`state:Gate(setup)` + `GateNode`로(`base/gate-plan.md`), State의
|
`state:Gate(setup)` + `GateNode`로(`base/gate-plan.md`), State의
|
||||||
재계산/전파 판정은 **소스 에포크 비교** 채택으로(`base/state-epoch-plan.md`,
|
재계산/전파 판정은 **소스 에포크 비교** 채택으로(`base/state-epoch-plan.md`,
|
||||||
구현은 M3) 닫혔다. 두 문서 모두 `research/`가 아니라 **`base/`**에 있다.
|
구현은 M3) 닫혔다. 두 문서 모두 `research/`가 아니라 **`base/`**에 있다.
|
||||||
남은 것은 판단이 아니라 구현 시 정할 것들 — `Gate`의 생명주기·재진입
|
**⚠️ [2026-08-21 정정 — 같은 날 `/code-review high`] "막는 항목이 없다"는
|
||||||
계약, M2에 `Blocker`까지 넣을지, 그리고 스파이크 `05` 재작성
|
너무 이르다.** 게이트가 유보했다 내보내는 emit이 **어느 source를 싣는지**가
|
||||||
|
안 정해져 있고(확정된 `setup`은 source를 안 받는데 에포크 수신 규칙은
|
||||||
|
`[source]` 키로 판정한다 — 그대로면 `blocker:Off()`의 배치 통지가 하류에서
|
||||||
|
삼켜진다), 이건 `setup` 시그니처를 바꿀 수 있어 **M2 착수 전 판단이
|
||||||
|
필요하다**(`question.md` 3번, `base/gate-plan.md`의 4번). M3 쪽으로는 두
|
||||||
|
맵의 초기값·병합·재계산 시 갱신 범위가 열려 있다(`state-epoch-plan.md`
|
||||||
|
§5 7번). 그 외 남은 것은 판단이 아니라 구현 시 정할 것들 — `Gate`의
|
||||||
|
생명주기·재진입 계약, M2에 `Blocker`까지 넣을지, 스파이크 `05` 재작성
|
||||||
(`luau-test/STATUS.md`).
|
(`luau-test/STATUS.md`).
|
||||||
|
|
||||||
**4라운드 — [2026-08-21] 종결.** 문항지는
|
**4라운드 — [2026-08-21] 종결.** 문항지는
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue