# notes — 70 · 정책 문서 뷰어 마일스톤 2 (읽는 표면 최소본)

계획서: `.claude/plans/insuniverse-docs-70-viewer-m2.plan.md`
구현 리뷰와 이탈 기록은 여기 있다 — 계획서 본문에 쓰면 `mccp-plan-codex` receipt 의 `plan_hash`
가 움직여 stale 로 떨어진다.

## Codex Implementation Review

> Codex skipped per MCCP_CODEX_DISABLED=1 (env-level policy)

- 호출: `node .../scripts/lib/codex-invoke.js adversarial-review` (fail-closed Bash wrapper, v0.2.2)
- 정책 봉인: `codex-policy.js seal` 이 이 워크트리 게이트 임시 경로에 `codex_disabled=true` 를
  기록했다. 이후 어떤 라운드도 이 변수를 해제하지 않는다
- 라운드 수: 1 (R1 에서 `disabled` 로 short-circuit — R2 는 존재하지 않는다)
- 합치 결론: 판정할 발견이 없다. `codex_verdict=skipped` 이며 근거는 `codex_disabled` 다 —
  승인이 아니라 **리뷰 부재의 정직한 기록**이고, 교차 게이트 dedupe 가 fail-closed 라
  `/mccp:pr` 에서 PR-Codex 는 그대로 자기 판정을 낸다
- YAGNI Triage: 발견 0건 — Codex 가 실행되지 않았으므로 판정할 항목이 없다
- Deferred to backlog: 0 (이 창의 발견 5건은 아래 §이 창의 발견 이 backlog 로 보낸 것이며 Codex 산출이 아니다)
- Open Questions: 없음

### 구현 시점 결정 (Codex 가 돌았다면 도전받았을 자리)

Codex 가 비활성이라 아래는 **리뷰받지 않은 결정**이다. 기록으로 남긴다.

| # | 결정 | 대안 | 채택 사유 |
|---|---|---|---|
| D1 | 문서당 HTML 1개 + 브라우저 페이지 이동 | SPA 라우터 + `fetch` 지연 로드 | 관측 8 — `file://` 에서 `fetch` 와 ESM import 가 막힌다. 독자가 서버를 못 띄운다. **Whale 실측으로 확인**(§브라우저 실측) |
| D2 | 검색 인덱스를 클래식 `<script src>` 로 싣는 `search-index.js` | `search-index.json` + `fetch` | 같은 이유. 실측에서 `file://` 로 310항목 · 538절 색인이 그대로 적재됐다 |
| D3 | 마크다운 본문을 원문 `<pre>` 로 싣되 코드·강조만 **줄 단위로** 인라인 변환 | 전체 마크다운 렌더러 자작 | UI5·UI7. 여러 줄 매칭은 짝 잃은 백틱 하나가 뒤 문단을 통째로 `<code>` 로 삼킨다 |
| D4 | 모든 문서 문자열이 `escapeHtml` 을 통과한다 | 신뢰 입력으로 취급 | 문서 본문이 서브모듈 코드를 인용해 `<`·`&` 가 실재한다. XSS 이전에 **렌더 정확성** 문제다 |
| D5 | 산출 파일명은 `doc_id` 가 아니라 문서 경로 slug | `doc_id` 직접 사용 | `doc_id` 는 문서 간 중복이 있다(`00-ia` 가 `20-site` 와 `30-admin` 둘). 생성기가 slug 충돌을 이름 있는 실패로 낸다 |
| D6 | 조각 종류를 **템플릿 정본의 플레이스홀더**(`### <… id>` · `\| <… id> \|`)에서 유도 | 절 이름을 생성기에 리터럴로 | INV4. 리터럴 목록은 템플릿이 절을 늘릴 때 조각을 조용히 빠뜨리고, 빠뜨린 조각은 스팬에도 카드에도 남지 않는다. **이 결정이 B1 정의를 바꿨다** — 아래 발견 3 |
| D7 | lane 은 "템플릿이 항목 H3 절을 둘 이상 선언하고 그 절이 문서에 실재할 때" 로 유도하고 이름은 절 제목 자체 | `정책 (기획)`·`강제 지점 (개발)` 리터럴 | 같은 이유. `flow-doc@1`·`ia-doc@1` 이 1개만 선언하므로 lane 부재가 **계산 결과**로 나온다 |

