fix(triage_rollout_enact): patch correct module for time-travel

- _fake_now now patches triage_with_llm.dt (the alias actually used by
  the issue grace check in _seconds_since_latest_marker_comment) instead
  of agent_shin_shared.dt, which was a no-op for issues.
- Drop the dead _FakeDt inner class that was never installed.
- Drop unused _DEFAULT_FUTURE_SECONDS constant.
- Update tests to assert against triage_with_llm.dt.

Co-authored-by: Yassin Kortam <yassin@berri.ai>
This commit is contained in:
Cursor Agent 2026-05-26 03:50:38 +00:00
parent 06145a7d71
commit ead3f1466b
No known key found for this signature in database
2 changed files with 21 additions and 30 deletions

View file

@ -31,7 +31,7 @@ preview shows those would-close decisions before they actually fire.
Time-travel is implemented in exactly one place: a ``current_time`` variable
is computed at the top of ``run()`` and threaded through ``review_gate(now=)``
for PRs. For issues (whose grace check goes through
``seconds_since_latest_marker_comment``), we patch ``agent_shin_shared``'s
``seconds_since_latest_marker_comment``), we patch ``triage_with_llm``'s
``dt.datetime.now`` under a context manager for the duration of each
``triage()`` call one small surface, easy to audit.
@ -66,7 +66,6 @@ _SCRIPTS_DIR = Path(__file__).resolve().parent
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
import agent_shin_shared # noqa: E402
import triage_with_llm # noqa: E402
from _agent_shin_actions import ( # noqa: E402
maybe_add_label,
@ -94,31 +93,23 @@ from triage_with_llm import ( # noqa: E402
# real run, so 24h+1s gives a "what fires tomorrow" preview without any edge
# cases at the boundary itself.
DEFAULT_SIMULATE_HOURS = 24
_DEFAULT_FUTURE_SECONDS = DEFAULT_SIMULATE_HOURS * 3600 + 1
@contextlib.contextmanager
def _fake_now(when: dt.datetime) -> Iterator[None]:
"""Patch ``dt.datetime.now`` in agent_shin_shared so issue triage's grace
"""Patch ``dt.datetime.now`` in triage_with_llm so issue triage's grace
check resolves against ``when`` rather than wall-clock time.
Patches ``agent_shin_shared.dt`` (the module's local alias) rather than
Patches ``triage_with_llm.dt`` (the module's local alias) rather than
the global ``datetime`` so we don't leak the override into unrelated code.
The patch is scoped to the ``with`` block once it exits, the original
``dt`` module is restored, so a per-item call to ``triage()`` is the only
code that ever sees the fake clock.
The issue grace check reads the wall clock inside
``_seconds_since_latest_marker_comment`` via this module's ``dt`` alias,
so this is the only surface that needs to be frozen. The patch is scoped
to the ``with`` block once it exits, the original ``dt`` module is
restored, so a per-item call to ``triage()`` is the only code that ever
sees the fake clock.
"""
real_dt = agent_shin_shared.dt
class _FakeDt:
"""Drop-in replacement for ``datetime`` with a frozen ``now``."""
timezone = real_dt.timezone
datetime = real_dt.datetime
@staticmethod
def datetime_now(tz: dt.tzinfo | None = None) -> dt.datetime:
return when if tz is None else when.astimezone(tz)
real_dt = triage_with_llm.dt
# We only need to override `dt.datetime.now`. Easiest path: install a
# tiny shim that proxies to the real `datetime` module for everything
@ -135,11 +126,11 @@ def _fake_now(when: dt.datetime) -> Iterator[None]:
def __getattr__(self, name: str) -> Any: # pragma: no cover - shim
return getattr(real_dt, name)
agent_shin_shared.dt = _DtShim()
triage_with_llm.dt = _DtShim()
try:
yield
finally:
agent_shin_shared.dt = real_dt
triage_with_llm.dt = real_dt
def _list_open_numbers(repo: str, kind: str) -> list[int]:

View file

@ -3,7 +3,7 @@
Covers:
* ``_fake_now`` the time-travel context manager. Patches
``agent_shin_shared.dt`` so ``datetime.now()`` returns a pinned value,
``triage_with_llm.dt`` so ``datetime.now()`` returns a pinned value,
and restores the original module on exit (even on exception).
* ``_apply_pr_result`` / ``_apply_issue_result`` dispatch tables that
turn a review_gate/triage verdict into one or two ``maybe_*`` calls.
@ -65,25 +65,25 @@ def enact_module(triage_module, actions_module, shared_module):
class TestFakeNow:
def test_patches_dt_now_inside_context(self, enact_module, shared_module):
def test_patches_dt_now_inside_context(self, enact_module, triage_module):
when = dt.datetime(2099, 1, 1, tzinfo=dt.timezone.utc)
with enact_module._fake_now(when):
assert shared_module.dt.datetime.now(dt.timezone.utc) == when
assert triage_module.dt.datetime.now(dt.timezone.utc) == when
def test_restores_dt_on_exit(self, enact_module, shared_module):
original = shared_module.dt
def test_restores_dt_on_exit(self, enact_module, triage_module):
original = triage_module.dt
with enact_module._fake_now(dt.datetime(2099, 1, 1, tzinfo=dt.timezone.utc)):
pass
assert shared_module.dt is original
assert triage_module.dt is original
def test_restores_dt_on_exception(self, enact_module, shared_module):
original = shared_module.dt
def test_restores_dt_on_exception(self, enact_module, triage_module):
original = triage_module.dt
with pytest.raises(RuntimeError):
with enact_module._fake_now(
dt.datetime(2099, 1, 1, tzinfo=dt.timezone.utc)
):
raise RuntimeError("boom")
assert shared_module.dt is original
assert triage_module.dt is original
# ---------------------------------------------------------------------------