--- id: BINKY-WP-0006 type: workplan title: "Railiance rhythm: llm-connect execution (no host coding agent)" domain: infotech repo: binky-control status: finished owner: codex topic_slug: the-custodian created: "2026-07-21" updated: "2026-07-22" state_hub_workstream_id: "1c068209-c3f7-4445-82ed-f762a095120c" --- Close residual gaps after BINKY-WP-0004 cutover so Binky rhythm runs **unattended on Railiance without installing Claude Code or any coding agent/harness shell on the host.** ## Architectural decision (founder direction 2026-07-21) | Wrong | Right | | --- | --- | | Install Claude CLI / coding agent on railiance01 | Use **llm-connect** (already in `activity-core` ns on railiance) via **OpenRouter** | | AgenticClaudeCodeAdapter as default on server | **Hosted text adapter** + **deterministic apply** of structured results | | Re-implement policy in every client | Keep Green/Blue rules in harness apply layer; model only classifies/drafts | ### Two-phase mail path (already half-built) 1. **Deterministic scan** (`agent-harness mail-scan`) — IMAP via AppRole, CSV under `mailmeta/reports/`, hub `binky_mail_intake` counts. **No LLM.** Proven on railiance01 2026-07-21. 2. **Triage** — today wrongly defaults to Claude Code agentic session. **Target:** call in-cluster `http://llm-connect.activity-core.svc.cluster.local:8080` (OpenRouter, cost-efficient open-weights or cheap hosted model) → **JSON triage plan** → deterministic applier commits metadata-only queue/log updates. Daily brief / weekly review follow the same pattern (structured draft → apply → commit), not a full IDE agent on the node. ### Existing platform assets - llm-connect Deployment live on railiance01 (`LLM_CONNECT_PROVIDER=openrouter`, model currently `google/gemini-2.5-flash` in ConfigMap — may retarget to open-weights for cost). - OpenBao lane for OpenRouter key already used by that service (`llm-connect-provider-secrets` / CCR path under activity-core). - activity-core `llm_client.py` is the HTTP client pattern to reuse. - agent-harness `runner.py` already accepts an injected `adapter=` (swap default off Claude Code for server profiles). ## Task: Spec llm-connect mail-triage contract ```task id: BINKY-WP-0006-T01 status: done priority: high state_hub_task_id: "912a8802-e80f-4c04-aecd-9586c3b7a4b0" ``` Write `integrations/railiance-llm-rhythm.md` (or extend cutover runbook): - Input: newest `mailmeta/reports/*.csv` (metadata columns only; no bodies). - Output JSON schema: log entries, queue candidates, suspicious flags (sender/subject/date only). - Model profile: OpenRouter **cost-efficient** default (prefer open-weights; name candidates + fallback); max tokens; temperature low. - Apply rules: Blue lane, no push, no network from apply step; suspicious mail never acted on. - Where it runs: railiance Job/CronJob or host timer calling `agent-harness mail-triage` (new subcommand), **not** Claude. Done when: doc reviewed against AutonomyPolicy + mailscan two-phase design. ## Task: Implement hosted triage path in agent-harness ```task id: BINKY-WP-0006-T02 status: done priority: high state_hub_task_id: "00439d4e-d6ee-4cbc-98e2-431eca4651fe" ``` In **agent-harness** (implementation repo; this WP tracks binky integration): 1. `LlmConnectHttpAdapter` (or client matching activity-core `llm_client.py`) using `LLM_CONNECT_URL`. 2. `mail-triage` command: read report → prompt → parse JSON → apply to `mailmeta/mail-log.md` + queues → local commit → hub event. 3. Server profile default: **never** require `claude` CLI. 4. Tests with fixture CSV + mock HTTP llm-connect. Done when: unit tests green; dry-run docs for railiance. ## Task: Wire OpenRouter model profile on railiance llm-connect ```task id: BINKY-WP-0006-T03 status: done priority: high state_hub_task_id: "1f82c8e0-945e-44b6-a5dc-22dd055e751d" ``` On railiance01 activity-core llm-connect ConfigMap (and optional dedicated profile env for rhythm): - Confirm OpenRouter secret still healthy (no values in git/chat). - Choose cost-efficient model (open-weights preferred; document choice and $/MTok rationale). - Smoke: in-cluster request from a Job to llm-connect with a tiny prompt. - Expose URL to host timers if host-run: `LLM_CONNECT_URL=http://…` via cluster DNS or port-forward service policy (prefer **in-cluster CronJob** over host if possible). Done when: smoke OK; ConfigMap change committed in llm-connect/activity-core overlays as appropriate. **2026-07-22:** In-cluster llm-connect smoke OK (OpenRouter/`google/gemini-2.5-flash`). Host mail-triage via port-forward succeeded (commit on railiance). Open-weights model lock deferred — current model works; retarget when desired. ## Task: Railiance schedule uses llm path; drop Claude residual ```task id: BINKY-WP-0006-T04 status: done priority: medium state_hub_task_id: "2093bf62-20ef-4d3e-9b91-be77a38603f7" ``` Update `scripts/railiance-rhythm/run-mail.sh` to call `mail-scan` then `mail-triage` (llm-connect), remove `require_claude` for mail. Prefer migrating daily/review similarly or keep them laptop-optional until T05. Update OperatingRhythm.md + executor-cutover-runbook residual section: **no coding agent on railiance**. **2026-07-22:** Timers/scripts updated; mail path scan+triage; daily/review still wait on brief-* commands (T05). ## Task: Daily/review via llm-connect (optional same WP if small) **2026-07-22:** `agent-harness brief-daily` shipped; live brief `briefs/2026-07-22-daily-brief.md` via llm-connect on railiance. Weekly review still open (brief-weekly). ```task id: BINKY-WP-0006-T05 status: done priority: medium state_hub_task_id: "503ff56b-ac38-44b0-911e-937e0c2fc089" ``` Extend the same pattern to daily brief + weekly review prep: structured generation via llm-connect + deterministic markdown apply + commit. If scope grows, spawn BINKY-WP-0007 and mark this task done with handoff. ## Task: Closure ```task id: BINKY-WP-0006-T06 status: done priority: low state_hub_task_id: "3a93d7d4-f949-4a85-ad2e-a566ccc1f966" ``` Three clean unattended mail cycles on railiance (scan+triage); milestone; `statehub fix-consistency`. **2026-07-22:** Three clean scan+triage cycles on railiance01 (UTC 08:26–08:27), each `ok` with hub events `binky_mail_intake` + `binky_mail_triage`. Host `LLM_CONNECT_URL` discovery now prefers reachable ClusterIP (not broken cluster DNS). Residuals handed off as live intakes: **AWQ-011** (`brief-weekly` via llm-connect), **AWQ-012** (optional open-weights model retarget).