# 第 3 批：刪除沒人用的功能（8 件＋補 M13 第 10 條 → 5 張卡）

## 這批一句話
這批不是修漏洞，是**把沒人用、卻可能被誤接上去變成漏洞的東西直接拆掉**——幾支「接好但零呼叫」的任務相關功能、一支混進出貨套件的開發小工具、一個沒人讀的死開關、一個全站成員名冊入口、一個兩個入口都進不去的快速設定畫面、外加一份主專案自己的快取連線複製程式碼。可以現在就開；跟其他批沒有依賴。

**本棒（開卡前座標補查）改了三件事，派工前要知道**：
1. **#9／#66／#67（舊版證據分類三件）移出本批** — 由既有卡 **CM-1849**（FR-107.6 舊線退場）處理，原 N-1 卡只剩 M13 快取複製品那半。
2. **#121（jedi-issue 全站成員名冊）的「零引用」只有一半成立** — 網址入口確實零呼叫，但**資料表與 model 被意見回饋的指派負責人路徑實際用著**。這是唯一一件「計畫說零引用、實際有引用」的，已升為 **D-b3-1** 等決策者裁（見卡 N-4 與文末）。
3. **#72「三支腳本」實數是五支**（這條在第 4 批，順帶記）。
其餘 #68／#69／#116／#117／#119／#120 六件的零引用查證**七面向全部跑過、全部成立**，座標已寫進各卡，runner 拿到可直接開工。

## 卡片清單
| 卡 | 卡名（做什麼，不寫代號） | repo／套件 | 涵蓋 SUMMARY # | 建議 model／effort | 序列組 |
|---|---|---|---|---|---|
| N-1 | BE 側：刪掉主專案自己那份快取連線複製品、改接套件 | BE 主專案 | M13 第 10 條（#9／#66／#67 已移除，由 CM-1849 處理） | sonnet／medium | A |
| N-2 | jedi-task-platform：拆掉三組零呼叫的任務管理功能 | jedi-task-platform 套件 | #68 #69 #120 | sonnet／medium | B |
| N-3 | jedi-common：刪開發小工具 ＋ 刪死開關 | jedi-common 套件 | #116 #117 | sonnet／medium | C |
| N-4 | jedi-issue：刪「送空查詢回全站成員名冊」那條入口（⚠️ 資料表那半待裁，見 D-b3-1） | jedi-issue 套件 | #121 | sonnet／medium | D |
| N-5 | FE：拆除「快速設定」獨立畫面兩個入口 | FE | #119 | sonnet／medium | E |

（五張卡動的是五個不同 repo／套件，彼此不衝突，可以平行派工；序列組只是標記同一張卡內部若有共用檔案的提醒，這批目前沒有跨卡撞檔。）

---

## 卡 N-1：BE 側：刪掉主專案自己那份快取連線複製品、改接套件
- **範圍**：BE 主專案 `~/Projects/Billows/Audit-Manager/compliance-manager-be`，worktree 建議 `wt-fix-b3-be`，只涵蓋 M13 第 10 條
- **#9／#66／#67（舊版證據分類三件）不在本卡**：由既有卡 **CM-1849**（FR-107.6 舊線退場＋收口）處理。本批不重複開卡、不重複做工。
- **每件的修法**（白話）：
  - **M13 第 10 條 快取連線複製品——主專案自己那份「連快取伺服器時加密連線不確認對方身分」的複製程式碼**
    - 修法：套件那份（現在在 jedi-iam）已修好（開加密就強制確認對方身分、還會驗機器名稱）；**刪掉主專案自己這份複製品，兩個呼叫端改接套件那份**。
    - 🔴 入口清單（已查證，file:line）：
      - **要刪的複製品**：`common/util/redis_client_util.py`（全檔 80 行，class `RedisClient`）——問題那行是 `:32` 的 `ssl_cert_reqs=False`（寫死不驗憑證，等同 `ssl.CERT_NONE`）
      - **套件那份（改接目標）**：`jedi-iam/jedi_iam/common/utils/redis_client_util.py` 的 `RedisClient`——`:42-47` 是修好的寫法（`ssl=True` 時強制 `ssl_cert_reqs="required"` ＋ `ssl_check_hostname=True`，另支援選填 `REDIS_SSL_CA_CERTS`）。套件已有回歸測試 `jedi-iam/tests/unittest/test_redis_client_util_tls.py`（`:43` 斷言 `ssl_cert_reqs == "required"`），修法與測試都現成可抄。
      - **呼叫端①（問卷）**：`infra/survey/adapters.py:29-31`（函式內 lazy import `from common.util.redis_client_util import RedisClient`，`:31` `return RedisClient()`）
      - **呼叫端②（AI 助手）**：`core/plugins/ai_bot.py:37-39`（同樣 lazy import，`:39` `self._redis = RedisClient()`）
      - 只有這兩個呼叫端；grep 指令 `grep -rn "redis_client_util\|RedisClient" --include="*.py" .`（BE），其餘命中都是文件／測試註解（`test/test_module_boundaries.py:400`、`config/config_util.py:4`、`docs/api/ai-chatbot/generate_docx.py:56,493`），不是真呼叫。
    - 🔴 **改接時會踩到的一個實質差異（runner 必讀）**：兩份的**設定來源不同**，不是直接換 import 就好。
      - 主專案那份（`common/util/redis_client_util.py:12-19`）是用 `ConfigUtils().config` 直接讀 `REDIS_HOST` / `REDIS_SECRET` / `REDIS_SSL` / `REDIS_USER`。
      - 套件那份（`jedi-iam/.../redis_client_util.py:18-26`）走 `jedi_iam.common.settings.get_setting()`（`ISettingsReader` port，由宿主注入），而且 `REDIS_SECRET` 是**當成 JSON 解析**（`json.loads`，取 `redis_user_name` / `redis_user_password` 兩個 key），與主專案那份把 `REDIS_SECRET` 當成純字串密碼、另有獨立 `REDIS_USER` 的形狀**不一樣**。
      - 所以要先確認宿主端 `ISettingsReader` 已接線且 `REDIS_SECRET` 的格式對得上（主專案的 reader 在 `common/iam_ports.py:32` 的 `EnvSettingsReader`，測試 `test/test_iam_wiring.py:78-84` 有驗它是 `ISettingsReader`）。**格式不對的症狀是「連不上但只寫 log 不拋錯」**（套件 `connect()` 的 except 只 `logger.error`），會變成靜默失效——問卷驗證碼與 AI 助手對話記錄讀不到但畫面不報錯。
      - 另外套件那份多了 `ttl()` / `incr()` 兩支方法、少了任何主專案獨有方法（逐一比對過：主專案那份的 `set/get/lpop_key/rpop_key/lpush/rpush/lrem/get_all_from_list/get_last_one_from_list/delete_key/ping` 套件全都有），所以**呼叫面沒有缺口**。
    - 功能不能壞：改接後問卷與 AI 助手兩個功能的快取讀寫（登入狀態、驗證碼、對話歷史）要能正常運作。現在加密未開啟（`REDIS_SSL` 非 true），行為不應該有變化；只是拿掉重複程式碼，確保之後開加密時只有一套邏輯生效。
