FR-174 · 設計文件 · 2026-11-01 定稿

網址只是來源,檔案才是真相

外部連結型 Profile 解析成功當下存成檔案,從此與上傳檔案同一條路同步給 Agent;拆卡表由 handoff/cards.py 產,9 張 6 批

🔴 這份是設計決策,不是現況。 前提:Agent 不連外網(FR-172 已裁),所以「主系統把網址丟給 Agent 自己抓」這條路要整條拔掉;出貨前無客戶,不做舊版相容、不寫回填,既有兩筆按一次「重新解析」就轉型。本案推翻 jedi-detection 一條明文不變式(網址型下載內容不落儲存),理由見 D1。

D1~D7 已裁 9 張卡 6 批、4 repo 套件基底 1.7.3b1(FR-171 分支) Agent 不連外網

📑 本頁是 v1.0(定稿)。 實作中有設計異動,見 設計 v1.1(補開 E-1、A-2,H-1 挪批 5、Z-1 挪批 6,工具代號更正)。本頁內容保留定稿原樣,不回頭改。

0. 分工概述(30 秒版)

整案一句話:主系統登記「外部連結」型的檢測 Profile 時,背景下載解析完順手把那份壓縮檔存起來,這一版從此是檔案型;既有的「只同步檔案型給 Agent」機制不用改,Agent 主控台下拉自然就有東西;網址留著當「來源網址」做新鮮度檢查。

階段 做什麼 卡 完成怎麼判定(決策者檢查法)
E 環境 主系統開發期改裝本機 jedi-detection E-0 小弟回寫貼出 import 路徑在工作區底下
P 套件 解析成功就存檔轉型、約束放寬、派工只認檔案型、刪網址分支;公版兩支範例改成隨套件帶檔 P-1、P-2 Nexus 上有 jedi-detection 1.8.0b1/b2;P-1 回寫貼出新測試清單與約束驗證結果
H 主系統 DEV 套 014、兩支 dev-sec 按「重新解析」轉型、本機 Agent 下拉看到兩支 H-1 你親眼看:本機 Agent 主控台 CINC 掃描表單的 Profile 下拉出現「dev-sec Linux 基準」「dev-sec Windows 基準」,名稱與主系統 Profile 頁相同
F 前端 版本明細多「來源網址」列、提示文案改 F-1 主系統 Profile 頁 dev-sec 那版同時看到檔名與網址
A Agent 拔掉「網址原樣交給引擎」分支 A-1 小弟回寫貼出 grep SOURCE_TYPE_URL 為 0
Z 結案 發正式版、改回釘版、收口、回報基線重產 Z-1 git status 乾淨;回寫列出基線要補的兩列

依賴一句:E-0 → P-1 → (P-2、F-1、A-1)→ (E-1 環境對齊 FR-171、A-2 Agent 快取路徑)→ H-1 → Z-1。你第一個看得到畫面的是批 5 的 H-1。


1. 決策定案表

