# 2026-08-21-03 — `Epoch`/`EpochMap`/`Brand` 전면 승격 + 해소 기록 flatten

**요지**: 앞 세션(`2026-08-21-02-qa-round5-and-gate-epoch-research.md`)이
컨텍스트 피로로 미뤄둔 승격을 실제로 수행하고, 그 과정에서 코퍼스 곳곳에 쌓여
있던 `[해소]`/`[정정]` 층을 걷어냈다. 지금 유효한 설계는 전부 `base/`가 소스 —
이 파일은 "무엇을 어디로 옮겼나"의 기록이다.

## 1. 사용자 지시

두 갈래였다.

1. **질문**: *"slot-attach-decomposition.md 은 이제 확정 이야기이기에, 리서치
   대상이 아니지 않나요? 또, 이것이 base에 적용되어 있습니까?"*
2. **작업 지시**: `state-epoch-plan.md`/`source-state-plan.md`/`effect-plan.md`/
   `brand-plan.md` 넷을 전면 승격하고, *"이 승격을 하며 더이상 필요 없어진
   요소들은 archive 하세요. 특히 재정정 기록이 쌓인 부분이 있는데, 풀어가며
   archive 해서 flatten 해야할 필요가 보입니다."*

## 2. 1번 질문에 대한 답 — 둘 다 예

- **`base/slot-plan.md`에 이미 반영돼 있다** — `materializeSlotTree` /
  `mountSlotTree` / 두 줄짜리 `attachSlot` 래퍼가 그 문서의 "재귀 메커니즘"
  절에 의사코드로 들어가 있고, 그 문서가 근거로 이 파일을 네 곳에서 인용한다.
- **`research/`에 있을 이유는 없다** — `.claude/README.md`의 폴더 기준상
  `research/`는 "아직 착수 전, 상의 필요"다.

