# task-2645 보고서 — DISPATCH_SPAWN_VERIFICATION_AND_PROMPT_LIMIT_GATE

- 단일소스 task md sha256: `42dd1259dac9b2156ba64ff4ae8ffbbf7802466d50cf44bd4347b369248a432f`
- 사고 박제 단일소스: `memory/feedback_dispatch_false_ok_and_prompt_limit_misclassification_260524.md`
- executor 시작 ts: 2026-05-24 02:45:55 (server time, KST)
- executor 종료 ts: 2026-05-24 03:00 (KST, 본 보고서 작성 시각 기준)

## 회장 보고 10 필드 1:1

- **branch**: `task/task-2645-dev2` (origin/main 베이스 fresh worktree)
- **commit**: 로컬 commit `30b39ab5` (push 금지, local-only)
- **changed files count**: 23 file (modified 1 + new 22)
- **classifier implementation**: `utils/prompt_byte_classifier.py` — 회장 verbatim 4 구간 + 4096 absolute + `wc -c` 등가 측정 + >3900 path-only compact mode 자동 전환. JSON dump schema `dispatch.prompt_byte_classification.v1`.
- **spawn verification 4-item**: `utils/dispatch_spawn_verifier.py` — workspace dir / executor process or activity / schedule visibility-or-marker / schedule_history 3 상태 분리 (in-progress / 종료 / silent drop). Schema `dispatch.spawn_verification_result.v1`.
- **regression 17 item coverage**:
  1) classifier 4 구간 + absolute → `test_prompt_byte_classifier_2645.py::test_classify_byte_count_boundaries` (11 매개변수, 0/3200/3201/3499/3500/3900/3901/4000/4096/4097/6500)
  2-5) verdict labels (OK_TARGET/OK_ABOVE_TARGET/WARNING_BUT_ALLOWED/HARD_BLOCK) → 동일 test + `test_verdict_labels_are_chair_verbatim`
  6) 4096 absolute 상수 명시 → `test_policy_constants_match_chair_verbatim`
  7) >3900 path-only compact mode → `test_path_only_compact_mode_uses_task_md_path_and_sha`
  8) cron_response=ok 단독 final success 금지 → `test_false_ok_replay_dispatch_py_settles_to_silent_drop` + `test_false_dispatched_report_must_fail_assertion`
  9) DISPATCH_SUBMITTED_UNVERIFIED enum → `test_dispatch_status_enum_2645.py::test_state_constants_are_chair_verbatim`
  10) spawn verification gate → `test_dispatch_spawn_verifier_2645.py::test_verified_spawn_from_fixture_success`
  11) 4 항목 verification → `test_signals_dataclass_has_all_4_items` + `test_partial_signals_do_not_falsely_confirm_silent_drop` (3 매개변수)
  12) silent drop → DISPATCH_SILENT_DROP_HOLD → `test_silent_drop_confirmed_from_673aa5a6_fixture`
  13) dispatch.py vs 직접 cron 비교 → `test_dispatch_py_and_direct_cron_share_same_gate_logic`
  14) 3270 bytes MUST be allowed → `tests/regression/dispatch_gate/test_prompt_3270_allowed.py`
  15) 3901 bytes MUST be blocked → `tests/regression/dispatch_gate/test_prompt_3901_blocked.py`
  16) 4096+ silent drop fixture (673AA5A6) → `tests/regression/dispatch_gate/test_prompt_4096_silent_drop.py`
  17) false dispatched report fails → `test_false_dispatched_report_must_fail_assertion`
  **결과: 55/55 PASS** (1차 wc-c 등가 testcase 1건은 `json.dumps` escape 부작용 정정 후 통과)
- **DISPATCH_SUBMITTED_UNVERIFIED state**: `utils/dispatch_status_enum.py` 상수로 박제 + `dispatch/__init__.py` 의 두 dispatched 반환 dict (line 2973 composite, line 3997 일반) 에 `dispatch_state=DISPATCH_SUBMITTED_UNVERIFIED` + `requires_spawn_verification=True` + `spawn_verification=None` 4 키 삽입. ANCHOR-1 주석 명기.
- **DISPATCH_SILENT_DROP_HOLD state**: `utils.dispatch_spawn_verifier.evaluate_signals` 가 4신호 (workspace 빈 채 + history log 부재 + executor process 0 + cron 비가시·marker 0) 모두 hit 시 자동 전이. 부분 hit 은 SUBMITTED_UNVERIFIED 재폴링. 회장 verbatim "단독 log 미존재 단정 금지" doctrine 코드화 (`_classify_history_state`).
- **live infra modified**: **0** — `/home/jay/.claude/settings.json` / `/home/jay/.claude/settings.local.json` / `/usr/local/bin/cokacdir` / `.github/**` / `hooks/**` / `memory/events/*.cron-*` / `memory/tasks/task-2644*` / `memory/specs/system_anu_callback_collector_control_plane_spec_260524.md` / `memory/specs/system_real_merge_executor_wiring_spec_260523.md` / `scripts/finish-task.sh` / `utils/replacement_pr_runner.py` 모두 미수정.
- **forbidden action count**: **0** — task-2644 혼합 / live settings / live cokacdir / ANU 직접 CI/Gemini polling / BOT App token / chair_authorization / real auto-merge / PR #141 pilot / merge execution 0 회 시도.

## 발견 이슈 및 해결

