Commit graph

22 commits

Author SHA1 Message Date
56f8269236
design: M2/M3 마일스톤 순서 교체 — 반응형 코어를 먼저, 디스패치를 그 위에
`question.md`에 열려 있던 마지막 항목(마일스톤 경계)을 사용자가
**(a) 순서 교체**로 닫았다 — 이제 **M2 = 반응형 코어(Source/State/Store),
M3 = 디스패치 엔진**이다. 결정과 기각된 선택지는
`archive/question-resolved.md`의 "마일스톤 경계" 절, 경위와 시행착오는
`session/2026-08-24-02-milestone-order-swap.md`.

## 왜 (a)인가

의존이 양방향처럼 보였지만 실제로는 한 방향이었다 — 디스패치→반응형은
*본체* 의존(`setLength`가 `State<number>`를, `setOffsetSource`가
`Source<number>`를 받고 `recompute`가 `offset:Set()`을 부름)이라 우회
불가고, 반대는 *등록 표면* 의존(핸들러 셋)이라 미루면 그만이다. 옛
순서로는 디스패치 마일스톤의 `mock 대상 테스트` 체크박스조차 State 없이
불가능했고, 반응형 코어는 순수 Lua라 `luau`만으로 단독 테스트가 된다.
(b) 분할은 기존 `M2` 참조 전부를 "M2a인가 M2b인가"로 만들어 오히려 비싸고,
(c) 유지는 "순서의 소스는 `ROADMAP.md`"라는 원칙을 스스로 무효화한다.

## 그냥 맞바꾸기로는 안 끝났다

- **`Brand`/`Relate`/`LifetimeHandle`(인터페이스)이 M2 앞머리
  `### 공통 기반` 절로** — 반응형이 이 셋을 먼저 요구한다(`Source`가
  `SourceBrand`+`EpochBrand`에 등록되고, State 전파 루프가 매 발화마다
  `canExecute`를 부르며, 그 판정이 `Relate` 위에 얹힌다). 셋 다 State-free
  이자 dispatch-free라 어느 쪽에도 안 걸린다.
- **2026-08-22에 디스패치로 앞당겼던 `EpochMap`/`GateNode`/`Blocker`가
  M2로 복귀** — 앞당길 이유 자체가 순서 교체로 사라졌다. "게이팅 먼저"는
  그대로 지켜진다(게이팅이 디스패치보다 먼저 지어진다).
- **Observer/Effect 동적 경로 가드 등록과 `ObserverEffectLeafHandler`는
  M3로** — 둘 다 "핸들러를 등록한다"뿐이라 본체와 잘라내기 쉽다. 이것이
  M2가 M3에 개념상 지던 유일한 의존이고, 미뤘으므로 빌드 순서엔 역방향
  간선이 없다.
- **`EpochMap`이 State 본체보다 앞으로**(감사 5라운드 발견) — State가
  `valueEpochMap`/`emitEpochMap` 둘을 컴포지션하므로 옛 배치로는
  `State.luau`를 못 짠다. 문서 자신이 *"아래 `Source.luau`/`State.luau`가
  이걸 전제로"*라 쓰면서 그 둘이 위에 있는 형태로 증거를 남기고 있었다.

## 대가 하나 — 게이트 둘이 "바로 다음"으로 올라왔다

`question.md` 낮은 우선순위에 있던 **중간 State GC 미검증**과
**`store:GetDynamic` 위치**가 원래 반응형(옛 M3)의 게이트였는데, 반응형이
M2가 되면서 착수 직전 항목이 됐다 — 최우선 절로 승격했다. 순수 *설계*
결정 대기는 여전히 0건이다(하나는 실측 미완, 하나는 표면 위치 선택).

## 번호 재부여의 경계

라이브 문서의 `M2`/`M3` 참조 248건은 전량 새 번호로 맞췄다(코퍼스의 참조가
거의 전부 "그 내용이 사는 마일스톤"을 가리켜 기계적 맞교환으로 의미가
보존됨). **`session/`·`archive/`·`qa-request/`는 히스토리라 소급 수정하지
않았다** — 그 문서들의 `M2`/`M3`는 옛 의미(M2=디스패치, M3=반응형)이고,
이 경고를 인덱스 다섯 곳에 박아뒀다.

일괄 치환에 `\bM([23])\b`를 썼다가 Python `\w`가 유니코드라 한글이 붙은
93건(`M2는`/`M2로`)이 안 바뀌는 실수를 냈고, `git show HEAD:<경로>`로
되돌린 뒤 ASCII 경계 lookaround로 재실행했다. 그 부수로 `Relate`/
`LifetimeHandle` 참조들은 구 M2 → 신 M2로 두 번 옮겨져 **번호가 우연히
보존**됐다는 것도 드러났다(치환하면 안 되는 자리 — 감사가 잡아 되돌렸다).

## 검증

`quad-doc-auditor` 루프가 라운드마다 각도를 바꿔 **7라운드에서 새 발견
0건으로 수렴**(10→8→1→1→2→2→0). 가장 값이 큰 건 5라운드(구현 순서
시뮬레이션)로, 위 `EpochMap` 배치 오류를 잡았고 **순서 교체의 전제 자체도
검증**했다(새 M2 전체를 디스패치 심볼로 훑어 "가드 등록 둘 말고는 없음").

**수렴 뒤 사용자가 돌린 `/code-review high`가 5건을 더 잡았고 전부 유효** —
넷이 *"라벨은 치환됐는데 그 라벨을 설명하던 산문이 안 고쳐진"* 종류였고,
그중 하나는 `research/` 안의 **히스토리 블록**이 치환을 맞아 원래 논거가
문장 그대로 거짓이 된 것이었다(소급 수정 제외 대상을 세 폴더로만 잡은 게
샜다). `conventions.md`의 *"`/code-review`는 감사자를 대체하지 않는다"*가
또 재확인됐다. `doc-check.py` ERROR 0.

Co-authored-by: qwreey <me@qwreey.moe>
Claude-Session: https://claude.ai/code/session_01Jjrec9xAS7TZstMx5gi3cm
2026-08-25 00:29:15 +09:00
19cd046275
design: 손 트레이싱 6라운드 전량 처리·반영 — H-1~H-54 확정, M2/M3 설계 게이트 해소
사용자 요청("같이 하나하나 처리해나가보자. 질문 모드로 계속 물어보며")으로
발견 보고 `H-1`~`H-54`를 문항지로 만들지 않고 **갈래 선택이 필요한 것만 급한
순서로** 물어 전량 결정하고 `base/` 24개 문서에 반영했다. 결정과 근거는
`qa-request/pre-implementation-handtrace-round6-followup.md`가 소스이고,
진행 경위와 사용자 발언 원문은
`session/2026-08-24-01-handtrace-round6-resolution.md`.

**M2/M3를 막던 것이 전부 닫혔다** — 말단 핸들러 4종의 `setLength`/
`setOffsetSource` 미등록(`H-39`), `New(): Quad`가 닫힌 타입이라 `quad.Dispatch`가
타입에러인 것(`H-25`), `Effect`의 leaf 사망 cleanup 배선 부재(`H-11`),
`:List`의 좌표계 결함 둘(`H-1`/`H-2`).

## 구조가 바뀐 것 넷