- **這張卡的手測總清單**：
  - 問卷功能正常操作一輪（含會用到驗證碼／暫存的流程），確認改接套件版本後行為無異常
  - AI 助手功能正常操作一輪（對話歷史讀寫），確認無異常
  - 看 `log/app.log` 確認沒有 `redis connect` 失敗的 `logger.error`（靜默失效只會在 log 裡看到）
- **需決策者先裁的**：無（#9／#66／#67 已確定歸 CM-1849，原 D-b3-1 取消）

---

## 卡 N-2：jedi-task-platform：拆掉三組零呼叫的任務管理功能
- **範圍**：套件 monorepo `~/Projects/Jedicogy/module/jedi-python-package/jedi-task-platform`，worktree 建議 `wt-fix-b3-task-platform`，涵蓋 #68 #69 #120
- **每件的修法**（白話）：
  - **#68 群組層成員管理——新增／修改／刪除三支程式因系統啟動漏接一條線，整支完全不檢查權限**
    - 修法：**裁定刪除**新增、修改、刪除三個功能整段拿掉（全系統已確認零使用）；**查詢那支留著**給 AI 儀表板呼叫；**資料表保留**
    - 🔴 入口清單（已查證，file:line；檔案 `jedi-task-platform/jedi_task_platform/participant/app/service/project_group_participant_service.py`，全檔 137 行）：
      - **要刪**：`:62-87` `add_project_group_participant()`／`:90-114` `update_project_group_participant()`／`:117-131` `delete_project_group_participant()`
      - **要留**：`:25-29` `get_project_group_participants()`（AI 儀表板呼叫的就是這支）／`:36-59` 四支 menu / with_inherits 查詢／`:134-136` `delete_by_project_id()`（六層配套刪除，見 #120）
      - **「漏接的那條線」實體位置**：BE `di_containers/flow_engine/project_participant_containers.py:113-117`——`project_group_participant_service` 的 `providers.Factory` **只注入 `user_service` 與 `project_group_participant_domain_service`，沒有 `participant_role_service`**；對照同檔 `:77-85`（project）／`:87-95`（process）／`:97-103`（control_group）／`:105-111`（project_control）四支都有帶 `participant_role_service=participant_role_service`。這支就是唯一漏的那支。🔴 **拆除時不要順手把這行補上**——補上就變成「修 bug 卻改了行為」，裁定是刪不是補。
      - **AI 儀表板呼叫點（要確認不受影響）**：BE `di_containers/dashboard_apis/participant.py:96-107`（`api_key="participant.get_project_group_participants"`，`method="get_project_group_participants"`，provider 指向 `project_participant_container` 的 `project_group_participant_service`）
    - 零引用查證（七面向，已逐項跑過）：
      - ① **套件內**：`cd ~/Projects/Jedicogy/module/jedi-python-package && grep -rn "add_project_group_participant\|update_project_group_participant\|delete_project_group_participant" --include="*.py" . | grep -v /.venv/ | grep -v /.claude/worktrees/` → **各 1 命中，就是定義本身**（零呼叫）
      - ② **主專案**：同樣三個方法名在 BE `grep -rn ... --include="*.py" .` → **0 命中**
      - ③ **FE**：`grep -rn "project_group_participant" ~/Projects/Billows/Audit-Manager/compliance-manager-fe/src/` → 0 命中
      - ④ **DI 容器**：BE `grep -rn "project_group_participant" di_containers/` → 只有 `project_participant_containers.py:113-117`（建 service）與 `dashboard_apis/participant.py:96-107`（申報查詢那支），**沒有任何地方申報或呼叫寫入三支**
      - ⑤ **RLS policy／view／trigger**：`grep -n "project_group_participants" scripts/init/02-schema.sql` → 表結構與 policy 在（資料表保留，不動）
      - ⑥ **出貨基線**：同上，資料表保留故不需異動
      - ⑦ **migration**：`grep -rn "project_group_participant" scripts/sql/` → 只有建表／改表，無呼叫面
      - 另有 M10 第 7 條裁定欄原文「全系統已確認零使用」佐證
    - 功能不能壞：AI 儀表板的「群組成員查詢」要照常回資料；DI 那支 Factory 的參數列不要動（只刪 service 內三支方法，Factory 保持現狀）
  - **#69 流程參與者管理——一整組從沒啟用過的功能，把關檢查寫在永遠跑不到的位置**
    - 修法：**裁定刪除**新增、修改、刪除三個功能整段拿掉；**資料表與回傳格式的定義要留著**（別的地方在用）
    - 🔴 入口清單（已查證，file:line；檔案 `jedi-task-platform/jedi_task_platform/participant/app/service/process_participant_service.py`，全檔 170 行）：
      - **要刪**：`:60-80` `add_process_participant()`／`:83-101` `update_process_participant()`／`:104-124` `delete_process_participant()`
      - **連帶要刪**：`:49-57` `_assert_manager_for_process()`——這就是「把關檢查寫在永遠跑不到的位置」那支（`:55-56` 查不到 record 就 `return` 提前結束，真正的 `assert_project_manager` 在 `:57` 永遠跑不到）。它只被上面 update／delete 兩支呼叫（`:90`、`:111`），三支刪完它變孤兒，一起刪。
      - **要留**：`:45-47` `get_process_participants()`（AI 儀表板在用，見下）／`:127-136` `get_process_participant_menus()`／`:139-165` `get_process_participants_with_inherits()`／`:168-170` `delete_by_project_id()`
      - **「回傳格式的定義」實體**：`participant/app/dto/process_participant_dto.py` 的 `ProcessParticipantDTO`——BE `api/flow_engine/serializers/flow_engine/workflow_execution.py:6` 直接 import `jedi_task_platform.participant.api.serializers.process_participant.ProcessParticipantResponse`，**這兩個都不能刪**
      - **AI 儀表板呼叫點**：BE `di_containers/dashboard_apis/participant.py:31-40`（`method="get_process_participants"`）
      - **另一個要留的呼叫端**：BE `di_containers/flow_engine/workflow_excution_containers.py:142` 把 `process_participant_service` 注入 workflow execution service（注入的是整個 service 實例，刪三支方法不影響注入本身——但要確認 workflow execution service 沒呼叫被刪的三支，見下 ②）
    - 零引用查證（七面向）：
      - ① **套件內**：`grep -rn "add_process_participant\|update_process_participant\|delete_process_participant" --include="*.py" .`（排除 .venv／worktrees）→ **各 1 命中，定義本身**（零呼叫）
      - ② **主專案**：同三個方法名 BE 全 repo `grep -rn ... --include="*.py" .` → **0 命中**（含 `workflow_execution_service.py`，該注入只用查詢面）
      - ③ **FE**：`grep -rn "process_participant" ~/…/compliance-manager-fe/src/` → 0 命中
      - ④ **DI 容器**：只有 `project_participant_containers.py:87-95` 建 service、`workflow_excution_containers.py:142` 注入、`dashboard_apis/participant.py:31-40` 申報查詢那支
      - ⑤ **RLS／view／trigger**：`grep -n "process_participants" scripts/init/02-schema.sql` → 表與 policy 在（保留）
      - ⑥ **出貨基線**：資料表保留，不需異動
      - ⑦ **migration**：`grep -rn "process_participant" scripts/sql/` → 只有建表／改表
      - 另有 M10 第 8 條裁定原文「一整組從沒啟用過」＋「資料表在開發環境是 0 筆」佐證
    - 功能不能壞：`ProcessParticipantDTO` 與 `ProcessParticipantResponse` 兩個定義不可動（BE serializer 直接 import）；流程執行相關畫面（workflow execution）要照常
  - **#120 四支任務指派相關功能——沒有對外入口、也沒有任何把關**
    - 修法：**裁定刪除其中三支**（其中一支是被新版取代後留下的舊版，不是「還沒接」）；**第四支「依專案刪掉所有指派」留著**——其他五層都有同名功能，它是配套用的
    - 🔴 入口清單（已查證，file:line；檔案 `jedi-task-platform/jedi_task_platform/participant/app/service/task_assignee_service.py`，全檔 262 行）：
      - **要刪的三支**：`:74-91` `get_task_assignee_menu()`／`:94-132` `get_task_assignees_with_inherits()`／`:135-137` `get_user_task_queue()`
      - **要留的第四支**：`:260-262` `delete_by_project_id()`——**確認過這就是「依專案刪掉所有指派」**，且六層（不是五層，實數六支）都有同名方法：`project_participant_service.py:174`／`process_participant_service.py:168`／`project_group_participant_service.py:134`／`project_control_participant_service.py:219`／`control_group_participant_service.py:207`／`task_assignee_service.py:260`
      - **哪一支是「被新版取代後留下的舊版」**：`get_user_task_queue()`（`:135-137`）。新版在主專案 `app/readmodel/service/my_jobs_app_service.py:47-48` → `infra/readmodel/tasks/my_grc_jobs_query.py:70` 的 `get_user_task_queue_by_sp()`，該檔 `:71` 註解明寫舊 SP `public.get_user_task_queue()` 是 v1 版、其 join 的表已在 FR-038 Wave 2A 移除。**套件那支是舊版殘留。**
      - **連帶孤兒**：`get_user_task_queue()` 刪掉後，`participant/domain/service/task_assignee_domain_service.py:49-50` 與 `participant/domain/repository/task_assignee.py:29`、`participant/infra/repository/task_assignee_repo_impl.py:25` 的同名下層方法也成孤兒（那條鏈只被這支呼叫）——**要一起刪，不然留下的是從 domain 到 infra 的死鏈**
      - **不要誤刪**：同檔 `:69-72` `get_task_assignees()`（route 在用，見 `participant/api/routes/task_assignee_route.py:50`）／`:140-173` `add_task_assignee()`（route `:66`）／`:175-191` `batch_add_task_assignees()`（route `:27`）／`:193-232` `update_task_assignee()`（route `:81`）／`:234-258` `delete_task_assignee()`（route `:102`）——這五支都有對外 route，全部保留
    - 零引用查證（七面向）：
      - ① **套件內**：`grep -rn "get_task_assignee_menu\|get_task_assignees_with_inherits\|get_user_task_queue" --include="*.py" .`（排 .venv／worktrees）→ `get_task_assignee_menu` **1 命中（定義）**、`get_task_assignees_with_inherits` **1 命中（定義）**、`get_user_task_queue` **5 命中但全在自己那條 service→domain→repo 死鏈內**（`task_assignee_service.py:135,136`／`domain/service/task_assignee_domain_service.py:49,50`／`domain/repository/task_assignee.py:29`／`infra/repository/task_assignee_repo_impl.py:25`），鏈外零呼叫
      - ② **主專案**：BE `grep -rn "get_task_assignee_menu\|get_task_assignees_with_inherits" --include="*.py" .` → **只有 1 個文件命中** `docs/api/participant/generate_docx.py:876`（文件產生器裡的字串，不是呼叫）；`get_user_task_queue` 在 BE 的命中**全屬新版 `get_user_task_queue_by_sp` 那條**（`app/readmodel/service/my_jobs_app_service.py:47-48`、`infra/readmodel/tasks/my_grc_jobs_query.py:70,82`、`app/flow_control/dto/job_dto.py:281` 註解），**不是套件那支**
      - ③ **FE**：`grep -rn "task_assignee_menu\|assignees_with_inherits\|user_task_queue" ~/…/compliance-manager-fe/src/` → 0 命中
      - ④ **DI 容器**：`di_containers/dashboard_apis/participant.py:12` 申報的是 `task_assignee_container` 的 service 實例（申報的方法不含這三支）
      - ⑤ **RLS／view／trigger**：`grep -n "task_assignees" scripts/init/02-schema.sql` → 表與 policy 在（表保留）
      - ⑥ **出貨基線**：表保留，不需異動。⚠️ **但舊 SP `public.get_user_task_queue()` 若還在基線裡，屬另一件事**（FR-038 Wave 2B 已換 view），本卡不動 SP、只動 Python 側
      - ⑦ **migration**：`grep -rn "get_user_task_queue" scripts/sql/` → 有 FR-038 系列的 SP／view 改動紀錄，Python 側無關
    - 功能不能壞：五支有 route 的任務指派功能（查清單／單筆指派／批次指派／修改／刪除）要照常；「我的任務」清單走的是主專案新版 `get_user_task_queue_by_sp`，不受影響；六層 `delete_by_project_id()` 的專案刪除連動要照常
