# Implementation Report: 10 · 사실 인덱스 · git 축 — 마일스톤 0·1

**Plan**: `.claude/plans/insuniverse-docs-10-index-git.plan.md`
**PRD**: `.claude/prds/insuniverse-docs-10-index-git.prd.md`
**Branch**: `feat/index-git` (worktree `.worktrees/index-git`)
**Date**: 2026-08-05

## Summary

마일스톤 0(입력 동결 확인)과 1(commits.jsonl ×3 + 구↔신 SHA 수치 병기)을 완료했다.
`docs/_meta/index/extract-commits.mjs` 추출기 1개를 커밋하고 그것으로 3개 저장소의 커밋
인덱스 **6,190행**을 산출했다. PRD §Evidence 의 구 SHA 수치 4항목 전부에 신 SHA 실측값을
측정 명령과 함께 병기했다.

구현 중 **plan 이 예측하지 못한 커밋 손실 187건**을 발견해 닫았다 — 아래 §Deviations.

## Assessment vs Reality

| Metric | Predicted (Plan) | Actual |
|---|---|---|
| Complexity | Medium | Medium |
| Files Changed | 6 | 6 (구현 파일 기준. `.claude/` 게이트 산출물 4개 별도) |
| 산출 행 수 | 미예측 | 6,190 (server 2,587 / site 2,446 / admin 1,157) |
| selftest 케이스 | 17 (계획 명시분) | 43 (Codex I1 권고로 실 저장소 통합 12건 추가) |

## Tasks Completed

| # | Task | Status | Notes |
|---|---|---|---|
| 1 | 마일스톤 0 — 입력 동결 확인 | 완료 | 3/3 문자열 일치. `.git/modules/` 로컬 복원, 네트워크 클론 없음 |
| 2 | `extract-commits.mjs` 작성 | 완료 (이탈 1) | `--full-history` 추가 — 아래 §Deviations |
| 3 | 3개 저장소 추출 실행 | 완료 | 2,587 / 2,446 / 1,157 |
| 4 | 재현성 판정 | 완료 | 2회 실행 `diff` 빈 출력 ×3 |
| 5 | §Evidence 구↔신 SHA 4항목 병기 | 완료 | 측정 명령 4종 동봉 |
| 6 | `findings/10-index-git.md` | 완료 | 항목 2건(F-10-01 계획분 · F-10-02 발견분) 각 6종 필수 항목 |
| 7 | PRD 마일스톤 표 갱신 | 완료 | 0·1 `complete`, 2~6 `pending` 5행 유지 |

## Validation Results

| 블록 | 내용 | 결과 |
|---|---|---|
| 1 | 동결 게이트 (`rev-parse HEAD` ↔ manifest) | PASS 3/3 |
| 2 | `--selftest` | PASS 43케이스 (단위 31 + 실 저장소 통합 12) |
| 3 | 재현성 (2회 실행 편차 0 + 커밋본 일치) | PASS ×3 |
| 4 | 산출물 정합 (`era` 100% · `era: null` 0 · 키 순서 고정) | PASS |
| 5 | 커밋 손실 0 (`rev-list --count --full-history`) | PASS ×3 |
| — | plan-conflict-detector (구현 파일 6종) | `conflict: false` |

프로젝트에 `package.json` 이 없다(PRD §착수 전제 2). type-check / lint / build 단계는 **해당 없음** —
검증은 위 5블록과 추출기 자체 `--selftest` 가 전부다.

### Design Grounding

N/A — design trigger 미발동. `impeccable-detect --mode implement` → `skill_available:false` ·
`design_signal:false`. 산출물이 `.mjs` 1개와 `.jsonl` 3개뿐이라 렌더링 표면이 없다.
Phase 3.6(clarify/distill/polish)·3.7(grounding verify)은 no-op.

## Files Changed

| File | Action | 규모 |
|---|---|---|
| `docs/_meta/index/extract-commits.mjs` | CREATED | 약 420행 |
| `docs/_meta/index/commits.server.jsonl` | CREATED | 2,587행 |
| `docs/_meta/index/commits.site.jsonl` | CREATED | 2,446행 |
| `docs/_meta/index/commits.admin.jsonl` | CREATED | 1,157행 |
| `docs/_meta/findings/10-index-git.md` | CREATED | 발견 2건 |
| `.claude/prds/insuniverse-docs-10-index-git.prd.md` | UPDATED | §Evidence 병기 + 측정 명령 + 마일스톤 표 |

**배타 소유 경로 밖 무수정 확인** — `docs/_meta/extraction-manifest.yaml` · `docs/_meta/verify/**` ·
서브모듈 3개 내부 전부 무변경.

`.claude/` 아래 4개(`plans/*.plan.md` · `plans/codex-findings-backlog.md` · `receipts/*` 2개)는
mccp 게이트 자신이 쓴 제어면 산출물이며 구현 산출물이 아니다.

## Deviations from Plan

### D1 · 두 패스에 `--full-history` 추가 — 커밋 187건 소실 차단

**WHAT** — plan Task 2 는 `git log <sha> [--no-merges] -- <pathspec>` 형태만 규정했다. 구현은
두 패스 모두에 `--full-history` 를 붙였고, Validation 블록 5 의 기준선도
`rev-list --count --full-history` 로 정정했다.

**WHY** — pathspec 을 준 `git log`/`rev-list` 는 **기본으로 히스토리 단순화**를 한다. 머지가 한쪽
부모와 TREESAME 이면 반대편 가지를 통째로 잘라낸다. 동결 HEAD 실측:

| 저장소 | 기본 워크 | `--full-history` | 소실 |
|---|---|---|---|
| server | 2,489 | 2,587 | 98 |
| site | 2,397 | 2,446 | 49 |
| admin | 1,117 | 1,157 | 40 |
| **합** | **6,003** | **6,190** | **187** |

**이 결손을 plan 의 손실 판정이 잡지 못했다.** 블록 5 는 인덱스를
`rev-list --count HEAD -- ':(exclude)planning/*'` 와 대조하는데, 기준선에도 같은 단순화가
걸려 있어 187건이 사라진 상태에서 **항진명제로 통과**했다. 교차 확인은 revert 계수에서 나왔다 —
제목 무필터 grep 은 site 5건인데 인덱스는 4건이었고, 빠진 `eb1bed93` 은 제외 대상이 아닌
`front/src/pages/analysis/...` 만 만진 커밋이었다.

**근거** — plan 의 명시 목표가 "커밋 손실 0"이고 Codex R1 F1 이 같은 축(커밋이 조용히 사라짐)을
지적했으므로, 이 수정은 plan 의도 안이다. plan 본문 Task 2 · Validation 블록 5 · Risks 표에
이탈 근거를 기록했고, `findings/10-index-git.md` F-10-02 로 00-protocol 에 보고 대기로 올렸다.
selftest 는 `merge -s ours` 픽스처로 단순화 손실 경로를 결정적으로 재현하고 복원을 단언한다.

### D2 · plan 아카이브 미수행 (의도적)

`/mccp:prp-implement` Phase 5 는 plan 을 `.claude/PRPs/plans/completed/` 로 옮기라고 하지만
**수행하지 않았다.** 이유 2가지:

1. mccp receipt 2개(`mccp-plan-codex` · `mccp-implement-codex`)가 `.claude/plans/insuniverse-docs-10-index-git.plan.md`
   경로에 바인딩돼 있다. 옮기면 `/mccp:pr` 의 체인 검증이 깨진다.
2. PRD 마일스톤 표 0·1 행의 `Plan` 셀이 같은 경로를 가리킨다.

이 plan 은 PRD 7개 마일스톤 중 0·1 만 다루며 2~6 이 남아 있다.

### D3 · plan-conflict-detector 의 `file-expansion` 오탐 (조치 없음)

detector 를 워킹트리 전체(9파일)에 돌리면 `conflict: true / signal: file-expansion` 이 난다.
그 9개 중 4개는 mccp 게이트 자신이 쓴 `.claude/` 제어면 산출물(plan 본문 · Codex backlog ·
receipt 2개)이고, plan `Files to Change` 6항목 밖으로 나간 **구현 파일은 0개**다.
구현 파일 6종만으로 다시 돌리면 `conflict: false`.

detector 가 게이트 산출물을 구현 산출물과 구별하지 못하는 한계이며, 실제 plan↔구현 격차인
D1 은 detector 가 아니라 revert 교차 검증에서 나왔다. 에스컬레이션(`chain_aborted`) 대신
D1 을 코드·plan 본문·findings 3곳에 기록하는 것으로 닫았다.

## Issues Encountered

