# 12 · 흐름 중심 재구조화 — PRD 수정 범위와 남는 문제 (2026-08-11)

> **성격**: 분석 + 제안. 규약이 아니다. `05-final-spec.md` 를 개정하지 않는다.
> **질문**: *"정책은 개별로 분리된 것이 아니라 하나의 흐름에서 분기로 나뉜다. '관련문서' 로 서로
> 연결되며 탐색하는 구조를 만들려면 PRD 를 어디까지 고쳐야 하는가."*
> **입력**: `.claude/prds/**`(11) · `.claude/templates/doc-kit/**` · `.worktrees/protocol` HEAD `b9219e7`
> (M5·M6·M7 산출물) · `.worktrees/docs-viewer` 70-viewer PRD + 뷰어 메타분석 · `meta/05~11`.
> **기준점**: main `aa3ef1c` · protocol worktree `b9219e7` · **`docs/` 정책 문서 본문 0건**.
> **선행**: `11-pipeline-reuse-analysis.md` §해당 없음 — 이 문서는 재사용이 아니라 **현 프로젝트의 구조**를 다룬다.
> **개정 · 2026-08-11 (작성 당일)** — 사용자가 뷰어 구조를 **`Flow → IA → Policy` 3단 depth** 로
> 확정하고 *"IA 만 리스팅하는 기능"* · *"페이지별 그룹핑"* 을 요구했다. 이 입력이 §1-2 의 판정
> (*"IA 재판정을 다시 열 필요가 없다"*)을 **뒤집는다.** 초판 판정은 온보딩 질문 집합 하나만 보고
> 내린 것이고, **뷰어라는 두 번째 소비자가 판정 스코프 밖에 있었다.** 정정은 §1-2 개정 블록과
> §2-6 · §4 에 반영했다. 초판 문장은 판단 경위로 남긴다.
>
> **정정 고지 (초판)**: 직전 세션에서 제안했던 `ia-doc@1` 의 `surfaces[].exits` 승격은 초판 시점에
> 무효로 판정했었다 — M6 이 `ia_doc_consumed: false` 를 확정했기 때문이다. **개정으로 다시 유효해진다.**
> `실패·이탈` 표 계약(§3-3)은 개정과 무관하게 그대로 필요하다.

---

## 0. 결론 요약

| # | 판정 | 근거 |
|---|---|---|
| 1 | **흐름 축은 이미 구조에 들어와 있다** | flow-doc@1 이 절차·권한·이탈을 강제하고 `refs` 로 문서 간 관계를 갖는다 |
| 2 | **IA 축은 닫혀 있고, 다시 열어야 한다** | M6 이 온보딩 질문만 보고 `ia_doc_consumed: false` 를 확정했다. 뷰어가 `Flow → IA → Policy` 3단이면 **중간 층이 비어 있다** (§1-2 개정) |
| 3 | **'관련문서' 를 문서에 쓰면 안 된다** | 사람이 쓴 상호참조는 rot 한다. 관계 3종이 이미 데이터에 있고 **계산 가능**하다 (§3-2) |
| 4 | **막는 것은 형식이 아니라 계약 2개** | 그래프 엣지의 소스인 `실패·이탈` 표와 화면 전이(`surfaces[].exits`)가 형식 계약 밖이다 |
| 5 | **관계 축 전체가 단일 PRD 에 몰려 있다** | flow 10건 전부 `50-domain` 소유. 그 PRD 는 18문서이며 "규모로 완주 실패" 확률 **높음**. IA 를 얹으면 악화된다 |
| 6 | **싼 창이 닫히는 중** | M5·M6·M7 이 worktree 에서 complete. 아직 main 미병합이고 문서 산출 0건이라 **지금이 마지막 저비용 구간** |
| 7 | **가장 비싼 항목은 문서 트리 개정이다** | IA 채택은 `05-final-spec.md` §4 의 50문서 트리를 바꾸고, 그것이 `00-index.md` 코드펜스·`entry_count`·`tree_sha256` 을 전부 건드린다 (§4-H) |

**한 줄**: 형식은 3종 그대로 두되, **계약 2개 + config 키 1개 + 인덱스 절 1개 + IA 채택 재판정 + 문서 트리 개정**.
앞의 넷은 지금 싸고, 뒤의 둘은 지금도 비싸지만 나중에는 더 비싸다.

---

## 1. 상태 실사 (2026-08-11)

### 1-1. main 과 worktree 가 갈라져 있다

| 항목 | main `aa3ef1c` | protocol worktree `b9219e7` |
|---|---|---|
| `00-protocol` M5 (template-config) | pending | **complete** — `docs/_meta/template-config.yaml` 존재 |
| M6 (onboarding-questions) | pending | **complete** — 55문항 · `docs/_meta/onboarding-questions.yaml` |
| M7 (00-index · ownership) | pending | **complete** — `docs/00-index.md` 145행 · `ownership.md` |
| `docs/` 정책 문서 | 0건 | 0건 |

**main 의 PRD 본문은 아직 pending 으로 적혀 있다.** 즉 세 마일스톤은 산출됐으나 병합 전이며,
하류 문서 PRD 5종은 아직 이것을 소비하지 않았다. **고치는 비용이 가장 낮은 지점이 정확히 지금이다.**

### 1-2. `ia-doc@1` 은 닫혔다 — 그리고 그 판단은 사용자 요구와 충돌하지 않는다

> **이 절은 게이트 입력이다.** `docs/_meta/onboarding-questions.yaml` 의 `ia_viewer_src` 가 이 절을
> 가리키고, 절 전문의 sha256 이 같은 파일의 `ia_viewer_sha256` 에 선언돼 있으며 golden 의
> `onboarding.ia_viewer_sha256` 에 동결돼 있다. `verify.sh` 의 `oq_ia_gate` 규칙 (g) 가 실측을
> 재계산해 선언과 대조한다 — **이 절을 고치면 `--check` 가 붉어지고, 침묵시키려면 yaml 선언을
> 사람이 고쳐야 하며 그 편집은 최종 diff 에 남는다.** 아래 소비자 2종 표가 뷰어 축의 근거 본체다.

`onboarding-questions.yaml` 헤더 발췌 (2026-08-07 재판정, 사용자 위임):

```
ia_doc_consumed: false
55건 중 화면을 언급하는 질문은 9건 — Q29·Q36·Q38·Q40·Q43·Q45·Q47·Q49·Q55.
그중 어느 것도 화면의 목록을 묻지 않는다. 묻는 것은 그 단계에서 무엇을 보는가,
그 화면 안에서 무엇이 가려지는가, 이 화면을 볼 수 있는지 어떻게 판정하는가이다.
전부 흐름·권한·조치의 한 지점으로서의 화면이라 flow-doc@1 의 각 단계가
이름을 적으면 답이 된다.  목록형 질문 0건.
```

~~사용자 요구 *"각 화면이 어떤 역할을 추구하고 어떤 기능을 하며, user flow 를 진행하면서"* 는
**흐름 진행 중의 화면**이다. 재판정 근거와 같은 문장이다. **재판정을 다시 열 필요가 없다.**~~