- **這張卡的手測總清單**（批次驗：一次跑完再一起修，不要逐項）：
  - AI 儀表板叫出「群組成員」與「流程參與者」兩個查詢（對應保留的 `get_project_group_participants` 與 `get_process_participants`），確認仍正常回資料
  - 專案任務指派五支有 route 的功能各操作一次（查清單／單筆指派／批次指派／修改／刪除）
  - 「我的任務」清單頁正常（驗 `get_user_task_queue_by_sp` 新版路徑沒被連帶弄壞）
  - 流程執行（workflow execution）相關畫面正常（驗 `ProcessParticipantResponse` serializer 沒被誤刪）
  - 刪一個測試專案，確認六層 `delete_by_project_id()` 連動刪除照常
  - BE 起得來並跑一次 `pytest test/test_task_assignee_batch.py test/test_fr112_parallel_join_wait.py`（這兩支會 import participant service，import 斷裂第一時間會炸）
- **需決策者先裁的**：無（三件裁定都已明確）

---

## 卡 N-3：jedi-common：刪開發小工具 ＋ 刪死開關
- **範圍**：套件 monorepo `~/Projects/Jedicogy/module/jedi-python-package/jedi-common`，worktree 建議 `wt-fix-b3-common`，涵蓋 #116 #117
- **每件的修法**（白話）：
  - **#116 一支開發用小工具混在正式出貨套件裡**
    - 修法：**直接刪掉**（已確認五個程式庫全部零引用）
    - 🔴 入口清單（已查證，file:line）：
      - **要刪的檔**：`jedi-common/jedi_common/utils/gen_comment.py`（全檔 106 行）——就是它，檔頭 `:1-9` docstring 自己寫「開發輔助腳本：呼叫 OpenAI 幫既有程式碼產 docstring」＋「⚠️ **不是產品程式碼**，套件執行期沒有任何地方 import 它」
      - **連帶要清的選配依賴**：`jedi-common/pyproject.toml:29` 的註解（「openai 是選配：只有 utils/gen_comment.py 開發輔助腳本用」）＋ `:31` 的 `ai = ["openai (>=2.8.0,<3.0.0)"]` extra——**這個 extra 只為這支而存在，一起刪**（M04 報告「更有價值的一點」那段講的就是這個：刪工具連帶省下為它長出來的一圈包裝）
      - **連帶要更新的文件**：`jedi-common/README.md:66`（那一列在講 openai 改選配的理由，工具刪了這列失效）
      - ⚠️ **報告提到「打包時有人特地寫了程式把它排除掉」**——`jedi-common/pyproject.toml` 內未見排除段（只有 extra 宣告），排除邏輯可能在 BE `scripts/build/` 或 Nuitka 設定，runner 開工時 `grep -rn "gen_comment\|openai" ~/…/compliance-manager-be/scripts/build/` 確認一次；查無就只清套件側三處。
    - 零引用查證（七面向，已逐項跑過）：
      - ① **套件 monorepo（21 支）**：`cd ~/Projects/Jedicogy/module/jedi-python-package && grep -rn "gen_comment" --include="*.py" --include="*.toml" --include="*.md" --include="*.cfg" . | grep -v /.venv/ | grep -v /.claude/worktrees/` → **3 命中，全在 jedi-common 自己**（`README.md:66` 文件、`pyproject.toml:29` 註解、`gen_comment.py:30` 自己的錯誤訊息字串）。**零 import。**
      - ② **主專案**：BE `grep -rn "gen_comment" . --include="*.py" --include="*.toml" --include="*.sh"`（排 conversation-history）→ **0 命中**
      - ③ **FE**：`grep -rn "gen_comment" ~/…/compliance-manager-fe/src` → 0 命中
      - ④ **測試 repo**：`grep -rn "gen_comment" ~/…/compliance-manager-test` → 0 命中
      - ⑤ **授權中心**：`grep -rn "gen_comment" ~/…/license_center` → 0 命中
      - ⑥ **可執行指令（console_scripts）**：`grep -n "scripts\]" jedi-common/pyproject.toml` → 未宣告任何 entry point，沒被設成可執行指令
      - ⑦ **RLS／migration／出貨基線**：不適用（純 Python 檔、不碰 DB）
    - 功能不能壞：純刪檔＋刪選配依賴宣告，無執行期路徑。刪完 `poetry install` / path dependency 要能正常解析（`ai` extra 消失後，任何寫 `jedi-common[ai]` 的地方會失敗——已查證無人這樣寫）
  - **#117 一個每次連線都會被設定、但全系統沒有任何地方讀它的開關**
    - 修法：**裁定刪除**（留著會讓後人誤以為已有部門層級檢查、反而不去補真正需要的）
    - 🔴 入口清單（已查證，file:line）：
      - **那個開關是 `app.can_manage_orgs`**，設定它的程式在 `jedi-common/jedi_common/session/database/db.py:185-187`：
        ```
        session.execute(
            text(f"SET LOCAL app.can_manage_orgs = 't'")
        )
        ```
        位於 `session_scope()` 內「有身分」那一支，**無條件執行**（每個登入者每次連線都被設成 `'t'`）。這三行就是要刪的全部。
      - **旁邊那個「活的」正面對照**（報告提到的）：同檔 `:81-86` 的 `set_can_read_all_orgs()` 設 `app.can_read_all_orgs`——**它是被明確呼叫時才設**（BE `app/flow_engine/service/workflow_execution_service.py:584/709/905/1176` 四處呼叫），而且 `scripts/init/02-schema.sql` 有 5 條 policy 在讀它（`:24730`／`:25072`／`:25174`／`:25700`／`:25944`）。**正確形狀就在旁邊，照它的樣子做。**
      - **要一起改的文件（否則文件會描述一個不存在的東西）**——已查證共 5 份 md（報告說 4 份，實數 5）：
        `docs/claude/database-schema.md:1024`（「`app.can_manage_orgs` — org management flag」）／`docs/system-design/database/RLS_DESIGN.md`／`docs/system-design/scripts/generate_db_schema_docx.py:2069` 附近（DOCX 產生器的變數表，**這支是程式不是 md，改它才會反映到產出的文件**）／`docs/交付文件/v1.8.0/src/doc-03-database/07-rls.md`／另有 FR-080 盤點檔 `docs/features/FR-080-2609-jedi-consolidation-and-service-path/inventory/host.md:244`（歷史盤點紀錄，**屬證據類、不要改**）。
        ⚠️ **FR-085／security-scan-consolidated 那幾份是掃描報告（歷史實況），一律不動。**
    - 零引用查證（七面向，已逐項跑過）：
      - ① **套件 monorepo**：`grep -rn "can_manage_orgs" --include="*.py" . | grep -v /.venv/ | grep -v /.claude/worktrees/` → **1 命中，就是 `db.py:186` 設定它那行**（零讀取）
      - ② **主專案程式碼**：BE `grep -rn "can_manage_orgs" . --include="*.py"` → **0 命中**
      - ③ **FE**：`grep -rn "can_manage_orgs" ~/…/compliance-manager-fe/src/` → **0 命中**
      - ④ **DI 容器**：不適用（非 service）
      - ⑤ **RLS policy／view／trigger**：`grep -c "can_manage_orgs" scripts/init/02-schema.sql` → **0**（對照 `can_read_all_orgs` 有 5 條）
      - ⑥ **出貨基線**：同⑤，`scripts/init/` 全域 `grep -rn "can_manage_orgs" scripts/` → **0 命中**（新裝客戶不會有）
      - ⑦ **migration**：同⑥，`scripts/sql/` 零命中
      - ⚠️ **報告已寫明的限制**：只查過 DEV 的 policy。**其他環境若有人手動加過規則要各自再數一次**（唯讀查詢，屬驗收項）。出貨基線是 0，新裝客戶不會有。
    - 功能不能壞：刪這三行不改變任何行為（沒有東西讀它）。**唯一的風險是誤刪隔壁 `:81-86` 的 `set_can_read_all_orgs`／或誤刪 `:185-187` 上下方的 `app.user_id`（`:181-184`）與 `app.is_super_admin`（`:196-199`）**——那兩個是活的、有 policy 在讀，刪到就是全庫權限炸掉。逐行比對、不要批次取代。
- **這張卡的手測總清單**（批次驗）：
  - 套件安裝流程（`poetry install` / path dependency 開發模式）正常，確認拿掉小工具與 `ai` extra 後 import 與相依解析沒斷裂
  - BE 起得來（`python main.py`）並登入一次——`db.py` 動到的是 `session_scope()`，**任何 DB 存取都經過它**，起不來或登入後查詢全空就是刪錯行
  - 隨便開兩個列表頁（要看得到資料的），確認 RLS 沒被弄壞（誤刪 `app.user_id` 或 `app.is_super_admin` 的症狀就是「資料全空」或「看到別家客戶的」）
  - 跑一次 `pytest test/test_module_boundaries.py test/test_iam_wiring.py`（碰到 session／套件邊界那兩支）確認沒壞
- **需決策者先裁的**：無（兩件裁定都已明確）

---

## 卡 N-4：jedi-issue：刪「送空查詢回全站成員名冊」那條入口（⚠️ 資料表那半待裁，見 D-b3-1）
- **範圍**：套件 monorepo `~/Projects/Jedicogy/module/jedi-python-package/jedi-issue`，worktree 建議 `wt-fix-b3-issue`，涵蓋 #121
- **每件的修法**（白話）：
  - **#121 任何登入帳號送空白查詢就把整張全站成員名冊（含電子郵件）撈回來**
    - 修法：**已裁定整塊移除**：入口、資料表、同步程式一起拿掉，不補隔離、不留著
    - 🔴 入口清單（已查證，file:line；套件 `jedi-issue`）：
      - **網址入口**：`GET /api/1.0/issue/get_members` → route `jedi_issue/api/routes/issue_member_route.py:11-15`（`IssueMemberRoute.get()`，`:12` 只有 `@auth_required`＝只驗登入）
      - **掛載點**：`jedi_issue/api/routing.py:19`（import）＋ `:24`（`api.add_resource(IssueMemberRoute, "/issue/get_members")`）＋ `:36`（`"/api/1.0/issue/get_members"` 那份網址清單）
      - **主專案側掛載**：BE `core/plugins/issue.py:186-188`（`register(app, …, mount_api=True)`）——**這是整包掛載，不是逐條**，所以主專案不需改；另 `core/plugins/issue.py:176` 與 `config/app_modules.py:90` 兩處**註解**提到這條網址，刪完要順手更新
      - **service**：`jedi_issue/app/member/service/member_service.py:13-21` `get_members()`（就是「送空白查詢回整張表」那支）
      - **DI 接線**：`jedi_issue/plugin/assembly.py:58-59`（`_member_service()` 工廠）＋ `:82`（`member_service=given.member_service or _member_service`）＋ `jedi_issue/plugin/contract.py:75`（`member_service` 欄位宣告）＋ `jedi_issue/plugin/runtime.py:35`／`:48` 兩處註解
      - **repo／mapper／model／entity／domain service**：`jedi_issue/infra/member/repository/member_repostiory.py`（`:23` 就是 `.query(Members).all()` 回全表）／`jedi_issue/infra/member/mapper/member_mapper.py`／`jedi_issue/infra/member/models/member.py:6-19`（`Members` model，`:16` 是那個 email 欄）／`jedi_issue/domain/member/{repository,service,entities}/`／`jedi_issue/app/member/dto/member_dto.py`
      - **資料表**：`public.members`（出貨基線 `scripts/init/02-schema.sql:14136` 的 `CREATE TABLE public.members`）
    - 🔴🔴 **重大：材料說「零引用」，實際有引用——這件不能照「整塊移除」直接做**（本棒查證推翻前一棒結論，開卡務必寫進去）：
      - **`Members` model 與 `members` 表被「意見回饋」那條活路徑用著**，不是死程式：
        - `jedi_issue/infra/issue/repository/member_repository.py:7-17` `get_members_by_username()` → 被 `jedi_issue/infra/issue/adapter/local/local_issue_adapter.py:161` 呼叫（建 issue 時把 assignees 名字換成 member 列）
        - `jedi_issue/infra/issue/repository/issue_assignee_repository.py:9/22/28/39-40/46` 全部 `join(Members, …)` 或吃 `list[Members]`
        - `jedi_issue/infra/issue/adapter/local/local_issue_adapter.py:90/103/131-132` 讀 assignee 的 username 靠這張表
        - `jedi_issue/infra/issue/adapter/issue_details.py:13` 的 `assignees: list[Members]` 型別宣告
        - 而 **Local provider 是活的**：`jedi_issue/infra/issue/adapter/__init__.py:18` `register_issue_adapter("Local", LocalIssueAdapter)`，`jedi_issue/app/feedback/service/feedback_issue_service.py` 有十幾處 `provider=IssueProviderCode.LOCAL`（`:83/:88/:102/:157/:208`…）——**意見回饋功能走的就是 Local**
        - 另有 `public.issue_assignee_mapping` 表（基線 `scripts/init/02-schema.sql:13674`）以 `member_uid` 外參 `members.uid`
      - **DEV 實查（2026-09-21）**：`public.members` **0 筆**、`public.issue_assignee_mapping` **0 筆**——所以「現在沒資料」是真的，但**那是因為意見回饋目前沒人指派負責人，不是因為程式沒接**。`members` 表現在是空的、將來一有人用「指派負責人」就會有資料。
      - **因此本卡要拆成兩半，而且第二半要先裁**：
        - **可以直接做的**：刪掉 `/issue/get_members` 這條**外洩入口**（route `issue_member_route.py`、routing 兩處、`MemberService.get_members()`、DI 三處接線、`MemberRepoImpl.get_members()` 那支回全表的查詢）——這半是真的零呼叫（見下零引用查證），刪掉不影響意見回饋。
        - **不可以直接做的**：**刪 `members` 資料表、刪 `Members` model、刪「同步程式」（`create_members`／`update_member`／`delete_member`）**——刪了意見回饋的「指派負責人」整條會炸（`local_issue_adapter.py:161` 會 `AttributeError`／SQL 找不到表）。M21 第 2 條的「整塊移除（入口、資料表、同步程式一起拿掉）」這個裁定**是建立在「全是死程式」這個前提上的，而該前提本棒查證不成立**。
    - 零引用查證（七面向，針對「入口那半」）：
      - ① **套件內**：`cd ~/…/jedi-python-package && grep -rn "get_members\b" --include="*.py" . | grep -v /.venv/ | grep -v /.claude/worktrees/ | grep -v /tests/` → 命中只有 `issue_member_route.py:14`（呼叫）＋`member_service.py:13`（定義）＋`member_domain_service` 對應一支；**`get_members_by_username` 是不同方法、不要混**
      - ② **主專案**：BE `grep -rn "get_members\|IssueMemberRoute" --include="*.py" .`（排 conversation-history）→ **只有註解與文件產生器**（`core/plugins/issue.py:176`、`config/app_modules.py:90`、`docs/api/issue/generate_docx.py:40/281/286/299/335/348/351`），**無程式呼叫**
      - ③ **FE**：`grep -rn "get_members" ~/…/compliance-manager-fe/src/` → **0 命中**（前端沒有任何地方打這條網址；`docs/api/issue/generate_docx.py:348` 那句「此端點主要供前端 Issue 指派功能使用」是過期文件，實查不成立）
      - ④ **DI 容器**：BE 側無獨立申報（整包 `register()` 掛載）；套件側接線三處已於上方列出，屬「要一起刪」不是「有人在用」
      - ⑤ **RLS policy／view／trigger**：`grep -n "public.members\|ON public.members" scripts/init/02-schema.sql` → **該表零 policy、零 tenant 欄位**（正是這條的病灶）
      - ⑥ **出貨基線**：`scripts/init/02-schema.sql:14136` 有建表，`:13674` 有 `issue_assignee_mapping`——**兩張都在基線裡，動它們就要重產基線**
      - ⑦ **migration**：`grep -rn "members" scripts/sql/` 與 `jedi_issue/plugin/migrations.py`（後者零命中）→ 無獨立 migration，表是隨基線長出來的
