399 lines
12 KiB
Python
399 lines
12 KiB
Python
"""Approach registry: match ops_run → cheapest correct executor (REIN-A-0002-T02).
|
|
|
|
Prefer deterministic / structured llm-connect adapters before open-ended agent
|
|
sessions. Add rows in APPROACH_RULES (order = priority).
|
|
|
|
How to add a row:
|
|
1. Implement a ``run_*`` function or CLI command.
|
|
2. Append an ApproachRule with match predicates (labels / blob substrings).
|
|
3. Wire execution in ``execute_approach``.
|
|
4. Add pure match tests in ``tests/test_approaches.py``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from rein_aharness.ops_run_client import OpsRun, OpsRunConfig, resolve_ops_target
|
|
from rein_aharness.taskspec import TaskSpecError
|
|
|
|
# Approach command names (stable; used in metrics + ops_run.result)
|
|
APPROACH_FI_RESEARCH_BRIEF = "fi-research-brief"
|
|
APPROACH_BRIEF_DAILY = "brief-daily"
|
|
APPROACH_BRIEF_WEEKLY = "brief-weekly"
|
|
APPROACH_MAIL_SCAN = "mail-scan"
|
|
APPROACH_MAIL_TRIAGE = "mail-triage"
|
|
APPROACH_MAIL_PIPELINE = "mail-scan+triage"
|
|
APPROACH_AGENT_SESSION = "agent-session"
|
|
APPROACH_UNMATCHED = "unmatched"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ApproachRule:
|
|
name: str
|
|
"""Match if any of these labels appear (case-insensitive)."""
|
|
labels_any: frozenset[str] = frozenset()
|
|
"""Match if all of these labels appear."""
|
|
labels_all: frozenset[str] = frozenset()
|
|
"""Match if any substring appears in definition id + labels + title + hint."""
|
|
blob_contains: frozenset[str] = frozenset()
|
|
"""If approach_hint equals this command name, select immediately."""
|
|
hint_name: str | None = None
|
|
|
|
|
|
# Ordered: first match wins. Cheapest / most specific first.
|
|
APPROACH_RULES: tuple[ApproachRule, ...] = (
|
|
ApproachRule(
|
|
name=APPROACH_FI_RESEARCH_BRIEF,
|
|
labels_any=frozenset({"research-brief", "freedom-intelligence"}),
|
|
blob_contains=frozenset(
|
|
{"fi-daily", "fi_daily", "fi-research", "freedom intelligence"}
|
|
),
|
|
hint_name=APPROACH_FI_RESEARCH_BRIEF,
|
|
),
|
|
ApproachRule(
|
|
name=APPROACH_BRIEF_WEEKLY,
|
|
labels_all=frozenset({"binky", "weekly-review"}),
|
|
blob_contains=frozenset(
|
|
{"binky-weekly-review", "binky_weekly_review", "weekly founder review"}
|
|
),
|
|
hint_name=APPROACH_BRIEF_WEEKLY,
|
|
),
|
|
ApproachRule(
|
|
name=APPROACH_BRIEF_DAILY,
|
|
labels_any=frozenset({"rhythm", "binky-daily", "daily-brief"}),
|
|
labels_all=frozenset(), # rhythm alone is enough for Binky daily
|
|
blob_contains=frozenset(
|
|
{"binky-daily", "binky_daily", "daily-rhythm", "daily_brief"}
|
|
),
|
|
hint_name=APPROACH_BRIEF_DAILY,
|
|
),
|
|
ApproachRule(
|
|
name=APPROACH_MAIL_PIPELINE,
|
|
labels_any=frozenset({"mail-intake", "mail_intake"}),
|
|
blob_contains=frozenset({"mail-intake", "mail_intake", "weekly-mail"}),
|
|
hint_name=APPROACH_MAIL_PIPELINE,
|
|
),
|
|
ApproachRule(
|
|
name=APPROACH_MAIL_SCAN,
|
|
labels_any=frozenset({"mail-scan"}),
|
|
hint_name=APPROACH_MAIL_SCAN,
|
|
),
|
|
ApproachRule(
|
|
name=APPROACH_MAIL_TRIAGE,
|
|
labels_any=frozenset({"mail-triage"}),
|
|
hint_name=APPROACH_MAIL_TRIAGE,
|
|
),
|
|
ApproachRule(
|
|
name=APPROACH_AGENT_SESSION,
|
|
labels_any=frozenset({"agent-session", "agent_session"}),
|
|
blob_contains=frozenset({"agent-session"}),
|
|
hint_name=APPROACH_AGENT_SESSION,
|
|
),
|
|
)
|
|
|
|
|
|
def _match_blob(run: OpsRun) -> str:
|
|
parts = [
|
|
run.activity_definition_id or "",
|
|
run.title or "",
|
|
run.approach_hint or "",
|
|
" ".join(run.labels or []),
|
|
run.source_id or "",
|
|
run.target_repo or "",
|
|
]
|
|
return " ".join(parts).lower()
|
|
|
|
|
|
def select_approach(run: OpsRun) -> str:
|
|
"""Return approach command name for this ops_run (pure)."""
|
|
hint = (run.approach_hint or "").strip().lower()
|
|
if hint:
|
|
for rule in APPROACH_RULES:
|
|
if rule.hint_name and hint in {
|
|
rule.hint_name,
|
|
rule.name,
|
|
rule.hint_name.replace("-", "_"),
|
|
}:
|
|
return rule.name
|
|
# Explicit known command as hint
|
|
known = {
|
|
APPROACH_FI_RESEARCH_BRIEF,
|
|
APPROACH_BRIEF_DAILY,
|
|
APPROACH_BRIEF_WEEKLY,
|
|
APPROACH_MAIL_SCAN,
|
|
APPROACH_MAIL_TRIAGE,
|
|
APPROACH_MAIL_PIPELINE,
|
|
APPROACH_AGENT_SESSION,
|
|
}
|
|
if hint in known or hint.replace("_", "-") in known:
|
|
return hint.replace("_", "-")
|
|
|
|
labels = {str(x).lower() for x in (run.labels or [])}
|
|
blob = _match_blob(run)
|
|
|
|
for rule in APPROACH_RULES:
|
|
matches_all = bool(rule.labels_all) and rule.labels_all.issubset(labels)
|
|
matches_any = bool(rule.labels_any) and bool(rule.labels_any & labels)
|
|
matches_blob = bool(rule.blob_contains) and any(
|
|
substring in blob for substring in rule.blob_contains
|
|
)
|
|
if matches_all or matches_any or matches_blob:
|
|
return rule.name
|
|
|
|
return APPROACH_UNMATCHED
|
|
|
|
|
|
@dataclass
|
|
class ApproachResult:
|
|
ok: bool
|
|
approach: str
|
|
result: dict[str, Any] = field(default_factory=dict)
|
|
reason: str = ""
|
|
reopen: bool = False
|
|
|
|
|
|
def execute_approach(
|
|
run: OpsRun,
|
|
*,
|
|
approach: str | None = None,
|
|
config: OpsRunConfig | None = None,
|
|
report_to_hub: bool = True,
|
|
commit: bool = True,
|
|
) -> ApproachResult:
|
|
"""Dispatch to the selected executor. Never raises for business failure."""
|
|
cfg = config or OpsRunConfig.from_env()
|
|
name = approach or select_approach(run)
|
|
|
|
if name == APPROACH_UNMATCHED:
|
|
return ApproachResult(
|
|
ok=False,
|
|
approach=name,
|
|
reason=(
|
|
"no approach matched labels/definition; "
|
|
f"labels={run.labels!r} def={run.activity_definition_id!r}"
|
|
),
|
|
reopen=False,
|
|
)
|
|
|
|
try:
|
|
target = resolve_ops_target(run, cfg)
|
|
except TaskSpecError as exc:
|
|
return ApproachResult(
|
|
ok=False,
|
|
approach=name,
|
|
reason=str(exc),
|
|
reopen=False,
|
|
)
|
|
|
|
try:
|
|
if name == APPROACH_FI_RESEARCH_BRIEF:
|
|
return _run_fi(target, report_to_hub=report_to_hub, commit=commit)
|
|
if name == APPROACH_BRIEF_DAILY:
|
|
return _run_brief_daily(target, report_to_hub=report_to_hub, commit=commit)
|
|
if name == APPROACH_BRIEF_WEEKLY:
|
|
return _run_brief_weekly(target, report_to_hub=report_to_hub, commit=commit)
|
|
if name == APPROACH_MAIL_SCAN:
|
|
return _run_mail_scan(target, report_to_hub=report_to_hub)
|
|
if name == APPROACH_MAIL_TRIAGE:
|
|
return _run_mail_triage(target, report_to_hub=report_to_hub, commit=commit)
|
|
if name == APPROACH_MAIL_PIPELINE:
|
|
return _run_mail_pipeline(
|
|
target, report_to_hub=report_to_hub, commit=commit
|
|
)
|
|
if name == APPROACH_AGENT_SESSION:
|
|
return _run_agent_session(run, target, report_to_hub=report_to_hub)
|
|
except Exception as exc: # noqa: BLE001 — surface as fail ops_run
|
|
return ApproachResult(
|
|
ok=False,
|
|
approach=name,
|
|
reason=f"{type(exc).__name__}: {exc}",
|
|
reopen=True,
|
|
)
|
|
|
|
return ApproachResult(
|
|
ok=False,
|
|
approach=name,
|
|
reason=f"approach not implemented: {name}",
|
|
reopen=False,
|
|
)
|
|
|
|
|
|
def _run_fi(target: Path, *, report_to_hub: bool, commit: bool) -> ApproachResult:
|
|
from rein_aharness.fi_research_brief import run_fi_research_brief
|
|
|
|
r = run_fi_research_brief(
|
|
target_repo=target,
|
|
report_to_hub=report_to_hub,
|
|
commit=commit,
|
|
)
|
|
return ApproachResult(
|
|
ok=r.ok,
|
|
approach=APPROACH_FI_RESEARCH_BRIEF,
|
|
result={
|
|
"date": r.date,
|
|
"path": r.path,
|
|
"wrote": r.wrote,
|
|
"committed": r.committed,
|
|
"skipped_existing": r.skipped_existing,
|
|
"collection_candidates": r.collection_candidates,
|
|
"head_after": r.head_after,
|
|
"target_repo": "freedom-intelligence",
|
|
},
|
|
reason=r.reason,
|
|
reopen=not r.ok and not r.skipped_existing,
|
|
)
|
|
|
|
|
|
def _run_brief_daily(
|
|
target: Path, *, report_to_hub: bool, commit: bool
|
|
) -> ApproachResult:
|
|
from rein_aharness.brief_daily import run_brief_daily
|
|
|
|
r = run_brief_daily(
|
|
target_repo=target,
|
|
report_to_hub=report_to_hub,
|
|
commit=commit,
|
|
)
|
|
return ApproachResult(
|
|
ok=r.ok,
|
|
approach=APPROACH_BRIEF_DAILY,
|
|
result={
|
|
"date": r.date,
|
|
"path": r.path,
|
|
"wrote": r.wrote,
|
|
"committed": r.committed,
|
|
"skipped_existing": r.skipped_existing,
|
|
"head_after": r.head_after,
|
|
"target_repo": "binky-control",
|
|
},
|
|
reason=r.reason,
|
|
reopen=not r.ok and not r.skipped_existing,
|
|
)
|
|
|
|
|
|
def _run_brief_weekly(
|
|
target: Path, *, report_to_hub: bool, commit: bool
|
|
) -> ApproachResult:
|
|
from rein_aharness.brief_weekly import run_brief_weekly
|
|
|
|
r = run_brief_weekly(
|
|
target_repo=target,
|
|
report_to_hub=report_to_hub,
|
|
commit=commit,
|
|
)
|
|
return ApproachResult(
|
|
ok=r.ok,
|
|
approach=APPROACH_BRIEF_WEEKLY,
|
|
result={
|
|
"date": r.date,
|
|
"path": r.path,
|
|
"wrote": r.wrote,
|
|
"committed": r.committed,
|
|
"skipped_existing": r.skipped_existing,
|
|
"milestone_moved": r.milestone_moved,
|
|
"risk005_state": r.risk005_state,
|
|
"head_after": r.head_after,
|
|
"target_repo": "binky-control",
|
|
},
|
|
reason=r.reason,
|
|
reopen=not r.ok and not r.skipped_existing,
|
|
)
|
|
def _run_mail_scan(target: Path, *, report_to_hub: bool) -> ApproachResult:
|
|
from rein_aharness.mailscan import run_mail_scan
|
|
|
|
r = run_mail_scan(target_repo=target, report_to_hub=report_to_hub)
|
|
return ApproachResult(
|
|
ok=r.ok,
|
|
approach=APPROACH_MAIL_SCAN,
|
|
result={
|
|
"report": r.report_path,
|
|
"new_messages": r.new_messages,
|
|
"auth_lane": r.auth_lane,
|
|
},
|
|
reason=r.reason,
|
|
reopen=not r.ok,
|
|
)
|
|
|
|
|
|
def _run_mail_triage(
|
|
target: Path, *, report_to_hub: bool, commit: bool
|
|
) -> ApproachResult:
|
|
from rein_aharness.mail_triage import run_mail_triage
|
|
|
|
r = run_mail_triage(
|
|
target_repo=target,
|
|
report_to_hub=report_to_hub,
|
|
commit=commit,
|
|
)
|
|
return ApproachResult(
|
|
ok=r.ok,
|
|
approach=APPROACH_MAIL_TRIAGE,
|
|
result={
|
|
"report": r.report,
|
|
"entries_applied": r.entries_applied,
|
|
"committed": r.committed,
|
|
"head_after": r.head_after,
|
|
},
|
|
reason=r.reason,
|
|
reopen=not r.ok,
|
|
)
|
|
|
|
|
|
def _run_mail_pipeline(
|
|
target: Path, *, report_to_hub: bool, commit: bool
|
|
) -> ApproachResult:
|
|
scan = _run_mail_scan(target, report_to_hub=report_to_hub)
|
|
if not scan.ok:
|
|
scan.approach = APPROACH_MAIL_PIPELINE
|
|
return scan
|
|
triage = _run_mail_triage(target, report_to_hub=report_to_hub, commit=commit)
|
|
return ApproachResult(
|
|
ok=triage.ok,
|
|
approach=APPROACH_MAIL_PIPELINE,
|
|
result={"scan": scan.result, "triage": triage.result},
|
|
reason=triage.reason or scan.reason,
|
|
reopen=not triage.ok,
|
|
)
|
|
|
|
|
|
def _run_agent_session(
|
|
run: OpsRun, target: Path, *, report_to_hub: bool
|
|
) -> ApproachResult:
|
|
from rein_aharness.ops_run_client import ops_run_to_taskspec
|
|
from rein_aharness.runner import run_task
|
|
|
|
# Infer agent/event from labels (reuse intake hints)
|
|
from rein_aharness.intake import EmittedIssue, infer_agent_and_event
|
|
|
|
pseudo = EmittedIssue(
|
|
issue_id=run.id,
|
|
title=run.title,
|
|
description=run.description,
|
|
labels=list(run.labels),
|
|
target_repo=run.target_repo,
|
|
activity_definition_id=run.activity_definition_id,
|
|
)
|
|
agent, event = infer_agent_and_event(pseudo)
|
|
spec = ops_run_to_taskspec(
|
|
run,
|
|
agent=agent,
|
|
completion_event_type=event,
|
|
)
|
|
# target already resolved into TaskSpec
|
|
assert spec.target_repo == target or True
|
|
r = run_task(spec, report_to_hub=report_to_hub)
|
|
return ApproachResult(
|
|
ok=r.ok,
|
|
approach=APPROACH_AGENT_SESSION,
|
|
result={
|
|
"committed": r.committed,
|
|
"head_after": r.head_after,
|
|
"tool_profile": r.tool_profile,
|
|
"tokens_spent": r.tokens_spent,
|
|
},
|
|
reason=r.reason,
|
|
reopen=not r.ok,
|
|
)
|