> **개정 · 같은 날 뒤집힘.** 위 판정은 **틀렸다.** 사용자가 이어서 뷰어 구조를
> `Flow → IA → Policy` 3단으로 확정하고 *"IA 만 리스팅하는 기능"* · *"각 특정 페이지별 그룹핑"* 을
> 요구했다. 이것은 **화면 인벤토리 요구**이며 "흐름의 한 지점" 으로 대체되지 않는다.
>
> **M6 판정 자체는 그 스코프 안에서 옳다.** 온보딩 질문 55건에 목록형이 0건인 것은 사실이다.
> 틀린 것은 **`ia_doc_consumed` 를 온보딩 질문 하나로 결정하게 설계한 것**이다 —
> 소비자가 둘(신입 개발자 · 뷰어)인데 판정 입력은 하나였다.
>
> | 소비자 | 화면 축을 요구하는가 | M6 이 봤는가 |
> |---|---|---|
> | 신입 개발자 (온보딩 질문 55건) | 아니오 — 목록형 0건 | **봤다** |
> | 뷰어 독자 (`Flow → IA → Policy` 3단 · IA 리스팅 · 페이지별 그룹핑) | **예** | **못 봤다** — 70-viewer 는 DEFERRED 였고 M6 입력이 아니었다 |
>
> `70-viewer` PRD 는 스스로 *"`ia-doc@1` 지원은 Out of scope — 소비 문서 0건. 판정은 `00-protocol` M6
> 소관"* 이라 적어 M6 에 넘겼고, M6 는 온보딩 질문만 보고 닫았다. **두 문서가 서로에게 판정을 미뤘고
> 그 사이로 요구가 빠졌다.**

대가는 실재한다 — IA 를 계속 닫아 두면 **흐름에 등장하지 않는 화면은 문서 어디에도 남지 않고**,
뷰어의 중간 depth 가 비며, "페이지별 그룹핑" 이 성립하지 않는다 (§5 P7).

### 1-3. 질문 55건의 소유 분포

| owner | 질문 수 | 비중 |
|---|---|---|
| `50-domain` | **23** | 42% |
| `20-foundation-access` | 10 | 18% |
| `40-runtime-vendor-legal` | 10 | 18% |
| `30-contracts-limits` | 9 | 16% |
| `60-history-deprecated` | 3 | 5% |

흐름형 질문(순서·단계·이탈을 묻는 것)은 실측 6건 이상이며 전부 `50-domain` 과 `30-contracts-limits` 에 있다. 예:

```
Q  알림톡 본인인증 흐름이 몇 단계이고 중간에 이탈하거나 뒤로가기를 누르면 무엇이 일어나는가
Q  환불·취소가 들어오면 주문·구독·정산·머니 상태가 각각 어떤 순서로 바뀌는가.
   이미 정산이 끝난 뒤에 취소되면 무엇이 달라지는가
Q  같은 흐름을 웹과 앱 웹뷰에서 각각 탔을 때 갈라지는 지점은 어디인가
```

**두 번째 질문이 중요하다** — 주문·구독·정산·머니는 `10-server/` 의 서로 **다른 문서**이고,
`03-contracts/04-state-transitions.md` 와도 걸친다. 한 흐름이 최소 4문서를 가로지른다.
현행 구조에서 이 질문에 답하려면 독자가 문서 4개를 스스로 이어 붙여야 한다.

---

## 2. 구조적 충돌 5

### 충돌 1 · 쓰기 경계가 그대로 읽기 경계가 됐다