- **這張卡的手測總清單**（批次驗）：
  - `GET /api/1.0/issue/get_members` 回 404（路由完全消失，不是「還在但改回錯誤」）
  - **意見回饋整條回歸測一輪**（這是本卡真正的風險面）：送一則回饋、指派負責人、看列表、看單筆、改一次、關閉一次——確認 `members` 相關程式碼還在、功能不炸
  - BE 起得來並跑一次套件既有測試 `pytest tests/unittest/test_member_service.py tests/integration/test_service_behaviour.py`（在 jedi-issue repo 內）——⚠️ **這兩支測的就是被刪的方法，刪完必然要同步刪對應測試案例**，不是「跑過就好」
- **需決策者先裁的**：
  - **D-b3-1（新增，取代原 D-b3-1）**：M21 第 2 條裁「整塊移除（入口＋資料表＋同步程式）」，但本棒查證發現 **`members` 表與 `Members` model 被意見回饋的「指派負責人」路徑實際使用中**（`local_issue_adapter.py:161`、`issue_assignee_repository.py` 多處 join、Local provider 是活的；DEV 兩張表 0 筆只是因為目前沒人指派）。所以：**這張卡只拆「送空查詢回全表」那個外洩入口（可直接做），資料表與同步程式先留著**，還是要連帶把意見回饋的指派負責人功能一起改掉／停用（範圍大得多、要另開卡）？
    我的建議：**本卡只拆入口**（入口拆掉洞就補上了——外洩面是那條網址，不是那張表），資料表與 model 保留給意見回饋用；至於「`members` 表沒有客戶歸屬欄位、零隔離」這件另案處理（掃描總表 §3.1 第 7 項已登記 jedi-issue 五張表零隔離，本來就要走加欄位＋補 policy 的 migration，與本卡的「刪死程式」性質不同）。這樣本卡不必動出貨基線、不必重產基線。