- **`slot._elemIndex`**(물리 요소→인덱스 역방향 맵) 신설로 `indexOfRaw`가 O(1)
  기본 경로가 되고, `:List`의 `keyIndex`는 **단순 키 집합(`prevKeys`)**으로
  강등. 사용자 역제안 — *"raw* 가 층위를 알아야할 이유를 모르겠는 상태 …
  realElem->index 해시맵을 만들어주고, index 밀고 당기는 동작에서 이걸 같이
  업데이트해주는 편이"*. 맵을 `_elements`와 같은 층에 두니 층 분리가 오히려
  깨끗해졌다
- **`_mounted`가 "물리 인스턴스 유무"만 뜻하게 좁혀지고 `slot._physicalTarget`
  신설** — 상태가 셋이 됐다(미실체화/실체화/마운트). `raw*`는 부기를 실체화
  시점부터 항상 하고 `native*`만 가른다. 그래야 최초 population 중
  `getOffsetAt`이 성립해 `updateFn`의 `index`를 거기서 뽑을 수 있다
- **`Ref.Callbacks`가 해시맵 셋 + `:Uncallback`**(사용자 발견) — 해제가 O(1)이
  되고 `ref-plan.md`의 `#t` border 실측 항목이 폐기됐다
- **`blocker:Policy(emit)` 노출** — `Debounce`/`Throttle`이 emit을 안 쥐고 자기
  Blocker를 On/Off만 하는 정책이 된다. `Gate`엔 `Flush`/`Cancel`을 안 둔다

요소 타입 검증은 블랙리스트에서 **주입 술어 `isInst` 기반 화이트리스트**로
뒤집혔고(`H-40`), 주입 op이 둘 늘었다(`isInst`/`onDestroying` — 조합 폴백이
불가능해 미주입이면 에러).

## 사용자가 에이전트 갈래를 뒤집은 자리가 여럿

`H-1`(세 갈래가 전부 차선), `H-2`(*"부기 확정에서 length 를 확정해도 되는거
아님?"* — 부기와 물리 마운트를 분리하라는 되물음), `H-40`(브랜드 판정이
2026-08-21 인스턴스 브랜드 재작성 이후 성립 불가임을 지적), `H-33`(제 중첩
합성안이 unblock 시 디바운스 창을 새로 시작시켜 창이 안 끝난다는 지적).
`Ref.Callbacks` 해시맵화와 `Ref` 콜백의 `canExecute` 확인은 사용자가 먼저 발견.

## 반영 후 재검토 — `/code-review high` 7건 + 감사 9라운드 34건

**`/code-review high` 7건 중 셋이 이번 반영이 만든 회귀**였다 — 상태가 셋이
됐다고 산문에 쓰고 코드엔 경계 하나만 남겨 `Slot { frameA }` 생성자가
크래시하던 것, `native*`를 `_mounted`로 가리면서 그게 곧 파괴였다는 걸 놓쳐
영구 누수를 만든 것, "`:List`와 CRUD는 상호배타"라며 승인받은 분기가
**재마운트 경로를 안 봐서** 포탈을 깬 것. 셋 다 코퍼스 정합성 각도로는
구조적으로 안 보이는 종류라 *"`/code-review`는 감사자를 대체하지 않는다"*가
실측으로 재확인됐다. 같은 리뷰가 `H-11`의 두 결정이 서로 모순임을 잡아
재결정했다 — *"`EffectHandle`이 자기 `bindLifetime` 직후에 건다"*는 **그 호출부가
실재하지 않았고**, 사용자 판단으로 `bindLifetime`/`unbindLifetime`이 `isEffect`를
보고 직접 처리하는 것으로 바뀌었다.

**`quad-doc-auditor` 감사 루프는 9라운드에서 새 발견 0건으로 수렴**(라운드별
5→7→2→2→3→9→2→4→0, 각도와 목록은 followup의 E절). 한 턴에 하나씩 돌리고
라운드마다 각도를 바꿨다. 가장 많이 잡은 6라운드(9건)는 *"이 체크박스로 코드를
짜면 무엇이 나오는가"*를 물은 라운드였고, 그때 `ROADMAP.md`의 미완료 항목이
대거 stale인 게 드러났다(폐기된 `pos` 공식이 "확정"으로, 접두합 캐시 무효화
계약이 통째로 부재, `bindLifetime`이 "둘만 한다"고 적혀 `H-11`과 직접 모순).

**반복된 실패 패턴은 하나 — "고쳐야 할 자리가 N개인데 일부만 고쳤다"**:
배너를 달고 그 배너가 부정하는 문장을 안 고침(1라운드), `base/` 19개를 바꾸고
`.claude/README.md`를 한 줄도 안 고침(2라운드), 그 README를 고칠 때 11행 중
6행만(7·8라운드). 셋 다 핸드오버 체크리스트가 명시적으로 경고하는 항목이라,
규율이 없어서가 아니라 지켰는지 스스로 확인하지 않아서 생긴 실패다.

## 백로그 하나

`Fallback`/`Traceback` 중 생성된 부분 트리의 회수(`H-26`) — 그 둘이 슈가라
구현 시점에 같이 다룬다. 같이 확인된 것: `dispatch-core-plan.md`가 잔여 부기를
인스턴스 GC가 정리한다고 적은 문장은 **gcconn 불멸성과 양립하지 않는 틀린
안전망 주장**이라 삭제했다.

**M2 착수를 막는 설계 항목은 이제 없다** — 남은 건 `question.md` 2번(M2↔M3
양방향 의존, 마일스톤 순서)뿐이다. `doc-check.py` ERROR 0.

Co-authored-by: qwreey <me@qwreey.moe>
Claude-Session: https://claude.ai/code/session_01Jjrec9xAS7TZstMx5gi3cm
2026-08-24 18:54:14 +09:00
0498816c10
design: Epoch/EpochMap/Brand 전면 승격 + 해소 기록 flatten
앞 세션이 컨텍스트 피로로 미뤄둔 승격(todos 000번)을 수행하고, 코퍼스에
쌓여 있던 [해소]/[정정] 층을 걷어냈다. 감사 3라운드 + /code-review high로
15건을 잡아 전부 반영했다.

승격 — base/ 넷 + 파급 넷
- state-epoch-plan.md 재작성: Epoch 인터페이스({Revision:number}, 그 자체로
  키가 되는 unique 테이블, Source가 구조적으로 만족)와 EpochMap(Update/
  Refresh/Sync/TrackFrom) 신설. State는 EpochMap을 둘 컴포지션 —
  sourceCountMap/sourceEmitMap -> valueEpochMap/emitEpochMap. §1~§8로 재편.
- brand-plan.md 전면 재작성: 공유 레지스트리 + Brand.get(객체당 태그 하나)
  -> 인스턴스 브랜드 Brand() + :register/:is, 다중 태깅 허용. 발단은 Source가
  SourceBrand이면서 동시에 EpochBrand여야 하는데 옛 모양으로 표현 불가.
  역조회는 제거(전수 조사에서 쓰는 자리 0). weak-key/테이블 아이덴티티/
  duck-typing 기각 근거/predicate 합성은 전부 유지.
- source-state-plan.md: Source가 Epoch도 구조적으로 만족(Revision은 공개여야
  타입 레벨에서 성립), Observer 클로저가 fn(self, from: (Epoch|EpochSet)?).
-  effect-plan.md: 다중 의존성 중복 발화 미해결 항목이 닫힘 —
  EffectHandle이 자기 EpochMap을 들어 첫 번째만 통과시킨다.
- gate-plan.md/architecture.md/bind-system-plan.md/ROADMAP.md 어휘 통일,
  EpochMap.luau가 M2에도 필요하다는 것 반영(GateNode가 씀).

리비전 갱신은 bit32.bnot(-rev) — 사용자 확정
- a>0이면 a-1, 0이면 4294967295인 랩어라운드 감소. 갱신과 랩이 FASTCALL
  하나로 끝난다(luau 실측). 근거: 2^53 포화는 도달 불가능한데 그걸 피하려고
  값을 double 영역까지 키울 이유가 없다, 매번 도는 hot path다.
- 에이전트가 이걸 band(rev+1, mask)로 잘못 옮기고 "그러니 bit32가 더 싼 건
  아니다"라는 틀린 단서까지 달았다가 사용자 정정("제가 말한건 bit32.bnot(-a)
  입니다"). 세 문서에 정정 경위를 남겼다.
- 따름정리: 리비전은 증가가 아니라 감소한다. ==/~= 만 쓰는 지금 규칙에서만
  무해하다는 경고를 §2에 명시.

archive / flatten
- archive/brand-shared-registry-reversed.md 신설(옛 Brand 표면 원문).
- question.md 421->208줄: 해소 항목 18건을 archive/question-resolved.md로
  이관. 그 문서가 스스로 정한 규칙("해소하면 여기서 지우고 archive로")을
  다시 어기고 있었다. 이관분의 옛 필드명은 소급 수정하지 않고 머리에 경고만.
- todos.md 000번 삭제, "M3 착수 전 필요" 목록에서 해소 항목 일곱 제거
  (실제로 열린 건 중간 State GC와 store:GetDynamic 둘뿐).
- research/ -> reference/ 이동 둘(epoch-brand-composition,
  slot-attach-decomposition). 확정된 결정의 근거 기록은 research(상의 필요)도
  archive(뒤집힘)도 아니므로, reference의 폴더 기준에 그 용도를 명문화했다.

감사 3라운드(각도: base 정합성 / 인덱스+luau-test / archive+qa-request)
- §8에 "기각된 대안 — 게이트를 에포크 경계로" 논거 신설(재작성 때 떨어뜨렸고,
  두 문서가 서로 다른 없는 §번호를 대고 있었다).
- luau-test 스파이크 22를 done/ -> rewrite-required/(옛 Brand.set/get을 직접
  구현). STATUS.md 개수와 절 제목의 하드코딩 개수 정리.
- state-epoch/source-state가 "Revision을 증가시킨다"고 적어놓고 20줄 뒤에
  "감소한다"로 반박하던 자기모순 정정.

커밋 전 /code-review high — 9건, 전부 유효(감사자가 못 보는 축)
-  {Epoch}는 Luau에서 배열인데 실제 게이트 배치는 {[Epoch]:true} 집합.
  그대로 ipairs로 구현하면 유보됐다 풀린 emit이 전부 삼켜진다(gate-plan 4번이
  애초에 고치려던 그 버그) -> EpochSet으로 확정.
-  새 노드 시딩이 확정된 EpochMap 표면으로 표현 불가능했다(:With의 상류는
  State이지 Epoch가 아니고, 키 열거/병합 연산이 없었음) -> :TrackFrom 신설.
- GateNode 예외(emitEpochMap을 전파 시점에 갱신)가 §4에 미기록.
- 설치 발화엔 from이 없다 -> 옵셔널로, Effect의 억제 플래그가 Update보다
  먼저여야 함을 명시.
- 2^32 랩을 "똑같이 도달 불가능"이라 한 근거가 틀렸다(같은 척도로 285년 vs
  72분). 실제 안전 근거는 충돌 조건이 한 점이라는 것으로 정정.
- 그 외 :Sync 용도 충돌, isEpoch 누락, TweenTag 3곳, Effect(fn,state?) 4곳,
  § 참조 3곳.

에이전트가 이름 붙인 연산 둘은 사용자 검토로 확정
- :Refresh 유지 — "Update는 받은 것을 처리, Refresh는 내가 받았던 걸 처리라
  표면적 의미 자체가 다르다"(오버로드로 합치지 않음).
- :Absorb -> :TrackFrom 개명 — absorb는 상위에서 제거할 것처럼 읽히고,
  gate-plan이 이미 "흡수 집합"을 다른 뜻으로 쓴다. TrackFrom은 이 맵의 존재
  이유("내가 뭘 추적하고 있나")를 그대로 쓰고 From이 비파괴를 못박는다.

Epoch/EpochMap/Brand에 열린 설계 항목 없음. doc-check.py ERROR 0.

Co-authored-by: qwreey <me@qwreey.moe>
Claude-Session: https://claude.ai/code/session_01TiW21rnti9SbLgF6twtn6D
2026-08-22 01:27:18 +09:00
168d3d8dcc
docs: 핸드오버 — Epoch/Brand 제안 승격을 다음 세션 첫 작업으로 지정
사용자 지시로 승격은 다음 세션에("이 세션은 너무 길어요"), 감사는 승격
이후에 돌린다. 이번 세션에서 알게 된 것 중 base/에 안 남아 있던 것을
전부 채웠다.

- base/ 네 문서 상단에 research/epoch-brand-composition.md를 가리키는 ⚠️
  배너 신설: state-epoch-plan(두 맵 -> EpochMap 둘), source-state-plan
  (Source가 Epoch를 구조적으로 만족 + Observer 클로저 fn(self, from)),
  brand-plan(인스턴스 브랜드 전면 재작성), gate-plan(emit 페이로드 타입).
  넷 다 "기제는 그대로 유효, 표현만 바뀐다 / 승격 전엔 이 문서가 정본"을
  명시.
-  base/effect-plan.md에 다중 의존성 Effect 갭을 미해결로 기록. 이번
  세션에 발견했는데 research 문서에만 있었다 — 제안이 기각되더라도 갭은
  남으므로 base에 있어야 한다. 해법 후보(Effect가 자기 EpochMap을 듦)와
  접은 대안(deps를 한 노드로 수렴), 그리고 "useEffect처럼 N번 허용"
  선택지까지 같이 적었다.
- base/blocker-plan.md에 HasBlockedEmit == (next(withheld) ~= nil)이라는
  것과 "빈 배치면 무통지"가 그 일반화라는 것 기록(구현 시 둘을 따로 들지
  말 것).
- todos.md에 000번 신설 — 다음 세션 첫 작업, 고칠 문서 넷과 남은 미정
  하나(bit32 랩 vs +1)까지 명시.
- question.md 1번에 리비전 증가 방식 항목 추가.
- session-summary.md의 이 세션 항목이 "Gate만 다음 세션 — M2를 막는 유일한
  항목"에서 멈춰 있던 것을 전량 갱신(같은 날 Gate/에포크가 확정됐고,
  code-review 두 번에 19건이 나왔으며, Epoch/Brand 제안이 대기 중).

doc-check.py ERROR 0.

Co-authored-by: qwreey <me@qwreey.moe>
Claude-Session: https://claude.ai/code/session_01TiW21rnti9SbLgF6twtn6D
2026-08-21 23:32:48 +09:00
a4b517aee2
design: native* 물리 조작 계층 확정 + C-7("부기가 물리보다 먼저") 역전
주입 op 셋(mountInst/unmountInst/disposeInst)으로는 Move/Swap을 아예 표현할
수 없다는 지적에서 시작해 물리 조작 계층 전체를 재설계했다.

층위 정의(사용자): raw*는 그 Slot 스코프 안의 연산(평탄화 전, _elements
인덱스), native*는 확정된 offset/length로 표현되는 물리 트리 연산(평탄화 후,
절대 좌표).

표면 여섯:
  nativeInsert (target, offset, elements)
  nativeExtract(target, offset, elements, newElements?)   -- 빼되 살림
  nativeRemove (target, offset, elements, newElements?)   -- 빼면서 파괴
  nativeMove   (target, fromOffset, elements, toOffset)
  nativeSwap   (target, offsetA, elementsA, offsetB, elementsB)
  nativeDispose(element)                                   -- 트리 밖 값 파괴

- Replace는 별도 op이 아니라 newElements가 있는 Remove/Extract(Splice도 동일)
  — 제거+삽입을 한 호출로 합쳐 리플로우 2회와 그 사이 인덱스가 어긋난 창을
  없앤다
- 파괴/비파괴를 불리언이 아니라 이름으로 가름 — 공개 CRUD의 Remove/Extract
  어휘를 물려받고, Roblox의 "Parent=nil 없이 그 자리에서 Destroy" 융합을 연다
- 빠지는 요소는 반드시 배열로 넘김 — (target, offset, count)로 대상을 찾을 수
  있는 건 DOM뿐이고 Roblox는 자식이 순서 없는 집합이라 offset 역조회가 안 됨
- nativeSwap은 별도 — Move는 사이를 전부 밀지만 Swap은 가운데 고정
- 미주입이면 에러가 아니라 조합 폴백(addTag 계열과 갈리는 지점)
- 전제: 한 Slot의 물리 자식은 부모 안에서 연속 구간을 차지한다

그 여파로 4라운드 C-7 일반 계약이 역전됨 — "Length를 먼저 올려 밀어내고 그
공간에 넣는다"는 그림은 base에 물리적으로 자리를 비워둘 수단이 없어 성립하지
않는다. 규칙이 "빼기는 물리 먼저/넣기는 부기 먼저" 두 얼굴에서
"자기 자리를 정하는 것(setOffsetSource) 먼저 / 뒤를 미는 것(setLength→
recompute) 나중" 하나로 줄었다. 배치 경로(materializeSlotTree→mountSlotTree)의
부기-전량-먼저는 C6가 요구하는 별개 사안이라 그대로.
원문은 archive/bookkeeping-before-physical-reversed.md.

같이: getOffsetAt을 사용자 의사코드대로 단일 함수 + invalidAfter로 정정
(무효화는 min(invalidAfter, i) 하나, recompute도 그 캐시 위에 얹혀 O(N)).

doc-check.py ERROR 0. 상세는
qa-request/pre-implementation-qa-round5-followup.md의 L절.

Co-authored-by: qwreey <me@qwreey.moe>
2026-08-21 20:07:21 +09:00
c6fdf1b348
qa: 구현 전 QA 5라운드 — 문항지·회신·전량 반영 + 감사 2라운드
4라운드 종결 때 "안 만든다"고 했던 5라운드를 사용자 요청으로 신설(205문항).
범위를 셋으로 좁힘 — (1) 4라운드에 문항이 아예 없던 영역(project-setup /
quad-types, 그리고 문서가 아니라 실제 커밋된 M1 코드), (2) 그 이후 확정된 것
(Detach/KeyGone/Owned/attachSlot 분해), (3) 큰 문서의 심화. 회신을 4차에 걸쳐
받아 전량 반영했고, 커밋 전 감사를 각도를 바꿔 2라운드 돌렸다.

주요 확정/역전:
- slot._detached lazy화, KeyGone엔 새 값 반환도 error,
  Owned=false에서 Detach는 _detached에 안 들어감(rawUnmount)
- Slot:Replace 신설 + rawReplace/rawAdd 의사코드 신설(문서에 정의가 없었음)
- raw* 인자를 index로 전부 통일 — 오래 열려 있던 캐비엇 종결.
  래핑은 raw* 바깥에서만(공개 표면 + settle), raw*는 물리 요소만 다룸
- 물리 조작을 주입 op로(mountInst/unmountInst/disposeInst, 이름 가칭) —
  base는 Parent를 모른다는 지적. mountInst는 0-based 절대 offset을 받음
- Dispatch.setLength에 anchor(생략 시 ownerKey) — 부기 키와 생명주기 앵커
  분리, 4라운드 D-56 역전(archive로)
- Dispatch.getOffsetAt 신설(pull) + 접두합 캐시(offsetDirtyFrom),
  setOffsetSource(None)은 얼리 리턴, None의 뜻을 "발행 채널 없음"으로 정정
- recompute가 owner 베이스에서 시작(중첩 offset이 부모 베이스를 못 받던
  결함), _baseObserver로 깊은 전파, Offset Source identity 재사용(포탈),
  bk.N or 0(빈 Slot 크래시)
- Effect(fn, ...deps) 확정 — Ref도 의존성(옛 "trailing args sugar 안 만듦"
  역전), Tween:Mapped, groupClaimKeys 키 = (inst, groupValue) → k
- 게이팅 먼저(M2로 앞당김) — 다만 대상이 Blocker가 아니라 공용 Gate 노드로
  바뀌었고, 설계는 사용자 지시로 다음 세션(M2 착수를 막는 유일한 항목)

새 research 둘: gate-primitive.md(다음 세션이 이어받을 재료),
state-epoch-validation.md(전파 중 Get이 섞인 값을 캐시하는 glitch — 정확성
결정이라 M3 전 결론 필요).

감사가 잡은 것 중 큰 것: 확정한 Owned가 Slot:List 시그니처에 배선이 안 돼
코드에 도달 못 하던 것, effect-plan.md의 역전 배너 없는 자기모순,
그리고 손대지 않은 문서(ROADMAP 백로그·debounce-throttle-plan)가 "Gate는
M3에서"로 남아 있던 사각지대.

doc-check.py ERROR 0. 상세는 qa-request/pre-implementation-qa-round5-followup.md
(A~K절, 마지막이 최신).

Co-authored-by: qwreey <me@qwreey.moe>
2026-08-21 18:19:58 +09:00
9b7f847014
design: Detach 보존 주체/KeyGone/Owned 확정 + attachSlot 분해
QA 4라운드 followup의 마지막 열린 항목(F-3)이 사용자 회신으로 전량
닫히면서 :List 요소 소유권 모델과 attachSlot 책임 분해를 base/에 반영.

- Detach 보존 주체를 userdata → slot._detached 필드로 전면 정정.
  근거는 gcconn 트릭 — detach된 quad-제작 Instance는 GC 폴백이 없어
  명시적 정리 경로가 필수인데 userdata는 :List에게 opaque라 처분 불가.
  재-Detach는 nop, prev 반환은 재마운트. raw 3형제(rawRemove/rawUnmount/
  rawDetach)로 "파괴하는가"와 "소유권을 놓는가"를 분리.
- KeyGone 센티널 신설 — 키가 사라진 자리도 조용히 처분하지 않고
  updateFn(KeyGone, 0, offset, prev, ud)로 한 번 더 묻는다. owner 사망
  시 최종 정리는 mountSlotTree가 거는 Effect가 담당.
- Owned 설치 플래그 신설 — Detach(사이클 단위)와 직교하는 축.
  state<Frame> 의미론 충돌(C-2)이 이걸로 닫힘.
