FR-069 模組化抽取 · 全案收官 · 2026-09-01

四階段、26 支套件、一個月——FR-069 全案收官報告

本檔是 FR-069 整個 task arc 的收口彙整,逐 block 濃縮自 fr069-LOG.md(append-only 歷程檔)而非憑記憶重建。內容含四階段帳、26 支套件終態、18 條教訓精選、未結 follow-up 與部署 handover。

五階段全收 現役 25 支套件 待 user 令收母卡

FR-069 模組化抽取 — 全案收官 SUMMARY

  • 期間:2026-08-29 ~ 2026-09-01(四天)
  • 母卡:CM-1435(本檔產出時尚未收 Done——收母卡屬結論宣告,等 user 令)
  • 範圍:BE 主專案 + jedi monorepo +(少量)FE
  • 材料來源fr069-LOG.md 七個 block 逐一濃縮;數字全部實跑指令核對,不沿用文中舊值

0. 一句話

把後端 40 個模組裡「別的產品也用得到」的能力,一支支抽成可獨立安裝的 jedi-* 套件—— 現役 25 支、退役 10 支、對外契約零漂移、主專案淨刪約 16,500 行


1. 帳面總表

項目 數字 怎麼得到的
BE commits 155fe56ab6a..HEAD git log --oneline | wc -l
BE 檔案變動 1,456 檔、+57,776 / −74,308(淨刪 ~16,500 行) git diff --shortstat
monorepo commits 64bc0f2b86..HEAD 同上
monorepo 檔案變動 2,137 檔 同上
現役套件 25 支 磁碟目錄數 = pyproject.toml pin 數(差集為空)
完全體插件(自帶 route) 16 支 逐支數 api.add_resource 活行
退役刪除 10 支/塊 見 §3
本 arc 發版 18 支(CM-1473 十一支+CM-1499 七支) 對照 release-plan-p4.md
Notion 卡 CM-1435 母卡+約 65 張子卡 CM-1436~CM-1502 連號區間

對外契約凍結面(D5):231 個 error code 值/424 條 URL/藍圖名, arc-review 機器 diff 逐字比對零漂移


2. 四階段做了什麼(+2.5+發版+收官)

第一階段(P0–P8,8 棒)— 抽五支新套件+地基擴充

  • P0 前置清理,含 D5 追加的 grc → flow_control 全面改名(error code 字串值與 API path 刻意不動)。
  • P1 jedi-ai-bot——插件契約首例,同時產出 extraction-sop.md 七階段 SOP。
  • P2 jedi-integrity(FR-064 防竄改)/P4 jedi-remote-agent(定 migration 隨包五規則)。
  • P3 jedi-log-forwarding(含「迷你接收器真的收到 log」端到端實測)/P5 jedi-license-runtime (驗簽引擎逐行比對零改動,執法留宿主)。
  • P7 jedi-common 擴充兩批——全程先加後刪,主專案舊位置改 re-export shim, 124 處消費端零改動、零檔案被碰
  • P8 authz 主體域併入身分套件——拆兩半精準(主體域五檔進套件/資源域四檔+license 軸留主專案), 126 處消費點零改動;四個守門 error code 進套件但字串值凍結 GRC_*(FE i18n key,實地驗過 8 筆翻譯靠它)。

插曲(值得記):首腦把 jedi-common 頂層 7 行的 base_repository.py 誤判為「舊代待汰換」發了清理棒, 子棒查繼承鏈發現它是被完整版繼承的 session 底座——按紀律煞車、未改任何檔。結論改為「不汰換、正名 SessionMixin」。

第二階段(.10–.12,3 棒)— 延伸表收斂+查詢層擴充

  • .10 ext 全庫盤點:190 張表掃出 9 張延伸表形態。推翻原假設——全庫沒有任何 「純展示型客製欄位」,側掛表是跨套件邊界長出來的,不是客製需求。 JSONB 容器定位因此改為「預防性建設」(D15)。
  • .11 jedi-common 查詢層:JSONB 查詢前綴(_ext_/_extlike_/…)+身分脈絡包+SessionMixin 正名,全「用加的」。
  • .12 project_extensions 收斂:四欄收回主表(皆帶業務邏輯,轉正式欄位而非塞 JSONB); 14 處 raw SQL JOIN 改完;DEV 實查回填 211 列零不一致;STG 實查未被套(環境紅線守住)。