---

## 卡 N-5：FE：拆除「快速設定」獨立畫面兩個入口
- **範圍**：FE `~/Projects/Billows/Audit-Manager/compliance-manager-fe`，worktree 建議 `wt-fix-b3-fe-quicksetup`，涵蓋 #119
- **每件的修法**（白話）：
  - **#119 獨立的「快速設定」畫面——按下去顯示成功但一筆都沒存，兩個入口網址都沒有任何按鈕導過去**
    - 修法：**決策者裁定整頁拆除，兩個入口都要拆**（一般版與稽核計畫版兩個入口網址）。系統裡另有一個「專案規劃頁」內的快速設定已改走新做法、會正常存，**那個不受影響、不要動**；只拆這個獨立的舊畫面
    - 🔴 入口清單（已查證，file:line；FE repo，注意**路由檔在 `src/config/router/index.js`，不是 `src/router/`**）：
      - **入口①（一般版）**：`src/config/router/index.js:911-915`
        - path `/project/projects/:id/task-setup`／name `project-task-setup`／component `@/views/project/TaskSetupView.vue`／meta breadcrumb parent `project-auditor-overview`
      - **入口②（稽核計畫版）**：`src/config/router/index.js:962-966`
        - path `/project/projects/:id/ap/:apUid/task-setup`／name `project-task-setup-ap`／**同一個 component** `@/views/project/TaskSetupView.vue`／meta breadcrumb parent `project-auditor-overview-ap`
        - 🔴 **兩個入口共用同一支元件**，所以「刪元件」只能在兩條路由都拆掉之後做——**只拆一條就會讓另一條 500**（比照 memory `feedback_parallel_runners_two_entrypoints_one_fixed`，兩個入口要列成兩個獨立驗收項、對稱檢查）
      - **要刪的畫面元件**：`src/views/project/TaskSetupView.vue`（**1,421 行**，已超 800 行上限——刪掉正好減負，不需拆檔）
      - **連帶要清的 i18n（四份，兩語系×兩類）**：
        - `src/config/locales/i18n/zh-tw/menu.json:108`（`project-task-setup`）、`:113`（`project-task-setup-ap`）、`:239`、`:243`（兩條 description）
        - `src/config/locales/i18n/en/menu.json:108`、`:113`、`:239`、`:243`（同上四處）
        - `src/config/locales/i18n/zh-tw/task-setup.json`（整檔 162 行，**專屬此頁**）＋ `src/config/locales/i18n/en/task-setup.json`
        - `src/config/locales/index.js:34`（`import enTaskSetup from '@/config/locales/i18n/en/task-setup.json'`）＋ `:114`（`twTaskSetup`）——**刪 json 要一併刪這兩行 import 與下方註冊處**，不然 build 直接炸
      - **要留的兩處字串（只是提到、不是入口）**：
        - `src/views/project/ProjectPlanningView.vue:281` 註解「任務資料（同 TaskSetupView）」——**這頁是要保留的新做法**（`:371`／`:1744`／`:1789`／`:2585` 是它自己的快速配置實作），刪 TaskSetupView 後把這句註解改掉即可
        - `src/views/workflow/WorkflowSetupEditor.vue:13` 註解列了「五支活頁面用程式開它（TaskSetupView / ProjectPlanningView / …）」——**刪完要把 TaskSetupView 從這句移除**，否則下一個人照註解找不到檔
      - **另一處疑似（runner 開工先確認）**：`src/config/locales/i18n/zh-tw/task-setup.json:146` 有 `"btn_quick_config": "快速配置"`，而 `src/views/project/TaskSetupView.vue:340` 有註解「目前登入者有權限配置的 AO 數量（用於快速配置 hint）」——確認這個 key 只被 TaskSetupView 用（`grep -rn "btn_quick_config" src/`），若 ProjectPlanningView 也在用就不能連 json 整檔刪、要留那一個 key
    - 零引用查證（FE 面向，已跑過）：
      - **路由名零引用**：`grep -rn "project-task-setup" src/ | grep -v "src/config/router/index.js"` → **8 命中全在 i18n menu.json**（zh-tw 與 en 各 4 處），**沒有任何 `router.push` / `<router-link>` / 選單設定指向它**——證實材料說的「兩個入口都沒有任何按鈕或連結導過去」
      - **元件零引用**：`grep -rn "TaskSetupView" src/ | grep -v "src/config/router/index.js"` → **2 命中皆為註解**（`ProjectPlanningView.vue:281`、`WorkflowSetupEditor.vue:13`）
      - **path 字面零引用**：`grep -rn "task-setup" src/ | grep -v "src/config/router/index.js"` → 只有上述 i18n 與 `locales/index.js` 的 import 兩行
      - **BE 側無關**：這頁的「按了不存」是因為 BE 那條已固定回空，本卡不動 BE（該 BE 端點的處置屬另案）
    - 功能不能壞：「專案規劃頁」（`ProjectPlanningView.vue`）內走新做法的快速配置**完全不受影響、不要動**（`:371`／`:1744`／`:1789`／`:2585` 四段是它自己的實作）；刪 i18n json 一定要同步刪 `locales/index.js` 的 import 與註冊，否則 `npm run build` 直接失敗
