diff --git a/docs/superpowers/plans/2026-07-22-in-plan-report-submit-review-fix.md b/docs/superpowers/plans/2026-07-22-in-plan-report-submit-review-fix.md
new file mode 100644
index 0000000..4456348
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-22-in-plan-report-submit-review-fix.md
@@ -0,0 +1,333 @@
+# In-Plan Report Submit/Review Bug-Fix Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Fix the dead "ส่งให้กองแผนตรวจสอบ" button on `agency-report-form/list-all` (typeUrl 20) so it actually submits selected reports to the planning division, add a status badge + checkbox guard to that list, and lock the report form for the agency once it's submitted — mirroring the pattern already shipped for out-of-plan reports.
+
+**Architecture:** No new tables/endpoints/components. Three surgical edits to existing files: (1) one added boolean condition in `make-year-plant-form.component.ts`'s `save()` so typeUrl 20 stops being intercepted by the generic `isListAllMode()` branch and instead reaches its own already-correct `else if(this.typeUrl == 20)` block; (2) a `[disabled]` binding plus a new `reportStatusBadge()` helper and badge `` in the list-all checkbox column; (3) one added `if` block in `agency-report-form.component.ts`'s existing `typeUrl == 202` branch, copied from the identical pattern already present in the `typeUrl == 282` branch two branches below it.
+
+**Tech Stack:** Angular 17, Angular Material (`mat-checkbox`), RxJS (`tap`).
+
+## Global Constraints
+
+- No backend (`rmutr-api`) changes — `AgencyReportService.updateStatusAgency()` (POST `update_status/1`) and `AgencyReportService.get()` already exist and already work (used today by the out-of-plan flow and by `check-project-report`).
+- typeUrl 12 and typeUrl 14 behavior in `make-year-plant-form.component.ts` must be **completely unaffected** — every change in this plan is gated so it only fires for `typeUrl === 20`.
+- Do not touch `check-project-report` (typeUrl 16) or any out-of-plan code (typeUrl 28x/29x) — both already work correctly per prior investigation and must not regress.
+- This repo has no meaningful unit-test culture for these Angular components. Per-task verification uses `ng build --configuration=production` (from `/Users/nut.looknut/Project/rmutr/rmutr-web`) to catch compile/template errors, plus a manual browser check via `npm start` for the final task.
+- Follow existing code conventions exactly: raw `Swal`/`this.swSV.confirmSave()`/`this.swSV.errText()` patterns already used in these files — don't introduce a new dialog library.
+
+---
+
+### Task 1: Fix the `save()` dispatch bug for typeUrl 20
+
+**Files:**
+- Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/make-year-plan-form/make-year-plant-form.component.ts:2380`
+
+**Interfaces:**
+- Consumes: nothing new — `this.typeUrl` (existing component field), `this.isListAllMode()` (existing method, ts:159-161)
+- Produces: nothing new — this task only changes which existing branch of `save()` executes for typeUrl 20. The already-existing `else if(this.typeUrl == 20)` block (ts:2439-2508) becomes reachable; its `case 'add':` sub-branch (ts:2457-2478) is what will now run when the button is clicked from `list-all` (route has no `:id`, so `this.state` is always `'add'` there).
+
+- [ ] **Step 1: Change the guard condition**
+
+Find, in `make-year-plant-form.component.ts`:
+```ts
+ save(){
+ console.log(this.typeUrl)
+
+ if(this.isListAllMode()){
+```
+Change to:
+```ts
+ save(){
+ console.log(this.typeUrl)
+
+ if(this.isListAllMode() && this.typeUrl !== 20){
+```
+
+- [ ] **Step 2: Build to confirm no errors**
+
+Run: `cd /Users/nut.looknut/Project/rmutr/rmutr-web && ng build --configuration=production`
+Expected: build succeeds with no new errors.
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/make-year-plan-form/make-year-plant-form.component.ts
+git commit -m "fix: route typeUrl 20 list-all submit through the correct save() branch"
+```
+
+---
+
+### Task 2: Checkbox guard + status badge on the `list-all` report column
+
+**Files:**
+- Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/make-year-plan-form/make-year-plant-form.component.ts` (add helper method near `isListAllMode()`, ts:159-161)
+- Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/make-year-plan-form/make-year-plan-form.component.html:472-493`
+
+**Interfaces:**
+- Consumes: `item.value.agency_report.agency_report_uid` / `item.value.agency_report.status_id` (existing form data, already patched from backend — confirmed present at ts:1446, 1448 in the `agency_report` FormGroup builder)
+- Produces: `reportStatusBadge(status_id: number | null): {label: string, bg: string, color: string}` — a new public method on `MakeYearPlanFormComponent`/`MakeYearPlantFormComponent`, called only from the template in this task. No other task depends on it.
+
+- [ ] **Step 1: Add the `reportStatusBadge()` helper method**
+
+Find, in `make-year-plant-form.component.ts`:
+```ts
+ isListAllMode(): boolean {
+ return this.state === 'add' && (this.typeUrl === 12 || this.typeUrl === 14 || this.typeUrl === 20)
+ }
+```
+Change to:
+```ts
+ isListAllMode(): boolean {
+ return this.state === 'add' && (this.typeUrl === 12 || this.typeUrl === 14 || this.typeUrl === 20)
+ }
+
+ reportStatusBadge(status_id: number | null): { label: string, bg: string, color: string } {
+ switch (status_id) {
+ case 1: return { label: 'รอตรวจสอบ', bg: '#fef3c7', color: '#92400e' }
+ case 2: return { label: 'ส่งแก้ไข', bg: '#fee2e2', color: '#991b1b' }
+ case 3: return { label: 'ตรวจสอบแล้ว', bg: '#dcfce7', color: '#166534' }
+ default: return { label: 'ร่าง', bg: '#e5e7eb', color: '#374151' }
+ }
+ }
+```
+
+- [ ] **Step 2: Add `[disabled]` to the checkbox and the badge `` in the template**
+
+Find, in `make-year-plan-form.component.html`:
+```html
+
+```
+
+- [ ] **Step 3: Build to confirm no errors**
+
+Run: `cd /Users/nut.looknut/Project/rmutr/rmutr-web && ng build --configuration=production`
+Expected: build succeeds with no new errors.
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/make-year-plan-form/make-year-plant-form.component.ts rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/make-year-plan-form/make-year-plan-form.component.html
+git commit -m "feat: add status badge and re-submit guard to list-all report checkboxes"
+```
+
+---
+
+### Task 3: Lock the in-plan report form once submitted
+
+**Files:**
+- Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/agency-report-form/agency-report-form.component.ts:469-508`
+
+**Interfaces:**
+- Consumes: `this.isLocked` (existing field, ts:180, already declared — no new field needed), `x.status_id` (existing `agency_report` field, already loaded by `AgencyReportSV.get()`)
+- Produces: nothing new — `isLocked` being `true` is already read by the existing template bindings at `agency-report-form.component.html:428` (locked banner) and `:430` (hides the "บันทึก" button), both unconditional on `typeUrl`, so they activate automatically for typeUrl `202`/`202163` once this task sets the flag. The `save_send()`/`save_pass()` buttons (gated by `typeUrl == 202161`) are untouched by this task and keep working because both methods call `getRawValue()`, which reads values from disabled controls.
+
+- [ ] **Step 1: Add the lock check to the `typeUrl == 202` branch**
+
+Find, in `agency-report-form.component.ts`:
+```ts
+ else if(this.typeUrl == 202 || this.typeUrl == 202161 || this.typeUrl == 202162 || this.typeUrl == 202163) {
+ this.AgencyReportSV.get(this.uniquekey).pipe(
+ tap((x:any)=>{
+ console.log(x)
+ this.form.patchValue(x)
+ this.change_year(x.budget_year_name_th)
+ if (x.agency_report_items) {
+ const formArray = this.form.get('agency_report_items') as FormArray
+ x.agency_report_items.forEach((agency_report_item) => {
+ formArray.push(this.agency_report_item_form(agency_report_item))
+ });
+ }
+ if (x.agency_report_details) {
+ x.agency_report_details.forEach((agency_report_detail) => {
+ let form_agency_report_details = this.form.get('agency_report_details') as FormArray
+ form_agency_report_details.push(this.agency_report_details_form(agency_report_detail))
+ });
+ const firstIssue = x.agency_report_details.find(d => d.type == 1 && d.text_1)
+ if (firstIssue) this.change_budget_strategy_faculty_strategic_edit(firstIssue.text_1)
+ }
+ if (x.agency_report_popularities) {
+ x.agency_report_popularities.forEach((p) => {
+ const arr = this.form.get('agency_report_popularities') as FormArray
+ arr.push(this.fb.group(p))
+ })
+ this.cdRef.detectChanges()
+ }
+ }),
+ concatMap((x: any) => x.change_project_detail_uid
+ ? this.ChangeProjectDetailSV.get(x.change_project_detail_uid)
+ : of(null)
+ ),
+ tap((changeDetail: any) => {
+ if (changeDetail?.project_name_th) {
+ this.form.get('project_name_th').setValue(changeDetail.project_name_th)
+ }
+ Swal.close()
+ })
+ ).subscribe()
+ }
+```
+Change to:
+```ts
+ else if(this.typeUrl == 202 || this.typeUrl == 202161 || this.typeUrl == 202162 || this.typeUrl == 202163) {
+ this.AgencyReportSV.get(this.uniquekey).pipe(
+ tap((x:any)=>{
+ console.log(x)
+ this.form.patchValue(x)
+ this.change_year(x.budget_year_name_th)
+ if (x.agency_report_items) {
+ const formArray = this.form.get('agency_report_items') as FormArray
+ x.agency_report_items.forEach((agency_report_item) => {
+ formArray.push(this.agency_report_item_form(agency_report_item))
+ });
+ }
+ if (x.agency_report_details) {
+ x.agency_report_details.forEach((agency_report_detail) => {
+ let form_agency_report_details = this.form.get('agency_report_details') as FormArray
+ form_agency_report_details.push(this.agency_report_details_form(agency_report_detail))
+ });
+ const firstIssue = x.agency_report_details.find(d => d.type == 1 && d.text_1)
+ if (firstIssue) this.change_budget_strategy_faculty_strategic_edit(firstIssue.text_1)
+ }
+ if (x.agency_report_popularities) {
+ x.agency_report_popularities.forEach((p) => {
+ const arr = this.form.get('agency_report_popularities') as FormArray
+ arr.push(this.fb.group(p))
+ })
+ this.cdRef.detectChanges()
+ }
+ if (x.status_id === 1 || x.status_id === 3) {
+ this.isLocked = true
+ this.form.disable()
+ }
+ }),
+ concatMap((x: any) => x.change_project_detail_uid
+ ? this.ChangeProjectDetailSV.get(x.change_project_detail_uid)
+ : of(null)
+ ),
+ tap((changeDetail: any) => {
+ if (changeDetail?.project_name_th) {
+ this.form.get('project_name_th').setValue(changeDetail.project_name_th)
+ }
+ Swal.close()
+ })
+ ).subscribe()
+ }
+```
+
+- [ ] **Step 2: Build to confirm no errors**
+
+Run: `cd /Users/nut.looknut/Project/rmutr/rmutr-web && ng build --configuration=production`
+Expected: build succeeds with no new errors.
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/agency-report-form/agency-report-form.component.ts
+git commit -m "feat: lock in-plan report form once submitted or reviewed"
+```
+
+---
+
+### Task 4: End-to-end manual verification
+
+**Files:** none (verification only)
+
+**Interfaces:**
+- Consumes: all of Tasks 1-3
+- Produces: nothing — this is the final confirmation task for this plan
+
+- [ ] **Step 1: Start the dev server**
+
+Run: `cd /Users/nut.looknut/Project/rmutr/rmutr-web && npm start`
+Expected: compiles, dev server serves on the configured local port.
+
+- [ ] **Step 2: Verify the list-all badge and checkbox guard**
+
+In the browser, log in and navigate to `app/agency-report-form/list-all` (typeUrl 20). Confirm:
+- Rows with a saved report but not yet submitted show a gray "ร่าง" badge and their checkbox is selectable.
+- Rows with no report at all show no badge and their checkbox is disabled.
+
+- [ ] **Step 3: Verify submit actually reaches the backend**
+
+Select 1-2 "ร่าง" rows → click "ส่งให้กองแผนตรวจสอบ" → confirm the dialog. Confirm:
+- No JS error in the browser console.
+- After the save completes and the list reloads, those rows' badges change to yellow "รอตรวจสอบ" and their checkboxes become disabled.
+
+- [ ] **Step 4: Verify the empty-selection error still fires**
+
+Deselect everything (or select only rows without a report) → click "ส่งให้กองแผนตรวจสอบ". Confirm the existing error message "กรุณาเลือกรายการที่ต้องการส่ง" appears and no request is sent.
+
+- [ ] **Step 5: Verify the report form locks for the agency once submitted**
+
+From `list-all`, open (แก้ไขรายงาน) one of the rows now in "รอตรวจสอบ" status. Confirm all fields are disabled and the "บันทึก" button is hidden, replaced by the "รายงานนี้ถูกล็อก ไม่สามารถแก้ไขได้" banner.
+
+- [ ] **Step 6: Verify `check-project-report` regression-free (planning division side)**
+
+Log in as (or switch to) a planning-division account and open `app/check-project-report`. Confirm the rows submitted in Step 3 appear under the "รอตรวจสอบ" tab. Click into one and click "ส่งแก้ไข". Confirm:
+- Status becomes 2 (ส่งแก้ไข).
+- Back on the agency's `list-all`, that row's badge is now red "ส่งแก้ไข" and its checkbox is selectable again.
+- Opening the report form for that row is now editable (not locked), since status_id is 2.
+
+- [ ] **Step 7: Verify "ตรวจสอบผ่าน" end state**
+
+Re-submit the same row from `list-all` (repeat Step 3). As the planning division, open it from `check-project-report`'s "รอตรวจสอบ" tab and click "ตรวจสอบผ่าน". Confirm status becomes 3, the row moves to the "ตรวจสอบแล้ว" tab, and on the agency's `list-all` the badge is now green "ตรวจสอบแล้ว" with the form permanently locked when reopened.
+
+- [ ] **Step 8: Regression check typeUrl 12/14 unaffected**
+
+If test access to a typeUrl 12 or 14 `list-all`-equivalent view is available, open it and confirm the existing save behavior (grouped `ChangeProjectSV.put()` per `change_project_uid`) is unchanged — no badge, no `[disabled]` guard, same button/label as before this plan.