FR-105 · 需求索引 · 本頁由 build 掃資料夾生成
✅ 三棒完成並驗收(2026-09-16)——module_frame 81 檔、oscal 163 檔全數分類,清理棒完成,主專案 route 從 195 減到 189。母卡 CM-1838。
主專案四層 636 檔/62,442 行裡,oscal 與 module_frame 兩支就佔一半 (244 檔/30,938 行)。FR-102 已把 26 個模組盤到「模組層級」,但對這兩支明講 「單一標籤蓋不住內部差異」——它們裡面同時住著主專案自己的業務(解析 Word/Excel、 產生範本、比對文件差異)與轉接殼(主專案不做事,只把前端請求翻譯成 jedi_oscal_v2 套件聽得懂的形狀),混在同一個 service 目錄,看目錄分不出來。
要動這兩個模組的人需要一張逐檔的圖:哪一支可以自由改,哪一支動了會牽動套件契約。 本案就是產這張圖。只分析、不動任何程式。
第 1 棒(module_frame,81 檔/10,942 行)已完成,三個主要發現:
真業務比殼多,不是各半。 行數比 50.8%(真業務):35.0%(轉接殼):14.1%(接線與型別), 真業務約為殼的 1.45 倍。FR-102 粗估的「約各半」方向對、比例要修正。
沒有兩份真相。 原本懷疑六支 v2 子物件殼(party/components/inventory/leveraged/ system_characteristic/ssp_resources)與 app/oscal/ 同名五支各寫一份欄位對應表—— 實查發現 oscal 那邊是直接 import module_frame 這邊的對應表, module_frame 是定義處、oscal 是引用者。連 app/flow_control/ 也在借同一份。 這不是重複,是刻意的單一真相。
沒有死碼,但有五個對外不可達的端點。 81 支全部通過四層查證 (模組內 import/模組外 import/DI 註冊/route 註冊)。 最接近的是兩個 route 註冊被註解掉的 Resource 類別, 以及三個前端只在「被註解掉的程式碼」裡呼叫的下載/匯入端點。 證據已備齊,但清不清屬異動,本案只記錄。
逐檔分類表、route 清單、統計數字、可立刻收的清單 → module-frame-classification.md
第 2 棒(oscal,163 檔/19,996 行)已完成,三個主要發現:
這是主專案最大的一塊自有資產,不是套件的殼。 行數比 57.5%(真業務 11,494): 23.8%(轉接殼 4,757):18.1%(接線與型別),真業務是殼的 2.4 倍,比 module_frame 那邊的 1.45 倍更懸殊。原因是它獨佔了「把客戶既有文件吃進系統」 那條線——Word 解析(2,027 行)與文件比對(1,384 行)合計 3,411 行零套件引用。
五支 SSP 子物件殼與 module_frame 那六支是同一份、不是兩份。 五支全部直接 import module_frame 那側的欄位對應表,檔頭都自陳「完全相同……故直接共用, 確保單一真相」。兩個模組的分類表在這一點上對得起來。
六支死碼、126 行,全在 api/ 層。 一支被註解掉的 route 檔,加上五支 互相引用成封閉圈的序列化器——它們用 marshmallow 的字串式 Nested 互相引用, grep import 抓不到,且與活著的同功能類別名字只差三個字母(SspSystemCharacteristic vs SystemCharacteristic),是清理時的地雷。另有兩條資源庫端點對外不可達。
逐檔分類表、61 條 route 清單、統計數字、可立刻收的清單 → oscal-classification.md
決策者裁示(2026-09-16):
module_frame 與 oscal 的依賴方向維持現狀、不反轉;FR-106 反轉稿留存暫不動。module_frame 側(CM-1841 已做)。| 棒次 | 範圍 | 卡號 | 狀態 | 產出 |
|---|---|---|---|---|
| 第 1 棒 | module_frame 81 檔/10,942 行、45 條 route |
CM-1839 | 🟢 完成(2026-09-16,HEAD 98d756bb) |
module-frame-classification.md |
| 第 2 棒 | oscal 163 檔/19,996 行、61 條 route |
CM-1840 | 🟢 完成(2026-09-16,HEAD 84b8426a) |
oscal-classification.md |
| 第 3 棒 | 清理棒:兩模組不可達端點/死序列化器/FE 殘骸,Excel 欄位契約去重 | CM-1841 | ✅ 驗收通過(2026-09-16,BE 22c65991+7b017144+b1a80773、FE ea74482) |
六條端點下架、五支死序列化器移除、Excel 欄位契約單一定義 |
兩個模組並排看(細節都在兩份分類表裡):
module_frame |
oscal |
|
|---|---|---|
| 規模 | 81 檔/10,942 行、45 條 route | 163 檔/19,996 行、61 條 route |
| 真業務 | 5,563 行(50.8%) | 11,494 行(57.5%) |
| 轉接殼 | 3,831 行(35.0%) | 4,757 行(23.8%) |
| 真業務:殼 | 1.45 : 1 | 2.4 : 1 |
| 死碼 | 0 | 6 檔/126 行(全在 api/) |
| 守門位置 | route 層 decorator 為主 | app service 層為主(資源域,要先反查專案才知道判誰) |
依賴方向是 oscal → module_frame:跨模組 import 共 12 處,其中 9 處是 oscal 引用 module_frame(五支 SSP 子物件的欄位對應表、Excel sheet 定義), 反向只有 3 處。這與規模直覺相反(oscal 兩倍大、概念上更底層),已列為待裁第一題。
兩支共 244 檔/30,938 行,一棒做不完。按模組切的理由是依賴方向: v2 子物件殼的欄位對應表在 module_frame 這邊定義,oscal 那邊是引用者—— 先做定義處,第 2 棒才有對照基準。
看依賴方向,不看檔名也不看直覺:
jedi_* 引用少或零;刪掉這支功能就消失。FR-100 與 FR-102 都踩過「前三層全空、第四層才發現是活的」:
src/config/api/api.jssrc/config/router/index.jspublic.ui_routesbin/、scripts/、端對端測試第 1 棒在第 2 層又抓到一種新情況:常數有定義、但呼叫它的前端程式碼整段被註解掉。 這種要標成「疑似死」而不是「活」,光看 grep 命中數會誤判。
| 原本記載 | 實查結果 |
|---|---|
派工卡寫 module_frame 對外 route「43 條」 |
45 條(AST 實跑)。FR-102 總表 A 寫的就是 45,是卡片轉述時的筆誤 |
| FR-102 寫最大兩支 service「全自寫」 | 1,008 行那支成立;1,500 行那支有 18 個套件引用(都是讀資料的來源),準確說法是「主體邏輯自寫、資料來源在套件」 |
FR-102 寫 generator.py 零套件引用 |
成立,且整包 excel_template/ 7 檔 1,656 行全都零套件引用 |
| FR-102 寫 party/components/inventory/leveraged「同型」 | 成立,但同型的是六支不是三支(要補 system_characteristic 與 ssp_resources) |
FR-102 對本模組的五句證據沒有一句已失效,兩句需要精確化、一句需要補充。
決策者看完兩棒分類表後說:「我覺得解偶還是沒有做的很好」。首腦據此寫了 FR-106 反轉稿(把 SSP 對應層從 module_frame 歸位 oscal,反轉依賴方向)。 決策者裁:「先不動,與主專案牽連太多,後續釐清再處理,紀錄留著」—— 反轉只解模組間方向,不解三支千行大檔殼與業務混住的糾纏,等新功能帶真實需求再回頭。
兩個模組都盤完後,有四件遇到「兩種合理判法」而不自選的:
🔴 module_frame 與 oscal 誰是誰的上游。 現況是大模組依賴小模組 (oscal 引用 module_frame 9 處),與直覺相反。維持現狀零風險,但每個新來的人 都會想反過來改、而改了會壞;反轉則要動 11 支 service。 判斷需要的資訊:這兩個模組未來會不會有一個進套件——若 module_frame(資源庫) 要進,現在的方向剛好;若 oscal 要進,方向就是錯的。
殼裡的「空值哨兵」要不要退回套件解。 套件的資料表有幾個欄位不准留空, 但前端允許使用者留空,主專案這一側就塞了假值進去(例如全零的識別碼)、 讀出來時再換回空白。要嘛維持現狀(零風險,但這個「看不見的約定」任何第三個 寫入者不知道就會寫壞資料),要嘛請套件把欄位改成可留空(要動套件的資料表定義與 migration)。 在專案 SSP 那一側問題更嚴重一級——範本是內部資料,專案 SSP 是租戶的實際稽核資料, 假值會跟著匯出進客戶的系統安全計畫書。 判斷需要的資訊:jedi_oscal_v2 除本專案外還有沒有別的使用者。
Excel 欄位契約那約 25 行逐字重複的常數要抽到哪裡。 三個候選落點 (common//留在 module_frame 讓 oscal 引用/進套件)各有代價。 與第 1 題綁死,建議一起裁。 另外兩邊的變數名不一致 (HEADER_LABELS vs EXPECTED_HEADERS)比重複本身更麻煩——grep 同一個名字只會找到一半。
那些對外不可達的東西要不要清。 兩個模組合計:module_frame 五個端點、 oscal 六支死碼(126 行)+兩條資源庫端點,另有三條前端指向不存在後端端點的殘骸。 證據都已備齊(四層查證結果寫在各自分類表的 §4), 但「清」屬異動,本案只分析不動程式。其中資源庫的「發布」端點風險最高—— 它是公版治理的唯一途徑,收了等於砍功能。
以下全部由 build 掃資料夾產生,新增檔案重 build 即自動出現。標題連結指向渲染後的 HTML,md 連向源檔。
| 文件 | 類型 | 標題 | 最後更新 |
|---|---|---|---|
| module-frame-classification / md | 文件 | module_frame 逐檔分類 | 2026-09-16 |
| oscal-classification / md | 文件 | oscal 逐檔分類 | 2026-09-16 |
卡片內容(決策紀錄、驗收條件)以 Notion 為準,本頁只記座標。
| 關係 | 卡號 | 標題 | 狀態 |
|---|---|---|---|
| 母案 | CM-1838 | FR-105 oscal/module_frame 逐檔分類 | — |