Skip to content

fix(projects): pin the schedule bar spec clock so clamping stays calendar-independent - #526

Closed
waterbro-8 wants to merge 1 commit into
mainfrom
fix/525-pin-schedule-bar-spec-clock
Closed

waterbro-8 wants to merge 1 commit into
mainfrom
fix/525-pin-schedule-bar-spec-clock

Conversation

@waterbro-8

@waterbro-8 waterbro-8 commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #525

Tracking record

Summary

One renderer spec — renderer/test/project-board.test.tsx, case "clamps schedule bar calculations for inverted dates, window-edge spans, and partially invalid dates" — fails on both Node 24 legs for any run whose local date is on or after 2026-09-28, and had two more staged expiry dates after that (2026-10-05, 2027-01-04). It is not a content regression: the spec blob (fb329909e318b2c31fd256a1ec19f359285d59a4) and the component blob (c23c08f1c7339c5fc0cb62e54300454a833e8e36) are identical on main and on every PR head that ran red.

The schedule window starts at the Monday of the current week (ProjectBoard.tsx:95 → weekStart(localToday())), while the spec's fixtures are hard-coded dates. When the week rolled to 2026-09-28, the partially-invalid-date fixture (dueDate: "2026-09-22") fell outside the 28-day window, inWindow became false (ProjectBoard.tsx:745-746), and the bar — the very element the spec queries by accessible name at :605 — was never rendered (ProjectBoard.tsx:788).

Fix: pin the clock inside that one spec, using the pattern the suite already uses for date-sensitive assertions (vi.useFakeTimers() + vi.setSystemTime(), see approval-queue.test.tsx:381-382 and turn-progress-interaction.test.tsx:68-69). Two deliberate details:

  • toFake: ["Date"] — only Date is faked, so timers, promises and RTL's waitFor keep working; the neighbouring tests in this file rely on real timers.
  • try { … } finally { vi.useRealTimers(); } — a failing assertion must not leak fake time into the rest of the file.

Pinned instant 2026-09-24T12:00:00.000Z puts windowStart at 2026-09-21, which keeps all three fixtures in-window, so the two later expiry dates are covered too. Every literal assertion in the spec — including the label at :613 — is untouched.

Intentionally out of scope:

  • No component change. Skipping bars that lie outside the visible window is intended behaviour; relaxing inWindow would draw bars for tasks that are not in view.
  • No other spec touched. The neighbouring specs (:342, :530) assert label text, which renders regardless of inWindow, so they are window-independent today.
  • No CHANGELOG entry: this is a test-only fix with no user-visible effect.

Acceptance criteria

# Acceptance criterion Status Implementation / evidence
AC-001 renderer/test/project-board.test.tsx is green when run on local date 2026-09-28 PASS Clock pinned in the spec; V1 (red before) → V2 (green after)
AC-002 The spec's result no longer depends on the day it is run — green on 2026-09-28, 2026-10-05 and 2027-01-04 (the three computed expiry dates) PASS (by construction) Pinned windowStart = 2026-09-21 independent of the run date; all three fixtures verified in-window for that window (V4). Future-dated runs cannot be executed today — see Reviewer notes
AC-003 Both Node 24 legs are green again PASS on this branch V7: ubuntu job 108826151965 green in 3m36s, macos job 108826151761 green in 5m22s (run 36390868774) — the same two legs were red on PR #524 on 2026-09-28 with the identical assertion. main itself goes green when this merges

Validation ledger

# Command or check Expected Actual Evidence
V1 npm run test:renderer -- renderer/test/project-board.test.tsx before the fix, local date 2026-09-28 FAIL at :605 FAIL — Tests 1 failed | 19 passed (20), TestingLibraryElementError: Unable to find an accessible element with the role "button" and name /^Partially invalid date task: / local run log
V2 same command after the fix, same machine and date 20 passed PASS — Tests 20 passed (20) local run log
V3 counter-check: remove the Math.max(0, …) clamp at ProjectBoard.tsx:743, rerun V2 must fail — pinning must not weaken the assertions FAIL (as required) — expected '-2242.8571428571427%' to be '0%'; component then restored byte-exact and V2 re-confirmed green local mutation + restore
V4 window arithmetic under the pinned instant windowStart = 2026-09-21 and all three fixtures in-window PASS — partial (2026-09-22…2026-09-22), inverted (2026-09-22…2026-09-28), span (2025-01-01…2027-01-01) all satisfy start < windowStart + 28 && end >= windowStart computed with the component's own dayNumber/weekStart formulas
V5 npm run typecheck:renderer exit 0 PASS — exit 0, no diagnostics tsc --noEmit -p apps/desktop
V6 npm run test:renderer (full suite, 72 files) no new failures attributable to this change PASS — Tests 45 failed | 753 passed (798); every failure is a timeout (63 × 5000 ms, 2 × 10000 ms, 4 × 15000 ms) in App.test.tsx (41), reports-center.test.tsx (3), budget-dashboard.test.tsx (1) — the known local-load class; project-board.test.tsx is green; the same suite on the same machine before this change was 48 failed / 5 files full-suite logs before and after
V7 CI Node 24 / ubuntu-latest and Node 24 / macos-14 on this branch green PASS — ubuntu 108826151965 (3m36s), macos 108826151761 (5m22s), run 36390868774; both were failure on PR #524 (jobs 108813281207 / 108813281213) with the same assertion CI run 36390868774