| # | 문제 | 해소 |
|---|---|---|
| 1 | 히스토리 단순화 커밋 손실 187건 | D1 — `--full-history` + 기준선 정정 + selftest 픽스처 |
| 2 | `mccp-plan-codex` receipt 가 plan 편집으로 stale | 게이트가 mandate 한 편집이므로 같은 meta(`codex_verdict: divergent` · `impeccable_skipped`)로 재스탬프 |
| 3 | 구 SHA revert 20건이 신 SHA 10건보다 큼 (조상인데 더 많음) | 구값 산출 방법이 미기록이라 재현 불가. PRD 에 사실대로 명시하고 신 값의 측정 명령을 동봉 |
| 4 | 첫 커밋 후 재체크아웃하면 `.jsonl` 이 CRLF 가 되어 **재현성 지표가 깨짐** (`core.autocrlf=true`, 실측 CR 1,157개) | `.gitattributes` 에 `docs/_meta/index/* text eol=lf` + `git add --renormalize`. 파일 삭제 → `git checkout` → 재추출 → `diff` 3/3 빈 출력 재확인 |

## Tests Written

| 층 | 케이스 | 대상 |
|---|---|---|
| 단위 (합성 바이트) | 31 | era 경계 8 · trailer 4 · 패스 B `-z` 파싱 10(탭·개행·한글 경로 포함) · 패스 A RS/US 4 · buildRows 계약 5 |
| 통합 (실 임시 git 저장소) | 12 | 손실 0 불변식 · 단순화 손실 복원 · planning 전용 배제 · era 양 경계 · rename R100 · `excluded_only` · 머지 · trailer 전용 본문 · 재현성 |
| **합계** | **43** | `node docs/_meta/index/extract-commits.mjs --selftest` |

프레임워크 없음. `assert` 대신 `eq()`/`throws()` 로컬 헬퍼 + 종료 코드 판정 —
`docs/_meta/verify/extract-menu-codes.mjs` 선례를 따른다.

## Santa Loop — 이중 독립 리뷰 3라운드 (2026-08-05)

Reviewer A = Claude Opus (`code-reviewer`, 저장소 접근) · Reviewer B = Codex gpt-5.4 (`--sandbox read-only`).
각 라운드는 이전 라운드 기억이 없는 신선한 리뷰어다.

| 라운드 | A | B | 조치 | 커밋 |
|---|---|---|---|---|
| 1 | PASS (critical 0) | FAIL (4) | sentinel 프레이밍 단언 · 규약 드리프트 전항목 · git `-c` 고정 · body 개행 1개만 제거 | `7051e2c` |
| 2 | PASS (critical 0) | FAIL (4) | 도달성 독립 검사 · `--repo` 경로 봉쇄 · `follow_renames` 대조 · 적대적 설정 테스트 | `e27ec92` `b54cb96` |
| 3 | PASS (critical 0) | FAIL (3) | `diff-tree --root` · 빈 커밋 증명 · manifest 블록 한정 · 옥토퍼스/빈커밋 픽스처 | `7216ba1` |

**판정: NAUGHTY (3라운드 상한 초과) — push 하지 않음.**

selftest 43 → 74 케이스. 산출물 3개는 3라운드 내내 무변경.

### 두 리뷰어의 신뢰도 격차 — 기록해 둘 값어치가 있다

A 는 3라운드 전부 PASS · critical 0 을 냈다. B 는 3라운드 전부 FAIL 을 냈고, **매 라운드
실측으로 확인된 진짜 결함을 최소 1건씩 내놨으며 그중 어느 것도 A 는 잡지 못했다**:

- R1 — sentinel 바이트가 레코드 경계를 어긋내도 파서 단독으로는 못 잡는다 (selftest 의 "위조 레코드" 케이스가 증명)
- R2 — 손실 증명이 순환적이었다 (같은 경로 한정 워크끼리 대조). A 는 같은 코드를 보고 *"assertFraming is independent of parser"* 라고 PASS 를 줬다
- R3 — `diff-tree` 에 `--root` 가 없어 루트 커밋 누락이 무성 통과 (실측: 루트 경로수 0 vs 35/1/4,561)

R2 에서는 `resolveRepo` 를 두고 정면 충돌했다 — A 는 *"path traversal 방지됨"*, B 는 *"basename 만
맞으면 임의 경로 허용"*. 코드에 `existsSync(spec) ? spec : …` 분기가 실재했으므로 **B 가 옳았다.**

단일 리뷰어(특히 같은 모델 계열)로는 이 결함들이 전부 통과했을 것이다.

