# RUN — PE "Ý kiến cấp duyệt" save-advance + creator-as-approver + reject-return (investigate)

- **folder:** `2026-07-12-S112-pe-signoff-save-advance`
- **session:** S112 (2026-07-12) · **lead:** Opus 4.8 MAX (owner-choice)
- **command:** `/fable-clone vai invest/review đề tài 2 ảnh` + "hmw cho Opus 4.8 MAX" (anh-directed)
- **mode:** HMW RUN-TRACE · **engine:** fable-clone ensemble (per-invocation H21) · model=opus effort=inherit(xhigh)
- **input:** 2 screenshot chat FDC anh Kiệt/PRO — yêu cầu đổi luồng PE sign-off (save-advance + creator=approver + reject-return)
- **spec:** `spec-pe-signoff-save-advance-12-07-2026.md`

## taskList snapshot
| Stage | Role | Lane | Lens |
|---|---|---|---|
| Investigate | investigator-codebase | A | Luồng hiện tại (ApproveV2Async advance + creator/approver identity) |
| Investigate | investigator-codebase | B | Thiết kế thay đổi (BE+FE, save=advance vs tách, self-approval flag) |
| Investigate | investigator-codebase | C | Reject/TraLai flow (return-to-commenter, re-comment, edge) |
| Review | reviewer | 0 | Adversarial verify (state-machine · self-approve · reject-loop · multi-cấp) |

## deliverable
Design-doc: luồng hiện tại + thay đổi BE/FE cần làm + reject-flow + risk → **anh quyết** design + scope. KHÔNG code lượt này.

## synthesis (em-main — wf_fd8f2bfe-9f6, 4/4 clean, Opus 4.8)

> sub-file naming = journal-order ≠ lane-order: `sub-invest-B`=Lane A (current-flow) · `sub-invest-C`=Lane B (change-design) · `sub-invest-A`=Lane C (reject) · `sub-reviewer`=reviewer.

**REVIEWER VERDICT: PASS_WITH_CONCERNS.** 3 lane khớp source thật 100% (mọi file:line verify đúng). Thiết kế sound, 0 vỡ 5-state.

### Cốt lõi (bất ngờ tốt): yêu cầu = sửa 1 CHỖ, cơ chế đã có sẵn
- "comment-in-own-box → save → advance" ĐÃ tồn tại trong dialog Duyệt (`ApproveV2Async` UPSERT opinion `:763-790` + advance `:927`, cùng 1 call).
- 🔴 ĐIỂM CHẶN = **`ApplyDrafterBypassOnSubmitAsync`** (`PurchaseEvaluationWorkflowService.cs:571-681`, gọi `:260-261`): lúc creator (=DrafterUserId=PRO) submit, nó TỰ ký hộ ô Cấp-1 bằng comment CANNED "(duyệt tự động…)" `:619` + advance qua → creator mất ô comment thật của mình (đúng cái screenshot Cấp-1 Bình muốn).
- 🔴 Đây là feature UAT **S60** (anh Kiệt cũ: "TP tạo thì bypass — NV khỏi duyệt lại"). Yêu cầu mới **NGƯỢC lại** → **đảo hành vi S60, cần sign-off**.
- **FIX:** gỡ/gate bypass → pointer đứng `(0,1)` → creator gõ comment thật + Duyệt → `ApproveV2Async` lo phần còn lại. **0 đổi ApproveV2Async · FE ~0**. Giữ **1-nút** (save=advance); 2-nút draft = thừa (cần schema+endpoint+DTO).

### Reject (item 3): phần lớn ĐÃ có (mode `Assignee` — về NV đã ký, giữ ChoDuyet, re-comment UPSERT, forward). 3 gap: default=Drafter(full-restart) · phải admin bật Allow*-per-slot · rejecter pick tay.

### Governance: creator-sign Cấp-1 = bước ĐỀ-XUẤT hợp lệ (CCM/CEO cấp trên mới approval thật, phân-tách nhiệm-vụ giữ). Lỗ CHỈ khi slot creator bật `AllowApproverFinalize`/`SkipToFinal` → self-finalize bỏ CEO (hiện 0 guard → đề-xuất guard B2, đã tồn-tại hôm nay qua bypass single-step-terminal).

### 🔴 EDGE-5 (reviewer nâng latent→REACHABLE, MAJOR pre-existing): Drafter forge `Reject+TargetPhase=TraLai` trên phiếu **DaDuyet của chính mình** (đã chốt ApprovedPrice) → mở lại → sửa → gửi lại. Controller `:130-143` chỉ `[Authorize]` class-level + handler `FEAT:510-539` KHÔNG guard fromPhase + `EnsureCanRejectV2Async` early-return khi Phase≠ChoDuyet `:321`. Vùng reject-return CHÍNH LÀ scope sửa → nên bịt luôn (guard `fromPhase∈{DaDuyet,TuChoi}→throw`).

### 6 quyết định owner (anh Kiệt): (1) A1/A2 gỡ-bypass · (2) guard-B2 self-finalize · (3) EDGE-5-fix · (4) 1-nút/2-nút · (5) stale-comment-khi-trả-lại · (6) migrate-data-cũ-bypass.

**Harvest:** pending @closeout — investigator-codebase + reviewer, stamp `[engine: fable-clone-ensemble · Opus-S112]`.
