feat(resolvers): binky_rhythm_status query in state-hub resolver
Due-items for the three Binky operating-rhythm definitions (BINKY-WP-0004-T02). Dueness derives from hub progress events recorded by the executing session (binky_daily_brief / binky_mail_intake / binky_weekly_review, detail.repo scoped); weekly_review carries milestone_moved from event_type=milestone events in the last 7 days (RISK-005 signal). Definitions' resolver comments updated; definitions stay enabled:false until cutover (BINKY-WP-0004-T06). 7 new tests; resolver test file 31/31 green. Pre-existing failures in test_railiance_ops_inventory_wiring/test_schedule_health are untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a2c53508ad
commit
b1eb5e6a55
5 changed files with 282 additions and 11 deletions
|
|
@ -841,3 +841,160 @@ def test_phase5_stabilization_check_passes(monkeypatch) -> None:
|
|||
assert result["checks"]["totals"]["pass"] is True
|
||||
assert result["checks"]["totals"]["workplans"] == 640
|
||||
assert result["checks"]["totals"]["workstreams"] == 640
|
||||
|
||||
|
||||
def _binky_resolver_env(monkeypatch, progress_by_type):
|
||||
def fake_get(url: str, **kwargs) -> DummyResponse:
|
||||
params = kwargs.get("params") or {}
|
||||
if url.endswith("/progress/"):
|
||||
return DummyResponse(progress_by_type.get(params.get("event_type"), []))
|
||||
return DummyResponse({})
|
||||
|
||||
from datetime import datetime, timezone
|
||||
|
||||
fixed_now = datetime(2026, 7, 17, 10, 0, tzinfo=timezone.utc)
|
||||
monkeypatch.setenv("STATE_HUB_URL", "http://state-hub.test")
|
||||
monkeypatch.setattr(httpx, "get", fake_get)
|
||||
monkeypatch.setattr(
|
||||
"activity_core.context_resolvers.state_hub._utc_now",
|
||||
lambda: fixed_now,
|
||||
)
|
||||
|
||||
|
||||
def test_binky_rhythm_daily_brief_due_when_no_events(monkeypatch) -> None:
|
||||
_binky_resolver_env(monkeypatch, {"binky_daily_brief": []})
|
||||
|
||||
result = StateHubContextResolver().resolve(
|
||||
"binky_rhythm_status", None, {"repo": "binky-control"}
|
||||
)
|
||||
|
||||
assert result["repo"] == "binky-control"
|
||||
assert result["items"] == [
|
||||
{
|
||||
"kind": "daily_brief",
|
||||
"due": True,
|
||||
"date": "2026-07-17",
|
||||
"last_run_at": None,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_binky_rhythm_daily_brief_not_due_after_todays_run(monkeypatch) -> None:
|
||||
_binky_resolver_env(
|
||||
monkeypatch,
|
||||
{
|
||||
"binky_daily_brief": [
|
||||
{
|
||||
"event_type": "binky_daily_brief",
|
||||
"created_at": "2026-07-17T06:30:00+00:00",
|
||||
"detail": {"repo": "binky-control"},
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
result = StateHubContextResolver().resolve(
|
||||
"binky_rhythm_status", None, {"repo": "binky-control"}
|
||||
)
|
||||
|
||||
item = result["items"][0]
|
||||
assert item["due"] is False
|
||||
assert item["last_run_at"] == "2026-07-17T06:30:00+00:00"
|
||||
|
||||
|
||||
def test_binky_rhythm_daily_brief_ignores_other_repo_events(monkeypatch) -> None:
|
||||
_binky_resolver_env(
|
||||
monkeypatch,
|
||||
{
|
||||
"binky_daily_brief": [
|
||||
{
|
||||
"event_type": "binky_daily_brief",
|
||||
"created_at": "2026-07-17T06:30:00+00:00",
|
||||
"detail": {"repo": "some-other-repo"},
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
result = StateHubContextResolver().resolve(
|
||||
"binky_rhythm_status", None, {"repo": "binky-control"}
|
||||
)
|
||||
|
||||
assert result["items"][0]["due"] is True
|
||||
|
||||
|
||||
def test_binky_rhythm_mail_intake_window(monkeypatch) -> None:
|
||||
recent = {
|
||||
"event_type": "binky_mail_intake",
|
||||
"created_at": "2026-07-14T09:40:00+00:00",
|
||||
"detail": {"repo": "binky-control"},
|
||||
}
|
||||
_binky_resolver_env(monkeypatch, {"binky_mail_intake": [recent]})
|
||||
result = StateHubContextResolver().resolve(
|
||||
"binky_rhythm_status", None, {"repo": "binky-control", "kind": "mail_intake"}
|
||||
)
|
||||
assert result["items"][0]["due"] is False
|
||||
|
||||
stale = dict(recent, created_at="2026-07-09T09:40:00+00:00")
|
||||
_binky_resolver_env(monkeypatch, {"binky_mail_intake": [stale]})
|
||||
result = StateHubContextResolver().resolve(
|
||||
"binky_rhythm_status", None, {"repo": "binky-control", "kind": "mail_intake"}
|
||||
)
|
||||
assert result["items"][0]["due"] is True
|
||||
|
||||
|
||||
def test_binky_rhythm_weekly_review_carries_milestone_moved(monkeypatch) -> None:
|
||||
_binky_resolver_env(
|
||||
monkeypatch,
|
||||
{
|
||||
"binky_weekly_review": [],
|
||||
"milestone": [
|
||||
{
|
||||
"event_type": "milestone",
|
||||
"created_at": "2026-07-15T12:00:00+00:00",
|
||||
"detail": {"repo": "binky-control"},
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
result = StateHubContextResolver().resolve(
|
||||
"binky_rhythm_status", None, {"repo": "binky-control", "kind": "weekly_review"}
|
||||
)
|
||||
|
||||
item = result["items"][0]
|
||||
assert item["due"] is True
|
||||
assert item["milestone_moved"] is True
|
||||
|
||||
|
||||
def test_binky_rhythm_weekly_review_milestone_outside_window(monkeypatch) -> None:
|
||||
_binky_resolver_env(
|
||||
monkeypatch,
|
||||
{
|
||||
"binky_weekly_review": [],
|
||||
"milestone": [
|
||||
{
|
||||
"event_type": "milestone",
|
||||
"created_at": "2026-07-01T12:00:00+00:00",
|
||||
"detail": {"repo": "binky-control"},
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
result = StateHubContextResolver().resolve(
|
||||
"binky_rhythm_status", None, {"repo": "binky-control", "kind": "weekly_review"}
|
||||
)
|
||||
|
||||
assert result["items"][0]["milestone_moved"] is False
|
||||
|
||||
|
||||
def test_binky_rhythm_unknown_kind_returns_error_shape(monkeypatch) -> None:
|
||||
_binky_resolver_env(monkeypatch, {})
|
||||
|
||||
result = StateHubContextResolver().resolve(
|
||||
"binky_rhythm_status", None, {"repo": "binky-control", "kind": "nope"}
|
||||
)
|
||||
|
||||
assert result["items"] == []
|
||||
assert "unknown kind" in result["error"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue