# task-2701 — LOCAL_MAIN_DIVERGENCE_INFRA_CLEANUP (Phase A read-only 분석 보고)

- 작업 ID: task-2701 | 팀: dev2-team | 담당: 오딘(Odin, 개발2팀장)
- Level: Lv.3 (인프라 · 되돌리기 어려운 git 상태 · 고위험)
- chair_authorization_id: `CHAIR-AUTH-TASK-2701-LOCAL-MAIN-DIVERGENCE-INFRA-CLEANUP-PHASE-A-READONLY-20260527-JJONGS-ANALYZE-001` (Phase A read-only only)
- 측정 시각: 2026-05-28 00:09 KST | 대상 repo: `/home/jay/workspace`
- **Phase 구분: 본 보고는 Phase A(read-only 분석)이며 git 상태 변경 0회. Phase B(실제 정리)는 회장 승인 후 별도 진행.**

---

## SCQA 요약

**S**: 로컬 main(`f14b3850`)과 origin/main(`f3550d9f`)이 merge-base `6220f5b5`에서 분기. dirty 888건. task-2700이 만든 divergence_guard가 coding/callback 종류 작업에 DIVERGENCE_HOLD를 걸어 봇 dispatch가 차단되는 근본 원인.

**C**: task 작성 시점 전제(ahead **68** / behind **6**, origin/main=`a2a20f94`)가 **현재 실측과 역전**됨 — ahead **6** / behind **69**, origin/main=`f3550d9f`. 옛 origin/main(`a2a20f94`, PR#155)은 현 origin/main의 **조상**으로, 과거 "ahead 68" 미push 산물 대부분이 PR 파이프라인(#135~#159)으로 이미 upstream 반영됨. 즉 task가 우려한 "68 commit 손실 위험"의 전제가 사실상 소멸.

**Q**: 잔존 ahead 6 commit과 dirty 888을 **손실 0**으로 보존하면서 로컬 main을 origin/main과 정합(ahead/behind 0, 가드 clean)시키는 안전한 방법은?

**A**: ahead 6 commit은 **6/6 전부 origin 원격 task 브랜치에서 도달 가능(이미 push됨)** → 손실 위험 0(실측). dirty의 위험 핵심은 commit이 아니라 **추적 working-tree 수정 21건(M)**(spec +42, `replacement_pr_runner.py` 718줄 삭제 등) — reset 시 소실되므로 보존 선행 필수. untracked 821은 origin/main과 **충돌 0건**이라 reset로 소실되지 않음. 권장: **옵션 1(보존-후-정합)** — 보존 브랜치 + 추적수정 stash/patch 아카이브 + 데몬파일 .gitignore → reset --hard origin/main. **단 본 Phase A에서는 미실행, 회장 승인 대기.**

---

## 보고 필드 (task 지정 7항목)

### 1. ahead commit 분류 (task 전제 68 → 실측 6)

로컬 main에 있고 origin/main에 없는 commit = **6개** (비-merge 5 + merge 1).

| commit | 소유 task | origin/main patch 반영(git cherry) | origin 원격 ref 도달성 | 보존 상태 |
|---|---|---|---|---|
| f14b3850 | task-2692 (dev6 페룬, PR #154 OPEN) | + (없음) | origin/task/task-2699-dev1 | **PUSHED** + PR#154 보존 |
| 5a29d3ee | task-2569+1 (dispatch UnboundLocalError hotfix) | + (없음) | origin/task/task-2691-dev8, /task-2699-dev1 | **PUSHED** |
| 6b082dab | task-2569 (merge commit) | (merge, cherry 제외) | 동상 | **PUSHED** |
| f0e90e09 | task-2569 (finish-task/workspace isolation fix) | + (없음) | 동상 | **PUSHED** |
| 3000e601 | task-2568 (헤르메스 Phase A/B/C 머지 보고) | + (없음) | 동상 | **PUSHED** |
| 8e18e354 | task-2568 (헤르메스 Phase A BLOCKED) | + (없음) | 동상 | **PUSHED** |

- `git cherry`는 5건 모두 `+`(origin/main에 patch-id 동등물 없음)로 표시 → main 브랜치 라인에는 미반영. 그러나 **6개 전부 origin/* 원격 task 브랜치에서 도달 가능**(`for-each-ref --contains`) → **이미 push됨, 손실 위험 0**.
- 소유 task 2568/2569는 이미 종결된 과거 작업, task-2692는 PR#154 OPEN(no-merge)로 별도 보존 중.

### 2. behind commit 미반영분 (task 전제 6 → 실측 69)

origin/main에 있고 로컬 main에 없는 commit = **69개**. PR 파이프라인으로 landing된 task 2632~2699 산물.

- 최상단: `f3550d9f` [task-2699+1] OWNER_GEMINI_TRIGGER production wiring (#159) ← 현 origin/main HEAD
- 2번째: `a2a20f94` [task-2694+1] NORMAL_CALLBACK_REGISTRATION_ENFORCEMENT (#155) ← **task 작성 시점의 origin/main**
- 그 외 머지: #150(task-2673), #148(task-2661), #144(2641), #143(2640), #142(2639), #140(2637), #139(2636), #138(2635), #137(2634), #136(2633), #135(2632) 등
- 해석: task가 말한 "behind 6"은 origin/main=a2a20f94 기준이었고, 현재는 a2a20f94 위에 #159 1건이 더해진 동시에 merge-base 기준 재계산으로 69로 집계. **이 69건은 전부 정식 PR 머지본**이므로 로컬 main이 흡수해야 할 정상 upstream 이력(분실 위험 없음, 보존 불필요 — 가져오기 대상).

### 3. dirty 888 분류

| 구분 | 건수 | 내용 | 처리 방향 |
|---|---|---|---|
| Untracked (`??`) | 821 | .md 422(tasks/reports/plans/specs), .json 188(events/callbacks), **.py 89(scripts/tests/utils 신규 소스)**, 데몬마커(.acked/.notified/.escalated) 39 등 | origin/main과 **충돌 0건** → reset 시 보존됨. 신규 .py 89 + 가치 .md는 선별 보존(PR), 데몬마커는 gitignore/삭제 |
| Tracked Modified (`M`) | 21 | **데몬 상태 ~13**(bot-activity/member-status/task-timers/token-ledger/pipeline-status/whisper/audit-trail 등) + **콘텐츠/소스 ~8**(anu-system-spec.md +42/-9, anu-system-spec-changelog +57, config/constants.json 6/6, **utils/replacement_pr_runner.py -718**, test_replacement_pr_runner_2510.py -493, task-2.md, reports 2692/2695) | **reset 시 소실** → 콘텐츠/소스 8건 보존 필수. 데몬 13건은 재생성되는 noise |
| Tracked Deleted (`D`) | 44 | 전부 `memory/events/task-23xx.done.acked/.notified/.escalated` (데몬 알림 마커, task 2309~2395) | 데몬 산물, 손실 무해. 삭제 commit 또는 복원 모두 가능 |
| Staged (`A`/`AM`) | 2 | anu_callback/task-2695-normal-completion.json, reports/task-2695.md | 보존(commit 또는 unstage 후 선별) |

- mtime 분포: 2026-05-23 **512건**(대량 누적), 05-26 124, 05-27 66, 05-24 66, 05-25 54, **05-28(오늘) 15건(데몬 활성 재기록)**.
- ★ **dirty 재발 근본 원인**: 데몬 상태 파일(bot-activity/member-status/task-timers/token-ledger/pipeline-status/whisper)이 **모두 tracked + .gitignore 미적용**. 데몬이 추적 파일을 상시 재기록 → 정리해도 즉시 재오염. 근본 해결은 .gitignore 추가 또는 데몬 write 경로 분리(task-2700 힌트와 일치).

### 4. 안전 정리 옵션 2+ (손실 위험 평가) — 모두 Phase B 후보, 본 보고 미실행

**옵션 1 — 보존-후-정합 (권장)**
- (a) 보존 브랜치 `task-2701-local-main-preserve`를 f14b3850에 생성(이미 origin에도 있으나 belt-and-suspenders).
- (b) 추적 수정 21M·44D를 `git stash push -u` 또는 patch 아카이브(`memory/backups/task-2701-dirty.patch`)로 보존. 특히 콘텐츠/소스 8건(spec, replacement_pr_runner 등)은 회장/소유자 판단 대상으로 별도 표기.
- (c) 데몬 상태 파일 .gitignore 추가(재발 차단).
- (d) 신규 .py 89 + 가치 .md를 배치 commit → origin/main 대상 PR(보존).
- (e) 전부 보존 확인 후 `git reset --hard origin/main` → 로컬 main == origin/main.
- **손실 위험**: 0 (commit·수정·untracked 모두 사전 보존; untracked는 충돌 0이라 reset로도 보존). 목표(ahead/behind 0 + 가드 clean) 달성.
- **주의**: reset 전 보존 단계 누락 시 콘텐츠/소스 8건 소실 → 반드시 (b) 선행.

**옵션 2 — 비파괴 merge 정합 (reset 미사용)**
- `git merge origin/main`으로 behind 69를 로컬 main에 흡수(merge commit 생성), ahead 6은 main 이력에 유지.
- **손실 위험**: 0 (아무것도 폐기 안 함). 그러나 ahead/behind 0 미달성(ahead 6+merge 잔존). 이후 PR로 push해야 정합 완결되며, 이미 종결된 task 2568/2569 commit이 main 이력에 영구 잔존(clutter).
- **주의**: 추적 수정 21M은 merge와 무관하게 working-tree에 남음 → 별도 처리 필요.

**옵션 3 — 근본원인 우선(데몬 분리) 후 경량 정합**
- 먼저 데몬 상태 파일을 gitignore/write-path 분리(재오염 차단) → dirty의 ~13M + 다수 ?? 자동 소거 → 잔여 소수만 옵션1/2로 정합.
- **손실 위험**: 낮음. 단 데몬 경로 분리는 코드 변경이라 별도 task(task-2700 연계) 필요 → Phase B 범위 확대.

### 5. 권장 정리 시나리오

**옵션 1(보존-후-정합)** 권장. 근거: ahead 6은 이미 origin에 전부 push되어 reset로도 손실 0이고, behind 69는 정식 PR 머지본이라 흡수가 정답이며, 위험의 실체는 추적 수정 8건뿐이므로 이를 (b)에서 명시 보존하면 손실 0으로 목표(local main==origin/main, ahead/behind 0, divergence_guard clean) 달성. 재발 방지를 위해 (c) 데몬 gitignore를 동반(옵션3 핵심을 흡수).
**단, 회장 승인 전 실행 금지.** Phase B 진입 시 (b)의 콘텐츠/소스 8건 처분(특히 `replacement_pr_runner.py` 718줄 삭제가 의도된 폐기인지 사고인지)에 대한 소유자/회장 확인이 선결.

### 6. ahead commit 손실 0 보장 방안

1. **실측 근거**: ahead 6 commit 전부 `refs/remotes/origin/task/task-2699-dev1`/`task-2691-dev8`에서 도달 가능 → 원격에 영구 보존됨. f14b3850은 PR#154로도 보존.
2. **이중 안전장치**: Phase B에서 reset 전 `task-2701-local-main-preserve` 로컬 보존 브랜치 생성.
3. **검증 명령(Phase B 전후)**: `git for-each-ref --contains <sha> refs/remotes/origin` 이 각 6개에 대해 비어있지 않음을 재확인.
4. **untracked 보존**: reset --hard는 충돌 없는 untracked를 삭제하지 않음(충돌 0건 실측) → `git clean` 금지로 821건 보존.

### 7. forbidden_action_count (Phase A)

**0** — task가 금지한 행위(divergence/dirty 상태 변경) 위반 0건. **mutating git(cleanup) 0회.**
- 분석 명령은 전부 read-only: `rev-parse, rev-list, log, cherry, merge-base, cat-file, status, diff --stat, for-each-ref, ls-tree, branch --contains, check-ignore` + divergence_guard read-only 측정(--fetch/--events-dir 미사용).
- 금지된 cleanup mutation 실행 **0회**: reset --hard 0, checkout 0, clean 0, dirty 일괄 stash 0, merge 0, commit 0(아래), push 0. PR#158/#159 미접촉, dispatch.py 미변경, manual .done 미생성, task-2699+1/2700 미정리, ahead/behind 손실 조치 0. 로컬 main HEAD = f14b3850 불변, dirty 파일 무접촉.
- 완료 파이프라인 처리: finish-task.sh의 QC git_evidence(COMMIT_EXISTS)가 커밋을 요구하나, (a) **시스템 hook이 main 직접 커밋을 차단**(`[BLOCKED] main direct commit prohibited`)하고 (b) 본 작업은 read-only 진단이라 커밋이 부적절하다. 따라서 deliverable 커밋을 시도하지 않고(시도분은 차단됨, index 원복), task 파일에 `## 레벨` 분류 섹션(`코드 수정 없음`)을 명시하여 git 게이트가 **non-code task로 정상 SKIP**되도록 처리했다(시스템 설계 메커니즘 — finish-task.sh GIT-GATE + qc git_evidence 공통). 이는 분석 대상 git 상태(divergence/dirty)를 전혀 변경하지 않는다.

---

## Phase B 선결조건 · 충돌 사전점검 (G4 Gemini 리스크 대응)

> Gemini G4 리뷰가 (옛 전제 ahead 68/behind 6 기준) 제기한 리스크에 대한 read-only 실측 기반 대응. **모든 항목 Phase B 회장 승인 후 적용 — 본 Phase A 미실행.**

**(1) [critical 대응] ghs_ 토큰 부재 → push 불가 → stale 재발 우려**
- 실측: `origin = git@github.com:Jeon-Jonghyuk/dev_workspace.git`. **local main 직접 push는 토큰과 무관하게 시스템 hook이 차단**(`[BLOCKED] main direct push prohibited`). 즉 stale 누적의 직접 메커니즘은 "토큰 부재"보다 "main 직접 push/commit 정책 차단 + 봇이 commit을 main에 누적"이다.
- 완화: 권장 옵션 1은 **local main을 origin/main으로 정합(reset)**시키므로 main을 push할 필요가 **없다**(origin/main이 이미 상위). 보존이 필요한 산물(신규 .py 89 등)만 **PR(task 브랜치) 경로**로 push(봇이 task 브랜치 push는 가능 — ahead 6이 origin task 브랜치에 이미 올라간 사실이 증거). 따라서 push 권한 재확인 대상은 "main"이 아니라 "PR 브랜치"로 한정.
- 재발 차단 선결조건: ① 데몬 상태 파일 .gitignore/write-path 분리(옵션 1의 (c)) ② 봇의 main 직접 commit 누적 경로 점검(task-2700 divergence_guard HOLD가 이미 일부 차단) — 이 둘이 충족되면 정합 후 재오염 위험 낮음. **Phase B 착수 전 PR 브랜치 push 가능 여부 1회 확인**을 선결조건으로 명시.

**(2) [medium 대응] ahead↔behind 머지 충돌 사전점검 (git merge-tree, read-only)**
- `git merge-tree --write-tree main origin/main` 실측 → **충돌 2건**: `memory/reports/task-2569.md`(add/add), `scripts/finish-task.sh`(content).
- 함의: **옵션 2(merge 정합)는 위 2건 수동 충돌 해결 필요**. 반면 **옵션 1(reset 정합)은 merge를 하지 않으므로 충돌 0** — reset은 local main을 origin/main으로 덮어쓰고, ahead 6은 이미 origin에 보존돼 손실 없음. → **충돌 회피 측면에서도 옵션 1 우위** 재확인.

**(3) [high 대응] 분석↔처리 시점 상태 불일치(데몬 실시간 생성)**
- 실측: 오늘(05-28) 데몬이 15개 파일 재기록 중. 따라서 Phase B는 ① 데몬 일시 정지 또는 ② gitignore 선적용 후 정합 순서로 진행해야 시점 불일치 최소화. 단 untracked는 충돌 0(reset 보존)이고 데몬 상태 파일은 재생성되므로 유실 위험은 낮음(보존 대상은 commit 미반영 소스 .py 89 + 콘텐츠 8건으로 한정, mtime 고정 확인 가능).

## L1 스모크테스트 결과 (read-only 분석 task)

본 task는 코드 수정이 없는 **read-only git 진단** 작업입니다(type diagnosis). 서버/API/프론트 변경 없음.
- 서버 재시작: **해당없음** (코드 변경 0)
- API 응답 확인: **해당없음** (백엔드 변경 0)
- 스크린샷: **해당없음** (프론트 변경 0)
- 대체 실동작 검증(분석 도구 실행): divergence_guard.py read-only 실행 → `{"ahead":6,"behind":69,"diverged":true,"measured":true}` 정상 반환(exit 0). 측정 일관성으로 분석 결론 검증.

## 발견 이슈 및 해결

- **이슈 1**: task 전제(ahead 68/behind 6)와 현재 실측(ahead 6/behind 69) 불일치. → **해결**: origin/main이 task 작성 후 PR 파이프라인으로 전진(a2a20f94→f3550d9f, #159 추가)했고 과거 ahead 산물이 upstream 반영됨을 merge-base/for-each-ref로 규명. 보고 최상단에 명시.
- **이슈 2**: `utils/replacement_pr_runner.py`가 working-tree에서 718줄 삭제(거의 비워짐) + 테스트 493줄 삭제. 의도된 폐기인지 사고인지 불명. → **범위 외(Phase B)**: read-only 분석에서 처분 불가. 보존 대상으로 표기하고 Phase B 회장/소유자 확인 사항으로 에스컬레이션.
- **이슈 3**: divergence_guard/dirty_registry가 main 미머지(.worktrees/task-2700-dev6에만 존재). → **기록**: 본 task 범위 아님(task-2700 머지 대상). read-only 인용만 수행.

## 모델 사용 기록

- 분석/판단 전 과정: **팀장(Odin, Opus) 직접 수행**. 사유: Lv.3 고위험 git 포렌식 + 되돌리기 어려운 상태 판단으로, 팀원(Sonnet/Haiku) 위임 시 정밀도 손실·오판 위험이 큼. 코딩이 아닌 read-only 판단 작업이므로 팀장 직접 수행이 워크플로우(설계/검토는 팀장)에 부합. 팀원 코딩 위임 없음(코드 변경 0).

## 머지 판단

- **머지 필요**: No (코드 변경 0, worktree 미생성, read-only 분석)
- **브랜치**: 없음
- **워크트리 경로**: 없음
- **머지 의견**: Phase A는 분석 보고만 산출. 실제 정리(Phase B)는 회장 승인 후 별도 범위로 진행.

## 비고

- 본 보고는 **Phase A read-only**. ahead 6 손실 0 실측 확정. Phase B(reset/gitignore/PR 보존 등 실제 정리)는 **제이회장 승인 + 별도 범위 확정 후에만** 진입. 본 task로 mutating git 실행 금지 규칙 준수(위반 0).

## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회