### Security Reviewer

> security-reviewer unavailable, skipped (auto-fallback): 이 세션은 Agent(Task) 도구 사용이
> 금지돼 있어 `subagent_type: security-reviewer` 를 띄울 수 없다.

대신 저작 시점에 두 축을 직접 잠갔고 게이트가 본다:

- **HTML 주입** — 문서 본문·프론트매터에서 온 모든 문자열이 `escapeHtml` 을 통과한다. 검색 인덱스
  값은 `jsonForScript` 가 `<` 를 `<` 로 바꿔 `</script` 로 스크립트를 끊을 수 없게 한다
- **경로 이탈** — 산출 파일명은 `[A-Za-z0-9._-]` 밖 문자를 `-` 로 접는 slug 이고 `..` 을 만들 수
  없다. 검사 `#6` 이 산출 파일 집합과 모집단의 양방향 차집합을 0으로 묶는다
- **원격 자원 0건** — 검사 `#11` 이 산출 HTML 전건에서 `src`·`href` 의 `http(s)://`·`//` 접두를
  0건으로 묶는다. 이 뷰어는 네트워크를 켜지 않는다

이 receipt 는 `security_skipped=true` 로 기록되며 **non-approving** 이다 — `/mccp:pr` 검증기가
이것을 blocking 으로 읽는 것이 설계 의도다. 우회하지 않는다.

### Design Review

> impeccable silent-skip (SKILL_AVAIL=1 · design_signal=0 · reason `no-signal`)

`impeccable-detect --mode implement` 가 구현 전후 두 번 다 `design_signal:false` 를 냈다. 원인은
**구조적**이다 — 이 마일스톤의 렌더 표면은 `docs/viewer/build/` 아래에 나고 `.gitignore:59` 의
`build/` 가 그 디렉터리를 추적 diff 와 `git ls-files --others --exclude-standard` **양쪽에서** 뺀다.
생성기 자신(`docs/viewer/build-docs.mjs`)은 `.mjs` 라 감지기의 UI 확장자 정규식에도 안 걸린다.

따라서 `MCCP_DESIGN_INTENT_REASON` 감사 우회를 쓰더라도 `renderingSurface` 는 0 으로 남고 finish
단계 명령은 전건 `recommend` 로 강등된다 — 우회는 receipt 에 사유만 남기고 실제로 바뀌는 것이
없다. **우회하지 않고 silent-skip 을 정직하게 기록한다.** Phase 3.6(finish 라우팅)은 트리거
미충족으로 건너뛰었고 Phase 3.7(grounding)은 capture 산출물이 없어 완전 no-op 이다.

계획서 §Design Routing Guide 가 예고한 "implement 단계 재도출" 의 결과가 이 항이다. 설계 품질은
대신 **실제 브라우저 구동**으로 봤다(아래).

## 브라우저 실측 (Whale · CDP)

계획서 Acceptance 는 이 항을 `(미검증)` 으로 뒀다. **실측했다.** playwright 기본 pipe 로는 Whale 이
붙지 않으므로 `--headless=new --remote-debugging-port` 로 띄우고 CDP 로 구동했다
(`whale.exe` → `Target.createTarget` → `Runtime.evaluate`). 산출은 `.playwright-mcp/`(gitignore).

