# V3.6 Control Plane — P1-B Finish-task Profile Contract 설계 초안 (★ 회장 야간 위임 2026-05-29 · 설계만 · 구현 dispatch 금지)

- 작성: 2026-05-29 02:30 KST · ANU chair-facing session night delegation
- 분류: **read-only design draft** (★ 회장 verbatim 금지 5: finish-task.sh 수정 / 코드 수정 / dispatch / PR·branch push·merge / task-2707 자동 발의)
- 상위: [[v36_runtime_harness_control_plane]] layer 5
- 입력: [[anu_operations_layer_incidents_8_post_task2703_260528]] #6 (`SEPARATE_VERIFICATION_REQUIRED`) + task-2703 EXTERNAL_DIRTY_BLOCKER + G4 soft warning incident
- 회장 verbatim 야간 위임 [2/3]·[3/3] 강제 10항목 포함

---

## 0. 목표 (★ 회장 verbatim)

> "finish-task.sh가 code/system_hook/local_runtime/read_only/callback_only/closeout_marker_only를 구분하지 못해서 task-2703/2704에서 꼬였던 문제를 설계로 정리한다."

본 contract 는 finish-task.sh **직접 수정 없이** task_mode 별 PASS/WARN/FAIL 기준을 외부 contract layer 로 분리한다.

---

## 1. task_mode enum 6 (★ 회장 verbatim 박제)

```
code                      — 일반 코드 변경 (PR/branch/머지 path · 가장 strict)
system_hook               — .claude/hooks/ · settings.json · runtime hook 등 (★ task-2703 본 분류)
local_runtime             — workspace local runtime (dispatch wrapper · watchdog config · taskctl · cron)
read_only                 — 진단/audit/report 만 (mutating 0 · 산출물 = md/json 마커)
callback_only             — ANU callback 발사 전용 (envelope only · 산출물 0 또는 envelope sha)
closeout_marker_only      — 회장 결정 박제 / closeout 운영 마커 작성 (.escalate.acked · decision-closeout)
```

### 1.1 task_mode 자동 분류 알고리즘 (★ inference contract)

task md 의 다음 signal 로 자동 분류:

- `--type coding` + expected_files 에 `.py/.ts/.tsx/.go/.rs/.java/.html/.css` 등 production 코드 path + PR 생성 명시 → `code`
- expected_files 에 `.claude/hooks/` · `settings.json` · `PreToolUse` 키워드 → `system_hook`
- expected_files 에 `dispatch.py` · `session-watchdog.sh` · `taskctl` · cron · runner → `local_runtime`
- task md 본문에 "read-only" · "read_only" · "diagnosis" · "audit" + git diff 0 expected → `read_only`
- task md 본문에 "ANU callback only" · envelope-only · "산출물 0 또는 envelope" → `callback_only`
- expected_files 에 `memory/events/<task_id>.escalate*` · `.done.acked` · `.cancelled.acked` · `decision-closeout-*` → `closeout_marker_only`

★ 다중 signal 충돌 시 우선순위: `code` > `system_hook` > `local_runtime` > `closeout_marker_only` > `callback_only` > `read_only` > `UNKNOWN`
★ 불명확 시 `UNKNOWN` → 회장 명시 (★ 단정 금지 doctrine 보존)

---

## 2. 각 mode 별 PASS/WARN/FAIL 기준 (★ 회장 verbatim 강제 8 게이트)

본 표는 가독성 목적. 실제 contract schema 는 JSON 화 권장.

### 2.1 `git_evidence` 적용 기준

| task_mode | PASS | WARN | FAIL |
|---|---|---|---|
| code | working tree change + commit author audit + git log main..HEAD diff | commit message actor prefix 미적용 | git diff 0 또는 actor verifiable 0 |
| system_hook | .claude/hooks/ + settings.json 결선 trace + DENY smoke evidence | hook config diff 있으나 production load trace 부재 | hook file 존재하나 settings.json wiring 0 |
| local_runtime | workspace local file change + 결선 evidence | runtime restart smoke 부재 | runtime path 변경 없이 보고만 |
| read_only | **git diff 0** (mutating 0) | git diff 0 이나 memory/events 마커 변동 있음 (★ 정상) | git diff 0 아님 (★ 우발 변경 의심) |
| callback_only | ANU-key cron registration + envelope sha 박제 | cron 등록은 됐으나 envelope sha 누락 | cron 등록 0 또는 self-key 등록 (SELF_COLLECTOR_FORBIDDEN) |
| closeout_marker_only | marker JSON schema valid + chair_authorization linkage | marker schema 부분 충족 | marker 없음 또는 schema fail |

