# Gate-D 운영 하드닝 수정 계획 Critic 재검토

검토 입력: immutable revision artifact `stage-21-revision.md`; assignment pin `648a22474462474154ce6a024d167873390d60d5012b05bbfbb7cda8bf87bd0c`. 세션 index의 stage 21 revision receipt가 같은 path와 SHA를 기록함을 확인했다. 제품 파일은 변경하거나 테스트하지 않았고, 현재 구현/설정/문서에서 대표 경로를 읽어 시뮬레이션했다.

## Verdict
**ITERATE**

Revision 21은 canonical review config, capability/lock order, persisted callback session, schedule receipt ledger/cutover, typed preflight, P2–P6 fault hooks를 추가하여 이전 결함의 대부분을 구체화했다. 그러나 이전 blocker 중 Topic 59의 모든 generic ingress 예약, operator menu의 시작/edit 계약, 전체 mutation TOCTOU matrix, downgrade-safe schedule cutover, P2–P6 exact outcomes/명령형 회귀 gate가 아직 executor 선택으로 남아 있으므로 실행 전에 한 번 더 보강해야 한다.

## Claim Checks

- **Artifact pin verified.** Parent session `index.jsonl`은 stage 21 revision path와 assignment SHA `648a...bd0c`를 동일하게 기록한다.
- **Review identity source-of-truth blocker is closed.** 계획은 adaptive config의 단일 `review_operator {user_id, chat_id, topic_id, version}`를 canonical로 정하고, 현재 chat/topic-only 설정은 disabled diagnostics에서만 읽고 production enable은 full triple 없이는 실패하게 한다. 현재 구현은 `AdaptiveNutritionConfig`에 chat/topic만 있고 `_nutrition_operator_address`가 별도 raw `nutrition_coaching.operator_review`를 읽으므로, 이 migration 지시는 실제 중복 경계를 제거하는 대표 구현 경로와 맞는다. Capability와 transition record가 authenticated review actor와 canonical owner snapshot을 모두 보존하는 정책도 명시됐다.
- **Current callback defect and proposed boundary fit are verified.** `telegram.py::_handle_adaptive_nutrition_callback`은 현재 incoming triple을 refreshed canonical owner triple과 직접 비교하고 모든 customer coordinator를 순회한다. 새 gateway-owned `AdaptiveOperatorService`와 persisted session/customer binding은 두 결함을 직접 해소하며 callback 길이와 consume/replay 정책도 구현 가능하다.
- **Generic ingress blocker is not closed.** 현재 `_is_nutrition_operator_space` 사용은 `_handle_text_message` 한 곳뿐이다. `_handle_command`, `_handle_location_message`, `_handle_media_message`는 review space를 선점하지 않고 generic handler로 보낼 수 있다. Revision 21은 raw input/generic append 금지 원칙만 반복하고 command/media/location/unrelated callback의 선점 지점과 zero-generic-agent tests를 열거하지 않는다.
- **Menu lifecycle is still partly underspecified.** 계획은 service/session ledger/action allowlist를 정했지만 menu를 여는 exact Telegram trigger/handler, `create` session에서 아직 존재하지 않는 revision/digest 필드의 nullability, `publish`와 `view`가 별도 action인지, raw text를 금지한 상태에서 `edit`가 어떤 typed value를 수집해 `revise_note` 또는 재계산에 전달하는지를 정하지 않았다. 현재 production code에는 proposal create/publish UI가 없고 edit/reconcile은 `operator_input_required`만 반환하므로 executor가 UX와 상태 전이를 발명해야 한다.
- **Shared lock/capability direction is sound, but the named matrix is incomplete.** Revision 21은 authority→lifecycle→store lock order, pre-append/pre-provider validation, capability pins를 정한다. 그러나 `Exact TOCTOU Matrix`는 delivery-enable/revoke, approve, activate, rollback, send, reconcile만 열거하고 create/publish/edit/hold/release를 제외한다. 현재 `revise_note`, hold/release도 append mutation이므로 “every transition” 원칙의 named tests/zero-delta acceptance가 아직 완전하지 않다.
- **Scheduled ledger core blocker is mostly closed.** `scheduled-deliveries.jsonl` fields/states, prepared→sending→provider→delivered→sent_audited, terminal unknown, receipt-only reconcile, legacy claims→unknown cutover, retry no-unlink, corruption/mixed-version rejection, crash-at-each-boundary tests가 구체화됐다. 이는 현재 claim-only `customer_schedule.py`, unlinking `release_customer_task_claim`, `customer_admin retry`, package export와 tick callsite를 교체할 충분한 핵심 계약이다.
- **Downgrade/cutover remains unimplementable as written.** Legacy claim files를 보존하면 migrated historical tasks는 old code에서도 막히지만, cutover 이후 새 ledger에만 예약된 날짜는 old binary가 보지 못하고 새 `.claim`을 만들어 재전송할 수 있다. “downgrade fail closed”라는 결과만 있고 old binary startup prohibition/version marker/dual-written legacy tombstone 중 어느 mechanism을 쓰는지 없다. 현재 old code는 new ledger/schema marker를 읽지 않으므로 executor가 추측해서는 이 acceptance를 보장할 수 없다.
- **Preflight/doc authority is materially improved.** Public `audit_gate_d_preflight(profile_root, customer_key) -> GateDPreflightReceipt`, bounded output, no creation/mutation boundary, human-provisioned account/token boundary가 정해졌다. Profile runbook을 authoritative로 지정한 점도 방향은 명확하다. 다만 root workspace `PILOT_RUNBOOK.md` duplicate가 pointer/historical artifact/synchronized copy 중 무엇인지 아직 분류되지 않았다.
- **P2–P6 are improved but not exact everywhere.** P2–P5의 fault point/provider count/major event delta는 실행 가능하다. P6는 after-reservation case를 `rejected/aborted or linked delivery_unknown`으로 두어 shared linearization contract의 terminal outcome과 충돌하며, P5/P6 exact Korean UI outcome도 없다. Cleanup/reset도 명시되지 않았다.
- **Verification commands are only partly closed.** Profile full command와 focused/cumulative gateway commands가 생겼다. 하지만 AC10의 “full gateway suites”에 대응하는 full command가 없고 cumulative command는 `test_telegram_group_gating.py`를 빠뜨려 바로 위 generic-ingress 위험을 검증하지 않는다. `py_compile for changed modules`도 실행 가능한 exact command가 아니다.

