# Bug P / Q / R — Parties Preview Form 3 個 Bug Implementation Plan

> **接手 SOP 註記**：plan mode 限制只能寫此 `~/.claude/plans/cached-marinating-puzzle.md`。
> ExitPlanMode + user approve 後，**Phase Z 第一步**是把本 plan copy 到 CLAUDE.md 規範位置
> `docs/features/FR-028-2605-ssp-oscal-alignment/implementation-plan-bug-pqr.md`。

---

## Context — 為何要做

Bug O 收尾 E2E 測試時 user 發現 3 個跟 Bug O scope 無關的 bug，全部集中在 **docx import preview UI 的負責單位 / 參與人員 sections**。Bug P/Q/R 是 H-N arc 第 11~13 個 follow-up，**本期 P/Q/R 是 H-N arc「user-facing complete」前的最後幾步** — 完成後 docx import update mode 整段才算 v1 stable。

3 bug 摘要：

| Bug | 症狀 | Root Cause | 影響範圍 |
|---|---|---|---|
| **P** | 對 organization party 點「連結帳號」會跳 Error，且 button label / dialog 應該是「連結組織單位」非「連結帳號」 | **(a) wording**：`PartiesSection.vue` org row 跟 person row 用同個 i18n key `party_link_button`=「連結帳號」<br>**(b) Error**：root cause 尚未抓到 console，**Phase 0.2 必須先 reproduce** | FE only |
| **Q** | Person party 缺「角色」下拉 | `PartiesSection.vue` 自 commit `47e9489` 起只有 read-only `ROLE_LABEL`，**從來沒有過 dropdown**。template-edit 端 `ModuleFramePartiesPanel.vue:438-446` 才有 Dropdown + `menuStore.sspPartyRoleMenu`（9 role keys） | FE only |
| **R** | Confirm 匯入後回 template-edit 看 parties 還是舊資料 | BE 確實有寫（log + `import_summary.parties_written=5` 雙重 verify）；`useModuleFrameParties.js` **module-scope 永久 cache 直到 invalidate**（2026-05-21 故意改）；`SspDocxImportPage.vue:649-691` confirm 完只 reset 自己 store + `router.push`，**從沒 call `invalidate()`** → user 回 template-edit page，`ModuleFramePartiesPanel.onMounted → loadParties` cache hit 拿到 stale data | FE only |

**已驗證可重用的既有資源**（不要 reinvent）：
- `menuStore.sspPartyRoleMenu` (9 OSCAL role keys) + `roleLabel(key)` 函式 → Bug Q 重用
- `useModuleFrameParties().invalidate(mfUid)` → Bug R 重用
- `useSspParties` (推測類似 composable，apiBase='ssp' 時 ModuleFramePartiesPanel 也適用) → Bug R 第二 case

---

## Goal

**FE-only 修補 3 個 bug，不動 BE / jedi-\* 套件**，使 docx import preview UI 的 parties section 跟 template-edit page parties tab 完整對齊，user confirm 匯入後 template-edit page 立刻看到新資料。

## Architecture / 設計取捨

1. **Bug R**：選 **「confirm 成功後在 SspDocxImportPage 端 explicit invalidate」**，不選「ModuleFramePartiesPanel.onMounted 永遠 force=true」
   - **理由**：永遠 force 會讓既有 cache 機制（5s TTL 改永久的設計意圖）失效，且每次切 tab 都重打 API。confirm 是 invalidation 的單一觸發源（CRUD 已有 invalidate per 本 panel），confirm path 補上即對齊。
2. **Bug Q**：選 **「PartiesSection 補 Dropdown 重用 menuStore master」**，不選「PartiesSection 內 hardcode role enum」
   - **理由**：避免兩處 enum 走樣（C8 已清過 manager/auditor/viewer），sspPartyRoleMenu 是 SSOT。
3. **Bug P**：選 **「PartiesSection button label 按 party_type 分流 i18n key」**，wording 跟 dialog title 對齊（dialog 已對齊 ✓，只剩 button）。Error 則先 reproduce 再針對抓到的 console error 修補。

## Tech Stack

- Vue 3 (Composition API) + Pinia + Vue Router
- PrimeVue 3.53 (Dropdown / Button / Tag / Dialog) — 注意 `Dropdown` 對 `value: null` 有 bug（per memory），role 預設可不選但 sentinel 處理（本期 role 必選，無此問題）
- vue-i18n（zh-tw + en）