- attachSlot을 materializeSlotTree(부기) + mountSlotTree(물리)로 분해.
  "부모에게 미는 길이는 최종값"(C6)과 "부기가 물리보다 먼저"(C7)가 한
  함수 안에선 동시 만족 불가라는 진단이 근거. 공개 표면은 두 줄짜리
  래퍼로 유지해 호출부 무변경. research/slot-attach-decomposition.md 확정.
- ROADMAP M6의 옛 Detach 서술 2건과 미결 마커 정정, question.md/todos.md
  해소 반영, session/2026-08-21-01 원문 + session-summary 색인 공백 4건 보강.

doc-check.py ERROR 0.

Co-authored-by: qwreey <me@qwreey.moe>
2026-08-21 11:55:11 +09:00
871c582771
tooling: 핸드오버 준비 — session-summary.md 색인 공백 + ROADMAP/CLAUDE.md stale 정정
session-summary.md에 오늘 세션 04~08 색인 항목이 통째로 빠져 있던 걸 발견해
신설. 더 크게는 CLAUDE.md/project-context.md/ROADMAP.md가 "구현 아직 시작
전"이라는 낡은 전제를 깔고 있었는데, 실측해보니 M0 스파이크 4개와 M1
스캐폴딩 대부분이 이미 완료돼 있어 전부 정정(ROADMAP 체크박스 갱신 포함,
wally.toml→pesde.toml 표기도 같이 정정). quad-roblox-types 백로그는
todos.md/ROADMAP.md M5에 짧은 포인터 보강.

Co-authored-by: qwreey <me@qwreey.moe>
2026-08-19 20:47:31 +09:00
14f733dde1
design: Debounce/Throttle 마지막 판단 대기 4개 닫고 base/로 승격
의미론은 (A) emit-gate로 확정(value-hold 안은 laziness와 상충해 철회),
제어 핸들은 개별 Ref 아웃파라미터 + 전체 팩토리 브로드캐스트(weak
레지스트리)로 수렴, Time/MaxTime은 number|State<number> 허용(스케줄
시점에만 폴링), 이름은 Debounce/Throttle 유지로 확정. 전부 닫히면서
quad-base에 새 코어 메커니즘을 안 더하는 순수 슈가로 재평가됨(Blocker의
gated state + Ref + 주입 op 2개 위에 전부 얹힘) — 우선순위 서술도 갱신.

Co-authored-by: qwreey <me@qwreey.moe>
2026-08-19 02:11:11 +09:00
2348ea8058
design: PopOnly 가칭을 Detach로 리네임 확정 + 공개 표면 위치 확정
`:List` reconcile의 비파괴 반환 sentinel 이름을 사용자와 후보 검토(Bench/
Stash/Hold/Detach 등) 끝에 Detach로 확정 — 이미 있는 Extract(호출자 직접
호출, 명령형 소유권 이관)와 동사가 겹쳐도 "화면에서만 떼고 관리 주체는
reconcile"이라는 의미가 자연스럽게 구분됨. 공개 표면 위치도 같이 확정 —
Slot이 함수라 Slot.Detach로 못 붙이므로, None sentinel의 선례(공개 표면은
패키지 최상위 export, 정의는 관련 로직 옆)를 그대로 따름.

base/slot-plan.md 전량 반영(가칭 표기 제거, 이름/배치 두 결정 불릿 신설),
question.md/todos.md/ROADMAP.md/archive/question-resolved.md/README.md
인덱스 갱신, session/2026-08-19-02-*.md로 논의 원문 남김. 키 소멸 시 홀드
중이던 요소 처분 문제는 이름과 무관한 별개 항목으로 여전히 미결.

quad-doc-auditor 1라운드(무발견) 거쳐 doc-check.py ERROR 0 확인 후 커밋.

Co-authored-by: qwreey <me@qwreey.moe>
2026-08-19 01:46:18 +09:00
4be9373124
design: New()의 내부 구성 확정 — InitXxx 팩토리 체이닝 + Relate 기반 멱등 Init 가드
InitRoblox(Module) backend 주입 패턴을 quad-base 자기 내부(Dispatch 등)에도
대칭 적용 — 각 서브시스템이 InitXxx(module)로 module을 뮤테이션. 서브시스템
간 호출 순서 문제는 각 InitXxx 파일 톱레벨에 Relate() 하나를 두고 module을
weak key 삼아 인스턴스별 완료 여부를 기록해 require처럼 멱등하게 만들어
해소(relate-plan.md 체크리스트에도 용례 추가). module-lifecycle-plan.md에
"New()의 내부 구성" 절 신설, architecture.md/dispatch-core-plan.md/
ROADMAP.md(M1 체크리스트)에서 상호 참조.

핸드오버 감사 루프 4라운드(무발견 1회로 수렴) — 라운드 1~2는 절 인용
사각지대·상호참조 누락·SetWeak/SetStrong 일관성을 잡았고, 라운드 3~4는 그
수정 자체가 남긴 커밋 개수/날짜 오기, 원문 인용 파라프레이즈 등을 추가로
잡음. 부수로 session/ 기록 공백(2026-08-18/19 다수 커밋에 원문 누락)을
발견해 이번 세션분만 session/2026-08-19-01-*.md로 남김 — 과거 공백 처리는
사용자 확인 대기.

Co-authored-by: qwreey <me@qwreey.moe>
2026-08-19 01:16:26 +09:00
d768e4cbc5
tooling: 감사 루프를 병렬 다패스에서 순차 1패스/턴으로 재설계
사용자 지침 — "시간이 걸려도 상관 없으니, 차라리 병렬 에이전트를 덜 써줘.
그냥 한 턴에 하나씩만 사용하고, 0 이 나올때 까지 턴 수를 늘리는게
나아보임. 토큰을 너무 많이 소비해서 다른 작업을 못 하고, 세션 한도에
닿더라고."

- quad-doc-auditor는 한 턴에 하나만 호출(병렬 금지) — 2026-08-16의
  "최소 2개, 변경이 많으면 3~4개" 지침을 대체. 커버리지는 병렬 폭이
  아니라 턴 수로 얻는다. 근거 실측: 전 코퍼스 한 패스가 서브에이전트
  토큰 21만/툴 호출 82회, 4개 병렬이면 한 번에 80만대.
- 프롬프트로 범위를 diff로 좁힐 것을 신설(바뀐 파일 + 그걸 인용하는 곳).
  전 코퍼스 스윕은 오래 안 돌렸을 때만. 라운드마다 각도를 바꿔 병렬로
  얻던 폭을 턴으로 대체.
- 종료 조건을 "무발견 2연속"에서 "무발견 1회"로 완화(2연속은 병렬 다패스
  전제였음). 비용 때문에 중간에 멈출 때도 몇 라운드에서 왜 멈췄는지
  반드시 보고.
- /code-review는 감사자를 대체하지 않는다를 실측과 함께 명문화 — 이번
  핸드오버에서 감사자 1패스(1건) 뒤 /code-review high가 10건을 더 잡았고
  전부 유효했다(보는 축이 다름). 사용자만 호출 가능하므로 큰 변경 커밋
  전엔 돌릴지 물어볼 것.

Co-authored-by: qwreey <me@qwreey.moe>
2026-08-18 19:45:36 +09:00
8b57cfbb3c
qa: 구현 전 QA 1라운드 결과를 base/에 전량 반영
`.claude/pre-implementation-qa.md`(사용자가 base/ 확정 문서를 문항으로
재심사한 결과)를 실제 문서에 반영하고, 그 문서를 qa-request/로 옮기며
1라운드임을 파일명·제목에 명시(2라운드는 새 파일).

