quad/.claude/session/2026-08-19-01-new-initxxx-composition-relate-guard.md
qwreey-agent-selene 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

7.7 KiB

2026-08-19 — New()의 내부 구성: InitXxx 팩토리 체이닝 + Relate 기반 멱등 Init 가드

요청: Quad.New()가 실제로 어떻게 구현돼야 하는지에 대한 사용자 아이디어 검토 요청으로 시작 — 결론까지 나서 base/에 반영, 이어서 핸드오버 감사 루프와 세션 기록 공백 점검까지 같은 세션에서 처리.

1. 제안 — New()를 InitXxx 팩토리 체이닝으로

사용자 원문: "New() 가 실행되면 Quad 를 만드는 함수가 있는것으로 처음부터 구현하는게 맞음. ... New 결과 안에 .New 함수를 넣어줌. 즉, 생성형식 자체는 비싱글톤이고, Dispatch 같은것도 Init(module) 을 받는 함수로써 ... module.Dispatch = ... 형식들로 구현되고 ... 재익스포트식으로 구현하겠다는 이야기였음. 처음부터 InitModuleName 식으로 구현하여 팩토리를 쌓아 모듈을 리턴하는 방식으로, quad v1 의 방식을 가져와봄직 하다는것."

조사: 기존 base/architecture.md 13번("모듈은 기본 싱글톤, New()는 추가 인스턴스가 필요할 때만")과 14번("pluggable 초기화는 팩토리 함수로"), module-lifecycle-plan.md가 이미 InitRoblox(Module) 형태의 backend 주입 패턴을 확정해뒀다는 걸 확인 — 이번 제안은 그 패턴을 quad-base 자기 자신의 내부 구성(Dispatch 등)에도 대칭 적용하자는 것이라 새 설계가 아니라 기존 원칙의 자연스러운 확장으로 판단. lifecycle-pattern.md가 거부한 rbvm InitNamespace 패턴(소비자가 라이브러리마다 수동으로 init을 부르는 것)과도 안 겹침 — 여기선 New() 하나만 외부에 노출되고 내부에서만 InitXxx(module)를 부름.

결론: 채택 추천 — module = {New = New} 자기참조도 이미 확정된 결정과 정확히 일치, type Dispatch = InitDispatch.Dispatch 재익스포트만 실제 Luau 동작 확인 필요하다고 남겨둠.

사용자 확인: "그거 타입 익스포트 잘 됨. 구체화 반영해줘." → 실측 확인됐다는 뜻으로 받아 module-lifecycle-plan.md에 "New()의 내부 구성" 절 신설, architecture.md 13번에서 포인터 연결.

2. 정제 — Init을 require처럼 멱등하게

사용자 원문: "Init 은 require 처럼 생각 가능한듯. Init 여러번은 한번만 작동하게 자신 모듈 최상단에 Relate 를 (함수 안 아님. 클로저 바깥) 놓고, 자신 모듈의 init 여부를 저장해. 그리고 한번만 작동하도록 두고, 자신 init 에선 필요한것들을 init 해줘. 디펜던시 느낌인거지. ... 맨 바깥 quad-base 진입점의 New 에선 모든 Init 을 그냥 실행해도 돼."

이게 1절 문서화 때 "구현 단계에서 정할 것"으로 남겨뒀던 "서브시스템 간 InitXxx 호출 순서 의존성" 문제를 실제로 푼다 — 각 InitXxx 파일이 자기 톱레벨(클로저 밖)에 local relate = Relate()를 두고 module을 weak key로 "이미 이 인스턴스에 Init됐는지"를 기록하면, 의존하는 쪽이 자기 의존성을 직접 호출해도 중복/순서 걱정이 없어짐(멱등) — require가 파일 단위로 하는 캐싱을, New()가 여러 module 인스턴스를 만들 수 있다는 차이 때문에 인스턴스 단위로 다시 구현하는 것.

relate-plan.md의 확정 API(Relate()/SetWeak/GetWeak/SetStrong/ GetStrong, "각 모듈이 자기 톱레벨에 Relate() 하나 재사용" 관례)와 정확히 부합함을 확인 — 새 메커니즘이 아니라 기존 프리미티브의 정확한 용례. module-lifecycle-plan.md에 반영, 순환 의존 대비를 위해 플래그를 실제 작업 전에 먼저 세우는 규칙도 같이 명문화.