### 2.2 `L1 smoke` 적용 기준 (★ "API curl 또는 Playwright" 기존 강제와 차별화)

| task_mode | PASS | WARN | FAIL | NOT_APPLICABLE |
|---|---|---|---|---|
| code | 서버 재시작 + API curl 또는 Playwright | smoke fixture 만 PASS · 실 서버 trace 부재 | smoke 0 또는 API/Playwright 실패 | — |
| system_hook | hook 실제 trigger DENY/HOLD trace (★ task-2703 본 case) | hook 등록만 + trigger trace 부재 | hook 미작동 trace | — |
| local_runtime | runtime restart + 기능 smoke (예: dispatch 재실행 + spawn 검증) | restart 없이 unit test 만 | restart smoke 실패 | — |
| read_only | — | — | — | **해당없음** (★ 산출물 mutating 0 · L1 smoke 자체가 의미 없음) |
| callback_only | callback fire 후 ANU collector spawn 확인 | cron 등록 + spawn 부재 | callback 미수신 | — |
| closeout_marker_only | — | — | — | **해당없음** (★ 마커 작성만 · 실 서버 영향 0) |

### 2.3 `scope guard` 적용 기준

| task_mode | PASS | WARN | FAIL |
|---|---|---|---|
| code | main..HEAD diff 가 expected_files 범위 내 + per-task scope-guard.sh | scope 일부 외부 · 회장 명시 인가 | scope 외 파일 변경 + 인가 0 |
| system_hook | .claude/ + harness/ + allowed_resources 명시 scope | settings.json 결선 외 추가 변경 (★ 조건부 허용 분기) | finish-task.sh 변경 또는 expected_files 외 변경 |
| local_runtime | workspace local file scope + allowed_resources 명시 | runtime 결선 외 utility 변경 (★ 정합 확인 필요) | 본 task 범위 외 파일 변경 |
| read_only | git diff 0 = automatic PASS | — | git diff 0 아님 |
| callback_only | envelope file 1건만 또는 cron 등록 trace | envelope + 추가 marker | scope 외 변경 |
| closeout_marker_only | memory/events/ + memory/reports/ scope | scope 일부 외부 | scope 외 또는 task_id 외 마커 변경 |

---

## 3. dirty workspace 처리 기준 (★ task-2703 EXTERNAL_DIRTY_BLOCKER 직접 후속)

### 3.1 dirty 분류 enum

```
OWN_DIRTY                 — 본 task 가 만든 변경 (★ scope 내 정상)
EXTERNAL_DIRTY            — 본 task 외 누적 dirty (★ 다른 데몬/태스크 origin)
DAEMON_RECURRENCE         — 데몬 자동 재기록 패턴 (★ task-2701 박제 패턴)
INHERITED_DIRTY           — 직전 task 의 stash 미해소 잔여
UNKNOWN_DIRTY             — 분류 불가
```

### 3.2 task_mode 별 dirty 처리 매트릭스

| task_mode | OWN_DIRTY | EXTERNAL_DIRTY | DAEMON_RECURRENCE |
|---|---|---|---|
| code | scope guard 검증 후 commit/push | 별도 stash + chair 보고 | task-2701 패턴 박제 + 별도 task |
| system_hook | scope 검증 후 commit | stash + chair 보고 | 동상 |
| local_runtime | scope 검증 후 commit | stash + chair 보고 | 동상 |
| read_only | OWN_DIRTY 발생 자체가 FAIL | 무시 가능 (read_only doctrine 정합) | 무시 |
| callback_only | OWN_DIRTY = envelope 만 PASS | 무시 | 무시 |
| closeout_marker_only | marker scope 내 OWN_DIRTY 만 PASS | 무시 | 무시 |

### 3.3 EXTERNAL_DIRTY_BLOCKER fail-closed 분기

- task-2703 시 외부 dirty 가 본 task 의 finish-task git gate 차단
- 본 contract 적용 시 EXTERNAL_DIRTY 인식 → `task_mode` 가 `code`/`system_hook`/`local_runtime` 일 때만 blocker 발동 (★ read_only/callback_only/closeout_marker_only 는 PASS 유지)
- EXTERNAL_DIRTY_BLOCKER 시 finish-task 정지 + chair 보고

---

## 4. `.done` / `.done.escalated` / `external-dirty-blocker` / `g4-soft-warning` 관계

### 4.1 마커 lifecycle

