FR-069.9 疆界地圖 — 全套件庫去向盤點

FR-069.9 疆界地圖——26 支套件+主專案候選模組的完整去向

狀態:分析完成,待決策者拍板|建立日期:2026-08-30|對應卡:CM-1446(FR-069.9) 母案:FR-069(design.md §7「疆界盤點案」)|純分析案,未動任何程式碼 本文為第四階段(P6/P9–P13)與第三階段(老套件補殼)的開工門檻——地圖未拍板前,該兩階段不排棒。

變更紀錄

日期 變更 對應
2026-08-30 初版:六組平行盤查+首腦交叉驗證,產出去向表與 D10–D16 待拍板清單 CM-1446

0. 三十秒版(先看這段)

盤了 26 支套件+15 個主專案候選模組,結論是「合併 3 組、退役 3 支、下架評估 1 支、其餘獨立補殼」,套件數從 26 收斂到約 15–17 支。

五組候選疆界的驗證結果只有兩組成立,這正是先盤點再動工的價值——若照原假設直接開工,有三組會白做:

原假設 驗證結果
① 檔案疆界(file-upload+resource-store) 假設錯。兩支零耦合、領域不同;真相是 resource-store 整支死碼該退役
② 問卷疆界(survey+task_survey) 成立且證據最硬(跨半 DB 外鍵)
③ 檢測疆界(device+detection_*) 見 §3 ③
④ OSCAL 疆界 ⚠️ 部分成立:oscal 應用層併入 v2,但 module_frame 不併;且「三角」的第三邊抓錯了
⑤ 追蹤疆界(issue+bulletin+feedback) 證偽。互相 import=0、整合範式相反、租戶模型相反

兩項退役查證:jedi-oscal v1 → 可退役;jedi-log → 不可整支退役,但應拆兩半(一半活著、一半是「載入即炸」的不可用碼)。

本案順帶挖出四個 P0 級問題(詳 §5),其中兩個是會炸的地雷、兩個是出貨污染。


1. 方法與證據標準

照 D9(jedi-iam 案)的同一套五判準,六組平行盤查後由本案彙整者逐項交叉驗證(不採信分組自報,關鍵結論一律親跑指令覆核):

# 判準 判讀方式
實際耦合 跨套件 import 具體到符號穿透層級(碰 app/domain 介面=淺;碰 infra/ORM model=深,深穿透=「一個能力被切成兩個目錄」的鐵證)
發版歷史 各支有無獨立的發版節奏
主專案膠水 A 產品特定(合理留宿主)/B 通用流程(任何產品都要重抄=切錯線的稅)/C 純重複
死碼盤點 零呼叫功能鏈、零筆資料的表
pyproject 依賴健檢 虛掛依賴、測試依賴錯放主依賴

三個會導致反向結論的陷阱(本案實際踩到,記錄供後續棒次沿用)

  1. pg_stat_user_tables.n_live_tup 對分割表母表一律回報 0。本案彙整者第一輪即被騙——pg_stat 說 api_logssystem_logs 是 0 列,實際 count(*)102,205615,122 列。若照 pg_stat 判讀,會得出「jedi-log 是死碼可退役」的完全相反結論。零筆判定一律用 count(*)
  2. DB 是多 schema 的public 58/oscal 58/compliance 46/survey 15/config 13 張表)。用 public.<table> 查 survey 或 detection 的表會得到「relation does not exist」而誤判成死碼。
  3. dist 名 ≠ import 名jedi-log 底下沒有任何 jedi_log 模組,實際是 jedi_api_logjedi_system_log 兩個 package。用 jedi_log 掃會零命中而做出假的退役結論。全 26 支中只有這一支有此落差。

發版歷史的判讀更正

monorepo 有兩支 CVE 全掃 commit0bdaddec6188f7)各一次動 18 支套件的 pyproject。這不是「同批升版=獨立生命從未兌現」的證據,只是資安批次補丁——它對 18 支一視同仁,若當成親緣證據會推出「全部 18 支都該合併」的荒謬結論。多數套件顯示「最後異動 2026-07-25」即由此造成的假象。判準②一律只看 feature/fix 驅動的 release。


2. 最終疆界地圖(26 支套件去向表)

圖例:🟢 獨立補殼(第三階段照常)|🔵 併入某疆界|🔴 退役/下架|⚪ 已定案(D9,不在本案範圍)

