CUST-ADR-002 accepted · accepted-2 the-custodian reviewed 2026-10-11generated from canonical source — do not edit

Custodian Agent Runtime — v0.1 Bootstrap Design

Source: the-custodian · canon/architecture/adr-002-custodian-agent-runtime-design.md · dcdec162d61398f0def4990957455d45a74a1b01

Review due: 2027-04-11

Status

Accepted.

Technical currency review — 2026-10-11

The accepted decision remains the v0.1 bootstrap architecture for the standalone package under runtime/, not a grant to every coding agent or an assertion that a privileged autonomous runtime is deployed. Current source still implements D1–D5: this package location, a single synchronous CLI cycle, configurable llm-connect with claude-code default, three explicit HTTP write classes, and a fenced JSON action plan plus a session note. All 32 existing offline runtime tests pass with HTTP calls mocked. That verifies the described prototype behavior; it proves no live authorization, runtime isolation, credential boundary, semantic correctness of task updates, or rollout state.

D4's original permission is limited to that bootstrap's named low-impact coordination operations. It is neither an approval token nor a Codex autopilot grant, and it does not override owner-source work records, API authorization, work-record lanes or agent-specific supervision. Current governance is docs/agent-autonomy-decision.md (founder, 2026-09-28): new scopes start supervised and autopilot requires a separate explicit scoped grant and verified runtime enforcement. The inspected custodian-codex supervision record remains supervised, with no autopilot grant and interactive isolation not yet migrated. This review changes no such authority or record.

The March dependency/deployment statements below are historical context. The review does not reaffirm that every deployment has Claude Code, that the bootstrap is scheduled or deployed, or that its narrow operation list provides credential isolation. API addresses and live change procedures belong to docs/agent-environment-orientation.md; task/workplan sources follow ADR-001 and the later accepted ADR-012. The code's direct task-status HTTP branch is prototype implementation evidence, not permission to override file-backed owner records. Any widening of D4 or promotion remains a separate decision.

The original 2026-03-12 decision and accepted-1 bytes are retained in docs/evidence/2026-10-11-adr-currency-review/adr-002-accepted-1.md. accepted-2 is this dated factual/scope annotation, not a new human adoption of an expanded action surface. The detailed source hashes and test receipt are in docs/evidence/2026-10-11-adr-currency-review/review.json.

Context (historical decision basis)

CUST-WP-0001 requires a first working skeleton of the Custodian as an acting agent: a loop that observes project state, reasons about it, and executes bounded write operations — without human interaction for each step.

The dependencies (llm-connect, operational Railiance infra) are now resolved. This ADR captures the five key architectural decisions for the v0.1 bootstrap.

Decisions

D1 — Location: runtime/ inside the-custodian (not a new repo)

Decision: The runtime lives under the-custodian/runtime/ as a standalone Python package (pyproject.toml, own venv) rather than a new repository.

Rationale: The runtime is tightly coupled to canon (reads constitution, memory) and the state-hub (its primary coordination layer). A separate repo adds friction with no v0.1 benefit. The existing runtime/ scaffold confirms the original intent. Extraction to its own repo is deferred to when the runtime has stable boundaries and multiple consumers.

D2 — OODA loop: single-pass synchronous CLI

Decision: One run() call = one complete Observe → Orient → Decide → Act cycle. Entry point is a CLI (agent.py) invoked manually or by a cron job.

Observe  — HTTP GET to state-hub: state summary or domain summary
Orient   — Load constitution + build structured LLM context prompt
Decide   — Single LLM call (via llm-connect) returns a JSON action plan
Act      — Execute only sanctioned write operations from the plan

Rationale: Async event loops and daemons add operational complexity that v0.1 doesn't need. A single-pass CLI is testable, debuggable, and can be scheduled externally. The transition to an event-driven loop is Phase 2.

D3 — LLM backend: ClaudeCodeAdapter by default

Decision: The runtime uses llm_connect.ClaudeCodeAdapter as its default LLM backend (shells out to claude --print). Provider is configurable via --llm flag to support gemini, openrouter, or openai.

Rationale: ClaudeCodeAdapter requires no API key and honours the Local-First value (V2). All current deployments have Claude Code available. The llm-connect abstraction means switching providers is a one-line change.

D4 — Action constraint: three sanctioned write operations only

Decision: The agent may execute exactly three state-hub write operations without human approval:

  1. add_progress_event — append an observation to the event log
  2. update_task_status — mark a task done/in_progress (reversible)
  3. flag_for_human — raise an intervention flag (escalation, not action)

All other operations (create workstream, record decision, resolve decision, write to canon) require human approval before execution.

Rationale: Constitution §3/§4 require bounded agency. The three operations are either append-only (progress events), reversible (task status), or explicitly escalating (flag). They cannot produce irreversible harm.

D5 — LLM response format: JSON block in markdown

Decision: The LLM is prompted to return a Markdown response with a fenced ```json block containing the structured action plan:

{
  "observations": ["..."],
  "progress_events": [
    {"summary": "...", "workstream_id": "...", "event_type": "note"}
  ],
  "tasks_to_update": [
    {"task_id": "...", "status": "done"}
  ],
  "tasks_to_flag": [
    {"task_id": "...", "note": "..."}
  ]
}

The surrounding Markdown is preserved as a human-readable reasoning trace and written to memory/working/ as a session note.

Rationale: JSON blocks are robust to extraction (delimited), LLMs produce them reliably with clear instructions, and the surrounding prose gives Bernd an auditable reasoning trace without requiring a separate reasoning step.

Consequences

  • runtime/ becomes a standalone Python package; make agent-run DOMAIN=x invokes it.
  • The runtime has no DB schema changes and no new API endpoints — it is a pure client of the existing state-hub HTTP API.
  • Autonomous actions are limited to append-only writes and escalations. Any expansion of the action surface requires a new ADR and human approval.
  • The v0.1 loop is single-user (Bernd). Multi-agent expansion is Phase 2+.

Deferred

  • Async event loop / daemon mode (Phase 2)
  • RAG over canon (Phase 1 roadmap item)
  • Tool adapters beyond state-hub HTTP (planned in runtime/tool_adapters/)
  • Deployment on Railiance k3s as a scheduled CronJob