fix(#2136): route operator-facing date fields through local clock day

clock.today() derived the calendar day by slicing a UTC ISO instant
(nowIso().split('T')[0]), so in any negative-UTC-offset zone during UTC's early
hours last_activity named a day the operator had not reached yet — ahead of
last_updated's local date, written by the same call.

- Add Clock.localToday(): host-local YYYY-MM-DD via getMonth/getDate/
  getFullYear, honoring the same GSD_NOW_MS pin as today()/nowIso().
- Route operator-facing date-only fields through localToday(): last_activity
  (state-transition ×7, state.cts), roadmap 'completed <date>' (phase.cts,
  roadmap.cts), milestone completion date (milestone.cts), todo/scaffold
  completion (commands.cts — now threaded through the realClock seam).
- Leave today() (UTC) as the source for internal/cosmetic stamps.

Closes #2136
This commit is contained in:
Tom Boucher
2026-07-12 12:10:27 -04:00
parent 51b6e3c35c
commit 71edb27fe1
7 changed files with 37 additions and 14 deletions

View File

@@ -497,7 +497,7 @@ function cmdRoadmapUpdatePlanProgress(cwd: string, phaseNum: string | null | und
const verificationPassed = readVerificationStatus(phaseDir).status === 'passed';
const isComplete = summaryCount >= planCount && verificationPassed;
const status = isComplete ? 'Complete' : summaryCount > 0 ? 'In Progress' : 'Planned';
const today = realClock.today();
const today = realClock.localToday();
if (!fs.existsSync(roadmapPath)) {
output({ updated: false, reason: 'ROADMAP.md not found', plan_count: planCount, summary_count: summaryCount }, raw, 'no roadmap');