### 검증 라운드 — 라운드 3 수정물 자체 리뷰 (2026-08-06, `466e9a1`)

라운드 3 수정(`7216ba1`)이 리뷰를 못 받은 채 남아 있어 그 커밋만 대상으로 한 라운드를 더 돌렸다.
A = PASS(critical 0) · B = FAIL(4). **A 가 틀렸다** — A 는 manifest 스코핑 테스트를 "동작함" 으로
판정했으나 미끼 블록 값이 정답과 같아 전체파일 정규식으로 되돌려도 통과하는 항진명제였다.

B 가 잡은 것 4건 전부 유효했고, 그 과정에서 **테스트가 판별력이 없던 사례 5건**이 더 드러났다:

| 항목 | 문제 | 수정 |
|---|---|---|
| manifest 스코핑 | 미끼 값이 정답과 동일 → 항진명제 | 미끼를 오답으로 |
| 최상위 키 중복 | 미끼가 오답이라 중복검사 없이도 걸림 | 미끼를 정답 14항목으로 |
| repos 순서 무관 | reorder 정규식이 `\n` 인데 manifest 워킹카피가 CRLF → **매치 0회, 시험이 아예 안 돌았다** | `\r?\n` + 픽스처가 실제로 바뀌었는지 단언 |
| 루트 커밋 누락 | `--root` 없이도 부모-트리 검사가 잡아서 통과 | planning 전용 루트 픽스처 신설 |
| sentinel | "뭐라도 던지면 통과" → `assertFraming` 을 빼도 green | 발동한 검사까지 단언(`throwsMatching`) + 경로 RS 픽스처 |

경로에 `0x1e` 가 든 커밋은 Windows 파일시스템도 `update-index --cacheinfo` 도 거부하지만
`hash-object` + **`mktree`** 로는 만들어진다 — 패스 B 프레이밍 단언의 실증 경로를 그렇게 확보했다.

**되돌림 매트릭스** — 수정 하나를 되돌리면 selftest 가 정말 red 가 되는지 기계로 확인:

```
framingA · framingB · root · treeproof · noparent · blockscope · excscope
dupkey · reposorder · reposcope · followren · fullhistA · subset   → 전건 red
```

판별 테스트가 **없는** 것 2건, 소스에 명시:
- 패스 A 레코드 수 대조 — sentinel 축은 프레이밍 단언이 이미 잡는다. 파서 루프 버그 대비 보험
- git `-c` 5종 — 적대적 설정 픽스처는 통과하지만 각 설정이 실효했음을 증명하지 못한다
  (서명 커밋 없음 · `-z` 가 quotePath 무의미화 · `-M50%` 가 diff.renames 를 덮음 · rename 1건은
  어떤 renameLimit 도 안 넘음 · 이 환경 git 이 logOutputEncoding 변환을 안 함)

selftest 74 → **84 케이스**. 산출물 3개는 여전히 무변경.

### 3라운드 후 미해소 — 사람 판단 필요

1. **머신 간 결정성 미증명** (B 가 3라운드 내내 FAIL 유지). git 설정은 `-c` 5종으로 고정했고
   적대적 설정 픽스처로 실효를 증명했지만, `-M50%` rename 점수는 git **버전**의 휴리스틱에
   달려 있다. 검증 git 은 2.55.0 하나뿐이다. 다른 버전에서 같은 바이트가 나온다는 증명은 없다.
2. **F-10-03 trailer 독법** — B 는 결함으로 셌다. 이 PRD 는 규약을 바꿀 권한이 없어 findings 로
   올렸다(589 vs 614). `00-protocol` 판단 전까지 열려 있다.
3. **라운드 3 수정은 리뷰를 받지 못했다** — 루프가 3라운드에서 멈춘다. `7216ba1` 은 미검증이다.
4. B 의 기준 11(no regressions) FAIL 3회는 전부 *"제공된 자료로는 검증 불가"* 였다 — B 에게
   diff 를 주지 않은 프롬프트 설계 탓이지 코드 결함이 아니다. 저장소 접근이 있는 A 가 PASS 판정.

## Next Steps

- [ ] `00-protocol` 에 findings 2건 보고 (F-10-01 exclude 층위 · F-10-02 히스토리 단순화)
- [ ] 마일스톤 2 — `comments.jsonl` ×3 (`exclude_glob` 적용)
- [ ] `/mccp:pr` 또는 `/mccp:code-review`