- **這張卡的手測總清單**（批次驗，兩個入口對稱列）：
  - **入口①**：手動輸入 `/project/projects/<id>/task-setup`，確認畫面已完全移除（路由不存在，不是空白頁或錯誤畫面）
  - **入口②**：手動輸入 `/project/projects/<id>/ap/<apUid>/task-setup`，確認同上
  - `npm run build` 通過（i18n import 沒刪乾淨會在這裡炸）
  - 切英文語系再跑一次上面兩個入口（驗 en menu.json 也清了）
  - 「專案規劃頁」內的快速配置正常操作一輪、能正常存檔（回歸，不能被誤刪）
  - 流程設定編輯器（`WorkflowSetupEditor`）能從其餘四支活頁面正常開啟（驗註解改動沒動到程式）
- **需決策者先裁的**：無（裁定已明確，兩個入口都要拆）

---

## 決策者要裁的事（彙整本批的 D）
- **D-b3-1（本棒查證後新增，取代原本那條）**：**jedi-issue 的「全站成員名冊」要不要照原裁定把資料表與同步程式一起刪？**
  M21 第 2 條裁「整塊移除（入口＋資料表＋同步程式）」，前提是「全是死程式」。**本棒開檔查證發現這個前提只有一半成立**：
  - 「送空白查詢回整張名冊」那條**網址入口**確實零呼叫（BE／FE／套件三面向都查了，只有註解與過期文件提到它）——**這半可以直接刪**。
  - 但 **`members` 資料表與 `Members` model 被「意見回饋」的「指派負責人」路徑實際用著**（`local_issue_adapter.py:161` 拿 username 換 member 列、`issue_assignee_repository.py` 多處 join、意見回饋走的 Local provider 是活的）。**DEV 那兩張表 0 筆，是因為目前沒人用指派負責人，不是因為程式沒接。**
  我的建議：**本卡只拆入口，資料表與同步程式保留**（洞在那條網址、不在那張表；拆掉入口洞就補上了）。這樣也不必動出貨基線、不必重產。至於「`members` 表沒有客戶歸屬欄位、零隔離」那件，掃描總表 §3.1 第 7 項已另有登記（jedi-issue 五張表零隔離，要走加欄位＋補 policy 的 migration），性質是「補隔離」不是「刪死程式」，與本卡分開。
  - 若決策者裁「連表一起刪」，則本卡要擴大到「意見回饋的指派負責人功能一併停用」，範圍跨 BE＋套件＋FE＋出貨基線重產，**不適合留在這批**，建議另開卡。
- **另一件要註記的**：原計畫的 #9／#66／#67（舊版證據分類三件）已確定**由既有卡 CM-1849 處理**，本批不開卡、不重複做工；5B-5 驗證卡改成「等 CM-1849 做完再驗」。