---

## File Structure — 改動範圍

| 檔案 | 動作 | 為何 |
|---|---|---|
| `compliance-manager-fe/src/components/grc/ssp-docx-import-v2/sections/PartiesSection.vue` | Modify | Bug P button label 分流；Bug Q 補 role Dropdown；hardcoded `ROLE_LABEL` 5 entry 改用 `menuStore.sspPartyRoleMenu` + `roleLabel(key)` |
| `compliance-manager-fe/src/components/grc/ssp-docx-import-v2/SspDocxImportPage.vue` | Modify | Bug R: confirm 成功後 (line 660 附近) call `useModuleFrameParties().invalidate(targetUid)`；若 update-ssp mode 也要 call SSP context invalidate（探 `useSspParties` 是否存在） |
| `compliance-manager-fe/src/components/grc/ssp-docx-import-v2/sections/PartyLinkDialog.vue` | **可能 Modify**（Bug P Error 修補要看 Phase 0.2 reproduce 結果） | 待 reproduce 看 console 才能 finalize 是否要動 |
| `compliance-manager-fe/src/config/locales/i18n/zh-tw/ssp-docx-import.json` | Modify | 新增 `party_link_button_org`, `party_link_button_user`, `party_relink_button_org`, `party_relink_button_user` 4 個 wording 分流 key（若決定不分而是動態組 label 則 skip）|
| `compliance-manager-fe/src/config/locales/i18n/en/ssp-docx-import.json` | Modify | 同 zh-tw |
| `compliance-manager-be/docs/features/FR-028-2605-ssp-oscal-alignment/design.md` | Modify | Phase Z 收尾：加 §11.35 / §11.36 / §11.37 段；index 加 3 條 |
| `compliance-manager-be/docs/changelog/2026-05-25-fix-bug-pqr-parties-form.md` | Create | Phase Z 收尾 changelog（type=fix，FE only） |
| `compliance-manager-be/docs/features/FR-028-2605-ssp-oscal-alignment/handoff/2026-05-25-bug-pqr-FIXED-SUMMARY.md` | Create | Phase Z 收尾 SUMMARY |
| 原 handoff `2026-05-25-bug-p-q-r-parties-form-handoff.md` | Modify | Phase Z 收尾加 ✓ FIXED block on top |

**不會動的檔案**（明確排除）：
- 所有 BE app/oscal/service/ 下檔案（BE write path 已 verify OK）
- jedi-\* 套件
- DB migration（無 schema 變更）

---

## Phase 0：Reproduce Bug P Error + Final root cause confirm

**目的**：Bug P 的「跳 Error」尚未抓到 console error 內容，無法寫精準 fix 程式碼。本 phase 用 playwright MCP reproduce + 抓 console / network。

### Task 0.1：Reproduce Bug P

- [ ] Step 1：用 playwright MCP 開 `/login`，blsadmin / Billows@123!
- [ ] Step 2：navigate 到 `/module-frame/2c4ba0b8-52e2-4e90-a8fc-df8dcf481002/template-edit` (job 171 之 MF — 已知有 5 party docx import 過)
- [ ] Step 3：點 批次維護 → 從文件匯入 → 重新上傳 docx → 等 parse → Step 3 預覽
- [ ] Step 4：切到「參與人員與單位」tab → 找 organization row → 點「連結帳號」按鈕
- [ ] Step 5：抓 `browser_console_messages` + `browser_network_requests`，看 Error 在哪
- [ ] Step 6：把 console / network log 記下來，**決定 Bug P fix scope**：
  - 若是 ORG_UNITS API 401/403 → 權限問題 → 排查 endpoint 是否需要特定 role
  - 若是 ORG_UNITS API 500 → BE 問題 → 升級為 BE bug（超出本 plan，先標 follow-up）
  - 若是 FE TypeError → 多半是 watch race condition 或 onSelect item.id undefined → 加 guard
  - 若 dialog 開不起來 → PartyLinkDialog props.party 為 null → 查 openLinkDialog 傳值

### Task 0.2：Final Bug R verify（已驗 BE OK，再驗 FE cache 真會 hit stale）