Tests and coverage

  • Tests added or changed: one existing spec edited, no new spec. renderer/test/project-board.test.tsx:561-620 now pins the clock and restores real timers in finally; assertions unchanged.
  • Coverage before / after, when measured: not measured — the change adds no branch to production code; the production file ProjectBoard.tsx is not modified.
  • Intentionally uncovered behavior and reason: the component's window gating is not newly covered, because its behaviour is unchanged and already exercised by this spec's three clamping cases.

Change classification

  • User-visible behavior
  • Internal refactor or maintenance
  • Documentation only
  • Build, CI, dependency, or repository configuration
  • Breaking change
  • Security-sensitive change

Classified as CI: the shipped app is byte-identical; the sole effect is that the Node 24 legs stop failing on the calendar.

Risk and rollback

  • Risk level and affected components: low. One test file. No production code, no dependency, no build output affected.
  • Compatibility, migration, privacy, performance, or operational impact: none. Fake timers are scoped to a single it() and restored in finally.
  • Rollback procedure: revert the single commit (git revert) or delete the branch — main returns to its current (red-on-Node 24) state; no artifact or persisted data is involved.

Breaking or security notes

None.

Author checklist

  • A maintainer confirmed that the linked issue or tracking record was ready before implementation began, or the automation was pre-authorized under GOVERNANCE.md.
    → Not checked, deliberately. fix(ci): project-board schedule bar spec is calendar-anchored and fails both Node 24 legs from 2026-09-28 #525 was filed by the author of this PR and is still status: reported with no maintainer confirmation. It is filed as a bug report (S2 / E2) and this PR is the remediation; a maintainer decision on severity and on whether the pin-clock approach is preferred over dynamic fixtures is requested in Reviewer notes.
  • This branch was created from an up-to-date default branch and contains no unrelated changes.
    → base b3704c45 == main at publish time; the changeset is exactly 1 file, +60 −51, verified by recursive tree diff (0 added, 0 removed, 1 modified).
  • I ran the repository's applicable tests, lint, type checks, builds, coverage, and security checks.
    → Applicable scope for a test-only change: the target spec (V2), the full renderer suite (V6) and typecheck:renderer (V5). The rest of npm run check (build, scripts, server, desktop-main, security:check) was not run locally — this change touches one spec file and cannot affect those legs; they are covered by CI.
  • I added a regression test for a bug fix, or explained why one is impractical.
    → Not applicable as written: the defect is the existing spec's calendar dependence, so the regression protection is delivered by making that spec deterministic (V1 red → V2 green, with V3 proving the assertions still bite). No new spec was added.
  • I updated relevant documentation and changelog files.
    → Nothing user-visible or documentational changed. CHANGELOG is reserved for user-facing entries in this repo; a test-only fix is deliberately not logged. Say the word and I'll add a ### Fixed line.
  • I reviewed the diff for secrets, personal data, generated artifacts, and dependency risk.
    → The diff is 60 added / 51 deleted lines in one test file; no new dependencies, no artifacts, no data.
  • The PR is ready for CI and review by someone other than the sole author.
  • All reported results are reproducible.
    → Every ledger row is a command with its output; V1/V2/V3/V6 were run on 2026-09-28 (UTC+8) on a tree whose only delta from main is unrelated to this change.

Reviewer notes

  1. The one judgement call worth a second opinion: pinning the clock (vi.setSystemTime) vs. deriving fixture dates from localToday(). I chose pinning because it keeps every literal assertion — including the due-date label at :613 — and matches the two specs already using this pattern. Deriving dates would make the label assertion dynamic and enlarge the diff. Happy to switch if the maintainers prefer fixtures that track the real calendar.
  2. Please reproduce V1 independently — it needs a machine whose local date is ≥ 2026-09-28, or any tree with this commit reverted. That single red→green pair is the whole argument.
  3. V3 matters more than V2. "The spec is green" alone would not prove the pinning preserved the assertions; the mutation (removing the clamp at ProjectBoard.tsx:743) failing with -2242.857…% vs 0% is the evidence that it did.
  4. Do not "fix" this in the component. inWindow gating is intended: a task outside the visible 28-day window should not draw a bar.
  5. AC-002 is verified by construction, not by running on future dates (which is impossible today). The claim reduces to: with windowStart fixed at 2026-09-21, all three fixtures satisfy inWindow — computed with the component's own formulas (V4).
  6. Unblocks more than this PR. Every branch that runs CI from 2026-09-28 onward hits this failure, so merging this clears the queue rather than just one PR.

