# rules/consistency-contract — 문서 간 일관성 계약 (Single-Owner + Reference-Only) > `rules/` 의 방법론 규칙. 문서 간 **모순의 근원은 재진술(복제)** 이다 — 같은 정책이 두 곳에 적혀 있으면 owner 쪽만 갱신될 때 모순이 *생산*된다. 본 계약은 재진술을 금지하고, 참조를 기계 검증하며, owner 변경을 역참조에 전파한다. > 집행 3층: ① 결정론 검사기 `.claude/hooks/wiki_consistency_check.py` (Layer 1) ② `wiki-consistency-auditor` 의미 대조 (Layer 2) ③ `/sync` 수거 명령 (Layer 3). ## 원칙 | 원칙 | 내용 | |---|---| | **Single-Owner** | 모든 결정·관심사는 **정확히 1개의 owner 문서**를 가진다 — branch-note 의 Decision Evidence Map `D` 행, 또는 project-note 의 `§` 섹션. 같은 관심사를 두 branch 가 `covered-here` 주장하면 `DUAL_OWNERSHIP`. | | **Reference-Only** | 타 문서는 owner 를 **포인터 + 1줄 요약**으로만 인용한다: `[[raw/branch-notes/]] D — <1줄 요약>`. 정책 세부(임계값·메커니즘·예외 목록)의 재진술 금지 — 재진술은 owner 진화 시 낡은 복제본이 된다 (`RESTATED_FOREIGN_DECISION`). | Project contract v2 에서는 project-note 의 Project Decision Registry 가 project-wide 결정 owner 다. ID 는 `DEC---NNN`, revision 은 양의 정수이며 branch 참조는 항상 `DEC-...@revision` 으로 pin 한다. ``·`` 은 uppercase kebab-case 다. ## 참조 형식 표준 (검사기 파싱 규약) | 대상 | 형식 | 금지 | |---|---|---| | branch 결정 | `[[raw/branch-notes/]] D` — wikilink **종료 후 같은 줄 100자 이내**에 `D` | bare 슬러그 + D (예: `feature-x-contract D7`) → `BARE_DECISION_REF` | | project 섹션 | `[[raw/project-notes/]] §` — 같은 줄 100자 이내 | 부재하는 § 번호 → `DANGLING_SECTION_REF` | | Coverage delegated owner | owner 셀에 wikilink 필수 | bare 이름 → `BARE_OWNER_REF` | | project 결정 | `DEC---NNN@` + `[[raw/project-notes/]]` | revision 없는 ID, 결정 상세 복제 | | project Work Item | `WI--NNN` | branch slug 만으로 project handoff 식별 | - `D` 토큰이 wikilink 에서 같은 줄 100자를 넘으면 검사기가 참조 엣지로 인식하지 못한다 — 링크 직후에 쓴다. - fenced code block 내부는 검사 대상 아님 (예시/템플릿 허용). ## 명명된 실패 모드 | 코드 | 층 | 의미 | |---|---|---| | `DANGLING_DECISION_REF` | 결정론 (`wiki_consistency_check.py`) | `[[feature-B]] D17` 인데 B 의 결정 표에 D17 부재 (B 실존 시 — 노트 부재는 `BROKEN_LINK` 몫) | | `BARE_DECISION_REF` | 결정론 | wikilink 없는 bare 슬러그 + `D` — 기계 추적 불가 | | `BARE_OWNER_REF` | 결정론 | Coverage delegated 행의 owner 셀에 wikilink 없음 | | `DUAL_OWNERSHIP` | 결정론 | 같은 관심사(정규화 exact)를 두 branch 가 `covered-here` 주장 | | `DANGLING_SECTION_REF` | 결정론 | `[[project-note]] §34` 인데 해당 § 헤더 부재 | | `STALE_SUMMARY` | 의미 (`wiki-consistency-auditor`) | 참조의 1줄 요약이 owner D-row 의 현재 내용과 어긋남 (owner 진화 후 무통보 낡음) | | `CONTRADICTION` | 의미 | 두 문서가 같은 사안에 대해 양립 불가한 진술 | | `RESTATED_FOREIGN_DECISION` | 의미 | 타 owner 의 결정 세부를 포인터 없이/포인터와 함께 본문에 재진술 (복제) | | `MISSING_PROJECT_BINDING` | 결정론 | `project-work-item`의 slug가 WI row와 다르거나, `branch-child`의 parent·project·work_item 상속이 누락/불일치/순환임 | | `MISSING_INHERITED_DECISION` | 결정론 | Work Item `Applies Decisions` 의 pinned ref 가 branch `inherits` 또는 Contract Packet 에 없음 | | `STALE_INHERITANCE_REVISION` | 결정론 | branch 의 pinned revision 이 project registry 의 현재 decision revision 과 다르고 migration/override 로 설명되지 않음 | | `CONFLICTS_WITH_PROJECT_DECISION` | 의미 | branch-local 결정·적용 요약이 inherited project decision 과 양립 불가하며 유효한 override 도 없음 | | `UNDECLARED_OVERRIDE` | 결정론 + 의미 | project 결정과 다른 동작을 취하면서 `overrides` 와 Declared Overrides 표에 같은 pinned ref·이유·승인을 선언하지 않음 | | `MISSING_EXPECTED_EDGE` | 결정론 | project→Work Item→branch, Work Item dependency, decision→branch inheritance 중 registry 가 기대하는 edge 가 없음 | | `DUPLICATE_DECISION_OWNER` | 결정론 + 의미 | 같은 stable Decision ID 또는 같은 정규화 관심사를 둘 이상의 project/branch owner 가 소유함 | | `DUPLICATE_BRANCH_ID` | 결정론 | 같은 stable Branch ID를 둘 이상의 v2 branch가 선언함 | ## Project → Work Item → Branch 상속 계약 1. project-note 가 `project_revision`, Project Decision Registry, Work Item Registry 를 소유한다. 2. Work Item row 는 적용 결정을 `DEC-...@revision` 으로 pin 하고 dependency 를 `WI-...` 로 가리킨다. 3. project 직접 자식 branch 는 `/branch-from-project` 로 만들며 frontmatter `project`·`work_item`·`inherits`·`depends_on` 과 `## Branch Contract Packet` 을 가진다. 4. branch 는 inherited 결정의 상세를 복제하지 않는다. project wikilink + pinned ref + project summary + branch application 만 기록한다. 5. branch-local 결정은 `D` owner row 로 유지한다. project 결정을 refine 하면 `refines`, 다르게 적용하면 `overrides` 와 Declared Overrides row 를 함께 기록한다. 6. `kind: project-work-item`은 자기 slug가 Work Item Registry의 `branch slug`와 exact match여야 한다. 7. `kind: branch-child`는 `parent_branch`가 필수이며 parent가 실존하고 cycle이 없어야 한다. child는 parent와 같은 `project`·`work_item`을 상속하고, Work Item row의 `branch slug`는 child가 아니라 parent `project-work-item` slug와 match한다. 8. `branch-child.inherits`는 parent inheritance + Work Item `Applies Decisions`의 superset이어야 한다. 제외는 같은 pinned ref가 frontmatter `overrides`와 승인된 Declared Overrides row 양쪽에 선언된 경우만 허용한다. 9. 직접 branch의 `id`는 Work Item ID의 `WI-`를 `BR-`로 바꾼 값이다. child는 `BR--CHILD-`를 사용하며 rename 뒤에도 ID를 재사용한다. 10. `planned`·`backlog`·`proposed`·`documented-only` Work Item은 branch 파일이 아직 없어도 expected-edge 실패로 보지 않는다. `in-progress` 이상 상태에서만 branch 실존을 요구한다. archive로 격리한 문서는 active graph 검사에서 제외한다. active project/branch는 v2 계약을 적용하며 legacy 문서가 발견되면 `LEGACY_GRAPH_CONTRACT`로 이관 대상임을 보고한다. ## 전파 owner 노트의 D-row (결정 표) 가 변경되면: 1. **PostToolUse 훅이 역참조 목록을 비차단 알림** — 쓰기는 이미 완료, 모델에 "이 결정을 참조하는 문서 N개" 정보만 전달. 2. **같은 세션에서 참조 요약 갱신을 권장.** 변경이 D-row 의 의미를 바꿨다면 참조 측 1줄 요약이 낡았을 가능성이 높다. 3. 같은 세션에서 못 갱신한 항목은 **`/sync` 가 수거** (Layer 2 의미 대조 → fix-plan). 신규 참조 작성 시에는 PreToolUse 가 `DANGLING_DECISION_REF`/`DANGLING_SECTION_REF` 를 **쓰기 차단** — owner 의 실제 Decision ID 를 확인하거나, 결정이 아직 없으면 owner 노트에 먼저 기록한다. ## 충돌 해소 우선순위 1. **owner 문서 우선** — 위임한 쪽(참조자)이 요약을 갱신한다. owner 본문을 참조자에 맞춰 고치지 않는다. 2. **hub(project-note) vs branch 충돌은 자동 적용 금지** — fix-plan 으로 사용자 판정. 보통 branch 가 더 최신·구체이므로 "project-note 갱신 제안" 형태가 기본이지만, 어느 쪽이 옳은지는 사용자가 정한다. 3. **적용은 항상 승인 후** — 어떤 해소도 사용자 확인 없이 본문을 바꾸지 않는다. ## retro 정책 - 기존 재진술·bare 참조는 **`/sync` 의 fix-plan 으로 점진 수거** — 일괄 자동 수정 금지. (2026-06 전수 dry-run: findings 190건 = `BARE_DECISION_REF` 129 · `BARE_OWNER_REF` 60 · 실제 `DANGLING_DECISION_REF` 1건 — D14 오귀속.) - **신규 작성은 본 규약 준수** — 작성 시 참조 형식 가이드는 capture 계열(`/branch-spec` 등)이 본 문서를 참조한다. ## 한계 — 귀속 모호성 검사기는 외부 링크 후방 윈도의 `D` 이 **인용자 자신의 DEM 에도 존재하면 침묵**한다 — 자기-결정 언급일 수 있기 때문 (실코퍼스: "X 에 의존 — 우회(D13)" 의 D13 이 인용자 자신의 D13). 따라서 결정론 층은 이런 케이스의 오귀속을 잡지 못하며, **의미 귀속 판정은 Layer 2 `wiki-consistency-auditor` 의 몫**이다.