### Representative implementation simulation

1. **Config/callback migration:** executor는 `AdaptiveNutritionConfig`에 full review model/version을 추가하고 `_nutrition_operator_address`와 adaptive ingress가 같은 model을 사용하게 한 뒤 capability를 mint할 수 있다. 이 경로는 revision 21로 결정 가능하다.
2. **Create→edit→publish card:** executor가 service/session file과 `an1` callback을 만들 수는 있지만, first menu trigger와 edit typed payload/state가 없어 현재 `operator_input_required`를 대체하는 서로 다른 구현들이 모두 가능하다. 이 task는 아직 추측 없이 진행할 수 없다.
3. **Schedule crash/restart/cutover:** new code의 prepared/sending/unknown/receipt recovery는 구현 가능하다. 그러나 cutover 뒤 old binary가 새 date를 처리하는 scenario는 현재 old claim API가 new ledger를 전혀 읽지 않아 명시된 fail-closed acceptance를 만족하지 않는다.

## Missing Evidence

1. Topic 59에서 text뿐 아니라 command, location, media/sticker, unrelated callback/channel-post를 generic agent/provider보다 먼저 선점하는 exact handler policy와 negative tests.
2. Operator control plane의 exact menu opener, create-before-revision session schema, publish/view enum, typed edit input/state/restart/card-post failure recovery.
3. Create/publish/edit/hold/release까지 포함한 complete action/pin/timing/zero-delta TOCTOU test matrix.
4. Cutover 후 생성되는 새 schedule intent도 old binary에서 재전송되지 않게 하는 concrete downgrade barrier와 deployment/rollback order.
5. P6의 단일 terminal state, P2–P6 exact Korean UI/ordered deltas/cleanup, full gateway/group-gating/compile commands.
6. Root workspace runbook duplicate의 pointer, historical-only label, 또는 synchronization rule.

## Approval Boundary

Read-only mapping과 planner/architect revision만 승인한다. Product execution은 위 여섯 계약을 보강한 다음 consensus review를 다시 통과해야 한다. Real accounts, credentials, live Telegram Gate-D, real-customer activation/delivery는 계속 승인 범위 밖이다.

## Summary

- **Clarity:** Identity/capability와 ledger는 높음; menu trigger/edit 및 downgrade barrier는 불충분.
- **Verifiability:** Provider counts/crash hooks는 좋아졌으나 P6/UI/full commands가 모호함.
- **Completeness:** 이전 blockers 대부분 보강; all-ingress reservation, 전체 action matrix, deployment rollback이 남음.
- **Big Picture:** Production-path Gate-D와 human-only 경계는 일관되고 안전 방향이 맞음.
- **Principle/Option Consistency:** Chosen option B와 role separation/no-retry 원칙은 일치; P6의 `or` outcome과 incomplete action matrix는 exactness 원칙에 맞지 않음.
- **Alternatives Depth:** Config/session/dedicated schedule ledger 선택은 이제 드러났으나 downgrade mechanism 선택은 숨겨져 있음.
- **Risk/Verification Rigor:** Crash/restart와 stale authority는 강함; generic ingress 및 old-binary rollback 증명이 부족함.

## Required Changes

1. `telegram.py`의 text/command/location/media/sticker/unrelated-callback entrypoints마다 exact review-space reservation을 지정하고 generic `handle_message`, media cache/vision, provider call이 0임을 검증하는 `test_telegram_group_gating.py`/adaptive tests를 추가한다.
2. Menu를 여는 one canonical Telegram trigger와 handler를 정하고, action enum을 분리하며, pre-proposal create session 필드 nullability, typed edit payload/validation/expiry/restart, post-card failure/retry behavior를 정의한다.
3. Shared matrix에 create, publish, edit, hold, release를 추가하여 각 action의 pins, linearization point, idempotent replay authorization, injected timing, exact zero row/state delta를 이름 붙인다.
4. Schedule cutover에 concrete compatibility barrier를 선택한다: new intent마다 old claim tombstone을 함께 durable-write하거나, old binary가 이해하는 deployment guard를 먼저 배포하고 downgrade를 금지한다. Exact code/config/data rollout order, abort condition, recovery-only rollback을 명시하고 `release_customer_task_claim`, `retry`, `__init__` export의 최종 API를 열거한다.
5. P6 after-reservation outcome을 linked terminal `delivery_unknown` 또는 별도 single named state 하나로 고정하고 P2–P6의 exact Korean UI, ordered row deltas, baseline/cleanup을 완성한다. Gateway full suite, `test_telegram_group_gating.py`, exact `py_compile` command를 release gate에 추가한다.
6. Root `PILOT_RUNBOOK.md`를 canonical profile runbook의 digest-pinned pointer, explicitly historical document, 또는 synchronized copy 중 하나로 지정한다.
