The four evaluators date report filenames, headers and tracker rows with the UTC day
Each of the four evaluators derives one today and uses it for three things that have to agree with each other and with the user's calendar:
gemini-eval.mjs:534 const today = new Date().toISOString().split('T')[0];
ollama-eval.mjs:520 const today = new Date().toISOString().split('T')[0];
openai-eval.mjs:528 const today = new Date().toISOString().split('T')[0];
batch-evaluate-gemini.mjs:296 const today = new Date().toISOString().split('T')[0];
It becomes:
| use | shape |
|---|---|
| the report filename | {num}-{slug}-{today}.md |
| the report header | **Date:** {today} |
| the tracker TSV row | the date column merge-tracker writes |
On the UTC day, an evaluation run on a Sunday evening anywhere in the Americas writes 042-acme-2026-08-18.md, dated the 18th, into a tracker row dated the 18th — while the user's calendar, and every date the other scripts stamp, says the 17th.
The filename is the part that cannot be corrected afterwards: reports are addressed by it, jds/ captures are matched to it by report number, and data/outcomes/ copies reference it.
tests/local-today-gates.test.mjs already covers this class for the scripts that gate a decision; lib/local-today.mjs is the shared helper.
Note for whoever tests this
batch-evaluate-gemini.mjs exports processOffer and is already driven with mocks in tests/batch-evaluate.test.mjs, so it has a real behavioural seam. The other three write their report inline in a CLI body that needs an API key.
reserve-report-num.mjs drops a {num}-RESERVED.md sentinel into the same reports directory, so a test that picks "the first .md" gets the sentinel rather than the report.
PR to follow.
Source: santifer/career-ops