Add fi_brief_status resolver for Freedom Intelligence daily briefs.
Mirror binky_rhythm daily due-checks against fi_daily_brief progress events so activity-core can schedule freedom-intelligence research without executing it.
This commit is contained in:
parent
9a4f6a4d75
commit
70a7db500d
2 changed files with 126 additions and 0 deletions
|
|
@ -19,6 +19,8 @@ Supported queries:
|
||||||
- legacy_meter_weekly_review: GET {STATE_HUB_URL}/legacy-meter/weekly-review
|
- legacy_meter_weekly_review: GET {STATE_HUB_URL}/legacy-meter/weekly-review
|
||||||
- binky_rhythm_status: due-items for the Binky operating-rhythm definitions,
|
- binky_rhythm_status: due-items for the Binky operating-rhythm definitions,
|
||||||
derived from /progress/ events (see _binky_rhythm_status)
|
derived from /progress/ events (see _binky_rhythm_status)
|
||||||
|
- fi_brief_status: due-item for Freedom Intelligence daily research brief,
|
||||||
|
derived from fi_daily_brief progress events
|
||||||
|
|
||||||
When STATE_HUB_URL points at the state-hub edge relay, allowlisted GET reads may
|
When STATE_HUB_URL points at the state-hub edge relay, allowlisted GET reads may
|
||||||
be served from a stale local cache during upstream outages (`X-StateHub-Edge-Cache:
|
be served from a stale local cache during upstream outages (`X-StateHub-Edge-Cache:
|
||||||
|
|
@ -158,6 +160,8 @@ class StateHubContextResolver(ContextResolver):
|
||||||
return _legacy_meter_weekly_review(params)
|
return _legacy_meter_weekly_review(params)
|
||||||
if query == "binky_rhythm_status":
|
if query == "binky_rhythm_status":
|
||||||
return _binky_rhythm_status(params)
|
return _binky_rhythm_status(params)
|
||||||
|
if query == "fi_brief_status":
|
||||||
|
return _fi_brief_status(params)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -470,6 +474,55 @@ def _binky_progress_events(event_type: str, repo: str) -> list[dict[str, Any]]:
|
||||||
return matched
|
return matched
|
||||||
|
|
||||||
|
|
||||||
|
_FI_BRIEF_EVENT_TYPE = "fi_daily_brief"
|
||||||
|
_FI_BRIEF_DEFAULT_REPO = "freedom-intelligence"
|
||||||
|
|
||||||
|
|
||||||
|
def _fi_brief_status(params: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
"""Due-item for Freedom Intelligence daily research brief.
|
||||||
|
|
||||||
|
Params: repo (default "freedom-intelligence"), timezone (IANA name,
|
||||||
|
default Europe/Berlin — "today" is evaluated in this zone).
|
||||||
|
|
||||||
|
due is true when no progress event with event_type=fi_daily_brief and
|
||||||
|
detail.repo matching the target repo exists for today. Executing sessions
|
||||||
|
must post that event on completion (see freedom-intelligence
|
||||||
|
docs/daily-brief-playbook.md).
|
||||||
|
"""
|
||||||
|
repo = str(params.get("repo") or _FI_BRIEF_DEFAULT_REPO)
|
||||||
|
kind = str(params.get("kind") or "daily_brief")
|
||||||
|
if kind != "daily_brief":
|
||||||
|
return {"items": [], "repo": repo, "error": f"unknown kind: {kind}"}
|
||||||
|
|
||||||
|
tz = _binky_timezone(params.get("timezone"))
|
||||||
|
now_local = _utc_now().astimezone(tz)
|
||||||
|
today = now_local.date()
|
||||||
|
|
||||||
|
events = _binky_progress_events(_FI_BRIEF_EVENT_TYPE, repo)
|
||||||
|
timestamps = [
|
||||||
|
ts
|
||||||
|
for ts in (
|
||||||
|
_parse_progress_timestamp(item.get("created_at")) for item in events
|
||||||
|
)
|
||||||
|
if ts > datetime.min.replace(tzinfo=timezone.utc)
|
||||||
|
]
|
||||||
|
last_run = max(timestamps, default=None)
|
||||||
|
due = last_run is None or last_run.astimezone(tz).date() != today
|
||||||
|
|
||||||
|
return {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"kind": kind,
|
||||||
|
"due": due,
|
||||||
|
"date": today.isoformat(),
|
||||||
|
"last_run_at": last_run.isoformat() if last_run else None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repo": repo,
|
||||||
|
"generated_at": _utc_now().isoformat(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _repo_sbom_status(params: dict[str, Any]) -> dict[str, Any]:
|
def _repo_sbom_status(params: dict[str, Any]) -> dict[str, Any]:
|
||||||
"""Resolve SBOM staleness against the State Hub.
|
"""Resolve SBOM staleness against the State Hub.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -998,3 +998,76 @@ def test_binky_rhythm_unknown_kind_returns_error_shape(monkeypatch) -> None:
|
||||||
|
|
||||||
assert result["items"] == []
|
assert result["items"] == []
|
||||||
assert "unknown kind" in result["error"]
|
assert "unknown kind" in result["error"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_fi_brief_status_due_when_no_events(monkeypatch) -> None:
|
||||||
|
_binky_resolver_env(monkeypatch, {"fi_daily_brief": []})
|
||||||
|
|
||||||
|
result = StateHubContextResolver().resolve(
|
||||||
|
"fi_brief_status", None, {"repo": "freedom-intelligence"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["repo"] == "freedom-intelligence"
|
||||||
|
assert result["items"] == [
|
||||||
|
{
|
||||||
|
"kind": "daily_brief",
|
||||||
|
"due": True,
|
||||||
|
"date": "2026-07-17",
|
||||||
|
"last_run_at": None,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_fi_brief_status_not_due_after_todays_run(monkeypatch) -> None:
|
||||||
|
_binky_resolver_env(
|
||||||
|
monkeypatch,
|
||||||
|
{
|
||||||
|
"fi_daily_brief": [
|
||||||
|
{
|
||||||
|
"event_type": "fi_daily_brief",
|
||||||
|
"created_at": "2026-07-17T05:30:00+00:00",
|
||||||
|
"detail": {"repo": "freedom-intelligence"},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
result = StateHubContextResolver().resolve(
|
||||||
|
"fi_brief_status", None, {"repo": "freedom-intelligence"}
|
||||||
|
)
|
||||||
|
|
||||||
|
item = result["items"][0]
|
||||||
|
assert item["due"] is False
|
||||||
|
assert item["last_run_at"] == "2026-07-17T05:30:00+00:00"
|
||||||
|
|
||||||
|
|
||||||
|
def test_fi_brief_status_ignores_other_repo(monkeypatch) -> None:
|
||||||
|
_binky_resolver_env(
|
||||||
|
monkeypatch,
|
||||||
|
{
|
||||||
|
"fi_daily_brief": [
|
||||||
|
{
|
||||||
|
"event_type": "fi_daily_brief",
|
||||||
|
"created_at": "2026-07-17T05:30:00+00:00",
|
||||||
|
"detail": {"repo": "other-lab"},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
result = StateHubContextResolver().resolve(
|
||||||
|
"fi_brief_status", None, {"repo": "freedom-intelligence"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["items"][0]["due"] is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_fi_brief_status_unknown_kind(monkeypatch) -> None:
|
||||||
|
_binky_resolver_env(monkeypatch, {})
|
||||||
|
|
||||||
|
result = StateHubContextResolver().resolve(
|
||||||
|
"fi_brief_status", None, {"repo": "freedom-intelligence", "kind": "weekly"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["items"] == []
|
||||||
|
assert "unknown kind" in result["error"]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue