# FR-089 交接現況（STATE）

> **這份是 living 檔**：每棒就地 Edit，只描述「此刻」。歷史脈絡看同資料夾的 `FR-089-LOG.md`。
> 本 arc 是**協調者級**：涵蓋 FR-089（套件結構統一）母案底下三段——jedi-asset 定形（Done）／FR-090 主專案側收斂（進行中）／FR-091 18 支套用（第一批派出）。

| 項目 | 值 |
|------|-----|
| 最後更新 | 2026-09-13 晚（首腦第 3 棒中繼交接：FR-091 25 卡全 Done 已 push；1736 PM 文件卡待驗；下一棒＝驗 1736 → 開 arc-review → 收口裁決） |
| branch | BE `feature/FR-075`；jedi monorepo `feature/FR-075`；FE `feature/FR-075`（1741 一顆）；test 不動 |
| 母案卡 | CM-1688 FR-089 https://app.notion.com/p/FR-089-jedi-asset-18-jedi-3d9346da4cd0812ca708cca57d3123b3 |
| 子母卡 | CM-1682 FR-090 https://app.notion.com/p/FR-090-app-port-wrapper-3d9346da4cd081bb803bc6448ec3b3d0 ／ CM-1690 FR-091 https://app.notion.com/p/FR-091-18-jedi-jedi-asset-5-3-A-F-logger-runtime-3d9346da4cd0815280cbeb3d644cb2b8 |
| 在跑的卡 | 無。**發版 (a) 進行中**：21 支已推 Nexus（1.0.0／oscal-v2 2.3.0，jedi 2837fea）、BE pin 還原＋pyproject 整理（e0496645）＋lock；本機 DEV 已裝 Nexus wheel 起服務，決策者驗中。剩 version-bump（BE 1.20.0／FE 對齊／release note／spec 快照）。**CM-1737** Done（首腦驗收，commit 0422c5d6）、**CM-1736** Done（決策者看過）。其餘 25 卡 Done。兩 repo `feature/FR-075` 已 push（BE 至 9fff7772、jedi 至 c26a313；之後文件 commit 已全推，第 4 棒查兩 repo 無領先） |
| Notion 回寫規範 | `docs/claude/notion-issue-tracker.md` |
| 接手前必讀 | §0 讀序 |

## 🧭 原始需求 / WHY

> **這節寫一次，之後各棒不動**。

- **決策者原話**（2026-09-12）：「然後有超多 dataclass 直接寫在 plugin 裡面，這不能另外整理嗎？那隻一堆東西都沒有整理過很難閱讀」→「都做吧，這個作為範例」→「我想要全部做法都統一」→「我不想要在一堆地方設定，這樣接手的人很難上手」。
- **要解決什麼**：FR-069／FR-080 把功能抽成 21 支 jedi-* 套件後，每支長得不一樣（plugin.py 塞 400 行、守門函式各叫各的、logger 五種名、註解塞滿棒號）；主專案側接一支套件要翻四五處且每支翻的地方不同。接手者無從上手。
- **目標模型**：
  - 套件側：`plugin/` 五檔（__init__／contract／runtime／assembly／migrations）；`api/` 拆 `guards.py`（`runtime()`＋三個 lazy decorator）與 `routing.py`；logger 掛 `common.jedi_<pkg>`；entity 一律 dataclass；註解只留「為什麼」與「陷阱」；API 有吐 `created_user_name` 的才開 identity port。範例＝jedi-asset。
  - 主專案側：接一支套件三步——pyproject 一行、新建 `core/plugins/<pkg>.py`（adapter＋填表＋註冊三段同檔）、`PLUGINS` 清單加一列。只有主專案其他模組要直接用套件 domain service 才多第四步（DI）。
- **三段結構**：① jedi-asset 定形（FR-089 自身，Done）→ ② 主專案側收斂（FR-090）→ ③ 18 支套用（FR-091）。DI container 瘦身排 ③ 之後（FR-090 第 6 棒）。
- **與 FR-080 的關係**：FR-080 收口中，其「發版推 Nexus／pin 還原／SPEC／release note」四項 2026-09-12 移入本案，等 18 支整理完一次做（CM-1619 末段已記）。不掛回 FR-080。
- **說明文件**：`docs/features/FR-089-2609-package-shape-unification/plugin-anatomy.md`（袋子比喻、port/adapter、DI 與 runtime、設計模式對照、harness）——工程師第一次接觸先讀這份。

## §0 接手讀序

### 0.1 🔒 先懂需求 gate
1. 本檔 🧭 WHY 節
2. `docs/features/FR-089-2609-package-shape-unification/plugin-anatomy.md` §「這次整理做了什麼」「新手教程」「port 與 adapter」三節（**全讀**）
3. `docs/features/FR-069-2608-jedi-module-extraction/extraction-sop.md` §4.3（plugin 分檔規格）與 §5（主專案側）

### 0.2 現況與待辦
4. 本檔 §1、§3
5. `FR-089-LOG.md` 最後 1 個 block
6. 待驗的卡 `python3 scripts/notion_case.py get <id> > /tmp/x.txt`（直接 pipe 會截）：CM-1736 `3da346da4cd081a8af86d2a3bf50985c`。母卡 CM-1690 `3d9346da4cd0815280cbeb3d644cb2b8`