- [ ] Step 1：在 playwright Step 4 之前，先離開 template-edit page 進 docx import；改個 party name → confirm；router push 回 template-edit
- [ ] Step 2：在 template-edit 「責任單位」tab 看顯示的 party name — 預期：**stale 舊名稱**（reproduce Bug R）
- [ ] Step 3：手動 reload page → 再看 → 預期：**新名稱出現**（證明 BE 有寫、cache 是問題）
- [ ] Step 4：若 reload 後還是 stale → 升級為「BE 寫的不是 user 看的那張表」 follow-up（極小機率）

### Task 0.3：探 `useSspParties` composable 是否存在

- [ ] Step 1：`grep -rn "useSspParties\|useSSPParties" ~/Projects/Billows/Audit-Manager/compliance-manager-fe/src/`
- [ ] Step 2：若存在，Bug R fix 在 update-ssp mode 也要 call 它 `invalidate(sspUid)`
- [ ] Step 3：若不存在（apiBase='ssp' 時用主頁 stale fetch），標 follow-up，本期 Bug R 只 cover MF context

**Checkpoint**：Phase 0 完，Bug P 真實 root cause 應確認；若 Bug P 升級為 BE bug 或超出 FE scope，回頭跟 user 確認是否拆分本 plan。

---

## Phase A：Bug R fix — Confirm 後 invalidate parties cache

### Task A.1：在 `SspDocxImportPage.vue` import + call invalidate

**Files:**
- Modify: `compliance-manager-fe/src/components/grc/ssp-docx-import-v2/SspDocxImportPage.vue:649-691`（confirm fn）+ 上方 import 區

- [ ] **Step 1：read SspDocxImportPage.vue 上方 import 區確認 import 位置**

- [ ] **Step 2：加 import**

```js
import { useModuleFrameParties } from '@/composables/useModuleFrameParties'
// 若 Phase 0.3 確認 useSspParties 存在則一併 import:
// import { useSspParties } from '@/composables/useSspParties'
```

- [ ] **Step 3：在 confirm fn 內，line 660 `draftRef.clear()` 之後加 invalidate call**

```js
const res = await SspDocxImportService.confirm(parseUid.value, payload)
const data = res?.data?.data ?? res?.data ?? res ?? {}
draftRef.clear(parseUid.value)

// Bug R fix (2026-05-25): confirm 寫完 parties → invalidate template-edit
// 端 ModuleFramePartiesPanel 用的永久 cache，否則 router.push 回 template-edit
// 後 loadParties cache hit 拿到 stale data。詳見 design.md §11.37。
const { invalidate: invalidateMfParties } = useModuleFrameParties()
if (isCreate.value || props.mode === 'update-mf') {
    const targetMfUid = effectiveTargetUid || data.module_frame_uid || data.source_uid || props.targetUid
    if (targetMfUid) invalidateMfParties(targetMfUid)
}
// 若 Phase 0.3 useSspParties 存在:
// if (props.mode === 'update-ssp') {
//     const { invalidate: invalidateSspParties } = useSspParties()
//     invalidateSspParties(props.targetUid)
// }

// 既有邏輯 line 662 onwards
parseUid.value = null
parsedResult.value = null
// ...
```

- [ ] **Step 4：手動 verify — 重跑 Phase 0.2 reproduce 流程**

預期：confirm 完 router push 回 template-edit page，**parties tab 立刻顯示新名稱不需 reload**

- [ ] **Step 5：commit**

```bash
cd ~/Projects/Billows/Audit-Manager/compliance-manager-fe
git add src/components/grc/ssp-docx-import-v2/SspDocxImportPage.vue
git commit -m "fix(ssp-oscal-alignment): Bug R — confirm 後 invalidate ModuleFrame parties cache

BE 寫入 parties OK 但 useModuleFrameParties 是 module-scope 永久 cache
（2026-05-21 故意改），SspDocxImportPage.confirm 完 router.push 回
template-edit 後 ModuleFramePartiesPanel.onMounted → loadParties cache
hit 拿到 stale data。fix：confirm 成功後 explicit call invalidate(mfUid)。

詳見 design.md §11.37。"
```

---

## Phase B：Bug Q fix — PartiesSection 補 role Dropdown

### Task B.1：在 `PartiesSection.vue` 補 role Dropdown + 對齊 menuStore master

**Files:**
- Modify: `compliance-manager-fe/src/components/grc/ssp-docx-import-v2/sections/PartiesSection.vue` 整個 script + template

- [ ] **Step 1：刪除 hardcoded `ROLE_LABEL`（line 35-41）**

- [ ] **Step 2：在 `<script setup>` 加 menuStore + role label fn**

