# FR-069 收整G（CM-1508）：四類重佈局提案——現況 → 目標對照

- **日期**：2026-09-02
- **性質**：佈局設計稿（第一段產出，未經 user 點頭不搬任何檔）
- **canonical**：architecture-handbook `modularization-roadmap.md` §「產品本體最後剩什麼——四類」
- **branch**：`feature/FR-069.5`

---

## 30 秒版結論

盤完全樹後的誠實判斷：**四類實體在前面 A–F 各棒之後已大致就位**——櫃檯有 `infra/readmodel/`（D 棒剛分完子資料夾）＋`app/readmodel/`、接線盤散件已有「`infra/<模組>/adapters.py`／`app/<模組>/adapter/`」慣例落點、組裝根集中在 `core/`＋`config/`、業務加工就是 `app/<模組>/`。**不需要也不應該造新的頂層目錄樹**（理由見下節硬約束）。

本棒實際要做的是**三小批歸位＋清殘**：①刪四個零引用空殼目錄；②`detection_execution` 三層 shim 收尾（C 棒同款手法的殘留）；③櫃檯 app 層歸位一支（`auditor_dashboard_service`）。加上④文件同步。搬動總量約 **10 檔內**，對外契約零觸碰。

若 user 期望的是更大規模的「砍掉重擺」，那個方向我**明確反對**，理由寫在下面「為什麼不動頂層」節——請看完再裁。

---

## 硬約束（守衛與契約焊死的面，實查確認）

| 焊死的東西 | 誰焊的 | 含義 |
|---|---|---|
| 頂層八個 package 名 `api / app / domain / infra / di_containers / common / core / config` | `test_module_boundaries.py` 至少 5 處以此清單迭代掃描（死名守衛、反向 import 守衛等） | 改名／刪除任一個會讓多條守衛炸；**新增**頂層 package 則會落在守衛掃描範圍外＝盲區（arc-review 剛抓過「掃描根縮水」這病，不能再造一個） |
| `core/app_factory.py` 實體路徑 | `test_iam_wiring.py:28` 直接 `read_text` 這個路徑 | app_factory 不可搬不可改名 |
| `di_containers/auth/auth_containers.py` 路徑 | `test_iam_wiring.py:120` 同款 read_text | 不可搬 |
| `common/authz/` 六支殼 | `test_authz_subject_domain_files_are_shims_only` 等逐檔斷言 | CM-1506 保留名單，不碰 |
| `common/code/flow_control_error_code.py` | `test_grc_error_code_string_values_frozen`（讀實體路徑） | 不可搬 |
| `api/<name>/__init__.py` 的 `create_module()`＋目錄名＝`REGISTERED_APPS` 字串 | `main.py:142` `importlib.import_module(f"api.{name}")` | api 下 27 個模組目錄名是載入契約，不可改名 |
| `infra/readmodel/` 結構 | CM-1507（D 棒）剛照 README 四規格分完子資料夾 | 已是目標態，不動 |
| 對外 URL／error code／blueprint 名 | D5 凍結面（231 error code／423 URL） | 每批搬完 dump url_map 比對 |

## 為什麼不動頂層（DDD 四層 vs 手冊四類的關係）

手冊的「四類」是**功能分類**（這段程式碼為什麼留在產品本體），DDD 的 `api/app/domain/infra` 是**技術分層**（HTTP 邊界／編排／領域／資料存取）。兩者是正交的座標軸，不是競爭關係：櫃檯自己就跨 app＋infra 兩層（規格②的三層紀律）、接線盤在 common／app／infra 都有正當落點（各自檔頭有 SOP §5 落點論證）、組裝根住 core＋config、業務加工佔 api／app／domain／infra 四層。**把四類升級成頂層目錄＝拆掉 DDD 分層**，會同時砸掉守衛掃描面、`main.py` 載入契約、與全專案 import 慣例，換來的只是「目錄名跟手冊詞彙對得上」。四類的正確表達方式是**手冊的實例路徑清單＋各落點的 README／docstring**（現在就是這樣），不是目錄樹。

---

## 現況 → 目標對照表（全頂層盤點）

### Python 模組層