**어디로 옮길지**가 실제 판단이었다. `archive/`는 기준이 "뒤집혔거나 기각됨"
이라 채택된 결정의 근거 기록엔 안 맞고, `reference/`의 기준("결정 자체가
아니라 다른 문서가 근거로 인용하는 온디맨드 자료")엔 정확히 맞는다. 그래서
**`reference/`로 옮기고 폴더 기준 자체에 그 용도를 명문화**했다
(`README.md` 폴더 기준표 + `project-context.md`). 선례도 있다 —
`quad-v1-architecture.md`/`comparison-fusion-vide.md`가 `base/`에서 같은
이유로 이동해온 것.

같은 처리를 `epoch-brand-composition.md`에도 적용했다(승격이 끝나면 성격이
똑같아지므로).

## 3. 승격 — 무엇이 어떻게 바뀌었나

### `base/state-epoch-plan.md` (재작성)

- **`Epoch` 인터페이스 신설** — `type Epoch = { Revision: number }`, 그 자체로
  키가 되는 unique 테이블. `Source`가 구조적으로 만족하고 `EpochBrand`에
  등록된다. "루트 `Source`의 에포크"라는 좁은 서술이 전부 여기로 일반화됨.
- **`EpochMap` 신설** — `:Update(Epoch|{Epoch}) -> boolean`(반환값의 뜻은
  "뒤로 전파가 필요한가"), `:Refresh()`, `:Sync()`. 부기가 State에서 떨어져
  나와 재사용 가능한 객체가 됐다.
  - **`:Refresh`는 이 세션이 이름만 붙인 것**이다 — "순회"라는 연산 자체는
    이미 확정돼 있었고(`rawInvalid == false`일 때 자기 맵을 훑는다), 맵이
    키를 소유하므로 인자 없는 형태가 자연스러워서 그렇게 적었다. 새 설계가
    아니다.
- **State의 두 맵이 `EpochMap` 둘이 됐다** — `sourceCountMap` →
  `valueEpochMap`, `sourceEmitMap` → `emitEpochMap`. 이름을 바꾼 이유는
  "source"가 더 이상 정확하지 않아서다.
- **문서 구조를 §1~§8로 다시 짰다.** 옛 §3(에이전트 분석)과 §5(열린 질문)에
  결론과 정정 경위가 섞여 있었는데, 결론은 규칙 본문(§2~§5)으로 올리고
  경위는 걷어냈다(원문은 `qa-request/pre-implementation-qa-round5-followup.md`
  M·N절과 `reference/epoch-brand-composition.md`에 있다).

### `base/brand-plan.md` (전면 재작성)

- **인스턴스 브랜드** — `Brand()`가 브랜드마다 weak 집합 하나를 들고,
  `SomeBrand:register(x)` / `SomeBrand:is(x)`. **다중 태깅**이 이 재작성의
  존재 이유다(`Source`가 `SourceBrand`이면서 동시에 `EpochBrand`).
- **역조회 `Brand.get`은 없어졌다.**
- **유지된 것**: weak-key 레지스트리, 테이블 아이덴티티, duck-typing 기각
  근거 둘, 포함 관계 predicate 합성, `Brand`가 `None`에 의존하지 않는다는
  결정, Luau narrowing 주의.
- **새로 명시한 것 하나** — 다중 태깅이 가능해져도 **포함 관계는 계속
  predicate 합성으로 쓴다**(`Source`를 `StateBrand`에도 등록하지 않는다).
  등록 지점이 흩어지면 조용한 버그가 되고 포함 관계가 코드에서 사라지기
  때문. 정당한 다중 등록은 `Epoch`처럼 **다른 축의 계약**뿐이다.
- 옛 표면은 `archive/brand-shared-registry-reversed.md`.

### `base/source-state-plan.md`

- "Source가 State를 만족함" 절에 **`Epoch`도 같은 방식으로 만족**한다는 항목
  추가(`Revision`이 공개여야 하는 이유 포함).
- `state:Observer(fn)` 절에 **`fn(self, from: Epoch | {Epoch})`** 시그니처
  확정 추가. "값을 안 실어주는 구독" 계약이 안 깨지는 이유도 같이.
- 승격 대기 ⚠️ 배너 제거, 그 절들의 "에포크" 표현을 "리비전"으로 통일.

### `base/effect-plan.md`

- **⚠️ 미해결이던 다중 의존성 중복 발화가 닫혔다** — `EffectHandle`이
  `EpochMap`을 하나 들고 각 내부 Observer가 받은 `from`으로 `Update`,
  `true`일 때만 `fn`을 돌린다. `Effect`가 곧 deps의 공통 하류가 된다.
- **`Ref` 의존성은 이 맵에 안 낀다**(이 세션이 명시) — `Ref`는 `Epoch`가
  아니고 `:Callback`으로 발화해 `from`이 없다. 공통 상류 문제 자체가 없다.

### `base/gate-plan.md`

승격 대기 배너를 반영 완료로 바꾸고, 페이로드/`withheld`/`emitEpochMap`
표현을 `Epoch` 어휘로 통일. **기제는 하나도 안 바뀌었다.**

## 4. flatten — 걷어낸 것들

사용자가 지목한 "재정정 기록이 쌓인 부분"은 실제로 세 군데였다.

1. **`question.md`가 다시 해소 항목으로 절반이 찼다.** 그 문서 스스로
   *"항목을 해소하면 여기서 지우고 `archive/question-resolved.md`에 근거와
   함께 옮길 것 — 다시 쌓이면 같은 문제가 반복됨"*이라 규정해뒀는데,
   2026-08-21 하루에 여러 건이 닫히면서 예고대로 재발했다. **16건을 일괄
   이관**하고(`Gate` 이름 항목 포함), 파일이 421줄 → 208줄이 됐다.
   - 이관분 중 `Epoch` 관련 서술은 **이관 시점 표현 그대로 뒀고**, 대신
     이관 섹션 머리에 "필드 이름이 그 뒤 바뀌었다"는 경고를 붙였다. 히스토리
     문서의 원문을 소급해 고치지 않는다는 관례를 지키면서 오독만 막는 처리.
2. **`todos.md`의 "M3 착수 전에 결론이 필요한 항목 목록"이 절반 넘게 `[해소]`
   였다.** 실제로 열려 있는 건 둘뿐(중간 State GC 미검증, `store:GetDynamic`)
   인데 해소 항목 일곱이 섞여 있어 "지금 할 일"로 안 읽혔다. 걷어냄.
3. **`todos.md` 000번**(다음 세션 첫 작업 = 이 승격)은 완료됐으므로 삭제하고,
   00번 안에 승격 완료 사실만 남겼다.

## 5. 마지막 미정도 같은 세션에 닫힘 — 리비전 증가는 `bit32` 랩

승격 보고 직후 사용자가 그 자리에서 정했다: *"리비전 증가는 bit32 로
두고싶어요. 그건 luau 에서 native call 이라 아주 빨라요. 반면 double 의
연산이 느린편인데, 희소 수준이 아니라, 사실상 만나는걸 수년간 보기 어려운
라운드되어 동일해 무시되는 경우를 막기 위해 double 까지 올려야할 이유를
모르겠어요. 매번 도는 코드인지라, 값 싸게 native call + num 연산으로 가볍게
가고 싶어요."*

논거의 핵심은 **"`2^53` 포화는 어차피 도달 불가능한 시나리오인데, 그걸
피하겠다고 값을 double 영역까지 키울 이유가 없다"**이고, 매 `Set`마다 도는
hot path라는 것.

**⚠️ 에이전트가 형태를 잘못 옮겼고, 사용자가 그 자리에서 정정했다.**
에이전트는 이걸 `bit32.band(rev + 1, 0xFFFFFFFF)`로 적고, 거기에 "그러니
`bit32`라서 증가가 더 싼 건 아니다 — `n + 1`은 어느 쪽이든 double 덧셈이고
`bit32`는 fastcall을 하나 더 얹는다"는 단서까지 달았다. 사용자 정정:

> *"잠시만요. 제가 말한건, bit32.bnot(-a) 입니다"* — 그리고 REPL 출력 셋을
> 그대로 제시했다(`bit32.bnot(-1)` → `0`, `bit32.bnot(-0)` → `4294967295`,
> `bit32.bnot(-4294967295)` → `4294967294`).

**즉 그 세 줄은 예시가 아니라 연산 자체였다.** `luau`로 재확인한 결과:

```
bump(a) = bit32.bnot(-a)
0 → 4294967295 | 1 → 0 | 2 → 1 | 4294967295 → 4294967294
```

`a > 0`이면 `a - 1`, `0`이면 `4294967295`인 **랩어라운드 감소**이고, **갱신과
랩이 FASTCALL 하나로** 끝난다. 그래서:

- **에이전트가 붙였던 단서는 틀렸다** — `band(rev + 1, mask)`라는 **다른
  형태**를 놓고 한 비교였다. `bnot(-a)`는 덧셈 위에 얹히는 게 아니라 갱신
  자체를 대체하므로, *"native call 이라 아주 빨라요"*라는 사용자 서술이 맞다.
- **따름정리는 방향만 바뀌어 유효하다** — 단조 증가가 아닌 게 아니라 아예
  **감소**한다. 지금 규칙이 `==`/`~=`만 쓰기 때문에 무해하지만, 순서 비교를
  넣고 싶어지면 이 결정부터 되짚어야 한다. `Revision`이라는 이름이 순서를
  뜻하지 않는다는 것도 같이 적어뒀다.

**교훈**: 사용자가 근거로 든 REPL 출력을 "그 함수가 랩한다는 예시"로만 읽고
연산 형태를 에이전트가 임의로 재구성했다. `conventions.md`의 "사용자 발언을
근거로 인용할 때는 결론만 적지 말고 논거까지 남길 것" 항목이 겨냥하는 실패에
가깝다 — 이 경우엔 논거를 남기긴 했는데 **형태를 바꿔 남겼다.**

**그래서 `Epoch`/`EpochMap`/`Brand`에 열린 설계 항목은 하나도 남지 않았다.**

`doc-check.py` ERROR 0 유지(WARN 30건은 전부 이 세션 이전부터 있던 것 —
축약 파일명 인용과 날짜 없는 시한부 주장).


## 6. 감사 루프 기록

`conventions.md`의 "핸드오버 준비하고 커밋해" 절차대로 **한 턴에 하나씩**,
라운드마다 각도를 바꿔 돌렸다.

### 1라운드 — `base/` 정합성 + diff 범위

발견 넷 중 **하나는 stale**(감사자가 `bit32` 편집 이전 스냅샷을 읽어
"리비전 증가 방식이 아직 미정"이라 보고했으나 다섯 곳 전부 이미 갱신돼
있었음 — 감사자가 도는 도중 메인 세션이 같은 파일을 고치면 생기는 일이라,
**감사 리포트를 받으면 항상 현재 파일로 재확인할 것**). 나머지 셋은 유효:

1. **`state-epoch-plan.md`가 "§8에서 기각됨"으로 실재하지 않는 내용을
   가리켰다** — 재작성하면서 "게이트를 에포크 경계로" 기각 논거를 떨어뜨렸고,
   `gate-plan.md`와 `reference/`는 또 **다른** 번호(§5-3)를 대고 있었다.
   → §8에 실제 논거를 쓰고 세 포인터를 절 제목으로 통일. **`doc-check.py`는
   산문 속 `§번호`를 검증하지 못한다** — 문서를 재작성할 때 §번호 포인터는
   손으로 훑어야 한다.
2. `ROADMAP.md` M7이 `isState`/`isSource`를 아직 "공유 레지스트리 기반"으로
   서술 → 멤버십 기반으로.
3. **스파이크 `22`가 역전된 `Brand.set`/`Brand.get`을 직접 구현한 채
   `done/`에 있었다** → `rewrite-required/`로 이동. 검증 대상(포함 관계)은
   유효하므로 결론이 틀려서가 아니라 **구현자가 그 파일의 `Brand` 구현을
   참고 모델로 오독**하는 걸 막기 위함. `05`와 같은 처리.

### 2라운드 — 인덱스 레이어 + `luau-test` + 1라운드 이후 diff

여섯 건, 전부 유효:

1. **`state-epoch-plan.md`가 자기 문서 안에서 모순**했다 — §2가 "`Revision`을
   **증가**시킨다"라고 적어놓고 20줄 뒤에 "확정된 방식은 증가가 아니라
   **감소**한다"라고 반박. `bit32.bnot(-a)` 정정을 반영하면서 앞쪽 문장을
   안 고친 것. → 방향을 안 담는 "갱신한다"로.
2. `source-state-plan.md`에 **같은 문장이 복붙돼 있었다** — 같이 정정.
3. `README.md`의 `effect-plan.md` 행이 dedup 대칭을 아직 "미확인"으로 서술
   (본문은 `EF-3`에서 이미 "성립함"으로 확정). 세션 전부터 있던 stale.
4. `STATUS.md`의 `✅ done/` 절이 **같은 파일 위쪽과 어긋났다** — 배너와 표는
   `22` 이동을 반영했는데 절 제목("17건")과 본문 나열("런타임 9건 …/`22`")은
   옛 상태. → 하드코딩 개수를 빼고 폴더/표를 소스로.
5. `luau-test/README.md`의 "실행 환경 세 갈래" 표가 `21`/`22`/`23`을
   빠뜨리고 `13`을 아직 "런타임/타입 부분"으로 쪼개 적고 있었다(런타임
   절반은 2026-08-19에 `22`로 분리돼 나갔다). → 실제와 맞추고, 파일 헤더가
   소스임을 명시.
6. `reference/slot-attach-decomposition.md`의 제약 `C7`이 그 뒤 `native*`
   계층 확정으로 **일반 계약으로서는 폐기**됐는데 문서에 아무 표시가
   없었다. 결론 자체는 유효하지만(배치 경로가 부기를 먼저 끝내는 건 `C6`
   요구사항이라 별개) 표만 떼어 읽으면 오독하므로 상단에 캐비엇 배너.
   `reference/comparison-fusion-vide.md`가 이미 같은 형태의 캐비엇을 달고
   있어 선례를 따랐다.

**교훈 하나** — 4·5번은 둘 다 **"같은 문서 안에서 위쪽만 갱신되고 아래쪽이
남은"** 형태다. 개수를 제목에 박아두면 정확히 이렇게 갈라지므로, 이번에
`STATUS.md`의 절 제목 두 곳에서 하드코딩 개수를 뺐다.

### 3라운드 — `archive/` 배너 정합성 + `qa-request/`의 "반영 완료" 주장

**확실한 모순 발견 0건 — 수렴.** 감사자가 대조한 것: `archive/`의 배너
포인터 넷(`brand-shared-registry-reversed` / `question-resolved`의 이관 섹션 /
`always-propagate-no-dedup-superseded` / `invalidate-dedup-propagation-reversed` /
`bookkeeping-before-physical-reversed`), 2라운드가 고친 여섯 자리, 그리고
`round4/5-followup.md`가 소스라고 선언한 항목들.

특히 **2라운드에서 내가 새로 쓴 `C7` 캐비엇**(*"배치 경로가 부기를 먼저
끝내는 건 `C7`이 아니라 `C6`가 요구하는 별개 사안"*)이
`base/dispatch-core-plan.md`의 "일반 계약 — 물리와" 절 4번과 정확히 대응함이
확인됐다 — 새로 쓴 서술이 기존 확정과 어긋나지 않는지가 이 라운드의 핵심
질문이었다.

유일한 지적은 문체였다 — §8에 "`Revision`만 **올리면**"이라는 방향 어감이
남아 있던 것(§2가 이미 "방향은 계약이 아니다"로 커버하고 있어 모순은 아님).
같이 정리했다.

**수렴 판정**: 3라운드에서 새 발견 0건이므로 `conventions.md`의 루프 종료
조건을 만족한다. 총 소비는 서브에이전트 3패스(각도: `base/` 정합성 →
인덱스 레이어+`luau-test` → `archive/`+`qa-request/`).

## 7. 커밋 전 `/code-review high` — 감사 3라운드가 못 본 축에서 9건

`conventions.md`가 명문화한 그대로였다 — **감사자와 code-review는 보는 축이
다르다.** 감사자 3라운드가 수렴(0건)한 뒤 사용자가 `/code-review high`를
돌리자 **9건이 더 나왔고 전부 유효**했다. 감사자는 코퍼스 전체의 **의미론적
정합성**(A 문서 결정 ↔ B 문서 서술)을 보고, code-review는 **diff 자체의
결함**(이번에 새로 쓴 서술 안의 모순, 새 표면이 기존 계약과 충돌하는가)을
본다.

### 구현을 실제로 막았을 것들

1. **⭐ `{Epoch}` 표기가 실제 배치 모양과 달랐다.** Luau에서 `{Epoch}`는
   **배열**(`{[number]: Epoch}`)인데, 실제로 넘어오는 게이트 배치는
   `gate-plan.md` 4번이 확정한 `withheld : { [epoch] : true }` — **집합**이다.
   이 표기를 믿고 `ipairs`로 구현하면 배치 순회에서 **원소가 0개**가 되어
   유보됐다 풀린 emit이 전부 조용히 삼켜진다. `gate-plan.md` 4번이 애초에
   고치려던 바로 그 버그였다. → `type EpochSet = { [Epoch]: true }`로 확정.
   **집합이어야 하는 이유는 게이트 쪽 요구**다(흡수·unfold가 저절로 접혀야
   함). 이건 사용자 원 제안의 `Epoch|{Epoch}` 표기를 에이전트가 그대로
   옮기면서 **Luau 타입으로서 뭘 뜻하는지 확인 안 한** 결과다.
2. **⭐ 새 노드 시딩이 확정된 `EpochMap` 표면으로 표현 불가능했다.**
   "상류의 `Epoch`를 전부 끌어와 채운다"인데 `:With(a, b)`의 상류는
   **State이지 `Epoch`가 아니다.** 상류가 추적 중인 루트 집합은 그 State의
   `valueEpochMap` 안에만 있는데 §3 표면엔 **키 열거도 병합도 없었다.**
   → `:TrackFrom(other)` 신설(가칭 `Absorb`, 같은 날 개명 — §8 참고).
   `:Refresh`와 같은 성격으로 **이미 확정된 동작에 이름을 붙인 것**이지
   새 설계가 아니다(사용자 확정 문구가 이미 *"전부 가져와서, 실제 count 로
   둡니다"*였다). dep이 `Epoch`면 `:Sync`, State면 `:TrackFrom`으로 갈리는
   것도 같이 적었다.
3. **`GateNode` 예외가 §4에 기록돼 있지 않았다.** §4 의사코드는
   `emitEpochMap:Update(from)`을 **수신 시점에 무조건** 부르는데,
   `gate-plan.md` 4번은 게이트가 **전파할 때** `:Sync(batch)`로 갱신하는
   것으로 확정돼 있다. §4대로 구현하면 그 계약이 조용히 깨진다.
4. **설치 시 즉시 1회 발화에 `from`이 없다.** `fn(self, from)`을
   non-optional로 선언해뒀는데 등록 시점 발화엔 출처가 없다 —
   `Effect`의 확정 클로저가 `Update(nil)`을 부르게 된다. → 옵셔널로 바꾸고,
   `Effect`의 억제 플래그가 `Update`보다 **먼저** 와야 함을 명시(순서를
   뒤집으면 설치 발화가 맵을 건드려 그 파동의 첫 진짜 emit이 접힌다).

### 근거·표기 정확성

5. **`2^32` 랩이 "똑같이 도달 불가능"이라는 근거가 틀렸다.** 같은 척도(초당
   100만 `Set`)로 `2^53`은 285년인데 **`2^32`는 약 72분**이다(20만 배 차이).
   실제로 안전한 이유는 **도달 시간이 아니라 충돌 조건이 한 점**이라는 것
   ("정확히 `2^32`만큼 뒤처진 항목"이라야 하고, 한 바퀴 중 한 번이라도
   건드려지면 갱신됨). 이 문서가 바로 위 항목에서 *"근거를 정확히 적을 것"*
   이라 스스로 규정해놓고 20줄 뒤에 어긴 셈이라 그대로 정정했다.
6. `:Sync`를 "초기화에만 쓴다"고 적었으나 `gate-plan.md`가 flush 경로에서도
   쓴다 → "반환값이 필요 없다고 이미 아는 곳 둘"로 정정.
7. `ROADMAP.md`의 `Brand.luau` predicate 목록에 `isEpoch` 누락.
8. **역전된 옛 이름 잔재** — `TweenTag`(3곳)와 `Effect(fn, state?)`(4곳).
   후자는 code-review가 짚은 것보다 실제로 더 많았다
   (`architecture.md`/`lifecycle-hooks-plan.md`/`ROADMAP.md`/`README.md`).
9. **절 재편에 안 따라온 `§` 참조 3곳** — 1라운드가 같은 유형을 잡았는데도
   남아 있었다. `doc-check.py`가 `§번호`를 검증 못 하는 사각지대다.

**교훈**: 1·2번은 둘 다 **"사용자 제안의 표기를 그대로 옮겼는데 그게 실제
메커니즘과 안 맞는" 유형**이다. 승격은 문장을 옮기는 작업이 아니라 **표기가
가리키는 것이 실제로 성립하는지 확인하는 작업**이라는 게 이번의 교훈.

## 8. `:Refresh`/`:TrackFrom` — 에이전트가 이름 붙인 둘, 사용자 검토로 확정

`/code-review`까지 처리한 뒤, **에이전트가 임의로 이름 붙인 연산 둘을
사용자에게 명시적으로 올렸다.** 동작 자체는 회신에 이미 확정돼 있었지만 표면
이름은 에이전트 판단이었기 때문 — `conventions.md`의 "애매하면 임의로 정하지
말고 그 자리에서 사용자에게 보고할 것"에 해당한다.

**`:Refresh` — 그대로 확정.** 사용자가 `:Update`와 합치지 않는 이유를 직접
정리했다: *"Update 에 인자 없는건 좀 아니야. 뭔가 받아서 받은것들에 대해서
처리하겠다는건데, 리프레시는 아무래도 내가 받았던걸 처리하겠다는거라
표면적 의미 자체가 다르지."* — **인자를 받아 그것을 처리하는 연산**과
**자기가 이미 들고 있는 것을 처리하는 연산**은 표면적 의미가 달라서 오버로드로
합치면 안 된다는 것. 표면이 하나 줄어드는 것보다 이 구분이 값이 크다.

**`:Absorb` → `:TrackFrom`으로 개명.** 사용자 지적: *"absorb 는 조금 상위
요소꺼를 흡수해서 상위 요소에서 제거할것만 같은 이름이긴 하네."* 맞다 — 이
연산은 `other`를 **전혀 안 건드린다.** 덧붙여 `base/gate-plan.md`가 이미
"흡수 집합"을 **다른 뜻**(emit을 붙들고 있음)으로 쓰고 있어, 한 코퍼스 안에
같은 단어가 두 의미로 놓이는 문제도 있었다.

후보를 넷 올렸고(`TrackFrom`/`SeedFrom`/`Include`/`Adopt`) 권장안이 채택됐다.
`TrackFrom`을 고른 근거 셋:

1. **이 맵의 존재 이유를 사용자가 표현한 말이 "추적"이었다** —
   *"'내가 뭘 추적하고 있나' 가 필요하죠"*(§4의 시딩 규칙 근거). 코퍼스가 이
   맵을 설명하는 말과 메소드 이름이 일치한다.
2. **`From`이 방향을 못박아 비파괴가 드러난다** — `Absorb`가 실패한 지점.
3. **`SeedFrom`보다 오래 간다** — 동적 의존성으로 "생성 이후에 키를 더하는"
   자리가 생겨도 이름이 그대로 맞다. `Seed`는 그때 거짓말이 된다.

배제한 것도 근거가 있다 — `Extend`/`Inherit`은 `quad2-try`의 `Base:Extends`
OOP 상속이 **확인된 죽은 접근**이라 그 어휘를 되살리면 오독을 부르고,
`Extract`는 코퍼스에서 이미 "소유권을 통째로 넘긴다"는 뜻이다.

**두 이름 다 `question.md`에 안 올린다** — 같은 자리에서 확정됐으므로 열린
항목이 아니다.