```js
import { storeToRefs } from 'pinia'
import { useMenuStore } from '@/stores/menuStore'
import Dropdown from 'primevue/dropdown'
import { onMounted, computed } from 'vue'

const menuStore = useMenuStore()
const { sspPartyRoleMenu } = storeToRefs(menuStore)

// 沿用 ModuleFramePartiesPanel.vue:101-105 同樣 pattern
function roleLabel(key) {
    if (!key) return '—'
    const i18nKey = `lang.oscal_role.ssp_party_role.${key}.label`
    const v = t(i18nKey)
    return v === i18nKey ? key : v
}

const roleOptions = computed(() =>
    sspPartyRoleMenu.value.map((m) => ({
        label: roleLabel(m.key),
        value: m.key,
    })),
)

onMounted(() => {
    menuStore.fetchSspPartyRoleMenu()  // 已有 cache + 並發合併，多 mount 共用同份
})
```

- [ ] **Step 3：template 把 org row + person row 內的 `<span class="font-semibold mr-2">{{ ROLE_LABEL[p.role] || p.role || '—' }}</span>` 改成兩段**

對 org row（line 130-134 區段）：
```vue
<div class="flex align-items-center gap-2">
    <!-- 顯示用 label（保留現值時 read-only）-->
    <span v-if="isPartyReadonly(p.party_uid)" class="font-semibold">
        {{ roleLabel(p.role) }}
    </span>
    <!-- 編輯模式：Dropdown -->
    <Dropdown
        v-else
        :model-value="p.role"
        :options="roleOptions"
        option-label="label"
        option-value="value"
        placeholder="（選擇角色）"
        class="ssp-party-role-dropdown"
        @update:model-value="(v) => onFieldEdit(idx, 'role', v)"
    />
    <Tag :severity="statusSeverity(p)" :value="statusLabel(p)" />
    <Tag v-if="isPartyReadonly(p.party_uid)" severity="secondary" value="保留現值" />
</div>
```

對 person row（line 194-198 區段）做同樣改動。

- [ ] **Step 4：加 dropdown style（在 `<style scoped>` 內）**

```css
.ssp-party-role-dropdown {
    min-width: 160px;
}
.ssp-party-role-dropdown :deep(.p-dropdown-label) {
    font-weight: 600;
}
```

- [ ] **Step 5：手動 verify — playwright reload Step 3 預覽**

預期：
- person row 跟 org row 都有 role Dropdown（unless 保留現值）
- 改 role → 切去其他 tab → 切回來 → role 保持改後值（onFieldEdit 走 props.update emit）
- 9 個 role keys 全顯示（不只原本 5 個 hardcoded）

- [ ] **Step 6：commit**

```bash
git add src/components/grc/ssp-docx-import-v2/sections/PartiesSection.vue
git commit -m "fix(ssp-oscal-alignment): Bug Q — PartiesSection 補 role Dropdown

docx import preview 端 PartiesSection 自 commit 47e9489 就只有 read-only
ROLE_LABEL，從來沒有過 Dropdown。template-edit 端 ModuleFramePartiesPanel
早就有完整 Dropdown + menuStore.sspPartyRoleMenu (9 role keys)。

fix：preview 端也補上 Dropdown，重用同個 menuStore master 避免雙處
enum 走樣 (per Bug O 教訓「FE store 三處同步」)。read-only 模式
（保留現值 / keep_current）仍顯示 read-only label。

詳見 design.md §11.36。"
```

---

## Phase C：Bug P fix — Button wording 分流 + 視 Phase 0.2 reproduce 結果加 Error 修補

### Task C.1：i18n 新增 org / user 分流 button label

**Files:**
- Modify: `compliance-manager-fe/src/config/locales/i18n/zh-tw/ssp-docx-import.json`
- Modify: `compliance-manager-fe/src/config/locales/i18n/en/ssp-docx-import.json`

- [ ] **Step 1：在 zh-tw 加新 i18n key**

```json
"party_link_button_user": "連結帳號",
"party_link_button_org": "連結組織單位",
"party_relink_button_user": "重新連結帳號",
"party_relink_button_org": "重新連結組織單位"
```

保留既有 `party_link_button` / `party_relink_button` 不刪（其他元件可能 reference；本期 PartiesSection 改 ref 新 key）。

- [ ] **Step 2：en/ssp-docx-import.json 加對應 key**（英文 wording per 既有風格）