# 套件 規模 主專案 import 去向 依據摘要
1 jedi-common 105檔/5,104行 821 🟢 獨立(地基) 全生態地基。第二階段續瘦身;system_logs canonical 寫入者在此
2 jedi-auth 164檔/10,783行 181 ⚪ → jedi-iam D9 已拍板
3 jedi-login 90檔/4,361行 26 ⚪ → jedi-iam D9(→auth 30 處 import,深穿透 infra)
4 jedi-mfa 53檔/2,283行 10 ⚪ → jedi-iam D9
5 jedi-captcha 35檔/1,922行 5 ⚪ → jedi-iam(降 turnstile 模組) D9
6 jedi-department 41檔/2,164行 0 ⚪ 🔴 下架 D9 已判(與 org_units 重複、無多租戶)
7 jedi-oscal(v1) 452檔/24,672行 0(12 處全註解) 🔴 退役 四 repo 零可執行引用、無套件依賴、無 DB 表、poetry.lock 已無其蹤 → §3 ④
8 jedi-oscal-v2 445檔/26,321行 165 🔵 OSCAL 疆界核心(吸收 oscal 應用層通用件) → §3 ④
9 jedi-survey 98檔/10,928行 51 🔵 問卷疆界核心(吸收 task_survey 作答層) → §3 ②
10 jedi-flow-engine 133檔/10,201行 163 🟢 獨立補殼 主專案第 4 熱依賴;與 flow_control 的關係屬終局選項
11 jedi-file-upload 69檔/3,948行 37 🟢 獨立補殼(升級為檔案疆界) 有真實第二消費者(jedi-issue pin 並用 5 符號)+有獨立發版節奏 → §3 ①
12 jedi-resource-store 37檔/1,594行 0 🔴 退役 程式碼零 import、public.resources 表四環境皆不存在、路由 2026-02 已刪 → §3 ①
13 jedi-issue 149檔/5,596行 20 🟢 獨立(但58% 死碼待清 追蹤疆界證偽 → §3 ⑤
14 jedi-bulletin 42檔/1,338行 3 🔴 下架評估(比照 department) 套件 408 行 vs 主專案重寫 406 行;DEV 1 列/POC 0 列 → §3 ⑤
15 jedi-log 77檔/3,473行 8 🔵 拆兩半jedi_api_log 併入 log 疆界;jedi_system_log 🔴 刪除 api_log 活著(102,205 列);system_log import 即炸 → §3 ⑤
16 jedi-log-forwarding 37檔/3,281行 4 🔵 log 疆界 與 api_log 職責互補(一個管落地、一個管轉發)
17 jedi-device 41檔/2,882行 21 見 §3 ③
18 jedi-notification 44檔/3,745行 12 🟢 獨立補殼 D9 已驗證「維持獨立」(12 消費點多與身分無關)
19 jedi-system-config 46檔/3,625行 28 見 §3 ⑥
20 jedi-system-menu 45檔/2,446行 4 見 §3 ⑥
21 jedi-project 26檔/399行 75 見 §3 ⑥ ⚠️ 399 行卻被引 75 次、21 處穿透 infra
22 jedi-information-system 29檔/903行 9 見 §3 ⑥ FR-032 G 抽取範本
23 jedi-ai-bot 15檔/1,043行 2 🟢 已具 D6/D7 契約 第一階段 P1 新抽
24 jedi-integrity 29檔/4,721行 10 🟢 已具 D6/D7 契約 第一階段 P2
25 jedi-remote-agent 70檔/3,757行 28 🟢 已具 D6/D7 契約 第一階段 P4
26 jedi-license-runtime 49檔/4,266行 9 🟢 已具 D6/D7 契約 第一階段 P5

3. 五組候選疆界的驗證結果

① 檔案疆界 — ❌ 假設不成立(兩支零耦合),真相是一支該退役

結論:不合併。jedi-resource-store 退役;jedi-file-upload 獨立保留並吸收主專案通用膠水。信心度:高。

原假設把兩者放同組,推測是基於名字都像在講檔案。實測全錯:

面向 jedi-file-upload jedi-resource-store
管什麼 binary 檔案實體+多後端儲存(local/minio/seaweedfs) 一張 link TEXT 欄——書籤/連結庫,帶 publish/hidden/start_date 等內容發佈語意
有沒有檔案 有,2,672 筆實體檔 沒有任何檔案,只有 URL 字串
資料表 upload_files(2,672 列,三條 FK 被引用) public.resources——四環境全部不存在,連出貨基線 02-schema.sql 都沒有
外部依賴 jedi-common+minio SDK(不碰任何業務套件 jedi-common+jedi-auth 深穿透infra/models/resource.py:11 直吃 User ORM 建 FK)
真正的疆界歸屬 檔案 若真要留,是內容發佈/身分族,不是檔案族

雙向 import 皆為 0,連 generate_uuid() 都是各自複製(全庫 17 支套件各抄一份,canonical 早在 jedi_common/utils/common_utils.py:7)。

jedi-resource-store 退役——四項證據齊備

  1. 程式碼零 import:四 repo 掃描 code-level 命中 0;全庫唯一殘留是主專案 pyproject.toml:67 那一行 pin。無任何套件依賴它(poetry.lock 內零反向依賴)。
  2. 表從未被建立:DEV/基線庫/STG/POC 四環境 to_regclass('public.resources') 皆為 NULL。
  3. 路由已刪api/resource/ 於 commit 5c5de8d7(2026-02-14)移除。
  4. 已列管docs/release_notes/v1.14.0.md:152 記載「CM-1143 遺留模組整鏈退役(cruise-project/resource/report+jedi-resource-store):Not started」。

⚠️ 一項本案內部的判定更正(記錄以免後人重蹈):彙整者中途曾誤判「oscal.resources 0 列就是 resource-store 的表」,經①組實查推翻並由彙整者覆核確認分組正確、彙整者錯。全庫有兩處宣告 __tablename__ = "resources":resource-store 的未宣告 schema(解析到 public,jedi-oscal-v2 的明寫 {"schema": "oscal"},欄位集合幾乎無交集(後者是 citationrlinksbase64metadata_id,即 OSCAL back-matter.resources[])。同名不同物。 修正後的結論更強:不是「表 0 列」而是「表從未存在」⇒ 退役是純粹的「刪 pin+刪目錄」,零資料風險,且不需把表的處置交給 OSCAL 疆界。 教訓:判斷表歸屬要看欄位與 schema 宣告,不能看表名。

退役的附帶收益:resource-store 宣告 jedi-auth>=0.1.5 且 3 處 import 它。提前移除=第 2.5 階段 jedi-iam 合併時少一個要跟著改的下游

建議做法併入既有 CM-1143 退役案,不另開卡(避免兩張卡管同一件事);但在 CM-1143 補一條註記——本案已完成套件端四項查證,該支可無條件、零風險移除,不必等 cruise-project/report 的分析結果。若 CM-1143 長期不啟動,這行 pin 可在 FR-069 任一棒順手帶走。

jedi-file-upload 獨立保留——它是唯一有「第二消費者」的套件

  • 真實外部消費者jedi-issue/pyproject.toml:19 pin 它並實際使用 5 個符號(local_issue_attachment.py:4-8)。
  • 有獨立發版節奏:排除 CVE 批掃後仍有 6 次單獨發版(0.0.15/0.0.17/0.0.18/0.0.21/0.0.22 等)。
  • 膠水量化(主專案 1,019 行):A 產品特定 ~500(remote_agent_adapter.py 273 行等,留主專案正確)/B 通用流程 ~280(該上移)/C 純重複 14 處(file_upload_service=... 在 8 個 container 逐字重複)。

B 類該上移的證據最強ManagedFileUploadService 的 docstring 自承「取代 jedi_file_upload 原本從 env vars 讀取的邏輯」——套件的設定來源模型主專案完全沒用、整支覆寫;而「依檔案自記的 storage_type 挑對後端讀回」這 117 行核心語意套件根本沒有。疆界切在錯的地方:套件保留了它做不好的(設定解析),卻沒承接它該做的(多後端讀取路由)。 旁證:jedi-issue 已在為同一缺口付稅(local_issue_attachment.py:28 必須 file_upload_service or FileUploadService(...),主專案得顯式注射才不會退化成 local)。

P0-① 架構債查證結果:已修完api/uploadfileapp/uploadfile 等四目錄皆不存在,且 test/test_module_boundaries.py:92-96 有守衛測試焊死舊拼法不得復活。唯一殘留是 docs/api/uploadfile/ 文件目錄名(純文件,可順手更名)。

② 問卷疆界 — ✅ 成立,且是全案證據最硬的一組

結論:task_survey 併入 jedi-survey,P11 從「抽新套件」改寫為「合併」。信心度:高。

task_survey 不是「疊在 survey 上的一層」,而是 jedi-survey 被切掉的下半身。四項互相獨立的證據一致指向同一結論:

證據 1:跨半身的 DB 實體外鍵(最硬,單獨即足以定案)

live DB 實查 pg_constraint,兩條真實 FK 橫跨「套件的表」與「主專案的表」:

survey.question_answers                → survey.survey_questions
survey.question_answer_history_details → survey.survey_questions

套件邊界不可能靠 DB 外鍵縫合。 若照原 P11 抽成獨立的 jedi-task-survey,這兩條就變成「套件 A 的表對套件 B 的表下外鍵」——安裝順序、migration 順序、卸載全部綁死,本質上就不是兩個套件。而 jedi-survey 全庫零 migration.sql 只有測試用 initdb),survey.surveys 的 migration 全在主專案 scripts/sql/——套件的表由宿主維護,這就是同疆界的定義。

證據 2:DB schema 的切法是刻意設計(實查 migration 原始碼確認)

scripts/sql/migrate_schemas.sql(2026-03-21)同一支腳本內做了分流判斷:

-- 2. jedi-survey 核心表搬遷 → surveys / survey_pages / survey_questions ... → survey schema
-- 4. answer 相關表搬遷      → task_surveys / question_answers / ...        → survey schema
-- 5. job_evidences 搬至 compliance schema                                   → compliance schema

設計者當下對每張表逐一判斷,把 job_evidences 明確送去 compliance,卻把五張作答表**與問卷核心表並列在同一節、放進同一個 survey schema`。當時心中的疆界就是「問卷=設計+作答」。

證據 3:穿透方向單一——上半身乾淨、下半身帶著所有耦合

task_survey → jedi_survey 共 13 條 import,最深的 5 條直取 ORM model 與 RepoImplinfra/task_survey/models/task_survey.py:11Surveyquestion_answer.py:7SurveyQuestion、DI 直取 SurveyPageRepoImpl/SurveyQuestionRepoImpl)。反向 jedi-survey 對 task 概念零認知,只依賴 jedi_common,是乾淨葉節點。

這正是「被切一刀」的形狀:若是兩個真疆界,耦合應雙向或上半身有明確 port;現況是上半身根本不知道下半身存在,卻被下半身用 ORM 與 FK 直接抓住

證據 4:套件公開 API 已被宿主業務語意侵入

jedi-survey/.../survey_snapshot_domain_service.py:36 的公開簽名是 create_snapshot(source_survey_uid, project_uid):86f"__snapshots__{project_uid}" 造資料夾名。project_uid 是主專案概念,早已寫進套件公開 API 與資料落地格式——所謂「乾淨的上游」在源碼層面不成立。

膠水量化(4,514 行):A 產品特定僅 14%、B 通用流程 61%。 作答的 upsert 三路徑、歷史版本與還原、作答狀態機(未填→編輯→待審→補充→完成)、多人即時共編 socket——全部零稽核語意,任何「問卷要指派給人作答」的產品都得重抄。這直接推翻「作答層是產品特定所以該留主專案」的可能結論。

發版歷史:排除 CVE 批掃後 2026 年 8 支實質 commit,5/8 由作答層拉動(FR-049/049.1 顯示條件 4 支+snapshot 機制 1 支)。FR-049.1 是最強一支——一個功能跨兩個 repo 分兩處實作,且消費端得對自己剛加的欄位寫防禦:

# app/task_survey/service/question_answer_service.py:41
dc = getattr(q, "display_condition", None)   # 對自家剛加的欄位寫 getattr 防禦

被排除的選項——維持原 P11 抽獨立套件:技術上不可行。 要斷開必須拆掉兩條 DB 實體 FK、改成應用層維護參照完整性,同時把 TaskSurvey.survey 的 association_proxy(survey_uid/survey_name/survey_version_no,被 DTO 與通知信直接使用)全改批次 enrich。代價是拆掉 DB 幫你保證的完整性,換來兩個永遠要對版的套件——而對版壓力已被 FR-049.1 實證。

反悔條件:出現「只要問卷編輯器、不要作答」(如純表單設計器產品)的真實消費者。目前零外部消費者,不成立。

③ 檢測疆界 — ❌ 假設被推翻(device 不屬檢測)

結論:device 併入檢測疆界。P9 內容物去掉 device、加上散在 common/ 的檢測領域碼。形態=插件,不服務化。信心度:高。

待驗假設「設備管理本就是為檢測服務」被硬證據推翻:

證據 1:程式碼層零耦合。 掃 detection_tools(99 檔)+detection_execution(30 檔)全部 157 檔,對 jedi_device 的 import =0。反向 jedi-device 也不認得任何檢測概念。唯一含 "device" 字樣的 6 處全是同名不同義(agent.device_fingerprint 屬 remote-agent、network_device 是分類 slug)。

證據 2:資料層鐵證(本組最硬的一項)。 實查 job_execution_devices 3,012 列的任務型態分佈:

general 3,009 列 | survey 3 列 | detection_tool  0 列
(對照:job_executions 有 detection_tool 型 21 列)

檢測型任務從未綁定過任何 device。 檢測的掃描目標走 JSONB scan_targets.hosts 手打字串(裸 IP/CIDR/repo key),與 devices 表無 FK、無任何 code 路徑相連。

證據 3:device 的真實消費者全在資產域。 21 處 import 分佈於 system_asset_snapshot(8)、ssp_import_template_app_service(7)、job_execution_device_mapping_service(6)、ssp_inventory_itemsssp_resources_context(各 2)——清一色是資產盤點/SSP/範本匯入,零檢測。發版歷史也一致:jedi-device 近兩次真實開發分別由資產盤點(FR-032 F)主專案審計欄位規範驅動,零筆由檢測需求驅動

證據 4:商業模型也不同疆界。 tenant_licenses 實查——檢測三個 module key(plugindetection-profileremote-agent-manage)各只有 6/25 租戶開通,而 device25/25 全開。檢測是加購模組、device 是標配。

形態分流(P9):插件,不服務化

三問 實查回答
壞了宿主整停還是可降級? 可降級。檢測是 job 的一種型態(21 列 vs general 11,012 列),掛了只是那些工單卡住,稽核/SSP/問卷/證據照跑
需要自己的資料主權嗎? 不需要,且已證明不可能。檢測結果終點是宿主的 compliance.job_evidences 證據池與宿主 Minio;8 張檢測表有 6 張掛宿主 RLS
同一套實例服務多產品嗎? 不會。多實例分擔的角色已由 jedi-remote-agent 承擔(真正跑掃描的是客戶端 agent),雲端側只是「派工+收單+轉證據」的編排層

加碼證據DetectionOrchestrationService 對宿主 workflow 有 9 處 assert_project_participant(...)+1 處 complete_job()。服務化後這些全變成回打宿主的網路 RPC——把「同一 transaction 內的授權+狀態轉換」拆成跨網路兩段,換來分散式一致性問題,收益為零。

建議把 design.md §7 P9 那句「開工前先過服務化評估」改成結論式敘述,省掉開工前那一輪。

對 P9 的兩點修正(design.md 目前低估)

  1. 漏計 common/ 內的檢測領域碼約 1,567 行detection_tools_error_code.py(209)、detection_assignment_params.py(173)、detection_secret_params.py(154)、detection_profile_archive.py(348)、scan_target_spec.py(307)、profile_extractor/(188) 等。抽套件不搬這些,等於「套件在主專案裡留了一半領域碼」。
  2. 「grc 對它 7 條反向依賴」條數對、工程量嚴重低估:非測試 incoming 確為 7 行 import,但 app/flow_control/service/job_service.py 全檔 1,144 行中約 785 行(69%)散在 17 個方法裡處理檢測(單 _replace_detection_tool_agent_assignments 就 169 行)。「import 名不變就不用改」對那 7 條成立,但這 785 行的去向(搬進套件+IJobBinding port,或留 grc)是 P9 最大的單一設計題,design.md 目前沒提

device 自身的處置:改列入資產域評估(與 module_frame 的 inventory 段、oscal.ssp_inventory_items 的 27 列 ref-type: device soft-ref 一起看);套件本身走第三階段輕量升級即可(把 api/device/ 196 行收回套件、清 7 支錯誤依賴、修 devive_*.py 檔名 typo),不必等疆界拍板。

④ OSCAL 疆界 — ⚠️ 部分成立,且「三角」的第三邊抓錯了

結論:oscal 應用層的通用件併入 jedi-oscal-v2;module_frame 併入、另立候選;P13 真正的攔路虎是 flow_control 不是 module_frame。信心度:中高。

耦合實況(實查,與 design.md 的敘述不符):主專案對 jedi_oscal_v2 的 import 按模組分佈——

oscal 77 | flow_control 62 | module_frame 18 | project 8 | associations 0

flow_control 的 62 條是 module_frame 的 3.4 倍。 P13 若只處理 module_frame,做完仍有 62 條橫跨 flow_control ↔︎ v2,疆界沒收乾淨。

穿透深度是本組最重要的發現77 條 import 直取 jedi_oscal_v2.infra.repository.*RepoImpl(主專案 app service 自己 new 套件的 RepoImpl,繞過套件 app service 層)。最嚴重三處:assessment_result_app_service.py:36-64 一口氣拉 13 支、assessment_plan_app_service.py:37-55 拉 9 支、poam_app_service.py:24-30 拉 7 支。而 v2 自己的 AssessmentPlanServicePoamServiceAssessmentResultService 就擺在那沒人用

P13 真正的技術債是「v2 的 app service 層被判定為不夠用而遭繞過」,不是「應用層該不該進套件」。 建議 P13 開工前先派一棒查證:v2 service 的粒度是否為單文件 CRUD,而主專案要的是跨 AP/AR/POA&M/catalog 的交易編排?若是,正確做法是在 v2 內補一層編排 service,讓主專案從 13 支 RepoImpl 降為 1–2 支 service,而不是把 1,000 行的應用層整包搬進套件。

依賴方向:design.md 的建議要反過來。 design.md §7 建議「grc → oscal 單向」,實查現況是:

oscal → flow_control  11 條  |  flow_control → oscal  1 條

主導方向是 oscal → flow_control,與建議相反(要達成 design 的目標得反轉 11 條而非 1 條)。且其中 6 條關於 ssp_reference_document*兩條是 DDD 違規

  • infra/oscal/repository/ssp_catalog_title_query.py:29from app.flow_control.service.ao_derivation import AO_PART_NAMEinfra 層 import app 層
  • infra/oscal/clone/ssp_versioning_cloner_impl.py:19-20 — 直接 import infra.flow_control.model.ssp_reference_document{,_mapping} 兩個 ORM model

建議改為「oscal → flow_control 單向」(oscal 是被流程消費的資料層、flow_control 是流程編排層,讓資料層依賴流程層是反的)。前置決策:先拍板 ssp_reference_document* 兩張表的歸屬——它們現在住 flow_control,卻被 oscal 的 cloner 直接操作。這比 design.md 寫的「拍板依賴方向」更具體可執行。

module_frame 不併入 v2 的三個理由:① 概念不屬 OSCAL 標準(OSCAL 規範裡沒有 module frame);② 自有兩張表在 compliance schemamodule_frames 156 列/module_frames_trans 110 列),不在 oscal schema;③ 依賴方向是 oscal → module_frame(15:2)——module_frame 不是 oscal 的下游,是上游的範本供應者。但它對 flow_engine(4)/participant(2) 也有依賴,獨立成套件成本不低 ⇒ 建議 P13 先不動 module_frame,標為「第二支候選、第四階段後期評估」

jedi-oscal v1 退役查證:✅ 可退役(信心度:高)

證據 結果
BE 真 import 0grep -rnE "^\s*(from|import)\s+jedi_oscal[^_]" 回 0 筆);字串命中 12 處全是註解,內容一致為「FR-038 2A: jedi_oscal imports removed」
FE/test repo 0 可執行引用(命中全在 changelog、對話紀錄、註解)
其他套件依賴 0(全 26 支 pyproject 無人宣告 v1)
pyproject.toml jedi-oscal pin,只有 jedi-oscal-v2==2.2.3
poetry.lock v1 不在 lock 內
DB 表 v1-only 的 27 個表名在 DEV/基線庫一個都不存在
最後異動 2026-06-18 後靜止

v1/v2 不共存的機制已查實:兩者共用同一個 jedi_commonBase,且有 13 個同名表同在 oscal schema——同一 MetaData 註冊同名表即 InvalidRequestError。memory 記載的「全切被強制」是真的,機制是 MetaData 撞名而非人為約定

附帶收益:v1 宣告了 pymupdf(AGPL,即 2026-08 授權盤點的唯一紅燈 → CM-1235),v2 已不依賴它——退役可順帶消掉一個 AGPL 依賴來源

建議做法:Nexus 標 deprecated + monorepo 目錄歸檔(不刪 git 歷史),採「先標 deprecated、觀察一個版本週期再歸檔」而非直接刪(唯一理論缺口是未查 Nexus 是否有本地四 repo 以外的下載者)。主專案端另需 poetry install --sync.venv 殘留(見 §5 P0-3)。

FR-038 2B 查證結果(順帶回答)路由面大致完成——api/oscal/__init__.py live 61 條 vs 註解 6 條;api/module_frame/__init__.py live 43 vs 註解 2,且無任何 orphan route 模組。那 8 條註解掉的多是已判定的 DEAD-V1 清理(標記明確:「FE 無 caller」)。殘留集中在三支殭屍 service +其死測試(見 §5)。

⑤ 追蹤疆界 — ❌ 證偽(三者各自獨立)

結論:不存在「追蹤疆界」。三者各自獨立;唯一有數據支持的動作是 jedi-bulletin 下架評估。信心度:高。

「都在追蹤某種待辦/訊息」是命名層的錯覺,五個判準沒有任何一項支持合併:

反證 數據
互相 import = 0 jedi-issue 與 jedi-bulletin 的 import 剖面無任何交集項(除所有套件都吃的 jedi_common)
主專案整合範式相反 bulletin 是繼承式擴充(3 個 import 全是 as BaseXxx 再 subclass);issue 是委派式呼叫(當黑盒,18 處 method call)。同疆界的東西不會需要兩種相反的整合方式
租戶模型相反 jedi-issue 的 7 張表全部無 tenant_id、全部沒開 RLS;bulletin 與 feedback 則是 tenant-scoped + RLS 開啟。這與 D9 判 jedi-department 下架的判準是同一條
演化節奏無關聯 近 12 個月功能性 commit:bulletin 1 次、log 1 次、issue 5 次,且改動主題完全不相干
體積不同量級 jedi-issue 純套件碼 2,879 行 vs jedi-bulletin 408 行——這是「都很小」的真相,不是同疆界

jedi-bulletin 下架評估的理由(比照 D9 的 jedi-department):套件純碼 408 行,而主專案為了用它重寫了 406 行(repo 97 vs 套件 71、domain service 47 vs 42、mapper 34 vs 26 等,實查逐檔比對)——被包裝的東西比包裝紙還小。真正在用的只有 3 個 base class,套件的 repo/domain service/mapper/query entity 全被主專案的版本取代。資料面:bulletins DEV 1 列、POC 0 列。套件化在此是淨負值:多一個發版對版負擔,換來 3 個 base class。

gitlab/github 整合線的判定:不是「與公告語意遠」的問題,而是它根本沒在跑——DEV 與 POC 雙環境的 ISSUE_INTEGRATE_CONFIGenable:false + 空憑證,feedback_issues 的 gitlab/github uid 全 NULL,_is_gitlab_enable()_is_github_enable() 恆回 False。約 1,191 行套件碼從未執行過。

jedi-issue 死碼比例 58%(全案最高)project_member 全鏈 472 行(四 repo 零引用)+member 鏈約 700 行(唯一入口 /issue/get_members FE 零呼叫、membersrole_members 表 DEV 與 POC 皆 0 列)+gitlab/github 1,191 行。2,879 行套件碼中約 1,663 行從未被執行過。

gitlab/github 是「已廢棄」還是「留給未來客戶」屬產品決策,程式碼判不了——列為待拍板(D14)。

jedi-log 退役查證:❌ 不可整支退役,但應拆兩半

這是本案最容易做錯的一題(三個陷阱在此交會:import 名落差、分割表 pg_stat 假 0、dist 名誤導)。

子套件 判定 證據
jedi_api_log 活著,保留 主專案 8 條真實 importcommon/middleware/app_mw.py:5-8 掛在 middleware 上,每個 request 都寫);api_logs DEV 102,205 列、POC 27,084 列
jedi_system_log 🔴 刪除——不是死碼,是「載入即炸」的不可用碼 見下

jedi_system_log 的 P0 級問題(本案彙整者在 BE 的 .venv 內親自實測確認)

jedi-commonjedi-log 各自宣告了一個 __tablename__ = "system_logs" 的 model,且掛在同一個 Base.metadata。實測:

import jedi_common.logger.db_log.system_log.infra.models.system_log   # OK
import jedi_system_log.infra.models.system_log
# → InvalidRequestError: Table 'system_logs' is already defined for this MetaData instance.

任何人只要 import jedi_system_log,在 jedi-common 已載入的情況下(也就是必然)就會炸。 它不是「暫時沒人用」,是「用不了」。

且 615,122 列全部由 jedi-common 那支寫的pathfunc_nameline_no 三欄只存在於 jedi-common 的 model,實查 615,122 列全部帶值 ⇒ 100% 由 jedi_common...DBLogHandler 寫入,jedi_system_log 對這張表寫過 0 列。jedi-log 那份 model 已漂移成 9 欄、不符 live schema 的 12 欄。

真正該收斂的是「jedi-common 的 db_log ↔︎ jedi-log 的 system_log」(同表兩份 model,其中一份還會讓另一份炸),而不是原本假設的「jedi-log ↔︎ log-forwarding」。合理的 log 疆界終局是三塊各歸其位:

角色 現況 建議
系統 log 落地 jedi-common logger/db_log/(canonical,615k 列的實際寫入者)+ jedi-log jedi_system_log死+炸 留 jedi-common,刪 jedi_system_log
API 存取 log 落地 jedi-log jedi_api_log(活) 保留
log 轉發出去 jedi-log-forwarding 保留

三者互相在原始碼裡反覆提及對方的表(log-forwarding 的 plugin.py:106,111common/code.py:14 都在講 system_logs)——一個套件反覆提到另一個套件的表,就是疆界訊號。建議在第三階段補殼時三者同組討論。

稽核埋點是否重疊? 實查 615,122 列中 [AUDIT: 開頭僅 1,353 列(0.22%)common/util/audit_log.py(主專案已是 shim)→ jedi_common.utils.audit_log.audit()(決定訊息長相)→ DBLogHandler(決定怎麼落地)是一條鏈而非三份重複,jedi-common 檔頭已明說「互補而非重疊,故並存不合併」——實測同意。

⑥ 殘餘 12 支掃描 — 排除四個假候選,挖出一個真問題

結論:10 支確認獨立成立、1 支疆界待決(jedi-project)、0 支退役。 本組對「26→15」的收斂貢獻小,價值在排除假候選,避免第三階段花時間合併不該合的東西。

四個假候選全部不成立

候選 判定 決定性證據
system-config + system-menu 語意相反 結構像雙胞胎,但 system_configs(20 列)有 tenant_id + RLS=t,是租戶機密設定system_menus(74 列)無 tenant_id + RLS=f(group,key) 全域唯一,是全站共用列舉字典(導覽選單另有 ui_routes 55 列)。合併會把租戶機密與全域字典放進同一租戶模型,是退步
remote-agent + log-forwarding ❌ 零交集 log-forwarding 是進程內 logging handler 鏈(跑在雲端 BE 進程內);remote-agent 是機器身分與任務派送(mTLS/RS256 JWT/心跳)。「log-forwarding 是 agent 的一個能力」不成立
integrity + license-runtime ⚠️ 不合併,但有繞行要修 integrity 的 ISignatureVerifierIMachineFingerprint 實作由宿主從 jedi_license_runtime 注入——驗章能力繞了宿主一圈。合併能消掉這圈,但會把「檔案完整性」綁進「授權」。建議改把兩支純密碼學原語下沉 jedi-common,繞行自然消失
notification 維持獨立 ✅ D9 判斷成立 14 個消費點確認與身分無關;D9 說的「mfa 發信改 INotifier port」只需動 jedi-mfa/.../email_service.py 一個檔

🔴 真問題:jedi-project 是「被掏空的核心概念」(本組最重發現)

不是「太小該併掉」,而是一個疆界被切成兩半,主專案那半有 24,640 行

面向 數據
套件體量 26 檔 / 399 行(無 tests、無 harness)
主專案引用 75 處 / 53 檔(第 5 熱依賴)
infra 穿透 21 處(ORM model 15/mapper 4/repo_impl 2)
主專案對應業務量 flow_control 236 檔 / 24,640 行(套件的 62 倍)
被其他套件依賴 0
兩份實體並存 套件 ProjectEntity 11 欄 vs 主專案 FlowControlProjectEntity 40+ 欄(docstring 自陳整合了 projects+extensions+OSCAL AP 計算欄位)
表的歸屬 compliance.projects 211 列,掛在它上面的 7 張 FK 表全在主專案,套件一張都沒有

穿透原因不是「套件 domain service 能力不足」,是疆界切錯——兩類用途都不是 domain service 能提供的:① JOIN 需要 ORM class 本身(13 處,例如 Project outerjoin ProjectExtension 再依 15 個衍生欄位排序,那需要套件認得主專案的表);② SQLAlchemy relationship() 需要 model class(4 處)。

建議:短期凍結、標記「疆界待決」、不補殼。399 行空殼補 D6/D7 五層插件殼是純浪費(殼比內容物大);而現在吸收回主專案會主動關掉 design.md §7 終局選項「project 平台化」的門——compliance.projectsProjectStatus 是 jedi 生態的共同錨點。長期路徑與第二階段天然接軌:project_extensions 收進主表+JSONB 容器後,套件的 Project model 就長出主專案要的欄位,21 處穿透中至少 13 處的 JOIN 需求會自動消失

此條與 D9 的 login→auth 形狀相同但結論相反:login 案是「兩支套件同疆界→合併」;project 案是「套件與宿主同疆界,但宿主那半的歸屬是商業決策」。差別在對造方是套件還是主專案。

jedi-flow-engine 未做完整判定:它有 40 處 infra 穿透、形狀與 jedi-project 同類,但套件本身有 5,465 行實體(非空殼),故不列同級問題。判它需要 flow-engine(12,765 行)+flow_control(24,640 行)+project 三方分析——建議另立一棒專做「流程疆界」,它與 jedi-project 是同一個結。


4. 對第三、四階段棒次表的改寫建議

第四階段(P6/P9–P13)

原棒次 改寫建議 理由
P9 jedi-detection(detection_tools+detection_execution+device device 移出加入 common/ 檢測領域碼約 1,567 行;形態改為結論式「插件,不做服務化評估」;新增設計決策題job_service.py 785 行檢測碼的去向 device 與檢測零耦合、零資料關聯(§3 ③)
P11 jedi-task-survey(抽新套件) 改寫為「問卷疆界合併」——task_survey 併入 jedi-survey 跨半身 DB 外鍵使「抽成獨立套件」技術上不可行(§3 ②)
P13 oscal 三角拆解 三角的第三邊從 module_frame 改為 flow_control(62 條 vs 18 條);module_frame 降為「第二支候選、後期評估」;依賴方向建議反轉為 oscal → flow_control 單向前置決策改為「拍板 ssp_reference_document* 兩表歸屬」新增前置查證棒:v2 app service 為何被 77 條 RepoImpl 繞過 §3 ④
P12 jedi-participant 維持不變,仍建議先做 解核心環的鑰匙;且 P11 排在它之後可省一輪 port 設計
P6/P10 維持不變 本案未發現反證
「併入四階段評估」的 feedback 維持第四階段,但修正判準:design.md 說的「斷 2 條 system_config 線」實為空掛注入可直接刪(注入後從未呼叫);真正的阻礙是 jedi_auth.infra.models.User 的 infra 穿透 ⇒ 應排在第 2.5 階段 jedi-iam 之後 §3 ⑤
新增:「流程疆界」分析棒(flow-engine + flow_control + jedi-project 三方) jedi-project 與 flow-engine 是同一個結(§3 ⑥)

建議執行順序:P12(鑰匙)→ P11(問卷合併)→ P9(檢測)→ P10/P6 → P13(OSCAL,最重且需前置查證)。

第三階段(老套件補殼)——補殼名單以本地圖為準

不補殼的(避免「補完又扔」):jedi-resource-store(退役)、jedi-oscal v1(退役)、jedi_system_log(刪除)、jedi-bulletin(下架評估中)、jedi-project(疆界待決)。

建議首棒jedi-information-system——903 行、9 個消費點、已有 IUserLookup dependency inversion 與守衛測試,是全庫依賴最乾淨的一支(唯一零虛掛依賴的老套件),補殼成本最低,適合驗證第三階段 SOP。

補殼順帶收益:老套件的虛掛依賴會被 standalone harness 自動抓出來(裝不全就跑不起來)——harness 就是虛掛依賴的照妖鏡,不需另開清理棒。實證:五支 P1–P5 新套件(皆有 harness)零虛掛,六支老套件全部有虛掛。


5. 順手發現——四個 P0 級問題

皆為本案分析副產品,均已由彙整者親自覆核。建議獨立開卡,不必等疆界拍板。

# 問題 證據 風險
P0-1 jedi_system_log 載入即炸 在 BE .venv 實測:先 import jedi-common 的 system_log(必然發生)後再 import jedi_system_logInvalidRequestError: Table 'system_logs' is already defined 上膛的槍。任何人「看到有這套件就拿來用」,第一行 import 就 500。且若有人加 extend_existing=True 硬繞,會得到一個缺 3 欄的 model 靜默覆蓋正確的那個
P0-2 **jedi-issue 漏宣告 jedi-common pyproject.toml 相依清單無 jedi-common,源碼卻 import 它 28 次** 單獨 pip install jedi-issue 必炸。目前不暴露只因主專案剛好也裝了 jedi-common。這正是 FR-069「套件可獨立安裝」目標的直接反例,第三階段補殼首個會紅的
P0-3 .venv 殘留 jedi-oscal v1 .venv/.../jedi_oscaljedi_oscal-0.0.23.dist-info 實體仍在,但**不在 poetry.lockpoetry update 未清) 目前不炸(DI 走明文 allowlist 非萬用掃描),但它與 v2 有 13 個同名表同 schema 同 Base**——任何誤 import 立即 InvalidRequestError。建議 poetry install --sync
P0-4 6 張孤兒表隨 installer 出貨到每個客戶環境 component_definitionscd_capabilitiescd_componentscd_control_implementationscd_implemented_requirementscd_statements——全庫零 ORM 擁有者grep __tablename__ 零命中)、六張全 0 列,只活在 scripts/init/02-schema.sql 出貨污染。是 OSCAL Component Definition 的預留表,從未實作

其他可清理項(非 P0)

類別 內容
死碼 三支殭屍 app service 595 行(oscal_audit_service.py 409/ssp_versioning_service.py 151/project_system_info_service.py 35,method 全回 None{},外部 caller 皆 0)+ 1,236 行測試在測空殼test_ssp_versioning_service.py 檔頭是模組層 pytest.skip,26 個 test 從未執行);task_survey_ref_items 全鏈 219 行(DB 0 列,概念已被 in-memory dict 取代);job_execution_device_mapping 全鏈 772 行(路由已註解、四 repo 零引用、DB 0 列,但 DI 仍 live-wired);device 監控子能力 21 行+2 表(零實作的預留:無 ORM、無 repo、無 service、無任何 INSERT 路徑);問卷答案 Excel 匯入鏈 342 行(FE 唯一呼叫點在 _archived/);jedi_common/utils/gen_comment.py 84 行(四 repo 零引用,且是全生態 openai 依賴的唯一理由——刪它可讓 26 支套件少扛一個 LLM SDK);jedi_common/session/database/base_model.py 19 行(design.md 已定案待刪)
靜默失效 SurveyPageDomainService DI 漂移——survey_containers.py:49-54 注入 survey_repoquestion_answer_containers.py:50-54 漏注入,而套件側 verify_survey_exist() 寫的是無 else 的 if self.survey_repo: ⇒ 走後者取得的實例靜默不驗、回 None。目前未爆(該路徑只用 get_pages),屬上膛的槍
零呼叫者的完整實作 archive_workflow_execution()workflow_execution_service.py:1303)完整實作了 4 支歷史表歸檔,但全域零呼叫者(唯一非定義命中是 2026-04 對話裡被註解掉的一行);對應 hi_* 四張表全 0 列,而活表 workflow_executions 有 11,004 列。需 user 裁決:歸檔要接上(誰呼叫?專案結案時?)還是連表帶碼退役
依賴衛生 測試依賴錯放主依賴 20/26 支(非 D9 估的 5 支)——乾淨的只有 ai-bot/integrity/license-runtime/log-forwarding/remote-agent(皆有正確 dev group)+information-system(無測試)。主專案 pyproject.toml:113-118 已記載此問題會讓 --without dev 仍裝進 build 機 venv。虛掛依賴集中在六支老套件,最嚴重是 jedi-project 11 項全虛掛(399 行 CRUD 套件宣告了 pandas/pdfplumber/openpyxl/tqdm/ftfy,且 pytest 重複宣告兩次)。反向漏宣告:jedi-notification 用了 requests 未宣告、jedi-file-upload 用了 werkzeugurllib3 未宣告(standalone harness 會炸)
測試腐化 jedi-file-upload tests/unittest 63 passed / 16 errors(測試停留在 factory 重構前的舊 signature,且 patch 一個模組層不存在的符號);tests/architecture 5 檔 978 行從未執行(缺 pytestarch 宣告,collect 即失敗);jedi-resource-store 931 行測試全綠地測著一個零消費者的殭屍——「綠燈不代表活著」的樣本
命名混淆 FileUploadService vs UploadFileService 並存(僅語序不同、職責無法從名字分辨,route 同時注入兩支);jedi_system_log/app/service/system_menu_service.py 檔名與內容不符(class 是 SystemLogService,是從 jedi-system-menu 複製後沒改檔名的殘跡);generate_uuid() 全庫 17 支套件各抄一份(canonical 早在 jedi_common/utils/common_utils.py:7
潛在 500 ai_dashboard API registry 兩筆 device 條目(api_registry.py:244,254)指向 associations_containers.py不存在的 provider;因 lambda 延遲求值,app 起得來,但 AI 儀表板選到這兩支就炸
RLS 缺口(建議併入既有 CM-1445) jedi-issue 7 張表全無 tenant_id 且無 RLSlabels 35 列全租戶共享)——與 CM-1445 追蹤的三張表性質不同(那三張是「有欄位沒開 RLS」,這七張是「連欄位都沒有」);compliance.job_execution_devices 是租戶資料關聯表卻無 RLS;upload_files 無 RLS(靠 path/storage_type 間接隔離,程式碼有明文「繞 RLS 借同型設定」的 fallback)
版控污染 jedi-devicedist/(8 個舊版 whl)、coverage_unittest/testarch_report/ 進了版控;jedi-device/README.md 8 行且「目錄結構」段是空的 code block(D7-② 接入 README 標配完全缺席)
文件與事實不符 docs/spec-site/current/user-manual/developer-setup-guide.md:338 稱 jedi-resource-store 為「資源儲存抽象」(實為連結庫,且表從未存在);tests/DEPENDENCIES.md:23,308 描述其 resources 表與 fixture 寫法;docs/api/uploadfile/ 目錄名殘留舊拼法(程式碼已於 P0-① 收斂為 upload_file

6. 待決策點(D10–D16,供決策者拍板)

編號接續 design.md §3 的 D1–D9。拍板後才改寫第三、四階段棒次表

# 決策點 建議 信心 若反悔的條件
D10 問卷疆界合併:task_survey 併入 jedi-survey,P11 從「抽新套件」改為「合併」 同意合併。跨半身 DB 外鍵使原方案技術上不可行;A 類產品特定僅 14% 出現「只要問卷編輯器、不要作答」的真實消費者(如純表單設計器產品)
D11 檢測疆界:device 併入;P9 改為「detection_* + common/ 檢測碼」;形態=插件 同意。零 import、零資料關聯(檢測型任務綁 device 0 列)、商業模型也不同(device 25/25 租戶標配 vs 檢測 6/25 加購) 未來檢測改為「掃描目標必須來自資產主檔」的產品設計
D12 OSCAL 疆界:oscal 應用層通用件併入 v2;module_frame 另立;P13 第三邊改為 flow_control;依賴方向反轉為 oscal → flow_control 單向 同意,但 P13 開工前先派一棒查證「v2 app service 為何被 77 條 RepoImpl 繞過」——答案決定是「搬應用層進套件」還是「在 v2 內補編排層」 中高 查證結果顯示 v2 service 粒度其實夠用(則繞過屬歷史積習,該修的是主專案)
D13 三支退役/刪除:jedi-oscal v1(退役)、jedi-resource-store(併入 CM-1143)、jedi_system_log(刪除) 同意三支。證據鏈皆齊備,其中 jedi_system_log 建議優先處理(是會炸的碼,不只是死碼) v1/resource-store:Nexus 出現四 repo 以外的下載者(未查證項,故建議「先標 deprecated、觀察一版」而非直接刪)
D14 jedi-bulletin 下架評估(比照 D9 的 jedi-department) 建議下架,但屬產品決策:套件 408 行 vs 主專案重寫 406 行、DEV 1 列 POC 0 列 公告功能列入未來產品路線(則該做的是把主專案那 406 行收回套件,而非下架)
D15 jedi-project 疆界待決:短期凍結不補殼,長期併入「專案疆界」而非吸收回主專案 同意凍結。現在吸收會關掉 §7 終局選項的門;且第二階段 ext 收斂會自動消掉 13 處穿透 中高 商業決策明確放棄平台化(則改走「吸收回主專案、jedi-project 退役」,399 行搬回去是一天的工)
D16 jedi-issue 死碼去留(58% 從未執行):gitlab/github 整合 1,191 行 + member/project_member 鏈約 1,172 行 需 user 拍板——「已廢棄」還是「留給未來客戶」是產品決策,程式碼判不了。技術面已證明:雙環境 enable:false+空憑證+零筆資料

本案未查證事項(誠實列出,供拍板時權衡)

  1. Nexus 是否有本地四 repo 以外的消費者——三項退役結論的共同理論缺口。已確認「jedi-* 僅 Guidant AI 產品線消費」(design.md §3 D9 前置條件,2026-08-30 user 確認),但未查 Nexus 下載紀錄。故退役建議採「先標 deprecated、觀察一個版本週期」。
  2. STG/POC 的資料分佈多數未查——遵守環境紀律,除 ①⑤ 兩組有補查 POC 外,其餘只查 DEV 與基線庫。不影響任何程式碼層結論(零 import/零耦合與資料量無關),但涉及「表有無資料」的死碼判定,STG/POC 未逐一覆核。
  3. A/B/C 膠水行數為逐檔判讀後的估算,非逐行標註。相對比例可靠(如問卷 B 類 61% vs A 類 14% 差距 4 倍、OSCAL 通用件約 3,000 行),絕對數字誤差可達 ±30%,可用於棒次規模估算、不宜當驗收基準
  4. job_service.py 785 行檢測碼「搬 vs 留」未拆解——只做行數盤點,未逐方法判斷哪些依賴宿主 job entity。這是 P9 開工前該做的第二層分析。
  5. archive_workflow_execution 零呼叫者是靜態 grep 結論——若有透過反射/排程設定/BPMN listener 動態調用,grep 抓不到。
  6. jedi-flow-engine 的疆界未做完整判定——需 flow-engine+flow_control+project 三方分析,超出本案範圍,建議另立一棒。
  7. detection_profiles 內容庫服務化(FR-059/060,11 主檔/4,841 控制項列)有「中央維運公版、各站台拉取」的天然形狀,但未評估商業可行性(是否有中央維運需求、客戶站台是否允許外連)。標為第四階段之後的獨立評估項,不該塞進 P9

7. 願景達成度

design.md 原定願景是「26 支收斂為約 12–15 支輪廓清楚的疆界套件」。本案盤點後的實際收斂路徑:

現況 26 支
  −4  身分族合併(D9:auth+login+mfa+captcha → jedi-iam)
  −1  jedi-department 下架(D9)
  −3  本案退役(jedi-oscal v1、jedi-resource-store、jedi_system_log 子套件)
  −1  jedi-bulletin 下架(D14,待拍板)
  ±0  問卷/OSCAL 疆界是「吸收主專案模組」,套件數不減但輪廓變清楚
  ────
  ≈ 17 支(若 D14 不下架則 18 支)

未達 12–15 支,但這是正確的結果——本案排除了四個假候選(system-config+menu、remote-agent+log-forwarding、integrity+license-runtime、追蹤三支),若硬湊數字去合併它們,會把租戶機密與全域字典塞進同一租戶模型、把檔案完整性綁進授權,是用架構退步換數字好看

真正的收斂價值不在套件數,而在:

  • 三組疆界重劃(問卷吸收作答層、OSCAL 吸收應用層通用件、檢測吸收 common/ 領域碼)讓「安裝即有完整能力」成立;
  • 四個 P0 級地雷在動核心抽取前被拆除;
  • 約 6,000 行死碼(jedi-issue 1,663/oscal 殭屍 595+死測試 1,236/job_execution_device_mapping 772/task_survey_ref_items 219/Excel 匯入鏈 342/gen_comment 84 等)在搬家前先清掉——搬死碼進套件是純虧