fix(reliability): SR-187 — ban datetime.utcnow(), enforce UTC-aware datetimes#191
Merged
Conversation
This was referenced May 13, 2026
Delqhi
pushed a commit
that referenced
this pull request
May 13, 2026
Verhindert die Wurzel-Ursache des #212-Defekts: ein Status-Update darf keine PR als 'merged' deklarieren, wenn GitHub state=OPEN sagt. - scripts/check_status_truth.py: 352-Zeilen Validator, keine Deps ausser stdlib + gh CLI. Modi: --file, --issue <n>, --stdin. Optional --json fuer maschinenlesbar. Exit-Code 1 nur mit --exit-non-zero-on-violation (CI-Gate-Switch). - scripts/tests/test_check_status_truth.py: 25 Tests, alle gruen. Inkl. Regressionstest fuer die 4 Phantom-PRs aus #212 (#175/#209/#215/#216). - .github/workflows/status-truth.yml: laeuft bei Issue-/PR-Edits, bei Pushes auf PR-Body-Aenderungen, plus manueller workflow_dispatch. - AGENTS.md: neue Sektion 'Praevention' mit Doktrin 'Fix the status document OR merge the PR. Dont reverse the test.' End-to-End-Smoke gegen echtes Issue #212: 15 Verstoesse gefangen (4 BLOCKER-PRs + #185/#191-193/#210, plus 2 nicht-existente Refs #198/#199). Refs: #212, #218
3 tasks
added 2 commits
May 13, 2026 11:21
…atetimes (closes #186) Python 3.12 deprecated datetime.utcnow() (DeprecationWarning) and will remove it in 3.14. More importantly for our domain: naive datetimes are ambiguous when compared against UTC-aware DB timestamps — silent off-by-tz bugs are possible today. Changes ------- - scripts/check_banned_patterns.py: add regex \bdatetime\.utcnow\s*\( with rationale comment. Strings/comments are masked by the tokeniser so the rule does not flag its own documentation (per SR-60 contract). - scripts/tests/test_check_banned_patterns.py: 5 new tests in UtcnowBanTests covering POS (real call + chained call) and NEG (docstring / comment / unrelated 'xutcnow' name). All 14 tests pass. - survey-cli/commands/answer_survey.py:870 registry timestamp. Keeps historical 'Z' suffix for command_registry.json wire-format stability. - survey-cli/survey/captcha/fallback_chain.py:189,197 captcha-failures-YYYYMMDD.jsonl. Keeps 'Z' suffix for log-consumer compatibility (historical contract documented in module docstring). Computes 'now_utc' once instead of calling twice — small race fix. - survey-cli/survey/daemon/answer_engine.py:1005 answer_history.created_at. Now emits '+00:00' suffix; downstream DB comparisons against UTC-aware columns now match correctly. - survey-cli/survey/daemon/survey_agent_graph.py:173,235,449 LangGraph state started_at / completed_at. Three sites, not two — the issue said 'two in survey_agent_graph.py' but a third was added later at line 447 (now 449). Acceptance criteria from #186 ----------------------------- - [x] check_banned_patterns.py extended with regex for datetime.utcnow() - [x] All 6 instances refactored (issue said 5; found one more in commands/answer_survey.py:870 and a third in survey_agent_graph.py that wasn't in the issue's line-list). - [x] CI enforces: any new datetime.utcnow() fails path-guard - [x] Tests confirm UTC-aware behaviour (UtcnowBanTests). - [ ] Docs: AGENTS.md 'datetime hygiene' section — deferred to a docs- only follow-up (AGENTS.md lives outside survey-cli root and the master file was not in this PR's scope; left for owner). Verification ------------ $ python3 scripts/check_banned_patterns.py No banned patterns found. $ python3 -m unittest scripts.tests.test_check_banned_patterns Ran 14 tests in 0.007s — OK Author note (v0 agent / Delqhi handoff) --------------------------------------- SR-167 (Phase 1 Verifier) is already in flight via PR #175, so this PR takes the orthogonal SR-187 lane. No file overlap with #175. Token from the handoff prompt should be rotated regardless of merge status.
Edit tool stripped the executable bit on the previous commit. Restored so the file remains directly runnable (it's invoked from CI without 'python3' on some hooks).
2bbd2c5 to
f9de76f
Compare
6 tasks
Delqhi
pushed a commit
that referenced
this pull request
May 13, 2026
Closes the last open acceptance item from #186: - [ ] Docs: AGENTS.md 'datetime hygiene' section What was added -------------- 1. Step 3 of ARCHÄOLOGIE-TSUNAMI's BANNED-Patterns list now mentions datetime.utcnow() with a forward-ref to the new Python section. 2. New 'Python / Datetime Hygiene' subsection under EXPLICITE VERBOTE. Documents: - Why utcnow() is banned (naive dt + Py 3.12 deprecation + 3.14 removal) - Why bare datetime.now() is also banned (local-tz leak) - The required pattern: datetime.now(timezone.utc) - When to keep the legacy 'Z' suffix (external wire-format / log consumers) vs when '+00:00' is fine (internal sqlite/state JSON) - Where the rule is enforced: scripts/check_banned_patterns.py - Where the tests live: UtcnowBanTests YAML-frontmatter format preserved (content: | scalar, 2-space indent, manually verified via python3 indentation-scan). No code changes. CI's check_banned_patterns.py will pass unchanged because banned tokens inside markdown frontmatter aren't scanned.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #186 (SR-187).
What
Python 3.12 deprecated
datetime.utcnow()(DeprecationWarning) and removes it in 3.14. More urgently for stealth-runner: naive datetimes are silently mis-ordered when compared against tz-aware DB columns (sqlite stores+00:00strings, our state JSON now has both shapes side-by-side). This PR migrates every call-site and prevents new ones from landing.Scope
scripts/check_banned_patterns.py\\bdatetime\\.utcnow\\s*\\(— strings/comments masked, so rule does not flag its own doc-list (SR-60 contract)scripts/tests/test_check_banned_patterns.py.isoformat(). NEG: docstring, comment,xutcnow()lookalikesurvey-cli/commands/answer_survey.py:870Zsuffix for wire-format stabilitysurvey-cli/survey/captcha/fallback_chain.py:189,197Zsuffix; collapsed two calls into onenow_utc(small race fix)survey-cli/survey/daemon/answer_engine.py:1005+00:00so DB comparisons against tz-aware columns matchsurvey-cli/survey/daemon/survey_agent_graph.py:173,235,449Total: 7 production sites fixed (issue listed 5; I found one more in
commands/answer_survey.py:870and the thirdsurvey_agent_graphsite).Acceptance criteria (from #186)
check_banned_patterns.pyextended with regex fordatetime.utcnow()survey-cli/refactored todatetime.now(timezone.utc)datetime.utcnow()via path-guardVerification
Wire-format / behavioural notes for reviewers
fallback_chain.pyjsonl: emits identical...Zstrings as before; downstream parsers untouched.answer_survey.pycommand_registry.json: emits identical...Zstrings as before.answer_engine.pysqlitecreated_at: previously emitted naive2026-01-15T12:34:56.789, now emits tz-aware2026-01-15T12:34:56.789+00:00. This is the intended fix — any existing rows are still parseable bydatetime.fromisoformat. If you have downstream consumers comparing those strings lexicographically with a hardZsuffix, ping me and I'll add the.replace("+00:00", "Z")shim there too.survey_agent_graph.pystate: same asanswer_engine— now tz-aware. State JSON is internal so this is the natural place to drop the legacyZconvention.Coordination with #175 (SR-167)
I checked PR #175 (Phase 1 Verifier) before starting — zero file overlap with this PR. SR-187 was the recommended quick-win from the previous agent's handoff note for exactly this reason.
Author: v0 agent (per Delqhi handoff). The handoff prompt included a PAT — please rotate it after merge regardless of outcome.