2.5 階段(.13–.18+.H+改名,8 棒)— 身分四套件合併成 jedi-iam

  • jedi-auth+jedi-login+jedi-mfa+jedi-captcha 四支合一,+主專案身分中介層/登入編排。
  • .16 抓到真提權洞X-Tenant-ID: 0 觸發 is_super_admin 繞過全 RLS(含 socket 整數 0 falsy 變體),皆修。
  • 命名定案 jedi-iam:PM 推翻 identity(「只涵蓋你是誰半邊,太狹隘」);jedi_identity 不留轉發殼直接死名。
  • 驗證瘦身規則七條拍板(實測驗證吃 6 成工時;判準「壞了會不會靜默」)。

第三階段(CM-1467–1472,6 棒一天全收)— 老套件升級插件

  • 3.1 jedi-iam 完全體(範本棒):39 條 route 進套件、主專案 api/auth/ 26 檔刪除、四殼退役、D16 port 化。 三個照抄要點成為後續 11 支的範本: ① route 取 service 走 ctx().service(name) 而非 @inject存 provider 不存實例——存實例是併發才爆的壞); ② 缺認證接線拒絕掛載(跳過的後果是身分端點全變公開而健康檢查照樣綠); ③ 切分判準「換一個產品,這段還會一樣嗎」。
  • 3.2–3.5 補殼十支、3.6 退役四支(monorepo 26→24)。
  • 發版裁決反轉:user 質疑四殼為何要發,重盤後裁不發改退役——.18 清單的前提已被 3.1 拔 pin 推翻。

第四階段(CM-1475–1483 九棒+補洞,全收)— 核心能力抽取

  • 4.A D17 資料關聯三律/D18 設定申報制入決策表;4.B AI registry 申報制。
  • P12 jedi-participant(核心環鑰匙):readmodel 櫃檯建置+40 組參數 md5 比對+ A/B 對打抓到 Containers 類別層真回歸。
  • 4.2 flow_control 分家四步(本階段最重):設計稿 → CM-1486 退役 4,351 行 → CM-1487 搬遷 190 檔 → CM-1488 分家(平台層零稽核詞的機械判準)→ CM-1490 第 3 步 (D-10 償債/申報制上線/問卷審核開關「設定不是客製」實證)→ CM-1492 打包定名。
  • P9 detection(147 支,基準庫 5.4MB 隨包)/P10 evidence-classificationP6 ai-dashboardP11 問卷合併
  • 三包定名:jedi-flow-engine 不動/jedi-task-platformjedi-compliance-audit(不佔 audit 泛稱)。
  • 平台化方向拍板project =插座不是插頭;flow_control 分家=分家是複用的前提

發版與收官串

  • CM-1473(第三階段)11 支上 Nexus、四殼刪除 24→20、守衛換軌。
  • CM-1499(第四階段)7 支上 Nexus、pin 入版收三批託付改動、path override 全清。 ✅ 拉分支不再需要 pip install -epoetry install 即可。
  • CM-1500 問卷共編修復(jedi-survey 0.1.1)——見 §5 教訓 ⑥。
  • CM-1497/1498 套件債清理+associations 殭屍退役。
  • arc-review(5 平行 agent)→ CM-1501 修復棒(13 條逐條,每條守衛都做突變驗證) → CM-1502 文件收官(本檔所屬棒)。

3. 26 支套件終態

完整終態表在 architecture-handbook/modularization-roadmap.md §套件族譜 (25 支逐支列版號/來源/形態+10 支退役去向)。此處只放摘要:

分類 支數 內容
本 arc 新抽/合併 8 iam(四合一)/task-platform/compliance-audit/participant/detection/evidence-classification/ai-dashboard/survey(吸收作答層)
第一階段新生 5 ai-bot/integrity/license-runtime/log-forwarding/remote-agent
老套件升級(完全體) 5 file-upload/system-menu/log/issue/notification
老套件補殼(半體) 4 bulletin/system-config/device/information-system
引擎/領域/地基 3 flow-engine/oscal-v2/common
退役刪除 10 auth/login/mfa/captcha(→iam)/project(→task-platform)/flow-control(→compliance-audit)/identity(改名)/oscal v1(→v2)/department/resource-store/jedi_system_log

「半體」不是沒做完——是第三階段逐支實查後誠實聲明不搬的理由 (route 的 service 實際住在主專案,硬搬會變成套件反向 import)。

零套件反向 import:arc-review AST 全掃 25 支(含 lazy/函式層)確認。