### 0.3 需要細節才開
- jedi-asset 整支（範例）；`core/asset_wiring.py`＋`infra/asset/`（主專案側現況，第 5 棒會搬）
- FR-090 README `docs/features/FR-090-2609-host-side-residue-cleanup/README.md`；FR-091 README

### 0.4 ✅ 冷接自檢
1. 為什麼 adapter 最終放 `core/plugins/<pkg>.py` 不放 `infra/`？（答：一檔看完優先於分層純度，決策者裁；CM-1684 先搬 infra 再被 CM-1689 改判）
2. 哪些套件要開 identity port、哪些不要？判準是什麼？
3. 為什麼 `ctx()` 改叫 `runtime()`？
4. FR-080 剩哪些收口項、哪些移來了？
5. 能力點（capability）現在「誰宣告、誰判定、誰 seed」三件事各在哪？（答：套件 `plugin/contract.py` 的 `CAPABILITIES` 宣告；主專案 `core/plugins/_host.py` 填 `common.authz.require_capability` 判定，實體 jedi_iam.authz；seed 仍在主專案 `04-seed-core.sql`，主專案五條守衛比對）
6. 為什麼驗收要在該卡 commit 的 worktree 跑而不是 HEAD？（答：並行 runner 的後續 commit 會把前一張的綠跑成紅，HEAD 顏色不代表單卡成果；1733／1732 實例）

## §1 當前狀態

> 1.1／1.6 由首腦第 3 棒 2026-09-13 晚更新；1.2 commit 清單停在 09-12（之後 200＋commit 見 `git log --grep="CM-17"`，不再逐條列）；1.3 卡表為最新；1.4／1.5 未重跑。

### 1.1 兩 repo git 現況

這是兩個 repo 的 branch／領先／HEAD／working tree 狀況（`git status -sb` + `git log`），2026-09-12 18:42 查。未 commit 的檔多半是並行 runner 正在做的，照實列不判斷。

| repo | branch | 領先 origin | HEAD | working tree |
|------|--------|-----------|------|--------------|
| BE `compliance-manager-be` | `feature/FR-075` | 已 push 至 9fff7772，之後文件 commit 數個未推 | `c2f90adf` docs anatomy 重寫（CM-1736） | 未 commit：`poetry.lock`、`pyproject.toml`（**20 支 path dependency，刻意不 commit**；別台機器 clone 跑不起來，發版 pin 還原前留意） |
| jedi monorepo `jedi-python-package` | `feature/FR-075` | 已 push 至 c26a313，無領先 | `c26a313` refactor(common) CM-1735 | 乾淨 |

### 1.2 FR-089／090／091 相關 commit 清單

這是兩 repo 用 `--grep="CM-16[7-9][0-9]|FR-089|FR-090|FR-091"` 撈到的 commit（BE 前 40、jedi 前 30），2026-09-12 18:42 查。含少數同號段內的鄰近 arc（CM-1668～1675 FR-088、CM-1679/1681 註解收斂／修測）一併列出。

**BE（新→舊）**