### Task C.2：PartiesSection 對 party_type 分流 button label

**Files:**
- Modify: `compliance-manager-fe/src/components/grc/ssp-docx-import-v2/sections/PartiesSection.vue` org row line 145-151 + person row line 209-215

- [ ] **Step 1：org row button**

```vue
<Button
    size="small"
    outlined
    :label="isLinked(p)
        ? t('lang.ssp_docx_import.party_relink_button_org')
        : t('lang.ssp_docx_import.party_link_button_org')"
    icon="pi pi-building"
    @click="openLinkDialog(p, idx)"
/>
```

- [ ] **Step 2：person row button**

```vue
<Button
    size="small"
    outlined
    :label="isLinked(p)
        ? t('lang.ssp_docx_import.party_relink_button_user')
        : t('lang.ssp_docx_import.party_link_button_user')"
    icon="pi pi-user"
    @click="openLinkDialog(p, idx)"
/>
```

- [ ] **Step 3：手動 verify**

預期：
- org row button 顯示「連結組織單位」/「重新連結組織單位」+ 建築 icon
- person row button 顯示「連結帳號」/「重新連結帳號」+ 人形 icon

### Task C.3：視 Phase 0.2 reproduce 結果加 Error 修補

**待 Phase 0.2 抓到 console / network log 後再 finalize**。可能的修補方向：

- 若是 PartyLinkDialog `onSelect(item)` 在 org 情境 `item.id` undefined → 加 guard `if (!item?.id) { toast.warn('選項缺 id'); return }`
- 若是 watch race condition → 改 immediate + nextTick
- 若是 ORG_UNITS API 401 → 升級為 BE follow-up

- [ ] **Step 1（pending Phase 0.2 結果）：在此補 Error 修補 step**

### Task C.4：commit

- [ ] **Step 1：commit**

```bash
git add src/config/locales/i18n/zh-tw/ssp-docx-import.json \
        src/config/locales/i18n/en/ssp-docx-import.json \
        src/components/grc/ssp-docx-import-v2/sections/PartiesSection.vue \
        # 若 Phase C.3 有改 PartyLinkDialog 一併 add
git commit -m "fix(ssp-oscal-alignment): Bug P — PartiesSection link button 對 party_type 分流 wording

PartiesSection 對 organization 跟 person 共用同個 'party_link_button'
i18n key (=「連結帳號」)，wording 對 org 不對。dialog title 已有
party_link_dialog_title_org / _user 分流，button label 跟著對齊。

新增 4 個 i18n key (party_link_button_user/_org/relink_*)，
保留舊 key 不刪（其他元件可能 reference）。icon 也分（建築 / 人形）。

（若 Phase C.3 有 Error 修補，commit message 加第二段說明）

詳見 design.md §11.35。"
```

---

## Phase D：E2E Verify — Playwright 全 3 bug

### Task D.1：用 playwright MCP 跑完整流程

- [ ] **Step 1：reload FE（vite hot reload 自動，但先確認）**

- [ ] **Step 2：login → template-edit page → 批次維護 → 從文件匯入 → upload docx → 等 parse**

- [ ] **Step 3：Step 3 預覽 → 切「參與人員與單位」tab，逐項 verify**
  - **Bug Q ✓**：person row 看到 role Dropdown，9 個 role keys 全顯示
  - **Bug Q ✓**：org row 也看到 role Dropdown
  - **Bug P (wording) ✓**：org row button 顯示「連結組織單位」+ 建築 icon
  - **Bug P (wording) ✓**：person row button 顯示「連結帳號」+ 人形 icon
  - **Bug P (Error) ✓**：點 org row 「連結組織單位」→ dialog 標題「連結到組織單位」，不再 Error
  - **Bug P (link OK) ✓**：選一個 org → linked 狀態變 success Tag

- [ ] **Step 4：改某 party name → 確認匯入 → router push 回 template-edit page**
  - **Bug R ✓**：「責任單位」/「參與人員」tab 立刻顯示新 name（不需 reload）

- [ ] **Step 5：截圖存 `bug-pqr-fixed-*.png`**（per `bug-o-e2e-*.png` pattern）

### Task D.2：BE pytest smoke（雖然 BE 沒動但 safety check）

- [ ] **Step 1：跑既有 BE pytest**

```bash
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
poetry run pytest tests/test_ssp_docx_import_app_service.py \
  tests/test_ssp_docx_diff_service.py \
  tests/test_ssp_write_strategy.py -q
```