그대로 구현하면 반대로 돌던 것 2건:
- canBound의 판정 방향이 이름과 반대였음 → canBound(v) == not
  isBoundAlive(v), 게이트는 전부 `if not canBound(v) then error(...)`.
  canExecute와는 값이 같은 게 아니라 서로의 부정이고, 그게 오히려 이름
  분리의 명분이 됨(옛 근거 "값이 항상 같다"는 폐기).
- gcconn/gchold 보관이 SetStrong으로 적혀 있었음 → SetWeak. 근거 문장까지
  틀렸던 것이라 같이 교체(그대로 짰으면 두-Relate 상호 강참조 누수).

설계가 바뀐 것:
- Dispatch.drive의 None 스킵 분기 폐기 → NoneHandler는 재귀 전담,
  NilHandler 신설(k=number and v==nil 말단이 setLength/setOffsetSource
  등록). 깨진 전제는 "배열 파트의 None은 process를 안 탄다".
- Length/Offset 등록 책임이 "처음 매치한 Handler" → 말단 Handler.
- 이벤트 disconnect 센티널 false → None/nil.
- Ref 내부 구조를 .Callbacks 분리 + 평범한 .Value 필드로 단순화,
  RefLeafHandler에 빠져 있던 type(k)=="number" 추가(leaf는 배열 전용).
- :List reconcile의 nil 리턴은 다시 파괴가 기본, 값 교체와 PopOnly(가칭)만
  비파괴.
- base 소유 Fallback Handler 등록 주체를 백엔드 팩토리 → quad-base 자신으로
  재역전(백엔드 미로드 시 안내 에러 경로가 안 돌았음).
- "이벤트 콜백 시그니처는 Luau가 검증 못 한다"가 거짓임이 사용자 반례로
  확인 → onchange-plan.md의 파생 근거까지 교체(결론은 유지).

이름/표면: DI → D(Declarative) 확정 및 전수 반영, New 커링 + D는 전량
코드 생성, Attribute.Merged/Overridden 둘 다 제공, Quad.debug 신설,
store "key" 문자열 커링 기각(→ store:GetDynamic).

판단이 갈리던 4건(PopOnly 채택 / D-7 재역전 / NoneHandler·NilHandler 역할
분담 / 동적 키 경로)은 사용자에게 물어 확정.

커밋 전 검증: quad-doc-auditor 1패스가 1건, 사용자가 돌린
`/code-review high`가 10건을 더 잡아 전부 반영(ROADMAP이 SL-3 역전을 안
따라오던 것, 설계 갭 2건은 새 열린 질문으로 등록). doc-check.py ERROR 0.

Co-authored-by: qwreey <me@qwreey.moe>
2026-08-18 19:39:03 +09:00
40a5daf694
tooling: 절 인용 규약 신설 + doc-check 절 참조를 ERROR 게이트로 승격
사용자 제기 — doc-check.py가 정규식으로 결정론적 판정을 하는데 표기가 흔들리면
문제가 커지니, 정규식을 늘리기보다 "예상 가능 범위"를 컨벤션으로 좁히는 게 싸지
않냐. 실측해보니 날짜 표기는 이미 100% 균일해서 고칠 게 없었고(강제 장치 없이),
드리프트는 절 인용 쪽이었다 — WARN 86건 중 78건(91%)이 절 참조 불일치.

핵심은 그 78건이 코퍼스가 지저분한 게 아니라 **검사기가 못 읽는 것**이었다는
점이다. 이 코퍼스는 `**볼드**` 줄을 하위 절로 쓰는데 headings()가 `#`만 봤다.

## 동작 변화 (문서 정정으로만 보이지만 게이트가 바뀐다)

- 절 참조 불일치가 **WARN → ERROR**. 이제 절 인용 오류가 커밋을 막는다.
- 절 인식이 `#` 헤딩 + `**볼드**` 절로 확장. 단 볼드는 **빈 줄 다음이나
  리스트 항목 머리**만 인정 — 문단이 줄바꿈되며 우연히 줄머리에 걸린 강조를
  절로 오인하던 걸 커밋 전 감사가 잡아 조였다.
- 인용 길이 상한 60→160자. 60자를 넘으면 매칭 자체가 안 걸려 검사에서
  **조용히** 빠져나갔음(위양성보다 나쁜 구멍).
- 비교를 공백 무시로(줄바꿈 인용 대응), 선두 장식·상태/날짜 태그 정규화,
  `initreq/` 대상 인용은 절 검사 면제(읽기 전용 외부 원본).

## 규약

`conventions.md`에 "문서 표기 규약" 절 신설 — 절 인용 규약(의역 금지, 헤딩은
부분문자열/볼드는 앞부분일치, 태그로 닫히는 볼드 캐비엇, blockquote 함정),
세션은 산문 서수 말고 파일 ID로 지칭. 날짜 마커 라벨 어휘 닫기는 사용자 판단
으로 기각(기계 검사 대상이 아니라 읽는 쪽 판단 재료).

## 결과

절 참조 불일치 78 → 0. 36건은 검사기 수정으로 사라졌고(애초에 위양성), 42건은
인용을 실제 절 제목으로 손으로 고쳤다. 마지막 15건은 서브에이전트 3개에 병렬
위임해 추적 — **설계 서술이 유실된 건은 0건**, 대부분 코드 주석·본문 산문·
주제명처럼 애초에 절이 아닌 걸 절로 인용해온 것이었다.

부수로 드러나 같이 고친 것: onchange-plan이 9차 분할 때 일부러 안 옮긴 절을
잘못된 파일로 가리키던 것, brand-plan이 이미 이행된 정정을 "정정 대상"이라
부르던 것, ROADMAP의 blockquote가 인용 줄바꿈 때문에 깨져 있던 것,
pre-implementation-audit의 해소된 항목이 "아직 안 고침" 절에 남아 있던 것
(사용자 결정으로 "이미 고침"으로 이동).

커밋 전 감사 4라운드(에이전트 8개)를 돌렸고, 발견 추이는 2→2→1→0이다.
매 라운드 발견이 "직전 라운드 수정이 만든 새 결함"이었던 게 특징 — 규약을
세우는 커밋이 그 규약의 첫 위반자가 된다는 걸 실측으로 확인했다. 상세는
.claude/session/2026-08-16-03-doc-check-section-convention.md.

부수: __pycache__를 .gitignore에 추가하고 추적 해제(32e9db0에 실수로 딸려
들어가 있었음). quad-doc-auditor에 작업 트리를 바꾸는 git 명령 금지 규약 추가
— 감사자가 git stash를 걸어 메인 세션 스테이지가 반복적으로 풀렸다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011zk7XHkSfiBfdPLZUQHdZf
2026-08-16 10:47:29 +09:00
01c4abf727
docs: 감사 4라운드 반영 + const는 HUMAN_TODO로 이관(사용자 결정)
사용자 결정 — const 바인딩 재검토는 에이전트가 정할 문제가 아님. 논거:
pesde의 타입 추출(d.ts처럼 types emit)류 툴링이 미성숙해 const를 제공 못
하고 언제 가능해지는지도 불명확하며, 이는 사용자가 확인된 정보로 제공하는
게 맞다는 것. question.md에서 닫고 archive로 옮긴 뒤 HUMAN_TODO.md 8번
신설 — 사용자가 시점을 파악하거나 가능해질 때 에이전트에 알림. 그때까지
에이전트는 "일단 local로" 원칙 유지. architecture.md에 날짜와 논거 반영.

