查詢(DEV,localhost:5432/guidant_ai_dev,唯讀):
SELECT u.id, u.name, u.url, u.enable
FROM ui_routes u
WHERE u.pid IS NOT NULL AND u.pid <> 0
AND NOT EXISTS (SELECT 1 FROM route_capabilities rc WHERE rc.route_id = u.id)
ORDER BY u.name;(pid IS NULL 或 pid = 0 的節點是選單分組節點/頂層節點,本來就無 url、不是可綁 權限的葉節點,已排除。)
| id | name | url | enable | 為什麼可以 fail-open |
|---|---|---|---|---|
| 49 | project-dashboard | /project/dashboard | 1 | scripts/sql/2026-05-26-menu-group-restructure.sql Step 2 明寫「不掛 capability(所有 user 可見)」——搬遷自 AppMenu.vue 原本寫死的入口,本來就無角色限制。 |
| 50 | my-tasks | /project/task-manage | 1 | 同上(同一支 migration 同一段),待辦任務對任何登入者都可見。 |
| 54 | license-status | /license/status | 1 | scripts/sql/2026-08-10-fr062-8-license-menu-restructure.sql 明寫「/license/status(route 54)刻意不綁任何 capability:全租戶可見的自己狀態頁,維持現況 fail-open 可見」——與同群組的 license-manage(root 專屬管理頁,已綁 license.read)刻意區分。 |
沒有發現「真漏綁」的路由——三筆都能在各自來源 migration 找到明確的白話說明,判定為 既定設計而非疏漏。本棒不需回報決策者裁「要不要開卡補」(.3a 卡面待裁事項已解)。
license-manage 在 scripts/sql/2026-08-08-fr062-2-license-menu-route.sql 首次 新增時沒有綁 route_capabilities(該檔註解:「不綁 capability:本階段守門走既有 platform-admin 軸(route decorator),未新增 capability」)——這是「用另一套授權軸 守門、DB 層刻意留空」的設計,不是遺漏。兩天後 scripts/sql/2026-08-10-fr062-8- license-menu-restructure.sql 補上了 license.read 能力點與綁定,DEV 現況因此已不 在 fail-open 清單內(見上表,license-manage 未出現)。
守衛測試的白名單(test/test_package_ui_routes_have_route_capabilities.py 的 _INTENTIONAL_FAIL_OPEN_ROUTES)以 (檔案, name) 為單位登記,而非只用 name—— 只豁免 2026-08-08-fr062-2-... 這一支舊檔案,2026-08-10-fr062-8-... (已補綁定的那支)與 04-seed-core.sql(出貨 seed 攤平現況,license-manage 一列 已含綁定)都不在白名單內、必須實際過守衛。這樣設計是為了避免「用 name 當白名單」 把未來任何新增同名路由的檔案也一併豁免掉,形成守衛盲區。
.3c(CM-1762,套件 migration+jedi pyproject 對齊)盤點「哪些套件有頁面」時,本 白名單是既有出貨路由的基線;套件新增選單路由時仍受本守衛約束,不會被本白名單 豁免(豁免以「檔案」為單位,套件 SQL 檔名與這裡登記的主專案檔名不會撞名)。