fix(console): 駕駛艙首頁改讀 live 一手資料,stale 殘骸誠實標示

leo 抱怨「等你的事」錯了好幾天、首頁總覽對不上實際進度。診斷結論:
- dash_wait 沒有活的維護管線(最後寫入 07-04;等leo清單#11 已於 07-05
  銷案=誤判,dashboard 卻繼續掛著)——真相源其實是 InkStoneCo sprint 檔
  「## 等 leo 清單」(progress-guard 每日核實維護)
- dash_task 的 scope:today 沒有日期,07-04 的路線到 07-07 還被當今天的
- 燈號吃到 stale 任務;系統狀況/總覽無 live 信號

修法:
- 等你的事:首選 Gitea sprint 等leo清單(合併最新兩檔——07b 開了、🔴 仍
  掛 07a;需 GITEA_TOKEN secret);讀不到 fallback dash_wait 必標 age+stale
- 今日路線:台北日曆日判 is_today,非今日寫入降級「最後路線(N 天前)」
- 燈號只吃今日寫入任務+心跳+KBDB /health,附 light_reason
- 新增系統狀況(KBDB /health、embed enabled:false 誠實顯示、graph
  /triplets/stats、workflow 數)與總庫規模(entries/wiki_card/triplets)全 live
- 聚合判定抽純函式 lib/console-dashboard-model.ts,vitest 15 案例
  (含真實 sprint 檔形態:劃掉列/ 急迫欄/R1 字母編號/🔴 排序)

驗證:tsc --noEmit 0;vitest 65/66(1 失敗為 main 既有 executor.test.ts,
stash 復原後同樣失敗,非本次引入);parser 對 live sprint-2026-07a/07b
dry-run 撈出 🔴#13 等 9 項 open、清單維護於 31 分鐘前。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015d5jDbuqT5Htwv3Q88XXKk
This commit is contained in:
2026-07-07 11:02:22 +00:00
parent 29ba85e848
commit 99da35a885
7 changed files with 790 additions and 101 deletions
+35 -6
View File
@@ -326,6 +326,7 @@ function renderConsoleHtml(registryBase: string): string {
<div class="wait-box" id="ck-waitbox">
<div class="wait-head">等你的事</div>
<div id="ck-wait" class="wait-none">載入中…</div>
<div id="ck-waitmeta" style="margin-top:10px;text-align:center;font-size:12.5px;color:rgba(var(--ink-rgb),.45);line-height:1.7"></div>
</div>
</div>
<div>
@@ -699,6 +700,12 @@ function renderConsoleHtml(registryBase: string): string {
var cls = TASK_ICONS[t.status] ? t.status : 'blocked';
return '<li class="' + cls + '"><span class="ic">' + (TASK_ICONS[t.status] || '●') + '</span><span>' + esc(t.title) + '</span></li>';
}
function ckAge(m) {
if (m == null || m < 0) return '時間不明';
if (m < 60) return m + ' 分鐘前';
if (m < 2880) return Math.round(m / 60) + ' 小時前';
return Math.round(m / 1440) + ' 天前';
}
function loadCockpit() {
var now = new Date();
$('ck-date').textContent = CNUM[now.getMonth() + 1] + '月' + cnDay(now.getDate()) + '日';
@@ -711,24 +718,46 @@ function renderConsoleHtml(registryBase: string): string {
orb.style.animation = cfg.anim + ' 3.4s ease-in-out infinite';
$('ck-char').textContent = cfg.ch;
$('ck-title').textContent = cfg.title;
$('ck-sub').textContent = d.last_beat
$('ck-sub').textContent = (d.last_beat
? d.last_beat.actor + '・' + d.last_beat.ago_minutes + ' 分鐘前' + (d.last_beat.note ? '・' + d.last_beat.note : '')
: '尚無心跳資料';
: '尚無心跳資料') + (d.light !== 'green' && d.light_reason ? '' + d.light_reason + '' : '');
var done = d.today_done || 0, total = d.today_total || 0;
$('ck-done').textContent = done; $('ck-total').textContent = total;
$('ck-bar').style.width = (total ? Math.round(done / total * 100) : 0) + '%';
$('ck-inbox').textContent = d.inbox_new || 0;
var wb = $('ck-waitbox'), body = $('ck-wait');
// 等你的事:來源與維護時間攤開講(fix/console-dashboard-live-data)——
// Gitea sprint 等leo清單是活資料源;fallback dash_wait 一定標 stale,不假裝是今天的
var wb = $('ck-waitbox'), body = $('ck-wait'), wmeta = $('ck-waitmeta');
var wm = d.waiting_meta || {};
if (d.waiting && d.waiting.length) {
wb.classList.add('has'); body.className = '';
body.innerHTML = d.waiting.map(function (w) { return '<div class="wait-item"><span class="dm">◆</span><span>' + esc(w.title) + '</span></div>'; }).join('');
body.innerHTML = d.waiting.map(function (w) {
return '<div class="wait-item"><span class="dm">' + (w.urgency ? esc(w.urgency) : '◆') + '</span><span>' +
(w.id ? '<b>#' + esc(w.id) + '</b> ' : '') + esc(w.title) + '</span></div>';
}).join('');
} else {
wb.classList.remove('has'); body.className = 'wait-none';
body.textContent = '無,你不用做任何事';
body.textContent = wm.source === 'none' ? '(管線未接)' : '無,你不用做任何事';
}
if (wm.source === 'gitea_sprint') {
wmeta.innerHTML = '來源:sprint 等leo清單・維護於 ' + ckAge(wm.updated_ago_minutes) +
(wm.stale ? '<br><span class="err">⚠ 清單超過 2 天沒維護,可能過時</span>' : '');
} else if (wm.source === 'kbdb_dash_wait') {
wmeta.innerHTML = '<span class="err">⚠ ' + esc(wm.note || 'dash_wait 殘資料') + '・上次寫入 ' + ckAge(wm.updated_ago_minutes) + ',可能過時</span>';
} else if (wm.source === 'none') {
wmeta.innerHTML = '<span class="err">管線未接:Gitea sprint 清單與 dash_wait 皆無資料</span>';
} else {
wmeta.textContent = '';
}
// 今日路線:非今日寫入=誠實降級為「最後路線(N 天前)」,不假裝是今天的
var rm = d.route_meta || {};
var today = (d.tasks || []).filter(function (t) { return t.scope === 'today'; });
var week = (d.tasks || []).filter(function (t) { return t.scope === 'week'; });
$('ck-today').innerHTML = today.length ? today.map(taskLine).join('') : '<li class="todo"><span class="ic">○</span><span class="muted">今日無排定項目</span></li>';
if (rm.is_today || !today.length) {
$('ck-today').innerHTML = today.length ? today.map(taskLine).join('') : '<li class="todo"><span class="ic">○</span><span class="muted">今日無排定項目——dash_task 今天沒有寫入</span></li>';
} else {
$('ck-today').innerHTML = '<li class="todo"><span class="ic">○</span><span class="muted">今日尚無路線寫入——以下是 ' + ckAge(rm.updated_ago_minutes) + '的殘留路線</span></li>' + today.map(taskLine).join('');
}
$('ck-weekhead').style.display = week.length ? '' : 'none';
$('ck-week').innerHTML = week.map(taskLine).join('');
$('ck-stamp').textContent = '每 60 秒自動刷新・上次 ' + new Date(d.generated_at).toLocaleTimeString('zh-TW', { hour12: false });