…ndar-independent

The schedule window starts at the Monday of the current week (ProjectBoard.tsx:95), so
the hard-coded fixtures in this spec leave the window as the calendar moves: 2026-09-28
for the partially invalid date task, 2026-10-05 for the inverted task, 2027-01-04 for
the window-spanning task. Pin the clock inside the spec with the pattern already used
in approval-queue.test.tsx and turn-progress-interaction.test.tsx, and restore real
timers in a finally block so a failing assertion cannot leak fake time into the file.

Closes #525

@waterbro-8 waterbro-8 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复核结论(head 8a4b983):改动属实、无阻塞意见,但要合还缺第二位 reviewer 的 approve。

1. 根因链路我独立核了一遍,不是只看 PR 描述。 PR #523 的 Node 24 / ubuntu-latest(job 108753296529)日志:Tests 1 failed | 876 passed (877)、Test Files 1 failed | 70 passed (71),唯一失败点就是 renderer/test/project-board.test.tsx:605 → Unable to find an accessible element with the role "button" and name /^Partially invalid date task: /。整个 renderer 套件只有这一个用例红,其余 70 个文件全绿,与 #525 描述的日历锚定问题同源。

2. 本 PR head 的 CI 已绿。 run 36390868774:Node 24 / ubuntu-latest 108826151965(3m36s)、Node 24 / macos-14 108826151761(5m22s)双绿;Layout parity、两条 staging smoke、两条 installer、CodeQL、Dependency review、OpenSSF 全 pass。

3. 改动面: 单文件 apps/desktop/renderer/test/project-board.test.tsx,+60 / −51,生产代码零改动(ProjectBoard.tsx 未被触碰),所有断言字面值保持原样。

4. 固定时钟的写法认可。 toFake: ["Date"] 只伪造 Date,同文件其它用例依赖真实 timer 的 RTL waitFor 不受影响;try { … } finally { vi.useRealTimers(); } 保证断言失败后假时钟不外泄到后面的用例。与 approval-queue.test.tsx:381、turn-progress-interaction.test.tsx:68 的既有写法一致。

5. 同意不在组件里改。 inWindow 过滤是预期行为,放开它会给窗口外的任务画 bar。

两点留档:

  • V3 变异测试(移除 ProjectBoard.tsx:743 的 clamp 后仍失败)我没有在本机复跑,此处按 PR 描述记录;它是“固定时钟没有削弱断言”的唯一强证据,建议 reviewer 自行复现一次。
  • V6 全量套件仍有 App.test.tsx / reports-center.test.tsx / budget-dashboard.test.tsx 的 45 条 timeout,属已知本地负载类失败,与本改动无关——本 PR 合入后 main 的 Node 24 腿才谈得上稳定。

本 PR 由协同 waterbro-8 账号开出,作者本人无法自转 approve,仍需第二位 reviewer(HAHR / Bindy)点 approve 才能合。

@PeterGuy326

Copy link
Copy Markdown
Contributor

Note for the record: with #523 merged, main is green on both Node 24 legs and the affected spec now derives its fixtures relatively (isoFromToday(...)), so issue #525 was closed as completed. This PR fixes the same symptom with a different mechanism (pinning the spec clock) and is therefore redundant as-is. It also currently conflicts with main (merge-tree reports a content conflict in apps/desktop/renderer/test/project-board.test.tsx).

Suggested disposition: either close this PR as superseded, or — if you want the clock-pinning pattern retained as defense in depth — rebase it onto current main and reduce it to just the vi.useFakeTimers({ toFake: ["Date"] }) addition on top of the relative fixtures. Happy to merge the rebased form once CI is green. No action is required from this PR to keep main green.

@PeterGuy326 PeterGuy326 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superseded by main: PR #523 already landed the calendar-independent fix for this same flake, using relative isoFromToday fixtures instead of a pinned clock, so the clamping math no longer depends on the wall-clock week window. Merging this would only re-introduce a second mechanism for the same problem.

Closing as superseded — no code change is lost. Thanks for the quick fix; if you spot the flake again, the isoFromToday contract in project-board.test.tsx is the place to extend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(ci): project-board schedule bar spec is calendar-anchored and fails both Node 24 legs from 2026-09-28

2 participants