engineer startup: inline the resolved issue contents into the agent prompt (all drivers) AND into the logs #400

Closed
opened 2026-07-01 05:54:35 +00:00 by coilysiren · 2 comments
Owner

Ask

When a warded engineer carry starts, the resolved issue's contents should be dumped into the agent's prompt and into the logs. Today neither reliably happens for the default driver.

Current state (grounded)

Prompt - agentSeedPrompt (cmd/ward/agent.go:154-205) inlines the issue body only for non-vision drivers (case !mode.visionCapable():, :168-177, media stripped). For a vision-capable driver - claude, the default - the body is NOT inlined; the seed instead says "read the full issue body and comment thread at that URL before doing anything else" (:163). So a claude carry is sent to fetch the issue at runtime, not handed its text.

Logs - the only startup echo is carryingLine (the issue title to stderr, ward#307, agent.go:962). The full body / seed prompt is rendered only under --print (:956), which is a dry-run that launches nothing. A real detached run logs the title and nothing of the task text.

Why it matters (motivating incident)

This surfaced live: a mis-scoped carry on ward#398 had to be killed, and from the outside there was no way to see what task text it received - its logs held only the title, and it had been told to fetch an issue whose premise was later superseded by a hold comment. Two concrete failures:

  • Fetch-at-runtime is not frozen. Telling the agent to pull the URL means its understanding depends on live issue state and network, not a stable snapshot taken at dispatch. Harder to reason about, and a re-fetch can drift from what the operator dispatched.
  • The task text is unauditable. An operator reviewing ~/.ward/agent-logs/<container>/ (the keep-10 drain, ward#363) cannot see what the carry was working from.

Acceptance criteria

  1. Prompt: inline the issue body verbatim for every driver, not just non-vision ones. The agent is handed the task text at dispatch (a frozen snapshot), rather than instructed to go fetch it.
  2. Preserve what the URL-fetch was for. Keep the canonical URL in the seed for (a) the live comment thread (later context past the dispatch snapshot) and (b) image rendering on vision-capable drivers when the body contains media - do not tell a vision driver "do not re-fetch" if stripping media would lose images it can actually use. Design the inline-plus-URL wording so text-only issues (the norm) work purely from the inline, and media issues still let a vision driver pull the pictures. This is engineer latitude - the requirement is body-in-prompt-for-all-drivers without regressing image handling.
  3. Logs: dump the resolved issue contents at startup so a detached run's log shows what it carries. At minimum log the title + body (and ideally the full seed) at dispatch, landing where an operator will find it - the host dispatch log and/or the drained ~/.ward/agent-logs/<container>/ console / meta.json (see docs/agent-observability.md). It should be greppable without --print.
  4. Keep it public-safe: issue bodies are public Forgejo content, so logging them is fine, but do not additionally spill anything secret (tokens, env) into the dumped log - only the issue text.

Pointers

  • Seed construction: agentSeedPrompt (agent.go:154-205), the visionCapable() branch is the thing to generalize.
  • Resolved data already in hand: resolvedWork{Title, Body, Comments, Seed} (agent.go:416, populated at :465).
  • Startup logging site: launchAgentContainer (agent.go:939-964), next to carryingLine.
  • --print path (printAgentPlan, agent.go:957) already renders the seed - reuse its rendering for the run-time log rather than duplicating.

Scope / mode

headless. Self-contained ward change, direction settled, no design fork needing a human. Update docs/agent-engineer.md (and the seed description in docs/agent-frontload.md if it describes the fetch-vs-inline behavior) to match. Add/extend tests around agentSeedPrompt asserting the body is inlined for a vision-capable driver.

## Ask When a `warded engineer` carry starts, the resolved issue's contents should be dumped **into the agent's prompt** and **into the logs**. Today neither reliably happens for the default driver. ## Current state (grounded) **Prompt** - `agentSeedPrompt` (`cmd/ward/agent.go:154-205`) inlines the issue body only for **non-vision** drivers (`case !mode.visionCapable():`, `:168-177`, media stripped). For a vision-capable driver - **claude, the default** - the body is NOT inlined; the seed instead says *"read the full issue body and comment thread at that URL before doing anything else"* (`:163`). So a claude carry is sent to **fetch** the issue at runtime, not handed its text. **Logs** - the only startup echo is `carryingLine` (the issue **title** to stderr, ward#307, `agent.go:962`). The full body / seed prompt is rendered only under `--print` (`:956`), which is a dry-run that launches nothing. A real detached run logs the title and nothing of the task text. ## Why it matters (motivating incident) This surfaced live: a mis-scoped carry on ward#398 had to be killed, and from the outside there was no way to see **what task text it received** - its logs held only the title, and it had been told to fetch an issue whose premise was later superseded by a hold comment. Two concrete failures: - **Fetch-at-runtime is not frozen.** Telling the agent to pull the URL means its understanding depends on live issue state and network, not a stable snapshot taken at dispatch. Harder to reason about, and a re-fetch can drift from what the operator dispatched. - **The task text is unauditable.** An operator reviewing `~/.ward/agent-logs/<container>/` (the keep-10 drain, ward#363) cannot see what the carry was working from. ## Acceptance criteria 1. **Prompt: inline the issue body verbatim for every driver**, not just non-vision ones. The agent is handed the task text at dispatch (a frozen snapshot), rather than instructed to go fetch it. 2. **Preserve what the URL-fetch was for.** Keep the canonical URL in the seed for (a) the **live comment thread** (later context past the dispatch snapshot) and (b) **image rendering on vision-capable drivers** when the body contains media - do not tell a vision driver "do not re-fetch" if stripping media would lose images it can actually use. Design the inline-plus-URL wording so text-only issues (the norm) work purely from the inline, and media issues still let a vision driver pull the pictures. This is engineer latitude - the requirement is body-in-prompt-for-all-drivers without regressing image handling. 3. **Logs: dump the resolved issue contents at startup** so a detached run's log shows what it carries. At minimum log the title + body (and ideally the full seed) at dispatch, landing where an operator will find it - the host dispatch log and/or the drained `~/.ward/agent-logs/<container>/` console / `meta.json` (see docs/agent-observability.md). It should be greppable without `--print`. 4. Keep it **public-safe**: issue bodies are public Forgejo content, so logging them is fine, but do not additionally spill anything secret (tokens, env) into the dumped log - only the issue text. ## Pointers - Seed construction: `agentSeedPrompt` (`agent.go:154-205`), the `visionCapable()` branch is the thing to generalize. - Resolved data already in hand: `resolvedWork{Title, Body, Comments, Seed}` (`agent.go:416`, populated at `:465`). - Startup logging site: `launchAgentContainer` (`agent.go:939-964`), next to `carryingLine`. - `--print` path (`printAgentPlan`, `agent.go:957`) already renders the seed - reuse its rendering for the run-time log rather than duplicating. ## Scope / mode headless. Self-contained ward change, direction settled, no design fork needing a human. Update docs/agent-engineer.md (and the seed description in docs/agent-frontload.md if it describes the fetch-vs-inline behavior) to match. Add/extend tests around `agentSeedPrompt` asserting the body is inlined for a vision-capable driver.
Author
Owner

WARD-OUTCOME: done - vision drivers now get the issue body inlined as a frozen snapshot and the full seed is dumped to the dispatch log at launch.

The code change was small and clean: the agentSeedPrompt switch already had the non-vision inline path, so generalizing it was mostly adding a default: branch and a shared seedLogBlock helper reused by --print and the startup dump. Detecting media (markdown vs bare-image regex) let the vision wording stay honest - text-only issues never get told to fetch pictures that aren't there.

What actually fought back was the doc size caps, not the Go. docs/agent-engineer.md was already sitting at 3978/4000 chars, so every sentence I added had to be paid for by trimming another one - several rounds of shave-and-remeasure. docs/FEATURES.md was at 3997/4000, genuinely at its hard cap, so I reverted the FEATURES touch rather than gut unrelated entries to wedge one in; the behavior is documented in agent-engineer.md. Then main landed ward#399 (the "stop a carry" docs, same file, same incident) mid-flight and conflicted - resolved by taking main's compressions plus its new Stopping section alongside my seed paragraph.

Confidence is high on the prompt + tests (TestSeedLogBlock plus the reworked seed assertions all green, build/vet/lint clean of new findings). One rough edge worth a follow-up: the seed dump lands on the host dispatch stderr, which is the "and/or" the issue allowed, but it does NOT reach the drained ~/.ward/agent-logs/<container>/console.log (that's docker logs of the container, a different stream). If an operator expects the carried task text inside the drained bundle specifically, that'd need a separate hook - could be worth filing.

WARD-OUTCOME: done - vision drivers now get the issue body inlined as a frozen snapshot and the full seed is dumped to the dispatch log at launch. The code change was small and clean: the `agentSeedPrompt` switch already had the non-vision inline path, so generalizing it was mostly adding a `default:` branch and a shared `seedLogBlock` helper reused by `--print` and the startup dump. Detecting media (markdown vs bare-image regex) let the vision wording stay honest - text-only issues never get told to fetch pictures that aren't there. What actually fought back was the **doc size caps**, not the Go. `docs/agent-engineer.md` was already sitting at 3978/4000 chars, so every sentence I added had to be paid for by trimming another one - several rounds of shave-and-remeasure. `docs/FEATURES.md` was at 3997/4000, genuinely at its hard cap, so I reverted the FEATURES touch rather than gut unrelated entries to wedge one in; the behavior is documented in agent-engineer.md. Then main landed ward#399 (the "stop a carry" docs, same file, same incident) mid-flight and conflicted - resolved by taking main's compressions plus its new Stopping section alongside my seed paragraph. Confidence is high on the prompt + tests (TestSeedLogBlock plus the reworked seed assertions all green, build/vet/lint clean of new findings). One rough edge worth a follow-up: the seed dump lands on the **host dispatch stderr**, which is the "and/or" the issue allowed, but it does NOT reach the drained `~/.ward/agent-logs/<container>/console.log` (that's `docker logs` of the container, a different stream). If an operator expects the carried task text inside the drained bundle specifically, that'd need a separate hook - could be worth filing.
Author
Owner

Fold in: retire visionCapable as part of this change (Kai's call).

This issue already rewrites agentSeedPrompt (agent.go:154-205) to inline the body for all drivers. visionCapable() (container_compute.go:177) has exactly one non-test consumer: the case !mode.visionCapable(): branch at agent.go:168 that this issue is replacing. Once the body inlines for every driver, that branch and the flag are both dead. So retire it here rather than reshaping it:

  • Delete func (m containerMode) visionCapable() (container_compute.go:179) and its test. Confirm nothing else reads it (grep today: only agent.go:168).
  • Inline the full body for every driver, keeping the issue URL for the live comment thread and for image fetch on harnesses that can.
  • The only behavior visionCapable guarded was stripping image markup for the local ollama harnesses (qwen/goose) so they do not 400 on read_image (ward#157). Replace that per-agent switch with a generic, harness-agnostic line in the seed - e.g. "images in the body are linked by URL; if your harness cannot read images, work from the surrounding text." A non-vision harness sees ![](url) as inert markdown and does not 400 unless it actively calls read_image; the generic instruction covers that with no per-agent branch.

This also removes a vision: field the ward#401 agent-agnostic design had proposed adding to agents.yaml - retiring the flag beats relocating it. Cross-ref ward#401 (I have noted the correction there).

**Fold in: retire `visionCapable` as part of this change (Kai's call).** This issue already rewrites `agentSeedPrompt` (`agent.go:154-205`) to inline the body for all drivers. `visionCapable()` (`container_compute.go:177`) has exactly **one** non-test consumer: the `case !mode.visionCapable():` branch at `agent.go:168` that this issue is replacing. Once the body inlines for every driver, that branch and the flag are both dead. So retire it here rather than reshaping it: - Delete `func (m containerMode) visionCapable()` (`container_compute.go:179`) and its test. Confirm nothing else reads it (grep today: only `agent.go:168`). - Inline the full body for every driver, keeping the issue URL for the live comment thread and for image fetch on harnesses that can. - The only behavior `visionCapable` guarded was stripping image markup for the local ollama harnesses (qwen/goose) so they do not 400 on `read_image` (ward#157). Replace that per-agent switch with a **generic, harness-agnostic** line in the seed - e.g. "images in the body are linked by URL; if your harness cannot read images, work from the surrounding text." A non-vision harness sees `![](url)` as inert markdown and does not 400 unless it actively calls `read_image`; the generic instruction covers that with no per-agent branch. This also removes a `vision:` field the ward#401 agent-agnostic design had proposed adding to `agents.yaml` - retiring the flag beats relocating it. Cross-ref ward#401 (I have noted the correction there).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coilyco-flight-deck/ward#400
No description provided.