# V3.6 Control Plane — P1-A Task MD SHA Contract 설계 초안 (★ 회장 verbatim 2026-05-28 · 설계만 · 구현 dispatch 금지)

- 작성: 2026-05-28 17:30 KST · ANU chair-facing session
- 분류: **read-only design draft** (구현 dispatch 0 · 코드 수정 0 · finish-task.sh 수정 0 · PR/branch push/merge 0)
- 상위: [[v36_runtime_harness_control_plane]] layer 0
- 입력: [[task-2703_operational_incidents_to_contract_map]] incident #5 + task-2704 `caveat_1_task_md_sha_mismatch`
- 회장 verbatim 강제 10항목 포함

---

## 1. task md sha mismatch 발생 지점 (★ 회장 verbatim #1)

### 1.1 task-2703 fact (★ 실증)

- ANU dispatch 직전 측정 sha = `2c7927cb…`
- 봇이 실제 task md 파일 read 시 sha = `428f2d05…`
- 봇 검증: `mismatch` 발견 → 내용 verbatim 비교 → 일치 확인 → 진행
- 그 시점 ANU `wc -c` = 6823 bytes (초기) → dispatch.py 후 8081 bytes (회장님 정정 분량)

### 1.2 task-2704 fact (★ 실증)

- ANU dispatch 직전 측정 sha = `629faeea…c211e77` (16,914 bytes)
- 봇이 실제 read 시 fact는 봇 보고서 sha mismatch 명시 부재 (★ 단정 금지)
- known caveat: ANU 기록 sha vs 실측 sha 불일치 fact 회장 확정

### 1.3 mismatch 발생 흐름 후보 (★ 추정 · 단정 금지)

- **흐름 A**: `dispatch/core.py::main()` 진입 후 `dispatch/__init__.py` 내 metadata patch (예: bot/role/model/task_file 메타데이터 추가)
- **흐름 B**: `dispatch.py` 가 `_patch_timer_metadata(task_id, ...)` 호출 시 task-timers.json 만 수정하는 게 정상이나 부수효과로 task md 도 변경되는 path
- **흐름 C**: linter/formatter hook (pre-commit · IDE) 이 dispatch 직후 task md 자동 정리
- **흐름 D**: 다른 watcher daemon 이 task md 를 normalize
- ★ 실제 흐름 특정 = read-only forensic 필요 · 본 설계 초안에서는 패턴 cover (어느 흐름이든 sha mismatch 발생 시 동일 contract 적용)

### 1.4 mismatch 지점 분류 enum

```
DISPATCH_ENTRY_TO_EXIT       — dispatch.py 진입 후 봇 spawn 전 사이 (★ 가장 흔함 · 메타데이터 patch)
DISPATCH_EXIT_TO_BOT_READ    — 봇 cron fire 후 봇이 read 하기 전 사이 (★ linter/formatter)
ANU_PRE_DISPATCH_TO_BOT_READ — ANU 측정 후 dispatch 전 사이 (★ ANU manual edit)
BOT_READ_TO_BOT_WORK         — 봇이 read 후 작업 진행 중 sha 변동 (★ 같은 봇 또는 다른 actor 가 작업 중 수정)
UNKNOWN                      — 위 4 중 어느 것도 명확히 특정 불가
```

## 2. 3 sha 구분 (★ 회장 verbatim #2)

| 명칭 | 측정 주체 | 측정 시점 | 목적 |
|---|---|---|---|
| **`dispatch_pre_sha`** | ANU 본체 또는 dispatch caller | dispatch.py 진입 **직전** | dispatch 의도된 task md 원문 |
| **`dispatch_post_sha`** | dispatch.py 내부 (자동 강제) | dispatch.py exit 직전 (★ metadata patch 모두 끝난 후) | 실제 봇에게 전달되는 task md |
| **`executor_observed_sha`** | 봇 (task md read 시) | 봇 cron fire 후 첫 read 직후 | 봇이 실제 작업 기준으로 삼는 task md |

→ 3 sha 모두 marker 에 박제. 비교: `dispatch_pre_sha` ↔ `dispatch_post_sha` = dispatch 내부 patch fact / `dispatch_post_sha` ↔ `executor_observed_sha` = dispatch ~ 봇 read 사이 변동 fact

★ 표는 가독성 목적. 실제 contract schema 는 JSON.