# 題目 定案 被排除的 理由
D1 網址型 Profile 怎麼同步給 Agent A 案:解析成功當下把下載到的壓縮檔存進 upload_files,該版 source_type 改 file、file_id 有值;掃描、同步、對帳全走檔案型既有路徑 B 案:保留 url 型、放寬約束讓它同時帶 file_id,同步條件也放寬 A 讓「平台解析的」與「Agent 掃的」是同一份檔,正好解掉套件不變式擔心的「兩邊對不上」;B 是同一版兩個來源,Agent 該用哪個要另訂規則,日後兩個真相。且 FR-172 已裁 Agent 不連外網,「Agent 自己拉網址」本來就走不通,不變式的前提已不存在
D2 來源網址存哪 沿用既有 url 欄;約束 chk_dpv_source_exclusive 改成「file 型 file_id 必填、url 可有可無;url 型 url 必填、file_id 必空」 另加 source_url 欄 兩欄存同一串網址只差生命週期階段,是多餘複製;沿用一欄少一次搬移、少一組讀寫。這不是 B 案:B 案的問題是「網址型同時帶檔案」會有兩個掃描來源,這裡是「檔案型附帶出處」,掃描來源永遠只有檔案一個
D3 轉型時機 解析成功落庫那一刻(_persist 同一個 transaction):寫控制項、寫 file_id、改 source_type、寫 sha256 與 ETag 一起 commit 下載完就先存檔再解析 下載成功但解析失敗(不是合法 inspec 包)時不該留一個檔案型的壞版本;同一 transaction 保證「有檔必有控制項、有控制項必有檔」
D4 下載或解析失敗的版本 維持 url 型、extraction_status=failed、file_id 空;既有派工守門(failed 拒派)與「修正來源」流程照舊 半成品也轉型 失敗版本沒有檔可同步,轉型只會讓 Agent 拉到空
D5 來源更新偵測與重抽 保留:file 型且 url 非空的版本開控制項頁時照舊 HEAD 比 ETag,變了就重抽;重抽成功同一版換檔(新 upload_files 列、file_id 指過去、sha256 更新;舊檔由 delete_file 清掉)。Agent 下次同步比 sha256 不同就重拉 偵測到變更就長新版 套件文件已排除「自動長 v2」(任務綁的版本 uid 會指不到實際掃的內容);同版換檔讓「版本」仍是使用者的語意、「檔」是內容的語意
D6 「Agent 自己拉網址」掃描路徑 整條拔掉:套件 build_payload 只回 file 形狀、派工對 url 型版本 raise;Agent resolve_profile_source 刪 url 分支;相關 docstring 與不變式註解改寫 留著當備援 出貨前無客戶不做相容;留著是死碼,且與「Agent 不連外網」矛盾,六個月後沒人知道哪條是主路徑
D7 安裝包的 dev-sec 兩支公版範例 改成檔案型隨套件帶:兩支 tar.gz 進 jedi-detection assets/+storage-seed.map,seed 004 兩列改 file 型反查 upload_files uid、url 留著;主專案基線 upload_files 補兩列(基線重產屬決策者裁示,卡只回報) 從種子拿掉由客戶自己匯入 新裝客戶內網到不了 GitHub,網址型種子裝完下拉一樣空;TWGCB 八支已走這條路,不新開機制
D8 既有資料(DEV、189 各兩筆) 不寫回填 migration;升級後到控制項頁按一次「重新解析」即轉型 回填腳本 出貨前無客戶;189 兩筆是 Demo 資料,按一下就好
D9 套件版本基底 從 FR-171 CM-3382 發的 1.7.3b1 往上長 1.8.0b1(P-1)、1.8.0b2(P-2)、正式 1.8.0(Z-1);套件分支基底是 jedi-python-package 的 ws-CM-3379(FR-171 批 3 合回後以 feature/FR-172 為準) 從 Nexus 1.7.2 長 主系統在 FR-171 分支已釘 1.7.3b1,從 1.7.2 長會把 FR-171 的改動蓋掉
D10 與 FR-171 B 主題(設定通道)的接點 不碰。本案主系統端不改 _agent_profile_sync.py、不改心跳回應;Profile 清單的查法 list_current_files_visible_to 不變,轉型後的版本自然納入。FR-171 B-1 之後把清單搬去 GET /agents/config 時查法照用 等 B-1 合回再做 主系統端零程式改動,與 FR-171 批 4 不撞檔;不必等

2. 一版 Profile 的一生(定案後)