배타 소유 경로는 **병렬 worktree 충돌 방지 장치**다 (우산 §분할 맵: *"배타 소유 경로가 겹치는 행은
없다 — 이것이 동시 작업의 유일한 보증이다"*). 그런데 `00-index.md` §읽는 순서가 그 경계를 그대로
읽는 순서로 승계했다 — 12개 디렉터리를 번호순으로.

**쓰기 편의로 정한 경계가 독자에게 상속됐다.** 사용자가 원하는 읽기 축(흐름)은 쓰기 축(주제)과
직교하며, 흐름 1건이 주제 4~5개를 가로지른다.

> 파일을 옮기는 문제가 **아니다.** 소유 경계는 유지하고 **진입점을 하나 더 만드는** 문제다.

### 충돌 2 · 관계를 만들 권한이 한 PRD 에만 있다

기계 판독 가능한 문서 간 관계는 `flow-doc@1` 의 `refs` · `status_source` 뿐이고, 그 형식을 쓰는
문서 10건은 **전부 `50-domain` 소유**다 (`20-site/` 5 + `30-admin/` 5).

`policy-doc@1` 에는 상호참조 필드가 없다. 즉 `20`·`30`·`40`·`60` 이 쓰는 정책 문서 40여 건은
**자기가 어떤 흐름에 등장하는지 표현할 수단이 없다.** 이것은 결함이 아니라 소유 프로토콜의 필연이다 —
형제 PRD 의 문서를 편집할 수 없으므로.

### 충돌 3 · 관계 축이 가장 위험한 PRD 안에 있다

| 사실 | 값 |
|---|---|
| flow 문서 | 10건 — 전부 `50-domain` |
| `50-domain` 총 문서 | 18건 |
| `50-domain` Risk | *"18개 규모로 완주 실패"* 확률 **높음** |
| `50-domain` 이 소유한 온보딩 질문 | 23 / 55 (42%) |

**관계 축 전체가 단일 실패점 위에 있다.** `50-domain` 이 규모 때문에 중단되면 흐름 문서가 후순위로
밀려 그래프가 통째로 없어진다 — 나머지 40문서는 전부 완성돼 있는데 서로 연결되지 않은 상태로 남는다.

### 충돌 4 · 그래프의 엣지 소스가 형식 계약 밖이다

분기·이탈을 담는 것은 `flow-doc@1` 의 **`실패·이탈` 표**다. 현재 상태:

| 층 | 현황 |
|---|---|
| 라벨 존재 | lint 규칙 `flow-block` 이 4개 라벨(`전제`·`절차`·`권한`·`실패·이탈`) 존재와 각 아래 실질 줄 ≥ 1 을 강제 |
| 표 구조 | **계약 없음.** 예시가 `지점 · 조건 · 결과` 3열을 쓰지만 규칙이 아니다 |
| `결과` 열의 값 | **계약 없음.** 예시는 화면 id · HTTP 코드 · `막다름` · 산문이 섞여 있다 |

`권한` 표는 `auth_layers` 집합 일치로 계약돼 있는데 **`실패·이탈` 표는 아무 계약이 없다.**
그래프의 엣지가 나오는 유일한 자리가 형식적으로 가장 느슨하다.

### 충돌 5 · `placement`(본문/부록)가 데이터에 없다

우산 제약 #2 — *"현행 유효성이 판정되지 않은 정책은 본문에 넣지 않는다"* — 는 status 별로
본문/부록을 가른다. 판정표는 `05-final-spec.md` §3 과 `00-index.md` §상태 범례에 **산문으로만** 있다.

`template-config.yaml` 의 `statuses` 는 평탄한 스칼라 리스트라 "이 status 는 부록 격리 대상"이라는
사실이 **어디에도 기계 형태로 없다.** lint 는 값이 도메인 안에 있는지만 본다.

→ 제약 #2 는 현재 **사람 준수에만 의존한다.** 뷰어의 미판정 격리 필터도 같은 이유로 막혀 있다
(70-viewer 계약 요청 #2 — *"MVP 표면 1 이 죽는다. 유일한 MVP 차단 항목"*).

### 충돌 6 · 뷰어가 3단인데 중간 층이 비어 있다 (2026-08-11 신설)

사용자 확정 구조:

```
depth 1   Flow      흐름 목록 → 흐름 그래프 (분기·이탈·조건)
depth 2   IA        흐름의 각 단계 = 화면. 역할 · 액션 · 이탈 경로
depth 3   Policy    그 화면·단계에 걸리는 정책. 기획/개발 lane
          ↕         역방향도 성립 — 정책에서 화면으로, 화면에서 흐름으로
독립 진입  Flow 목록 · IA 목록 · Policy 목록 각각
그룹핑    actor(관리자/유저) · 페이지 · 단계(step/depth)
```

각 층이 요구하는 데이터와 현재 보유 상태:

| depth | 필요한 것 | 현재 | 판정 |
|---|---|---|---|
| 1 Flow | 흐름 목록 · 단계 순서 · 분기 · 조건 | `flow-doc@1` 10건. `실패·이탈` 표가 계약 밖 | 계약 1개로 해소 |
| **2 IA** | **화면 id · 역할 · 액션 · 이탈 경로** | **`ia-doc@1` 형식만 있고 소비 0건 · 채택 거부** | **축 자체가 없다** |
| 3 Policy | 정책 블록 · status · 앵커 · 적용 범위 | `policy-doc@1`. 계약 충분 | 있음 |
| 역방향 | 정책 → 화면 → 흐름 | `flow.refs` 단방향 | 빌드가 뒤집어 계산 |

그룹핑 3축의 데이터 소재:

| 그룹핑 축 | 소스 | 현재 |
|---|---|---|
| actor (관리자 / 유저 …) | `## 적용 범위 (actor × 서비스)` 표 | **전 문서에 이미 강제됨** — 계산 가능 |
| 단계 (step / depth) | `flow` 의 `절차` 번호 · `실패·이탈` 표 | 계약되면 가능 |
| **페이지** | **화면 id** | **IA 축 필요** |

**그룹핑 3축 중 둘은 이미 되고, 페이지 축만 IA 를 요구한다.**
그리고 depth 2 가 통째로 비면 3단 drill-down 이 2단으로 접힌다 — 흐름에서 정책으로 바로 뛰게 되고,
사용자가 목표로 지목한 형태(화면 단위 탐색)가 성립하지 않는다.

> **`.md` 는 AI 에이전트용, 뷰어는 사람용**이라는 사용자 확정은 `70-viewer` PRD §Users 의
> *"Not for: AI 에이전트 — 에이전트는 마크다운 원본과 프론트매터를 직접 읽는다. 뷰어는 사람용이며
> 에이전트를 위해 형식을 바꾸지 않는다"* 와 정확히 일치한다. **이 축은 이미 합의돼 있다.**
> 다만 그 합의가 한 방향으로 더 밀린다 — 형식은 기계 판독을 우선하고, 사람 편의는 전부 렌더가 맡는다.
> 즉 **문서에 '관련문서' 절을 넣지 않는다는 §3-2 의 결론이 이 확정으로 더 강해진다.**

---

## 3. 해결 원리

### 3-1. 문서를 옮기지 않는다 — 진입점을 더한다

흐름 축을 위해 파일을 재배치하면 배타 소유 경로표가 무너지고 8개 worktree 의 동시 작업 보증이
사라진다. **소유는 주제 축, 읽기는 흐름 축**으로 분리한다. 물리 구조 불변, 인덱스 절 1개 추가.

```
소유 (불변)                     읽기 (신설)
  docs/01-foundation/**   →      00-index.md §흐름으로 읽기
  docs/02-access/**                흐름 A: 문서 3 → 5 → 12 를 이 순서로
  docs/04-limits/**                흐름 B: ...
  docs/20-site/**  (flow)        각 흐름은 자기 flow 문서로 진입
```

### 3-2. '관련문서' 는 쓰지 않는다 — 계산한다

문서에 `관련문서` 절이나 필드를 두면 두 가지가 동시에 나빠진다.

| 문제 | 이유 |
|---|---|
| **rot** | 앵커 rot 가 예정된 사건이듯(EMSE 2023, 70-viewer Evidence) 사람이 쓴 상호참조도 낡는다. 정책이 옮겨가면 링크가 죽는다 |
| **동기화 지점 2배** | A→B 를 쓰면 B→A 도 써야 한다. 소유가 다른 PRD 면 그것부터 불가능하다 |
| **필수 절 상한** | `doc-template-kit` 이 필수 절 상한 5 를 확정했다. 6번째 절은 그 판단을 되돌리는 것 |

**관계 3종이 이미 데이터에 있다.** 뷰어가 빌드 시점에 계산하면 rot 하지 않는다.

| 관계 | 소스 | 방향 | 현재 상태 |
|---|---|---|---|
| **흐름 소속** — 이 정책이 등장하는 흐름 | `flow.refs` · `status_source` | 단방향 → 빌드가 뒤집어 역참조 | 계약 있음 |
| **actor 공유** — 같은 actor 에 걸리는 다른 정책 | `## 적용 범위 (actor × 서비스)` 표 | 무방향 집합 | **전 문서에 이미 강제됨** |
| **앵커 공유** — 같은 코드를 강제 지점으로 갖는 정책 | `policies[].anchors` | 무방향 집합 | 계약 있음 · 프론트매터 |

세 번째가 특히 값싸다 — `anchors` 는 필수 필드이고 파일 경로가 겹치면 그 자체가 관계다.
`corp-analysis-view.guard.ts` 를 앵커로 가진 정책은 서로 다른 문서에 있어도 같은 관문을 말한다.

> **사람이 쓰는 유일한 관계는 `flow.refs` 하나로 유지한다.** 나머지는 전부 계산이다.

### 3-3. 엣지를 계약한다 — `실패·이탈` 표

흐름 내부의 분기는 `실패·이탈` 표가 유일한 소스다. IA 채택 여부와 **무관하게** 계약이 필요하다 —
IA 가 화면 간 전이를 담아도 "어떤 조건에서 그 전이가 일어나는가" 는 flow 쪽에만 있다.
**`권한` 표가 `auth_layers` 로 계약된 것과 같은 방식**이며 새 개념이 아니다.

```
| 지점 | 조건 | 결과 |
```

- **열 3개 · 이름 고정** — `flow-block` 이 이미 라벨 존재를 보므로 그 아래 표의 헤더만 추가 검사
- **`결과` 열의 첫 토큰**을 어휘 4종 + 형태 2종으로 한정 (마일스톤 3 구현 결과):

| 종류 | 값·형태 | 예 | 의미 |
|---|---|---|---|
| 어휘 (`config.exit_kinds`) | `막다름` | `막다름` | 종단. 이 흐름에서 더 나아가지 않는다 |
| 어휘 (`config.exit_kinds`) | `fail-open` | `**fail-open** — 통과시킨다` | 장애 시 통과 |
| 어휘 (`config.exit_kinds`) | `fail-closed` | `**fail-closed** — 403` | 장애 시 차단 |
| 어휘 (`config.exit_kinds`) | `계속` | `계속 목록 화면에 머문다` | 같은 흐름 안에 머문다 |
| 형태 | `HTTP <숫자>` | `HTTP 403. 목록 화면에 머문다` | 응답 코드로 끝난다. 숫자 뒤 구두점 허용 |
| 형태 | `<상대경로>.md#<id>` | `./ia-example.md#alimtalk-expired-access-link` | 문서 간 엣지. 절대경로·URI 는 이식 불가라 거부 |
| 형태 | bare `<id>` | `alimtalk-step-fail` | 아래 범위 안일 때만 |

**어휘는 lint 가 아니라 `config.exit_kinds` 에 산다.** 형태 3종은 값이 아니라 문법이므로 config 밖에서 `REF_RE`·`resolveRefItem`·정규식이 맡는다. 그 분리가 없으면 뷰어가 읽을 기계 정본이 없어 같은 상수를 복제하게 되고, 두 축이 갈라져도 검사가 전부 통과한다.

**bare id 의 해석 범위 — 초판의 「같은 흐름의 단계 이름」 은 실측으로 거짓이었다.**

~~같은 흐름의 단계 이름~~ → **같은 문서 `flows[].id` ∪ 그 flow 자신의 `refs` 대상**. **정정한다(2026-08-18).**

근거: 승격 전 `flow-example.md` 의 bare id 3건(`alimtalk-expired-access-link` · `alimtalk-used-analysis-permission` · `alimtalk-step-fail`)은 **전부 `flows[].id` 가 아니라 `ia-example.md` 의 `surfaces[].id`** 였다. 「같은 흐름의 단계 이름 대조」 로 구현하면 3건 중 **0건**이 해석된다. 마일스톤 3 은 `refs` 에 있는 1건만 bare 로 남기고 나머지 2건을 전체 ref 형태로 승격해 이 갭을 닫았다.

범위를 **문서 전역**으로 넓히지 않는다 — 다른 flow 의 `refs` 에만 있는 id 가 통과하면 어느 흐름의 엣지인지가 사라진다. fixture `bad-flow-exit-scope.md` 가 그 미탐을 상시 검사한다. 비교는 `===` 정확 일치다 — `startsWith` 로 구현하면 `alimtalk-step-fail-typo` 가 통과하며, fixture `bad-flow-exit-prefix.md` 가 그것을 잡는다.

산문 설명은 뒤에 붙일 수 있게 하되, **행의 시작이 위 어휘·형태 중 하나**여야 한다.
`flow-refs` 규칙의 참조 해석 로직을 그대로 재사용하므로 신규 파서 0.

### 3-4. `statuses` 를 매핑으로 승격한다

```yaml
# 현재 — 평탄 리스트
statuses: [active-prod, conditional-enabled, ..., deprecated-confirmed]

# 승격 후
statuses:
  - id: active-prod
    label: 정상
    placement: body
  - id: conditional-unknown
    label: 게이트 미확인
    placement: appendix
  - id: deprecated-confirmed
    label: 폐기 확정
    placement: deprecated
```

얻는 것이 뷰어 기능 하나가 아니다:

1. **우산 제약 #2 가 기계 검사 가능해진다** — `placement: appendix` 인 정책이 본문 절에 있으면 lint 위반. 지금은 사람 준수에만 의존한다
2. 70-viewer MVP 표면 1(미판정 격리 필터)의 유일한 차단 항목이 풀린다
3. `status_precedence` 와 같은 파일에서 두 축(지배 순서 · 배치)이 함께 읽힌다

**비용**: `lint-docs.mjs` 의 config 파서가 맵 리스트를 읽어야 한다 — 프론트매터 `policies:` 파싱에
**이미 있는 기능**이다(schema §지원 문법 4번). config 쪽에 붙이는 것뿐이다.

### 3-5. IA 축을 다시 연다 — 최소 형태로

`ia-doc@1` 은 **형식이 이미 있고 lint 도 이미 지원한다.** 여는 데 드는 것은 형식 비용이 아니라
**문서 트리에 자리를 만드는 비용**이다.

여는 방식 3안:

| 안 | 내용 | 문서 수 | 트리 개정 | 소유 |
|---|---|---|---|---|
| **A** | 흐름이 지나는 화면군만 IA 화 — `20-site/` `30-admin/` 의 flow 10건이 참조하는 화면군 | +2~4 | 필요 | `50-domain` (이미 18문서 → 20~22) |
| **B** | 서비스별 IA 1건씩 — `20-site/00-ia.md` · `30-admin/00-ia.md` | **+2** | 필요 | `50-domain` |
| **C** | IA 를 `docs/_meta/index/screens.yaml` 인덱스로 — 문서 아님 | +0 | 불필요 | `11-index-code` |

**권고: B.** 근거 —

- **A 는 화면군 단위를 미리 정해야 한다.** 무엇이 한 화면군인지는 흐름을 써 봐야 안다.
  픽스처만 보고 경계를 정하면 `doc-template-kit` M7 이 되돌린 판단(문서 5건을 위한 형식 신설)과 같은 부류가 된다.
- **C 는 뷰어 요구를 못 채운다.** 사용자는 *"IA 만 리스팅"* 을 요구했다. 인덱스는 사실이지 서술이 아니라
  화면의 **역할·액션**을 담을 자리가 없다 — `ia-doc@1` 의 `주요 영역`·`주요 액션`이 정확히 그것이다.
  그리고 인덱스는 `_meta/` 소속이라 "사람이 읽는 문서 아님" 이다.
- **B 는 문서 2건이고 트리 개정도 2행이다.** `50-domain` 을 18 → 20 으로 만들지만, 그 둘은
  **흐름 문서 10건과 같은 재료**(라우트·컴포넌트)에서 나오므로 한계 비용이 낮다.

`ia-doc@1` 을 열면 **`surfaces[].exits` 승격**(직전 세션 제안)이 다시 유효해진다.
화면 전이 그래프의 노드·엣지가 프론트매터로 올라오고, 뷰어 depth 2 가 채워진다.

> **주의**: IA 를 열어도 **화면 전수 인벤토리는 목표가 아니다.** admin 라우트만 282개다.
> B 안의 IA 문서 2건은 **흐름이 지나는 화면 + 그 이탈 대상**을 덮는다. 나머지는 결손으로 남기고
> 그 사실을 문서 상단에 적는다 — `05-final-spec.md` §6 의 "확정된 한계" 서술 방식과 같다.

### 3-6. 시간 축 — 계산은 정본을 대조한다

`status` 는 *지금 유효한가* 에 답하지만 *언제부터 언제까지였나* 는 어느 필드도 답하지 않는다.
그 축을 여는 것은 두 조각이다 — 문서가 손으로 적는 `## 연혁` 표와, 앵커 × 커밋 인덱스 조인이
계산해 내는 연혁. **둘의 지위가 다르다.**

정본은 문서 `## 연혁` 표이고 조인 결과는 **대조용**이다. <!-- history_canon -->
어긋난 행은 정정 대상이 아니라 질문 대상이다.

조인이 정본을 이기게 두면 두 가지가 동시에 깨진다. 하나는 소유다 — 인덱스는 `10-index-git`
산출물이고 그 재생성 주기가 문서의 사실을 갈아 끼우게 된다. 다른 하나는 의미다 — 파일 이벤트는
정책 시점이 아니다. `treat.controller.ts` 의 최초 이벤트는 2024-09-02 인데 그 파일에 레이트리밋
정책이 들어온 것은 2026-07-01 이다(약 2년 어긋난다). 반대편에서는 `fca574b4` 한 커밋이 `D` 6건과
`M` 16건을 남겨, 그것을 경계로 읽으면 **탄생일이 폐기일로** 계산된다.

#### 조인 규약 — 5단계

1. **앵커에서 키 만들기.** `policies[].anchors` 원소 `<repo>/<path>[:line]` 에서 첫 세그먼트를
   저장소 이름으로, 나머지에서 `:<줄번호>` 를 뗀 것을 저장소 기준 경로로 쓴다. <!-- join_order:1 -->
2. **alias closure.** 경로를 rename 을 따라 **치환하지 않고 합집합으로** 넓힌다. 순차 walk 는
   rename 이 병렬로 일어난 커밋에서 한쪽만 보고 갈린다 — 실측으로 `treat.controller.ts` 가
   47 vs 48, `analyze-admin.controller.ts` 가 13 vs 14 로 갈렸다. <!-- join_order:2 -->
3. **SHA 합집합 중복 제거.** 정책의 이벤트 집합은 그 정책 앵커 전건의 closure 합집합이다.
   한 커밋이 두 앵커를 함께 만지면 한 번만 센다. <!-- join_order:3 -->
4. **신선도 경계.** 조인은 기준 HEAD 에 결박된다 — 동결 오라클의 기준 HEAD 가 manifest
   `repos[].head` 와 같아야 하고, 인덱스 파일 자신의 `sha256`·행 수가 동결과 같아야 하며,
   인덱스 최신 행이 그 HEAD 의 조상이어야 한다. 셋 중 하나라도 어긋나면 **오답이 아니라
   낡은 오라클**이다. <!-- join_order:4 -->
5. **0건 분류.** 이벤트가 0건인 앵커는 사유를 붙인다. 아래 표가 그 분류다. <!-- join_order:5 -->

**경계 정렬 규약** — 최초·최종은 `date` **오름차순**의 양 끝이며 동률은 `sha` **사전순**으로
가른다. 정렬 기준을 적지 않으면 JSONL 등장 순서·author date·위상 순서가 각각 다른 경계 SHA 를
내고, 동결 파일이 한 구현의 우연한 순서를 정답으로 굳혀 뷰어의 독립 구현과 어긋나도 어느 쪽이
틀렸는지 판정할 수 없다.

**SHA 접두 일치 규약** — 문서 표의 SHA 셀은 접두여도 된다. **7자리 미만은 위반**이고, 인덱스에
2건 이상 걸리면 **모호**이며 조인에서 뺀다. 첫 일치를 고르는 구현은 틀린 커밋을 정책 이력으로
싣는다 — 실데이터에 7자리 중복이 0건이라 이 분기는 합성 인덱스로만 밟힌다.

#### 0건 사유 3분류

| # | 사유 | 관측 조건 | 이 실행 환경에서 |
|---|---|---|---|
| (i) | 기준 HEAD 이후에 생긴 경로 | 인덱스 범위 밖 | (ii)와 구별 불가 |
| (ii) | 경로가 없다 (오타·삭제) | 인덱스에도 트리에도 없다 | (i)과 구별 불가 |
| (iii) | 변경이 없었다 | 기준 HEAD 트리에 **있고** 인덱스에 이벤트가 없다 | 갈린다 |

<!-- zero_reason:1 --> <!-- zero_reason:2 --> <!-- zero_reason:3 -->

(i)과 (ii)는 이 워크트리에서 같은 관측값을 낸다 — 서브모듈 3종이 **기준 HEAD 그 자체**라
"기준 HEAD 이후에 생긴 경로" 를 만들 수단이 없다. 둘을 합쳐 `미상 — 인덱스 범위 밖이거나 경로
없음` 으로 내고 (iii)과만 가른다. 숨기지 않고 출력에 적는다.

#### 렌더 규약

**계산된 단일 날짜를 렌더하지 않는다.** 최초·최종 중 무엇을 "정책의 날짜" 로 삼을지는 열려
있고(우산 PRD §Open Questions), 어느 선택이든 위 두 반례를 피하지 못한다. 공통 최소선은
"계산 결과를 단일 날짜로 접어 사람에게 보여 주지 않는다" 이다.

기준 HEAD 와 산출 날짜를 병기하지 않은 계산 연혁은 렌더하지 않는다. <!-- render_pin -->
병기가 없으면 독자가 그것을 문서 표와 같은 등급의 사실로 읽고, 인덱스가 재생성되는 순간
"어제 본 값" 과 "오늘 본 값" 이 달라진 이유를 댈 수 없다.

#### 기계 표식

산문이 아니라 표식을 게이트가 읽는다. 값이 아래 집합을 벗어나면 검사가 떨어진다.

| 표식 | 값 | 고정하는 것 |
|---|---|---|
| `history_canon` | `document_table` | 정본은 문서 표이고 조인은 대조다 |
| `join_order` | `5` | 조인 규약의 단계 수 |
| `zero_reason` | `3` | 0건 사유 분류 수 |
| `render_pin` | `required` | 기준 HEAD·날짜 병기 없는 계산 연혁은 렌더 금지 |

판정 도구는 `lint-docs.mjs --probe=history` 다. **저장·렌더·캐시가 없다** — 계약이 약속한 것을
계약만으로 뽑아 exit code 를 내는 도구이며 뷰어의 계산 엔진이 아니다. 인덱스는 읽기만 한다.

---

## 4. PRD 별 수정 목록

| # | PRD | 수정 | 비용 | 시점 |
|---|---|---|---|---|
| A | `doc-template-kit` | ~~M8 신설~~ **`13` 위임으로 완료** — `실패·이탈` 표 계약 + lint 규칙 `flow-exits` + 예시·fixture 갱신 | 규칙 +1 · 새 템플릿 0 | **완료** (`d6e7ec4` · `docs/_meta/ownership.md` 위임 표 125 행) |
| B | `00-protocol` | M5 산출물 정정 — `template-config.yaml` `statuses` 매핑 승격 | config 1파일 + lint config 파서 | **지금** (M5 complete, 미병합) |
| C | `00-protocol` | `00-index.md` 에 `## 흐름으로 읽기` 절 추가. **트리 코드펜스 불변** | 절 1개 · golden 변동 **0키** (실측) | **지금** (M7 complete, 미병합) <!-- flow_entry --> |
| D | `50-domain` | 마일스톤 순서 역전 — 흐름 10건을 1순위로. 흐름 커버리지 종료조건 신설 | PRD 본문 | 착수 전 |
| E | `70-viewer` | 계약 요청 #6·#7 신설 · 표면에 **3단 depth 탐색** + 리스팅 3종 + 그룹핑 3축 · MVP 재정렬 | PRD 본문 | 지금 (M0 은 게이트 무관) |
| F | 우산 PRD | **지표 #4 신설** — "흐름 미연결 정책 비율". 제약으로 만들지 않는다 | Success Metrics 1행 | 지금 |
| **G** | `00-protocol` | **M6 산출물 정정 — `ia_doc_consumed: true` 재판정** (§1-2 개정 · §3-5) | 판정문 + 게이트 대조 로직 | **지금** |
| **H** | `05-final-spec` §4 + `00-protocol` | **문서 트리 개정 — IA 문서 2건 추가** (안 B). `00-index.md` 코드펜스·`entry_count` 51→53·`doc_count` 50→52·`tree_sha256` 갱신 | **가장 비쌈** — 3단계 개정 경로 필수 | **지금** <!-- tree_revised --> |
| I | `doc-template-kit` | ~~M8~~ **`13` 위임으로 완료** — **`surfaces[].exits` 승격**(화면 전이를 프론트매터로) · lint 규칙 `ia-exits` | 규칙 +1 | **완료** (`d6e7ec4` · 같은 위임 행) |
| J | `50-domain` | IA 문서 2건 수용 — 18 → 20문서. 마일스톤에 편입 | PRD 본문 + 완주 리스크 악화 | 착수 전 |
| K | `20`·`30`·`40`·`60` | **수정 없음** | 0 | — |

**A~F 는 흐름 축, G~J 는 IA 축이다.** 흐름 축만 하면 뷰어가 2단(Flow → Policy)으로 접히고,
사용자가 지목한 화면 단위 탐색과 페이지별 그룹핑이 성립하지 않는다.

### A · `doc-template-kit` M8

```
M8 · flow-doc@1 의 실패·이탈 표 계약
  - 열 3개 이름 고정 (지점 · 조건 · 결과)
  - 결과 열 첫 토큰을 3형태로 한정 (같은 흐름 단계 | refs 형식 참조 | 막다름)
  - lint 규칙 flow-exits 신설 — flow-refs 의 참조 해석 재사용
  - examples/flow-example.md 갱신 · fixtures/bad-flow-exits.md 신설
  - README lint 규칙 표 15 → 16
```

**M7 의 선례를 그대로 따른다** — `auth_layers` 가 그랬듯 조건부가 아니라 flow 전용 무조건 규칙이며,
키 부재를 조용히 넘기지 않는다. 소비 문서 0건이라 **재작성 비용이 0인 유일한 시점**이다.

### B · `template-config.yaml` `statuses` 승격

M5 는 complete 이지만 **main 미병합 · 소비 문서 0건**이다. 지금 고치면 config 1파일 + lint 파서다.
문서가 나온 뒤면 전 문서 재검증이 붙는다.

`golden.txt` 에 `template_config.*` 행이 있으므로 개정은 명시적 `--update` 로 드러난다 — 조용한 발산이 아니다.

### C · `00-index.md` `## 흐름으로 읽기`

`00-index.md` 는 파서 계약을 가진 파일이다. **건드리면 안 되는 것과 되는 것을 구별한다:**

| 대상 | 가부 | 이유 |
|---|---|---|
| `## 문서 트리` 코드펜스 | **불가** | `index.entry_count` 53 / `index.doc_count` 52 / `index.tree_sha256` 이 게이트 계약 |
| `## 상태 범례` 표 | **불가** | `05-final-spec.md` §3 과 순서까지 exact-diff |
| **`## 흐름으로 읽기` 절 신설** | **가능** | 새 절이며 기존 추출 계약과 겹치지 않는다 |

**3단계 개정 경로는 트리 코드펜스(item H)의 것이고 새 절에는 적용되지 않는다.** `00-index.md` 자신이 규정한 3단계 — 정본 개정 → 인덱스 재생성 → `verify.sh --update` — 는 `index.entry_count` · `index.doc_count` · `index.tree_sha256` 이 걸린 코드펜스를 움직일 때의 경로다. `## 흐름으로 읽기` 는 어떤 추출 계약과도 겹치지 않아 **golden 이 한 키도 움직이지 않고 인덱스 재생성도 재동결도 부르지 않는다** — M7 이 두 편집을 다 얹은 상태에서 `verify.sh --check --bootstrap` 을 돌려 exit 0 · `golden.txt` 변동 0키를 실측했다.
**정본(`05-final-spec.md` §4)에 흐름 축을 먼저 적는 것은 그대로다.** 3단계와 무관하게 사본이 정본을 정의하지 않게 하는 규범이며, M7 은 정본 소절을 먼저 넣고 사본 절을 뒤에 넣었다.

### D · `50-domain` 마일스톤 역전

현행은 서비스별(server 8 → site 5 → admin 5) 순서로 보인다. 문제는 **관계 축이 후반에 몰린다**는 것이다.

```
현행   server 8문서 → site 5(흐름) → admin 5(콘솔)
       └ 완주 실패 시 흐름이 통째로 없어진다

제안   site 흐름 5 + admin 콘솔 5  →  server 8문서
       └ 완주 실패해도 그래프는 남고, server 각론이 빠진다
```

**흐름이 먼저 나와야 나머지 문서가 어디에 걸리는지 알 수 있다.** 종료조건도 추가한다 —
*"흐름형 온보딩 질문 전부가 최소 1개 flow 에 걸린다"*.

### E · `70-viewer`

| 항목 | 변경 |
|---|---|
| Hypothesis | **재작성.** 현행은 "배지·lane·검색·역인덱스를 갖춘 단일 표면" — 3단 depth 탐색이 가설에 없다 |
| 계약 요청 #2 | 근거 보강 — 뷰어 기능이 아니라 **우산 제약 #2 의 기계 강제** (§3-4) |
| 계약 요청 #6 | 신설 — `실패·이탈` 표 형태. A(M8)가 만들면 자동 충족 |
| 계약 요청 #7 | 신설 — `surfaces[].exits`. I 가 만들면 자동 충족 |
| Out of scope | *"`ia-doc@1` 지원 — 소비 문서 0건"* **철회.** G 로 판정이 뒤집힌다 |
| 표면 목록 | **3단 depth 탐색 신설**. 현행 7개 어디에도 없다 |
| MVP | 1(상태 배지·미판정 격리) · **2(Flow→IA→Policy 3단 탐색)** · 3(리스팅 3종) · 4(그룹핑 3축) · 5(lane) · 6(검색) |
| 착수 게이트 조건 3 | 유지 — 실문서 1건. **단 "flow 1건 + IA 1건" 으로 구체화** 권고. 정책 문서만으로는 3단을 못 만든다 |
| 마일스톤 0 | 게이트와 무관하게 지금 수행 가능 — 계약 요청 제출이 그것 |

표면을 사용자 확정 구조로 다시 적으면:

| 표면 | 내용 | 데이터 |
|---|---|---|
| 탐색 depth 1 | 흐름 목록 → 흐름 그래프(분기·조건·이탈) | flow + `실패·이탈` 계약(A) |
| 탐색 depth 2 | 단계 = 화면. 역할·액션·이탈 경로 | **IA + `exits` 계약(G·I)** |
| 탐색 depth 3 | 화면·단계에 걸리는 정책. 기획/개발 lane | policy + `refs` 역참조(계산) |
| 리스팅 | Flow 목록 · IA 목록 · Policy 목록 독립 진입 | 각 문서 프론트매터 |
| 그룹핑 | actor · 페이지 · 단계 | 적용 범위 표 · 화면 id · flow 절차 |
| 신뢰 등급 | 상태 배지 · 미판정 격리 | `statuses` 매핑(B) |

### F · 우산 PRD 지표 #4

```
| 흐름 미연결 정책 | 사유 기재 100% | 자동 | 각 문서 PRD 종료 시 |
  어느 flow 의 refs 에도 등장하지 않는 정책을 열거하고, 각각에 대해
  "흐름과 무관한 정책인가"를 판정해 사유를 적었는가
```

**제약이 아니라 지표다.** 고립이 정당한 정책이 실재한다 — 데이터 모델 제약·감사 로그 등은
흐름의 한 지점이 아니다. 제약(위반 시 산출물 거부)으로 만들면 억지 연결을 만들게 된다.

### G · `00-protocol` M6 — `ia_doc_consumed` 재판정

M6 은 **온보딩 질문 55건만 보고** `false` 를 확정했다. 판정 자체는 그 스코프 안에서 옳다 —
목록형 질문 0건은 사실이다. 정정할 것은 **판정 입력**이다.

```
현행   ia_doc_consumed 를 온보딩 질문 집합 하나가 결정한다
       (verify.sh 게이트가 screen_inventory: true 질문 존재와 대조)

정정   소비자가 둘이다 — 신입 개발자(온보딩 질문) · 뷰어 독자(70-viewer)
       어느 한쪽이 요구하면 true
```

게이트 로직도 따라 바뀐다 — `screen_inventory` 질문 부재만으로 `false` 를 강제하면 안 되고,
뷰어 요구를 근거로 `true` 를 선언할 수 있어야 한다. **판정문에 근거 2줄을 병기**한다.

### H · 문서 트리 개정 — 가장 비싼 항목

IA 문서 2건(§3-5 안 B)을 넣으면 연쇄가 크다.

| 대상 | 변경 |
|---|---|
| `05-final-spec.md` §4 | **정본.** `20-site/00-ia.md` · `30-admin/00-ia.md` 2행 추가 |
| `00-index.md` §문서 트리 코드펜스 | 사본 갱신 — 정본과 exact-diff |
| `00-index.md` §읽는 순서 표 | 변경 없음 — 디렉터리 단위라 이미 덮는다 |
| `golden.txt` | `index.entry_count` 51 → **53** · `index.doc_count` 50 → **52** · `index.tree_sha256` 갱신 |
| 우산 PRD §PRD 분할 맵 | 변경 없음 — glob(`docs/20-site/**`)이라 이미 덮는다 |
| `50-domain` PRD | 18 → 20문서 (J) |

개정 경로는 `00-index.md` 자신이 규정한 **3단계 고정**이다:

> 정본 개정 → `00-protocol` 소유자가 인덱스 재생성 → `bash docs/_meta/verify/verify.sh --update`

정본만 고치고 `--update` 하면 막힌다 — `run_index_gate` 가 사본과 정본을 exact-diff 하고
`--update` 는 `validate_candidate` 를 먼저 통과해야 한다. **인덱스 재생성이 반드시 중간에 낀다.**

`00-index.md` 는 스스로 *"이 트리는 잠정이다"* 라고 적고 개정 경로를 미리 열어 뒀다 —
설계된 경로를 쓰는 것이지 우회가 아니다.

### I · `surfaces[].exits` 승격

G 가 확정되면 직전 세션 제안이 다시 유효해진다.

```yaml
surfaces:
  - id: alimtalk-step-two
    path: /alimtalk/step-two/:code
    status: active-prod
    evidence: [S-test, S-route, S-component]
    exits: [alimtalk-step-three, alimtalk-step-fail]   # ← 신설
    anchors: [...]
```

본문 「이탈 경로」 불릿은 삭제한다 — 같은 사실을 두 곳에 쓰지 않는다.
역참조 필드는 만들지 않는다. 뷰어가 `exits`·`refs` 를 뒤집어 계산한다 (§3-2).

**구현 결과 (마일스톤 3).** 원소는 `<대상>` 또는 `<대상> :: <조건>` 이다. 대상은 넷 중 하나 —
같은 문서 `surfaces[].id` · **`config.surface_exit_kinds` 멤버** · 패턴 `외부 <경로>` ·
`<상대경로>.md#<id>`. 허용 어휘는 `flow-exits` 와 같은 이유로 lint 가 아니라 config 에 산다.
두 어휘 키는 종단 토큰 `막다름` 을 공유하며 그것은 우연이 아니라 같은 뜻이다 — `exit_kinds` 와
`surface_exit_kinds` 를 하나로 합치지 않는 이유는 축이 다르기 때문이다(합치면 flow 결과 열에
`없음` 이, 화면 이탈에 `fail-open` 이 합법이 된다).

**본문 불릿의 괄호·대시 부연은 버리지 않고 `:: <조건>` 으로 보존한다.** 초안은 그 부연이
flow 의 `조건` 열로 간다고 적었으나 **실측으로 거짓이다 — 정정한다(2026-08-18).**
`alimtalk-step-fail` 의 `막다름(재시도 불가 사유일 때)` 는 **화면 층의 분기 조건**이고,
대응하는 flow 행의 조건은 `심평원 인증 실패` 다. 서로 다른 사실이라 옮길 자리가 없다.
승격에서 보존한 부연은 4건이며 게이트 `#4` 가 이전 전 본문과 전수 대조한다.

조건 문면은 사람이 쓰는 자유 서술이고 계약이 지키는 것은 **대상 토큰**이다. 맵 리스트
(`- to:`/`when:`)를 쓰지 않는 이유는 평탄 파서가 그 형태를 던지지 않고 **조용히 오파싱**하기
때문이며, INV1 이 파서 라이브러리 도입을 막는다.

lint 규칙 `ia-exits` — 대상이 위 넷 중 하나인지, `exits` 가 1개 이상인지, `:: ` 뒤 조건이
비지 않았는지 검사. `flow-refs` 의 해석 로직 재사용이므로 신규 파서 0.

### J · `50-domain` 이 IA 2건을 수용

18 → 20문서. **완주 실패 리스크(확률 높음)가 악화된다.** 완화는 D 와 같은 장치다 —
마일스톤 순서에서 **흐름 5 + 콘솔 5 + IA 2 = 12건을 앞에**, `10-server/` 8문서를 뒤로.
완주 실패 시 잃는 것이 서버 각론이 되고, 뷰어 3단 구조는 살아남는다.

### K · `20`·`30`·`40`·`60` 은 고치지 않는다

역참조 요청 파일(`flow-requests/`)을 만들 뻔했으나 **만들지 않는다.**
`term-requests/` 는 glossary 가 단일 소유라 병합이 필요했지만, 흐름 연결은 **계산 가능**하다.
사람 동기화 지점을 늘리는 대신 뷰어와 게이트가 리포트한다.

---

## 5. 남는 문제

| # | 문제 | 성격 | 소유 |
|---|---|---|---|
| **P1** | 관계를 만들 권한이 `50-domain` 단독 — 단일 실패점 | 구조적. 소유 프로토콜의 필연 | 사용자 |
| **P2** | **흐름 커버리지가 미지수** — 정책 몇 %가 흐름에 걸리는지 아무도 모르고 측정 수단도 없다 | 지표 #4 가 신설되어야 측정 시작 | 우산 |
| **P3** | `실패·이탈` 표가 계약 밖 | A(M8)로 해소 | doc-template-kit |
| **P4** | `placement` 가 데이터에 없어 제약 #2 가 사람 준수 의존 | B 로 해소 | 00-protocol |
| **P5** | `00-index` 가 주제 축 단일 | C 로 해소 | 00-protocol |
| **P6** | M5·M6·M7 이 complete — 개정에 santa 재수렴이 붙을 수 있다 | 절차 비용. 미병합이라 아직 낮다 | 00-protocol |
| **P7** | ~~흐름에 등장하지 않는 화면이 문서 어디에도 없다~~ → **판정 입력이 소비자 하나만 봤다** | G 로 해소. 아래 개정 참조 | 00-protocol |
| **P8** | `절차` 단계는 여전히 산문 — 흐름 안에서의 화면 역할이 자유 서술 | IA 가 열리면 역할·액션은 IA 가 담는다. flow 라벨 5번째는 여전히 만들지 않는다 | doc-template-kit |
| **P9** | **화면 전수 인벤토리는 여전히 목표가 아니다** | admin 라우트만 282. IA 2건은 흐름이 지나는 화면 + 이탈 대상만 덮는다. 결손을 문서 상단에 명시 | 50-domain |
| **P10** | **문서 트리 개정이 3단계 절차를 요구한다** | 설계된 경로이나 santa 재수렴이 붙을 수 있다. M5~M7 이 complete 이므로 재개정이다 | 00-protocol |
| **P11** | `50-domain` 이 18 → 20문서 | 완주 실패 리스크(확률 높음) 악화. 마일스톤 순서로만 완화된다 | 사용자 |

**P1 과 P2 가 짝이다.** 커버리지를 모르는 채로 단일 PRD 에 맡겨 두면, 완주 실패했을 때
무엇을 잃었는지조차 알 수 없다. 지표 #4 는 그 가시성을 만드는 최소 장치다.

> **P7 개정 (2026-08-11).** 초판은 *"P7 은 되돌릴 판단이 아니다"* 라고 적었다. **틀렸다.**
> M6 판정 근거(*"목록형 질문 0건"*)는 온보딩 질문 집합 안에서는 사실이지만,
> 뷰어라는 두 번째 소비자가 `Flow → IA → Policy` 3단과 *"IA 만 리스팅"* · *"페이지별 그룹핑"* 을
> 요구한다. **되돌릴 것은 판정 결과가 아니라 판정 입력이다** — 소비자 둘 중 하나만 보고 결정하게
> 설계한 것이 결함이고, 그 설계는 `70-viewer`(판정을 M6 에 위임)와 M6(온보딩 질문만 조회)이
> 서로에게 미룬 결과다. 정정은 §1-2 개정 블록과 §4-G.

**P10 과 P11 이 이 개정의 실제 비용이다.** 나머지 항목은 계약·규칙·절 추가로 끝나지만,
이 둘은 이미 complete 판정을 받은 산출물과 이미 위험한 PRD 를 건드린다.
그래도 **지금이 가장 싸다** — 문서 본문 0건이므로 재작성 대상이 없다.

---

## 6. 시점 — 무엇이 언제까지 싼가

```
지금 (docs/ 산출 0건 · M5~M7 미병합)
  A  flow-exits 계약        재작성 비용 0
  B  statuses 매핑 승격     config 1파일 + lint 파서
  C  00-index 흐름 절       golden --update 1회
  E  viewer 계약·표면 갱신   PRD 본문
  F  지표 #4               PRD 1행
  G  ia_doc_consumed 재판정  판정문 + 게이트 로직        ← 가장 먼저. H·I·J 가 여기 종속
  H  문서 트리 +2행         3단계 개정 경로 · golden --update
  I  surfaces[].exits 계약   규칙 +1 (G 확정 후)
  J  50-domain 18 → 20      PRD 본문 (착수 전)
        │
        ▼  M5~M7 병합 · 문서 PRD 착수
  A·I → 전 flow·IA 문서 재작성
  B   → lint 수정 + 전 문서 재검증
  C·H → 동결 선언된 파일 개정 + 하류 재확인 + 트리 재생성
  G   → 판정 뒤집기가 화면 문서 신규 작성을 유발
        │
        ▼  50-domain 흐름 10건 산출 후
  D·J → 순서 역전이 의미 없어짐 (이미 나왔거나 이미 밀렸다)
```

**열 개 전부가 "지금" 칸에 있다.** 어느 것도 문서 본문을 건드리지 않는다 — 본문이 0건이기 때문이다.

**순서는 G 가 먼저다.** IA 채택 여부가 확정되지 않으면 H(트리)·I(exits)·J(50-domain 규모)가
전부 공중에 뜬다. G 는 사용자 결정 사항이며, `70-viewer` 와 M6 이 서로에게 미뤄 둔 그 판정이다.

---

## 7. 이 분석의 한계

- **worktree 상태 기준이다.** protocol worktree `b9219e7` 의 M5~M7 산출물을 읽었고, 그것이 main 에
  병합될 때 형태가 달라질 수 있다. main 의 PRD 본문은 아직 pending 으로 적혀 있다.
- **흐름 커버리지를 측정하지 않았다.** "정책 몇 %가 흐름에 걸리는가" 는 정책 문서 0건이라 계산 불가다.
  P2 를 문제로만 적고 수치를 못 적은 이유다.
- **`실패·이탈` 표의 실사용형을 모른다.** 계약 제안은 `flow-example.md` 3건 기준이다. 실문서가
  나오면 3형태 한정이 좁을 수 있다 — 그때 형태를 넓히되 자유 산문으로 되돌리지는 않는다.
- **`anchors` 공유 관계의 밀도를 재지 않았다.** §3-2 의 세 번째 관계가 실제로 유용한 밀도를 갖는지는
  정책 문서가 나와야 안다. 앵커가 전부 서로 다르면 엣지가 0이다.
- **index-runtime worktree 를 읽지 못했다** (디렉터리 비어 있음). 12축 산출물이 흐름 축에
  기여할 수 있는지는 확인하지 않았다.
- 세션 중 `.claude/plans/insuniverse-docs-12-index-runtime*.plan.md` 2건이 사라졌다(세션 초 존재).
  병렬 세션의 조작으로 보이나 확인하지 않았다.
- **IA 안 B(서비스별 1건씩)의 적정 규모를 재지 않았다.** site features 26 / pages 19,
  admin 라우트 282 에서 "흐름이 지나는 화면 + 이탈 대상" 이 몇 건인지는 흐름 문서를 써 봐야 안다.
  2건으로 담기지 않으면 A 안(화면군 단위)으로 갈라야 하고 문서 수가 더 는다.
- **`ia_doc_consumed` 게이트의 실제 구현을 읽지 않았다.** `verify.sh` 의 대조 로직이
  `screen_inventory` 질문 존재만 보는지, 다른 입력도 받는지는 §4-G 를 실행할 때 확인해야 한다.
- **뷰어 3단 depth 의 상호작용 설계는 이 문서 범위 밖이다.** 데이터 계약까지만 다뤘다.
  시각 설계는 `70-viewer` §Design Direction 이 착수 게이트 이후로 미뤄 둔 그대로다.