| 目錄 | 歸類 | 目標 | 動作 |
|---|---|---|---|
| `infra/readmodel/`（tasks/audit/oscal/detection 四子夾＋README 四規格） | **① 櫃檯** | 現況即目標（CM-1507 剛落） | 不動 |
| `app/readmodel/service/`（my_jobs_app_service） | **① 櫃檯**（app query service 層，規格②落點） | 收齊櫃檯的 app 層薄殼 | **批3：`app/flow_control/service/auditor_dashboard_service.py` 搬入**（它的 SQL 早在 `infra/readmodel/audit/auditor_dashboard_query.py`，app 殼卻還留在 flow_control；引用僅 2 處：`di_containers/flow_control/flow_control_containers.py:99`＋`api/flow_control/__init__.py` 經 DI 取用不 import） |
| `common/iam_ports.py`、`common/integrity/` | **② 接線盤**（port 宣告／完整性 adapter） | 手冊實例路徑，位置正確 | 不動 |
| `app/flow_control/adapter/`、`app/license/adapter/`、`app/remote_agent/adapter/` | **② 接線盤** | 手冊實例路徑 | 不動 |
| `infra/<模組>/adapters.py` 群（survey/detection_tools/evidence_classification）、`infra/participant/user_directory_adapter.py`、`infra/upload_file/remote_agent_adapter.py`、`infra/remote_agent/recon_query_adapter.py`、`infra/ai/redis_chat_history_store.py`、`infra/mail/smtp/`、`infra/auth/root_admin_reader.py`、`infra/information_system/jedi_auth_user_lookup.py` | **② 接線盤**（各語境的 port 實作） | 「接線住在它服務的語境目錄」是既定慣例，集中會斷語境 | 不動 |
| `infra/{notification,system_menu,issue,api_log}/*_plugin_wiring.py` 四支 | **②/③ 之間**（插件接線，被 `core/app_factory.py` 呼叫） | 檔頭已有 SOP §5 落點論證（core 需要的不可放 api，故落 infra） | 不動——搬 `core/` 會集中組裝根但斷「語境優先」慣例，且四支檔頭論證是 P3 各棒審過的 |
| `core/`（app_factory／iam_wiring／upload_file_wiring／scheduler／extensions） | **③ 組裝根** | 手冊實例路徑；app_factory 路徑被測試焊死 | 不動 |
| `config/`（app_modules／di_modules／config／socketio_namespaces） | **③ 組裝根**（裝哪些積木＋設定值） | 手冊實例路徑 | 不動 |
| `di_containers/`（32 個子夾＋containers.py） | **③ 組裝根**（DI 組裝切片） | `auth/auth_containers.py` 被測試焊死；其餘照模組切片 | 不動 |
| `app/<業務模組>/`（oscal／module_frame／flow_control／cloud_integration／project_summary_report／auth／system_config／associations／participant／project 等） | **④ 業務加工** | 手冊實例路徑 | 不動 |
| `domain/`（10 個業務子夾） | **④ 業務加工**（domain 層） | 抽剩的都是本產品專屬 | 不動 |
| `api/`（27 個模組目錄） | **④ 業務加工**（HTTP 邊界）＋插件接線殼（ai／survey／task_survey） | 目錄名＝載入契約 | 不動 |
| `common/`（authz／code／dto／enum／middleware／util…） | 共用工具（四類之外的橫切層，被守衛禁止反向 import） | 現況即目標 | 不動 |

### 殘渣（本棒要清的）

| 目錄 | 現況 | 動作 |
|---|---|---|
| `app/issue/`、`app/log/`、`app/system_menu/` | 只剩一支註解式 `__init__.py`，**全樹 0 引用**（實查 grep）；對應功能已上移 jedi-issue／jedi-log／jedi-system-menu（P3.5） | **批1：刪除** |
| `app/detection_execution/`（含空的 `service/`） | 只剩空 `__init__.py`，**0 引用**；功能已進 jedi-detection（4.4） | **批1：刪除** |
| `domain/detection_execution/`、`infra/detection_execution/` | 整棵掛回 jedi_detection 的 **shim**（4.4／CM-1489 留下，頭注寫「等 CM-1490 收口後清」——CM-1490 已收）；引用僅 4 檔：`di_containers/detection_execution/detection_execution_containers.py`、`di_containers/detection_tools/detection_orchestration_containers.py`、`test/test_detection_execution_delete_and_timeout.py`、`test/test_detection_execution_domain_service.py` | **批2：caller 改直接 import 套件 → 刪 shim**（C 棒 CM-1506 同款手法，屬其清退範式的漏網殘留） |

### 非 Python 頂層目錄（皆不動）

`bin/`（報表下載器）、`content/`（detection profiles 資料）、`docker/`、`examples/`（範例碼）、`file/`（runtime 上傳落地）、`pki/`（dev 憑證）、`reports/`（測試報告輸出）、`resources/`、`static/`——部署資產與 runtime 產物，不在四類語彙內，重擺無收益有風險（部署腳本／nginx／.gitignore 都指著現路徑）。

---

## 執行計畫（第二段，等點頭後）

照 canonical 批次法，每批獨立 commit 可獨立回滾，每批後：守衛 80 綠＋url_map 423 條零差異＋`python main.py` 起得來。

| 批 | 內容 | 風險 |
|---|---|---|
| 批1 | 刪 4 個零引用空殼目錄 | 零（0 refs 已實查） |
| 批2 | detection_execution shim 收尾：4 個 caller 檔改 import → 刪 `domain/…`＋`infra/…` 兩棵 shim | 低（shim 本身就是 1:1 轉發；跑 2 支 detection test 檔驗證） |
| 批3 | `auditor_dashboard_service.py` → `app/readmodel/service/`，改 1 個 DI import | 低（app 殼 6 行類別；dump url_map 驗 `/grc/audits/my/list` 不漂移） |
| 批4 | 文件同步：CLAUDE.md 目錄表、`docs/claude/architecture-details.md`、handbook「主專案剩什麼」節實例路徑補 `app/readmodel/`、`infra/readmodel/README.md` 成員表 | 零 |
| 收口 | 全量 pytest 對照 2149 基準＋url_map 零差異＋實打 3–5 條核心 API＋8000/8002 兩模式起服務＋190 直跑驗證（卡片要求） | — |

## 給 user 的裁決點

1. **接受「小幅歸位」的定調嗎？**——四類已就位是 A–F 各棒累積的結果，本棒把最後殘渣清掉＋文件固化，而不是大搬遷。
2. **批2（detection shim 收尾）要不要做？**——嚴格說屬 C 棒 shim 清退範疇的殘留，做了佈局才算乾淨；不做則列 backlog 開卡。
3. **批3（auditor_dashboard 歸位）要不要做?**——唯一一件「檔案住錯地方」的實搬；不搬則櫃檯 app 層繼續一支在 readmodel、一支在 flow_control。
4. 有沒有你心中想動、但本提案列「不動」的目錄？——特別是 plugin_wiring 四支（我判維持 infra）與非 Python 頂層目錄。
