--- id: agent-harness kind: architecture title: Agent Harness status: active --- # Agent Harness Charter should make the spec executable by handing durable work items and attached spec context to an implementation agent. The harness should be pluggable. Codex, Letta Code, Claude Code, local scripts, and CI workers should all be possible execution targets. Charter should construct the request packet; the agent runner can be swapped. ## Command ```bash charter agent ``` The command: 1. scans the current `spec/` projection for extra context and unsynced drift. 2. loads queued `WorkItem` records from the database. 3. selects the requested slice of durable work, usually one item via `agent next`. 4. builds an agent request from that durable work item and attached spec documents. 5. stores the request in the database with attached spec document ids. By default it does not execute an agent and does not claim work. Work moves to `running` only when an executable runner starts. This keeps request planning from creating stale claimed rows. ## Execution ```bash charter agent --exec --agent-command 'codex exec -s workspace-write -' ``` The command receives: - prompt content on stdin. - `{promptFile}` substitution in the command string as a temporary compatibility export for tools that need file-based prompts. - `CHARTER_PROMPT_FILE` in the environment when `{promptFile}` is used. - `CHARTER_REQUEST_ID` in the environment. - `CHARTER_PROMPT_SHA256` in the environment. This is intentionally crude. A stable stdin contract plus optional temporary prompt-file export is enough to support real runners without hardcoding one vendor's CLI semantics into Charter. Codex uses `codex exec -s workspace-write -` because it reads the prompt from stdin and needs workspace write access for normal test/build caches. ## Prompt contents The agent request includes: - repository path - durable work items - current projection changes, if any - attached spec document ids and bodies - expected response shape The prompt is not the agent's memory. It is a work packet derived from durable work items and spec documents. Durable context belongs in attached spec documents, not in a separate memory store. ## Completion and verification Agents should not rely on final prose as the only completion signal. They should call Charter before exiting: ```bash charter report --request "$CHARTER_REQUEST_ID" --status done --message "Implemented X; checks pass." charter report --request "$CHARTER_REQUEST_ID" --status failed --message "Failed because ..." charter report --request "$CHARTER_REQUEST_ID" --status blocked --message "Blocked because ..." ``` The runner also captures stdout, stderr, and exit code for the `AgentRequest`. If no explicit report exists, Charter infers request/work status from exit code and stores captured output as the fallback report. Verification therefore has three layers: 1. `AgentRequest.status` / `WorkItem.status` for machine-readable state. 2. `AgentRequest.resultText` and `WorkItem.resultText` for the agent's explicit report. 3. `AgentRequest.stdoutText`, `stderrText`, `outputText`, and `exitCode` for the raw run audit trail. `charter requests` is the human inspection path. ## One-item runner ergonomics The default development loop should be one queue item at a time: ```bash charter queue charter queue next charter agent next --exec --letta-agent agent-0ddda04a-3d4c-410a-9fb7-d1940281a93f ``` `queue` is the waiting-room view: it shows unclaimed queued items by default. `work --all` is the full ledger. `queue next` and `agent next` select the oldest queued item. Planning a request without `--exec` must not claim work; work becomes `running` only when the runner actually starts. Old planned claims can be recovered explicitly with `--status claimed`. The Letta runner convenience expands to: ```bash letta --agent --new -p "$(cat {promptFile})" ``` This is deliberately small and slightly ugly. The prompt-file bridge avoids trying to quote a large prompt directly in the Charter command while still matching Letta Code's headless `-p "..."` interface. If Letta grows a stdin prompt contract, Charter can switch to that.