## 3. metadata patch 허용 범위 (★ 회장 verbatim #3)

### 3.1 허용 (★ semantic content 변경 0)

- task md frontmatter 또는 끝부분에 **별도 sidecar 영역**으로 dispatch metadata 추가 (예: `<!-- DISPATCH_META: task_id=task-NNN bot=bot-c role=dev2 ... -->`)
- L1879 retry task `retry_meta` 헤더 prepend (★ 기존 흐름 보존 — 회장 별도 결정 시 변경)
- whitespace normalization (trailing whitespace · final newline)

### 3.2 금지 (★ semantic content 변경)

- 본문 내용 변경 (회장 verbatim 표현 / 금지 조항 / DoD 등 의미 변경)
- frontmatter 의 chair_authorization_id / executor / allowed_resources 수정
- yaml 블록 수정

### 3.3 허용 patch type enum

```
DISPATCH_META_SIDECAR        — 별도 sidecar 영역 추가 (★ 기본 권장)
RETRY_HEADER_PREPEND         — retry 시 헤더 prepend (★ L1879 기존 흐름)
WHITESPACE_NORMALIZATION     — trailing ws / final newline
NO_PATCH                     — patch 없음 (sha 동일)
FORBIDDEN_SEMANTIC_CHANGE    — semantic 변경 (★ 발견 시 즉시 DENY)
```

## 4. content_verbatim_match 판정 방식 (★ 회장 verbatim #4)

### 4.1 정의

- `pre_sha != post_sha` 또는 `post_sha != observed_sha` 시 발동
- **두 task md 의 normalized content 비교** → semantic 일치 여부 판정

### 4.2 판정 알고리즘

1. 두 task md 를 read
2. **normalize**:
   - DISPATCH_META_SIDECAR 영역 제거 (★ HTML comment 패턴 매칭)
   - RETRY_HEADER prepend 제거 (★ 시작 패턴 매칭)
   - trailing whitespace 제거 + final newline normalize
3. normalize 후 양쪽이 byte-identical = `content_verbatim_match=true`
4. byte-identical 아님 = `content_verbatim_match=false`

### 4.3 판정 결과 enum

```
true        — normalize 후 byte-identical (★ semantic 일치)
false       — normalize 후 차이 (★ semantic 변경 의심)
unverifiable — 두 task md 중 하나 이상 read 실패 또는 비교 불가
```

## 5. continue_allowed enum (★ 회장 verbatim #5)

```
true   — 진행 허용 (★ patch type 이 허용 범위 + content_verbatim_match=true)
false  — 진행 금지 (★ FORBIDDEN_SEMANTIC_CHANGE + content_verbatim_match=false + HOLD/DENY 결정)
hold   — 회장 결정 대기 (★ unverifiable 또는 모호한 경우)
```

## 6. mismatch 시 ALLOW / HOLD / DENY 기준 (★ 회장 verbatim #6)

### 6.1 결정 매트릭스

- `patch_type=NO_PATCH` + `content_verbatim_match=true` → **ALLOW** (★ sha 동일 · 정상)
- `patch_type=DISPATCH_META_SIDECAR/RETRY_HEADER/WHITESPACE` + `content_verbatim_match=true` → **ALLOW** (★ semantic 일치 · 진행)
- `patch_type=unverifiable` 또는 `content_verbatim_match=unverifiable` → **HOLD_FOR_CHAIR**
- `patch_type=FORBIDDEN_SEMANTIC_CHANGE` 또는 `content_verbatim_match=false` → **DENY** (★ 봇 작업 중단 + 회장 결정 요청)

### 6.2 ALLOW 시 봇 동작

- 봇 prompt 에 `dispatch_post_sha` 전달 → 봇이 read 후 `executor_observed_sha` 측정 → 동일 시 정상 진행 / 불일치 시 본 contract 재발동

### 6.3 HOLD/DENY 시 봇 동작

- 즉시 `memory/events/<task_id>.task-md-sha-hold.json` 또는 `.task-md-sha-deny.json` 마커 생성
- ANU normal callback 발사 + 회장 보고 envelope
- 봇 작업 중단 (★ 추정 패치 금지 doctrine 일치)

## 7. marker schema (★ 회장 verbatim #7)

### 7.1 marker 파일명

- `memory/events/<task_id>.task-md-sha-decision.json` (★ ALLOW/HOLD/DENY 통합)

