design: 게이트가 게이트 emit을 받는 경우 확정 — 출처를 넘기지 말고 풀어서 합친다
사용자 발견. R절까지의 규칙("출처가 GateNode면 받은 출처를 그대로 아래로
넘긴다")은 받는 쪽이 또 게이트인 경우가 정의돼 있지 않았고, 그대로 넘기면
깨진다 — 상류 게이트는 자기 전파가 끝나자마자 table.clear 하므로 하류
게이트가 출처만 들고 유보했다가 나중에 풀면 빈 집합을 내보내 변경이 통째로
증발한다.
확정: 수신 시점에 unfold 해서 자기 _withheld에 합친다(Source면 하나,
GateNode면 그 집합 전부). 게이트가 몇 겹으로 겹쳐도 각 층이 자기 집합을
들고 있으므로 어느 층이 먼저 풀리든 정보가 안 샌다.
같이 못박은 것: 게이트의 sourceEmitMap은 수신 때가 아니라 실제로 전파할 때
집합 전체에 대해 갱신한다 — 그래야 "내가 하류로 던진 에포크"라는 맵의 뜻이
게이트에서도 참이 된다.
반영은 base/gate-plan.md 4번, base/state-epoch-plan.md §2, followup S절.
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
930e45bfad
commit
e2b85bca55
4 changed files with 66 additions and 3 deletions
|
|
@ -138,9 +138,28 @@ end)
|
|||
생각이 될 수 있겠지만, 사실 여기서 지연과 비지연을 구분할 이유가
|
||||
없습니다."* 하류가 보는 차이는 집합의 원소가 하나냐 여럿이냐뿐이고 판정
|
||||
규칙은 완전히 같다.
|
||||
- 하류는 출처가 `GateNode`면 그 집합을 순회하며 각 소스에 평소 규칙(1~3)을
|
||||
적용하고, 하나라도 걸리면 **받은 출처를 그대로** 더 아래로 넘긴다
|
||||
(`state-epoch-plan.md` §2).
|
||||
- 하류가 **평범한 노드**면, 출처가 `GateNode`일 때 그 집합을 순회하며 각
|
||||
소스에 평소 규칙(1~3)을 적용하고, 하나라도 걸리면 **받은 출처를 그대로**
|
||||
더 아래로 넘긴다(`state-epoch-plan.md` §2).
|
||||
- **⭐ [2026-08-21 신설] 하류가 또 다른 게이트면 — 받은 집합을 풀어
|
||||
자기 `withheld`에 합친다.** 게이트가 게이트 emit을 받는 경우가 정의돼
|
||||
있지 않던 구멍이었다(사용자 발견). **출처를 그대로 넘기면 안 된다** —
|
||||
상류 게이트는 자기 전파가 끝나자마자 `table.clear` 하므로, 하류 게이트가
|
||||
유보했다가 나중에 풀면 그때는 **빈 집합**을 내보내게 되어 변경이 통째로
|
||||
증발한다. 그래서 수신 시점에 **풀어서 옮겨 담아야** 한다:
|
||||
```
|
||||
-- 출처가 Source면 그 하나를, GateNode면 그 집합 전부를 편다
|
||||
for source in unfold(origin) do
|
||||
self._withheld[source] = true
|
||||
end
|
||||
-- 그 다음 평소대로 정책 실행
|
||||
```
|
||||
게이트가 몇 겹으로 겹쳐도 각 층이 자기 집합을 들고 있으므로 어느 층이
|
||||
먼저 풀리든 정보가 안 샌다.
|
||||
- **게이트의 `sourceEmitMap`은 수신 때가 아니라 실제로 전파할 때** 갱신한다
|
||||
(집합 전체에 대해 한꺼번에). 그래야 "내가 하류로 던진 에포크"라는 맵의
|
||||
뜻이 게이트에서도 참이 된다 — 유보 중 같은 에포크가 다른 경로로 또 오면
|
||||
규칙 2로 걸려 정책을 한 번 더 태우는데, 이미 집합에 있으므로 무해하다.
|
||||
- **`OffWithoutEmit()`로 emit 없이 풀어도 문제가 없다** — 하류의
|
||||
`sourceEmitMap`이 뒤에 남지만, 그 소스의 다음 진짜 emit이 규칙 1/2로
|
||||
걸려 스스로 낫는다.
|
||||
|
|
|
|||
|
|
@ -104,6 +104,10 @@ count가 이미 최신이면 **캐시는 유효한 채로 남는다**(아래 2
|
|||
그대로 적용**하고, 하나라도 1번이나 2번에 걸렸으면 **받은 출처(그 게이트)를
|
||||
그대로** 뒤로 넘긴다. 전부 3번이면 삼킨다 — 다이아몬드에서 같은 해제 통지가
|
||||
두 번 도착해도 두 번째가 접히는 건 소스 emit과 똑같다.
|
||||
- **⚠️ 단, 받는 쪽이 또 다른 `GateNode`면 출처를 넘기지 않고 집합을 풀어
|
||||
자기 `withheld`에 합친다** — 상류 게이트가 전파 직후 집합을 비우기 때문에
|
||||
출처만 들고 있으면 나중에 빈 집합을 내보내게 된다(`base/gate-plan.md`의
|
||||
4번).
|
||||
- **재계산 판정**:
|
||||
- `rawInvalid == true` → 그냥 재계산한다. 순회할 이유가 없다(이미 확정).
|
||||
- `rawInvalid == false` → **그때만 `sourceCountMap`을 훑는다.** 목적은 하나뿐 —
|
||||
|
|
|
|||
|
|
@ -1114,3 +1114,34 @@ Q절은 "노드가 정책이 그 자리에서 `emit()`을 불렀는지 되짚어
|
|||
그래서 **`:With` 병합 규칙은 필요 없어졌다** — 생성 시점 라이브 count로
|
||||
통일되므로 두 상류가 다른 count를 들 일이 없다. P절 Med-3이 열어둔 (b)/(c)가
|
||||
이걸로 전부 닫혔다.
|
||||
|
||||
---
|
||||
|
||||
# S절 — 미정의 행동 하나: 게이트가 게이트 emit을 받으면 (2026-08-21)
|
||||
|
||||
사용자 발견. R절까지의 규칙은 "출처가 `GateNode`면 집합을 순회하고 **받은
|
||||
출처를 그대로** 아래로 넘긴다"였는데, **받는 쪽이 또 게이트인 경우**가 정의돼
|
||||
있지 않았다.
|
||||
|
||||
**그대로 넘기면 깨진다**: 상류 게이트는 자기 전파가 끝나자마자
|
||||
`table.clear(withheld)` 한다. 하류 게이트가 그 출처만 들고 유보했다가 나중에
|
||||
풀면, 그때 참조하는 집합은 **이미 비어 있다** → 변경이 통째로 증발.
|
||||
|
||||
**확정**: 수신 시점에 **풀어서 자기 `withheld`에 합친다.**
|
||||
|
||||
```
|
||||
for source in unfold(origin) do -- Source면 하나, GateNode면 그 집합 전부
|
||||
self._withheld[source] = true
|
||||
end
|
||||
-- 그 다음 평소대로 정책 실행
|
||||
```
|
||||
|
||||
게이트가 몇 겹으로 겹쳐도 각 층이 자기 집합을 들고 있으므로 어느 층이 먼저
|
||||
풀리든 정보가 안 샌다.
|
||||
|
||||
**같이 못박은 것**: 게이트의 `sourceEmitMap`은 **수신 때가 아니라 실제로
|
||||
전파할 때** 집합 전체에 대해 갱신한다. 그래야 "내가 하류로 던진 에포크"라는
|
||||
맵의 뜻이 게이트에서도 참이 된다(유보 중 같은 에포크가 다른 경로로 또 오면
|
||||
규칙 2로 걸려 정책을 한 번 더 태우지만, 이미 집합에 있으므로 무해).
|
||||
|
||||
반영: `base/gate-plan.md` 4번, `base/state-epoch-plan.md` §2.
|
||||
|
|
|
|||
|
|
@ -323,3 +323,12 @@ O절 커밋 직후 사용자가 돌린 리뷰에서 12건이 나왔고 전부
|
|||
`rawInvalid = true`(순회가 훑을 목록이 곧 이 맵이라 비워두면 "유효"로
|
||||
오판). 이걸로 `:With` 병합 규칙이 필요 없어지며 `/code-review` Med-3이
|
||||
닫혔다. 전량은 followup의 R절.
|
||||
|
||||
## 15. 게이트가 게이트 emit을 받는 경우 — 미정의였고, 풀어 담는 것으로 확정
|
||||
|
||||
사용자가 마지막으로 잡은 구멍. "받은 출처를 그대로 넘긴다"는 규칙이 하류가
|
||||
또 게이트일 때 깨진다 — 상류 게이트가 전파 직후 집합을 비우므로 하류 게이트가
|
||||
나중에 풀면 빈 집합을 내보내 변경이 증발한다. 수신 시점에 **풀어서 자기
|
||||
`withheld`에 합치는** 것으로 확정. 겹겹이 쌓여도 각 층이 자기 집합을 들고
|
||||
있어 어느 층이 먼저 풀리든 정보가 안 샌다. 부수로 게이트의 `sourceEmitMap`은
|
||||
수신 때가 아니라 **실제 전파 때** 갱신하는 것으로 못박았다. 전량은 S절.
|
||||
|
|
|
|||
Loading…
Reference in a new issue