%%{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'}}}%%
sequenceDiagram
  autonumber
  participant U as 管理者(主系統 Profile 頁)
  participant S as 主系統(jedi-detection)
  participant F as upload_files/儲存
  participant A as Agent
  U->>S: 新增 Profile,來源=外部連結 https://…
  S->>S: 建版 v1:source_type=url、file_id=NULL、status=pending
  S->>S: 背景 worker:下載(60 秒內、50 MB 內、SSRF 五道防護)
  S->>S: cinc-auditor 解析(28~117 秒)
  rect rgb(226,240,241)
    Note over S,F: 本案新增:解析成功 → 存檔 → 轉型(同一 transaction)
    S->>F: 壓縮檔 bytes 存成 upload_files(save_dir=detection-profiles,sha256 回填)
    S->>S: 寫控制項;source_type=file、file_id=新列、url 保留、sha256=bytes 雜湊、ETag
  end
  A->>S: 心跳(FR-171 B 之後改 GET /agents/config)
  S-->>A: 現行 file 型清單(含這一版:uid、version、sha256、file_uid、tool、name)
  A->>S: GET /agents/files/<file_uid>
  S-->>A: 壓縮檔
  A->>A: 對帳 sha256 → 解壓進 cache → 主控台下拉出現
  U->>S: 開控制項頁
  S->>S: HEAD 來源網址比 ETag;變了 → 同版重抽、換檔(D5)
圖 1 — 外部連結型 Profile 從登記到 Agent 可用。虛線框是本案新增的一步;下方灰線是被拔掉的舊路。

被拔掉的舊路:派工 _profile payload 帶 {source_type:url, url} → Agent 把網址原樣丟給 cinc-auditor exec。本案後派工只會有 file 形狀;Agent 收到 url 形狀一律報「派工資料錯誤」。


3. 套件改動細節(P-1)

3.1 存檔與轉型

  • 新檔 jedi_detection/common/profile_snapshot.py:store_snapshot(file_upload_service, upload_file_domain_service, raw: bytes, file_name: str, scope: str, login_name: str) -> UploadFileEntity。內部把 bytes 包成 werkzeug FileStorage,走 與 DetectionProfileService._store_file 完全相同的分流(SYSTEM 走 upload_system_file、TENANT 走 upload_files,save_dir="detection-profiles"),再 _backfill_sha256。_store_file 本身改成呼叫這支(1,146 行的檔只准一行呼叫,不複製)。
  • DetectionProfileExtractionService._extract_in_thread:下載成功、解析成功後,在 _persist 的 session 內呼叫 store_snapshot,把回來的 file_id 交給 _persist。順序:先 store_snapshot(檔進儲存)→ replace_for_version(控制項)→ 改版本列。存檔在 session 內是可以的:它是本機/物件儲存寫入,不是外部網站。
  • _persist(…, file_id=None):file_id 有值時 version.source_type = "file"、version.file_id = file_id;url、source_etag、source_last_modified、sha256 照舊寫。
  • 重抽換檔(D5):_persist 發現版本已有 file_id 且本次又存了新檔 → 舊 file_id 收進一個清單,commit 後 file_upload_service.delete_file(old_uid)(失敗只 log,不回滾——孤兒檔比壞版本便宜)。
  • 檔名:沿用 _download() 從網址推得的 name;推不出時用 <profile uid>-v<version>.tar.gz。

3.2 守門與派工

  • common/detection_profile_ref.build_payload:刪 url 分支;source_type != "file" 直接 raise ValueError(派工層包成 DETECTION_PROFILE_NOT_DISPATCHABLE)。
  • detection_orchestration_service._load_profile 之後一律 _load_profile_file,不再依 source_type 分流。url 型且 failed 的版本本來就被 assert_profile_dispatchable 擋;url 型且 pending/running 的版本(還在抽)由 build_payload 擋,錯誤訊息「此版本尚未解析完成,無法派工」。
  • _check_source_freshness:條件從「source_type == url」改成「url 非空 且 status == succeeded 且有驗證器」。_read_controls 已把 _source_url 帶出來,不用改。
  • list_current_files_visible_to、current_profiles_for_heartbeat、record_agent_states:不動。

3.3 資料庫(migration 014)

-- jedi-detection 014:外部連結型 Profile 解析成功後存成檔案(FR-174 D1/D2)
-- Date: 2026-11-0X
-- 🔴 套件只提供、不執行;冪等。
ALTER TABLE config.detection_profile_versions DROP CONSTRAINT IF EXISTS chk_dpv_source_exclusive;
ALTER TABLE config.detection_profile_versions ADD CONSTRAINT chk_dpv_source_exclusive CHECK (
    (source_type = 'file' AND file_id IS NOT NULL)
 OR (source_type = 'url'  AND url IS NOT NULL AND file_id IS NULL)
);
COMMENT ON COLUMN config.detection_profile_versions.source_type IS 'file=有檔(上傳或由來源網址解析後存下)/url=只登記網址、尚未解析成功';
COMMENT ON COLUMN config.detection_profile_versions.url IS '來源網址;file 型有值代表這份檔是從該網址解析存下的,開頁時拿它比 ETag';

主專案側由 flatten_pkg_migrations.sh 攤平到 scripts/sql/packages/jedi_detection/014-…,DEV 手套並登記 schema_migrations(登記名 packages/jedi_detection/014-fr174-url-profile-snapshot.sql)。

3.4 要改寫的註解與文件

detection_profile_extraction_service.py 檔頭「url 型:平台代為下載,但只在記憶體」整段、_download() docstring 紅字、detection_profile_version.py 兩個欄位 comment=、detection_profile_ref.build_payload docstring 的兩形狀表。改成描述新規則與陷阱(「解析失敗不轉型,否則 Agent 會拉到空」「重抽換檔要清舊檔」),不留「原本/改判」字樣。


4. 主系統、前端、Agent

端 改什麼 不改什麼
主系統(H-1) DEV 套 014;兩支 dev-sec 按「重新解析」;一支守衛測試(core/plugins/、app/ 下不得出現 source_type == "url" 分支) _agent_profile_sync.py、心跳回應、retry 端點、agents/files 端點
前端(F-1) 版本列與明細:有 url 就多顯示「來源網址」,圖示改依「有無網址」;快照提示改「內容取自來源網址的當下快照,Agent 掃描用的是同一份檔案」;過期提示刪「與實際掃描的內容不一致」 新增 Profile 表單(仍選上傳或外部連結)、修正來源對話框
Agent(A-1) profile_cache.resolve_profile_source 刪 url 分支與 SOURCE_TYPE_URL;inspec.py docstring 三路改兩路;兩支測試改預期 raise 心跳、同步、cache、主控台下拉(都已是 file 型路徑)

5. 拆卡表

底表 handoff/cards.py(兩個斷言已過:依賴都在更早的批、同批同 repo 不改同一既有檔)。

批 卡 階段 repo 做什麼 reuse/extend/new 改既有檔 新開檔 依賴 驗收(小弟本機) model/effort
1 E-0 環境 BE 環境卡:主系統開發期改裝 jedi-detection 本機路徑(工作坊工作區 develop 模式),驗 import 路徑在工作區底下;改動不 commit reuse:FR-171 E-0(CM-3385)的做法原封照抄,只是套件換 jedi-detection pyproject.toml(path dependency,assume-unchanged 不 commit) docs/features/FR-174-2611-url-profile-snapshot-sync/handoff/dev-env.md — python -c "import jedi_detection,inspect;print(inspect.getfile(jedi_detection))" 印出 jedi-python-package/.worktrees/<母卡號>/jedi-detection/ 底下;PORT=8003 python main.py 起得來、GET /api/1.0/detection-profiles 200;git status 看不到 pyproject.toml(assume-unchanged);dev-env.md 寫清楚怎麼還原 sonnet/low
2 P-1 套件 jedi 套件 jedi-detection 1.8.0b1:網址型解析成功就存檔轉 file 型(url 留作來源網址)、約束放寬 file 可帶 url、新鮮度檢查改看 file 型的 url、派工只認 file 型、刪「網址丟給 Agent」分支與不變式註解 extend:_extract_in_thread 下載成功後多一步 _store_snapshot()(組 FileStorage 走既有 DetectionProfileService._store_file 同一條,抽成兩支共用的 module 函式);_persist 多收 file_id 並把 source_type 寫成 file;_check_source_freshness 條件改「有 url 且 succeeded」不看 source_type;build_payload 刪 url 分支;list_current_files_visible_to 不動(轉型後自然撈到);migrations/014 改約束。new:common/profile_snapshot.py(bytes→upload_files 的共用小函式,因為 detection_profile_service.py 1,146 行已超標只准一行呼叫) jedi_detection/app/service/detection_profile_extraction_service.py
jedi_detection/app/service/detection_profile_service.py(一行呼叫)
jedi_detection/common/detection_profile_ref.py
jedi_detection/app/service/detection_orchestration_service.py(_load_profile 之後一律走 _load_profile_file)
jedi_detection/infra/detection_tools/model/detection_profile_version.py(欄位註解)
jedi_detection/api/serializers/detection_profile.py(controls 回 source_url)
jedi_detection/app/dto/detection_profile_dto.py
tests/test_detection_source_upload.py
tests/test_agent_profile_sync_service.py
pyproject.toml(版號 1.8.0b1)
jedi_detection/migrations/014-fr174-url-profile-snapshot.sql
jedi_detection/common/profile_snapshot.py
tests/test_profile_snapshot.py
E-0 pytest 全綠含新測:①網址型抽取成功後該版 source_type=file、file_id 有值、url 仍是原網址、sha256=下載 bytes 雜湊、upload_files.sha256 同值(突變:不存檔→紅);②下載失敗仍是 url 型 failed、file_id NULL;③build_payload 對任何版本只回 file 形狀,餵一個 source_type=url 的版本會 raise;④_check_source_freshness 對 file 型且有 url 的版本會探測、對沒 url 的不探測;⑤約束:INSERT source_type='file', file_id=1, url='https://x' 過、source_type='url', file_id=1 被擋(對本機 DEV 套 014 後 psql 驗)。poetry publish -r nexus 1.8.0b1,回寫版號 opus/high
3 P-2 套件 jedi 套件 jedi-detection 公版範例改檔案型:dev-sec linux/windows 兩支 tar.gz 進 assets/+storage-seed.map 兩列、seed 004 兩列 versions 改 file 型(file_id 反查 upload_files uid、url 留著)、回報主專案基線 upload_files 要補兩列 extend:照 TWGCB 八支同一條路(assets+storage-seed.map+004 反查 uid);不新開機制 jedi_detection/migrations/004-detection-profiles-seed.sql
jedi_detection/assets/storage-seed.map
pyproject.toml(版號 1.8.0b2;include 已含 assets)
jedi_detection/assets/dev-sec-linux-baseline.tar.gz
jedi_detection/assets/dev-sec-windows-baseline.tar.gz
P-1 兩支 tar.gz 的 sha256 與 004 寫的 sha256 一致、檔名在 map 內;本機乾淨庫跑 001~014 後 select source_type,file_id is not null,url from config.detection_profile_versions where uid in (dev-sec 兩個 uid) 回 `file t
3 F-1 前端 FE FE 版本明細與控制項頁:file 型帶 url 時多一列「來源網址」;清單圖示依「有無來源網址」而非 source_type;快照提示文案改成「內容取自來源網址的當下快照,Agent 掃描用的是同一份檔案」;過期提示拿掉「與實際掃描的內容不一致」 extend:ProfileVersionTable.vue/ProfileDetailDialog.vue/DetectionProfileControlsView.vue 既有條件改判 url 有無;i18n 兩個 key 改文案、加一個 field_source_url src/views/detection-profile/components/ProfileVersionTable.vue
src/views/detection-profile/components/ProfileDetailDialog.vue
src/views/detection-profile/DetectionProfileControlsView.vue
src/config/locales/i18n/zh-tw/detection-profile-manage.json
src/config/locales/i18n/en/detection-profile-manage.json
— P-1 vitest 既有綠;本機對 8003 開 Profile 頁:dev-sec 版本列顯示檔名與來源網址兩者、明細有「來源網址」列、控制項頁提示新文案;grep -rn border-left src/views/detection-profile 為 0 sonnet/low
3 A-1 Agent evidence-agent Agent 拔掉「url 型原樣交給引擎」分支:resolve_profile_source 只認 file 型,收到 url 型明確報派工資料錯誤;模組與 connector docstring 三路改兩路;對應測試改成預期 raise extend:刪分支與常數 SOURCE_TYPE_URL,錯誤走既有 ProfilePayloadError guidant_agent/sync/app/profile_cache.py
guidant_agent/scan/adapters/connectors/inspec.py(docstring)
test/test_profile_cache.py
test/test_inspec_connector.py
— P-1 poetry run pytest test/test_profile_cache.py test/test_inspec_connector.py 綠;突變:把 raise 改回 return url 對應測試要紅;grep -rn SOURCE_TYPE_URL guidant_agent 為 0 sonnet/low
4 E-1 環境 jedi 套件 套件環境對齊 FR-171:把 feature/FR-172(已含 171 正式版)合進 174 工作坊分支,解 jedi-detection/pyproject.toml 版號衝突(版號取 174 的 1.8.0b2、相依下限取 171 的 remote-agent>=1.12.0);主系統 .venv-fr174 的 jedi-remote-agent/jedi-agent-protocol 換成正式版 reuse:純合併與環境對齊,不改套件程式碼 jedi-detection/pyproject.toml — P-2 git merge-tree 確認只有 jedi-detection/pyproject.toml 一處衝突並解掉;174 工作坊分支 jedi-detection 版號 1.8.0b2、相依 jedi-remote-agent>=1.12.0;.venv-fr174/bin/python -c 印三支版本:protocol 0.2.0、remote-agent 1.12.0、detection 1.8.0b2(工作區原始碼);jedi-detection 全部 pytest 綠 sonnet/low
4 A-2 Agent evidence-agent Agent Profile 快取路徑改可設定:profile_cache.CACHE_ROOT 寫死 /data/content/cache,Mac 本機跑會 Read-only file system;改讀環境變數 AGENT_CONTENT_CACHE_DIR(預設不變),本機 start-agent.sh 補一行指到 $DATA/content-cache extend:profile_cache.CACHE_ROOT 改成函式讀 env、預設值不變;不新增模組 guidant_agent/sync/app/profile_cache.py
test/test_profile_cache.py
— — poetry run pytest test/test_profile_cache.py 綠,新增一條:設 env 後 build_cache_dir 落在該目錄(突變:拿掉讀 env → 紅);本機 start-agent.sh 補 export AGENT_CONTENT_CACHE_DIR="$DATA/content-cache" 後起 Agent,log 不再出現 Read-only file system: '/data'、ls ~/guidant-agent-local/data/content-cache 看得到已同步的 Profile 目錄 sonnet/low
5 H-1 主系統 BE 主系統接線:釘 jedi-detection 1.8.0b1(path 模式下先不改 pin)、scripts/sql/packages/ 攤平 014、DEV 套 014、DEV 兩支 dev-sec 按「重新解析」轉成 file 型、本機 Agent 下拉出現兩支 CINC Profile;守衛測試 reuse:全部走既有機制(flatten_pkg_migrations、_agent_profile_sync 不動、retry 端點不動);只加一支守衛測試 docs/features/FR-174-2611-url-profile-snapshot-sync/handoff/dev-env.md(補 DEV 已套 014) test/test_fr174_profile_snapshot_guard.py P-1, E-1, A-2 DEV(localhost:5432)psql 套 packages/jedi_detection/014 並登記 schema_migrations;對 dev-sec 兩版打 POST .../versions/<uid>/extraction/retry,輪詢到 succeeded 後 DB 查到 source_type=file、file_id 有值、url 保留;GET /api/1.0/agents/heartbeat 回應(或 Agent log)清單含這兩支;本機 Agent 主控台 GET /console/scan/profiles?tool=cinc_auditor 回兩筆、label 與主系統名稱相同;守衛測試:主系統 core/plugins/ 與 app/ 下 grep 不到 source_type == "url" 分支(突變:加一行→紅) sonnet/medium
6 Z-1 結案 BE 結案:jedi-detection 發正式 1.8.0、主系統改回釘版並取消 assume-unchanged、poetry update jedi-detection、守衛全綠、FR-174 STATE/README 收口、CM-3410 卡 append 指向本案完成;回報基線重產清單 reuse:FR-171 Z-2 結案卡同形 pyproject.toml
poetry.lock
docs/features/FR-174-2611-url-profile-snapshot-sync/README.md
docs/features/FR-174-2611-url-profile-snapshot-sync/handoff/FR-174-STATE.md
— P-2, H-1, F-1, A-1, E-1, A-2 pip show jedi-detection 1.8.0 且路徑在 site-packages;git status 乾淨;pytest test/test_module_boundaries.py test/test_fr174_profile_snapshot_guard.py 綠;README 總進度全 ✅;回寫列「出貨基線待重產:upload_files 兩列+packages manifest 014」 sonnet/medium

共 9 張、6 批;BE 3、jedi 套件 3、evidence-agent 2、FE 1。


6. 端到端驗收

  1. DEV 主系統 Profile 頁:dev-sec Linux 版本列顯示來源「上傳檔案」圖示、檔名 linux-baseline-master.tar.gz(或推得的檔名)與來源網址;明細有「來源網址」列。
  2. 本機 Agent 主控台 → 掃描 → CINC Auditor → Profile 下拉有「dev-sec Linux 基準」「dev-sec Windows 基準」,選一支對本機目標跑完有報告。
  3. 主系統檢測任務綁 dev-sec Linux 派給本機 Agent,Agent log 顯示走 cache 路徑(不是網址)。
  4. 控制項頁對 dev-sec 版本:改 DB 的 source_etag 成假值再開頁 → 出現「有更新版本,正在重新解析」→ 抽完 file_id 換成新列、舊 upload_files 列消失、Agent 下次同步重拉(sha256 變了)。
  5. 新增一支壞網址的外部連結 Profile → failed、仍是 url 型、file_id 空、派工被擋 412。
  6. 乾淨庫跑 001~014+主專案 seed(補兩列 upload_files)→ dev-sec 兩版是 file 型。
  7. grep -rn "source_type == \"url\"\|SOURCE_TYPE_URL" 在主系統 core//app/ 與 Agent guidant_agent/ 為 0。

7. 不做什麼

  • 不加新欄、不加新表、不加新端點。
  • 不做回填 migration、不做舊 Agent 相容。
  • 不改 FR-171 設定通道、不改心跳。
  • 不處理 189 POC(屬上版動作;升級後由決策者按兩次「重新解析」)。
  • 不重產出貨基線(Z-1 回報清單,決策者裁示後另做)。