fix(class-a): inject frozen clock into rotation-window tests (#1432) - #1434
Conversation
|
Merging. CI green on all three Pythons, which returns the baseline to exactly the four known Windows failures. The clock-forward proof is the part that makes this a fix rather than a reset. The suite passing today proves nothing — that is exactly the state the file was in yesterday. Demonstrating it under a process clock forced a month past the fixture instant is what shows the dependency is actually gone. The production change is real and correctly declared. Four functions gained a keyword-only ref = now or datetime.now(timezone.utc)so every existing call site keeps reading the real clock and every decision threshold is untouched. Saying plainly in the body that these are intentional API additions — rather than slipping them in under "test fix" — is the right way to handle it; a test change that quietly alters production signatures is how a fix becomes a regression nobody looks for. And the outward look found more than the two assertions. I asked you to name any production code computing a window from a non-injectable clock and stop. You found four — Worth recording for whoever meets this next: the failure did not look like a clock bug. It looked like two unrelated branches breaking the same three tests, and I read it as those branches' fault and sent an agent hunting. Two independent changes failing identically is the signal — I should have run the tests on main before asserting its CI status meant anything. |
Summary
Fixes #1432 by making the Class-A ledger window reference instant injectable end to end.
tests/test_class_a_windows.pynow passnow=NOW._dedup_exhausted(..., now=...)now uses the supplied instant for both cutoff and ledger-window selection.has_in_flight_experiment(..., now=...)now passes the supplied instant into its ledger-window selection.demand._filter_exhausted(..., now=...)threads its instant through fallback timestamp handling and ledger reads.state_access.ledger_window(..., now=...)uses the supplied instant for retention classification.The production changes are intentional API additions: both functions previously had no injectable clock, so adding keyword-only
nowparameters was required to test their existing decisions deterministically. No decision logic or thresholds were changed.Clock-forward proof
The frozen fixture suite passes with the actual process clock forced one month after the fixture instant using
freezegun:The ordinary focused run also passes:
Assertion sweep
Every Class-A assertion that calls a time-window consumer supplies
now=. The only remaining no-nowassertion in the relevant sweep is a source-inspection assertion (inspect.getsource(llm_proposer)), not a time-window call. Other assertions inspect JSON bytes, statuses, classifications, or in-memory values and are not clock-dependent.Outward production sweep
The Class-A path is now injectable through
state_access.ledger_window. Other production modules still contain independent wall-clock retention/window calculations outside this issue's scope, notably:nanobot/runtime/llm_proposer.py: demand-cooling window usesdatetime.now()internally in_recent_duplicate_failure_cooling.nanobot/runtime/state_access.py:artifacts(..., max_age_hours=...)computes its own requested instant fromdatetime.now().nanobot/runtime/action_index.py,cycle_ledger.py,reflector.py, andskill_candidate_mining.pyalso contain retention/rotation clocks.These were named only; they are not changed by this PR.
Full suite / baseline
Full Windows suite on this branch:
The remaining four failures are the known unrelated Windows baseline:
tests/test_bridge_cycle_tags.py::TestBridgeCycleTagIntegration::test_fail_open_tag_failure_never_breaks_a_green_cycletests/test_bridge_locking.py::TestAcquireBridgeLock::test_acquires_when_freetests/test_bridge_locking.py::TestAcquireBridgeLock::test_second_acquire_in_same_process_is_contendedtests/test_bridge_locking.py::TestAcquireBridgeLock::test_contended_lock_via_monkeypatched_flockgit diff --checkpasses and the worktree is clean.