fix(console): 駕駛艙「今日完成」跨 UTC 日界修正——07b 板實態 3 個 parser 落差(0/10 假空)
SDD:system-dev/docs/3-specs/arcrun/search-console/tasks.md task 5.6(本次新增,Phase 5 稽核輪延伸)
live bug(2026-07-08 21:40 台北實測):sprint-2026-07b.md 台北今晨 05:50
(=UTC 07-07 21:50,cloud-worker 跨 UTC 日界寫入)勾了 CP1/T7/T5-provider,
/console/dashboard-data 卻回 today_done:0/today_total:10、done_today_titles:[]。
根因=任務板 parser 與 07b 實際維護慣例的三個落差(lib/console-dashboard-model.ts):
1. COMPLETED_DAY 要求日期緊貼「完成(」——07b 起署名在日期前
(「完成([cloud-worker] 2026-07-07,…)」)→ T7 的完成日抓不到,
[x] 無日期=整項蒸發(不冒領原則誤傷)。
2. 完成標記寫在縮排子彈上(CP1/T5-provider 的任務行只有 [x])→ 舊 parser
只掃行首 checkbox 行 → 同樣蒸發。
3. cloud-worker sandbox 時鐘是 UTC、routine 排程台北清晨(06:30=UTC 前一日
22:30)——它戳的日期 D 其實是台北 D+1 凌晨的完成,todayKey 永遠對不上。
修法(全在純函式層,route 零改動):
- DONE_STAMP 改抓「完成(…)」括號內容、日期允許前綴;縮排補充行的完成
標記歸到所屬任務(子彈本身仍不是任務)。
- [cloud-worker] 署名戳 D 展開台北候選日 {D, D+1};progress-guard 跑台北
16:30(UTC 同日)不展開——展開只會多冒領;本機/總管台北戳照舊精確,
「昨天完成的明天不冒領」不變。
- 狀態欄放寬:[🔄→只剩1042](07b 實例)認 doing,任務不再蒸發、today_total
不再少算;x 帶註記仍不冒領 done(寬鬆容錯原則不變)。
驗證:新增 5 案例(07b 逐字真實形態)修前紅(today_done:0、done_today_titles
[]=live 症狀重現)修後綠;model 測試 27/27;全套 vitest 87/88(1 失敗=
pre-existing executor「不存在的零件」,stash 於未改樹複驗同樣紅);tsc 錯誤
行數與未改樹相同(12 行,全在 validate/webhooks-named,workers-types 環境
漂移,非本次引入);另用 Gitea live 07a+07b 檔直灌新 parser:today_done:3、
done_today_titles=[T5-provider, CP1, T7]=板上真相。不部署(留 task 5.5)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -235,12 +235,13 @@ describe('parseSprintTaskBoard — 任務板解析(寬鬆容錯,解析不了
|
||||
expect(tasks!.every((t) => t.sprint === 'sprint-2026-07x.md')).toBe(true);
|
||||
});
|
||||
|
||||
it('完成日期:抓行內所有「完成(YYYY-MM-DD」;[x] 無日期=空陣列(不冒領)', () => {
|
||||
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)
|
||||
// [cloud-worker] 署名=UTC 時鐘寫手 → 戳 D 展開台北候選日 {D, D+1}(跨日界修正)
|
||||
expect(tasks[3].completed_days).toEqual(['2026-07-02', '2026-07-03']);
|
||||
expect(tasks[4].completed_days).toEqual(['2026-07-04', '2026-07-05']); // 一行兩次完成(無 UTC 署名不展開)
|
||||
expect(tasks[1].completed_days).toEqual(['2026-07-04']); // doing 項的部分完成標記也記著([總管]=台北戳不展開)
|
||||
});
|
||||
|
||||
it('標題:優先取第一個粗體;無粗體取全形冒號前', () => {
|
||||
@@ -287,3 +288,96 @@ describe('buildSprintRouteModel — 「今日完成」只認今天(台北)
|
||||
expect(buildSprintRouteModel(tasks, afterMidnight).today_done).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
// ── 2026-07-08 live bug 重現(fix/console-today-write-dayline)───────────────────
|
||||
// 實況:sprint-2026-07b.md 台北 07-08 05:50(=UTC 07-07 21:50,cloud-worker 跨 UTC
|
||||
// 日界寫入)勾了 CP1/T7/T5-provider,dashboard 卻回 today_done:0/today_total:10、
|
||||
// done_today_titles:[]。三個 parser 落差(樣本=07b 檔逐字真實形態,非編造):
|
||||
// 1. T7 的完成標記「完成([cloud-worker] 2026-07-07,…)」——07b 起署名在日期前,
|
||||
// 舊 regex 要求日期緊貼「完成(」→ 抓不到 → [x] 無日期=整項蒸發。
|
||||
// 2. CP1/T5-provider 的完成標記寫在縮排子彈(任務行只有 [x])→ 舊 parser 只掃行首
|
||||
// checkbox 行 → 同樣蒸發。
|
||||
// 3. cloud-worker 的 sandbox 時鐘是 UTC、routine 排程台北清晨——它的日期戳 07-07
|
||||
// 其實是台北 07-08 凌晨的完成,todayKey(台北 07-08)永遠對不上。
|
||||
const BOARD_07B_MD = `# Sprint 2026-07b
|
||||
|
||||
## 任務板(P1 > P2 > P3,worker 由上往下取)
|
||||
|
||||
### P1 不卡框架、可無人值守獨立跑(明早可直接開工)
|
||||
|
||||
- [🔄→只剩1042] **T5-graph(Graph 查詢 workflow 化)**:把 kbdb-graph-plugin 的 neighbors/traverse 改寫成 **arcrun workflow**。
|
||||
- 做法:在 \`Leo/arcrun-rag\` 建 \`workflows/graph-neighbors.yaml\`。
|
||||
- [x] **T5-provider(LLM 換源 Gemini recipe)**:精耕/圖萃取要能換 provider。
|
||||
- **\`acr run\` 端到端驗過([cloud-worker] 2026-07-07,\`arcrun-rag\` commit \`8b41dca\`)**:真回生成文字。**✅ 部署驗收完成(2026-07-08 總管)**:production 同步查詢回真 Gemini 生成文字。
|
||||
- [x] **CP1(content-pipeline 多出口架構驗證)**:另一條產品線。
|
||||
- **完成([cloud-worker] 2026-07-07,\`content-pipeline\` commit \`3376322\`)**:T1+T2 皆完成。零回歸。
|
||||
- 驗收:**硬約束=197 篇既有原稿跑 publish,blog 產物與擴展前逐檔一致(無回歸)**。
|
||||
|
||||
### P2 卡框架(等 Arcrun#24/#25 SDD 回來才動)
|
||||
|
||||
- [ ] **T1 Portal 拆分**(Arcrun#24,[→Arcrun]):獨立搜尋面。
|
||||
|
||||
### P3 本 repo 可做但非明早首選(有依賴或量體大)
|
||||
|
||||
- [x] **T7 golden questions 框架**:模板先行。**完成([cloud-worker] 2026-07-07,\`arcrun-rag\` commit \`3d44f0a\`)**:模板含題目表。
|
||||
- [ ] **P1-spike 地端**:miniflare 起 kbdb+cypher。
|
||||
|
||||
## 等 leo 清單(RAG 線)
|
||||
|
||||
| # | 事項 | 急迫 |
|
||||
|---|------|------|
|
||||
| R1 | pilot 客戶挑選 | 🟡 x |
|
||||
`;
|
||||
|
||||
describe('07b 跨 UTC 日界 live bug(今天勾的 [x] 不得判成非今天)', () => {
|
||||
it('parser:署名在日期前的完成標記抓得到;縮排子彈上的完成標記歸到所屬任務', () => {
|
||||
const tasks = parseSprintTaskBoard(BOARD_07B_MD, 'sprint-2026-07b.md')!;
|
||||
const byTitle = new Map(tasks.map((t) => [t.title, t]));
|
||||
// 1. T7:標記在任務行上、但署名 [cloud-worker] 在日期前
|
||||
expect(byTitle.get('T7 golden questions 框架')?.completed_days).toContain('2026-07-07');
|
||||
// 2. CP1 / T5-provider:標記在縮排子彈上
|
||||
expect(byTitle.get('CP1(content-pipeline 多出口架構驗證)')?.completed_days).toContain('2026-07-07');
|
||||
expect(byTitle.get('T5-provider(LLM 換源 Gemini recipe)')?.completed_days).toContain('2026-07-08');
|
||||
// 縮排子彈本身仍不是任務(「做法:」「驗收:」行沒被誤收)
|
||||
expect(tasks.some((t) => t.title.startsWith('做法') || t.title.startsWith('驗收'))).toBe(false);
|
||||
});
|
||||
|
||||
it('parser:狀態字元帶註記([🔄→只剩1042])不再讓任務蒸發,仍是 doing;x 帶註記不冒領 done', () => {
|
||||
const tasks = parseSprintTaskBoard(BOARD_07B_MD)!;
|
||||
expect(tasks.find((t) => t.title.startsWith('T5-graph'))?.status).toBe('doing');
|
||||
// 不誤判成完成的寬鬆容錯不變:x 後帶註記=看不懂 → 略過,不冒領 done
|
||||
const weird = parseSprintTaskBoard('## 任務板\n- [x?] 半勾的東西\n- [ ] 正常項')!;
|
||||
expect(weird.map((t) => t.title)).toEqual(['正常項']);
|
||||
});
|
||||
|
||||
it('UTC 昨天 21:50=台北今天 05:50:cloud-worker 的 UTC 日期戳算進台北今天', () => {
|
||||
const tasks = parseSprintTaskBoard(BOARD_07B_MD)!;
|
||||
// 台北 2026-07-08 21:40(=UTC 13:40,live 實測時刻):三個今天勾的 [x] 都要進今日完成
|
||||
const m = buildSprintRouteModel(tasks, Date.parse('2026-07-08T13:40:00Z'));
|
||||
expect(m.done_today_titles).toEqual(
|
||||
expect.arrayContaining([
|
||||
'T5-provider(LLM 換源 Gemini recipe)',
|
||||
'CP1(content-pipeline 多出口架構驗證)',
|
||||
'T7 golden questions 框架',
|
||||
]),
|
||||
);
|
||||
expect(m.today_done).toBe(3);
|
||||
expect(m.today_total).toBe(3 + 3); // 3 完成 + 開項(T5-graph doing、T1 todo、P1-spike todo)
|
||||
// 台北 07-08 05:55(=UTC 07-07 21:55,剛寫完當下)看也一樣算今天
|
||||
expect(buildSprintRouteModel(tasks, Date.parse('2026-07-07T21:55:00Z')).today_done).toBe(3);
|
||||
});
|
||||
|
||||
it('不冒領邊界:台北 07-09 看,07-07/07-08 的完成全部不再是今日', () => {
|
||||
const tasks = parseSprintTaskBoard(BOARD_07B_MD)!;
|
||||
const m = buildSprintRouteModel(tasks, Date.parse('2026-07-09T04:00:00Z')); // 台北 07-09 12:00
|
||||
expect(m.today_done).toBe(0);
|
||||
expect(m.done_today_titles).toEqual([]);
|
||||
});
|
||||
|
||||
it('本機/總管的台北日期戳不受 UTC 展開影響(昨天完成的明天不冒領)', () => {
|
||||
const md = '## 任務板\n- [x] **T-local**:本機做的。**完成(2026-07-07,[總管]派本機 subagent)**:全綠。';
|
||||
const tasks = parseSprintTaskBoard(md)!;
|
||||
expect(tasks[0].completed_days).toEqual(['2026-07-07']); // 不展開 +1 日
|
||||
expect(buildSprintRouteModel(tasks, Date.parse('2026-07-08T13:40:00Z')).today_done).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user