狀態:第一階段執行中(P0 進行中)|建立日期:2026-08-29 討論脈絡:本案無獨立討論稿——盤點(三棒平行探勘)與拍板均在對話中完成,決策脈絡完整收錄於本文件 §3 決策定案表。
| 日期 | 變更 | 對應 |
|---|---|---|
| 2026-08-31 | D17/D18 拍板入決策表(第四階段地基棒 CM-1475)——D17 資料關聯三律(想建外鍵=該合併疆界/真跨疆界=軟參照存 id 不建約束/流程耦合=事件;外鍵塞不進 port,故疆界切分在資料層另立判準)、D18 設定 schema 申報制(schema 進套件、值與存放留宿主、模組開關表永遠留宿主;含作用域必填/預設活在 code DB 只存差異/fallback 鏈由套件申報三補充)。§7 第四階段節改寫為定序棒次表(4.A→4.B→P12→flow_control→P13→P9/P10→P11→P6,每棒附排序理由);架構手冊新增 package-taxonomy.md 套件分類地圖(十族) |
母案 / CM-1475 |
| 2026-08-30 | 改名棒 CM-1464 執行完畢——monorepo jedi-identity/ → jedi-iam/(import 名 jedi_iam)、三支轉發殼轉發目標同步、主專案 96 處 import 改名;jedi_identity 不留轉發殼(未上 Nexus、無外部消費者),守衛測試加擋舊名復活;§3 D9 與 §7 2.5 節同步改名 |
CM-1464 |
| 2026-08-30 | flow_control 拆出由終局選項升格為第四階段議程(user 指示;與 P13 同席設計,D12 單向依賴耦合);終局選項縮為 project 平台化一項 | 母案 |
| 2026-08-30 | D9 命名修訂:PM 拍板 jedi-identity→jedi-iam(CM-1464 執行);D12 前置查證完成(CM-1462,改寫方向待裁);§8 圖表同步改名 | 母案 / CM-1462 / CM-1464 |
| 2026-08-30 | 新增 §8「套件階層依賴圖」(living)——三層架構圖+port 接線圖+逐套件狀態表(維護主體);原端到端驗收改列 §9。此節隨每次拍板同步更新 | 母案 |
| 2026-08-30 | D13 追加:jedi-department/jedi-resource-store 即刻退役執行(user 拍板、首腦四面查證零使用)——主專案拔 pin 清殘行、monorepo 兩包標 deprecated;§8.3 兩列改「已退役」 | 母案 / CM-1435 |
| 2026-08-29 | 初版定案:P0–P8 第一階段收案(P6 ai-dashboard 降觀望)、P9–P13 凍結進路線圖、authz 拆兩半併 jedi-auth、抽取配方沿用 jedi-information-system 範本 | 母案 |
| 2026-08-29 | D5 拍板——grc 模組改名 flow_control,納入 P0 範圍(P0-⑤) | 母案 / CM-1436 |
| 2026-08-29 | P0 實作已由平行 session 開工(working tree 現況:uploadfile 合併+error code 歸位進行中)。P0-⑤ 改名(D5)為後拍板追加,該棒需讀 CM-1436 卡的範圍追加補做 | CM-1436 |
| 2026-08-29 | D6 拍板——目標升級為「插件(plugin)模式」:套件自帶 api 層 / DI container / migration,隨插即用;驗收核心=拔掉測試。P1 負責定義插件契約首例,P2–P5 照抄 | 母案 / CM-1437 |
| 2026-08-29 | D7 拍板——複用保障三件套:standalone harness+接入 README 為每支套件標配(P1 首做入 SOP);插件 vs 服務化形態分流準則入凍結路線圖 | 母案 / CM-1437 |
| 2026-08-30 | D8 拍板——全路線解凍(廢除「有第二個消費者才抽」原則,P6+P9–P13 全解凍)+新增第二階段(ext 表收斂+JSONB 擴充容器)與第三階段(既有 20 支老套件升級插件模式)+插件契約補「身分脈絡標準件」(user/tenant/org_unit 名冊 port 一族)。§7 由「凍結路線圖」改寫為「第二~四階段與終局選項」 | 母案 / CM-1435 |
| 2026-08-30 | D9 拍板——身分族合併為 jedi-identity(auth+login+mfa+captcha 四合一+主專案身分性質中介層收進套件),插隊為第 2.5 階段(第二階段表收斂後、第三階段補殼前);notification 維持獨立、agent 機器身分不併;順手清理四項(jedi-department 下架等)。另立 RLS 覆蓋缺口獨立 case(user_roles/user_tenants/user_org_units 三表無 RLS),不混入本案 | 母案 / CM-1435 |
| 2026-08-30 | 立疆界盤點案(FR-069.9)——第四階段開工前置:照 jedi-identity 案的方法對全套件庫(26 支+主專案候選模組)做疆界地圖,定出合併/獨立補殼/退役/下架的完整去向,供拍板後改寫第三、四階段棒次表。與第二階段平行(純分析不動碼)。順手:D9 前置條件改「已確認」(user 確認 jedi-* 僅 Guidant AI 消費) | 母案 / CM-1435 |
| 2026-08-30 | D10–D16 批次拍板——疆界地圖(boundary-map.md)與 ext 盤點(phase2-ext-table-inventory.md)裁決落地:問卷疆界合併成立(P11 改寫)、device 不併檢測(P9 改寫)、OSCAL 三角第三邊改 flow_control(P13 開工前先查證)、三支退役先標 deprecated、bulletin 維持獨立套件、CM-1449 首批改打 project_extensions、基礎依賴 port 化通則入插件契約。⚠️ 編號註記:boundary-map §6 自用的 D15(jedi-project 凍結)/ D16(jedi-issue 死碼去留)尚未拍板,本表 D15/D16 依決策表 canonical 重新編定,地圖那兩項改列「待另議」 | 母案 / CM-1446 / CM-1447 |
主專案(compliance-manager-be)歷經多輪功能開發,累積約 40 個註冊模組、1500+ 個 py 檔,多數功能未模組化——所有能力都長在主專案內,未來其他產品要複用相同功能(遠端 agent、授權驗證、防篡改、log 轉發…)時無法直接轉移套用。本案目標:把可通用的功能抽成 jedi- 外部套件*,放進既有 monorepo(~/Projects/Jedicogy/module/jedi-python-package/,已有 20 個套件),未來新產品以 poetry 依賴直接安裝複用。
participant ↔︎ flow_engine ↔︎ grc ↔︎ associations ↔︎ oscal ↔︎ module_frame 六模組互相雙向引用、直接穿透 ORM model,合計約 760 檔(超過全專案一半)——這一團是產品本體,第一階段不切片。common/ + jedi_common,是低風險抽取候選。common/ 不能整包搬:需三分——真橫切的進 jedi-common、授權語意的進 jedi-auth、業務語意的歸位回各模組。且 common/ 現有 9 條反向 import 上層(違反分層方向),是搬遷前的硬前置。IUserLookup 窄介面解耦、套件不含 api 層、守衛測試防回歸。本案全程沿用該配方。整案一句話:先清理地基(P0),再把六支零耦合的基礎設施模組抽成新 jedi 套件(P1–P5),最後把 common/ 的橫切能力上移進 jedi-common 與 jedi-auth(P7–P8);核心業務模組的抽取凍結成路線圖,等這一輪做完拿實際成本數據再議。
| 階段 | 做什麼 | 產出 | 完成怎麼判定(決策者檢查法) |
|---|---|---|---|
| P0 前置清理 | 合併重複的上傳模組、斷 common/ 的 9 條反向引用、錯放的錯誤碼歸位 | 乾淨的主專案地基(不產新套件) | 請 Claude 跑 `grep -rE "from (domain |
| P1 jedi-ai-bot | 把 AI 聊天機器人抽成套件(最小、練手走通整條流程) | 新套件上 Nexus + 主專案 pin | poetry show jedi-ai-bot 看得到版號;系統內 AI 聊天功能手測正常 |
| P2 jedi-integrity | 防篡改偵測(FR-064 成果)抽成套件 | 新套件 + pin | 同上檢查法;落地版啟動防篡改 gate 手測正常 |
| P3 jedi-log-forwarding | Log 轉發(FR-068 成果)抽成套件 | 新套件 + pin | 同上;UI 設定轉發到測試 syslog 收得到 log |
| P4 jedi-remote-agent | 遠端 agent 註冊/心跳/派工(mTLS+JWT)抽成套件 | 新套件 + pin | 同上;agent 心跳與檢測派工手測正常 |
| P5 jedi-license-runtime | 授權驗證/機器指紋/到期狀態機抽成套件 | 新套件 + pin | 同上;換一顆 license 檔啟用流程手測正常 |
| P7 jedi-common 擴充 | 全專案最熱的通用工具(回應包裝等)上移進 jedi-common | jedi-common 新版 | poetry show jedi-common 版號有進;請 Claude 跑既有測試全綠 + 抽測三個常用頁面 API 回應格式不變 |
| P8 authz 併 jedi-auth | 授權守門「主體域」半包併入 jedi-auth | jedi-auth 新版 | 同上;用 admin / 一般帳號各登一次,權限行為與改前一致 |
依賴一句:P0 是 P7/P8 的硬前置;P1–P5 彼此獨立可平行派工;每支套件抽完都要等發版上 Nexus 才能 commit 主專案的目錄移除。決策者功課:每階段收口時做上表「檢查法」欄的手測。
| # | 決策 | 定案 |
|---|---|---|
| D1 | 第一階段範圍 | 收 P0–P5 + P7–P8 共 8 個 arc。P6 jedi-ai-dashboard 降到觀望梯隊——AI 儀表板生成引擎概念上通用,但資料源 registry 深綁本產品 data API,port 化成本不小,且目前無第二個產品確定要此能力;按「沒消費者不抽」原則不過線。P5 jedi-license-runtime 收案——License Center 已是獨立系統、落地版產品線是既定方向,消費者可預期,複用價值最明確。 |
| D2 | 核心模組(P9–P13)處置 | 凍結進路線圖(§7),不排時程。理由:①抽取收益只在「有第二個消費者」時兌現,目前無具體產品在等;②P0–P8 做完會拿到「抽一支套件實際花幾棒、踩什麼坑」的可信成本數據,屆時評估 5–10 倍工程量的核心抽取才有依據;③凍結不是丟掉——P0 清理與守衛測試會讓核心環邊界持續變乾淨,前置成本已被順手支付一部分。例外:若近期出現要用檢測能力的新產品,P9(jedi-detection)可單獨解凍提前。 |
| D3 | authz 去向 | 拆兩半:主體域(platform.py / admin.py / capability.py / decorators.py / signed_token.py——只看「你是誰」)併入 jedi-auth;資源域(project.py / ssp.py / workflow.py / menu_license_filter.py——要先 resolve 資源才知道判誰,帶產品語意)留主專案。被排除方案=獨立 jedi-authz:會立刻新增一條 jedi-auth 依賴邊(monorepo 第 5 條非 common 邊)、強迫消費者永遠裝兩包對版本,且不存在「只要 authz 不要 auth」的消費場景。附帶收益:9 條反向 import 中指向 domain.oscal / domain.participant 的 5 條全在資源域那半,拆半後 P8 的搬遷面自動避開它們。 |
| D4 | 抽取標準配方 | 全程沿用 jedi-information-system(FR-032 G)範本:①dist 名連字號、import 名底線,消費端 import 句一字不改;②跨套件耦合一律 dependency inversion——套件內定義窄 ABC port(如 IUserLookup / INotifier),主專案寫 adapter + DI 注入;③套件只到 app service 層——route、DI container、DB migration 留主專案(套件只 ship ORM model);④extract → publish → pin 是綁定順序——主專案「移除目錄 + pin」的 commit 必須等套件推上 Nexus 之後;⑤每支套件寫一支守衛測試(grep 套件源碼 0 處主專案 import),比照 test/test_fr032g_information_system_decouple.py。 |
| D5 | grc 模組改名 | grc 改名 flow_control(流程控管),納入 P0 範圍(P0-⑤)。改的範圍=python 目錄與類名:app/grc/→app/flow_control/、domain/grc/、infra/grc/、api/grc/ 同步,DI 註冊(containers.py / app_modules.py)跟改,GrcErrorCode→FlowControlErrorCode 等類名跟改。不改=error code 字串值(GRC_404001 等——wire 契約,FE error-code.json i18n 靠字串比對)、對外 API URL path、DB 內容。理由:GRC(治理/風險/法遵)名大於實——模組實際是「流程節點掛業務(頁面/功能/狀態)」的流程控管層,稽核只是第一個掛進來的業務,未來問卷填寫審核等流程都會套此模式;與 flow_engine 成對命名(引擎推流程、控管掛業務);業界對應概念為 case management / process orchestration 的應用層。被排除方案:audit(綁死稽核語意,與通用化願景衝突)、engagement(稽核/顧問業行話,對通用流程不直覺)、process_control(撞工業控制語感)、case_management(case 一詞內部已被 Notion 任務卡佔用)。 |
| D6 | 插件契約(plugin contract) | 抽取目標從 library 升級為 plugin——隨插即用。每支新套件自帶完整垂直切片:①api 層(Flask blueprint / resources)②預設 DI container ③migration 腳本(隨包 package data,安裝時套用)④標準註冊入口(register(app, adapters) 形式,具體簽名由 P1 定案;簽名需預留四類插槽——config(參數值差異)/ adapters(行為差異)/ schema_extensions(欄位差異)/ mount_api(端點卸載逃生門),詳細指引見 CM-1437 卡「API 差異化四道防線」段;此為 D6 實作細節,不另立決策)。主專案接觸面收斂為:config/app_modules.py 一行註冊 + port adapter 實作。驗收核心=拔掉測試:註解掉註冊行後 BE 正常啟動、該模組選單/路由消失、其餘功能不受影響。邊界誠實聲明:做到 install-time pluggable(安裝時可插拔),非 runtime 熱插拔——DB schema / RLS 不可能執行中掛載,對齊業界(GitLab / Odoo 模組)等級。與現行慣例的關係:刻意偏離既有 jedi-* 慣例(套件無 api 層、route 留主專案,即 D4-③)——新契約自 P1 起適用於 FR-069 新抽套件,既有 20 支套件不回頭改;D4 其餘各項(import 名不變、port 解耦、extract→publish→pin、守衛測試)照舊適用。地基盤點:app_modules.py 本就是模組註冊表(fail-fast 載入)、選單走 DB ui_routes、license gating 機制已在——半成品地基可承接。 |
| D7 | 複用保障三件套 | 防「開發完一包就沒了」的三條標配。① standalone harness(每支新套件標配):套件自帶最小獨立宿主——dev_app.py(最小 Flask app)+ docker-compose(起獨立 DB)+套件內測試可脫離主專案獨立跑,並由 CI 獨立執行。動機:「模組能離開宿主活著」是業界判斷真解耦的黃金標準——harness 跑得起來就是零隱藏耦合的鐵證(比 grep 守衛強一級);且 CI 天天跑讓套件有自己的生命跡象,主產品改動導致套件壞掉會當天被抓到,而不是等下個產品要用時才發現屍體。② 接入 README(每支新套件標配):「新產品如何接入」文件——要實作的 port 清單、註冊行寫法、migration 套法、10 分鐘 quickstart。動機:第二個產品團隊評估「讀懂套件 vs 自己重寫」時,沒這份必選重寫(業界 library 複用最常見死因)。③ 形態分流準則(入凍結路線圖 §7,非本階段執行項):P9 之後每支候選啟動前先判——有獨立生命週期的能力(如未來檢測平台)評估走服務化(License Center 模式:獨立部署、網路 API、自有 DB,邊界是網路協定想耦合都耦合不了);與宿主同進退的能力走插件(D6 契約)。理由背景:業界 rule of three——沒有兩三個真實消費者前,抽出的東西只是搬了位置的產品程式碼;①②就是在消費者出現前維持套件可信度的機制。 |
| D8 | 全案解凍與階段重整(2026-08-30) | 廢除「有第二個消費者才抽」原則——動機重新定調為「把歷史發散、耦合過高的能力一次整併成可抽換式,安裝即有 API 可用」,此目標本身即成立,不等客戶。P6(AI 儀表板)與 P9–P13 全部解凍。全案階段結構重整(詳見 §7):第一階段=P0–P8(原範圍,收尾中);第二階段=ext 表收斂+JSONB 擴充容器(純展示型客製欄位收回主表+容器、帶業務邏輯欄位轉正式欄位、auth/user 優先、ext 表模式收斂後退役)+ jedi-common 查詢層補能力(JSONB 擴充欄位查詢/過濾+「身分脈絡包」共通定義);第三階段=既有 20 支老套件升級插件模式——每支補:自帶 api 層、register 四插槽、harness、接入 README、拔掉測試、(有表的)migration 隨包、(有吐稽核欄位的)身分脈絡名冊。分級處理:完整升級(auth / file-upload / notification / survey / flow-engine 等多消費者)/輕量升級(captcha / bulletin / system-menu 等小而穩)/評估退役或合併(盤點時現形者)。jedi-auth 排首棒示範——P8 動完邊界、第二階段動完表、此階段補殼=新標準完全體;第四階段=原凍結區全解凍(P6、P9、P10、P11、P12〔人員指派,解耦鑰匙,本階段先做〕、P13〔OSCAL 三角,P12 之後〕),內部順序於第一階段總結時定;終局選項=flow_control / 專案本體平台化,唯一保留「等商業決策」。三案交錯原則:jedi-auth 被 P8(邊界)→ 第二階段(表)→ 第三階段(殼)各動一個面向,依此順序不互踩。 |
| D9 | jedi-iam 身分套件合併案(2026-08-30) | jedi-auth+jedi-login+jedi-mfa+jedi-captcha(降級為 turnstile 小模組)合併為 jedi-iam;jedi-notification 維持獨立(12 個消費點多與身分無關——flow_control / task_survey / flow_engine / notify_config 都在用;mfa→notification 依賴改 INotifier port 由宿主注入,範式照抄 jedi-auth UserChangePasswordService 現有做法);agent 機器身分(mTLS / X-Agent-Uid)明確不併——身分套件管「人」、jedi-remote-agent 管「機器」,兩套認證體系平行。合併範圍除四支套件外,主專案下列身分性質程式一併收進套件:common/middleware/jwt_mw.py(170 行,JWT 驗證的唯一 HTTP 入口)、socketio_auth.py 的驗簽/blocklist 段(socket 身分入口)、user_context_builder.py(57 行,allowed_tenant_paths 的唯一產生點——RLS 鑰匙)、安全政策定義(security_policy 約 300 行的政策 schema/DEFAULTS——登入鎖定/密碼週期/JWT 效期/強制 MFA;值的存放留宿主)、tenant_provisioning_service.py(214 行,租戶開通;販售模組清單常數拆出留宿主)、root_admin_reader.py/tenant_admin_role_flag.py(合併後改走 domain service,消滅 raw SQL 繞道)、使用者批次匯入編排(寄信 port 化)。UserContextDTO 留 jedi-common(它是 session_scope() 的直接輸入,搬走會讓 jedi-common 反向依賴身分套件)——DTO 當防腐邊界、組裝邏輯進 jedi-iam。X-Tenant-ID 作用租戶覆寫收進套件並補授權檢查(現況只信 header、無任何檢查)。數據支撐:①login→auth 21 處 import 且穿透到 infra 層(password_adapter 直接 import UserRepoImpl)=一個套件被切成兩個目錄;②近三次發版五支全部同批 bump=「獨立演化」從未兌現;③B 類通用膠水 1,300 行(登入編排/MFA 分派/JWT 接線——任何產品都要重抄)+C 類純重複 320 行(LoginConfig 組裝 ×4、OTP 信組裝 ×2 逐字元相同、DI 重複宣告 ×5 container)=第二個產品的重工稅;④死鏈三條(mfa send_otp_mail+11 個測死代碼的測試、圖形驗證碼全鏈 FE 零呼叫、login_logs 五層 8 檔 DB 0 列)。與 D3 的關係:不衝突,是 D3 判準的延伸——D3 排除獨立 jedi-authz 的三理由(新增依賴邊/強迫裝兩包對版/無單獨消費場景)逐字適用於 jedi-login。排序=第 2.5 階段(第二階段表收斂之後、第三階段補殼之前);理由:補殼成本與套件數成正比——先合併再補殼=補 1 次,先補殼再合併=補 5 次然後扔掉 4 次。名稱定案 jedi-iam(2026-08-30 PM 拍板改定,取代開發期暫名 jedi-identity):IAM=Identity and Access Management,業界類別正名(Keycloak/Auth0/Okta 皆屬 IAM 解決方案);identity 一詞僅涵蓋「你是誰」半邊,本套件實質是身分+存取管理(authz 主體域/capability/安全政策/租戶覆寫檢查)全疆界,用 identity 太狹隘。原 2026-08-30 首輪定名 jedi-identity 的推導(「Identity Platform 是業界類別詞」)經 PM 指正不精確——那是廠商行銷用語,類別正名是 IAM。被排除:沿用 jedi-auth(auth 慣例偏認證,承載不了合併後三倍內容物)、jedi-authz(只指授權小角,方向相反)。改名執行=CM-1464(.16 之後、.18 publish 之前一次改;2026-08-30 執行完畢)。jedi_identity 不留轉發殼——它從未推上 Nexus、無任何外部消費者,留殼只會養出第二條 import 路徑;守衛測試已焊死舊名不得復活(test_module_boundaries.py)。被合併的四支舊名(jedi-auth / jedi-login / jedi-mfa / jedi-captcha)維持轉發殼——它們已發版在外,第三階段才下架。前置確認:已確認(2026-08-30)——user 確認 jedi-* 目前僅 Guidant AI 產品線消費,無 breaking change 風險。順手清理四項+依賴大掃除:①jedi-department 下架(與 org_units 同概念兩份實作、無 tenant_id 無 path 不支援多租戶、主專案 pyproject 已註解未安裝——留著日後有人裝回來就是兩張部門表);②「判 root tenant」三份複製收斂為一份(jedi-common _session_paths_have_root() 為 canonical,jedi_auth/authz/platform.py 與 capability_service.py 兩份改為呼叫——三份漂移=授權事故);③roles.is_admin 殭屍旗標連呼叫端刪除(tenant_admin_role_flag.py 檔頭已自承);④jedi-login infra/test_local/ 手測腳本(265 行)清出正式目錄。加碼:合併時 pyproject 依賴大掃除——4 個虛掛依賴(captcha 的 Flask-JWT-Extended/bcrypt、notification 的 bcrypt、auth 的 babel 疑似)+5 支套件的 testcontainers/pytest 錯放主依賴,一併修正。反悔條件:若未來出現「只要登入不要帳號管理」的真實消費者(如純 SSO gateway 產品、user 存外部 IdP),login 獨立才成立。 |
| D10 | 問卷疆界合併(2026-08-30) | 成立——task_survey(作答層)與 jedi-survey(問卷設計)合併為問卷疆界套件;P11 從「抽新套件」改寫為「合併」。鐵證:資料庫有兩條外鍵直接跨過套件與主專案的邊界——套件邊界不可能靠 DB 外鍵縫合,原「把作答層抽成獨立新套件」技術上不可行;2026-03 schema 搬遷腳本刻意把作答表與問卷表放同一 schema,設計意圖即「問卷=設計+作答」。反悔條件:出現「只要問卷編輯器、不要作答」的真實消費者(如純表單設計器產品)。詳 boundary-map.md。 |
| D11 | 檢測疆界——device 不併(2026-08-30) | device 不屬於檢測疆界;P9 改為「detection_tools+detection_execution+common/ 檢測碼」,不含 device。證據:檢測程式對 jedi-device 引用 0;綁 device 的任務 3,012 筆中檢測型 0 筆;商業模型不同(device 是 25/25 租戶標配、檢測是 6/25 加購模組)。device 真正歸屬=資產盤點語境,形態=獨立插件補殼。反悔條件:未來檢測改為「掃描目標必須來自資產主檔」的產品設計。 |
| D12 | OSCAL 疆界改寫(2026-08-30) | 方向同意:真三角的第三邊是 flow_control(62 條)而非 module_frame(18 條),且依賴方向與原 §7 P13 所寫相反——oscal 應用層通用件併入 v2、module_frame 另立、依賴方向定為 oscal → flow_control 單向。附加開工門檻:P13 開工前先派一棒查證「v2 app service 為何被 77 條 RepoImpl 繞過」——答案決定是「搬應用層進套件」還是「在 v2 內補編排層」,查證通過才改寫。查證已完成(2026-08-30,CM-1462):77 條中 97% 為服務層缺能力(讀取面未開放 41+寫入面缺 21)、僅 2 條便宜行事——原設兩選項皆不成立(編排層已存在且在用;應用層含 GRC 產品知識不宜進套件),查證棒建議中間路線「套件補讀取面+缺口寫入、應用層留主專案」,改寫方向待決策者拍板;報告見 docs/analysis/2026-08-30-oscal-v2-repoimpl-bypass-attribution.md。 |
| D13 | 三支退役(2026-08-30) | jedi-oscal v1(可退役,七項證據齊備,順帶消 AGPL 依賴)、jedi-resource-store(殭屍——程式碼零引用、資料表四環境從未建立、路由 2026-02 已刪,併入既有 CM-1143 追蹤)、jedi_system_log(一載入即炸——與 jedi-common 宣告同一張表,import 即錯)三支退役。執行方式:先標 deprecated 觀察一版再刪(防 Nexus 上有未查證的四 repo 以外下載者);jedi_system_log 屬「會炸的碼」優先處理。jedi-log 不整支退役——拆兩半:API 存取紀錄活著(DB 10 萬筆)留、炸的那半(jedi_system_log)刪。2026-08-30 user 拍板追加:jedi-department/jedi-resource-store 即刻退役執行(首腦四面查證零使用——department 主專案零引用/venv 未裝/DB 表不存在/其他套件零依賴;resource-store 程式與 e2e 零引用/表從未建立,惟主專案仍 pin 0.0.11)。已落地:主專案拔 resource-store pin+清 department 註解殘行;monorepo 兩包 README 頂部退役標記+pyproject description 前綴 [DEPRECATED](不動版號不 publish,目錄保留觀察期)。 |
| D14 | jedi-bulletin 去留(2026-08-30) | 維持獨立套件(user 定案:公告是可拔去其他產品使用的插件,有複用前景);被排除方案=下架收回主專案(boundary-map 原建議,基於「套件 408 行 vs 主專案重寫 406 行」的形態稅論——user 以複用前景否決)。其對身分名冊/通知的依賴照 D16 基礎依賴 port 化通則處理,第三階段照補插件殼。 |
| D15 | ext 盤點裁決三件(2026-08-30) | ①CM-1449 首批對象改為 compliance.project_extensions——盤點證實 auth/user 域無可收斂延伸表(totp_secrets 是刻意安全邊界、user_tenants/user_org_units 根本是關聯表非延伸表),project_extensions 是全庫 190 張表中唯一明確收斂對象(4 欄全帶業務邏輯、與主表 1:1 零缺列),也是「跨套件邊界長出側掛」的典型示範;主要風險=14 處手寫 SQL JOIN 散在 9 檔、主表屬 jedi-project 套件(加欄位走套件異動規範)。②**assessment_plan_extensions 孤兒問題(193 列全對不上主表)另開獨立 case 查證**——刪表 vs 修斷鏈是相反動作,查清前 CM-1449 跳過此表。③JSONB 容器定位調整:由「消化存量的手段」改為「預防性建設——今後新客製一律走容器、不再開側表」;理由=盤點證實本系統的側掛表不是客製欄位長出來的、是跨套件邊界長出來的,9 張中純展示型 0 張。 |
| D16 | 基礎依賴 port 化通則(2026-08-30,入插件契約) | 套件對「基礎能力」的依賴,一律判「疆界內知識 vs 疆界外服務」:疆界外服務(誰是使用者/怎麼發通知/檔案存哪/設定值放哪)→ 一律 port 化,套件只宣告需要的服務形狀、宿主接線時注入;疆界內知識留套件。標準 port 四張:身分脈絡包三名冊(IUserNameResolver/ITenantResolver/IOrgUnitResolver,已落 jedi-common)、INotifier(通知)、檔案存取介面(file-upload)、設定讀取。唯一例外=jedi-common——允許直接依賴的地基(session/查詢基底/例外類),不 port 化。第三階段每支補殼必辦:把該套件直接 import 其他套件之處換成標準 port。終局圖景:一層地基(jedi-common)+一排彼此不直接相依的疆界插件,插件間協作全經宿主接線盤——拔走任一支時把接線盤上的線重接到新產品實作即完成移植。 |
| D17 | 資料關聯三律(2026-08-31,入插件契約) | 表與表的關聯不靠外鍵縫合疆界,改用三條判斷律——外鍵是資料庫自己執行的規則,塞不進 port(程式碼可以「不直接認識」,外鍵不行),因此疆界切分必須在資料層另有一套判準。① 想建外鍵=兩張表該住一起:天天連表查、要同生共死的資料,代表同一個能力被切錯了,正解是合併疆界而非硬拆——D10 問卷疆界合併(設計表與作答表跨界外鍵)即此律的鐵證案例,本條把該個案通則化。② 真跨疆界=軟參照:A 表只存 B 的識別碼(id / uid / 帳號),不建外鍵約束;要顯示名稱走名冊 port(D16 三名冊)批次問,查不到必降級顯示識別碼、絕不炸——jedi-log-forwarding 已是實作樣板,「禁止套件 JOIN 宿主 user / tenant / org 表」(D8 身分脈絡標準件既有禁令)是本律的既有特例。③ 流程耦合=事件/port,連識別碼都不存:「B 發生事、A 要知道」是流程通知不是資料關聯,走 port 呼叫或事件,事過境遷不留關聯——INotifier 即此類。業界對應:律①=DDD「兩個領域老要互摸對方的表=邊界劃錯」;律②=GitLab loose foreign keys、微服務 database-per-service、DDD reference-by-ID、Shopify 禁跨模組 JOIN(CI 強制掃);律③=事件驅動架構。已知代價與收拾方式:軟參照後資料庫不再擋「指到已刪除的資料」,由兩件事收拾——「查不到必降級」(律②內建)+定期清理孤兒資料(背景作業,比照 GitLab loose FK+清理器;本案尚未建置,隨第四階段各疆界落地時補)。適用範圍:第四階段每支候選(P12 participant hub/問卷合併/flow_control 拆出/P13 OSCAL)判斷疆界切分時一律依此三律;既有跨界外鍵不回頭全面拆除,隨各疆界抽取時逐案處理(拆到誰才判誰)。與 D10/D11 的關係:D10(問卷合併)是律①的先例、D11(device 不併檢測)是「無外鍵亦無共生關係→本就不同疆界」的反面印證,兩者皆不因本條改判。view 條款(2026-08-31 補):DB view=存在資料庫裡的跨疆界查詢,同受本三律管轄;但守衛測試掃 Python 掃不到 view(CM-1474 實證——.12 雙寫守衛漏掉 vw_user_job_queue),故 view 歸屬必須人工列冊(現存 4 張的歸屬表見 CM-1478 卡)。 反悔條件:若未來出現「跨疆界一致性必須由 DB 保證」的硬需求(如金額結算類),該處回到單一疆界內處理,而非把外鍵加回跨界。 |
| D18 | 設定 schema 申報制(2026-08-31,D6 config 插槽升格正式規格) | 設定的 schema 進套件、值與存放留宿主、模組開關表永遠留宿主——套件管規格書、主產品管實際的值。① schema 進套件:套件於 register() 時申報自己的設定 schema(鍵名/型別/預設值/驗證規則/作用域),隨包出貨;② 值與存放留宿主:宿主照舊集中存 system_configs,並拿套件申報的 schema 驗證寫入——套件不自建設定表、不決定值存哪;③ 模組開關表永遠留宿主:「哪些模組對這個租戶開啟」是宿主的組裝決策,不隨任何套件走(套件不該知道自己有沒有被啟用)。三項補充(2026-08-31 討論定案):(a) 作用域為 schema 的必填欄位——分系統級(這個部署:LDAP 連線、儲存後端,平台管理員改)/租戶級(這個客戶:通知偏好,租戶管理員改)/租戶可覆寫系統級(平台給預設、客戶可換自己的:SMTP 信箱伺服器)三種;儲存機制現成(system_configs 已有 tenant_id 與 (tenant_id, group, key) 唯一鍵),不需新表。(b) 預設活在 code、DB 只存差異——讀取時查無列即回 schema 內建預設,免除 seed 時序問題(套件裝上就有預設值可用,不必等 migration 跑完);若確實需要 seed,只補缺鍵、絕不覆蓋既有值(冪等鐵則——覆蓋會把客戶調過的設定打回原廠)。(c) 讀取 fallback 鏈由套件申報、宿主執行:租戶值→系統值→schema 預設,全鏈皆無則 fail loudly(不可靜默回 None 讓呼叫端當沒設定);客戶只能改自己作用域內的值、寫入須過 schema 驗證、跨作用域寫入須有授權守門。業界同構:GitLab/Odoo/WordPress register_setting()/K8s CRD——皆為「插件宣告形狀、平台管存放」。first case:2.5 階段 security_policy.py(七個設定鍵 schema 進套件、值留 system_configs)已是本制的實例,本條把它升格為正式規格。加分項(非必辦):設定頁由申報 schema 自動生成表單——不必每加一個設定就手刻一次畫面。適用範圍:第四階段新抽套件一律照辦;既有 20 支套件於第三階段補殼時回頭套用(有設定者才做,無設定者免)。 |
created_user / updated_user / tenant_id / org_unit_id 或業務性 user 參照(如 matched_user_id)者,套件必須內建對應名冊 port 並在 app 層批次 enrich(*_user_name、租戶名、單位名);純引擎型無此輸出者免做——標準是「有吐才要補」,不是每支硬裝。IUserNameResolver——帳號批→顯示名稱。P3 jedi-log-forwarding 已有樣板(批次防 N+1、選填、降級顯示帳號),為本標準件的參考實作。ITenantResolver——tenant_id 批→租戶名稱。IOrgUnitResolver——org_unit_id 批→單位名稱。| 分類 | 模組 | 處置 |
|---|---|---|
| ① 產品核心(不抽) | grc(236檔)、oscal(193)、module_frame(79)、associations(67)、participant(90)、project(25)、project_summary_report(36)、task_survey(65)、detection_tools(99)、detection_execution(28)、evidence_classification(42)、device、subtask_status_history、information_system 殼、flow_engine 殼 | 第一階段不動;抽取路徑見 §7 凍結路線圖 |
| ② 既有 jedi 套件的殼(免抽) | auth、survey、bulletin、notify_config、system_config、system_menu、captcha、log、issue/feedback、upload_file | 主專案剩 route + DI wiring + 專案特有 enrich,正是設計上「該留主專案」的部分 |
| ③ 抽取候選(本案主體) | ai(6檔)、integrity(+common/integrity ≈23)、log_forwarding(+common/log_forwarding ≈32)、remote_agent+agent_task(+agent_auth ≈87)、license(+common/license ≈44) | P1–P5 逐支抽出 |
| ④ common/ 分類搬遷 | 通用 util → jedi-common;authz 主體域 → jedi-auth;業務 error code/enum → 歸位回各模組 | P0 歸位、P7/P8 上移 |
di_containers/containers.py 是隱形 hub(401 行、43 個 import):每次抽取都要同步改註冊,是每支套件的固定成本,各 arc 驗收條件都含這一項。| 債 | 內容 | 影響 |
|---|---|---|
| uploadfile / upload_file 雙模組 | api 層用 uploadfile(舊 route)、app/infra/di 用 upload_file(managed file upload),同功能兩套命名並存 |
抽取地雷;P0-① 合併 |
| common/ 的 9 條反向 import | common/authz → domain.oscal(3)、domain.participant(2);1 條 → infra.integrity;3 條 → di_containers.containers |
違反分層方向;不斷掉則 P7/P8 上移後套件對主專案循環依賴。其中 5 條在 authz 資源域(D3 拆半後不隨包搬,但仍該斷);P0-② 處理 |
| 模組專屬 error code / enum 錯放 common/ | grc_error_code(94 處)、module_frame_error_code 等 8 支 + participant_enum(16) 等業務 enum 放在 common/code、common/enum |
讓 common/ 看起來比實際「通用」,混淆 P7 搬遷面;P0-③ 歸位 |
| 去向 | 內容 | 熱度 |
|---|---|---|
| → jedi-common(P7) | util/response_util.py(124 處 import、123 處來自 api 層,全 repo 最熱單檔)、common_util、audit_log、audit_nickname、serialization_util、redis_client_util、excel_util、file_util、pdf_util、savepoint、scheduled_time、code/error_code.py(45)、enum/schema_code.py(60)、http_code_enum、dao_error_enum |
util 267 處 / 43 模組 |
| → jedi-auth(P8) | authz/ 主體域半包:platform.py(9)、admin.py(3)、capability.py(2)、decorators.py、signed_token.py |
authz 全包 140 處 / 32 模組 |
| 留主專案 | authz 資源域(project/ssp/workflow/menu_license_filter)、各模組 error code/enum(P0 歸位)、OSCAL/CMMC 業務工具(control_id_matcher、profile_extractor、cmmc yaml…)、guard 群(participant/round/workflow_project) | — |
| 隨模組帶走 | common/integrity/(→P2)、common/log_forwarding/(→P3)、common/util/agent_auth/(→P4)、common/license/(→P5) |
各自僅該模組使用 |
| 子項 | 內容 | 要點 |
|---|---|---|
| P0-① | 合併 uploadfile / upload_file |
收斂為 upload_file 單一命名;api route 檔搬併、config/app_modules.py 與 containers.py 註冊同步;歷史 URL 路徑不變(只動 python 模組名不動 endpoint path) |
| P0-② | 斷 common/ 9 條反向 import | authz→domain 的 5 條:resolve 邏輯改由呼叫端傳入 entity 或改 port 注入;→infra.integrity 1 條:改 DI 注入;→di_containers 3 條:改 lazy import 或注入。完成判準:`grep -rE "^from (domain |
| P0-③ | error code / enum 歸位 | 8 支模組專屬 error code + 業務 enum 從 common/code、common/enum 搬回各模組目錄(如 app/grc/code/);import 路徑變動用 IDE 級全域替換 + 全量 import 檢查 |
| P0-④ | 守衛測試基礎 | 建 test/test_module_boundaries.py 骨架:斷言 common/ 無反向 import、後續各套件抽出後逐支加「不得 import 主專案」斷言 |
| P0-⑤ | grc → flow_control 改名(D5) | 與 P0-③ error code 歸位同批 import 替換執行(省一輪全域替換與全量驗證)。目錄/類名/DI 註冊改,error code 字串值、API URL path、DB 內容不動。完成判準:grep 全 codebase 無 app.grc / domain.grc / infra.grc import 殘留;grep -rn "GRC_" (error code 字串值)確認未被改動;BE 重啟 + 稽核主流程手測正常 |
驗收:上述 grep 為 0;BE 啟動正常;檔案上傳、任一稽核頁面手測正常;flow_control 改名三判準(import 殘留 0 / 字串值未動 / 主流程手測)全過。
api/ai/(blueprint / route)與 app/ai/(AiBotService,Anthropic Claude + Redis 對話)一併進套件——套件自帶 api 層 + 預設 DI container + register(app, adapters) 註冊入口(本模組無 migration,migration 隨包機制的首例落在 P2–P5 中第一支有表的套件)。6 檔,最小案例。register() 簽名、blueprint 掛載方式、container wiring 慣例、拔掉測試步驟,全部在本 arc 定案並寫進 SOP checklist,P2–P5 照抄。dev_app.py + compose(本模組吃 Redis,compose 起獨立 Redis)+套件測試獨立跑;接入 README 含 port 清單 / 註冊行 / quickstart。app/infra/di 的 integrity 模組 + common/integrity/ 全包(lockdown / startup_gate / runtime_check / tamper_marker / forensic / lc_report / unlock)。config.config 的讀取改為初始化參數傳入。common/log_forwarding/(forwarder / gelf_handler / settings_reader)。remote_agent(註冊/enroll token/心跳)+ agent_task(派工/查詢)+ common/util/agent_auth/(ca/tls/jwt/heartbeat mTLS)。約 87 檔,第一階段最大宗。IAgentTaskPayloadProvider 之類的派工內容組裝介面——檢測派工的 params 組裝屬產品知識,留主專案實作注入;套件只管「派工信封」的生命週期(pending/領取/回報/逾時)。common/license/(engine / machine_fingerprint / public_keys / schema)。INotifier(notify(recipients, subject, body) 級別的窄介面),主專案 adapter 包 jedi_notification。common/integrity 若有交叉引用,依 P2 先行的套件邊界處理(license-runtime 依賴 jedi-integrity 是合法 jedi 間邊)。>= 約束不需動;主專案 poetry update jedi-common 一次收攏。common/authz/ 的 platform / admin / capability / decorators / signed_token 進 jedi-auth 新子包 jedi_auth/authz/;資源域四支留原地。common/authz/__init__.py 保留 re-export shim(主體域符號轉 import jedi_auth.authz),消費端 32 個模組零改動;shim 標 deprecated,後續新 code 直接 import jedi_auth。| Arc | 依賴 | 可平行 | 驗收條件 |
|---|---|---|---|
| P0 前置清理(含 grc→flow_control 改名)|進行中(平行 session) | — | 起點 | 反向 import grep=0;上傳功能手測正常;守衛測試骨架進 test/;grc 舊 import 路徑殘留 0、error code 字串值未動、稽核主流程手測正常 |
| P1 jedi-ai-bot|與 P0 檔案零重疊,可平行派工 | 無(P0 非硬前置) | 與 P2–P5 平行 | 套件上 Nexus + pin;AI 聊天手測;插件契約定案(register 簽名等)+拔掉測試通過;harness+接入 README 首例(D7);SOP checklist 產出 |
| P2 jedi-integrity | 建議在 P1 SOP 之後 | ∥ | 套件 + pin;落地版 startup gate 手測;守衛測試;拔掉測試;harness+接入 README |
| P3 jedi-log-forwarding | 同上 | ∥ | 套件 + pin;轉發到測試 syslog 收到 log;守衛測試;拔掉測試;harness+接入 README |
| P4 jedi-remote-agent | 同上 | ∥ | 套件 + pin;agent 心跳 + 檢測派工端到端手測;守衛測試;拔掉測試;harness+接入 README |
| P5 jedi-license-runtime | 同上 | ∥ | 套件 + pin;license 啟用/到期流程手測;INotifier adapter 生效;守衛測試;拔掉測試;harness+接入 README |
| P7 jedi-common 擴充 | P1–P4 完成後(讓新套件先穩定) | 否 | jedi-common 新版;pytest 全綠 + 頁面手測清單;response_util 批單獨回歸 |
| P8 authz 併 jedi-auth | P0-② + P7 | 否 | jedi-auth 新版;四角色權限行為一致;shim 生效、消費端零改動 |
每個 arc 共同的固定項目:di_containers/containers.py 註冊同步、config/app_modules.py 同步(如適用)、jedi-packages.md 補該套件一段、Notion 子卡回寫、extract→publish→pin 順序紀律。
2026-08-30 D8 拍板:原「凍結路線圖」全解凍,「等第二消費者」原則廢除——整併本身即目標。各階段先後與交錯於第一階段(P0–P8)總結時排定;下表順序為建議序。
2026-08-30 盤點定案(D15,phase2-ext-table-inventory.md):掃 190 張實體表,真正的延伸表 9 張、純展示型 0 張——本系統的側掛表不是「客製欄位」長出來的,是「跨套件邊界」長出來的(主表在套件、產品欄位沒地方放才側掛)。故本階段實作重點由「JSONB 容器消化存量」調整為「跨套件側掛欄位收回主表正式欄位」;JSONB 容器定位改為預防性建設——今後新客製一律走容器、不再開側表(查詢能力已由 CM-1448 落地 jedi-common)。
project_extensions)、留置 6、待查證 1(assessment_plan_extensions 孤兒表,另開 case)、結構異常另記 1(ssp_system_characteristics 缺唯一性約束)。compliance.project_extensions(D15;原「auth/user 優先」經盤點證實無收斂對象,已改)。「只加不破」五步搬遷(加欄位→回填→雙寫期→逐處切讀取→觀察後 drop)+帶舊資料升級驗證;主要風險=14 處手寫 SQL JOIN(散 9 檔,改漏不報錯只靜默少過濾軟刪除)+主表屬 jedi-project 套件(加欄位走套件異動規範)。_ext_* 前綴族)、身分脈絡包三名冊、SessionMixin 正名皆落地,未發版。session/database/ 頂層 base_repository.py(7 行 session property)不是舊代殘留——它是 lazy session 提供者(mixin),repository/base_repository_impl.py 的完整查詢基底自己就繼承它(class BaseRepositoryImpl(IBaseRepo[T, Q], BaseRepository, ...))。兩者是「連線底座 vs 完整基底」的零件關係,不是新舊兩代。@property session」條)——不做汰換。repository/base_repository.py(查詢介面)同名不同角色,會誤導讀者當成待淘汰舊版——本案調查即因此差點誤發一輪假清理。SessionMixin;挪位與否屆時定),舊名留轉發(32+ 處引用零即刻改動)。base_model.py 為真重複(與 model/base_model.py 內容漂移)且全生態 0 引用——直接刪。插在第二階段(表收斂)之後、第三階段(補殼)之前——先合併再補殼=補 1 次;先補殼再合併=補 5 次扔 4 次。完整決策內容與數據支撐見 §3 D9。
| 動作 | 內容 |
|---|---|
| 四支合一 | jedi-auth+jedi-login+jedi-mfa+jedi-captcha(降級為 turnstile 小模組)→ jedi-iam(暫名 jedi-identity,PM 2026-08-30 定調改名);舊套件名留轉發過渡 |
| 主專案身分程式收進套件 | jwt_mw(JWT 入口)、socketio_auth 驗簽段(socket 入口)、user_context_builder(RLS 鑰匙產生點)、安全政策定義(值存放留宿主)、tenant_provisioning(販售模組常數留宿主)、root_admin_reader/tenant_admin_role_flag(改走 domain service)、使用者批次匯入編排(寄信 port 化)、X-Tenant-ID 覆寫(並補授權檢查) |
| 明確不併 | jedi-notification(基礎設施,12 消費點多與身分無關;mfa 發信改 INotifier port);agent 機器身分(mTLS/X-Agent-Uid,歸 jedi-remote-agent);UserContextDTO 留 jedi-common 當防腐邊界(組裝進套件) |
| 順手清理 | ①jedi-department 下架(與 org_units 重複且無多租戶)②「判 root tenant」三份複製收斂為一份(jedi-common canonical)③roles.is_admin 殭屍旗標連呼叫端刪④jedi-login infra/test_local 手測腳本清出;加碼 pyproject 依賴大掃除(4 虛掛+5 支測試依賴錯放) |
| 前置確認 | ✅ 已確認(2026-08-30):jedi-* 僅 Guidant AI 產品線消費 |
純分析案不動碼。第四階段的開工門檻——P6/P9–P13 各棒開工前必須先有疆界地圖定案。動機:以前按技術功能切太碎,26 支裡不少是「同一個能力被切成幾半」(jedi-iam 案已證實一組);先畫清疆界再動工,避免抽完又要合。
方法(照 jedi-iam 案的同一套判準與分析深度):①實際耦合數據(跨套件 import 具體到符號、穿透層級)②發版歷史(獨立生命有無兌現)③主專案膠水量化(A 產品特定/B 通用重寫/C 純重複三分類)④死碼盤點⑤pyproject 依賴健檢。
待驗證的疆界候選五組(初步假設,以數據為準):
| # | 候選疆界 | 內容物 | 對既有計畫的影響 |
|---|---|---|---|
| 1 | 檔案疆界 | jedi-file-upload+jedi-resource-store | — |
| 2 | 問卷疆界 | jedi-survey+主專案 task_survey | 改寫 P11——從「抽新套件」變「合併成疆界套件」 |
| 3 | 檢測疆界 | jedi-device+主專案 detection_tools/detection_execution | 改寫 P9 |
| 4 | OSCAL 疆界 | jedi-oscal(退役查證)+jedi-oscal-v2+module_frame+oscal 應用層 | 改寫 P13 |
| 5 | 追蹤疆界(弱候選) | jedi-issue+jedi-bulletin+主專案 feedback | 可能只是「都很小」而非同疆界,待數據 |
另兩項退役查證:jedi-log(vs 新日誌體系的重疊度)、jedi-oscal v1(vs v2 的取代狀態)。
產出物:「最終疆界地圖」——26 支現有套件+主專案候選模組的完整去向表(合併/獨立補殼/退役/下架),供 user 拍板後改寫第三、四階段的棒次表。
願景:套件庫從 26 支細粒度零件收斂為約 12–15 支輪廓清楚的疆界套件。
身分族已於第 2.5 階段合併為 jedi-iam——本階段補殼時身分族僅 1 支。補殼對象清單已按疆界盤點結果(2026-08-30 拍板 D10–D14)更新如下——同疆界者先合併再補殼,避免補完又扔。
每支補齊 D6/D7/身分脈絡標準件(老套件缺的是「插件的殼」,最難的解耦已完成,工程量比新抽輕),並執行 D16 基礎依賴 port 化(把直接 import 其他套件之處換成標準 port 四張——這是每支補殼的必辦項)。主專案側同步把每支的接線收斂成「一個接線檔」,並隨升級逐步刪除 28 處 app 層自行補名的歷史重工。
隨棒必做(追蹤卡 CM-1461):2.5 階段與本階段各棒動到某模組的 route 註冊時,順手拔除該模組的裸路徑死端點(62 支總帳見卡——同 Resource 雙路徑註冊缺 handler、打了必 500 但 FE 零裸打;CM-1454 Postman 掃描發現),拔前確認 e2e/agent 無呼叫,拔後回卡勾銷。
| 分級 | 套件 | 處理 |
|---|---|---|
| 完整升級 | jedi-iam(2.5 階段產物,首棒示範)、jedi-file-upload(唯一有第二使用者的套件——jedi-issue 在用)、jedi-notification、jedi-flow-engine、問卷疆界套件(D10 合併產物) | 完整五件套(api 層/register/harness/README/拔掉測試+適用者補 migration 隨包與名冊)+D16 port 化 |
| 輕量升級 | jedi-bulletin(D14 定案維持獨立,名冊/通知依賴 port 化)、jedi-system-menu、jedi-system-config、jedi-device(D11 定案不併檢測、歸資產盤點語境)、jedi-information-system、jedi-issue、jedi-log(拆兩半後留下的 API 存取紀錄半) | 補 register 入口+README,harness 從簡+D16 port 化 |
| 退役/下架(D13,先標 deprecated 觀察一版) | jedi-oscal v1(七項證據、消 AGPL)、jedi-resource-store(併 CM-1143)、jedi_system_log(會炸的碼,優先);另 jedi-department(D9 已定下架) | 不補殼,不花力氣升級殭屍套件 |
| 待另議 | jedi-project(boundary-map 建議短期凍結不補殼、長期併「專案疆界」——涉終局選項,未拍板)、jedi-issue 內 58% 死碼去留(gitlab/github 整合 1,191 行——「已廢棄 vs 留給未來客戶」屬產品決策,未拍板) | 決策前不動 |
每支候選開工前,先判「插件 vs 服務化」:
棒次順序(2026-08-31 user 拍板定序,卡號 CM-1475–1483 九張連號)——每棒一句排序理由:
| 棒次 | 卡 | 項目 | 內容 | 為什麼排這個位置 | 關鍵事實 |
|---|---|---|---|---|---|
| 4.A | CM-1475 | 地基棒 | D17/D18 通則入 §3 決策表+套件分類地圖(架構手冊 package-taxonomy.md)+跨疆界聚合查詢正名搬 infra/readmodel/ |
排第一因為後面每棒都讀它——runner 讀不到對話只讀得到文件,通則不落地就每棒重新發明判準;聚合查詢正名是 P12 的硬前置(「我的任務」聚合正躺在即將抽出的人員指派模組裡,SQL 字串裡的跨表依賴 grep 守衛與 harness 都抓不到,誤搬進套件=靜默炸) | 8 支聚合 query 約 1,500–2,000 行、每支跨 3–4 schema(盤點見 docs/analysis/2026-08-31-cross-boundary-aggregation-api-inventory.md) |
| 4.B | CM-1476 | AI registry 自註冊 | api_registry.py 由硬編碼表改為各套件自註冊,宿主只組裝目錄;順修 2 個指向不存在 provider 的壞條目 |
排在核心抽取之前、但可與 P12 平行——不改的話每抽一支套件就要回頭改 525 行硬編碼表+14 個 container resolver,抽越多改越多次;與 P12 無共用檔案故可平行 | api_registry.py 28 條 API 覆蓋 14 模組;:244/:254 兩條 provider 不存在(lambda 延遲求值故起服務不炸,AI 選到才 500) |
| 4.1 | CM-1477 | jedi-participant(P12) | 多層級任務指派(~90 檔) | 核心抽取首棒——解核心環的鑰匙:抽完最粗兩條邊自動消失,後續各支才好動;且 out-degree 僅 8,是全部候選裡改動面最小的 | 解核心環的鑰匙:全專案最大 hub(被 60+ 處依賴)但 out-degree 僅 8——import 名不變原則下,消費端(flow_engine 52 條、grc 43 條)零改動,只需斷自己的 8 條線。抽完核心環最粗兩條邊自動消失,後續各支才好動。TaskAssigneeService 切法已裁(2026-08-31):純 CRUD 隨套件走、跨疆界聚合讀取留主專案 |
| 4.2 | CM-1478 | flow_control 拆出+流程疆界三方設計 | 稽核執行業務層拆出(拆法「先單純拆出、切分後議」,見下方終局選項段);flow-engine/flow_control/project 三方同席定 route 與 app 層歸屬 | 排 P12 之後、P13 之前——P12 先解掉最粗的 participant 邊,搬家包袱最小;又必須在 P13 之前,因 D12 已定 oscal → flow_control 單向依賴,OSCAL 疆界設計必須知道 flow_control 自己也會成為套件,否則拆完返工 | 稽核執行業務層 37 支 service/236 檔;3.4 實查證實 flow-engine 19 條 route 有 17 條吃稽核業務、6,375 行 app/flow_engine 疆界未定(CM-1470 裁併入本案) |
| 4.3 | CM-1479 | OSCAL 疆界(D12 改寫) | oscal 應用層通用件併入 jedi-oscal-v2;module_frame 另立;三角第三邊=flow_control(62 條,非原寫的 module_frame 18 條);依賴方向定為 oscal → flow_control 單向(與原文件所寫相反) | 緊接 flow_control 之後——三角第三邊是 flow_control,它定形了 OSCAL 才有邊界可劃 | 開工門檻(D12):先派一棒查證「v2 app service 為何被 77 條 RepoImpl 繞過」——答案決定「搬應用層進套件」還是「在 v2 內補編排層」。P12 之後做。中間路線已裁採納(2026-08-31):套件補讀取面+缺口寫入、應用層留主專案(CM-1462 查證結論,推翻原兩選項) |
| 4.4 | CM-1480 | jedi-detection | detection_tools + detection_execution + common/ 檢測碼(不含 device——D11 定案,device 歸資產盤點、獨立補殼) | 核心環解完才動邊緣——它往外只有 participant/flow_engine/notification 各 1–2 條,那些線在 4.1/4.2 已先斷乾淨 | 核心邊緣:往外僅 participant/flow_engine/notification 各 1–2 條,全 port 化即可;grc 對它的 7 條反向依賴不需處理(届時 import 套件)。形態分流:檢測平台有獨立生命週期特徵,開工前先過服務化評估 |
| 4.5 | CM-1481 | jedi-evidence-classification | AI 佐證分類引擎(~42 檔) | 可與 4.4 平行——零 incoming、與檢測無共用檔案 | 零 incoming;6 條 outgoing 用 IEvidenceSource / IControlCatalog 兩 port 斷 |
| 4.6 | CM-1482 | 問卷疆界合併(D10 改寫:不再抽新套件) | task_survey 作答層併入 jedi-survey 成問卷疆界套件 | 排後段因為它是合併不是抽取——不解核心環、也不擋別人,且問卷作答層與 flow_control 有牽連(任務帶問卷),等 4.2 定形後再併最省事 | 鐵證:兩條 DB 外鍵跨過套件邊界,原「抽獨立新套件」技術上不可行;2026-03 schema 搬遷已把作答表與問卷表放同 schema。合併後照 D6/D7 補殼 |
| 4.7 | CM-1483 | jedi-ai-dashboard | AI 儀表板生成引擎(~30 檔) | 收尾棒——資料源 registry 是全系統疆界的鏡子,等所有疆界都定形了才知道要對哪些套件宣告介面(4.B 已先把註冊機制換好) | 零跨模組依賴;資料源 registry 深綁本產品 data API,抽引擎、資料源以 port 注入(原 D1 降觀望,D8 解凍收回) |
| 待定序 | 未開卡 | cloud_integration / feedback / setup | Drive 整合(~72 檔)/意見回饋(~25 檔)/開通精靈(~12 檔) | 三支併入第四階段排程時一起定順位(原文保留) | cloud_integration:7 條 flow_engine/participant 線;feedback:斷 2 條 system_config 線即 A 級;setup:與安裝流程綁定深——三支併入第四階段排程時一起定順位 |
| 不抽 | — | associations | 純關聯表膠水(67 檔) | — | 永不獨立成套件——每張關聯表跟著「擁有語意的那端」走,在第四階段過程中逐步溶解 |
flow_control 拆出——移入第四階段議程(user 2026-08-30 指示;已確認指的是稽核執行業務層那 37 支 service/236 檔,非 flow_engine 的範本管理):不再等商業決策,Phase 4 規劃時與 P13 同席討論。拆法已定調(user 同日拍板):「先單純拆出、切分後議」——照 jedi-iam 驗證過的「先搬家、後裝修」路徑:第一步整包搬成套件(import 名不變、稽核業務邏輯原樣帶走、機械比對證明零行為改動),第二步「通用流程骨架 vs 稽核專屬業務」的切分等真實第二種流程(問卷審核等)要掛入時拿真需求當設計輸入再做——無第二消費者硬切必畫錯線。技術耦合:D12 已定 oscal → flow_control 單向依賴,OSCAL 疆界設計必須知道 flow_control 自己也會成為套件,否則拆完返工;排序(P12/P13 之前或之後)屆時拿依賴數據定,初步傾向 P12 之後(先解最粗的 participant 邊、搬家包袱最小)。
終局選項(仍等商業決策):project 平台化——產品本體抽套件、主專案變組裝殼。等第二個合規產品線出現再議。
另案追蹤(不屬本案範圍):RLS 覆蓋缺口——
user_roles/user_tenants/user_org_units三張授權關聯表未啟用 RLS(live DEV 實查確認),現靠「讀取都經過受 RLS 的 users/roles」間接保護;任何直查這三張表的新程式即為跨租戶洩漏風險。已另開獨立 Notion case 追蹤(D9 身分族盤點的附帶發現),不混入 FR-069。
🔴 本圖描述 D1–D16 拍板後的「目標架構」,不是現況——現況到目標的路徑見 §7 各階段章節。每次新決策改變套件版圖(合併定形、退役期滿刪除、查證翻案),本圖與下方狀態表同步更新,並在變更紀錄註記。決策編號引用以 §3 決策表為 canonical。
維護慣例:狀態表是維護主體(好 diff、好逐列更新),mermaid 圖是給人看全貌的輔助——改表必改圖,改圖必改表。
架構只有一條實線規則:疆界插件只准直接依賴 jedi-common(層 0);插件彼此絕不直接相依,插件間的協作一律經宿主(層 2)的接線盤走 port 注入(見 8.2)。
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart TB
subgraph L2["層 2 宿主/產品(接線盤)"]
HOST["Guidant AI 主專案<br/>核心業務(flow_control/OSCAL 應用層)<br/>+各插件 adapter 接線+資源域授權"]
end
subgraph L1["層 1 疆界插件(彼此不直接相依)"]
direction TB
subgraph L1A["身分與基礎服務"]
IDENTITY["jedi-iam<br/>(2.5 階段成形:auth+login<br/>+mfa+captcha+身分中介層)"]
NOTIF["jedi-notification"]
FILEUP["jedi-file-upload"]
end
subgraph L1B["第一階段五支(已發版)"]
AIBOT["jedi-ai-bot"]
INTEG["jedi-integrity"]
LOGFWD["jedi-log-forwarding"]
RAGENT["jedi-remote-agent"]
LICRT["jedi-license-runtime"]
end
subgraph L1C["疆界重劃中(第四階段定形)"]
SURVEY["jedi-survey-suite(暫名)<br/>survey+作答層(D10)"]
DETECT["jedi-detection<br/>不含 device(D11)"]
OSCALV2["jedi-oscal-v2 系<br/>(D12 查證後定形)"]
end
subgraph L1D["獨立補殼者(第三階段)"]
OTHERS["jedi-device/jedi-bulletin/jedi-issue<br/>jedi-system-config/jedi-system-menu<br/>jedi-information-system/jedi-project<br/>jedi-flow-engine/jedi-log(存活半)"]
end
end
subgraph L0["層 0 地基(唯一允許被直接依賴)"]
COMMON["jedi-common<br/>session/查詢基底(含 JSONB)/例外<br/>身分脈絡包介面/SessionMixin"]
end
HOST --> L1
L1A --> COMMON
L1B --> COMMON
L1C --> COMMON
L1D --> COMMON
退役中(D13,標 deprecated 觀察一版後刪):jedi-oscal(v1)、jedi-resource-store、jedi_system_log(jedi-log 的壞半)。已下架:jedi-department(org_units 重複概念)。這四支不入圖。
D16 通則的四張標準 port:插件宣告需要什麼形狀的服務,宿主接線時把提供方的實作(包成 adapter)注入——提供方與消費方互不知道對方存在。
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart LR
PLUGINS["任一疆界插件<br/>(bulletin/license-runtime/<br/>log-forwarding/survey-suite …)"]
HOST["宿主接線盤<br/>(register 時注入 adapters)"]
IDENTITY["jedi-iam"]
NOTIF["jedi-notification"]
FILEUP["jedi-file-upload"]
SYSCONF["jedi-system-config"]
PLUGINS -. "①身分名冊 port<br/>(user/tenant/org 三名冊)" .-> HOST
PLUGINS -. "②INotifier port" .-> HOST
PLUGINS -. "③檔案存取 port" .-> HOST
PLUGINS -. "④設定讀取 port" .-> HOST
HOST -. 注入 adapter .-> IDENTITY
HOST -. 注入 adapter .-> NOTIF
HOST -. 注入 adapter .-> FILEUP
HOST -. 注入 adapter .-> SYSCONF
| 套件 | 層級 | 狀態 | 決策編號 |
|---|---|---|---|
| jedi-common | 0 地基 | 已發版 0.0.32(P7 擴充:JSONB 查詢/身分脈絡包/SessionMixin 正名) | D4、D16、正名案 |
| jedi-iam(原暫名 jedi-identity) | 1 | 合併中(2.5 階段:.13/.14/.15/.16/.17 已收;改名棒 CM-1464 已執行完畢,餘 .18 收口發版) | D9 |
| jedi-notification | 1 | 待補殼(維持獨立——12 消費點多與身分無關) | D9、D16 |
| jedi-file-upload | 1 | 待補殼(維持獨立——jedi-issue 為第二消費者) | 疆界地圖① |
| jedi-ai-bot | 1 | 已發版 0.0.1(插件契約首例) | D6、D7 |
| jedi-integrity | 1 | 已發版 0.0.1 | D6、D7 |
| jedi-log-forwarding | 1 | 已發版 0.0.1 | D6、D7 |
| jedi-remote-agent | 1 | 已發版 0.0.1(migration 隨包首例;機器身分不併 identity) | D6、D7、D9 |
| jedi-license-runtime | 1 | 已發版 0.0.1(驗證核心;執法留宿主) | D6、D7 |
| jedi-survey-suite(暫名) | 1 | 待合併(第四階段:jedi-survey+主專案作答層,跨邊界 FK 鐵證) | D10 |
| jedi-detection | 1 | 待抽取(第四階段 P9,不含 device) | D11 |
| jedi-oscal-v2 系 | 1 | 待定形(第四階段 P13——真三角第三邊為 flow_control,開工前先派查證棒) | D12 |
| jedi-device | 1 | 待補殼(獨立——歸資產盤點語境,非檢測) | D11 |
| jedi-bulletin | 1 | 待補殼(維持獨立套件——user 定案可拔往其他產品;名冊/通知依賴走 port 化) | D14、D16 |
| jedi-issue | 1 | 待補殼(死碼去留「待另議」——boundary-map 原 D16 項) | 疆界地圖⑤ |
| jedi-system-config | 1 | 待補殼 | — |
| jedi-system-menu | 1 | 待補殼(輕量——與 ui_routes 無重複,已查證) | — |
| jedi-information-system | 1 | 待補殼(輕量;FR-032 G 抽取先例) | D4 |
| jedi-project | 1 | 待補殼(凍結與否「待另議」——boundary-map 原 D15 項;project_extensions 收斂會動它,見 CM-1449) | D15-① |
| jedi-flow-engine | 1 | 待補殼(P12 participant 抽出後評估殼內邏輯併回) | 凍結路線圖 |
| jedi-log(存活半) | 1 | 待拆兩半(API 存取紀錄 10 萬筆活資料留;jedi_system_log 壞半退役) | D13 |
| jedi-oscal(v1) | — | 退役中(deprecated 觀察一版;順帶消 AGPL 依賴) | D13 |
| jedi-resource-store | — | 已退役(2026-08-30 user 拍板即刻執行:主專案 pin 已拔、deprecated 標記完成;實體下架待觀察期) | D13 |
| jedi_system_log | — | 退役中(一載入即炸——與 jedi-common 表衝突) | D13 |
| jedi-department | — | 已退役(2026-08-30 user 拍板即刻執行:主專案註解殘行已清、deprecated 標記完成;實體下架待觀察期) | D9 清理項/D13 追加 |
| jedi-login/jedi-mfa/jedi-captcha | — | 併入 jedi-iam 後消失(舊名留轉發過渡) | D9 |
pyproject.toml 以 == pin;jedi-common、jedi-auth 各完成一波擴充發版。common/ 只剩資源域 authz、業務工具與真正的主專案膠水。grep 0 處主專案 import;common/ 反向 import 0 條。docs/claude/jedi-packages.md 補齊六支新套件段落(並順帶補上既有未登記的 13 支)。