```
[정상 path]
WORK_CLOSEOUT_STARTED → FINISH_IN_PROGRESS → .qc-done → .scope-guard-done →
  .callback-cause → .callback-launch → .completion.txt → .done → .done.notified → .anu-notified
  
[abnormal path 1: external dirty]
WORK_CLOSEOUT_STARTED → FINISH_IN_PROGRESS → .external-dirty-blocker.json → 
  (task_mode 에 따라) .done blocked 또는 .done generated with WARN

[abnormal path 2: g4 soft warning]
WORK_CLOSEOUT_STARTED → FINISH_IN_PROGRESS → .g4-soft-warning.json → 
  .done generated (★ G4 soft warning = blocker 아님 · WARN 으로 분류)

[abnormal path 3: g4 hard escalate]
WORK_CLOSEOUT_STARTED → FINISH_IN_PROGRESS → .g4-failed → 
  .done.escalated (★ blocker 활성 · chair 결정 필요)

[task-2705 HOLD path]
WORK_CLOSEOUT_STARTED → 봇 자체 HOLD_FOR_CHAIR → .task-md-sha-decision.json (HOLD) →
  보고서 → .done 없음 → timer running 유지 (★ 회장 closeout 결정 대기)
```

### 4.2 마커별 의미 contract

- `.done` = finish-task.sh 정상 종결 (모든 게이트 PASS · task_mode 별 기준 정합)
- `.done.notified` = .done 후 notification 발사 (zero-byte 또는 trace)
- `.done.escalated` = G4 hard fail · chair 결정 필요 (★ task_mode 별 분류 가능)
- `.external-dirty-blocker.json` = EXTERNAL_DIRTY 발견 trace (★ task_mode 분기로 PASS/WARN/FAIL 결정)
- `.g4-soft-warning.json` = G4 soft warning (★ blocker 아님 · WARN)
- `.anu-notified` = ANU 세션 인지 trace (32 byte zero placeholder · ANU 회수 신호)
- `.task-md-sha-decision.json` = task-2705+1 P1-A contract 의 ALLOW/HOLD/DENY 결정 (★ 본 task 범위 아님 · P1-A 영역)

### 4.3 task_mode 별 마커 발생 정합성

| task_mode | .done 필수? | .done.escalated 정합? | external-dirty 정합? | g4-soft 정합? |
|---|---|---|---|---|
| code | YES | YES | YES (blocker 활성) | YES |
| system_hook | YES | YES | YES (blocker 활성) | YES |
| local_runtime | YES | YES | YES (blocker 활성) | YES |
| read_only | OPTIONAL (HOLD/INFO 분기 가능) | NO (★ read_only 는 escalate 0) | INFO only (blocker 0) | NO |
| callback_only | OPTIONAL | NO | INFO only | NO |
| closeout_marker_only | YES (closeout marker 자체 = .done 동등) | NO | INFO only | NO |

---

## 5. task-2703 / task-2704 incident 연결

### 5.1 task-2703 incident 연결

- task_mode = `system_hook` (★ PreToolUse hook + .claude/settings.json + harness/v36/ 신규)
- `.external-dirty-blocker.json` 14:06 발생 → 본 contract 적용 시 `system_hook` task_mode 의 EXTERNAL_DIRTY_BLOCKER 분기 = **blocker 활성** (★ 정상 동작)
- `.g4-soft-warning.json` 14:09 발생 → WARN 분기 (★ blocker 아님)
- L1 smoke = hook 실제 trigger DENY/HOLD trace (★ "API curl 또는 Playwright" 아님 · system_hook 분기 정합)
- 본 contract 가 task-2703 의 종결 path 를 `system_hook` 분기로 자동 정합

### 5.2 task-2704 incident 연결

- task_mode = `system_hook` + `local_runtime` 복합 (★ dispatch.py 결선 + session-watchdog.sh 결선 + scripts/harness/v36/ 신규)
- 동일 분기 적용
- 본 contract 가 multi-mode 분기 시 우선순위 적용 (system_hook > local_runtime 또는 합집합 mode)

### 5.3 task-2705 incident 연결

- task_mode = `system_hook` + `local_runtime` (★ dispatch.py 결선 + harness/v36/)
- 봇이 자체 HOLD_FOR_CHAIR → finish-task 미실행 → .done 없음 + timer running
- 본 contract 적용 시 HOLD path 도 분기 인지 가능 (★ §4.1 task-2705 HOLD path)

---

## 6. finish-task.sh 직접 수정 없이 contract 분리 전략 (★ 회장 verbatim 강제)

### 6.1 외부 contract layer 분리 원칙

- `finish-task.sh` 자체 = **NO TOUCH** (★ 회장 verbatim)
- 외부 contract = `scripts/harness/v36/finish_task_profile_classifier.py` (★ 신규 모듈) — finish-task 호출 시점 또는 마커 분석 시점에 read-only 분기 판정
- finish-task.sh 가 자체 분기 추가 없이도 외부 contract layer 가 task_mode 분류 + PASS/WARN/FAIL 판정 + 마커 작성

