卡號 CM-1710(母卡 CM-1703)。只盤不修,未執行任何 DROP/DELETE/DDL,DEV 全程只
SELECT,STG/POC 未連線。
盤點對象是 DEV 庫 guidant_ai_dev(192.168.50.188:25432,帳號 cmmgr,密碼查 .env)的全部 189 張表與 4 個 view。判斷「有沒有人碰」走四路對照,任何一路命中就不算孤兒:
.py,抓 __tablename__ 與 __table_args__ 的 schema。主專案 22 支、jedi 現役套件 134 支(archive 與 tests 另計)。FROM/JOIN/INTO/UPDATE/DELETE FROM 後接的 schema 限定表名與裸表名,涵蓋 infra/readmodel/、各套件 *_query.py 的 text("...")。pg_views 的 definition 逐個展開找被引用的表。.format() 組出來的 SQL,把常數解回實際表名。工具是 psql 16.10 與 python 3.11.9 手寫 AST 腳本,沒有用 vulture/deptry。每一條「沒人碰」的結論都另外開檔核對過,核對內容寫在下面各組的證據欄。
機械對照第一輪判了 36 張表「無 ORM」,逐一開檔核對後有兩類是誤判,值得記下來給後面幾棒參考:
Table() 不是 __tablename__。public.bulletin_org_units 是 jedi-bulletin 的公告↔︎部門橋表,用 Table("bulletin_org_units", Base.metadata, ...) 宣告,AST 抓 __tablename__ 完全看不到它,但 bulletin_repo_impl.py 與 bulletin_org_unit_repo_impl.py 對它有 select/insert/delete。它是活的。log_forwarding_setting.py 寫 SETTINGS_SCHEMA = os.getenv("LOG_FORWARDING_SETTINGS_SCHEMA", "config"),AST 只看得到變數名。實際落在 config.log_forwarding_settings,而且昨天(9/11)還有寫入。它是活的。這兩條的共同教訓是:靜態抓 __tablename__ 會漏,任何「零命中」都必須開檔看過才能下結論。
| 項目 | 數量 |
|---|---|
| DEV 表 | 189 |
| DEV view | 4 |
| 有 ORM model 對應 | 155(主專案 21/jedi 134) |
| 無 ORM model | 34 |
| 其中是分區子表(父表有 ORM) | 10 |
| 真正的候選(逐一核對過) | 24 |
| 分類結果 | A 類 3 條/B 類 4 條/D 類 3 條/E 類(基礎設施,不列入)14 條 |
命中欄位:— 表示零命中,✓ 表示有命中且已開檔確認是真的引用。
| schema.table | ORM(主) | ORM(套件) | SQL 字串 | view | 筆數 | 最後寫入 | 建表來源 | 分類 |
|---|---|---|---|---|---|---|---|---|
| oscal.component_definitions | — | — | — | — | 0 | 從未 | scripts/init/02-schema.sql |
A |
| oscal.cd_capabilities | — | — | — | — | 0 | 從未 | 同上 | A |
| oscal.cd_components | — | — | — | — | 0 | 從未 | 同上 | A |
| oscal.cd_control_implementations | — | — | — | — | 0 | 從未 | 同上 | A |
| oscal.cd_implemented_requirements | — | — | — | — | 0 | 從未 | 同上 | A |
| oscal.cd_statements | — | — | — | — | 0 | 從未 | 同上 | A |
| compliance.hi_workflow_executions | — | — | — | — | 0 | 從未 | 同上 | A |
| compliance.hi_element_variables | — | — | — | — | 0 | 從未 | 同上 | A |
| compliance.hi_workflow_templates | — | — | — | — | 0 | 從未 | 同上 | A |
| public.hi_job_executions | — | — | — | — | 0 | 從未 | 同上 | A |
| public.device_monitors | — | — | — | — | 0 | 從未 | 同上 | A |
| public.device_monitor_archive | — | — | — | — | 0 | 從未 | 同上 | A |
| public.role_members | — | — | — | — | 0 | 從未 | 同上 | A |
| public.operations | — | — | — | — | 8 | seed 當下 | 同上+04-seed-core.sql 灌 8 列 |
B |
| compliance.project_job_execution_device_mapping | — | — | ✓ 只讀 | — | 0 | 從未 | 同上 | B |
| public.system_logs_old | — | — | — | — | 104,310 | 2026-06-03 | 分區改造時的舊表 | B |
| survey.question_answers_dedup_backup_20260428 | — | — | — | — | 1,736 | 2026-03-19 | 2026-04-28-survey-answer-dedup.sql |
D |
| config.detection_tool_profiles_deprecated_20260803 | — | — | — | — | 13 | 2026-08-01 | FR-059 profile 改版備份 | D |
| public.alembic_version | — | — | — | — | 1 | — | 02-schema.sql |
D |
| public.bulletin_org_units | — | ✓ Core Table() |
✓ | — | 7 | — | — | 活 |
| config.log_forwarding_settings | — | ✓ 動態 schema | ✓ | — | 1 | 2026-09-11 | — | 活 |
| config.tenant_license_events | — | ✓ | ✓ | — | 31 | 2026-09-10 | — | 活 |
| public.integrity_tamper_events | — | ✓ | ✓ | — | 7 | 2026-08-24 | — | 活 |
| public.license_clock_watermark | — | ✓ | ✓ | — | 1 | 2026-08-14 | — | 活 |
public.schema_migrations、public.schema_version 屬版位追蹤基礎設施,依卡片指示不列入候選。10 張 api_logs_*/system_logs_* 分區子表的父表 public.api_logs、public.system_logs 都有 ORM,是正常分區不是孤兒。
三組都是「表建了、功能沒做或已退役、一列資料都沒寫過」。三組在出貨基線庫 guidant_ai 裡也都存在且同樣是 0 列,意思是每一個客戶裝機都會拿到這 13 張空表。
① OSCAL Component Definition 全族(6 張) component_definitions 與五張 cd_* 子表。整個 codebase 對 component_definition/cd_*/ComponentDefinition 做過大小寫不敏感全文搜尋,主專案與 jedi 套件(含 archive)零命中——沒有 ORM、沒有 entity、沒有 repository、沒有 route。唯一提到它們的地方是兩支清理腳本把它們列進 truncate 清單。OSCAL 標準有 Component Definition 這個文件型別,但本產品從未實作,表是照標準先開好的。
② flow-engine history 全族(4 張) hi_workflow_executions、hi_element_variables、hi_workflow_templates、hi_job_executions。這組不需要我重新舉證:docs/analysis/2026-09-01-cm1494-zombie-verification.md 已經把它判為「確定死」,證據是 STG/POC 兩環境四張表筆數皆 0、五個宣告依賴 jedi-flow-engine 的套件零引用、plugin.py 也沒對外暴露。我這一輪在 DEV 複驗,四張表同樣 0 列、程式碼同樣零命中,結論一致。套件側那 34 支 hi_* 檔(1,522 行)該分析建議併入 jedi-flow-engine 下次改版處理,不屬本棒範圍。
③ 裝置監控預留(2 張)+ 舊權限模型殘留(1 張) device_monitors/device_monitor_archive 是預留功能,docs/api/device/generate_docx.py 白紙黑字寫「Blueprint 中有註解掉的 DeviceMonitorsRoute/DeviceMonitorRoute⋯⋯目前未啟用」。 role_members 是舊角色模型的殘留:jedi-iam 的 role.py 留了一行註解說「你若仍保留舊的 Web 權限可在此處維持舊關聯」但關聯本身是註解掉的,RoleEntity 雖有 role_members 屬性、role_domain_service 也有兩處讀它,但 RoleMapper.to_entity() 從來不填這個欄位,所以執行期恆為 None。現行角色授權走 user_roles/role_capabilities/v_user_capabilities,與這張表無關。
⚠️
role_members這條要注意:表可以 drop,但套件那段讀role.role_members的程式碼是活的、而且恆讀到None。get_user_roles_member()裡roles = roles + role.role_members對None會直接TypeError。這是既有的潛在雷,與 drop 表無關,但值得另開卡看那個分支到底走不走得到。
public.operations(8 列)——沒有任何程式讀它,但 scripts/init/04-seed-core.sql 每次裝機都會灌進 VIEW/INSERT/UPDATE/DELETE/EXPORT/IMPORT/SUBMIT/VERIFY 八個動作字典。這是舊權限模型的字典表,現行 capability 模型不用它。要 drop 就得同時改 seed 腳本,而 04-seed-core.sql 是產生檔(gen_seed_sql.sh 從基線庫重產),照 scripts/init/README.md 的鐵則必須先改基線庫再 regenerate,不能手改。
compliance.project_job_execution_device_mapping(0 列)——這張不能只看筆數就 drop。jedi-task-platform 的 DeviceReferenceQuery 會 SELECT COUNT(*) 它,而且該 query 經 core/plugins/asset.py 的 AssetReferenceAdapter 活接線,供前端刪設備前的確認框用。但全 codebase 沒有任何一處 INSERT 進這張表——它是只讀不寫的表,永遠回 0,對那個計數毫無貢獻。要退役得同時改套件的 SQL(三表相加改兩表),屬跨套件異動。
public.system_logs_old(104,310 列,56 MB)——分區改造前的舊系統日誌表,最後寫入 2026-06-03。只存在於 DEV,出貨基線庫沒有這張表,所以不影響客戶。scripts/check_env_scoped_migrations.py 把它與 detection_tool_profiles_deprecated_20260803 一起標為環境專屬殘留。留著只佔 DEV 空間,drop 前確認沒人還要查 6 月前的舊 log。
survey.question_answers_dedup_backup_20260428(1,736 列,304 KB)——scripts/sql/2026-04-28-survey-answer-dedup.sql 去重前的備份,最後資料停在 2026-03-19。DEV 專屬,基線庫沒有。config.detection_tool_profiles_deprecated_20260803(13 列,168 KB)——FR-059 檢測 Profile 改版時的舊表備份,表名自帶 _deprecated_ 與日期。DEV 專屬,基線庫沒有。public.alembic_version(1 列 f20ca741a9bd)——主專案根本不用 alembic:pyproject.toml 沒有這個依賴,也沒有 alembic.ini 或 alembic/ 目錄,migration 走 scripts/sql/ + schema_migrations。用 alembic 的是 License Center,那是獨立 repo 獨立 DB。這張表卻隨出貨基線進了每個客戶的庫,而且 04-seed-core.sql 還會灌那個 version_num 進去。屬歷史殘留,建議連同 seed 一起清。現役套件與主專案掛零——155 個 ORM model 全部在 DEV 有對應的表,沒有「程式碼領先 DB」的情況。
唯一三筆是 jedi-common 的測試檔內臨時 model(_sample_translatable、t_person_extras、t_plain_extras),跑測試時動態建表用,本來就不該在 DEV 出現,屬正常。
順帶做的 DEV↔︎出貨基線對照也乾淨:基線有而 DEV 缺的表 0 張;DEV 有而基線沒有的 3 張,正是上面 D 類與 B 類點名的環境專屬殘留(system_logs_old、detection_tool_profiles_deprecated_20260803、question_answers_dedup_backup_20260428)。
cd_* 全族 6 張——零引用、零資料、零爭議,唯一要動的是 scripts/init/02-schema.sql 的重產與那兩支清理腳本的 truncate 清單。一次少 6 張出貨表。public.alembic_version——主專案不用 alembic 卻隨基線出貨給每個客戶,還帶著一個假的 version stamp。誤導性最強:後來的人看到這張表會以為 migration 走 alembic。compliance.project_job_execution_device_mapping——不是最髒,但最容易被誤判。它有活的讀取者,光看筆數會以為能刪;實際是「只讀不寫、恆回 0」的空轉查詢,套件那句三表相加的 SQL 有一項永遠是 0。本棒只盤不修,以下都要另開卡:
sql-migration skill。注意這不是單純寫一支 migration——02-schema.sql 與 04-seed-core.sql 是產生檔,必須先改基線庫再跑 gen_schema_sql.sh/gen_seed_sql.sh 重產,照 scripts/init/README.md 的鐵則。operations 要連 seed 一起清嗎/project_job_execution_device_mapping 要不要連套件 SQL 一起改/system_logs_old 的舊 log 還要不要)。role_domain_service.get_user_roles_member() 與 get_roles_by_uid_list() 讀 role.role_members,而 mapper 從不填該欄位,恆為 None,roles + None 會 TypeError。與本棒的 drop 無關,是查證途中發現的既有問題。subtask_status_history 後那張表會變成新的孤兒」。本棒盤點時 compliance.subtask_status_histories 仍有 ORM model(infra/subtask_status_history/models/),故不列入。第 3 棒做完要回頭補進 drop 清單。compliance.project_device_mapping(2026-05-22 C3 退役)在 DEV 已經不存在,出貨基線庫也沒有,該案已清乾淨,不必處理。