| # | 구동 | 결과 |
|---|---|---|
| 1 | `file://` 로 `index.html` 적재 | `window.__VIEWER_INDEX__` 항목 310 · 절 538 적재. 문서 행 56(미작성 38 표기) |
| 2 | 검색창에 `monitoring:fax-status` 입력 + `input` 이벤트 | 명중 3건, 첫 링크 `d/05-schedules-03-monitoring.html#monitoring:fax-status` |
| 3 | policy 문서에서 lane 버튼 3개(전체 · 정책 (기획) · 강제 지점 (개발)) 렌더 | 카드 4건 · 조각 4건 · 부재 표기 4건 |
| 4 | `강제 지점 (개발)` 클릭 | `aria-pressed=true`, 보이는 조각의 lane 이 1종으로 좁혀짐, `localStorage["insuniverse-viewer-lane"]` 에 저장 |
| 5 | **다른 문서로 페이지 이동** | 저장값이 유지되고 그 문서의 같은 lane 버튼이 눌린 상태로 복원 |
| 6 | `ia-doc@1` 문서 | `nav.lane` 0건 · `.lane-none` 1건("이 템플릿에는 기획/개발 lane 이 없다 — `ia-doc@1` 정본이 항목 H3 절을 1개만 선언한다") · 카드 181건 |

**`file://` 에서 `localStorage` 가 살아 있는 것이 5의 실체다.** Chromium 계열은 `file://` 를
단일 origin 으로 묶어 페이지 간 `localStorage` 를 공유한다 — 관측 8 이 `fetch` 만 보고 `localStorage`
는 보지 않았는데, 상태 유지 요구(PRD §Design Direction 2)가 그 위에 서 있었다. 이제 실측이 있다.

**스크린샷에서 결함 1건을 잡아 고쳤다** — 프론트매터 레코드 표에 `parseFlatYaml` 의 파서 내부 키
(`__line` · `__lastEmptyKey`)가 독자에게 노출되고 있었다. 이름 목록이 아니라 `__` 접두로 걸렀다.
**게이트 14건 전건이 이 결함을 통과시켰다** — 어느 검사도 "카드에 파서 내부가 새는가" 를 보지
않는다. 정적 검사의 사각이고, 눈으로 본 것이 잡았다.

## 이탈 (계획 대비)

| # | 계획 | 실제 | 사유 |
|---|---|---|---|
| E1 | 검사 `#1` 이 "워킹트리 clean" 을 단언 | 더러운 경로가 **계획서 §Files to Change ∪ 이 결정의 mccp 산출물** 안인지를 본다 | clean 단언은 커밋 전에 구조적으로 통과할 수 없고, 커밋 뒤에만 도는 게이트는 다음 마일스톤까지 이 자리를 막는다(선례: 워킹트리 diff 게이트가 커밋에 죽는 축). 허용 집합은 리터럴이 아니라 계획서 표와 이 결정의 slug 에서 유도한다 |
| E2 | 검사 `#12` 가 "산출 HTML 에 타임스탬프 0건" | **생성기 소스**에서 `Date.now(`·`new Date(`·`toISOString(`·`hrtime(` 참조 0건을 본다 | 문서 본문에 정당한 날짜(`## 연혁` 표)가 실재해 산출물 grep 은 그것과 생성 시각을 구별하지 못한다. 산출 축은 2회 실행 diff 0바이트가 이미 진다 |
| E3 | B1 스팬 조각을 "프론트매터 · lane H3 · **적용 범위** 표 행" 3종으로 | 템플릿 정본이 선언한 항목 H3 절 **전건** + 항목 표 절 **전건** | D6. 관측 5 의 좁은 정의는 `## 예외·폴백` 표 행과 `## 부록` H3 를 조각에서 뺐다 — Task 6 이 정의를 고정하라고 한 자리가 바로 그것이다. 계수가 3~4 에서 4~7 로 넓어졌고 그 사실을 PRD Open Question 과 backlog 에 적었다 |
| E4 | (계획에 없음) | `docs/00-index.md` 는 프론트매터가 없어 `parseDoc` 이 절을 0건으로 낸다. `indexUnfronted` 로 ATX·코드펜스 두 축만 채웠다 | 그 파일이 nav 순서 정본을 지므로 0절을 "절 없음" 으로 읽으면 정본을 못 읽는다. setext 는 채우지 않는다(그 파일에 없다) — 늘어나면 이름 있는 실패를 낸다 |
| E5 | (계획에 없음) | 검사 `#3` 이 `--selfprobe` 를 부른다 | Validation 블록은 `checkN` 만 호출하므로 이 자리가 없으면 `--selfprobe` 는 등록만 되고 한 번도 돌지 않는다 |
| E6 | (계획에 없음) | 발신 정본 PRD 경로를 후보 2곳(`prds/` · `prds/archived/`)에서 해석한다 | `insuniverse-docs-13-relation-contract.prd.md` 는 이미 archived 다. 리터럴 하나로 못박으면 검사가 파일 부재로 죽는다 |