預期：~210+ passed（跟 Pre-flight 一致）

---

## Phase Z：收尾文件（per CLAUDE.md「做 summary」段）

按 CLAUDE.md「做 summary」段 step 5 必跑「回頭更新所有相關文件」。

### Task Z.1：把本 plan copy 到正式位置

- [ ] Step 1：`mkdir -p docs/features/FR-028-2605-ssp-oscal-alignment/`（已存在）
- [ ] Step 2：`cp ~/.claude/plans/cached-marinating-puzzle.md docs/features/FR-028-2605-ssp-oscal-alignment/implementation-plan-bug-pqr.md`

### Task Z.2：design.md 加 §11.35 / §11.36 / §11.37

**Files:**
- Modify: `compliance-manager-be/docs/features/FR-028-2605-ssp-oscal-alignment/design.md`

- [ ] Step 1：在 §11 index 加 3 條
- [ ] Step 2：寫 §11.35 (Bug P — wording 分流 + Error 修補) / §11.36 (Bug Q — role Dropdown 補回 + 重用 menuStore master) / §11.37 (Bug R — cache invalidate)
  - 每段含：症狀 / root cause / 修補 / 教訓 / commit clause
  - 教訓重點：「dialog title 已分流但 button label 沒分流是橫向漏網」「永久 cache 設計必須伴隨『所有寫入 path 都 invalidate』的 SOP」「FE 兩處 UI 該用同 SSOT master，不該各自 hardcode」

### Task Z.3：changelog (type=fix, FE only)

**Files:**
- Create: `compliance-manager-be/docs/changelog/2026-05-25-fix-bug-pqr-parties-form.md`

frontmatter:
```yaml
---
type: fix
modules: [ssp-oscal-alignment, frontend-only]
issue: docs/features/FR-028-2605-ssp-oscal-alignment/handoff/2026-05-25-bug-p-q-r-parties-form-handoff.md
---
```

內容：3 bug 各一段（症狀 / fix / commit hash 待 commit 後回填）

### Task Z.4：FIXED-SUMMARY

**Files:**
- Create: `compliance-manager-be/docs/features/FR-028-2605-ssp-oscal-alignment/handoff/2026-05-25-bug-pqr-FIXED-SUMMARY.md`

對齊 `2026-05-25-bug-o-FIXED-SUMMARY.md` 樣式：
- 收尾日期 / branch / spec / plan / changelog / handoff (本期接手) 連結
- Commits 清單（FE only）
- 跨 repo 範圍表
- 行為差異一句話
- 規範文件齊全度 checkbox
- 已知 follow-up（若有，e.g. SSP context cache 沒 cover）
- 部署 handover（FE rebuild only，無 DB migration，BE 不需重啟）
- E2E 驗證紀錄（playwright screenshots 列檔）

### Task Z.5：原 handoff 標 ✓ FIXED

**Files:**
- Modify: `compliance-manager-be/docs/features/FR-028-2605-ssp-oscal-alignment/handoff/2026-05-25-bug-p-q-r-parties-form-handoff.md`

在標題後加 block：
```markdown
> ✓ FIXED — 2026-05-25 收尾，見
> [`2026-05-25-bug-pqr-FIXED-SUMMARY.md`](./2026-05-25-bug-pqr-FIXED-SUMMARY.md)
```

### Task Z.6：更新橫向文件（per CLAUDE.md「回頭更新文件」step）

- [ ] **memory feedback**：本 arc 至少 1 條教訓寫成 memory entry — 候選：
  - `feedback_permanent_cache_needs_invalidate_at_all_writers.md`：永久 cache 設計必須伴隨「每個寫入 path 都 invalidate」SOP，本期 confirm path 漏掉
  - 或 `feedback_button_label_align_dialog_title_per_type.md`：dialog title 已對 type 分流，但 button label 沒跟著分是常見漏網點
  - 加完 MEMORY.md index 同步加 1 行

- [ ] **`docs/claude/frontend-overview.md`**：本期沒新增重要元件 / API payload key / store 行為，**評估為不需動**（讓 Phase Z 報告明列「✗ 不需動，原因：...」）

- [ ] **`docs/api/<module>/api-spec.md`**：本期沒動 BE endpoint，**評估為不需動**

- [ ] **design.md §11 index**：Z.2 同 commit 順手更