3. 핸드오버 감사 루프 (2라운드, conventions.md의 "핸드오버 준비하고

커밋해" 절차)

바뀐 파일: base/architecture.md, base/module-lifecycle-plan.md, base/dispatch-core-plan.md, README.md, ROADMAP.md.

라운드 1(quad-doc-auditor, agentId ad95c24230306ab0f) — 확실 3건 + 의심 3건 + 사용자판단 1건:

  • 확실: architecture.md의 "M0 스캐폴딩에 주는 함의" 불릿이 이미 InitXxx 절이 답한 질문을 여전히 미결정으로 서술 / README.md 색인에 새 절 요약 누락 / module-lifecycle-plan.md의 "지금은 없음"이 날짜 없는 시한부 주장.
  • 의심: _initializedBy 상호 참조가 정의를 못 찾게 함(bind-system-plan.md 누락) / GC 인과 서술이 relate-plan.md의 "inst는 항상 weak" 규칙과 어긋나게 읽힘 / dispatch-core-plan.md가 새 절을 안 가리켜 상호참조 누락.
  • 사용자판단(문서만으론 못 정함, 이번엔 직접 판단해 처리): InitXxx 구조가 M0/M1 중 어느 마일스톤부터인지 → ROADMAP.md의 "M0 — 스켈레톤 + 기술검증" 절 실제 내용(스파이크 전용, "진짜 마일스톤 아님")을 근거로 M1로 확정, ROADMAP.md M1 체크리스트에 항목 신설.

전부 반영(위 6곳 수정 + M1 체크박스 추가).

라운드 2(agentId a1d60d10fc36621ed) — 라운드 1 수정 자체가 새 stale 2건을 만든 걸 발견:

  • architecture.md가 인용하던 "M0 스캐폴딩에 주는 함의"라는 절 제목 문구를 라운드 1 수정이 지워버려 절 인용 규약 사각지대(파일명 없는 같은 문서 내 인용이라 doc-check.py가 안 잡음) 발생 → 원래 제목 문구를 불릿 맨 앞에 복원하면서 내용만 정정.
  • "위 'M0 — 스켈레톤 + 기술검증' 절 참고"가 실제로는 ROADMAP.md 안의 절인데 파일명이 빠져 같은 문서 안인 것처럼 읽힘 → ROADMAP.md의 명시.
  • 추가로 "확실": 이 새 절 자체가 사용자 발언 3건을 인용하면서 session/2026-08-19-*.md 포인터가 없음(이 문서가 그 포인터).
  • 의심: relate-plan.md의 "언제 Relate를 쓰는가" 체크리스트에 새 용례가 안 실림 → 다섯 번째 불릿 추가.
  • 사용자판단: Init-완료 플래그 값(true)을 SetWeak/SetStrong 중 뭘로 적을지가 문서 간 안 맞음 → boolean은 GC 대상이 아니라 실질 차이는 없지만 relate-plan.md의 일반 규칙("다른 곳에서 안 붙잡는 값은 Strong") 기준 SetStrong으로 통일.

전부 반영. 라운드 3은 이 문서 작성 이후 진행 예정(아래 미해결 참고).

4. 세션 기록 공백 발견 (사용자가 감사 진행 중 별도로 제기)

사용자 질문: "세션 기록들 요즘 왜 안 적어? ... 18일 자가 하나 뿐이네."

확인 결과 — git log엔 2026-08-18에 커밋 10개(QA 1~2라운드, 감사 루프 재설계, GitHub co-author 정책, git 원격 정책 등), 2026-08-19에 커밋 1개 (QA 3라운드)가 있는데, session/엔 2026-08-18 파일이 pre-implementation-qa-applied.md 하나뿐이고 2026-08-19 파일은 이 문서 이전엔 0개였다. 즉 QA 2라운드/3라운드(todos.md 00번이 상세히 서술하는, RC-1/RC-3/RC-4를 실제로 찾아 해결한 세션들)와 tooling/research 커밋 다수가 session/ 원문 없이 커밋됨 — 실제 공백.

한계: 이 세션은 그 과거 대화의 실제 트랜스크립트에 접근할 수 없다(커밋 메시지와 현재 파일 상태만 볼 수 있음) — session/의 정의 자체가 "시행착오 포함 원문"이라, 원문을 못 본 채로 "raw log"를 지어내면 오히려 그 자체가 허위 기록이 된다. 그래서 이 문서는 이번 세션분만 원문으로 채웠고, 과거 공백(08-18 QA 2/3라운드 등)을 어떻게 처리할지는 사용자에게 별도로 물어야 함(요약만 session-summary.md에 사후 추가할지, 아예 공백으로 인정하고 넘어갈지 등 — 이 문서 자체가 그 판단의 근거 자료).