# S83 (2026-06-22) — H-15 v3 memory-budget full-parity + PE require-workflow guard

**Anh:** `/session-start` → directive ngân-sách Tầng-1 "Giờ là 220k, đầu phiên nạp thêm cho đủ" + sub 60K/wf 50K + "tao đánh dấu User-mark" → forward screenshot phiếu PE/2026/A/040 "Sao cái này lại là quy trình cũ?" → (sau khi tự-khỏi) "Giờ thì lại được? do cache?" → **"(B) Vá guard BE (require workflow create+submit) + test-before → deploy. Thôi vá đi. Rồi Session-end."**

2 commit prod-verified: `2c7fd63` (governance H-15 v3, docs-only CI-skip) + `fc1f19d` (PE fix, **Run #333 PASS ~4m59s**).

---

## Part 1 — H-15 v3 memory-budget (anh owner-directive, 0 production code)

**Directive:** lead Tầng-1 **220K** · mem-sub **60K** · wf-sub **50K** = **full AI_INFRA parity**. Sửa self-shrink S82 (AI tự lập-luận "SE nhỏ hơn → 60K" = đúng cái `role_boundary_note` + mark `RC-…01-58-01` cấm: token-saving = quên-việc). + nguyên-tắc: khi SPAWN sub → nạp context tới budget bằng **prompt GIÀU** (relevant gotcha + state + full task-context + docs/memory), KHÔNG nạp rác; hot-load = token giá-trị-nhất.

**Implement (em-main single-writer D9, 4 file):**
- `memory-budget.json`: `token_governor.tier1_hotfeed_tokens` lead 200K→**220K** / mem-sub 20K→**60K** / wf-sub 16K→**50K** + NEW block **`spawn_fill_directive`** (rich-prompt-to-budget + quality_gate highest-value-distilled).
- `harness-11-engine.md`: NEW **§G.5** (v3 delta: full-parity + spawn-fill + byte-cap-là-1-lát correction) + §G.1/§G.4 số + adopt-delta S83 + header/table-row.
- `agents/README.md`: anti-truncation **retire `≤8K brief`** → rich INPUT to budget (#53 = RETURN-truncation, vá bằng memoryDelta return KHÔNG starve input).
- `ACTIVE-MARKS.md`: H-15 mark **v3-delta** stamp `RC-pqhuy1987-22-06-2026-16-35-37` (anh-directed; report-before-stamp).

**Correction cốt-lõi:** byte-cap MEMORY.md (30720B ~9.3K tok) CHỈ là 1 lát của Tầng-1 sub — **prompt-spawn em-main viết** nạp phần còn lại tới token-budget. ⟹ 60K/50K KHÔNG phải "headroom vô-dụng" (bác lập-luận S82 "byte-cap binds first").

**Self-gate:** JSON valid · detector **26 baseline (0 new drift)** · A7 GATE 217/217.

**Dogfood:** spawn cicd-monitor (Part 2) bằng prompt GIÀU theo `spawn_fill_directive` mới (relevant gotcha #41/#69/#65 + state bundle/Mig + full task-context) — đúng tinh-thần directive.

---

## Part 2 — PE require-workflow guard (fix "quy trình cũ", prod-verified)

**Diagnose (em-main, read-only):** banner "Phiếu này dùng quy trình cũ — workflow chi tiết không khả dụng" + "Lịch sử duyệt (0)" = FE [PeWorkflowPanel.tsx:333](../../../fe-user/src/components/pe/PeWorkflowPanel.tsx) render khi `flow = evaluation.approvalFlow` rỗng. BE dựng `approvalFlow` chỉ khi `ApprovalWorkflowId` pinned. Submit [PurchaseEvaluationWorkflowService.cs:226]: `CurrentApprovalLevelOrder = ApprovalWorkflowId is not null ? 1 : null`. ⟹ banner ⟺ phiếu **null-workflow** → nhánh V1-legacy.

**Root cause = validate BẤT ĐỐI XỨNG FE↔BE (họ gotcha #44):** FE create bắt buộc (`canSubmit = … && !!form.approvalWorkflowId`) nhưng BE `CreatePurchaseEvaluationCommand` chỉ validate NẾU-CÓ-TRUYỀN (`if (request.ApprovalWorkflowId is Guid awId)`) + submit không guard → null lọt vào ChoDuyet.

**Diagnostic heuristic (anh hỏi "do cache?"):** phiếu A/040 **tự-khỏi khi reload** ⟹ **cache** (TanStack stale detail bundle), KHÔNG phải data-hỏng (data-hỏng reload KHÔNG khỏi). Em KHÔNG đụng prod/data — chỉ đọc code. Đính-chính trung-thực: "ApprovalWorkflowId null" lúc đầu là **suy-luận từ màn FE**, chưa xác-nhận DB; tự-khỏi-khi-reload nghiêng hẳn về cache.

**Fix (anh chọn B — test-before BẮT BUỘC bug-fix):**
1. **TEST-BEFORE (RED confirmed):** 2 test repro (validator null + submit null) chạy code cũ = 2 FAIL.
2. **Validator** ([PurchaseEvaluationFeatures.cs](../../../src/Backend/SolutionErp.Application/PurchaseEvaluations/PurchaseEvaluationFeatures.cs)): `RuleFor(x => x.ApprovalWorkflowId).NotEmpty()`.
3. **Submit guard** ([PurchaseEvaluationWorkflowService.cs](../../../src/Backend/SolutionErp.Infrastructure/Services/PurchaseEvaluationWorkflowService.cs)): `if (ApprovalWorkflowId is null) throw ConflictException(...)` đặt **SAU Section-3 block, TRƯỚC `Phase = ChoDuyet`** (orthogonal — ưu tiên báo thiếu data).

**Spec-conflict triage (bài học chính):** full-suite bắt **9 test đỏ** sau fix → triage thay vì đập bừa:
- **2 genuine-spec-conflict** (`Submit_AllFourMet_NoWorkflow_SetsChoDuyet` + `Submit_V1Phieu_…SubmitsOk`) = **cố ý** khẳng định V1-submit OK → **rewrite** assert-throws (V1-submit deprecated, an toàn vì data V1 đã wipe S59).
- **6 collateral** (Section-3 tests seed null-workflow để test Section-3) → **dời guard ra sau Section-3** giữ cả 6 nguyên vẹn (KHÔNG churn).
- **1 validator** (`Validator_WorkItemIdPresent_NoErrorOnWorkItemId` assert IsValid=true) → +param `approvalWorkflowId` BuildCreateCommand.
- Relocate 2 validator test → `PeWorkItemGuardTests` (nhà của validator); xóa file riêng tạm.

**Test 354→356** (+2 validator). Full slnx build sạch (gotcha #65).

**Deploy (commit `fc1f19d`, cicd-monitor Run #333 PASS):**
- Smoke api/admin/eoffice 200 · Mig **57** no-pending · test gate pass.
- **Bundle KHÔNG rotate** admin `CsJetgZH`/user `BVS0ApIm` (= #330) — **#69-REFINE:** 0 `fe-*/src` change → Vite deterministic same-hash; ship-signal THẬT = **Last-Modified in-window** (17:10/17:11), KHÔNG hash-delta. `deploy.yml` rebuild FE unconditional NHƯNG output byte-identical. (Canonical gotcha #69 cần fold refinement này — defer monthly 2026-07-01; recorded cicd-memory.)

**FE:** KHÔNG sửa — submit null-workflow → BE 409 → FE toast "Phiếu chưa chọn quy trình duyệt…". Phiếu A/040 kẹt sửa bằng Trả lại→Sửa→chọn quy trình→gửi lại.

---

## §L spawn-records (3 sub, Agent-tool spawn — KHÔNG Workflow fan-out → 0 run-trace folder)

| agent | task | nấc | evidence |
|---|---|---|---|
| 🟫 tooling-auditor (H1) | session-start 4-mặt freshness + diff | verified | roster 11/11 inherit no-`[1m]` · plugin 18/15/3 · 5 stale-cite (ef-core SKILL Mig56→57 + skills/README #69→#71) carry monthly · docs canonical FRESH |
| ⬜ harvest-curator (H2) | session-start harvest-integrity scan | verified | 0 orphan-run · 0 un-appended delta · 0 corruption · all L1 <soft-cap |
| 🟩 cicd-monitor | verify deploy Run #333 (rich-prompt dogfood) | verified | PASS — smoke 200×4 + Mig 57 no-pending + #69-REFINE; wrote own MEMORY (#333 + #69-REFINE) |

**§L.a AS-scan:** 0 hit. Test-before + full-suite bắt spec-conflict **pre-commit** (working-as-intended, KHÔNG broken commit). S82 self-shrink correction = guard-held (mark `RC-…01-58-01` + role_boundary), KHÔNG RCA mới.

---

## 🔴 NEXT SESSION
- **Anh:** ⚠️ restart CLI activate budget `token_governor` v3 (220/60/50) + `spawn_fill_directive` runtime (config JSON sống ngay; engine/command `.md` no hot-reload).
- **Em (carry):** frontend-designer 26.1KB + test-specialist 27.7KB WATCH strike-1 (<2, no-action). **Monthly audit 2026-07-01:** fold **#69-REFINE** vào gotcha canonical · ef-core SKILL Mig56→57 + skills/README #69→#71 stale-cite · STATUS/HANDOFF re-tier (bloat).
- **Pending product:** "Ngưỡng giá CEO" Mig 54 Designer UAT · "C" chuyển phiếu→dự án chờ spec. **FE proactive guard** (disable submit nút cho phiếu null-workflow) = offered, chờ anh.
- **Ops S58/S59:** tzutil VPS UTC+7 · anh Chương email typo · 5 real-staff pw · gán CNTT.