- `wc -c` 등가 측정 testcase 가 `json.dumps()` escape 로 한글 multibyte 문자열 인코딩에서 byte 수 불일치 (py=54 vs wc=75). bash heredoc 우회 대신 임시 파일에 raw UTF-8 bytes 를 기록한 뒤 `wc -c < file` 로 측정하는 방식으로 정정. 의미는 동등 (`len(s.encode("utf-8"))` == raw UTF-8 bytes).
- `dispatch/__init__.py` 의 두 `"status": "dispatched"` 반환 위치에 새 키 4개만 삽입하여 기존 caller 의 `result["status"] == "dispatched"` 의존성을 손상시키지 않음. legacy 호환을 유지하면서 `dispatch_state` 필드로 SUBMITTED_UNVERIFIED 의미를 명시.
- worktree 단위 pyright "Could not be resolved" diagnostic 은 `pyrightconfig.json` extraPaths 가 본 `/home/jay/workspace` 만 가리키기 때문 — pytest 실제 실행은 worktree `conftest.py` 가 `WORKSPACE_ROOT` 기반 sys.path 정리 후 정상 작동. 단위 테스트 55/55 PASS 로 동작 검증 완료.
- pre-existing baseline 7 fail (`tests/test_dispatch_gate.py` 3건 + `tests/test_dispatch_workflow.py` 4건) 은 origin/main 베이스라인에서도 동일하게 재현됨 — 본 task 패치와 무관 (격리된 환경 비교 완료, `/tmp/wt-baseline-2645` 임시 worktree 에서 검증 후 제거).

## allowed_resources 1:1

- `utils/prompt_byte_classifier.py` ✓
- `utils/dispatch_spawn_verifier.py` ✓
- `utils/dispatch_status_enum.py` ✓
- `dispatch.py` (shim 유지) / `dispatch/__init__.py` 패치 ✓
- `schemas/dispatch_status_v1.json` ✓
- `schemas/spawn_verification_result_v1.json` ✓
- `tests/fixtures/dispatch_gate/**` (6 fixture) ✓
- `tests/regression/dispatch_gate/**` (3 regression + __init__) ✓
- `tests/test_prompt_byte_classifier_2645.py` ✓
- `tests/test_dispatch_spawn_verifier_2645.py` ✓
- `tests/test_dispatch_status_enum_2645.py` ✓
- `tests/test_dispatch_gate_integration_2645.py` ✓
- `memory/tasks/task-2645.md` ✓ (sha256 일치 복사)
- `memory/reports/task-2645.md` ✓ (본 파일)
- `memory/events/task-2645.done` ✓ (commit 후 생성)
- `INDEX.md` — 본 작업 범위 내 task index 파일 없음, SKIP

## frozen anchor 검증

- **ANCHOR-1**: ✅ `dispatch.py` 두 반환 위치 + `assert_not_premature_success` + `dispatched_dict_is_unverified` 로 cron_response=ok 단독 final success 차단.
- **ANCHOR-2**: ✅ classifier 상수 4 구간 + 4096 + `wc -c` 측정 + ≤3900 hard limit 박제.
- **ANCHOR-3**: ✅ spawn verifier 4 항목 + silent drop 4신호 모두 hit doctrine 코드화.
- **ANCHOR-4**: ✅ 3270 allow / 3901 block / 4096+ silent drop regression 3건 단독 파일 + 통합 fixture 박제.
- **ANCHOR-5**: ✅ task-2644 영역 (`hooks/`, `task-2644.*`, `finish-task.sh`) 0 touch · live infra 0 modify.

## 회장 verbatim 6 금지 행위 0 회 (verbatim 박제)

- task-2644 ↔ task-2645 혼합: 0
- live settings.json 수정: 0
- live cokacdir 수정: 0
- ANU 직접 CI/Gemini polling: 0
- BOT App token / chair_authorization / real auto-merge / PR #141 pilot: 0
- merge execution: 0 (worktree 로컬 commit 만)

## ANU normal callback cron

finalize 시점 등록 완료:
- collector key: `c119085addb0f8b7` (ANU · executor self-key `fedf78d1d09509f5` 금지 박제)
- envelope UTF-8: **2249 bytes** (`wc -c`) — OK_TARGET 박제 정책 (≤3200) 통과
- canonical_root: `/home/jay/workspace`
- result.json 9 필드 포함 (schema / task_id / canonical_root / branch / commit_sha / base_sha / regression_total / regression_passed / callback_prompt_utf8_bytes / live_infra_modified_count)
- 등록 schedule id: `EBEF96C8` (2026-05-24 03:09:09 KST 발사, 1회성 자동 삭제)
- 선행 schedule `4BDC7229` (1746 bytes envelope, doctrine token 누락 박제 미흡) 은 자체 remove 후 doctrine 박제 강화 envelope 으로 교체
- 등록 응답 `status=ok` — 단 회장 verbatim ANCHOR-1 박제 준수상 cron_response=ok 단독으로 final success 보지 않음 → 본 callback 도 DISPATCH_SUBMITTED_UNVERIFIED 상태. 5분 후 schedule_history/EBEF96C8.log + workspace dir 4 신호 확인 후 ANU 가 final transition.

## validate_spawn_callback_contract self-check

`dispatch.spawn_callback_contract_validator.validate_spawn_callback_contract(...)` 호출 결과:

- **verdict: PASS**
- classifications: 빈 list
- prompt_has_anu_key: True
- prompt_has_collector_role: True
- prompt_has_required_doctrine: True (SELF_COLLECTOR · SENDFILE_ONLY · NOT_REGISTERED 박제 완비)
- executor_key (`fedf78d1d09509f5`) ≠ anu_key (`c119085addb0f8b7`) — self-collector 금지 doctrine 준수
- 1차 envelope (1746 bytes) doctrine 누락 감지 → 즉시 envelope 보강 후 재등록 + PASS 재검증 → 회장 verbatim §finalize 7 통과

끝.