감사 4라운드(설계 코퍼스 각도):
- todos.md의 개수 하드코딩 2건 제거("체크리스트 8개", "11개 전원 완료")
  — 이 프로젝트 자신의 "개수는 소스 하나만" 규칙 위반이던 기존 항목
- 코퍼스 확실 발견 0건(설계 문서 쪽은 깨끗)

주입 경로 실측 둘:
- 에이전트 정의는 이번엔 HEAD보다 1커밋 전 — 뒤처지는 폭이 실행마다 다름을
  관측표에 4번째 행으로 추가
- CLAUDE.md @import 컨텍스트는 세션 시작 시점에 고정됨(감사자가 받은 것이
  세션 시작 커밋과 일치, 그 사이 7커밋). 메인 세션도 동일해 옛 conventions.md
  를 들고 있었음. 동작이 명확한 별개 경로라 conventions.md에 규칙으로 명시 —
  @import 파일을 고친 세션은 기억이 아니라 파일을 직접 Read해서 따를 것

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 04:01:29 +09:00
e0b6c992fa
docs: '감사자가 opus' 관측은 뷰잉 이슈로 해소 — 최상단 배너는 세션 헤더
서브에이전트 뷰 최상단의 'Opus 5 · Claude Max'는 Claude Code 세션 헤더(메인
모델)이지 서브에이전트 모델이 아님(사용자가 화면 직접 확인). 감사자는 다섯
실행 전부 message.model=claude-sonnet-5이고 frontmatter model: sonnet은 정상
동작. 모델을 잘못 읽을 자리 셋(문자열 grep이 usage.iterations에 낚임 / 세션
헤더 / 폐기된 워크플로의 픽스 에이전트는 실제 opus였음)과 신뢰할 소스
하나(트랜스크립트 최상위 message.model)를 세션 로그에 기록.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 03:48:49 +09:00
1935dd4670
docs: "커밋된 HEAD에서 읽힌다"도 반증 — 가설 세우지 않고 관측표만 남김
2라운드 감사자 둘이 독립적으로 같은 반증을 가져옴: 그들이 받은 정의
지시문이 어느 커밋과도 일치하지 않는 하이브리드였음(배너는 구버전,
"출력 형식"의 사용자 판단 문단은 신버전). git log -S로 독립 확인 — 그
텍스트는 커밋된 적 없는 중간 워킹트리 상태였다.

이 세션은 같은 문제에 세 번 결론을 냈고 앞의 둘("세션 시작 스냅샷",
"커밋된 HEAD")이 다 틀렸으므로 세 번째 가설을 세우지 않는다. 관측표와
실무 규칙 하나만 남김 — 정의를 고쳐도 반영됐다고 가정하지 말고, 중요하면
마커 문구를 넣어 감사자에게 물어 확인할 것(이 반증이 정확히 그 방법으로
나왔음: 2라운드 프롬프트에 마커 확인을 끼워 물었다).

memory: project → Write/Edit 결론은 유지 — 제거 이후 후보 텍스트가 전부
그 옵션을 안 가지므로 정의 불확실성의 영향을 안 받음. 미해결은 tools:
필드 미반영뿐.

2라운드 감사 발견 반영:
- todos.md 항목 7 헤더가 "남은 건 (d) 하나"인데 본문은 (d) 해소라 자기모순
- README.md의 conventions.md 색인 행에 신설된 "설계 원칙" 절 누락
- conventions.md의 "출처 없는 원칙" 경위 서술 중복 축약
- 사용자 판단 등급 판정 기준을 quad-doc-auditor.md "출력 형식"으로 단일화
- session 로그 §2에 "뒤에서 정정됨" 포인터(구체 결론은 안 적음 — 두 번
  뒤집힌 자리라 또 stale해짐)

감사자 모델 재확인: 다섯 실행 전부 message.model=sonnet. 실행마다
message.usage.iterations[]에 opus 항목이 1개씩 붙는 게 오독 원인.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 03:43:38 +09:00
3721692ed4
chore: agent-memory 추적 시작 — 커밋 전 노출 스캔 + 낡은 결론 정정
사용자 결정으로 .claude/agent-memory/(감사 에이전트의 영속 메모리)를 커밋해
추적하기로 함. 논거: 개발 환경이 다수라 메모리가 레포를 따라다녀야 하고,
실 기록이지 빌드 디펜던시가 아니며, 환경 노출 위험은 SAFETY.md의 파이프라인
(컨테이너 개발 → 프라이빗 git → 검토 후 머징)의 마지막 사람 감사가 방어선.

커밋 전 처리:
- 절대경로/이메일/키/토큰 패턴 스캔 — 노출 소지 없음(히트는 전부 frontmatter
  name: 과 마크다운 링크)
- project_agent_prompt_caching_bug_reproduced.md가 아직 "캐시 가설"로 서술돼
  있어 커밋된 HEAD 모델로 다시 씀(같은 정정으로 memory: project → Write/Edit
  진단이 지지됨을 반영), MEMORY.md 색인 줄도 갱신

README.md의 agent-memory/ 행에 결정과 사용자 논거를 기록 — 추적하는 쪽으로
정해졌으므로 이 폴더 내용도 감사 대상임을 명시.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 03:38:07 +09:00
07c77e5a5a
docs: 감사 루프 재설계(워크플로 폐기) + 정의 파일은 커밋된 HEAD에서 읽힘으로 재정정
사용자 결정 반영:
- 감사 루프를 Workflow에서 "메인이 quad-doc-auditor를 병렬 호출 → 메인이
  일괄 수정 → 반복"으로 재설계. .claude/workflows/quad-handover-audit.js 삭제,
  절차 소스는 conventions.md "작업 방식". 폐기 근거 셋 — 토큰 과다, 파일별
  픽스 에이전트가 또 부정확한 서술을 생산, 서브에이전트는 사용자에게 못 물음.
- 패스 수는 최소 2에서 변경 규모에 따라 증가. 감사자 모델은 sonnet 유지
  (haiku 배제).
- 출처 없던 원칙 "드문 오용/가상 미래 요구까지 방어·최적화하려고 구조를
  복잡하게 만들지 않는다"를 conventions.md "설계 원칙" 절로 명문화(선택지 a).
  modifier-plan.md 인용을 그쪽으로 재조준, question.md 항목은 archive로 이전.
- 신설 관례: 사용자 발언을 근거로 인용할 때 결론만 적지 말고 논거까지 남길 것
  (논거 원문은 session/에, 라이브 문서는 결론+짧은 논거+포인터).

 재정정 — 직전 커밋의 "정의 파일은 세션 시작 시점 스냅샷" 결론은 틀렸음.
정의는 워킹트리가 아니라 **커밋된 HEAD**에서 읽힌다(감사 패스가 받은 지시문이
세션 도중 만든 HEAD 커밋의 blob과 바이트 단위로 동일, git rev-parse로 독립
확인). 규칙이 "재시작"에서 "고쳤으면 커밋 후 실행"으로 싸짐. 이 정정으로
오래 미확정이던 (d)도 해소 — memory: project가 Write/Edit을 딸려온다는 진단이
맞았고, "빼도 그대로"로 보였던 건 제거가 아직 커밋 안 됐던 탓. 남은 미해결은
tools: 필드 미반영뿐.