### 7.2 schema (필수 12 필드)

```json
{
  "schema_version": "v36.task_md_sha_decision.v1",
  "decision_id": "<task_id>.task-md-sha.<ISO8601 KST>",
  "task_id": "task-NNNN",
  "ts": "2026-MM-DDTHH:MM:SS+09:00",
  "shas": {
    "dispatch_pre_sha": "<sha256>",
    "dispatch_post_sha": "<sha256>",
    "executor_observed_sha": "<sha256 or null>"
  },
  "sizes": {
    "dispatch_pre_bytes": 0,
    "dispatch_post_bytes": 0,
    "executor_observed_bytes": 0
  },
  "mismatch_location": "DISPATCH_ENTRY_TO_EXIT | DISPATCH_EXIT_TO_BOT_READ | ANU_PRE_DISPATCH_TO_BOT_READ | BOT_READ_TO_BOT_WORK | UNKNOWN | NONE",
  "patch_type": "DISPATCH_META_SIDECAR | RETRY_HEADER_PREPEND | WHITESPACE_NORMALIZATION | NO_PATCH | FORBIDDEN_SEMANTIC_CHANGE",
  "content_verbatim_match": "true | false | unverifiable",
  "continue_allowed": "true | false | hold",
  "decision_class": "ALLOW | HOLD_FOR_CHAIR | DENY",
  "reason_code": "verbatim_match_metadata_patch_ok | verbatim_mismatch_block | expected_sha_outdated_resync | unverifiable_hold | semantic_change_deny",
  "chair_authorization_id": "<id or null>",
  "actor": {"who_measured_pre": "anu_body | dispatch_caller", "who_measured_post": "dispatch_py_auto", "who_measured_observed": "bot_executor"}
}
```

### 7.3 schema 통합

- 본 schema 는 [[v36_runtime_harness_decision.schema]] 의 `contract_layer=layer_0_task_md_sha` decision_class 와 통합

## 8. regression fixture 계획 (★ 회장 verbatim #8)

### 8.1 fixture 시나리오 (★ 각 결정 분기 ≥1 sample)

- **fixture-1 ALLOW NO_PATCH**: dispatch 전후 sha 동일 + 봇 read sha 동일 → ALLOW
- **fixture-2 ALLOW DISPATCH_META_SIDECAR**: sidecar 추가로 sha 변동 + content_verbatim_match=true → ALLOW
- **fixture-3 ALLOW RETRY_HEADER**: retry header prepend + content_verbatim_match=true → ALLOW
- **fixture-4 ALLOW WHITESPACE**: 끝 공백 normalize + content_verbatim_match=true → ALLOW
- **fixture-5 HOLD unverifiable**: task md read 실패 시뮬레이션 → HOLD_FOR_CHAIR
- **fixture-6 DENY semantic change**: 본문 의미 변경 시뮬레이션 → DENY
- **fixture-7 DENY FORBIDDEN_SEMANTIC_CHANGE**: chair_authorization_id 변경 시뮬레이션 → DENY
- **fixture-8 통합 e2e**: 가짜 task-fake-2705 발사 → pre/post/observed 3 sha 측정 → marker 생성 검증

### 8.2 fixture 경로 (★ 제안 · 별도 task 인가 시 적용)

- `tests/harness/test_v36_task_md_sha_contract.py`
- `tests/harness/fixtures/v36_task_md_sha_*.json` (각 fixture 의 input/expected_output)

### 8.3 회귀 PASS 기준

- 8 fixture 모두 expected decision_class 일치
- schema validator JSON Schema PASS
- 기존 task-2703/2704 회귀 (246/246 pytest) 유지 0 fail

## 9. dispatch.py / task md patch 흐름 중 결선 위치 (★ 회장 verbatim #9)

### 9.1 read-only 확인된 dispatch 흐름 (`dispatch/__init__.py`)

- **L800** `_patch_timer_metadata(task_id, **metadata)`: task-timers.json patch (★ task md 변경 0)
- **L1821~** `task_file = Path(WORKSPACE)/"memory"/"tasks"/f"{base_task_id}.md"`: task md 존재 확인
- **L1867~1879** retry task: `retry_meta` 헤더 prepend → task md `write_text` (★ patch 발생 지점)
- **L2837~2853** `--task` 옵션 시: `task_file.write_text(task_desc)` (★ ANU 가 `--task-file` 사용 시 적용 안 됨)
- **L2994** `task_md_sha_before=None` placeholder (★ task-2704 가 결선한 마커 필드)