### 6.2 결선 위치 (★ finish-task.sh 외부)

- **위치 A — task md 분석 시점**: dispatch.py 또는 별도 hook 이 task md 를 분류 → `task_mode` marker 사전 작성 (`memory/events/<task_id>.task-mode-classified.json`)
- **위치 B — 마커 작성 시점**: finish-task.sh 가 만든 마커 (`.qc-done`/`.scope-guard-done`/`.g4-*`/`.external-dirty-blocker`/`.done`)를 외부 분류기가 read-only 분석 후 task_mode 분기 판정 마커 추가
- **위치 C — 회수 시점**: ANU 가 closeout marker watcher 회수 시 task_mode 별 분기로 ACCEPT/HOLD/DENY 자동 판정 (★ 본 contract 의 ANU 측 적용)

★ 권장: **A + C** 통합 (★ dispatch 시 사전 분류 + ANU 회수 시 자동 분기). finish-task.sh 0 touch.

### 6.3 safe-fail 강제 (★ task-2704 패턴 보존)

- 분류 실패 → `UNKNOWN` task_mode → 회장 명시 (★ 단정 금지 doctrine 정합)
- 마커 read 실패 → log only · finish-task 본동작 차단 0

---

## 7. 향후 구현 task 후보와 금지 범위 (★ ANU 자동 발의 금지 박제)

### 7.1 구현 task 후보 (★ 회장 결정 사항 · ANU 자동 발의 0)

- **후보 A — `task-2706` (★ 회장 verbatim "task-2706 자동 dispatch 금지" 박제 · ANU 자동 발의 0)**: P1-B finish-task profile contract layer 5 코드화 (★ 본 설계 초안의 정합 구현)
  - 신규 모듈: `scripts/harness/v36/finish_task_profile_classifier.py` (★ task_mode 자동 분류)
  - 신규 모듈: `scripts/harness/v36/finish_task_profile_judge.py` (★ task_mode 별 PASS/WARN/FAIL 결정 매트릭스)
  - tests/harness/ 신규 (★ 6 task_mode 각 fixture)
  - dispatch.py 최소 결선 (★ 사전 분류 마커 작성)
  - ANU 측 회수 시 자동 분기 결선 (★ 본 contract 활성 시 task-2703/2704/2705 모든 후속 task 가 정합)
- **후보 B — 향후 finish-task.sh refactor (★ chair 명시 인가 필요)**: 본 contract 가 안정화된 후 finish-task.sh 가 외부 contract 호출하도록 minimal 결선 (★ 회장 별도 결정 사항)
- **후보 C — `EXTERNAL_DIRTY_BLOCKER` 자동 정리 (★ 별도 task)**: dirty 분류 enum + 데몬 추적 (★ task-2701 패턴 연계)

### 7.2 절대 금지 (★ 회장 야간 위임 + 일반 doctrine 박제)

- finish-task.sh 직접 수정 0 (★ 회장 verbatim)
- 코드 수정 0 · dispatch 0 · PR/branch push/merge 0 · task-2707 자동 발의 0
- ANU 본체 직접 구현 0
- task_mode 우선순위 ANU 임의 변경 0
- 회장 verbatim 표현 ('Hermes 직접 코딩 0' 단정 금지) 보존
- 본 설계 초안을 "확정 spec" 으로 단정 0 (★ draft 상태 보존)

---

## 8. 본 설계 초안의 단일 효력

- 본 문서 = **설계 초안만** · 구현/검증 모두 회장 별도 결정 후
- task-2706 자동 발의 0 (★ 회장 verbatim)
- 다음 작업 방향 = 회장 verbatim 없이 ANU 단정 0

## 9. ANU 자기 한계 (★ 정직)

- task_mode 자동 분류 알고리즘 (§1.1) 은 read-only 추정 — 실 구현 시 추가 검증 필요
- PASS/WARN/FAIL 기준 매트릭스 (§2) 는 본 설계 초안의 가설 · 실 구현 시 fixture validation 필요
- dirty 분류 enum (§3.1) 은 task-2701 패턴 + task-2703 incident 기반 추정 · 다른 패턴 발견 시 enum 확장 필요
- task-2703/2704/2705 incident 연결 (§5) 은 read-only 사후 분석 · 실 contract 적용 시 후속 시나리오 검증 필요

★ 회장 야간 위임 2026-05-29 P1-B 설계 초안. 6 task_mode + 3 게이트 매트릭스 + dirty 5 enum + 마커 lifecycle + 결선 위치 A+C 권장 · finish-task.sh 0 touch. 구현 dispatch 0 · task-2707 자동 발의 0 · "설계 확정" 단정 0.

끝