## 이 창의 발견 (backlog 5행)

전건 `.claude/plans/codex-findings-backlog.md` 에 `| Date | Severity | Source plan | Finding |`
행으로 적었다 — 평문 행은 mccp 소비자에게 비가시다.

1. **MEDIUM** — `docs/_meta/findings/70-viewer.md` 신설 이월. golden 2키(`fixtask.findings_headings` ·
   `findings.files`). 인수처: `docs/_meta/verify/**` 재동결 슬롯 해제 시점
2. **MEDIUM** — `ownership.md` §승계 시 추가 의 뷰어 산출 루트 등재 이월. `ownership.inherited_additions`
   2키. **산출물을 커밋하기로 판정하면 선행조건이 된다**
3. **MEDIUM** — B1 정의 고정이 초판 기준선을 무효화했다(E3). 감소율 계산 불가. 인수처: 마일스톤 3
4. **LOW** — 계획서 관측 6 의 미작성 계수 `37` 은 `38` 이다. 자기 참조 1건을 두 번 뺐다.
   생성기와 검사 `#5` 는 계수를 리터럴로 갖지 않아 산출은 처음부터 38 이었다 — INV4 가 값을 냈다
5. **LOW** — `docs/00-index.md` 의 `parseDoc` 0절(E4). 인수처: doc-kit 소유자

## 음성 프로브 — 검사 14건 전건이 깨진 입력에서 exit 1

계획서 Validation 블록의 `# 음성:` 은 "(구현 시 실측)" 으로 예고돼 있었다. 아래가 그 실측이다.
각 변이는 즉시 원복했다.

