build_plans now computes the concrete routing answer for each route_answer action
in-process (reuses the catalog; read-only, no subprocess/network) and render_plans
shows it as a `draft:` line. The dry-run demonstrates the actual answer the executor
(T3) will send, not just an intent. RuleBrain stays the default; the llm-connect brain
(T2) is gated on llm-connect being operational + its /execute contract.
230 tests, lint clean. Live dry-run verified against the real inbox.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>