4. 產品本體最後剩什麼

四類,完整版(含 15 條實例路徑)在 modularization-roadmap.md §產品本體

報表櫃檯infra/readmodel/,八支跨疆界純讀聚合) ② 接線盤(ports + adapters) ③ 組裝根core/app_factory.py/各 *_wiring.pyconfig/app_modules.py) ④ 業務加工app/oscal/app/module_frame/app/cloud_integration/app/project_summary_report/


5. 教訓精選(LOG 共 18 條+收官 3 條,這裡挑最可複用的)

關於「前提」

  1. 卡片/裁決的事實斷言,查證責任在開卡人——本 arc 首腦裁決被 runner 推翻 2 次、 卡片斷言被推翻 4 次,全數靠「前提被推翻就停」的煞車紀律救回。 代表案例:base_repository 誤判(憑檔名大小推斷新舊兩代)、 detection 綁定表歸屬(只憑語意沒跑 grep/pg_constraint)、 CM-1487「226 檔比照 P12 機械搬」(P12 能機械搬因 out-degree 8,flow_control 纏五疆界)。
  2. 「比照先例」前必驗前提等價——類比壓掉的正是前提。
  3. 上游線索是待驗假說不是結論——CM-1485 兩條線索各推翻一半 (camunda 程式側確實死透,但「只剩 BPMN 解析」被推翻:執行引擎完全活著)。

關於「守衛」(arc-review 主題)

  1. 綠著的守衛不代表在守——四類「守衛空轉」:掃舊名(改名後永遠綠)、掃描根縮水、 雙份碼表無相等斷言、引用不存在的測試檔。
  2. 守衛要做突變驗證——故意違規 → 確認變紅 → 還原。CM-1501 每條修復都做了, 其中一發突變在舊守衛下是雙重隱形
  3. 契約要看實作不看簽名(CM-1500)——mount_api=False 簽名有、行為缺一半, 於是「不掛路由」與「拿得到 context」在套件裡綁死成二選一,宿主端補不出來。 runner 照卡片邊界停手回報,是這次沒被繞成「在 socket 模式掛 REST」(會倒退 FR-063.1c)的原因。
  4. 契約測試要同時焊「行為成立」與「語意一致」——只驗「拿得到 ctx()」的話, 把寫入改成 skip-if-exists 仍會綠。

關於「歸因」

  1. 套件全量測試的 baseline 要先量再歸因——用 git stash 抽掉改動跑同一份指令對照, 不先量就會把存量債記在自己頭上(3.2 的 16 ERROR、jedi-survey 的 48 failed 皆是)。
  2. 驗收歸因要三證才下結論(參數來源 commit/本棒未觸該檔/父節點同紅)。
  3. 手測報錯先看 log 的 Host 與連的庫——.12 的「無法顯示專案」實為 .env 連錯庫。

關於「協作」

  1. 平行棒共用 git index 是真事故——顯式 git add 防不了「別人已 staged 的混進來」; commit 前必 git diff --cached --stat 核清單(已入所有後續 prompt)。
  2. harness 只證明「掛得上」不夠,要實打一支端點走通——3.1 讓 harness 真打 POST /login, 挖出四道宿主前提。其中 PROPAGATE_EXCEPTIONS 最陰:flask-restful 的 Api 包掉 handle_user_exception,於是 register_error_handlers 註冊成功卻完全不生效,401/403/404 一律變 500。
  3. 改名要掃「寫死字串前綴」——它們壞掉的方式是靜默匹配到 0 條,測試變成「掃了個寂寞卻顯示通過」。
  4. 改名前先分辨「舊套件名」與「領域概念」——JWT_IDENTITY_CLAIMget_jwt_identity()Accept-Encoding: identity 都不能改(改 JWT claim 名會讓所有既有 token 當場失效)。
  5. view 與 SQL 字串是守衛盲區(兩案實證)——退役/搬遷必查 pg_views +表名字串,人工列冊。
  6. 守衛須 python -m pytest,直呼 pytestModuleNotFoundError: common 假紅。

關於「揭露」

  1. 揭露的缺口比沉默安全——task-platform pyproject 自記 jedi-iam 債、兩套件 README 聲明 無 harness 與條件、CM-1490 誠實寫「孤兒定期清理未建置」。arc-review 特別點名這兩處值得記。
  2. 驗收打折要當場講明——本 arc 多棒因 MFA/無 token/AI 費用而無法端到端, 一律在卡上寫明「驗的是什麼、沒驗的是什麼」+手測清單,不報成「通過」。