| 검사 | 변이 | rc | 출력 |
|---|---|---|---|
| `#1` | `docs/_meta/findings/70-viewer.md` 를 만든다 | 1 | `#1 FAIL: §Files to Change 밖 미커밋 1건 … · docs/_meta/findings/70-viewer.md 실재 — UI1 이 이 창에서 금지한 파일` |
| `#2` | R-3 의 `회부 내용` 열을 고친다 | 1 | `#2 FAIL: 발신 정본과 불일치 — R-3 회부 내용 불일치 — 정본 "표면·계약 요청 (기존)" 이 인계 셀에 없다` |
| `#3` | 계획서 Task 3 의 서브커맨드 선언을 `check9` 까지로 좁힌다 | 1 | `#3 FAIL: 서브커맨드 집합 차집합 — 계획서에만 [] · 등록부에만 [check10 check11 check12 check13 check14]` |
| `#4` | walk 제외에서 `_meta` 를 뺀다 | 1 | `#4 FAIL: _meta 유입 38건 — docs/_meta/findings/00-protocol.md …` |
| `#5` | `index.html` 의 문서 링크 순서를 뒤집는다 | 1 | `#5 FAIL: 부분수열 위반 — 30-admin-04-authority-management.html 가 트리 순서에 없거나 앞선 항목보다 앞섬` |
| `#6` | 산출 HTML 한 건을 지운다 | 1 | `#6 FAIL: 렌더 누락 … docs/30-admin/00-ia.md · ia-doc@1` |
| `#7` | 검색 인덱스에서 `surfaces` 축을 통째로 뺀다 | 1 | `#7 FAIL: 재열거에만 있는 항목 181건 (축 surfaces) …` |
| `#8` | 적용 범위 행이 없는 항목에 빈 표 행을 만들어 넣는다 | 1 | `#8 FAIL: 원본에 없는 조각 1건 — docs/05-schedules/03-monitoring.md#monitoring:alimtalk-status → 적용 범위 (actor × 서비스):9999` |
| `#9` | PRD 의 B1 정의 문면을 지운다 | 1 | `#9 FAIL: B1 스팬 정의가 PRD 에 없다 — 값만으로는 재현이 성립하지 않는다` |
| `#10` | lane 부재 문서에서 `lane-none` 표기를 지운다 | 1 | `#10 FAIL: 1건 — docs/30-admin/00-ia.md lane 부재 표기 누락` |
| `#11` | 인덱스를 `fetch("search-index.json")` 로 바꾼다 | 1 | `#11 FAIL: file:// 비호환 — fetch( 1건` |
| `#12` | 생성기가 `built_at: new Date().toISOString()` 를 박게 한다 | 1 | `#12 FAIL: 2회 실행 diff 1640바이트` |
| `#13` | `golden.txt` 의 `index.doc_count` 를 한 글자 고친다 | 1 | `#13 FAIL: golden 변동 — UI2 가 이 창에서 금지한 축` |
| `#14` | 마일스톤 2행 Status 만 되돌리고 `*Status:` 는 complete 로 둔다 | 1 | `#14 FAIL: 마일스톤 표와 `*Status:` 불일치 — 표 in-progress · 토큰 complete` |

### 이 프로브가 자기 자신에 대해 남긴 사고 1건

**1회차 프로브의 원복이 `git checkout --` 이었고 그것이 두 방향으로 틀렸다.**

- **미추적 파일에서는 조용히 실패한다.** 계획서 · 게이트 하네스 · 생성기가 전부 미추적이라
  `#3`(계획서 Task 3 축소) · `#4`(`EXCLUDE_DIRS` 축소) · `#12`(생성기 타임스탬프) 세 변이가
  워킹트리에 남았고, 뒤따른 `#6`·`#10` 의 출력이 그 잔류로 오염됐다(`#10` 은 아예 `ENOENT` 로 죽었다).
- **추적 파일에서는 인덱스로 되돌린다.** `.claude/prds/…70-viewer.prd.md` 와 `docs/_meta/ownership.md`
  는 추적 중이었고 이 창의 실제 편집이 스테이징되지 않은 상태였으므로, 원복이 **이 창의 작업까지
  같이 지웠다.** 세션 시작 시점에 이미 있던 미커밋 편집(마일스톤 2 `in-progress` 전이 + 사용자
  판정 머리 주석)도 함께 사라졌다.

복구: 미추적 3건은 변이의 역치환으로 되돌렸고(`plan_hash` 재검증으로 receipt stale 아님을 확인),
PRD 는 HEAD 위에 이 창의 편집을 다시 얹으면서 **사용자 판정 머리 주석을 재작성**했다 — 원문
바이트가 아니라 계획서 §Summary 가 인용한 같은 판정을 옮긴 재구성이다. 2회차 프로브(`#10`·`#14`)는
git 이 아니라 **스냅샷 복사**로 원복했다.

**규칙**: 워킹트리를 변이하는 프로브의 원복은 `git checkout --` 이 아니라 스냅샷 복사여야 한다.
`git` 은 미추적 파일에 무력하고, 추적 파일에서는 "변이 이전" 이 아니라 "인덱스" 로 되돌린다.

## 로컬 코드 리뷰 흡수 (2026-08-27 · `/mccp:code-review` 미커밋 축)

