fix(console): 真實性稽核修正——時區統一台北、精耕層數字、今日完成接 sprint 任務板

稽核輪(fix/console-truth-audit,總管派工+leo 三裁決):

B1 時區:console 全站日期/時間顯示統一 Asia/Taipei(新 lib/taipei-time.ts,
   server 判定與頁面 client JS 同一套、等價性單測互鎖)。原本用瀏覽器本地
   getter——換裝置就換日期;憑證頁改顯「日期+時分(台北)」:raw 1783349834
   =台北 2026-07-06 22:57,7/6 顯示是真的,缺的是時分與固定時區。

B2 總庫數字(leo 裁:14-E 遺產 deprecated 不再顯示):總庫搜尋/設定頁頭部
   統計改讀新 GET /console/kb-scale-data(精耕層 wiki 卡/三元組/已嵌入+最近
   寫入,全 live、limit=1/stats 聚合不掃庫);搜尋仍搜全庫、結果 wiki_card
   加「精耕」綠標;dashboard-data 的 entries 計數補 owner_id(原混租戶:
   459,137 vs leo 458,732)。

B3 設定頁:vectorize 文案改具體事實(config kbdb_embed+acr update,#32
   教訓)+live 狀態;新增 MCP token TTL 誠實佔位(GET /console/settings-data,
   可調功能=Arcrun#19,不假裝能遠端改)。

C 今日完成/今日路線接 sprint 任務板(leo 拍板):比照 #36 等leo清單模式,
   同一輪 Gitea fetch(90s CF cache 共用)解析「## 任務板」checkbox;「今日
   完成」只認「完成(今天台北日)」標記,[x] 無日期不冒領;快取存原始
   completed_days,跨台北午夜不誤算;板檔今天沒 commit → 頁面誠實標「今日
   任務板未更新」;dash_task 降 fallback、燈號判定不變。parser 以 live
   07a/07b 檔實測(標題抽取棄「首個粗體」——實讀發現會抽到完成註記)。

驗證:tsc exit 0;vitest 82/83(1 失敗=pre-existing executor「不存在的
零件」,stash 於 main 複驗同樣紅);新增 taipei-time 10 案例(含跨日/跨年/
client-server 等價/leo 實測 epoch)+任務板 parser 7 案例;兩頁 inline JS
渲染後抽 <script> node --check 全過(mistakes #24 手法)。
This commit is contained in:
cloud-worker
2026-07-07 12:25:31 +00:00
parent ef779e3ef6
commit 5690d61438
9 changed files with 660 additions and 138 deletions
@@ -192,3 +192,98 @@ describe('pickLatestSprintFiles / humanAge', () => {
expect(humanAge(3 * 24 * 60)).toBe('3 天前');
});
});
// ── sprint 任務板(「今日完成」活資料源,fix/console-truth-audit)────────────────────
// 樣本取自 sprint-2026-07a/07b.md 真實維護慣例:`- [x]`+行內「**完成(YYYY-MM-DD…)**」、
// `- [🔄]` 進行中、`- [!]` 卡住、縮排子彈(「 - 做法:」)非任務、一行多次完成(T6-T9 分兩天)。
import {
parseSprintTaskBoard,
buildSprintRouteModel,
cleanBoardTitle,
} from '../src/lib/console-dashboard-model';
const BOARD_MD = `# Sprint 2026-07x
## 任務板(P1 > P2 > P3worker 由上往下取)
### P1
- [ ] **T5-graphGraph 查詢 workflow 化)**:把 neighbors/traverse 改寫成 workflow。
- 做法:在 \`Leo/arcrun-rag\` 建 workflows/graph-neighbors.yaml
- 驗收:curl 原始輸出貼證據。
- [🔄] **T-cockpit 駕駛艙三件套**:①②完成(2026-07-04[總管]),③ spike 卡方向。
- [x] RECONgit proxy 不攔 Gitea06-30 已驗)
- [x] **T-loop1d 今晚任務**:實作 console v0。**完成(2026-07-02[cloud-worker]**:全綠。
- [x] T6-T907-06~08 提前):**T8/T9 完成(2026-07-04commit 1d19d46****T6/T7 完成(2026-07-05 本機總管)**。
- [!] 附帶:acr search npm 漏發——**卡住(2026-07-05[cloud-worker]**:缺 NPM_API_TOKEN。
### 候補區(本 sprint 不佔額度)
- 短網址通用化——規劃見 content-pipeline.md
## 等 leo 清單
| # | 事項 | 急迫 |
|---|------|------|
| 1 | 假裝這裡有一項 | 🟡 |
`;
describe('parseSprintTaskBoard — 任務板解析(寬鬆容錯,解析不了寧可略過)', () => {
it('只收行首 checkbox;縮排子彈/候補區敘述/等leo清單表格都不是任務', () => {
const tasks = parseSprintTaskBoard(BOARD_MD, 'sprint-2026-07x.md');
expect(tasks).not.toBeNull();
expect(tasks!.map((t) => t.status)).toEqual(['todo', 'doing', 'done', 'done', 'done', 'blocked']);
expect(tasks!.every((t) => t.sprint === 'sprint-2026-07x.md')).toBe(true);
});
it('完成日期:抓行內所有「完成(YYYY-MM-DD」;[x] 無日期=空陣列(不冒領)', () => {
const tasks = parseSprintTaskBoard(BOARD_MD)!;
expect(tasks[2].completed_days).toEqual([]); // RECON:勾了但沒帶日期 → 不歸任何一天
expect(tasks[3].completed_days).toEqual(['2026-07-02']);
expect(tasks[4].completed_days).toEqual(['2026-07-04', '2026-07-05']); // 一行兩次完成
expect(tasks[1].completed_days).toEqual(['2026-07-04']); // doing 項的部分完成標記也記著(狀態仍 doing)
});
it('標題:優先取第一個粗體;無粗體取全形冒號前', () => {
const tasks = parseSprintTaskBoard(BOARD_MD)!;
expect(tasks[0].title).toBe('T5-graphGraph 查詢 workflow 化)');
expect(tasks[2].title).toBe('RECON');
expect(cleanBoardTitle('附帶:`acr search` npm 漏發')).toBe('附帶:acr search npm 漏發'.slice(0, 80));
});
it('沒有任務板段落 → nullcaller 誠實 fallback dash_task', () => {
expect(parseSprintTaskBoard('# 只有標題\n\n## 等 leo 清單\n| 1 | x | 🔴 |')).toBeNull();
});
});
describe('buildSprintRouteModel — 「今日完成」只認今天(台北)的完成標記', () => {
it('NOW=07-0707-04/07-05 完成的不算今日;開項全列(今日路線)', () => {
const tasks = parseSprintTaskBoard(BOARD_MD)!;
const m = buildSprintRouteModel(tasks, NOW);
expect(m.today_done).toBe(0); // 板上今天沒勾任何完成 → 誠實 0
expect(m.done_today_titles).toEqual([]);
// 開項:todo + doing + blocked 3today_total 0 完成 + 3 開
expect(m.today_total).toBe(3);
expect(m.tasks.map((t) => t.status)).toEqual(['todo', 'doing', 'blocked']);
});
it('完成標記日期=今天 → 進今日完成清單;歷史完成項不出現', () => {
const tasks = parseSprintTaskBoard(BOARD_MD)!;
// 模擬 progress-guard 今天勾掉 T5-graph
tasks[0] = { ...tasks[0], status: 'done', completed_days: [taipeiDayKey(NOW)] };
const m = buildSprintRouteModel(tasks, NOW);
expect(m.today_done).toBe(1);
expect(m.done_today_titles).toEqual(['T5-graphGraph 查詢 workflow 化)']);
expect(m.today_total).toBe(1 + 2); // 1 完成 + doing/blocked 2 開項
expect(m.tasks[0].status).toBe('done'); // 今日完成排最前(✓ 給 leo 看「做了什麼」)
// 07-02/07-04/07-05 完成的三項(含無日期 RECON)都不在清單裡
expect(m.tasks.find((t) => t.title === 'RECON')).toBeUndefined();
});
it('跨台北午夜:昨天的完成不會被冒領成今天(快取安全性的模型面保證)', () => {
const tasks = parseSprintTaskBoard(BOARD_MD)!;
tasks[0] = { ...tasks[0], status: 'done', completed_days: ['2026-07-07'] };
// 台北 07-08 00:10UTC 07-07 16:10)再看:07-07 的完成已不是「今日」
const afterMidnight = Date.parse('2026-07-07T16:10:00Z');
expect(buildSprintRouteModel(tasks, afterMidnight).today_done).toBe(0);
});
});