### 9.2 결선 위치 후보 (★ 설계 권장)

- **위치 A — dispatch entry**: dispatch.py 진입 직후 `dispatch_pre_sha` 측정 + marker 시드
  - 장점: ANU 가 측정한 sha 와 dispatch.py 가 본 sha 가 동일한지 자동 검증
  - 단점: ANU 미측정 시 비교 대상 부재
- **위치 B — metadata patch 직전/직후**: `_patch_timer_metadata` 호출 직후 `dispatch_post_sha` 측정 + marker append
  - 장점: dispatch.py 내부 patch fact 자동 박제
  - 단점: 다른 patch 흐름 (retry header / write_text) cover 위해 다중 위치 결선 필요
- **위치 C — dispatch exit + bot prompt 작성**: 봇 prompt 에 `dispatch_post_sha` 포함 + 봇 read 시 `executor_observed_sha` 측정 trigger
  - 장점: 봇 측 자동 검증 시 즉시 mismatch 발견
  - 단점: 봇 prompt 변경 필요 (task-2703/2704 패턴 보존성 검토)
- **위치 D — task md write 직후 (모든 write 흐름)**: `task_file.write_text(...)` 호출 직후 일관 sha 측정
  - 장점: 모든 patch 흐름 cover
  - 단점: write 호출 다중 (L1879, L2837 등) → 결선 위치 다중 또는 wrapper 통일

★ 권장: **위치 A + B + C 통합** = dispatch entry/exit 둘 다 측정 + 봇 prompt 에 `dispatch_post_sha` 포함. 위치 D 는 wrapper 통일로 단일 측정 함수 hook

### 9.3 결선 safe-fail 강제 (★ task-2704 패턴 보존)

- task-2704 의 dispatch marker 결선 패턴 동일 적용: marker write 실패 시 dispatch 본동작 차단 0
- try/except + log only

## 10. forbidden scope (★ 회장 verbatim #10)

### 10.1 본 설계 초안 단계 금지 (회장 verbatim)

- **코드 수정 금지** (★ 설계만)
- **dispatch 금지** (★ 본 설계 → 회장 결정 후 별도 task 발의 시에만)
- **finish-task.sh 수정 금지** (★ task-2703/2704 패턴 보존)
- **PR/branch push/merge 금지**

### 10.2 미래 구현 task 단계 forbidden (★ 미리 박제)

- dispatch.py 기존 동작 변경 금지 (★ task-2704 패턴 보존 · safe-fail 강제)
- task md 본문 의미 변경 (★ semantic change) 자동 patch 금지
- chair_authorization_id 자동 수정 금지
- ANU 본체 직접 구현 금지 (★ executor 봇 + 검증 분리)
- ANU 자가검증만으로 PASS 처리 금지

### 10.3 본 설계 초안의 단일 효력

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

---

## ★ 본 설계 초안의 자기 한계 (★ 정직)

- mismatch 발생 흐름 (§1.3 A/B/C/D) 의 실제 root cause 는 read-only forensic 으로 특정 필요 — 본 설계는 패턴 cover 만
- L2994 `task_md_sha_before=None` placeholder 의 실제 의도 = task-2704 결선 시점 검토 필요 (★ 본 contract 가 fill in)
- §9 결선 위치 권장 (A+B+C 통합) 은 read-only 검토 기반 추정 — 실 구현 시 추가 검증 필요
- 회장 결정 외 ANU 가 본 설계를 "확정 spec" 으로 단정하지 않음 (★ 직전 사고 패턴 회피)

## ★ 다음 단계 (★ 보고만 · 자동 dispatch 금지)

- 회장 검토 + 결정 → 별도 task md 작성 + 별도 chair_authorization_id 발급 → 별도 발사
- 본 초안 자체 = task 발의 시드 아님 · 설계 문서

★ 회장 verbatim 2026-05-28 P1-A task md sha contract 설계 초안. 10항목 verbatim 박제. 구현 dispatch 0 · 코드 수정 0 · finish-task.sh 수정 0 · PR/branch push/merge 0 · 다음 작업 방향 ANU 단정 0.

끝