첫 감사 라운드(새 절차) 반영: 자기 메모리 2건의 stale 서술,
documentation-content-map.md "943줄, 최대 문서"(실측 203줄, 최대는 slot-plan
1970줄), README.md 패스 수 하드코딩. 직전 커밋의 미재감사 6건은 회귀 없음으로
확인해 todos.md ⚠️ 블록 닫음.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 03:32:09 +09:00
134379632f
docs: 스냅샷 주장 축소 — 워크플로는 name=스냅샷/scriptPath=실시간, 재감사 누락 명시
직전 커밋(298dac2)이 "정의 파일은 세션 시작 시점 스냅샷으로 고정된다
(에이전트·워크플로 공통)"고 일반화했는데, 그건 증거보다 센 주장이었다 —
scriptPath 경로를 테스트한 적이 없었다. 프로브로 갈랐음:

- Workflow({name}) = 세션 시작 시점 스냅샷 (실행된 스크립트가 세션 시작
  상태와 바이트 단위 동일, 같은 세션 편집 반영 0)
- Workflow({scriptPath}) = 디스크 실시간 (세션 시작 후 새로 만든 스크립트가
  실행되고, 고친 뒤 다시 부르니 고친 값이 반환됨)

따라서 워크플로 쪽 해법은 세션 재시작이 아니라 scriptPath다 — conventions.md의
핸드오버 감사 절차에 반영. 에이전트 정의 stale은 자기 보고뿐이라 근거 등급이
낮음을 명시하고(Grep/Glob 불일치와 같은 등급), 우회 수단이 없으니 재시작을
보수적 해법으로 유지.

또 첫 실동이 수렴 못 하고 최대 라운드로 끊긴 결과, 마지막 라운드의 발견
6건이 반영만 되고 재감사되지 않은 채 커밋됐다는 것을 todos.md/세션 로그에
명시 — 다음 실동의 첫 임무. README.md의 audit/ "현재 6개"(실제 7개)도
폴더-소스로 전환.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 03:05:40 +09:00
298dac2510
docs: 감사 툴링 재시작 검증 + 핸드오버 감사 첫 실동(수렴 실패), 인용 3건 정정
전 세션이 "재시작해야만 확인 가능"으로 남긴 3건을 전부 닫음:
- @import 3개(conventions/project-context/todos) 실제 로드 확인
- quad-doc-auditor 레지스트리 등록 확인(전 세션 전멸은 .claude/agents/가
  세션 도중 생긴 디렉토리였던 탓)
- frontmatter model: sonnet 반영 확인(트랜스크립트에 claude-sonnet-5 기록,
  워크플로에 opts.model 명시 불필요)

정의 파일은 세션 시작 시점 스냅샷으로 고정된다는 것을 1차 증거로 확정 —
quad-handover-audit이 실제 실행한 스크립트가 세션 시작 시점 상태와 바이트
단위로 동일했고 같은 세션의 편집은 반영 0. 에이전트 정의도 동일.
정의를 고쳤으면 재시작 뒤에 감사를 돌릴 것(안 그러면 거짓 초록불).
전 세션 감사가 남긴 긴 가설 배너(80줄)를 검증된 것만 남겨 압축.

quad-handover-audit 첫 실동: 에이전트 67개/6라운드, 수렴 실패
(새 발견 28→15→16→7→11→6, 라운드5에서 되레 증가). MAX_ROUNDS와
"연속 dry 2회" 조건 재검토 필요 — 결과 자체는 위 스냅샷 문제로 옛
스크립트가 돈 것이라 재시작 후 재실동 대상.

감사가 잡은 것 반영: slot-plan.md 정정 배너가 그 뒤 재역전(retract=언마운트)을
놓치고 있던 것, "spikes 44개"(실제 48개) 류 하드코딩 개수의 단일 소스화,
doc-check.py docstring이 검사 심각도를 실제 코드와 다르게 서술하던 것 등.

인용 출처 3건 재분류 — 2건은 인용 대상만 틀린 것이라 실제 소스로 재조준
(v1-compat-plan.md→component-composition-plan.md+store-plan.md,
pre-implementation-audit.md→ROADMAP.md). 진짜 출처가 없는 1건
(modifier-plan.md:536)만 question.md 3번으로 올려 사용자 판단 대기.

워크플로 개선: 반환값에 findings 추가(커밋 전 diff 리뷰 근거),
totalFindingsFixed→findingsSentToFix 개명(과대계상), 반영 에이전트 sonnet 명시.

.claude/agent-memory/는 의도적으로 커밋 제외(추적 여부는 사용자 판단).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 03:02:33 +09:00
8aeec7644f
docs: CLAUDE.md 4분할(매 세션 로드 1537→354줄), 워크플로 가짜 초록불 수정
## CLAUDE.md 분할
1537줄이라 (a) 사람이 검토 불가, (b) 공식 권장치(파일당 200줄) 7.7배
초과로 지침 준수도 자체가 저하, (c) 긴 파일 편집 시 에이전트 실수 증가.

  CLAUDE.md (39줄, 진입점)
  ├─ @.claude/conventions.md      언어/모델 관례 + 작업 방식
  ├─ @.claude/project-context.md  프로젝트 설명 + 문서 구조
  └─ @.claude/todos.md            지금 할 일
     .claude/session-summary.md   ← import 안 함(의도적, 온디맨드)

세션 히스토리 1231줄(전체의 80%)은 그 문서 스스로 "항상 읽을 필요 없음,
base/가 소스"라고 명시해온 색인이라 @import에서 뺐음. 내용 유실 없음
(1537→1647줄, 추가 헤더만큼 증가).

주의: @import는 컨텍스트를 줄이지 않음(전부 로드됨). 분할이 사는 건
사람 검토성 + 편집 정확도 + 파일 단위 자동생성 가능성.
CLAUDE.md 계열의 블록 HTML 주석은 주입 전 제거되므로 지시는 본문에 쓸 것.

## 워크플로 가짜 초록불 수정
첫 실측에서 감사 에이전트 6개 전원 실패했는데 converged:true가 나왔음
(전멸하면 fresh가 비어 "깨끗한 라운드"와 구분 불가). 감사 도구 최악의
실패 모드라 (1) 전멸이면 throw, (2) 반영 에이전트 실패 시 그 발견을
seen에서 빼 다음 라운드가 재시도하도록 수정.

## 부수
- 분할로 깨진 상호참조 20여 곳 정정(병렬 에이전트 3개).
  ref-plan.md:541의 사전 존재 오류(→ pre-implementation-audit 1-5)도 정정.
- doc-check.py: 새 파일 4개를 OURS에 등록(안 하면 깨진 참조가 WARN으로만
  잡힘), is_history()로 session-summary.md를 archive/와 같이 면제.
- doc-include-plan.md: 목적지가 통째로 생성되는 파일이 되면서 양방향
  마커 설계의 절반(목적지 마커)이 불필요해져 단방향 생성으로 단순화.

doc-check.py ERROR 0 유지.

미해결: session-summary.md 자동생성 미착수(91개 세션 파일 마커 삽입 필요),
orphan 인용 3건(modifier-plan.md:536, v1-compat-plan.md:50,
pre-implementation-audit.md:434 — 분할 이전부터 존재).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HvEKn9f67kkx2nGLG8PsP
2026-08-16 01:35:02 +09:00