### Task Z.7：commit + 對話歷史歸檔

- [ ] **Step 1：commit BE docs**（design.md / changelog / handoff / SUMMARY / 本 plan 副本）

```bash
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
git add docs/features/FR-028-2605-ssp-oscal-alignment/design.md \
        docs/features/FR-028-2605-ssp-oscal-alignment/implementation-plan-bug-pqr.md \
        docs/features/FR-028-2605-ssp-oscal-alignment/handoff/2026-05-25-bug-pqr-FIXED-SUMMARY.md \
        docs/features/FR-028-2605-ssp-oscal-alignment/handoff/2026-05-25-bug-p-q-r-parties-form-handoff.md \
        docs/changelog/2026-05-25-fix-bug-pqr-parties-form.md
git commit -m "docs(ssp-oscal-alignment): Bug P/Q/R 收尾 — plan + design §11.35-37 + changelog + FIXED SUMMARY"
```

- [ ] **Step 2：對話歷史歸檔**（per CLAUDE.md「Claude 主動從 JSONL extract」段）

```bash
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
python3 scripts/extract_claude_sessions.py --date 2026-05-25 --topic bug-pqr-parties-form --auto
wc -l docs/conversation-history/2026-05-25/bug-pqr-parties-form/*.md
git add docs/conversation-history/2026-05-25/bug-pqr-parties-form/
git commit -m "docs(conversation-history): 2026-05-25 — bug-pqr-parties-form session dump"
```

- [ ] **Step 3：問 user 是否 push（CLAUDE.md「push 永遠要 user 明確指示」）**

不自動 push。報告 push 規模（FE N commits + BE M commits）給 user 拍板。

---

## Verification — 端到端怎麼驗

| Check | 命令 / 動作 | 預期 |
|---|---|---|
| Bug P wording | playwright Step 3 預覽 → 看 org row button label | 顯示「連結組織單位」 |
| Bug P Error | 點 org row 「連結組織單位」 | dialog 開「連結到組織單位」標題，**無 console error** |
| Bug Q dropdown | playwright Step 3 預覽 → person/org row 看 role 欄 | 顯示 Dropdown（非保留現值時），9 個 role 全列 |
| Bug R cache | playwright 改 party name → confirm → router 回 template-edit | parties tab **不需 reload** 立刻顯示新 name |
| BE 不退化 | `poetry run pytest tests/test_ssp_docx_import_app_service.py tests/test_ssp_docx_diff_service.py tests/test_ssp_write_strategy.py -q` | 210+ passed |
| BE log 健全 | `tail -100 log/app.log \| grep ERROR` | 無新 ERROR |

---

## 行為規範重申（per CLAUDE.md + memory）

- ✗ 不切 branch — 兩 repo 永遠 `feature/ssp-oscal-alignment`
- ✗ 不自動 push — 收尾 Phase Z.7 報 push 規模給 user 拍板
- ✓ 跨 repo 改 FE 前已 read FE CLAUDE.md（接手 §0 第 6 點已 cover）
- ✓ 本期 **不改 BE service 層** → 不需要提醒 user 重啟 BE
- ✓ 改 FE → vite hot reload，不需 user 動作
- ✓ `git add` 顯式檔名（per memory `feedback_subagent_explicit_git_add.md`）— 每個 commit 都列檔
- ✓ 不晶晶體（per memory）— commit message 用中文動詞
- ✓ Plan 假設先 verify — Phase 0 reproduce Bug P Error 確認後才寫 Phase C.3 fix code
- ✓ 收尾 Phase Z 回頭更新橫向文件（per CLAUDE.md「做 summary」step 5）

## 不在本期 scope

- BE / jedi-\* 套件變更
- SSP context cache invalidate（若 Phase 0.3 verify 確認 `useSspParties` 不存在，標 follow-up）
- BE / FE 版號 bump（user 拍板才動）
- DB migration
- Bug O / H-6 / H-N 回頭 revisit（已 closed）

## 預估時間

| Phase | 時間 |
|---|---|
| Phase 0 (Reproduce + verify) | 30 分 |
| Phase A (Bug R) | 30 分 |
| Phase B (Bug Q) | 60 分 |
| Phase C (Bug P) | 60-90 分（依 C.3 結果） |
| Phase D (E2E verify) | 30 分 |
| Phase Z (收尾) | 60 分 |
| **總計** | **3.5-5 小時** |
