本卡屬 FR-091(母卡 CM-1690),能力點前置三卡的第三張,收口卡。前置:CM-<asset 定形卡>、CM-<回補卡>、CM-1726、CM-1727 都收回,且 1715/1725 收回後首腦決定 compliance-audit/iam 要不要列清單。只動主專案一個測試檔+(若有)seed 檔漏筆的 migration。建議 model:Sonnet 5,effort:medium。
前兩張卡讓每支套件都用程式讀得到的方式列出「我需要哪些能力點」。這張卡在主專案加一支測試:把 core/plugins/init.py 的 PLUGINS 清單裡每支套件的 CAPABILITIES 讀出來,逐一去 scripts/init/04-seed-core.sql 找有沒有 INSERT INTO public.capabilities ... 'name' 那一行。少一筆就紅,並印出「哪支套件的哪個能力點 seed 檔沒有」。反過來也查:seed 檔裡有、但沒有任何套件宣告也不是主專案自己的能力點,列成警告(不紅,因為主專案自己的模組還沒列清單)。
這支測試不改任何執行期行為,它的價值是:以後任何人加能力點忘了 seed、或改名忘了同步,pytest 當場紅,而不是客戶裝好後某個按鈕永遠 403。它也是未來 migrate arc 的地基——等清單被證明準確,才談由清單產生 SQL。
scripts/init/04-seed-core.sql 是 gen_seed_sql.sh 從基線庫 pg_dump 出來的產生檔,每筆能力點一行 INSERT INTO public.capabilities (id, name, resource_type, action, description, is_platform) VALUES (...) ON CONFLICT DO NOTHING;,目前 127 筆。用正則抓 name 就好,不要去解析 SQL。core/plugins/__init__.py 的 PLUGINS 是 list[Plugin],每個 Plugin 有 name 與 mount。Plugin 物件目前不帶套件模組的引用——runner 要決定怎麼從 Plugin 找到該套件的 jedi_<pkg>.plugin.CAPABILITIES:建議 Plugin dataclass 加一個選填欄位 capabilities: tuple = (),各 core/plugins/<pkg>.py 在 PLUGIN = Plugin(...) 時傳 capabilities=jedi_<pkg>.plugin.CAPABILITIES。這樣 PLUGINS 就是主專案「知道自己掛了哪些能力點」的唯一入口,未來 migrate arc 也從這裡讀。這是設計選擇,runner 開工前先回寫本卡問首腦確認,首腦在卡上回覆後再動。core/plugins/__init__.py 的 Plugin 定義與每支 core/plugins/<pkg>.py 一行)。test/test_module_boundaries.py 加 test_plugin_capabilities_are_seeded:讀 seed 檔抓 name 集合;遍歷 PLUGINS 收集所有 Capability;差集非空就 fail,訊息列 <plugin name>: <capability name>。test_plugin_capability_names_follow_resource_action:每個 name 都等於 resource.action(jedi-common 的 Capability 已在 post_init 驗,這條是在主專案再守一次,確保沒人繞過型別直接塞 tuple)。pytest.warns 或 print 列出,不 fail。這份清單首腦要拿去看哪些是主專案自己模組的、哪些是廢的。device.create 那行,測試指向 tmp 檔 → 紅且訊息含 asset: device.create。做完還原。Capability("foo","foo","bar","x") → post_init 先炸;改成 Capability("foo.bar","foo","bar","x") → 守衛紅。還原。/api/1.0/devices/menu 401。