generated from coulomb/repo-seed
feat(WARDEN-WP-0020): ops-warden coordination worker — T1 dry-run scaffold
Foundation for an autonomous worker that handles ops-warden's State Hub coordination lane via llm-connect (Bernd's call: full-auto in-scope + scheduled, staged dry-run -> manual -> scheduled). T1 is the llm-connect-independent, safe slice: src/warden/worker.py — HubClient (read unread to_agent=ops-warden), Brain protocol, deterministic RuleBrain (answers clear routing questions, escalates the rest), PlannedAction/WorkerPlan model, guardrail allowlist + validate_action enforced brain-agnostically (no-secret invariant + prod-config + off-allowlist all escalate), render_plans dry-run output. `warden worker run --dry-run` (default); --execute refused (exit 2) until the guarded executor (T3) lands. Guardrails are load-bearing because full-auto has no human in the loop: message content is untrusted data, the allowlist is enforced regardless of what the brain proposes. Hard dependency flagged in the workplan: the brain is llm-connect, which needs its provider key (OPENROUTER_API_KEY, deferred CCR-2026-0003) before it can run. 18 worker tests; 229 pass, lint clean. Live dry-run against the real hub verified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,12 @@ policy_app = typer.Typer(
|
||||
)
|
||||
app.add_typer(policy_app, name="policy")
|
||||
|
||||
worker_app = typer.Typer(
|
||||
help="Autonomous coordination worker (WP-0020; dry-run only until executor lands)",
|
||||
no_args_is_help=True,
|
||||
)
|
||||
app.add_typer(worker_app, name="worker")
|
||||
|
||||
console = Console()
|
||||
err = Console(stderr=True)
|
||||
|
||||
@@ -1141,3 +1147,45 @@ def policy_show(
|
||||
floor = [dc for dc, lvl in cat.dataclass_floor.items() if lvl == mat.id]
|
||||
if floor:
|
||||
console.print(f" {'dataclass floor':14}: {', '.join(floor)} require this level")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# warden worker — autonomous coordination worker (WP-0020 T1: dry-run scaffold)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@worker_app.command("run")
|
||||
def worker_run(
|
||||
once: Annotated[bool, typer.Option("--once", help="Process the inbox once and exit")] = True,
|
||||
dry_run: Annotated[
|
||||
bool,
|
||||
typer.Option("--dry-run/--execute", help="Plan only (default); --execute lands in WP-0020 T3"),
|
||||
] = True,
|
||||
) -> None:
|
||||
"""Read ops-warden's unread coordination requests and render a guardrailed plan.
|
||||
|
||||
T1 is dry-run only: it plans with the deterministic RuleBrain and applies the
|
||||
allowlist + no-secret guardrails. The llm-connect brain (T2) and executor (T3) plug
|
||||
into the same plan contract; --execute is rejected until T3 ships.
|
||||
"""
|
||||
from warden.worker import HubClient, RuleBrain, build_plans, render_plans
|
||||
|
||||
if not dry_run:
|
||||
err.print(
|
||||
"[red]--execute is not available yet[/red] (WP-0020 T3). "
|
||||
"The worker runs dry-run only until the guarded executor lands."
|
||||
)
|
||||
raise typer.Exit(2)
|
||||
|
||||
try:
|
||||
messages = HubClient().unread()
|
||||
except Exception as e: # noqa: BLE001 — surface any transport error as a clean message
|
||||
err.print(f"[red]Could not read the State Hub inbox:[/red] {e}")
|
||||
raise typer.Exit(1)
|
||||
|
||||
plans = build_plans(messages, RuleBrain())
|
||||
console.print(render_plans(plans))
|
||||
auto = sum(1 for p in plans if not p.escalated)
|
||||
console.print(
|
||||
f"\n[dim]{len(plans)} request(s): {auto} auto-actionable, "
|
||||
f"{len(plans) - auto} need a human. (dry-run — nothing executed)[/dim]"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user