| hash | 卡號 | 一句話 |
|------|------|--------|
| `0ebc6c4f` | FR-080 | 收口——design／README／登記表改「已落地」、LOG 第 4 棒 |
| `7fad430c` | CM-1690～1695 | docs(FR-091) 登記 18 支套用標準形狀——母卡＋第一批五張 |
| `3149e48f` | CM-1686 ⑤⑥ | refactor(iam) 兩支 wrapper 瘦成純守門，補暱稱回套件 |
| `12b16e68` | CM-1686 ④ | refactor(iam) 兩支登入薄殼併進 core/iam_wiring.py |
| `f6f47385` | CM-1686 ③ | refactor(iam) 刪兩支轉發殼，port 實作歸位 infra/iam/ |
| `629ed2c2` | CM-1687 ④ | docs+test 補暱稱唯一路徑焊死＋主專案統一形狀進 SOP |
| `c4129183` | FR-089 | docs 插件解剖說明頁 plugin-anatomy.md 13 節 13 張 mermaid |
| `d9f88a1b` | CM-1687 ② | refactor(common) 補暱稱 helper 改走 D8 IdentityContext |
| `a3f1b626` | CM-1687 ① | refactor(di) 身分名冊收成單一 IdentityContext |
| `ceb455df` | FR-089/090 | docs 補母卡登記與資料夾——族譜歸位 |
| `5a009656` | CM-1684 | refactor(layout) FR-090 第 2 棒——五處 jedi port 實作歸位 infra/<pkg>/ |
| `580ae2df` | CM-1683 | chore(cleanup) FR-090 第 1 棒——刪套件上移後留的空殼 |
| `412809a5` | CM-1678 | docs(spec) 資訊系統盤點頁——enum 值域統一與租戶隔離 |
| `18a0a572` | CM-1668 | 註解收斂 BE 51 行「棒號當主詞」 |
| `aa90006b` | — | 註解收斂 main.py 補一處遺漏 |
| `adcae4e1` | — | 註解收斂 scripts/ |
| `f959c312` | — | 註解收斂 domain/、infra/ |
| `000acb2b` | — | 註解收斂 di_containers/ |
| `7f4cc9bf` | CM-1677 ⑤ | docs(SOP) §4.3 定名 runtime() 不叫 ctx() |
| `42a86c1e` | — | 註解收斂 api/、app/、common/、core/upload_file_wiring.py |
| `d5c914a3` | — | 註解收斂 api/*、setup/* 四層 |
| `fe8f6883` | — | 註解收斂 config/config_util.py、config_loader.py、app_modules.py |
| `bbbb547f` | — | 註解收斂 config/config.py |
| `4cec216d` | — | 註解收斂 core/scheduler.py |
| `a13676e0` | — | 註解收斂 core/app_factory.py |
| `b190584d` | — | 註解收斂 main.py |
| `32ac9e40` | CM-1677 ② | fix(asset) 引用計數改查表分派，未知型別留 warning |
| `306ef445` | CM-1670 | docs(FR-088) 驗收 H2——三條「只驗身分不驗歸屬」屬實 |
| `e9507d29` | FR-088.1 | docs H2 任務／留言／證明資安掃描報告 |
| `f66e52a5` | CM-1676 | SOP §3.4 補 logger 名要 common.jedi_<pkg> |
| `71040f4e` | CM-1676 | SOP 把 jedi-asset 整理出的形狀寫進 extraction-sop |
| `ad78cb49` | CM-1676 | spec 更正資訊系統頁 to_oscal_dict 錯述 |
| `d9d057e2` | CM-1669～1675 | docs(FR-088) jedi-flow-engine 資安掃描 arc 開卡 |

**jedi monorepo（新→舊）**

| hash | 卡號 | 一句話 |
|------|------|--------|
| `dba46e0` | CM-1686 ② | refactor(iam) 拔 User 審計暱稱 association_proxy，只留 identity port |
| `1668abb` | CM-1686 ① | feat(iam) 開 identity port 讓補審計暱稱回套件 |
| `4cee35b` | CM-1681 | test(system-core) 修回 FR-080 後變紅的 5 條 |
| `634763e` | CM-1681 | test(bulletin) 修回 FR-080 第 5 棒後變紅 |
| `3d9772f` | CM-1678 補充三 | fix(asset) system_status enum 值域統一小寫 |
| `02ec7bd` | CM-1679 | 註解收斂 四支 harness/dev_app.py |
| `0b9dace` | CM-1678 | feat(asset) information_systems 補 RLS 四條 policy＋FORCE，devices.tenant_id NOT NULL |
| `f9f6faf` | CM-1677 ⑤ | refactor(jedi-asset) ctx() 定名 runtime() |
| `c7659b0` | CM-1679 | 註解收斂 detection／compliance-audit／flow-engine／evidence-classification／iam |
| `08ea021` | CM-1679 | 註解收斂 jedi-common |
| `bc526f8` | CM-1679 | 註解收斂 jedi-ai-dashboard |
| `528603e` | CM-1679 | 註解收斂 jedi-survey |
| `57d4833` | CM-1679 | 註解收斂 jedi-issue |
| `65b07ef` | CM-1679 | 註解收斂 jedi-remote-agent |
| `0733454` | CM-1677 ④ | docs(jedi-asset) README 已知落差表補「啟用狀態的表達」 |
| `4b18e61` | CM-1679 | 註解收斂 jedi-system-core |
| `c9afe6f` | CM-1677 ③ | test(jedi-asset) 補 service 行為整合測試——真 postgres 18 支 |
| `d1f4d52` | CM-1679 | 註解收斂 jedi-log api_log/forwarding |
| `7620b02` | CM-1679 | 註解收斂 jedi-log plugin.py |
| `39bfd5b` | CM-1679 | 註解收斂 jedi-task-platform task/project |
| `bd9fc73` | CM-1679 | 註解收斂 jedi-task-platform participant |
| `319131d` | CM-1679 | 註解收斂 jedi-bulletin domain/infra/plugin |
| `546d289` | CM-1679 | 註解收斂 jedi-bulletin api/dto/service/error_code |
| `73aa9f0` | CM-1677 ① | refactor(jedi-asset) 刪 InformationSystemService.upsert_by_name |
| `731c4d7` | CM-1676 | jedi-asset logger 改掛 common.jedi_asset |
| `72926b9` | CM-1676 E | jedi-asset 刪零呼叫者 OSCAL 序列化模組＋error_code 搬出 enum 目錄 |
| `e27ae44` | CM-1676 D | jedi-asset 六個 entity 改 dataclass＋修四個小問題 |
| `60b2ada` | CM-1676 C | jedi-asset 統一設備／資訊系統兩套內部寫法 |
| `f0ab1f2` | CM-1676 B | jedi-asset plugin.py 拆四檔、api/__init__.py 拆兩檔 |
| `da091c2` | CM-1676 A | jedi-asset 清施工日誌型註解 |

### 1.3 Notion 卡狀態

這是 17 張卡用 `scripts/notion_case.py get <id> --props-only` 逐張查到的狀態，2026-09-12 18:45 查。

| 卡號 | 所屬 | 標題（截 40 字） | 狀態 |
|------|------|-----------------|------|
| CM-1688 | FR-089 母卡 | FR-089 套件結構統一——用 jedi-asset 定一種標準形狀，先收主專案側殘留 | Not started |
| CM-1676 | FR-089 | FR-089 jedi-asset 結構整理——當作 19 支套件的範例：清施工日誌註解 | Done |
| CM-1677 | FR-089 | FR-089 jedi-asset 收尾修正——刪零呼叫者的 upsert_by_name、引用計數改明 | Done |
| CM-1678 | FR-089 | FR-089 資訊系統表補 RLS＋設備表 tenant_id 收緊——jedi-asset 002 migra | Done |
| CM-1682 | FR-090 母卡 | FR-090 主專案側殘留收斂——套件上移後留在 app/ 的空殼、放錯層的 port | Not started |
| CM-1683 | FR-090 | FR-090 第 1 棒 空殼清理——套件上移後留在 app/ 與 api/ 的 8 個空目錄 | Done |
| CM-1684 | FR-090 | FR-090 第 2 棒 port 實作歸位——app/ 下五處實作 jedi 套件 port 的 adap | Done |
| CM-1686 | FR-090 | FR-090 第 3 棒 iam 宿主側收斂——app/auth/ 六檔與 user_auth_provider | Done |
| CM-1687 | FR-090 | FR-090 第 4 棒 補審計暱稱統一走 D8——common/util/audit_nickname.py | Done |
| CM-1689 | FR-090 | FR-090 第 5 棒 接線收成一支一檔——core/plugins/<pkg>.py 固定三段 | Done |
| CM-1696 | FR-090 | FR-090 第 6 棒 主專案 DI container 瘦身——不再重複建套件 service | Not started |
| CM-1690 | FR-091 母卡 | FR-091 18 支 jedi-* 套件套用 jedi-asset 定的標準形狀——三批各 5 支 | Not started |
| CM-1691 | FR-091 | FR-091 第一批 jedi-ai-bot——照 jedi-asset 形狀整理：清註解、plugin.py | Done |
| CM-1692 | FR-091 | FR-091 第一批 jedi-notification——照 jedi-asset 形狀整理：清註解 | Done |
| CM-1693 | FR-091 | FR-091 第一批 jedi-integrity——照 jedi-asset 形狀整理：清註解 19 處 | Done |
| CM-1694 | FR-091 | FR-091 第一批 jedi-remote-agent——照 jedi-asset 形狀整理：清註解 47 | Done |
| CM-1695 | FR-091 | FR-091 第一批 jedi-ai-dashboard——照 jedi-asset 形狀整理：清註解 17 | Done |
| CM-1697 | FR-091 | 第二批 jedi-bulletin | Done（第 2 棒驗收） |
| CM-1698 | FR-091 | 第二批 jedi-file-upload | Done（第 2 棒驗收；主專案 URL 守衛紅→1702） |
| CM-1699 | FR-091 | 第二批 jedi-system-core | Done（第 2 棒驗收） |
| CM-1700 | FR-091 | 第二批 jedi-evidence-classification（含主專案側 G 組） | Done（第 2 棒驗收） |
| CM-1701 | FR-091 | 第二批 jedi-license-runtime（含主專案側 G 組） | Done（第 2 棒驗收） |
| CM-1712 | FR-091 | 第三批 jedi-issue | Done（第 2 棒驗收） |
| CM-1713 | FR-091 | 第三批 jedi-log | Done（第 2 棒驗收） |
| CM-1714 | FR-091 | 第三批 jedi-flow-engine | Done（第 2 棒驗收） |
| CM-1715 | FR-091 | 第三批 jedi-compliance-audit（identity port＋G 組雙接線） | Done（驗收棒 2026-09-13，十步全過；FR-048 403 路徑因 DEV 孤兒資料未複驗採信 runner） |
| CM-1725 | FR-091 | 大卡 jedi-iam | Done（第 3 棒驗收補三項：506 passed、404 AUTH_404005 實打） |
| CM-1726 | FR-091 | 大卡 jedi-detection | Done（第 3 棒驗收：105 passed 含 integration、三突變紅、拔掉 35→0 條） |
| CM-1727 | FR-091 | 大卡 jedi-task-platform | Done（第 3 棒驗收：139 passed、80 條 import、兩 harness smoke；task 接線與 compliance-audit 同形共用 blueprint） |
| CM-1716 | FR-091 | 第三批 jedi-survey（暱稱收 D8＋順收 evidence-classification 死引用） | Done（第 2 棒驗收） |
| CM-1702 | FR-091 | 收尾小卡：asset／bulletin PKG_ROOT、file-upload URL 守衛、三支 harness --smoke | Done（第 2 棒驗收） |
| CM-1728 | FR-091 | 能力點清單定形：jedi-asset contract.py 收 CAPABILITIES＋jedi-common Capability 型別 | Done（第 3 棒驗收） |
| CM-1729 | FR-091 | 能力點回補四支＋survey 守門走 port | Done（第 3 棒驗收：comm 90/90 零回歸、403 GRC_403022 實打） |
| CM-1730 | FR-091 | 主專案守衛：Plugin 帶 capabilities，五條測試對 seed | Done（第 3 棒驗收：95 passed、四突變紅；127 筆四類零未歸類） |
| CM-1731 | FR-091 | jedi-integrity 版面歸位 | Done（第 3 棒驗收：乾淨 worktree 109 passed、兩突變紅） |
| CM-1732 | FR-091 | file-upload／issue／notification 頂層 ports 併 domain、issue tests 搬根 | Done（第 3 棒驗收） |
| CM-1733 | FR-091 | api/ 四件統一：license-runtime／remote-agent／log forwarding | Done（第 3 棒驗收） |
| CM-1734 | FR-091 | 能力點全套件普查與回補 | Done（第 3 棒驗收：21 支 80 筆、對 seed 零缺、凍結集合雙向守） |
| CM-1735 | FR-091 | oscal-v2＋common 收尾 | Done（第 3 棒驗收：123／234 passed、兩突變紅） |
| CM-1736 | FR-091 | PM 白話說明頁＋anatomy 補更新（純文件） | Not started，可派 |

### 1.4 測試基線

這是三組測試在 BE `.venv` 下跑 `pytest -q` 的末行結果，2026-09-12 18:45 查。

| 測試 | 結果 |
|------|------|
| jedi-asset `tests/` | 67 passed, 1 skipped, 12 warnings |
| jedi-iam `tests/` | 457 passed, 26 warnings |
| BE `test/test_module_boundaries.py` | 90 passed（含帶 file-upload 工作樹；URL 守衛已改讀 FROZEN_URLS） |
| jedi-bulletin／file-upload／system-core `tests/` | 159／173／225 passed（各含 testcontainers 整合測試 18／14／21） |

### 1.5 DEV DB 實況（唯讀）

這是 DEV `guidant_ai_dev`（188:25432，`cmmgr`）查 pg_class／pg_policies／pg_enum 的結果，2026-09-12 18:45 查。

| 表 | RLS 開 | FORCE | policy 數 |
|----|--------|-------|-----------|
| `compliance.information_systems` | t | t | 4 |
| `public.devices` | t | f | 4 |

| enum（schema `compliance`） | 值域 |
|------|------|
| `system_status_enum` | operational \| under-development \| disposition \| under-major-modification \| other |
| `security_sensitivity_level_enum` | low \| moderate \| high |

### 1.6 BE 服務

2026-09-13 晚：BE 以 `python main.py` 起在 8000，**不需 PYTHONPATH**（20 支 path dep 都指工作樹，只剩 oscal-v2 之外全 path）。`/api/1.0/devices/menu` 回 401 即活著。決策者正在 DEV 走一輪頁面測回歸（零行為變更的 arc，測「什麼都沒壞」）。

## §2 已定裁示（不要重新討論）

| 裁示 | 何時 |
|---|---|
| 全部做法統一成 jedi-asset 形狀 | 2026-09-12 |
| 先做主專案側（FR-090）再做 18 支（FR-091） | 2026-09-12 |
| `ctx()` 改名 `runtime()`，19 支一起改 | 2026-09-12 |
| identity port 只給 API 有吐 `created_user_name` 的套件（survey／detection／task-platform／compliance-audit／log；iam 已於 CM-1686 開）。**第 2 棒實查修正**：log 已有 `IUserNameResolver` port 不另開；survey 現有三條補名路徑收成 D8 一條（CM-1716） | 2026-09-12／13 |
| enum 值域統一小寫（A 案），已套 DEV（CM-1678） | 2026-09-12 |
| adapter 與填表同檔放 `core/plugins/<pkg>.py`，改判 CM-1684 的 infra/ 位置 | 2026-09-12 |
| iam 兩支 wrapper 留原地瘦成純守門（A 案），不搬不改套件 route | 2026-09-12 |
| DI container 瘦身排 18 支之後（CM-1696），先問決策者定「主專案怎麼用套件」規則 | 2026-09-12 |
| FR-080 發版／pin／SPEC／release note 移入 FR-089，等 18 支完一次做 | 2026-09-12 |
| 資安掃描不因結構整理重掃；報告行號以整理前 commit 為準 | 2026-09-12 |
| **能力點跟著套件走，21 支全列、形式一致（沒有的 `CAPABILITIES = ()`，不只 route 有守門的）**：每支套件在 contract.py 列 `CAPABILITIES`（jedi-common `Capability` 型別，含 read 與 default_roles）；survey／detection 寫死字串一律收成 config 形狀；config 不獨立成檔留 contract.py。守門判定仍由主專案 `_host.py` 填 `common.authz.require_capability`，不動 | 2026-09-13 |
| **「init image 在 migrate 時讀套件清單自動補 capabilities row」是獨立大 arc，另開 FR，不在 FR-091**（要處理 seed 檔 127 對寫死整數 role_capabilities、Administrator 初始持有集合、ui_routes）。FR-091 只做前置三卡 | 2026-09-13 |
| **SOP §4.3 `api/` 四件**：guards.py／routing.py／routes/／serializers/，route 與 schema 不平鋪 api/ 頂層，子插件各自照長 | 2026-09-13 |
| **SOP §4.3 三條版面規則**：頂層 `jedi_<pkg>/` 只放 `__init__.py`＋分層目錄；port 只放 `domain/ports.py` 不開頂層 `ports/`；tests 在套件根。驗收 SOP ③ 殘留掃描加這三項 | 2026-09-13 |
| 能力點歸屬四裁：`report` 不進套件清單；`flow_template.*` 歸主專案（判準＝誰的 route 在守）；主專案 39 筆要宣告但歸 FR-090 另開卡、1730 先豁免名單；疑廢 20 筆＋DEV 殘留 7 筆照列不刪 | 2026-09-13 |
| oscal-v2 做輕量版（ports 併 domain／logger 統一／entity 抽查），common 只清註解與 logger、不動結構；兩支不建 plugin。開 CM-1735 | 2026-09-13 |

## §3 下一步

**🔴 下一個動作**（依序）：
1. ~~驗 CM-1736~~（決策者 2026-09-13 看過收 Done）；~~CM-1737~~ 已收。**PM 頁 `for-pm.md`「三個零件裝法還是舊式」段要拿掉**（純文件小改，首腦順手或併 arc-review 後的文件卡）。原 1 項：**驗 CM-1736**（PM 白話頁 `for-pm.md`＋anatomy 重寫）。驗法：① 兩頁 grep `CM-1|第 .* 棒|原本|收斂前|改判|後來|2026-09-12` 出現即退回（決策者裁「文件只寫現況」）；② PM 頁允許 plugin／主專案／接線／能力點／守衛測試五個詞且首次出現有括號白話，不得出現 adapter／port／DI／runtime／dataclass／contract／seed；③ 有「一支 plugin 裡面有什麼」五部分表；④ 數字對現況（21 支、25 卡、80 筆、`core/plugins/` 17 檔）；⑤ `render_index.py` 重 build 後 `for-pm.html` 側欄有、mermaid 沒壞。過了把 `for-pm.md` 全文貼給決策者轉 PM。
2. ~~arc review＋修正卡~~ **全收**：CM-1738 報告（2C／6I／5M）＋ 1739～1744 六張 Done。乾淨 worktree HEAD 守衛族 141 passed。未開卡留存：I-5 九處 import 改指＋刪三支 ports/ 殼（1744 白名單標「I-5 收」，收完要拿掉白名單）、M-1～M-5、註解 58 條、四支既有紅測試（不掛 FR-089）、`ProjectRoleGuardAdapter` 兩份同名不同實作（`infra/survey/adapters.py` vs `core/plugins/evidence_classification.py`）。
3. **決策者裁**（拿 review 結果一起談）：(a) FR-080 移來的發版推 Nexus／pin 還原／SPEC／release note——STG 測試的前提；(b) CM-1696 DI 瘦身規則「主專案哪些模組可直接用套件 domain service」；(c) FR-090 補卡：主專案自有 36 筆能力點申報處（三支接線那張已開 CM-1737，見 §4）；(d) migrate FR：出貨 image 不撿套件 migration＋能力點自動補 row＋Administrator 初始權限（seed 檔 127 對寫死 role_capabilities 整數）。
4. **follow-up 要不要開卡**：evidence-classification 3 條／ai-bot 1 條寫入 API 無 schema；issue `tests/vcs_integration/` 21 紅（需外部服務）；compliance-audit／task-platform 無 testcontainers 整合測試；`FlowControlJobEntity` 未 dataclass（參數名≠屬性名）；`report` 能力點無點；`task-setup/tree` 500（raw SQL `ap.uid` 應為 `uuid`）。
5. **順手**：CLAUDE.md「DB 環境清單」DEV 列改 localhost（188 `guidant_ai_dev` 標退役快照）、`docs/claude/sql-migration-conventions.md` 與 3-way diff 腳本同步；「說明文件只寫現況」已進 CLAUDE.md 不用再補；驗收 SOP 措辭改「驗既有紅唯一方法＝開 worktree 到開工前 commit」放最前（禁 stash 放尾巴擋不住，1727／1735 連兩張伸手）。
6. FR-089／FR-091 母卡收 Done、SUMMARY——等決策者下令收尾。

**驗收 SOP（第 3 棒定型，全程在該卡 commit 的 worktree 跑）**：⓪ `git status` 該卡目錄零 `??`（1731 事故：`commit -- 路徑` 不納未追蹤新檔）；① 讀回寫（重導到檔再讀）；② `git log` 對卡；③ 殘留掃描（ctx()／施工日誌關鍵字／非 dataclass entity／logger 名／addopts／--smoke／PKG_ROOT／頂層平鋪／頂層 ports/／套件內 tests/／api 頂層只允許 __init__ guards routing）；④ 套件 pytest 全量**在該卡 commit 的 worktree、PYTHONPATH 只指 worktree**（HEAD 紅要歸因到後續哪張卡；runner 報「與 baseline 相同」必看 `comm` 輸出，沒貼就自己比）；⑤ 版面守衛突變——**突變前 `git diff --stat` 確認真的改到檔**（BSD sed `0,/pat/s//` 空替換、`Nd` 行號算錯都會假綠）；⑥ harness --smoke；⑦ 主專案 boundaries＋logger 守衛＋fr048；⑧ AST 掃主專案 import 逐一 importlib；⑨ BE 重啟零新 Traceback、真實 URL 從套件 routing.py 抓、先看 GET／POST 與 FE payload；⑩ 有 G 組加拔掉測試。**驗收是首腦本職，不外派**（決策者 2026-09-13 裁）。

> ⚠️ 這節是**建議清單，不是執行授權**。接手方讀完盤點完即停，等 user 發令。

## §4 待辦與風險

- **runner 側紀律缺口（本棒三張退回的共同根因）**：並行時 HEAD 不代表單卡成果；`commit -- 路徑` 漏未追蹤檔；「與 baseline 相同」靠記憶不靠 comm；驗既有紅伸手 `git stash`。派工 prompt 尾註要固定加：收工前全量在自己 commit 的 worktree 跑、`git status` 看 `??`、對照用 comm 貼輸出、禁 stash 改 worktree。
- **三 repo 領先 origin 未推**：BE 自 56a15397 起（1737＋review＋六張修正＋docs）、jedi 自 b077ed8 起（1741／1739／1742 三顆）、FE 6b7b0f2（1741 退路）。等令。
- **發版前置**：jedi-common 新 export `fill_user_names`，五支對 jedi-common 下限（asset≥0.0.34／iam≥0.0.23／survey≥0.0.33／compliance-audit≥0.0.33／bulletin≥0.0.34）發版時要提到含該函式那版，否則裝舊版直接 ImportError。
- **守衛 `test_converged_plugin_adapters_do_not_return_to_infra` 會被 `__pycache__` 假紅**：它用 `path.exists()` 判舊目錄，舊樹上殘留的 pycache 目錄會讓它紅（首腦驗 1737 撞到）。改法：判目錄內有無 `.py` 檔，或 `git ls-files`。小卡候選。

- **出貨 image 不撿套件 migration**（CM-1678 runner 查證）：`scripts/init/` 與升級腳本只讀主專案 `scripts/sql/` manifest，套件的 `migrations/` 永遠套不到客戶端。enum bug 三環境＋出貨基線都有。未開卡。
- **CM-1684 守衛盲點**：`test_jedi_port_implementations_live_in_infra` 只認繼承 jedi port ABC 的 class，duck typing 的 adapter 看不到。1689 改守衛時要一併擴。
- **1687 幾處 `_enrich_nicknames` 之外的補暱稱**：`app/flow_control/service/project_service.py:315`（受評標的負責人）與 `feedback_issue_dto.py` 不是補審計暱稱，刻意不動，別誤收。
- **admin（tenant 1）建的資料在 blsadmin 視角暱稱回 None**：RLS 範圍 `/1/102/` 看不到 tenant 1，不是 bug。
- **五支套件（asset／system-core／log／task-platform／bulletin）未發版**：主專案走 path dependency（`pyproject.toml:184` 附近，不 commit），別台機器 clone 要先有 jedi 套件目錄。
- **主專案 venv 四支（issue／file-upload／flow-engine／ai-bot）仍讀 Nexus wheel**——首腦 2026-09-13 裁：派 CM-1730 前一律改 path dependency（不 commit），否則守衛在主專案讀不到清單會誤判。原文：多數套件是 Nexus wheel 不是工作樹（CM-1692 runner 抓到）：`pyproject.toml` 只有 asset／iam 等少數走 path override，其餘是 pin。在主專案驗任何套件改動前先 `python -c "import jedi_<pkg>;print(jedi_<pkg>.__file__)"`，指到 site-packages 就要 `PYTHONPATH=<套件目錄>` 重跑，否則假綠。已通知第一批其餘四張 runner。
- **主專案 venv 的 site-packages 裝了 jedi-asset 的 `tests/` 當頂層套件**（path dependency 副作用）：其他套件 `from tests.xxx import` 被遮蔽，預設模式 pytest collection error。用 `pytest --import-mode=importlib`，或下批卡要求各套件 `pyproject.toml` 加 `addopts = "--import-mode=importlib"`。整理前就存在。
- **第一批三支 harness 沒有 `--smoke`**（ai-bot／remote-agent／ai-dashboard 都是 `app.run()` 綁 port），首腦驗收時撞 port 撞了三次。第二批起卡上加「harness 補 --smoke（test client 不綁 port）」，並回頭給這三支開一張小卡補。
- **三支接線（ai-bot／ai-dashboard／remote-agent）已收進 `core/plugins/`**（CM-1737，0422c5d6）：`_CAPABILITY_PACKAGES_OUTSIDE_PLUGINS` 清空、`PLUGINS` 20 支。留此行只為對照舊待辦。
- **1689 範圍外仍有 infra/ adapter**：`infra/{license,remote_agent,survey,detection_tools,notification,upload_file}/` 各 1～4 檔沒搬，因為那些套件不在 PLUGINS 迴圈（走 REGISTERED_APPS 或另掛）。守衛 90 passed 表示它們不繼承 jedi ABC 或已白名單。FR-091 對應批次時順手收進 `core/plugins/`。
- **`core/plugins/` 是新目錄，Nuitka 打包白名單未驗**（1689 runner 列出）。出包前必查 `scripts/build/`。
- **並行 runner 共用工作樹時裸 `git commit` 會吞別人 staged 的檔**（首腦 ea34a17e 吞了 1689 的 22 檔；1692 也吞了 1693 的 14 檔）。首腦與 runner 一律 `git commit -- <路徑>`。
- **疆界守衛 `PKG_ROOT` 陷阱（CM-1699 runner 發現，第 2 棒實查）**：`Path(<pkg>.plugin.__file__).parent` 在 plugin 拆子套件後指到 `plugin/`，三支「不得 import 主專案」守衛只掃 5 檔仍全綠。asset／bulletin 已由 1702 修好（掃 58／42 檔，突變會紅）；**第三批起開卡固定寫「PKG_ROOT 從套件頂層模組取」**。
- **bulletin 整合測試只套 001 不套 002**，RLS 未覆蓋（runner 當場講明，首腦收下）。system-core 那支有套 002 可當範本。
- **第一批五支的 plugin.py 都能無痛拆五檔**：已由 runner 回寫證實，推測撤銷。

## §5 Pre-flight（必跑）

```bash
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be && git status -sb | head -3 && git log --oneline -5
cd ~/Projects/Jedicogy/module/jedi-python-package && git status -sb | head -3 && git log --oneline -5
# 待驗的卡
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
python3 scripts/notion_case.py get 3da346da4cd081a8af86d2a3bf50985c --props-only 2>&1 | grep -E "^Case No|^狀態"   # CM-1736
# 21 支全指工作樹＋能力點可讀
source .venv/bin/activate && python -c "import importlib;print(sum(len(importlib.import_module(m).CAPABILITIES) for m in ['jedi_asset.plugin','jedi_iam.plugin','jedi_detection.plugin','jedi_survey.plugin']))"   # 42
# 守衛基線
python -m pytest test/test_module_boundaries.py test/test_jedi_package_logger_names.py -q 2>&1 | tail -1   # 100 passed
# BE 在不在
curl -s -o /dev/null -w "%{http_code}\n" localhost:8000/api/1.0/devices/menu   # 401 = 活著
```
憑證一律查 `.env`，不寫明文。

## §6 環境座標

| 項目 | 值 |
|---|---|
| BE repo | `~/Projects/Billows/Audit-Manager/compliance-manager-be`，branch `feature/FR-075` |
| jedi monorepo | `~/Projects/Jedicogy/module/jedi-python-package`，branch `feature/FR-075`；21 支套件各一目錄 |
| 範例套件 | `jedi-asset/`（整支）；harness DB 走 `harness/docker-compose.yml` port 5493 |
| DEV DB | **2026-09-13 起搬本機 `localhost:5432` `guidant_ai_dev`**（決策者裁，小弟 pg_dump 自 188 拉回；188:25432 那份退為快照不再是 DEV），帳號 `cmmgr`，密碼查 `.env`。接手先 `psql -h localhost -p 5432 -U cmmgr -d guidant_ai_dev -c "select count(*) from schema_migrations"` 確認搬完；沒搬完就仍是 188 |
| BE 起法 | `kill -9` 舊的再 `python main.py`（port 8000）；log `log/app.log` |
| 登入 | `POST /api/1.0/login` body `{"username":"blsadmin","password":<查 memory reference_dev_login>}`；**欄位是 username 不是 login_name** |
| Notion CLI | `python3 scripts/notion_case.py get/status/append`；建卡 `scripts/notion_create_case.py <spec.json>` |
| STG／POC | **本 arc 全程不碰**（開發階段只動 DEV） |

## §7 行為規範提醒

- 派工＝開 Notion 卡，卡上帶母卡號；runner prompt 薄（母卡號＋卡 URL＋讀卡照做＋branch＋並行注意＋commit 回寫不等令），並標建議 model。
- 驗收不採信 runner 自報：自己跑測試、grep 殘留、重啟 BE 打端點。
- **驗守門類功能前先查清楚定義**（2026-09-12 首腦教訓：以為 blsadmin 是 root admin，實際受保護的是 tenant 1 的 admin，誤把測試帳號停用後手動復原）。
- 檔案內註解只寫為什麼與陷阱；施工日誌進 commit message。
- 不發版、不推 Nexus、不 push、不切 branch、不碰 STG／POC。
- 中繼交接：STATE 就地 Edit、LOG append、在跑的子卡不動。
- **commit 一律 `git commit -- <路徑>`**，共用工作樹有並行 runner 時裸 commit 會吞別人 staged 的檔（第 1 棒踩兩次）。
- **驗收打端點前先看 route 是 GET 還是 POST**：第 2 棒三條列表端點都是 POST 帶 `RequestMetaSchema`（pager／sort／filters），用 GET 打回 405 不是壞。上傳是 multipart 帶 `json` part。
- **驗收要批次**：三張卡的 pytest／突變／smoke／import 掃描一次跑完再看，不要一張一張。