리뷰 시점 하네스는 **14/14 green** 이었다. 아래는 전부 **게이트가 보지 않던 자리**이며, 흡수는
매 건 반사실로 확인했다 — 수정 전 상태에서 검사가 붉어지는 것을 실행해 봤다.

| # | 층위 | 지적 | 흡수 | 반사실 |
|---|---|---|---|---|
| H1 | 1 | `restSections` 가 첫 H2 이전 구간을 아무도 싣지 않고, 덮인 절에서는 첫 `###`·`\|` 에서 끊어 조각 뒤 산문을 버렸다 | 뺄 단위를 절에서 **행**으로 바꿨다(`eatenLines`·`keptText`). 본문 행이 전건 카드로 간 표는 머리·구분행도 함께 뺀다 | 옛 산출에 `#15` 를 물리면 `유실 125건`(첫 H2 이전 104 · 절 안 21), 새 산출은 0 |
| H2 | 2 | 본문 완전성을 보는 검사가 0건. `#7`·`#8`·`#11` 은 **항목 조각**만 재열거한다 | 검사 `#15` 신설 — 마크다운을 내용 단위로 쪼개 산출 텍스트와 집합 대조. 사각지대(10자 미만 3054건)를 출력에 드러낸다 | 위와 같음. `contentUnits` 는 `--selfprobe` 등록부에 넣었다(14×2=28쌍) |
| M1 | 6 | `--docs-root` 이 `docs/` 밖이면 nav 대조 키의 네임스페이스가 갈려 **트리 전건 미작성 + 문서 전건 트리 밖**이 rc 0 으로 통과했다 | 대조 키를 `--docs-root` 기준 `treeKey` 로 바꾸고, `00-index.md` 를 뺀 일치가 0건이면 이름 있는 실패 | `#12` fail-closed 에 `트리불일치` 픽스처를 더해 4종 → **5종**. 픽스처가 rc 1 을 낸다 |
| M2 | 6 | 검색 haystack 이 항목 **310/310** 전건 400자에서 잘려 있었고, `#11` 표본 질의는 전부 항목 id(=haystack 맨 앞)라 그 절단에 구조적으로 눈멀었다 | 절단 제거 + 절 축에 **카드가 싣지 않은 본문**을 색인. `#11` 에 **꼬리 질의**(각 `### <id>` 블록의 끝 어휘)를 더했다 | 400자 절단을 재주입하면 `#11 FAIL: 꼬리 질의 미명중 249/289`. 제거 상태에서 289/289 |
| L1 | 7 | `assembled_span_bytes: 0` 은 측정이 아니라 정의의 재진술이고, `#9` 가 `=== 0` 을 요구해 **뒤집힐 수 있는 입력이 없었다** | 카드의 조각 영역 바이트를 실제로 재 `assembled_span_avg/max_bytes` 로 낸다. `#9` 는 부등호(조립 avg < 원본 avg)를 본다 | 산출: policies 36225→3231 · surfaces 149899→2015 · flows 13411→4338 |
| L2 | 4 | slug 충돌 검사가 쓰기 루프 **안**에 있어 실패 시 앞 문서 HTML 이 out 에 남았다 | 전수 선검사로 옮겼다 | (미검증) — 충돌 픽스처를 만들지 않았다 |
| L3 | — | `"docs/" === p.slice(0,5) ? p : "docs/" + p.replace(...)` 의 else 가지에서 `replace` 가 무조건 no-op | M1 재작성으로 소멸 | — |

### 리뷰가 안 낸 것 — 수정 중에 나온 2건

