Bug H — Word 匯入 Parties Label 統一 + 欄位缺漏 Handoff(2026-05-25 晚)

項目 內容
緣由 User 手測亞航 docx 匯入後實際觀察 4 個 FE 缺漏:① label「責任單位」/「單位」/「人員」/「責任人員」雜亂未統一、② 預覽 + template-edit 兩畫面 org party 缺地址 / 電話、③ 同上 person party 缺地址 / 電話。BE 端已有完整資料(parsed_result.parties JSONB 含 address / telephone_number / title),純 FE label 對齊 + 欄位 render 補齊。
Branch feature/ssp-oscal-alignment(兩 repo 一致;BE local 領先 origin 4 commits + FE 1 commit 待 push)
主要 file FE 兩 vue(PartiesSection.vue + ModuleFramePartiesPanel.vue)+ 2 個 i18n locale(zh-tw + en)
接手前必讀 本文件自包含 — 按 §0 順序讀本檔 + reference 文件即可
預估時間 1.5-2 小時(FE 改 2 元件 + 4 個 i18n key 更新 + 跨 repo Read FE CLAUDE.md + Playwright 驗證 + 文件)

0. 接手讀序(fresh session 必照順序)

  1. 本文件 — Bug H 完整 context(4 個子 bug / FE 元件改動範圍 / 驗證 / 收尾)
  2. docs/features/FR-028-2605-ssp-oscal-alignment/handoff/2026-05-25-bug-d-FIXED-SUMMARY.md — Bug D + E + F 完整 fix(前一輪歷史;本 Bug H 是 user 手測過程發現的後續 FE 缺漏,跟 Bug D 同個 user-facing 區塊 (party tab))
  3. ~/Projects/Billows/Audit-Manager/compliance-manager-fe/CLAUDE.md — 跨 repo 改 FE 必讀(per memory feedback_cross_repo_read_claude_md_first
  4. docs/features/FR-028-2605-ssp-oscal-alignment/ssp-import-flow-reference.html §3.2 (確認階段) + §6.4 (cross-source 不一致教訓) — 整體 import flow + party fields 在 BE 寫入位置
  5. compliance-manager-fe/src/components/grc/ssp-docx-import-v2/sections/PartiesSection.vue — 預覽 (/module-frame/import-docx) 元件,行 100~270
  6. compliance-manager-fe/src/components/grc/ModuleFramePartiesPanel.vue — 編輯 (/module-frame/<uid>/template-edit) 元件,DataTable + Dialog 全段
  7. compliance-manager-fe/src/views/module_frame/ModuleFrameTemplateEditView.vue 行 1985~2011 — template-edit 兩 party tab 注入點
  8. compliance-manager-fe/src/config/locales/i18n/{zh-tw,en}/{ssp-docx-import.json,module-frame.json} — i18n label 字典

讀完後跑 §6 Pre-flight + §7 Verify Bug D close → §3 Verify 各 bug 對應檔案行號 → §4 開工順位。


1. 症狀(4 個子 bug 完整列表)

Bug H-1:「責任單位」/「單位」label 不一致,統一改成「負責單位」

畫面 目前 label i18n key 來源 file:line
Word 文件內欄位(user 提到) 責任單位 (純 docx 內文) (對應 BE party_type='organization')
Preview Section title 單位 lang.ssp_docx_import.parties_organization_section_title ssp-docx-import.json:165
Preview Role label 責任單位 (硬寫在 vue 內 ROLE_LABEL map) PartiesSection.vue:36
Template-edit Tab header 責任單位 ({count}) lang.module_frame.tab_responsible_org module-frame.json:5
Template-edit DataTable Column header 單位名稱 (硬寫) ModuleFramePartiesPanel.vue:356

期望:上述 5 處 user-facing label 全部改成「負責單位」(DataTable 該欄改「負責單位名稱」)。


Bug H-2:負責單位(org)缺地址 + 電話欄位

現況 — 預覽 (/module-frame/import-docx,元件 PartiesSection.vue org card line 154-163):

<div class="grid">
  <div class="col-12 md:col-6 mb-2">
    <label>{{ t('lang.ssp_docx_import.field_name') }}</label>
    <InputText :model-value="p.name || ''" .../>
  </div>
  <div class="col-12 md:col-6 mb-2">
    <label>{{ t('lang.ssp_docx_import.field_email') }}</label>
    <InputText :model-value="p.email_address || ''" .../>
  </div>
</div>

只有 name + email,缺 address / telephone_number。

現況 — Template-edit (/module-frame/<uid>/template-edit,元件 ModuleFramePartiesPanel.vue):

  • DataTable 行 349-391 只 render:name / role / email / 鉤稽 / 操作 — 缺 address / telephone
  • Edit Dialog 行 402-444 只有 matched_org_unit + role + name + email(email 又只 person 顯示)— org 缺 address / telephone input

BE 證據(parse_job 148 確認 BE 已有完整資料,user docx 也有抓到):

PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -A -t -c "
SET app.is_super_admin='t';
SELECT jsonb_pretty(parsed_result->'parties'->0) FROM oscal.ssp_docx_parse_jobs WHERE id=148;
"
# 預期 output 含完整欄位:
#   {"name": "屏東飛機維修廠", "address": "No. 330, Shengli Rd., ...",
#    "telephone_number": "08-7656111", "party_type": "organization", ...}

期望:兩畫面都加 address + telephone_number 欄位 render(InputText with placeholder + label)。


Bug H-3:「人員」/「責任人員」label 不一致,統一改成「參與人員」

畫面 目前 label i18n key 來源 file:line
Word 文件內欄位 參與人員 (純 docx 內文) (對應 BE party_type='person')
Preview Section title 人員 lang.ssp_docx_import.parties_person_section_title ssp-docx-import.json:167
Template-edit Tab header 責任人員 ({count}) lang.module_frame.tab_responsible_person module-frame.json:6
Template-edit DataTable Column header 姓名 (硬寫) ModuleFramePartiesPanel.vue:356

期望:Section title + Tab header 改成「參與人員」(DataTable column「姓名」可保留,是 person 內部欄位本來就叫姓名不是 person 整體 label)。


Bug H-4:參與人員(person)缺地址 + 電話欄位(職稱已有)

現況 — 預覽 (PartiesSection.vue person card line 210-222):

<div class="grid">
  <div class="col-12 md:col-6 mb-2"> <!-- 已有 -->
    <label>{{ t('lang.ssp_docx_import.field_name') }}</label>
    <InputText :model-value="p.name || ''" .../>
  </div>
  <div class="col-12 md:col-6 mb-2"> <!-- 已有 -->
    <label>{{ t('lang.ssp_docx_import.field_title') }}</label>
    <InputText :model-value="p.title || ''" .../>
  </div>
  <div class="col-12 md:col-6 mb-2"> <!-- 已有 -->
    <label>{{ t('lang.ssp_docx_import.field_email') }}</label>
    <InputText :model-value="p.email_address || ''" .../>
  </div>
</div>

→ 有 name + title + email,缺 address + telephone_number。

現況 — Template-edit:同 Bug H-2 — DataTable + Dialog 都缺。

期望:兩畫面都加 address + telephone_number 欄位 render。


2. 前次教訓(避免重蹈覆轍)

  1. Bug D + E + F 教訓:「parties_written 計數騙人」/「OSCAL 語意 vs Product 語意現實」/「跨 stack consistency check」— Bug H 也是 BE 寫完了但 FE 沒 render → user 視角 = 「沒寫」。修完必拿 Playwright 截圖驗。
  2. Bug B Issue 1 部分前例PartyDiffCard.vue(diff stepper,update mode 用)已修過缺電話/職稱/地址欄位(commit FE f31d0cf),但 create mode preview 元件 (PartiesSection.vue) 跟 template-edit 元件 (ModuleFramePartiesPanel.vue) 是不同 vue 檔,當時沒一起改。本 Bug H 補完三個元件的對齊。
  3. i18n key 改字串 ≠ i18n key 改 key 名:本期只改 zh-tw + en 的 value(label 字串),不動 key 名 — vue 端不用動 t() 呼叫。
  4. 跨 repo 改 FE 必先 read FE CLAUDE.md(per memory feedback_cross_repo_read_claude_md_first)— 直接動 vue 沒 read FE CLAUDE.md 容易踩 PrimeVue quirks(如 SelectButton unselectable)。

3. Root cause 候選

本 Bug H 不是 root cause 候選題,是「FE render + label 沒對齊 BE 已有資料」純 UI bug。Verify 點只有:

3.1 Verify A — 確認 BE response 真有完整欄位

# A1: 確認 docx parse_job parsed_result 有完整 party 欄位
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -A -t -c "
SET app.is_super_admin='t';
SELECT
  jsonb_array_elements(parsed_result->'parties') ->> 'name' AS name,
  jsonb_array_elements(parsed_result->'parties') ->> 'party_type' AS ptype,
  jsonb_array_elements(parsed_result->'parties') ->> 'address' AS address,
  jsonb_array_elements(parsed_result->'parties') ->> 'telephone_number' AS tel,
  jsonb_array_elements(parsed_result->'parties') ->> 'title' AS title
FROM oscal.ssp_docx_parse_jobs WHERE id=148;
"
# 預期 5 row 都填好 address / tel(org 也有),person 多 title

# A2: 確認 module-frame parties API response 含完整欄位
#(用前一輪 E2E mf 369 確認;BE 須在跑)
curl -s "http://localhost:8000/api/1.0/module-frame/f489a145-47da-4506-8675-5cf45a6f218b/parties" \
  -H "Authorization: Bearer <token>" \
  | jq '.data[] | {name, party_type, address, telephone_number, title}'
# 預期 row 含 address / telephone_number / title
# 若 API response 缺 → 表 BE entity → dict 轉換漏(需追 _to_dict)

3.2 Verify B — 確認 PartyContextService.list_parties 回傳含完整欄位

若 A2 發現 API response 缺,須追:

grep -nE "def list_parties\|def _to_dict\|address\|telephone" \
  /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/app/oscal/service/party_context_service.py 2>&1 | head -20

# 對應的 PartyEntity (jedi-oscal)
ls ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/domain/entity/base/oscal_party*

推測:BE list_parties API 應該已含完整欄位(前一輪 Bug B/D fix 時 BE entity 已 OK,preview 路徑用同個 PartyEntity)— 純 FE 沒 render。Verify A1 + A2 通過即直接動 FE。


4. 開工順位

  1. 跑 §6 Pre-flight + §7 Verify Bug D close(5-10 分鐘)
  2. 跑 §3.1 Verify A1 + A2(5 分鐘)— 確認 BE 真有完整資料
  3. 若 A2 BE API 缺 → 加做 Verify B + 補 BE _to_dict(少數情境,可能 +30 min)
  4. 改 FE(純改動,依序)
    • Step 1: i18n labels(兩 locale 4 個 key)— 同步改 zh-tw + en
    • Step 2: PartiesSection.vue org card 加 address + telephone input(line ~163 後 append 兩 <div>
    • Step 3: PartiesSection.vue person card 加 address + telephone input(line ~222 後 append 兩 <div>
    • Step 4: ModuleFramePartiesPanel.vue Edit Dialog 加 address + telephone input(line ~439 後 append;注意 org / person 都該顯示,不像 email 只 person)
    • Step 5: ModuleFramePartiesPanel.vue DataTable 加 address + telephone column(line ~390 之前 append 兩 <Column>;考慮 responsive — 可能要折疊或 truncate)
    • Step 6: ModuleFramePartiesPanel.vue DataTable Column header「單位名稱」改「負責單位名稱」(line ~356)
  5. i18n key 補 (若需):可能要新增 field_telephone key(已有 field_address/field_title,要查 field_telephone 是否已存)
  6. vite build pass + FE dev server restart 看實況
  7. Playwright 驗證(mirror Bug D E2E pattern):
    • .playwright-mcp/uploads/asia-airlines-cmmc-ssp.docx 跑完整 docx import flow(CMMC L1 / module_frame create mode)
    • 預覽頁切 parties tab,截圖確認「負責單位」+「參與人員」label + address / tel 欄位都顯示
    • confirm 後跳 template-edit,再切「負責單位」+「參與人員」tab,截圖確認 DataTable + Dialog 都顯示完整
    • console 0 errors / 0 warnings
  8. 補 design.md §11.26(mirror §11.25 寫法,含「為何 Bug B Issue 1 fix PartyDiffCard 沒順手改 PartiesSection / ModuleFramePartiesPanel」分析)
  9. commit + Bug H FIXED SUMMARY handoff(mirror 2026-05-25-bug-d-FIXED-SUMMARY.md
  10. 問 user push 規模(永不自動 push)

5. 該讀的檔案 / 預期改動範圍

5.1 FE 端(主要改動)

檔案 為何 read / 改
compliance-manager-fe/src/components/grc/ssp-docx-import-v2/sections/PartiesSection.vue (line 100-270) 預覽元件;org card line 154-163 加 2 input + person card line 210-222 加 2 input
compliance-manager-fe/src/components/grc/ModuleFramePartiesPanel.vue (line 340-450) 編輯元件;DataTable 加 2 column + Dialog 加 2 input + Column header 改「負責單位名稱」
compliance-manager-fe/src/views/module_frame/ModuleFrameTemplateEditView.vue (line 1985-2011) template-edit 兩 party tab 注入點 — 不用改(i18n key 改了 tab header 自動跟)
compliance-manager-fe/src/config/locales/i18n/zh-tw/ssp-docx-import.json 改 line 165 parties_organization_section_title「單位」→「負責單位」+ line 167 parties_person_section_title「人員」→「參與人員」;確認 field_telephone key 是否存在
compliance-manager-fe/src/config/locales/i18n/en/ssp-docx-import.json 對應 en:Organizations → Responsible Organizations? People → Participating Persons? 等 user 拍板 en label,或先改 zh-tw 不動 en
compliance-manager-fe/src/config/locales/i18n/zh-tw/module-frame.json 改 line 5 tab_responsible_org「責任單位 ({count})」→「負責單位 ({count})」+ line 6 tab_responsible_person「責任人員 ({count})」→「參與人員 ({count})」
compliance-manager-fe/src/config/locales/i18n/en/module-frame.json 對應 en(同上等拍板)

5.2 BE 端(僅 verify,多半不用動)

檔案 為何 read
app/oscal/service/party_context_service.py (list_parties / _to_dict) 確認 API response 含 address / telephone_number / title — 若缺則補
app/oscal/service/ssp_docx_import_app_service.py:_load_current_parties 預覽用同個 service 取 current parties,確認 list dict 完整
~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/domain/entity/base/oscal_party_entity.py 套件 PartyEntity 欄位定義(不該動,純 verify)

5.3 跨 repo 必做

  • 改 FE 前必 read ~/Projects/Billows/Audit-Manager/compliance-manager-fe/CLAUDE.md(per memory feedback_cross_repo_read_claude_md_first

6. Pre-flight Command(必跑)

# 1. 兩 repo branch
git -C ~/Projects/Billows/Audit-Manager/compliance-manager-be branch --show-current
git -C ~/Projects/Billows/Audit-Manager/compliance-manager-fe branch --show-current
# 預期:兩個都 feature/ssp-oscal-alignment

# 2. 兩 repo working tree
git -C ~/Projects/Billows/Audit-Manager/compliance-manager-be status --short
# 預期:M pyproject.toml only + 3 .png 在根目錄(artifact,不 commit)
git -C ~/Projects/Billows/Audit-Manager/compliance-manager-fe status --short
# 預期:clean

# 3. local vs origin(user 還沒 push 上一輪)
git -C ~/Projects/Billows/Audit-Manager/compliance-manager-be log origin/feature/ssp-oscal-alignment..HEAD --oneline
# 預期 4-5 commits(取決於 Bug G handoff swap 後狀態)
git -C ~/Projects/Billows/Audit-Manager/compliance-manager-fe log origin/feature/ssp-oscal-alignment..HEAD --oneline
# 預期 1 commit(Bug E + F)

# 4. BE listener
lsof -t -i:8000   # 若無 listener,重啟一次
# 重啟方式(避雷:zsh 對 .env 內 { } 會踩 parse error,per 前次教訓用 inline python):
# nohup python -c "
# import os, runpy, sys
# from pathlib import Path
# for line in Path('.env').read_text().splitlines():
#     line = line.strip()
#     if not line or line.startswith('#') or '=' not in line: continue
#     k, _, v = line.partition('=')
#     os.environ[k.strip()] = v.strip()
# sys.argv = ['main_app.py', 'run']
# runpy.run_path('main_app.py', run_name='__main__')
# " > /tmp/be_restart.log 2>&1 &

# 5. FE dev server
lsof -t -i:5180   # 預期已在跑(user dev server)

# 6. BE test smoke
poetry run pytest tests/test_ssp_docx_import_app_service.py tests/test_ssp_write_strategy.py \
  tests/test_cmmc_ssp_adapter_v3_bundle.py tests/test_ssp_excel_import_app_service.py -q
# 預期:~140 passed

7. Verify Bug D + E + F 確實 close(必跑,避免回頭追)

# 7.1 — Bug D DB 驗證:mf 369 (docx E2E) + mf 370 (Excel E2E) 都應有 4 LA
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -c "
SET app.is_super_admin='t';
SELECT mf.uid, mf.name, COUNT(la.id) AS la_count
  FROM public.module_frames mf
  JOIN oscal.profiles p ON p.uid::text = mf.oscal_profile_uid
  JOIN oscal.system_security_plans s ON s.profile_id = p.id
  LEFT JOIN oscal.ssp_leveraged_authorizations la ON la.ssp_id = s.id
 WHERE mf.uid IN ('f489a145-47da-4506-8675-5cf45a6f218b', 'bf6aed86-3b12-4df4-8c62-cc3bacae983f')
 GROUP BY mf.uid, mf.name;
"
# 預期 2 row 都 la_count=4

# 7.2 — Bug B link verify(mf 368 應 5 link)
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -c "
SET app.is_super_admin='t';
SELECT count(*) FROM oscal.oscal_responsible_parties
 WHERE context_type='module_frame' AND context_id=368;
"
# 預期 5

8. 行為規範重要提醒(per CLAUDE.md + memory)

  • 永不切 branch(per CLAUDE.md / feedback_no_branch_switch
  • 可自行 commit,不自動 push(per CLAUDE.md / feedback_stage_commit_no_ask
  • 跨 repo 改 FE 前必 read FE CLAUDE.md(per memory feedback_cross_repo_read_claude_md_first)— 本 Bug H 主要動 FE,必做
  • 改 BE service 層後必提醒 user 重啟(per memory feedback_be_restart_after_service_change)— 若 §3.2 觸發改 BE party_context_service 才需重啟;純 FE 不用
  • PrimeVue 3.53 quirks(per memory):本 Bug H 用 InputText / Column 不踩 Steps / SelectButton / Dropdown null bug,但仍要 vite build pass 確認沒型別錯
  • DB 兩張表都 verify 才算 done(§2 教訓)— 本 Bug H verify 用 Playwright 截圖 + DOM query 確認欄位 visible,不靠 console log
  • 不要把 bug 標 caveat 甩給 user(直面修,不迴避)
  • changelog 收尾 batch 寫(user 說「summary / 告一段落」才寫)
  • 不要晶晶體(per memory feedback_no_chinglish — 動詞 / 連接詞用中文)
  • plan 假設先 verify(per memory feedback_plan_vs_reality_verify_first)— 本 handoff §3 候選都已附 verify 命令
  • subagent dispatch 必加「git add 顯式檔名,禁用 -am」(per memory feedback_subagent_explicit_git_add
  • en locale label 要等 user 拍板或先只改 zh-tw — user 給的 4 個新 label 都是中文,沒指定 en 對應

9. 收尾流程(fix 完後)

  1. design.md §11.26 補 Bug H fix entry(mirror §11.25 寫法):
    • 症狀:4 子 bug label 雜亂 + 欄位缺
    • Root cause:跨 vue 元件 i18n 不同步 + Bug B Issue 1 只 fix PartyDiffCard 沒順手改 PartiesSection / ModuleFramePartiesPanel
    • Fix scope:純 FE 改 + i18n 改字串(不動 key 名)
    • 教訓:「3 元件對齊」必三檔同時改 — 列入未來新增 party 元件 checklist
  2. 更新 docs/features/FR-028-2605-ssp-oscal-alignment/ssp-import-flow-reference.html — 如有相關內容更新
  3. handoff/2026-05-25-bug-h-FIXED-SUMMARY.md 寫短收尾報告(mirror 2026-05-25-bug-d-FIXED-SUMMARY.md 樣式)
  4. commit(FE 5 檔 + BE design.md + handoff SUMMARY)
  5. 問 user 確認 push 規模(含本期 Bug H + 累積未 push 的 BE 4-5 commits + FE 1 commit + 本期 FE 1 commit)
  6. user 拍板才做 jedi 進版 / 版號對齊 / silent-skip pattern 全套件 audit 等 follow-ups

10. 不在 Bug H scope

  • 改 BE PartyEntity / oscal_parties schema(已有完整欄位,不用動)
  • 改 docx parser / adapter(前一輪 Bug B/D 已 fix;本期純 FE 對齊)
  • en locale 字串更新(等 user 拍板對應英文 label)— 可先 placeholder 用「Responsible Organizations」/「Participating Persons」
  • ssp-import-flow-reference.html §6.1 ~ §6.10 其他風險點(屬另一個 bug arc,可獨立 handoff)
  • Bug G init_ap_controls silent failure(已撤回 — 是靜態分析非實證 bug;user 拍板才修)
  • PartyDiffCard.vue(update mode diff stepper)— Bug B Issue 1 已修,本期不再動
  • 共用 <PartyFieldRow> 元件抽出 refactor(3 元件 render 同樣 5 欄位很適合抽,但 scope 太大,列 future cleanup)
  • Phase 5 OSCAL Export(已 deferred)
  • L8 jedi-oscal 進版 + Nexus push(user 拍板)
  • L9 BE/FE 版號對齊(user 拍板)

11. 前次 session(2026-05-25 整日)commits 清單

origin/feature/ssp-oscal-alignment 已包含(user 之前 push 過):

BE 已 push (4441d3f4 ~):
4441d3f4 docs(ssp-oscal-alignment): Bug B 收尾 SUMMARY + Bug D handoff
fef5eecf fix(ssp-oscal-alignment): Bug B 第二段 — docx _dict_to_parsed_parties 補 default role
ba7885d4 docs(ssp-oscal-alignment): design §11.22 + §11.23 — Bug B + Issue 1 reconciliation
...

FE 已 push (f31d0cf):
f31d0cf  fix(ssp-oscal-alignment): Bug B parties default decision + Issue 1 缺欄位 render

本期(2026-05-25 晚)BE local 領先(含本 handoff swap)

[本 commit] docs(ssp-oscal-alignment): swap Bug G handoff → Bug H (Word parties labels + fields)
38336bae   docs(ssp-oscal-alignment): Bug G handoff + HTML 參考 + CLAUDE.md 加交接模式 + design §11 索引更新
1406047b   chore(ssp-oscal-alignment): 加 E2E Excel fixture generator script
a2989c6e   docs(ssp-oscal-alignment): Bug D FIXED SUMMARY 補 §6.1 Bug E + F cascade fixes
20496b65   fix(ssp-oscal-alignment): Bug D — docx adapter T7 (Category) 也 emit LeveragedAuthorization
190254ba   docs(ssp-oscal-alignment): Bug D handoff 補完整 context  (上一輪已 push 但 origin 未到)

本期 FE local 領先 1 commit

c7d5791 fix(ssp-oscal-alignment): Bug E + F — DocumentPoolPanel TDZ + Bug B 引發的 i18n missing keys

狀態:BE 5 + FE 1 待 push(user 接手後拍板)。本期 swap commit 也算進去。


12. 給 fresh session 的超短 prompt(user 用這個貼)

請閱讀交接文件,了解現況,並接手繼續修正 bug。

交接文件路徑:
docs/features/FR-028-2605-ssp-oscal-alignment/handoff/2026-05-25-bug-h-word-parties-labels-fields-handoff.md

按本檔 §0 接手讀序執行 — 必跑 §6 Pre-flight + §7 Verify Bug D close →
§3 Verify BE 資料完整 → §4 開工順位。注意 §2 前次教訓 + §8 行為規範。
前次 (Bug B / D / E / F) 已全 close;Bug G (init_ap_controls silent failure)
已撤回 (是靜態分析非實證 bug,user 拍板才修)。本期純 FE label 對齊 + 欄位
render 補齊,BE 端資料已完整不需動。