Have you ever shipped a green local suite, then watched CI fail on a box that never loaded your shell profile? I spent forty-eight hours treating that mismatch like a flake, and the flake kept a boring secret the whole time. My laptop exported TZ from a forgotten line in ~/.zshrc, so every timestamp assertion looked honest on my machine. The assistant-generated patch never left that room, which is why the remote clock told a different story.
The symptom that wasted the first day
The failing test stamped report filenames with the local calendar day, then compared that string against an expected UTC date. Locally, pytest tests/test_report_name.py passed on every retry I threw at it during office hours. On CI, the same assertion died after 17:00 local time, which I misread as an unlucky runner instead of a clock boundary. I asked the usual questions out loud: was pytest caching an old module, was Docker's clock skewed, or was the wheel stale?
What I tried before I suspected the shell
None of those hunches survived a second look, but they still ate the first evening and most of the next morning.
- Deleted
.pytest_cacheand the localvenv, then reinstalled fromrequirements.lock. - Forced
PYTHONHASHSEED=0even though the failure was a date string, not a shuffled set. - Printed
datetime.datetime.now()inside the test, which matched my wall clock and made me overconfident. - Blamed the coding assistant for nondeterministic codegen because the patch had arrived from a chat window.
That last accusation was unfair, and I should have seen it sooner than I did. The generated code was deterministic on every rerun I captured. My interactive environment was not deterministic, because login files kept coaching the process. Have you ever treated a green bar as evidence when the green bar only exists inside your own prompt?
A minimal reproduction you can run
I reduced the exporter to one function that should be timezone-explicit and currently is not. Save this as report_name.py if you want to follow the same trail, and treat it as a labeled example rather than production code.
from datetime import datetime
def report_filename(prefix: str = "daily") -> str:
# Labeled example: this is the buggy shape I actually shipped.
stamp = datetime.now().strftime("%Y-%m-%d")
return f"{prefix}-{stamp}.csv"
The test I trusted mixed an aware UTC expectation with a naive local stamp, which Python allows and still gets wrong.
from datetime import datetime, timezone
from report_name import report_filename
def test_report_filename_uses_utc_day():
expected = datetime.now(timezone.utc).strftime("%Y-%m-%d")
assert report_filename() == f"daily-{expected}.csv"
On my laptop the assertion passed while local time and UTC still shared a calendar day. After late afternoon, UTC had already rolled the date, and CI failed while I was making dinner. Have you checked whether your so-called UTC test actually constructs datetime.now(timezone.utc), or only agrees with your office hours?
The env dump that ended the guessing
I finally printed the process environment instead of arguing with the clock, using a one-liner I should have run on hour one.
python3 -c "import os,time,datetime as d; print('TZ=', os.environ.get('TZ')); print('tzname=', time.tzname); print('local=', d.datetime.now()); print('naive_utc=', d.datetime.utcnow()); print('aware_utc=', d.datetime.now(d.timezone.utc))"
TZ was set to a Pacific zone I did not remember exporting. time.tzname confirmed it, and datetime.utcnow() was already a yellow flag because it returns a naive datetime. Mixing that naive value with an aware UTC expectation is legal Python, and it is still a lying comparison. As of current CPython docs, utcnow() is the wrong tool; datetime.now(timezone.utc) is the explicit one.
Hour 20: the clean box that did not have my dotfiles
I needed a second machine that would not source ~/.zshrc before Python started. A container on my laptop was not enough, because I still bind-mounted $PWD and leaked env through compose files. The useful move was a remote shell that started from a login environment I did not author, with a cwd that had never seen my aliases.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
I used MonkeyCode's free model access to draft an isolation script, then ran that script on the free server option so my dotfiles could not coach the process. I am not going to invent model names, quotas, hardware, or timings I cannot verify from here. The value was boring in the best way: a second cwd, a second env, and a model that could rewrite the script after I pasted the remote dump.
Isolation script I actually kept
#!/usr/bin/env bash
set -euo pipefail
echo "== identity =="
uname -a
id
pwd
echo "== clock =="
date -u
date
python3 - <<'PY'
import os, time, datetime as d
print("TZ=", os.environ.get("TZ"))
print("tzname=", time.tzname)
print("local=", d.datetime.now())
print("utc_aware=", d.datetime.now(d.timezone.utc))
PY
echo "== env keys that usually lie =="
env | awk -F= '
$1 ~ /^(TZ|LANG|LC_|PYTHON|PYTEST|VIRTUAL_ENV|PATH)$/ {print}
' | sort
On the remote box, TZ was unset, tzname was UTC, and the same pytest file failed immediately. Local green versus remote red left no flake left to argue with. Would I have seen that if I had kept asking the model to make the test more resilient? Probably not, because resilient often meant assert less.
A cheap preview still exists on the laptop if you do not want to open a remote shell yet.
env -i HOME="$HOME" PATH="/usr/bin:/bin" TZ=UTC python3 -m pytest tests/test_report_name.py -q
If that command disagrees with your normal interactive shell, you do not have a flake. You have a profile, and the profile is winning.
What the assistant kept doing wrong
I asked for a fix three times before I pasted the remote env dump into the transcript. Each reply looked brilliant on my laptop and collapsed on the clean box, which should have been the review checklist by itself.
- Pin the expected date to
datetime.now().date()in the test, which hides the production bug. - Sleep and retry around midnight, which is how flakes get a lease on life.
- Hard-code
America/Los_Angelesin the exporter, which just relocates the landmine to the next hire.
None of those patches survived the clean shell, and that is the part I would repeat as a habit. The model was not evil; it optimized for the transcript I gave it, and that transcript was a local success. If your prompt never includes env | sort from a machine you do not own, the model will keep sanding down the test until the red bar goes quiet.
The patch I would actually keep
from datetime import datetime, timezone
def report_filename(prefix: str = "daily", now=None) -> str:
clock = now or datetime.now(timezone.utc)
if clock.tzinfo is None:
raise ValueError("report_filename requires an aware datetime")
stamp = clock.astimezone(timezone.utc).strftime("%Y-%m-%d")
return f"{prefix}-{stamp}.csv"
The test injects noon UTC and 23:30 UTC so office hours cannot rescue you, and it never calls datetime.now() for the expected value.
from datetime import datetime, timezone
from report_name import report_filename
def test_filename_is_utc_even_late_in_local_day():
noon = datetime(2026, 9, 16, 12, 0, tzinfo=timezone.utc)
late = datetime(2026, 9, 16, 23, 30, tzinfo=timezone.utc)
assert report_filename(now=noon) == "daily-2026-09-16.csv"
assert report_filename(now=late) == "daily-2026-09-16.csv"
def test_naive_datetime_is_rejected():
naive = datetime(2026, 9, 16, 12, 0)
try:
report_filename(now=naive)
except ValueError:
return
raise AssertionError("naive datetime should not be accepted")
Notice the assertions no longer consult the wall clock at all. That is the whole lesson, dressed up as two tests and one guard. If a future assistant reintroduces datetime.now() in the expected value, the late-day case should fail on any machine, not only on CI after dinner.
Decision table I wish I had on hour one
| Signal | Trust local laptop? | Need a clean remote shell? | Ask the model again? |
|---|---|---|---|
| Failure mentions dates, logs, or filenames | No | Yes | Only after you paste date -u and TZ
|
Test uses datetime.now() or utcnow()
|
No | Yes | Ask it to inject a clock, not to retry |
| Passes only inside your interactive shell | No | Yes | Do not paste the local green output alone |
Fails identically under env -i
|
Maybe the code | Optional | Yes, now the transcript is honest |
| Depends on GPU, licensed data, or secrets | Stop | Not this workflow | Not this workflow |
I would tape that table above the chat window next time, because the chat window is extremely good at agreeing with whatever machine you are sitting at. The table is not clever. It is a speed bump that asks whether the evidence was collected somewhere your dotfiles cannot reach.
What broke when I tried to be clever
The remote session still surprised me, which is why these notes are not a victory lap. python pointed at a distro interpreter that lacked pytest, so I almost declared the server broken instead of creating a venv. date without -u printed a locale string I misread as UTC because the offset was +0000. I also uploaded the test file and forgot report_name.py, which produced ModuleNotFoundError and a fresh twenty minutes of noise that had nothing to do with timezones.
Sequence I would repeat
I would still repeat the boring sequence, because every shortcut I took sent me back to the laptop's green bar.
- Dump identity, clock, and a small allow-list of env keys on both machines before opening a chat.
- Run the same pytest invocation under
env -ilocally, withTZ=UTC, before asking for a patch. - Paste the two dumps into the model, not the stack trace alone, so the transcript contains the lie.
- Require an injected clock in any patch that touches timestamps, filenames, or log lines.
- Re-run the patch on the clean box before I trust the local green bar, even if the diff looks tiny.
If you want a second machine that does not carry your .zshrc, MonkeyCode's free server option is a straightforward place to park that isolation script. That is the only product nudge I need here, and the workflow still stands if you use any other clean shell you already own.
Limitations, and who should skip this
This workflow will not help you if the bug lives inside a vendor API, a hardware device, or a dataset you cannot copy onto a second box. A free remote shell also will not reproduce GPU kernels, macOS-only filesystem quirks, or corporate SSO cookies, and you should not upload secrets just to win an argument with CI. I also would not use an assistant to stabilize tests by weakening assertions, because that is how timezone bugs become quarterly incidents instead of one loud failure.
The field notes are a method, not a guarantee, and they assume you can run the same files in two environments. Clocks, locales, and shell profiles still lie, and they lie faster if you only measure them at your desk. Would I start with the clean dump next time, before I accuse pytest of flaking on a date string? Yes, and that is the only habit from these forty-eight hours I plan to keep.
Top comments (0)