- **`fmRecordRange` 경계 off-by-one.** `doc.bodyStart` 는 본문 첫 행의 0-based 인덱스라 마지막
  프론트매터 내용 행은 `bodyStart - 2` 인데, 순회가 그 행을 뺐다. **각 축의 마지막 레코드가 한 행
  짧게** 잡혔다(`docs/02-access/01-authn.md` `NONPROD_AUTH_BYPASS` 12행 중 11행). 카드는
  프론트매터 조각을 표로 다시 펴므로 화면에 안 드러나고 B1 스팬에만 섞였다. `#8` 은 라벨 대조에서
  프론트매터를 건너뛰므로 이 자리를 보지 않는다. 반사실: 그 마지막 행의 토큰
  (`cookie.helper.ts:12`)이 옛 색인에 0건 · 새 색인에 1건.
- **이중 백틱 코드 범위.** `` `` `<status>` `` `` 에서 단일 백틱 규칙이 먼저 물어 코드 범위가 뒤로
  새고 리터럴 백틱이 화면에 남았다. 이중 백틱을 먼저 푸는 규칙 1행을 앞에 뒀다.

### 산법 정정 — 첫 계수 두 개가 틀렸다

유실을 처음 잰 산법이 **471건**, 그 앞 판이 **1002행**을 냈고 둘 다 틀렸다. HTML 태그를 공백으로
치환하면 `(`+`<code>` 자리에 공백이 끼고, 빈 문자열로 치환하면 인접 어휘가 붙는다 — 둘 다 산법
잡음이다. 엔티티를 태그보다 **먼저** 풀면 `&lt;status&gt;` 가 `<status>` 가 된 뒤 태그 지우기에
함께 먹혀 실재하는 내용이 유실로 잡힌다(거짓 2건). 정본 산법은 **태그 제거 → 엔티티 해제 →
공백·강조 제거 → 부분문자열**이고 그 값이 **125건**이다. `#15` 가 그 산법을 진다.

### 이 라운드가 움직인 관측값

`§브라우저 실측` 표의 `절 538` 은 그 실행의 기록이다. 절 축에 카드가 싣지 않은 본문이 들어오면서
현행 산출은 **556** 이다 — 표 행은 그 실행의 사실이라 고치지 않고 이 절이 델타를 진다.

### 흡수 뒤 브라우저 재실측 (Whale · CDP · 2026-08-27)

색인이 406 KB → 945 KB 로 커졌고 문서 페이지에 본문이 더 실렸다. **정적 검사만으로 접지 않는다** —
같은 CDP 경로로 다시 구동했다.

| # | 구동 | 결과 |
|---|---|---|
| 1 | `file://` 로 `index.html` 적재 | 항목 310 · 절 **556** 적재(적재 JSON 664 KB) · 문서 행 56(미작성 38) |
| 2 | `SESSION_ESTABLISHMENT` 블록의 **끝 어휘**(`최고관리자·기업`)로 질의 | 명중 1건, 링크 `d/02-access-01-authn.html#SESSION_ESTABLISHMENT`. 조각이 질의 자리에서 오려져 나온다 — **절단 상태였다면 0건** |
| 3 | 표 범례 문장(`그 서비스가 이 정책의 경로에 등장하지 않음`)으로 질의 | 명중 4건. 흡수 전 이 문장은 **색인에도 화면에도 없었다** |
| 4 | `30-admin/05-treat-monitoring` 페이지 | 등급 고지(`admin 은 자동화된 테스트 근거가 없다`) 렌더 `true` · 문서 제목 렌더 `true` · 카드 5 |
| 5 | `02-access/01-authn` 페이지 | 표 뒤 범례 렌더 `true` · lane 버튼 3 · 카드 8 · 파서 내부 키 노출 `false` |
| 6 | `강제 지점 (개발)` lane 클릭 | `aria-pressed=true` · `localStorage` 저장 · 보이는 lane 1종 |
| 7 | 다른 문서로 페이지 이동 | 저장값 유지 + 같은 버튼이 눌린 상태로 복원 |

2·3·4·5 는 **흡수 전에는 존재하지 않던 관측**이다 — 그 자리에 내용이 없었으므로 질의도 렌더도
성립하지 않았다. 6·7 은 회귀 확인이다.

