Compare commits
1 Commits
codex/agen
...
codex/know
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dc0a2ef53 |
@@ -2159,23 +2159,40 @@
|
||||
"title": "知識殿堂",
|
||||
"searchPlaceholder": "搜尋知識條目...",
|
||||
"allCategories": "全部",
|
||||
"rail": {
|
||||
"categoryTitle": "動態分類",
|
||||
"total": "總 KM",
|
||||
"aiExtracted": "AI 萃取",
|
||||
"controlledQueue": "受控隊列",
|
||||
"ragChunks": "RAG chunks"
|
||||
},
|
||||
"assetLens": {
|
||||
"title": "資產維度",
|
||||
"scope": "目前列表",
|
||||
"project": "專案",
|
||||
"product": "產品",
|
||||
"website": "網站",
|
||||
"service": "服務",
|
||||
"package": "套件",
|
||||
"tool": "工具"
|
||||
},
|
||||
"dataChain": {
|
||||
"errorTitle": "知識條目資料鏈路異常",
|
||||
"errorDescription": "主知識條目 API 未成功回應:{reason}。下方治理軌道仍會顯示 Hermes owner-review 與陳舊 KM 狀態,避免誤判成知識庫真的歸零。",
|
||||
"errorDescription": "主知識條目 API 未成功回應:{reason}。下方治理軌道仍會顯示 Hermes 受控覆核與陳舊 KM 狀態,避免誤判成知識庫真的歸零。",
|
||||
"retry": "重新讀取"
|
||||
},
|
||||
"decision": {
|
||||
"badge": "KM 治理警戒",
|
||||
"readback": "Hermes / AwoooP 只讀回讀",
|
||||
"readback": "Hermes / AwoooP 受控回讀",
|
||||
"title": "KM 自動化掌控台",
|
||||
"subtitle": "先看 stale ratio、負責人審查、ready / blocked 與寫回數,再進入條目列表;這裡把 KM、PlayBook、腳本、排程與 Verifier 的沉澱狀態前移到首屏。",
|
||||
"openWorkItems": "處理 Owner Review",
|
||||
"subtitle": "先看 stale ratio、受控覆核、ready / blocked 與寫回數,再進入條目列表;這裡把 KM、PlayBook、腳本、排程與 Verifier 的沉澱狀態前移到首屏。",
|
||||
"openWorkItems": "處理受控隊列",
|
||||
"card": {
|
||||
"staleRatio": "Stale Ratio",
|
||||
"staleRatioSub": "門檻 {threshold}",
|
||||
"staleTotal": "Stale KM",
|
||||
"staleTotalSub": "距離門檻需處理 {count}",
|
||||
"ownerQueue": "Owner Review",
|
||||
"ownerQueue": "受控覆核",
|
||||
"ownerQueueSub": "ready {ready} / blocked {blocked}",
|
||||
"writeback": "已寫回",
|
||||
"writebackSafe": "只讀回讀;讀取不寫入",
|
||||
@@ -2184,6 +2201,11 @@
|
||||
"ragIndexSub": "sources {sources};0 代表語意檢索尚未沉澱"
|
||||
}
|
||||
},
|
||||
"workspace": {
|
||||
"records": "Records",
|
||||
"automation": "Automation",
|
||||
"queue": "Queue"
|
||||
},
|
||||
"overview": {
|
||||
"metricTotal": "總條目",
|
||||
"metricLoaded": "目前列表",
|
||||
@@ -2196,7 +2218,7 @@
|
||||
},
|
||||
"assetLedger": {
|
||||
"title": "自動化資產沉澱總帳",
|
||||
"subtitle": "把目前 KM 清單與 Hermes owner-review 讀模型,對齊到 PlayBook、腳本、排程、Verifier 五類資產。",
|
||||
"subtitle": "把目前 KM 清單與 Hermes 受控覆核讀模型,對齊到 PlayBook、腳本、排程、Verifier 五類資產。",
|
||||
"readOnly": "只讀總覽",
|
||||
"boundary": "此區塊只顯示可追蹤沉澱狀態,不代表已授權修復、已寫回 KM 或已提高 PlayBook trust。",
|
||||
"asset": {
|
||||
@@ -2214,11 +2236,11 @@
|
||||
},
|
||||
"monitoring": {
|
||||
"title": "排程 / 監控規則",
|
||||
"detail": "含告警 / 監控訊號 {ready} 筆;負責人審查 待處理 {pending} 筆。"
|
||||
"detail": "含告警 / 監控訊號 {ready} 筆;受控覆核待處理 {pending} 筆。"
|
||||
},
|
||||
"verifier": {
|
||||
"title": "Verifier 回寫",
|
||||
"detail": "已批准或完成驗證 {ready} 筆;待 負責人審查 {pending} 筆。"
|
||||
"detail": "已批准或完成驗證 {ready} 筆;待受控覆核 {pending} 筆。"
|
||||
},
|
||||
"rag": {
|
||||
"title": "RAG / 向量索引",
|
||||
@@ -2244,7 +2266,7 @@
|
||||
"incident": "Incident",
|
||||
"playbook": "Playbook",
|
||||
"review": "審核",
|
||||
"sourceDetail": "AI {ai} / 人工 {human}",
|
||||
"sourceDetail": "AI {ai} / 受控建立 {human}",
|
||||
"knowledgeDetail": "已載入 {count}",
|
||||
"countDetail": "{count} / {total}"
|
||||
},
|
||||
@@ -2257,8 +2279,8 @@
|
||||
"missingIncidentAction": "補齊來源事故或標記為通用知識",
|
||||
"missingPlaybook": "缺 Playbook",
|
||||
"missingPlaybookAction": "匹配既有 Playbook或建立草稿",
|
||||
"reviewBacklog": "待 Owner 審核",
|
||||
"reviewBacklogAction": "高影響條目先人工覆核再批准"
|
||||
"reviewBacklog": "待受控覆核",
|
||||
"reviewBacklogAction": "高影響條目先進 controlled review,再由 AI 補齊 verifier 與寫回證據"
|
||||
},
|
||||
"entryLineage": {
|
||||
"title": "單筆證據鏈",
|
||||
@@ -2270,12 +2292,12 @@
|
||||
},
|
||||
"workItems": {
|
||||
"title": "Work Items 接續狀態",
|
||||
"subtitle": "這裡只讀取 Hermes KB owner-review queue,讓你知道陳舊 KM 是否已進入可追蹤工作項;實際預覽、確認與寫回仍在 AwoooP Work Items 執行。",
|
||||
"subtitle": "這裡只讀取 Hermes KB controlled review queue,讓你知道陳舊 KM 是否已進入可追蹤工作項;實際預覽、確認與寫回仍在 AwoooP Work Items 執行。",
|
||||
"open": "打開 Work Items",
|
||||
"loading": "讀取中",
|
||||
"threshold": "門檻 {value}",
|
||||
"entriesToThreshold": "需處理 {count} 筆才回到門檻",
|
||||
"ownerPendingSub": "待 Owner review",
|
||||
"ownerPendingSub": "待受控覆核",
|
||||
"readyBlockedSub": "ready / blocked",
|
||||
"burndownStatus": "狀態:{status}",
|
||||
"topCandidate": "最高優先處理項",
|
||||
@@ -2285,9 +2307,9 @@
|
||||
"topCandidateRefs": "Incident {incident} / Playbook {playbook}",
|
||||
"noCandidate": "目前沒有可顯示的 stale KM candidate。",
|
||||
"ownerSplit": "主責分工",
|
||||
"hermes": "Hermes:建立 KM 更新草稿與owner-review queue。",
|
||||
"hermes": "Hermes:建立 KM 更新草稿與 controlled review queue。",
|
||||
"openclaw": "OpenClaw:補告警分類、Playbook與上下文摘要。",
|
||||
"owner": "Owner:在 AwoooP Work Items 預覽後確認寫回。",
|
||||
"owner": "Controlled lane:在 AwoooP Work Items 預覽後確認寫回邊界。",
|
||||
"noWritesOnRead": "讀取不寫入",
|
||||
"unexpectedWrites": "偵測到 read endpoint 宣告會寫入",
|
||||
"flow": {
|
||||
@@ -2295,9 +2317,9 @@
|
||||
"scope": "從偵測到寫回與比例回測的目前位置",
|
||||
"node": {
|
||||
"detected": "偵測",
|
||||
"ownerReview": "Owner Review",
|
||||
"ownerReview": "Controlled Review",
|
||||
"dryRun": "乾跑預覽",
|
||||
"ownerConfirm": "Owner 確認",
|
||||
"ownerConfirm": "邊界確認",
|
||||
"writeback": "寫回 KM",
|
||||
"recheck": "比例回測"
|
||||
},
|
||||
@@ -2310,7 +2332,7 @@
|
||||
},
|
||||
"detail": {
|
||||
"detected": "目前 {ratio};門檻 {threshold}",
|
||||
"ownerReview": "{count} 筆等待 owner 審核",
|
||||
"ownerReview": "{count} 筆等待受控覆核",
|
||||
"dryRun": "{ready} 筆可乾跑;{blocked} 筆卡住",
|
||||
"ownerConfirm": "確認後才允許寫回,避免 AI 固化錯誤知識",
|
||||
"writeback": "{count} 筆已有 completion audit",
|
||||
@@ -2356,7 +2378,7 @@
|
||||
},
|
||||
"source": {
|
||||
"ai_extracted": "AI 萃取",
|
||||
"human": "人工建立"
|
||||
"human": "受控建立"
|
||||
},
|
||||
"signals": {
|
||||
"label": "訊號",
|
||||
@@ -2371,14 +2393,15 @@
|
||||
"execution": "執行紀錄",
|
||||
"execution_failed": "執行失敗",
|
||||
"failure": "失敗",
|
||||
"human_approved": "人工批准",
|
||||
"human_intervention": "人工介入",
|
||||
"human_approved": "受控批准",
|
||||
"human_intervention": "受控介入",
|
||||
"incident": "事故",
|
||||
"postmortem": "事後檢討",
|
||||
"telegram": "Telegram",
|
||||
"warning": "警告"
|
||||
},
|
||||
"category": {
|
||||
"AI自動化/Ansible受控修復": "AI 自動化 / Ansible 受控修復",
|
||||
"AI治理": "AI 治理",
|
||||
"alert_handling": "告警處理",
|
||||
"infrastructure": "基礎設施",
|
||||
@@ -2396,13 +2419,20 @@
|
||||
"flywheel_health": "飛輪健康",
|
||||
"ssl_cert": "SSL 憑證",
|
||||
"devops_tool": "DevOps 工具",
|
||||
"incident_postmortem": "事故檢討"
|
||||
"incident_postmortem": "事故檢討",
|
||||
"project": "專案",
|
||||
"product": "產品",
|
||||
"website": "網站",
|
||||
"service": "服務",
|
||||
"package": "套件",
|
||||
"tool": "工具",
|
||||
"source_control": "Source Control"
|
||||
},
|
||||
"filterByType": "篩選類型",
|
||||
"filterByStatus": "篩選狀態",
|
||||
"entries": "筆",
|
||||
"empty": "尚未建立任何知識條目",
|
||||
"emptyDescription": "知識庫將自動從 Incident 中萃取案例,你也可以手動新增",
|
||||
"emptyDescription": "知識庫將自動從 Incident、LOG、PlayBook 與 verifier 中萃取案例",
|
||||
"semanticSearchPlaceholder": "輸入語意搜尋查詢...",
|
||||
"semanticOn": "語意",
|
||||
"semanticOff": "語意",
|
||||
|
||||
@@ -2159,23 +2159,40 @@
|
||||
"title": "知識殿堂",
|
||||
"searchPlaceholder": "搜尋知識條目...",
|
||||
"allCategories": "全部",
|
||||
"rail": {
|
||||
"categoryTitle": "動態分類",
|
||||
"total": "總 KM",
|
||||
"aiExtracted": "AI 萃取",
|
||||
"controlledQueue": "受控隊列",
|
||||
"ragChunks": "RAG chunks"
|
||||
},
|
||||
"assetLens": {
|
||||
"title": "資產維度",
|
||||
"scope": "目前列表",
|
||||
"project": "專案",
|
||||
"product": "產品",
|
||||
"website": "網站",
|
||||
"service": "服務",
|
||||
"package": "套件",
|
||||
"tool": "工具"
|
||||
},
|
||||
"dataChain": {
|
||||
"errorTitle": "知識條目資料鏈路異常",
|
||||
"errorDescription": "主知識條目 API 未成功回應:{reason}。下方治理軌道仍會顯示 Hermes owner-review 與陳舊 KM 狀態,避免誤判成知識庫真的歸零。",
|
||||
"errorDescription": "主知識條目 API 未成功回應:{reason}。下方治理軌道仍會顯示 Hermes 受控覆核與陳舊 KM 狀態,避免誤判成知識庫真的歸零。",
|
||||
"retry": "重新讀取"
|
||||
},
|
||||
"decision": {
|
||||
"badge": "KM 治理警戒",
|
||||
"readback": "Hermes / AwoooP 只讀回讀",
|
||||
"readback": "Hermes / AwoooP 受控回讀",
|
||||
"title": "KM 自動化掌控台",
|
||||
"subtitle": "先看 stale ratio、負責人審查、ready / blocked 與寫回數,再進入條目列表;這裡把 KM、PlayBook、腳本、排程與 Verifier 的沉澱狀態前移到首屏。",
|
||||
"openWorkItems": "處理 Owner Review",
|
||||
"subtitle": "先看 stale ratio、受控覆核、ready / blocked 與寫回數,再進入條目列表;這裡把 KM、PlayBook、腳本、排程與 Verifier 的沉澱狀態前移到首屏。",
|
||||
"openWorkItems": "處理受控隊列",
|
||||
"card": {
|
||||
"staleRatio": "Stale Ratio",
|
||||
"staleRatioSub": "門檻 {threshold}",
|
||||
"staleTotal": "Stale KM",
|
||||
"staleTotalSub": "距離門檻需處理 {count}",
|
||||
"ownerQueue": "Owner Review",
|
||||
"ownerQueue": "受控覆核",
|
||||
"ownerQueueSub": "ready {ready} / blocked {blocked}",
|
||||
"writeback": "已寫回",
|
||||
"writebackSafe": "只讀回讀;讀取不寫入",
|
||||
@@ -2184,6 +2201,11 @@
|
||||
"ragIndexSub": "sources {sources};0 代表語意檢索尚未沉澱"
|
||||
}
|
||||
},
|
||||
"workspace": {
|
||||
"records": "Records",
|
||||
"automation": "Automation",
|
||||
"queue": "Queue"
|
||||
},
|
||||
"overview": {
|
||||
"metricTotal": "總條目",
|
||||
"metricLoaded": "目前列表",
|
||||
@@ -2196,7 +2218,7 @@
|
||||
},
|
||||
"assetLedger": {
|
||||
"title": "自動化資產沉澱總帳",
|
||||
"subtitle": "把目前 KM 清單與 Hermes owner-review 讀模型,對齊到 PlayBook、腳本、排程、Verifier 五類資產。",
|
||||
"subtitle": "把目前 KM 清單與 Hermes 受控覆核讀模型,對齊到 PlayBook、腳本、排程、Verifier 五類資產。",
|
||||
"readOnly": "只讀總覽",
|
||||
"boundary": "此區塊只顯示可追蹤沉澱狀態,不代表已授權修復、已寫回 KM 或已提高 PlayBook trust。",
|
||||
"asset": {
|
||||
@@ -2214,11 +2236,11 @@
|
||||
},
|
||||
"monitoring": {
|
||||
"title": "排程 / 監控規則",
|
||||
"detail": "含告警 / 監控訊號 {ready} 筆;負責人審查 待處理 {pending} 筆。"
|
||||
"detail": "含告警 / 監控訊號 {ready} 筆;受控覆核待處理 {pending} 筆。"
|
||||
},
|
||||
"verifier": {
|
||||
"title": "Verifier 回寫",
|
||||
"detail": "已批准或完成驗證 {ready} 筆;待 負責人審查 {pending} 筆。"
|
||||
"detail": "已批准或完成驗證 {ready} 筆;待受控覆核 {pending} 筆。"
|
||||
},
|
||||
"rag": {
|
||||
"title": "RAG / 向量索引",
|
||||
@@ -2244,7 +2266,7 @@
|
||||
"incident": "Incident",
|
||||
"playbook": "Playbook",
|
||||
"review": "審核",
|
||||
"sourceDetail": "AI {ai} / 人工 {human}",
|
||||
"sourceDetail": "AI {ai} / 受控建立 {human}",
|
||||
"knowledgeDetail": "已載入 {count}",
|
||||
"countDetail": "{count} / {total}"
|
||||
},
|
||||
@@ -2257,8 +2279,8 @@
|
||||
"missingIncidentAction": "補齊來源事故或標記為通用知識",
|
||||
"missingPlaybook": "缺 Playbook",
|
||||
"missingPlaybookAction": "匹配既有 Playbook或建立草稿",
|
||||
"reviewBacklog": "待 Owner 審核",
|
||||
"reviewBacklogAction": "高影響條目先人工覆核再批准"
|
||||
"reviewBacklog": "待受控覆核",
|
||||
"reviewBacklogAction": "高影響條目先進 controlled review,再由 AI 補齊 verifier 與寫回證據"
|
||||
},
|
||||
"entryLineage": {
|
||||
"title": "單筆證據鏈",
|
||||
@@ -2270,12 +2292,12 @@
|
||||
},
|
||||
"workItems": {
|
||||
"title": "Work Items 接續狀態",
|
||||
"subtitle": "這裡只讀取 Hermes KB owner-review queue,讓你知道陳舊 KM 是否已進入可追蹤工作項;實際預覽、確認與寫回仍在 AwoooP Work Items 執行。",
|
||||
"subtitle": "這裡只讀取 Hermes KB controlled review queue,讓你知道陳舊 KM 是否已進入可追蹤工作項;實際預覽、確認與寫回仍在 AwoooP Work Items 執行。",
|
||||
"open": "打開 Work Items",
|
||||
"loading": "讀取中",
|
||||
"threshold": "門檻 {value}",
|
||||
"entriesToThreshold": "需處理 {count} 筆才回到門檻",
|
||||
"ownerPendingSub": "待 Owner review",
|
||||
"ownerPendingSub": "待受控覆核",
|
||||
"readyBlockedSub": "ready / blocked",
|
||||
"burndownStatus": "狀態:{status}",
|
||||
"topCandidate": "最高優先處理項",
|
||||
@@ -2285,9 +2307,9 @@
|
||||
"topCandidateRefs": "Incident {incident} / Playbook {playbook}",
|
||||
"noCandidate": "目前沒有可顯示的 stale KM candidate。",
|
||||
"ownerSplit": "主責分工",
|
||||
"hermes": "Hermes:建立 KM 更新草稿與owner-review queue。",
|
||||
"hermes": "Hermes:建立 KM 更新草稿與 controlled review queue。",
|
||||
"openclaw": "OpenClaw:補告警分類、Playbook與上下文摘要。",
|
||||
"owner": "Owner:在 AwoooP Work Items 預覽後確認寫回。",
|
||||
"owner": "Controlled lane:在 AwoooP Work Items 預覽後確認寫回邊界。",
|
||||
"noWritesOnRead": "讀取不寫入",
|
||||
"unexpectedWrites": "偵測到 read endpoint 宣告會寫入",
|
||||
"flow": {
|
||||
@@ -2295,9 +2317,9 @@
|
||||
"scope": "從偵測到寫回與比例回測的目前位置",
|
||||
"node": {
|
||||
"detected": "偵測",
|
||||
"ownerReview": "Owner Review",
|
||||
"ownerReview": "Controlled Review",
|
||||
"dryRun": "乾跑預覽",
|
||||
"ownerConfirm": "Owner 確認",
|
||||
"ownerConfirm": "邊界確認",
|
||||
"writeback": "寫回 KM",
|
||||
"recheck": "比例回測"
|
||||
},
|
||||
@@ -2310,7 +2332,7 @@
|
||||
},
|
||||
"detail": {
|
||||
"detected": "目前 {ratio};門檻 {threshold}",
|
||||
"ownerReview": "{count} 筆等待 owner 審核",
|
||||
"ownerReview": "{count} 筆等待受控覆核",
|
||||
"dryRun": "{ready} 筆可乾跑;{blocked} 筆卡住",
|
||||
"ownerConfirm": "確認後才允許寫回,避免 AI 固化錯誤知識",
|
||||
"writeback": "{count} 筆已有 completion audit",
|
||||
@@ -2356,7 +2378,7 @@
|
||||
},
|
||||
"source": {
|
||||
"ai_extracted": "AI 萃取",
|
||||
"human": "人工建立"
|
||||
"human": "受控建立"
|
||||
},
|
||||
"signals": {
|
||||
"label": "訊號",
|
||||
@@ -2371,14 +2393,15 @@
|
||||
"execution": "執行紀錄",
|
||||
"execution_failed": "執行失敗",
|
||||
"failure": "失敗",
|
||||
"human_approved": "人工批准",
|
||||
"human_intervention": "人工介入",
|
||||
"human_approved": "受控批准",
|
||||
"human_intervention": "受控介入",
|
||||
"incident": "事故",
|
||||
"postmortem": "事後檢討",
|
||||
"telegram": "Telegram",
|
||||
"warning": "警告"
|
||||
},
|
||||
"category": {
|
||||
"AI自動化/Ansible受控修復": "AI 自動化 / Ansible 受控修復",
|
||||
"AI治理": "AI 治理",
|
||||
"alert_handling": "告警處理",
|
||||
"infrastructure": "基礎設施",
|
||||
@@ -2396,13 +2419,20 @@
|
||||
"flywheel_health": "飛輪健康",
|
||||
"ssl_cert": "SSL 憑證",
|
||||
"devops_tool": "DevOps 工具",
|
||||
"incident_postmortem": "事故檢討"
|
||||
"incident_postmortem": "事故檢討",
|
||||
"project": "專案",
|
||||
"product": "產品",
|
||||
"website": "網站",
|
||||
"service": "服務",
|
||||
"package": "套件",
|
||||
"tool": "工具",
|
||||
"source_control": "Source Control"
|
||||
},
|
||||
"filterByType": "篩選類型",
|
||||
"filterByStatus": "篩選狀態",
|
||||
"entries": "筆",
|
||||
"empty": "尚未建立任何知識條目",
|
||||
"emptyDescription": "知識庫將自動從 Incident 中萃取案例,你也可以手動新增",
|
||||
"emptyDescription": "知識庫將自動從 Incident、LOG、PlayBook 與 verifier 中萃取案例",
|
||||
"semanticSearchPlaceholder": "輸入語意搜尋查詢...",
|
||||
"semanticOn": "語意",
|
||||
"semanticOff": "語意",
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
"typecheck": "tsc --noEmit",
|
||||
"audit:ui-density": "node scripts/audit-ui-density.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@awoooi/lewooogo-core": "workspace:*",
|
||||
|
||||
130
apps/web/scripts/audit-ui-density.mjs
Normal file
130
apps/web/scripts/audit-ui-density.mjs
Normal file
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { readdirSync, readFileSync, statSync } from 'node:fs'
|
||||
import { join, relative } from 'node:path'
|
||||
|
||||
const appRoot = join(process.cwd(), 'src', 'app')
|
||||
|
||||
const textLikePatterns = [
|
||||
/<p\b/g,
|
||||
/<span\b/g,
|
||||
/<li\b/g,
|
||||
/<h[1-6]\b/g,
|
||||
/<ReactMarkdown\b/g,
|
||||
/prose\b/g,
|
||||
/leading-\d/g,
|
||||
]
|
||||
|
||||
const controlPatterns = [
|
||||
/<button\b/g,
|
||||
/<select\b/g,
|
||||
/<input\b/g,
|
||||
/<Link\b/g,
|
||||
/role=["']tab/g,
|
||||
/type=["']checkbox/g,
|
||||
]
|
||||
|
||||
const layoutPatterns = [
|
||||
/grid-cols-/g,
|
||||
/flex\b/g,
|
||||
/overflow-y-auto/g,
|
||||
/sticky\b/g,
|
||||
]
|
||||
|
||||
const textWallPatterns = [
|
||||
/max-w-(?:2xl|3xl|4xl|5xl|6xl|7xl)/g,
|
||||
/line-clamp-[4-9]/g,
|
||||
/prose\b/g,
|
||||
/<ReactMarkdown\b/g,
|
||||
]
|
||||
|
||||
const manualTerms = [
|
||||
/\u4eba\u5de5/g,
|
||||
/\u624b\u52d5/g,
|
||||
/manual/gi,
|
||||
/needs_human/gi,
|
||||
/human_/gi,
|
||||
]
|
||||
|
||||
const fixedTaxonomyPatterns = [
|
||||
/const\s+CATEGORIES\s*=/g,
|
||||
]
|
||||
|
||||
function collectPages(dir, acc = []) {
|
||||
for (const name of readdirSync(dir)) {
|
||||
const fullPath = join(dir, name)
|
||||
const stats = statSync(fullPath)
|
||||
if (stats.isDirectory()) {
|
||||
collectPages(fullPath, acc)
|
||||
} else if (name === 'page.tsx') {
|
||||
acc.push(fullPath)
|
||||
}
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
function countMatches(source, patterns) {
|
||||
return patterns.reduce((sum, pattern) => sum + (source.match(pattern) ?? []).length, 0)
|
||||
}
|
||||
|
||||
function routeFromPage(filePath) {
|
||||
return relative(appRoot, filePath)
|
||||
.replace(/\/page\.tsx$/, '')
|
||||
.replace(/\[locale\]/, ':locale')
|
||||
}
|
||||
|
||||
const pages = collectPages(appRoot)
|
||||
.map(filePath => {
|
||||
const source = readFileSync(filePath, 'utf8')
|
||||
const lineCount = source.split('\n').length
|
||||
const textSignals = countMatches(source, textLikePatterns)
|
||||
const controls = countMatches(source, controlPatterns)
|
||||
const layoutSignals = countMatches(source, layoutPatterns)
|
||||
const textWallSignals = countMatches(source, textWallPatterns)
|
||||
const manualSignals = countMatches(source, manualTerms)
|
||||
const fixedTaxonomySignals = countMatches(source, fixedTaxonomyPatterns)
|
||||
const nestedCardSignals = (source.match(/rounded-(?:md|lg|xl)[^`'"]*border/g) ?? []).length
|
||||
const score =
|
||||
Math.round(
|
||||
(textSignals * 1.2)
|
||||
+ (textWallSignals * 8)
|
||||
+ (manualSignals * 4)
|
||||
+ (fixedTaxonomySignals * 10)
|
||||
+ (nestedCardSignals * 0.7)
|
||||
- (controls * 1.5)
|
||||
- (layoutSignals * 0.4),
|
||||
)
|
||||
|
||||
return {
|
||||
route: routeFromPage(filePath),
|
||||
file: relative(process.cwd(), filePath),
|
||||
lines: lineCount,
|
||||
score,
|
||||
textSignals,
|
||||
controls,
|
||||
layoutSignals,
|
||||
textWallSignals,
|
||||
manualSignals,
|
||||
fixedTaxonomySignals,
|
||||
nestedCardSignals,
|
||||
}
|
||||
})
|
||||
.sort((a, b) => b.score - a.score)
|
||||
|
||||
console.log('AWOOOI_UI_DENSITY_AUDIT')
|
||||
console.table(
|
||||
pages.slice(0, 20).map(page => ({
|
||||
route: page.route,
|
||||
score: page.score,
|
||||
lines: page.lines,
|
||||
text: page.textSignals,
|
||||
controls: page.controls,
|
||||
manual: page.manualSignals,
|
||||
fixedTaxonomy: page.fixedTaxonomySignals,
|
||||
})),
|
||||
)
|
||||
|
||||
console.log(JSON.stringify({
|
||||
page_count: pages.length,
|
||||
top_routes: pages.slice(0, 20),
|
||||
}, null, 2))
|
||||
@@ -1288,7 +1288,6 @@ const COMMANDER_INSERTED_REQUIREMENT_FALLBACK: PriorityWorkOrderResponse = {
|
||||
"windows99_uptime_unknown",
|
||||
],
|
||||
stockplatform_public_api_runtime_status: "stockplatform_public_api_runtime_ready",
|
||||
stockplatform_public_api_runtime_ready: true,
|
||||
stockplatform_public_api_controlled_recovery_preflight_status:
|
||||
"not_required_stockplatform_runtime_ready",
|
||||
stockplatform_public_api_controlled_recovery_data_dependency: "none",
|
||||
|
||||
@@ -17,10 +17,11 @@ import { useTranslations } from 'next-intl'
|
||||
import Link from 'next/link'
|
||||
import { cn } from '@/lib/utils'
|
||||
import {
|
||||
Search, BookOpen, FileText, Shield, Cpu,
|
||||
type LucideIcon,
|
||||
Search, BookOpen, FileText, Shield, Cpu, Globe2,
|
||||
Server, Eye, Bot, ChevronRight, Plus, Sparkles, ClipboardList, Tag, TriangleAlert,
|
||||
GitBranch, CheckCircle2, Clock3, Link2, FileSearch, ListChecks, ArrowRight, Users,
|
||||
PlayCircle, Database, RotateCw,
|
||||
PlayCircle, Database, RotateCw, Package, Wrench,
|
||||
} from 'lucide-react'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
@@ -130,20 +131,58 @@ interface KnowledgeGovernanceTelemetry {
|
||||
}
|
||||
|
||||
type AutomationAssetKey = 'km' | 'playbook' | 'script' | 'monitoring' | 'verifier' | 'rag'
|
||||
type WorkspaceViewKey = 'records' | 'automation' | 'queue'
|
||||
type AssetLensKey = 'project' | 'product' | 'website' | 'service' | 'package' | 'tool'
|
||||
|
||||
// =============================================================================
|
||||
// Category Config
|
||||
// =============================================================================
|
||||
|
||||
const CATEGORY_ICONS: Record<string, React.ReactNode> = {
|
||||
'AI自動化/Ansible受控修復': <Bot className="w-4 h-4" />,
|
||||
AI治理: <Bot className="w-4 h-4" />,
|
||||
alert_handling: <TriangleAlert className="w-4 h-4" />,
|
||||
infrastructure: <Server className="w-4 h-4" />,
|
||||
application: <FileText className="w-4 h-4" />,
|
||||
ai_system: <Bot className="w-4 h-4" />,
|
||||
security: <Shield className="w-4 h-4" />,
|
||||
host_resource: <Cpu className="w-4 h-4" />,
|
||||
database: <Database className="w-4 h-4" />,
|
||||
auto_repair: <Wrench className="w-4 h-4" />,
|
||||
postmortem: <FileSearch className="w-4 h-4" />,
|
||||
incident_postmortem: <FileSearch className="w-4 h-4" />,
|
||||
external_site: <Globe2 className="w-4 h-4" />,
|
||||
backup_failure: <Database className="w-4 h-4" />,
|
||||
kubernetes: <Package className="w-4 h-4" />,
|
||||
flywheel_health: <RotateCw className="w-4 h-4" />,
|
||||
ssl_cert: <Shield className="w-4 h-4" />,
|
||||
devops_tool: <Wrench className="w-4 h-4" />,
|
||||
general: <BookOpen className="w-4 h-4" />,
|
||||
}
|
||||
|
||||
const CATEGORIES = ['infrastructure', 'application', 'ai_system', 'security'] as const
|
||||
const DEFAULT_CATEGORY_ORDER = [
|
||||
'alert_handling',
|
||||
'AI自動化/Ansible受控修復',
|
||||
'infrastructure',
|
||||
'host_resource',
|
||||
'postmortem',
|
||||
'AI治理',
|
||||
'application',
|
||||
'ai_system',
|
||||
'security',
|
||||
'database',
|
||||
'auto_repair',
|
||||
'kubernetes',
|
||||
'external_site',
|
||||
'devops_tool',
|
||||
'ssl_cert',
|
||||
'backup_failure',
|
||||
'flywheel_health',
|
||||
'general',
|
||||
]
|
||||
|
||||
const KNOWN_CATEGORY_KEYS = new Set([
|
||||
'AI自動化/Ansible受控修復',
|
||||
'AI治理',
|
||||
'alert_handling',
|
||||
'application',
|
||||
@@ -160,8 +199,14 @@ const KNOWN_CATEGORY_KEYS = new Set([
|
||||
'infrastructure',
|
||||
'kubernetes',
|
||||
'postmortem',
|
||||
'project',
|
||||
'product',
|
||||
'service',
|
||||
'security',
|
||||
'source_control',
|
||||
'ssl_cert',
|
||||
'tool',
|
||||
'website',
|
||||
])
|
||||
|
||||
const CATEGORY_OVERVIEW_LIMIT = 8
|
||||
@@ -222,17 +267,29 @@ const STATUS_COLORS: Record<string, string> = {
|
||||
}
|
||||
|
||||
const CATEGORY_BAR_COLORS: Record<string, string> = {
|
||||
'AI自動化/Ansible受控修復': 'bg-purple-500',
|
||||
AI治理: 'bg-claw-blue',
|
||||
alert_handling: 'bg-status-warning',
|
||||
infrastructure: 'bg-claw-blue',
|
||||
host_resource: 'bg-status-warning',
|
||||
application: 'bg-status-healthy',
|
||||
ai_system: 'bg-purple-500',
|
||||
security: 'bg-status-warning',
|
||||
database: 'bg-status-critical',
|
||||
auto_repair: 'bg-purple-500',
|
||||
external_site: 'bg-status-healthy',
|
||||
devops_tool: 'bg-claw-blue',
|
||||
kubernetes: 'bg-claw-blue',
|
||||
ssl_cert: 'bg-status-warning',
|
||||
backup_failure: 'bg-status-critical',
|
||||
flywheel_health: 'bg-status-healthy',
|
||||
general: 'bg-nothing-gray-400',
|
||||
postmortem: 'bg-status-critical',
|
||||
__other__: 'bg-nothing-gray-300',
|
||||
}
|
||||
|
||||
const CATEGORY_ORDER_RANK = new Map(DEFAULT_CATEGORY_ORDER.map((category, index) => [category, index]))
|
||||
|
||||
const TAG_TONES: Record<string, string> = {
|
||||
critical: 'border-status-critical/20 bg-status-critical/10 text-status-critical',
|
||||
execution_failed: 'border-status-critical/20 bg-status-critical/10 text-status-critical',
|
||||
@@ -285,6 +342,28 @@ const getSignalTags = (entry: KnowledgeEntry) => {
|
||||
})
|
||||
}
|
||||
|
||||
const entrySearchText = (entry: KnowledgeEntry) =>
|
||||
[
|
||||
entry.title,
|
||||
entry.category,
|
||||
entry.entry_type,
|
||||
entry.source,
|
||||
...entry.tags,
|
||||
].join(' ').toLowerCase()
|
||||
|
||||
const entryMatchesAny = (entry: KnowledgeEntry, candidates: string[]) => {
|
||||
const text = entrySearchText(entry)
|
||||
return candidates.some(candidate => text.includes(candidate.toLowerCase()))
|
||||
}
|
||||
|
||||
const mergeCategoryCounts = (rows: CategoryCount[]) => {
|
||||
const merged = new Map<string, number>()
|
||||
rows.forEach(row => {
|
||||
merged.set(row.category, (merged.get(row.category) ?? 0) + row.count)
|
||||
})
|
||||
return Array.from(merged.entries()).map(([category, count]) => ({ category, count }))
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Component
|
||||
// =============================================================================
|
||||
@@ -319,6 +398,7 @@ export default function KnowledgeBasePage({
|
||||
const [semanticResults, setSemanticResults] = useState<(KnowledgeEntry & { score: number })[]>([])
|
||||
const [semanticLoading, setSemanticLoading] = useState(false)
|
||||
const [selectedEntry, setSelectedEntry] = useState<KnowledgeEntry | null>(null)
|
||||
const [workspaceView, setWorkspaceView] = useState<WorkspaceViewKey>('records')
|
||||
|
||||
// KB-D: approve/archive state (2026-04-03 ogt)
|
||||
const [actionLoading, setActionLoading] = useState(false)
|
||||
@@ -519,8 +599,8 @@ export default function KnowledgeBasePage({
|
||||
|
||||
const categoryRows = useMemo(() => {
|
||||
const sourceRows = categories.length > 0
|
||||
? [...categories].sort((a, b) => b.count - a.count)
|
||||
: CATEGORIES.map(category => ({ category, count: 0 }))
|
||||
? mergeCategoryCounts(categories).sort((a, b) => b.count - a.count)
|
||||
: DEFAULT_CATEGORY_ORDER.map(category => ({ category, count: 0 }))
|
||||
|
||||
const visibleRows = sourceRows.slice(0, CATEGORY_OVERVIEW_LIMIT).map(row => {
|
||||
const pct = totalCount > 0 ? Math.round((row.count / totalCount) * 100) : 0
|
||||
@@ -542,6 +622,81 @@ export default function KnowledgeBasePage({
|
||||
]
|
||||
}, [categories, totalCount])
|
||||
|
||||
const categoryNavigationRows = useMemo(() => {
|
||||
const sourceRows = categories.length > 0
|
||||
? mergeCategoryCounts(categories).filter(row => row.count > 0)
|
||||
: DEFAULT_CATEGORY_ORDER.map(category => ({ category, count: 0 }))
|
||||
|
||||
return [...sourceRows].sort((a, b) => {
|
||||
const aRank = CATEGORY_ORDER_RANK.get(a.category) ?? Number.MAX_SAFE_INTEGER
|
||||
const bRank = CATEGORY_ORDER_RANK.get(b.category) ?? Number.MAX_SAFE_INTEGER
|
||||
if (aRank !== bRank) return aRank - bRank
|
||||
return b.count - a.count
|
||||
})
|
||||
}, [categories])
|
||||
|
||||
const assetLensRows = useMemo(() => {
|
||||
const rows: Array<{
|
||||
key: AssetLensKey
|
||||
icon: LucideIcon
|
||||
count: number
|
||||
tone: string
|
||||
}> = [
|
||||
{
|
||||
key: 'project',
|
||||
icon: GitBranch,
|
||||
count: displayedEntries.filter(entry => entryMatchesAny(entry, [
|
||||
'project', 'repo', 'repository', 'gitea', 'branch', 'workflow', 'source_control',
|
||||
])).length,
|
||||
tone: 'text-claw-blue',
|
||||
},
|
||||
{
|
||||
key: 'product',
|
||||
icon: Package,
|
||||
count: displayedEntries.filter(entry => entryMatchesAny(entry, [
|
||||
'product', 'awoooi', 'awooop', 'iwooos', 'vibework', 'stockplatform', 'momo', 'awooogo', 'agent-bounty', 'tsenyang',
|
||||
])).length,
|
||||
tone: 'text-purple-600',
|
||||
},
|
||||
{
|
||||
key: 'website',
|
||||
icon: Globe2,
|
||||
count: displayedEntries.filter(entry =>
|
||||
entry.category === 'external_site'
|
||||
|| entryMatchesAny(entry, ['website', 'site', 'nginx', 'ssl', 'domain', 'route', 'frontend']),
|
||||
).length,
|
||||
tone: 'text-status-healthy',
|
||||
},
|
||||
{
|
||||
key: 'service',
|
||||
icon: Server,
|
||||
count: displayedEntries.filter(entry =>
|
||||
['infrastructure', 'application', 'ai_system', 'database', 'host_resource', 'kubernetes', 'alert_handling'].includes(entry.category)
|
||||
|| entryMatchesAny(entry, ['service', 'daemon', 'api', 'worker', 'runtime', 'container', 'pod']),
|
||||
).length,
|
||||
tone: 'text-primary',
|
||||
},
|
||||
{
|
||||
key: 'package',
|
||||
icon: Package,
|
||||
count: displayedEntries.filter(entry => entryMatchesAny(entry, [
|
||||
'package', 'dependency', 'npm', 'pnpm', 'node', 'python', 'pip', 'prisma', 'next', 'library',
|
||||
])).length,
|
||||
tone: 'text-status-warning',
|
||||
},
|
||||
{
|
||||
key: 'tool',
|
||||
icon: Wrench,
|
||||
count: displayedEntries.filter(entry =>
|
||||
entry.category === 'devops_tool'
|
||||
|| entryMatchesAny(entry, ['tool', 'ansible', 'mcp', 'playbook', 'telegram', 'wazuh', 'kali', 'sentry', 'signoz', 'runner']),
|
||||
).length,
|
||||
tone: 'text-status-critical',
|
||||
},
|
||||
]
|
||||
return rows
|
||||
}, [displayedEntries])
|
||||
|
||||
const qualityRows = useMemo(() => {
|
||||
const loaded = displayedEntries.length
|
||||
const now = Date.now()
|
||||
@@ -887,11 +1042,20 @@ export default function KnowledgeBasePage({
|
||||
] as const
|
||||
}, [formatCount, governanceSummary, governanceTelemetry.ragStats, t])
|
||||
|
||||
const workspaceViews = useMemo(
|
||||
() => [
|
||||
{ key: 'records' as WorkspaceViewKey, icon: BookOpen },
|
||||
{ key: 'automation' as WorkspaceViewKey, icon: Bot },
|
||||
{ key: 'queue' as WorkspaceViewKey, icon: ListChecks },
|
||||
],
|
||||
[],
|
||||
)
|
||||
|
||||
const content = (
|
||||
<div className="flex min-h-[calc(100vh-64px)] flex-col lg:h-[calc(100vh-64px)] lg:flex-row">
|
||||
|
||||
{/* 左側分類導航 */}
|
||||
<aside className="w-full flex-shrink-0 border-b border-nothing-gray-200/50 bg-white/50 p-3 lg:w-52 lg:border-b-0 lg:border-r">
|
||||
<aside className="w-full flex-shrink-0 border-b border-nothing-gray-200/50 bg-white/60 p-3 lg:w-60 lg:border-b-0 lg:border-r">
|
||||
<h2 className="font-heading text-xs font-semibold text-tertiary uppercase tracking-widest mb-3 px-2">
|
||||
{t('title')}
|
||||
</h2>
|
||||
@@ -911,10 +1075,16 @@ export default function KnowledgeBasePage({
|
||||
<span className="text-xs text-muted">{totalCount}</span>
|
||||
</button>
|
||||
|
||||
<div className="mt-3 flex items-center justify-between px-2">
|
||||
<span className="text-[10px] font-label uppercase tracking-wider text-muted">{t('rail.categoryTitle')}</span>
|
||||
<span className="text-[10px] font-body tabular-nums text-muted">{categoryNavigationRows.length}</span>
|
||||
</div>
|
||||
|
||||
{/* Category items */}
|
||||
<div className="mt-1 grid grid-cols-2 gap-0.5 lg:block lg:space-y-0.5">
|
||||
{CATEGORIES.map(cat => {
|
||||
const count = categories.find(c => c.category === cat)?.count ?? 0
|
||||
<div className="mt-1 grid max-h-60 grid-cols-2 gap-0.5 overflow-y-auto pr-1 lg:block lg:space-y-0.5">
|
||||
{categoryNavigationRows.map(row => {
|
||||
const cat = row.category
|
||||
const icon = CATEGORY_ICONS[cat] ?? <BookOpen className="w-4 h-4" />
|
||||
return (
|
||||
<button
|
||||
key={cat}
|
||||
@@ -926,13 +1096,63 @@ export default function KnowledgeBasePage({
|
||||
: 'text-secondary hover:bg-nothing-gray-100'
|
||||
)}
|
||||
>
|
||||
{CATEGORY_ICONS[cat]}
|
||||
{icon}
|
||||
<span className="flex-1 text-left">{formatCategoryLabel(cat)}</span>
|
||||
<span className="text-xs text-muted">{count}</span>
|
||||
<span className="text-xs text-muted">{formatCount(row.count)}</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="mt-4 rounded-md border border-nothing-gray-200 bg-white/75 p-2">
|
||||
<div className="mb-2 flex items-center justify-between gap-2">
|
||||
<span className="truncate text-[10px] font-label uppercase tracking-wider text-muted">{t('assetLens.title')}</span>
|
||||
<span className="shrink-0 text-[10px] font-body text-muted">{t('assetLens.scope')}</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-1">
|
||||
{assetLensRows.map(row => {
|
||||
const Icon = row.icon
|
||||
return (
|
||||
<div key={row.key} className="rounded border border-nothing-gray-100 bg-nothing-gray-50 px-2 py-1.5">
|
||||
<div className="flex items-center justify-between gap-1">
|
||||
<span className="truncate text-[10px] font-body text-secondary">{t(`assetLens.${row.key}` as never)}</span>
|
||||
<Icon className={cn('h-3 w-3 shrink-0', row.tone)} aria-hidden={true} />
|
||||
</div>
|
||||
<p className={cn('mt-1 text-sm font-heading font-semibold tabular-nums', row.tone)}>
|
||||
{formatCount(row.count)}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 grid grid-cols-2 gap-2 lg:grid-cols-1">
|
||||
{[
|
||||
{ label: t('rail.total'), value: isKnowledgeListUnavailable ? '--' : formatCount(total), icon: BookOpen },
|
||||
{ label: t('rail.aiExtracted'), value: formatCount(visibleSummary.aiExtracted), icon: Bot },
|
||||
{
|
||||
label: t('rail.controlledQueue'),
|
||||
value: governanceLoading ? '--' : formatCount(governanceSummary.ownerPending),
|
||||
icon: ListChecks,
|
||||
},
|
||||
{
|
||||
label: t('rail.ragChunks'),
|
||||
value: governanceLoading ? '--' : formatCount(governanceTelemetry.ragStats?.total_chunks ?? 0),
|
||||
icon: FileSearch,
|
||||
},
|
||||
].map(item => {
|
||||
const Icon = item.icon
|
||||
return (
|
||||
<div key={item.label} className="rounded-md border border-nothing-gray-200 bg-white/75 px-2.5 py-2">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<span className="truncate text-[10px] font-label uppercase tracking-wider text-muted">{item.label}</span>
|
||||
<Icon className="h-3.5 w-3.5 shrink-0 text-claw-blue" aria-hidden="true" />
|
||||
</div>
|
||||
<p className="mt-1 text-lg font-heading font-semibold tabular-nums text-primary">{item.value}</p>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* 主區域 */}
|
||||
@@ -1077,9 +1297,31 @@ export default function KnowledgeBasePage({
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className="mt-3 flex flex-wrap gap-1 rounded-md border border-nothing-gray-200 bg-nothing-gray-50 p-1">
|
||||
{workspaceViews.map(view => {
|
||||
const Icon = view.icon
|
||||
return (
|
||||
<button
|
||||
key={view.key}
|
||||
type="button"
|
||||
onClick={() => setWorkspaceView(view.key)}
|
||||
className={cn(
|
||||
'inline-flex flex-1 items-center justify-center gap-1.5 rounded px-3 py-1.5 text-xs font-label transition-colors sm:flex-none',
|
||||
workspaceView === view.key
|
||||
? 'bg-white text-claw-blue shadow-sm'
|
||||
: 'text-muted hover:bg-white/70 hover:text-secondary',
|
||||
)}
|
||||
>
|
||||
<Icon className="h-3.5 w-3.5" aria-hidden="true" />
|
||||
{t(`workspace.${view.key}` as never)}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 xl:grid-cols-[minmax(0,1fr)_280px]">
|
||||
{workspaceView === 'records' && (
|
||||
<div className="grid grid-cols-1 items-start gap-4 xl:grid-cols-[minmax(0,1fr)_280px]">
|
||||
<div className="grid grid-cols-2 gap-2 lg:grid-cols-4">
|
||||
{[
|
||||
{ label: t('overview.metricTotal'), value: formatCount(total), sub: t('overview.scopeFiltered'), tone: 'text-claw-blue' },
|
||||
@@ -1120,7 +1362,9 @@ export default function KnowledgeBasePage({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{workspaceView === 'automation' && (
|
||||
<div className="mt-3 rounded-md border border-nothing-gray-200 bg-white/70 px-3 py-2">
|
||||
<div className="mb-3 flex flex-col gap-1 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div className="min-w-0">
|
||||
@@ -1169,7 +1413,9 @@ export default function KnowledgeBasePage({
|
||||
{t('assetLedger.boundary')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{workspaceView === 'records' && (
|
||||
<div className="mt-3 rounded-md border border-nothing-gray-200 bg-white/70 px-3 py-2">
|
||||
<div className="mb-2 flex items-center justify-between gap-2">
|
||||
<p className="text-[10px] font-label uppercase tracking-wider text-muted">{t('quality.title')}</p>
|
||||
@@ -1194,7 +1440,9 @@ export default function KnowledgeBasePage({
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(workspaceView === 'records' || workspaceView === 'automation') && (
|
||||
<div className="mt-3 grid grid-cols-1 gap-3 xl:grid-cols-[minmax(0,1.25fr)_minmax(260px,0.75fr)]">
|
||||
<div className="rounded-md border border-nothing-gray-200 bg-white/70 px-3 py-2">
|
||||
<div className="mb-3 flex items-center justify-between gap-2">
|
||||
@@ -1259,7 +1507,9 @@ export default function KnowledgeBasePage({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{workspaceView === 'queue' && (
|
||||
<div className="mt-3 rounded-md border border-nothing-gray-200 bg-white/70 px-3 py-3">
|
||||
<div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div className="min-w-0">
|
||||
@@ -1413,6 +1663,7 @@ export default function KnowledgeBasePage({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* 結果列表 */}
|
||||
|
||||
@@ -20,6 +20,24 @@
|
||||
- P0-006 仍 fail-closed:最新 production reboot SLO scorecard 仍有 `active_blocker_count=11`,包含 99 remote execution channel、VMware autostart readback、host boot detection、service/backup/Wazuh 類 blocker;不得宣稱 10 分鐘全主機自動恢復 SLO 已完成。
|
||||
- 未讀 secret / token / `.env` / raw sessions / SQLite / auth;未使用 GitHub / gh;未 workflow_dispatch;未重啟 host / VM / service;未 Docker / Nginx / K3s / DB / firewall restart;未 DROP / TRUNCATE / restore / prune / delete / force push。
|
||||
|
||||
## 2026-07-03 — 00:31 Knowledge Base 分類/資產鏡頭與 UI density audit
|
||||
|
||||
**完成內容**:
|
||||
- `/zh-TW/knowledge-base` 左側分類由固定四類改為 production category readback 驅動,合併重複分類計數,移除 misleading 的 `應用層 0`、`安全 / 合規 0` 這類空殼分類。
|
||||
- 新增資產鏡頭:專案、產品、網站、服務、套件、工具,先用目前列表的 title / category / type / tags 做可解釋分群,讓 KM 不再只以少數技術類別呈現。
|
||||
- Knowledge Base 工作區改成 `記錄 / 自動化 / 隊列` 分段視圖,降低治理文字牆一次全部攤開的問題。
|
||||
- 新增 `pnpm --dir apps/web audit:ui-density`,自動掃描所有 `src/app/**/page.tsx`,依文字密度、控制項不足、人工語意、固定分類等訊號排出 UI/UX 優先序;不用再靠逐頁人工回報。
|
||||
- 修正 rebase 後 `awooop/work-items` fallback summary 多出的 `stockplatform_public_api_runtime_ready` 欄位,讓最新 main 的 web typecheck 恢復通過。
|
||||
|
||||
**驗證**:
|
||||
- `python3.11 -m json.tool apps/web/messages/zh-TW.json`、`python3.11 -m json.tool apps/web/messages/en.json`:通過。
|
||||
- `pnpm --dir apps/web typecheck`:通過。
|
||||
- `pnpm --dir apps/web audit:ui-density`:通過;目前前五優先頁為 `:locale/iwooos`、`:locale/awooop/work-items`、`:locale/awooop/runs`、`:locale/awooop`、`:locale/awooop/approvals`。
|
||||
- Local web + production API smoke:desktop / mobile HTTP 200,主 KM total `727` 可見,`告警處理 225`、`AI 自動化 / Ansible 受控修復 154`、資產鏡頭可見,API response 全部 200,console error `0`。
|
||||
|
||||
**仍維持**:
|
||||
- 未讀 secret / token / `.env` / raw sessions / SQLite / auth;未使用 GitHub / gh;未 production DB schema / data destructive write;未重啟主機 / VM / Docker / K3s / DB / firewall。
|
||||
|
||||
## 2026-07-03 — 00:11 P0-006 latest CD profile / Windows99 live probe readback
|
||||
|
||||
**完成內容**:
|
||||
|
||||
Reference in New Issue
Block a user