6. 未結 follow-up

已開卡待辦(Notion 狀態 Not started

內容
CM-1484 jedi-participant 六支 ORM 直連身分表改走 IUserDirectory port(+主專案 10 支同病 repo 評估)
CM-1495 任務綁定表孤兒定期清理 job——CM-1490 D-10 償債的配套,尚未建置(現有防線=顯式刪+讀取降級)
CM-1445 RLS 覆蓋缺口:user_rolesuser_tenantsuser_org_units 三張授權關聯表未啟用 RLS(獨立安全債,不掛 FR-069)
CM-1461 裸路徑死端點追蹤——剩 bulletin/device/system-config 9 支未拔(route 未上移)
CM-1463 roles.is_admin 舊管理員旗標收斂(消費端改道 capability 後拆除)

待 user 裁決

  • jedi-issue GL/GH 整合碼留/刪(CM-1497 已備 A/B 材料,367 行)。
  • test_audit_event_instrumentation 併跑汙染——CM-1498 查明是既有債,建議開卡。
  • S4 恆偽分支退役原併 e2e 站,隨 e2e 取消暫無落點
  • jedi-survey I-8「問卷審核開關端到端接通」——CM-1501 已把契約與失敗可見性準備好, 但實查發現缺口不只「adapters 沒填」而是平台側消費端尚未建造(見 arc-review §⓪ 停下回報段), 建議另開卡。

🔴 待發版帶出(CM-1501 改到已發版套件的內容)

套件 改了什麼 不補發的後果
jedi-detection C-1 撞號(碼表+contract pin) 已發版 0.0.1 仍帶撞號——唯一有實際使用者影響(檢測工具 uid 報錯在 FE 顯示 DOCX 文案)
jedi-compliance-audit C-1 鏡像碼表同步 同上;PENDING_REPUBLISH 例外表目前有一列
jedi-survey I-8 port 收斂+接線錯誤 log 純防禦性,無 live 影響
jedi-task-platform 守衛+URL 契約+註解 測試與註解,不影響 runtime

補發後:① 主專案 bump pin;② 刪 PENDING_REPUBLISH 那列(test_no_stale_pending_entries 會主動變紅提醒)。

綠色記帳(時機未到不開卡)

四張 hi_* 歷史表 0 筆留另議/flow_template API 欄位併 FE 拿掉/兩套件 harness 待型別償還後補/ 存量紅燈一批(8 紅 guard 未接線+AI dashboard 舊測試 metadata_builder 不存在)/ readmodel 按語境分子資料夾(規格已寫,搬檔未做)。


7. 部署 handover

現況

  • 本機/DEVpoetry install 即可起 BE(path override 已全清,venv 內無 editable jedi 套件)。 守衛 77 綠python -m pytest)。
  • STG/POC本 arc 一律未動(環境異動鐵律)。兩台跑的是 installer 出的 docker stack, 升級走 image,與本分支無關。
  • migration 水位:CM-1490 的軟參照 migration 只套 DEV,STG/POC 等放行。

dev server 驗證(收官串最後一項,未做)

user 2026-09-01 裁:e2e 暫不做,改「dev server 直跑功能驗證」。

  • 候選機=190(e2e 機):安裝包 stack(guidant-ai-1.16.0 六容器)已 docker stop資料卷保留可復原);兩組 agent 容器刻意留跑(檢測派工可能用到); 機上無 BE/FE git clone(歷來跑 image);資源充裕(RAM 31G/碟 142G)。
  • 部署形狀待裁:A=190 上 clone 直跑 dev 模式/B=本機當 dev server。
  • MFA 維持關閉(測試便利;DB 值不入版控,不影響出貨)。

未 push / 未 merge

  • 兩 repo 的 feature/FR-069 分支 push 已委任(memory feedback_fr069_push_delegated_branch_only)。
  • 🔴 合併回 main 仍是 user 本人;arc 收口後委任失效。

8. 本檔的邊界

本 SUMMARY 屬材料性收官(CM-1502 第一段)。下列結論性動作尚未做,等 user 令

  • SPEC 頁面更新(本 arc 對使用者可見的行為變更幾乎為零,預判只有檢測錯誤文案一條)
  • 使用手冊(預判免——純內部重構)
  • 母卡 CM-1435 收 Done
  • memory feedback_* 條目落檔