## main 병합 관측 (2026-08-27 · `12d0e36`)

`origin/main` 이 `1bc972b` → `4d847f6` 로 41커밋 전진해 병합했다(rebase 아님 — receipt 3종이
`head_sha` 결박이다). 들여온 형제 창 산출은 `20-foundation-access` M4 · `30-contracts-limits` M3 ·
`40-runtime-vendor-legal` M4 · `50-domain` M4 · `60-history-deprecated` M2b 이고, 그중 문서를 낸
것은 앞 셋이다 — `docs/**` 신설 6건(`02-access/02-authz-roles` · `02-access/03-authz-capabilities` ·
`03-contracts/03-data-model` · `06-vendors/01-treat-providers` · `06-vendors/03-payments` ·
`06-vendors/04-messaging`). 병합을 PR 발행보다 **먼저** 둔 것은 backlog `60m2` 행이 관측한
post-PR stale-head(재앵커 불가)를 애초에 만들지 않기 위해서다.

**충돌 1건 · `.claude/plans/codex-findings-backlog.md`.** 양쪽이 같은 표 끝에 행을 덧붙였다 —
이쪽은 70-m2 이월 5행, `origin/main` 쪽은 형제 창 지적 14행(충돌 헌크 안). 어느 쪽도 상대 행을
고치지 않으므로 **양측 전건을 보존**했다.

| 축 | 값 |
|---|---|
| 병합 전 이쪽 (`d003991`) | 표 행 77 (그중 70-m2 5행) |
| 병합 전 `origin/main` (`4d847f6`) | 표 행 101 |
| 병합 후 | 표 행 106 = 101 + 5 · 소실 0 |
| 머리행 `Date / Severity / Source plan / Finding` | 255행에 유지 |

배치는 `origin/main` 블록을 앞, 이쪽 5행을 뒤로 뒀다. 70-m5 창(`826779b`)·40-m3 창(`10950ff`)과
같은 배치다.

**병합 후 게이트 실측 — 재지정 없이 전건 통과.**

| 축 | 결과 |
|---|---|
| `m2.gates.mjs check-all` | `15/15 ok` · rc 0 |
| `#1`·`#13` 기준선 | merge-base 가 `1bc972be` → `4d847f69` 로 자동 이동 · golden 무변동 |
| 모집단 | 문서 18 → 24 · 항목 310 → 378 (flows 20 · policies 109→177 · surfaces 181) |
| 생성기 델타 | 0행 — `docs/viewer/build-docs.mjs` 를 한 줄도 고치지 않았다 |

**병합 직후 1회차는 8/15 였고 원인은 하네스가 아니라 산출 디렉터리였다.** `docs/viewer/build/` 는
`.gitignore:59` 의 `build/` 가 덮는 미추적 산출이라 병합이 갱신하지 않는다. 병합 전 18문서 산출이
그 자리에 남은 채 `#5`·`#6`·`#7`·`#8`·`#10`·`#11`·`#15` 가 24문서 모집단과 대조돼 붉어졌고,
`#10` 은 신설 문서의 HTML 부재로 `ENOENT` 를 던져 `check-all` 이 그 자리에서 죽었다. 재생성 후
같은 명령이 15/15 다. **관측 가치는 계수가 아니라 그 반대다** — 형제 창이 문서 6건을 더했는데
생성기·하네스 어느 쪽도 고칠 자리가 없었다. 계수를 리터럴로 두지 않는다는 INV4 가 병합에서
값을 낸 자리이며, 동시에 `check-all` 이 **미추적 산출의 신선도를 자기 전제로 갖는다**는 사각을
드러낸다 — 재생성을 강제하는 검사는 이 하네스에 없다. 이월하지 않는다: 산출 재생성은 `#12`
(2회 실행 재현성)가 매번 임시 디렉터리에서 하고 있고, 재생성 강제는 마일스톤 6 빌드 게이트
승격의 소관이다(계획서 §NOT Building).
