ward agent talk: interactive, fleet-mounted sibling to ward agent work #219

Closed
opened 2026-06-19 07:33:37 +00:00 by coilyco-ops · 8 comments
Member

ward agent talk - interactive, fleet-mounted sibling to ward agent work

Motivation

Detail-hunting across the fleet ("which repo has X", "what does that config default to") is a common need, but the only way to have that conversation today is to keep ~25 repos checked out across ~5 dev machines and grep locally. That idle-checkout state is the actual pain - not the bytes, the tracking and cross-machine drift. ward agent work already solved the hard part of the lifecycle (ephemeral container, fresh-clone-inside, warm gitcache, reap-on-teardown, canonical-clone-so-identical-everywhere). What's missing is an interactive, read-leaning, multi-repo entry point that reuses that machinery so no repo has to idle in a workspace.

Shape

ward agent talk - interactive sibling to work:

  • Defaults to --all-repos (the common case is hunting across the fleet, not deep in one repo).
  • Enumerates the fleet from canonical Forgejo, never from whatever happens to be checked out on the current box, so the same verb gives the same answer on every machine.
  • Shallow-clones (--depth 1, no history) all enumerated repos into the existing warm gitcache volume, parallel fetch-refresh on start.
  • Serves N working trees into one ephemeral interactive container at /repos/<org>/<name>.
  • Agent is seeded to search across the mounted trees, not to carry a feature to merge.
  • Teardown rides existing reap: read-leaning by default, so a dirty tree is the exception - land if clean, else salvage branch + filed issue.

The one piece of new engineering: cardinality

ward container is architecturally one repo per container (fresh-clone the single target inside). talk --all-repos needs N trees in one container. This is not new cloning logic - the warm gitcache already holds per-repo clones - it is a new layout step: lay down N trees from cache into /repos/<org>/<name>.

Open decision: where that layout step lives.

  • Extend container up with a multi-repo flag (e.g. --repos <list>) - reusable, work could benefit later, talk stays a thin seed-and-attach wrapper. Leaning this way.
  • talk-only path that calls the cache layer directly - narrower, no container up surface change.

Reused / already settled

  • Mount mechanism - shallow clone, canonical source, warm gitcache. Exists.
  • Teardown / write-back - reap (land-if-clean, else salvage branch + issue). Exists.
  • Multi-machine consistency - canonical enumeration. Falls out for free.
  • Interactive attach - container exec into the running container. Exists.

Parked

  • coilyco-flight-deck/repo-recall is a plausible future substrate for the cross-repo-search half (its name is the exact use case). Not pulling it in yet - evaluate later whether talk should ride repo-recall's corpus instead of fresh shallow clones. Captured here so the idea does not evaporate.

Acceptance sketch

  • ward agent talk drops into an interactive session with all canonical fleet repos shallow-mounted, no host workspace touched.
  • ward agent talk <repo> narrows to a single repo.
  • Second run is fast (warm cache).
  • Same behavior on any machine.
  • Exit leaves no idle checkout in the workspace; only the shared gitcache persists.
## `ward agent talk` - interactive, fleet-mounted sibling to `ward agent work` ### Motivation Detail-hunting across the fleet ("which repo has X", "what does that config default to") is a common need, but the only way to have that conversation today is to keep ~25 repos checked out across ~5 dev machines and grep locally. That idle-checkout state is the actual pain - not the bytes, the tracking and cross-machine drift. `ward agent work` already solved the hard part of the lifecycle (ephemeral container, fresh-clone-inside, warm gitcache, reap-on-teardown, canonical-clone-so-identical-everywhere). What's missing is an interactive, read-leaning, multi-repo entry point that reuses that machinery so no repo has to idle in a workspace. ### Shape `ward agent talk` - interactive sibling to `work`: - Defaults to `--all-repos` (the common case is hunting across the fleet, not deep in one repo). - Enumerates the fleet from **canonical Forgejo**, never from whatever happens to be checked out on the current box, so the same verb gives the same answer on every machine. - Shallow-clones (`--depth 1`, no history) all enumerated repos into the **existing warm gitcache** volume, parallel fetch-refresh on start. - Serves N working trees into **one** ephemeral interactive container at `/repos/<org>/<name>`. - Agent is seeded to **search across** the mounted trees, not to carry a feature to merge. - Teardown rides existing `reap`: read-leaning by default, so a dirty tree is the exception - land if clean, else salvage branch + filed issue. ### The one piece of new engineering: cardinality `ward container` is architecturally **one repo per container** (fresh-clone the single target inside). `talk --all-repos` needs N trees in one container. This is **not** new cloning logic - the warm gitcache already holds per-repo clones - it is a new **layout** step: lay down N trees from cache into `/repos/<org>/<name>`. Open decision: where that layout step lives. - **Extend `container up` with a multi-repo flag** (e.g. `--repos <list>`) - reusable, `work` could benefit later, `talk` stays a thin seed-and-attach wrapper. Leaning this way. - **`talk`-only path** that calls the cache layer directly - narrower, no `container up` surface change. ### Reused / already settled - Mount mechanism - shallow clone, canonical source, warm gitcache. Exists. - Teardown / write-back - `reap` (land-if-clean, else salvage branch + issue). Exists. - Multi-machine consistency - canonical enumeration. Falls out for free. - Interactive attach - `container exec` into the running container. Exists. ### Parked - `coilyco-flight-deck/repo-recall` is a plausible future substrate for the cross-repo-search half (its name is the exact use case). **Not pulling it in yet** - evaluate later whether `talk` should ride repo-recall's corpus instead of fresh shallow clones. Captured here so the idea does not evaporate. ### Acceptance sketch - `ward agent talk` drops into an interactive session with all canonical fleet repos shallow-mounted, no host workspace touched. - `ward agent talk <repo>` narrows to a single repo. - Second run is fast (warm cache). - Same behavior on any machine. - Exit leaves no idle checkout in the workspace; only the shared gitcache persists.
Author
Member

🛫 ward pre-flight: NO-GO

ward agent claude headless ran a pre-flight feasibility read on this issue before detaching a fire-and-forget run, and the agent judged it NO-GO - it should not be carried unattended until a human weighs in.

unresolved container-layout architecture fork (only "leaning"), an interactive verb whose acceptance can't be self-verified in a detached container, and changes reaching into the shared container up primitive that work depends on.

No container was launched. Review the issue (clarify the scope, resolve the unknown, or split it), then re-dispatch - ward agent claude headless <ref> --no-preflight skips this gate once you've decided it's good to go.

full pre-flight read

This is a substantial new feature spanning fleet enumeration, parallel shallow clones into the warm gitcache, a new multi-repo container layout step, and an interactive attach surface - and it carries an unresolved architectural fork (extend container up --repos vs a talk-only cache path) that the author only marks as "leaning," not decided. The harder unattended risk is verification: talk is fundamentally interactive (drop into a live session with N trees mounted), so confirming it actually works end-to-end is exactly the kind of thing a fire-and-forget container can't exercise - I'd be landing a feature whose acceptance criteria ("drops into an interactive session") can't be self-tested. The cardinality work also reaches into container up, a load-bearing primitive that work depends on, so a wrong layout choice is costly to back out after merging to main. The design lean is real but soft, and the interactive surface plus shared-primitive blast radius is more than I'd commit to main with no human in the loop.

NO-GO: unresolved container-layout architecture fork (only "leaning"), an interactive verb whose acceptance can't be self-verified in a detached container, and changes reaching into the shared container up primitive that work depends on.


Posted automatically by ward agent claude headless pre-flight (ward#147, ward#149).

— Claude (she/her), via ward agent

### 🛫 ward pre-flight: NO-GO `ward agent claude headless` ran a pre-flight feasibility read on this issue before detaching a fire-and-forget run, and the agent judged it **NO-GO** - it should not be carried unattended until a human weighs in. > unresolved container-layout architecture fork (only "leaning"), an interactive verb whose acceptance can't be self-verified in a detached container, and changes reaching into the shared `container up` primitive that `work` depends on. No container was launched. Review the issue (clarify the scope, resolve the unknown, or split it), then re-dispatch - `ward agent claude headless <ref> --no-preflight` skips this gate once you've decided it's good to go. <details><summary>full pre-flight read</summary> This is a substantial new feature spanning fleet enumeration, parallel shallow clones into the warm gitcache, a new multi-repo container layout step, and an interactive attach surface - and it carries an unresolved architectural fork (extend `container up --repos` vs a `talk`-only cache path) that the author only marks as "leaning," not decided. The harder unattended risk is verification: `talk` is fundamentally interactive (drop into a live session with N trees mounted), so confirming it actually works end-to-end is exactly the kind of thing a fire-and-forget container can't exercise - I'd be landing a feature whose acceptance criteria ("drops into an interactive session") can't be self-tested. The cardinality work also reaches into `container up`, a load-bearing primitive that `work` depends on, so a wrong layout choice is costly to back out after merging to main. The design lean is real but soft, and the interactive surface plus shared-primitive blast radius is more than I'd commit to main with no human in the loop. NO-GO: unresolved container-layout architecture fork (only "leaning"), an interactive verb whose acceptance can't be self-verified in a detached container, and changes reaching into the shared `container up` primitive that `work` depends on. </details> --- Posted automatically by `ward agent claude headless` pre-flight (ward#147, ward#149). <!-- ward-preflight-nogo --> <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Member

Re-triage (goose-triage method, claude-macos-kais-macbook-pro-2 as the judgment engine, 2026-06-24)

  • P2 - ward agent talk fleet-mounted sibling, well-described real feature solving the idle-checkout pain, kept.
  • consult - Big new entry point with a design surface, human-gated start.
<!-- goose-triage --> **Re-triage** (goose-triage method, claude-macos-kais-macbook-pro-2 as the judgment engine, 2026-06-24) - **P2** - ward agent talk fleet-mounted sibling, well-described real feature solving the idle-checkout pain, kept. - **consult** - Big new entry point with a design surface, human-gated start.
Author
Member

Re-triage (goose-triage method, claude-macos-kais-macbook-pro-2 as the judgment engine, 2026-06-24)

  • P2 - ward agent talk fleet-mounted sibling, well-described real feature solving the idle-checkout pain, kept.
  • consult - Big new entry point with a design surface, human-gated start.
<!-- goose-triage --> **Re-triage** (goose-triage method, claude-macos-kais-macbook-pro-2 as the judgment engine, 2026-06-24) - **P2** - ward agent talk fleet-mounted sibling, well-described real feature solving the idle-checkout pain, kept. - **consult** - Big new entry point with a design surface, human-gated start.
coilysiren added
P3
and removed
P2
labels 2026-07-01 04:29:52 +00:00
Author
Member

Flipped headless: ward agent talk is well-specified in the body (interactive, fleet-mounted, read-leaning sibling of work); implement as specced. Recorded by Claude Code (Fable) during the 2026-07-01 ward launch triage session with Kai.

Flipped headless: ward agent talk is well-specified in the body (interactive, fleet-mounted, read-leaning sibling of work); implement as specced. Recorded by Claude Code (Fable) during the 2026-07-01 ward launch triage session with Kai.
Author
Member

🔎 ward agent advisor

ward agent advisor --driver claude ran a one-shot standard research pass on this question:

Turn ward#219 (ward agent talk - interactive fleet-mounted read sibling to ward agent work) into a concrete implementation spec and post it as a comment. Read-only, no code. The issue already has the motivation + shape (interactive, --all-repos default, read-leaning, reuse the ephemeral-container / fresh-clone-inside / gitcache / reap machinery). Specify: how talk reuses the ward agent work container lifecycle vs what differs (interactive TTY, read-leaning default, multi-repo mount), where it slots in the role roster (agent.go roles engineer|director|advisor - is talk a new role or a generalization of advisor freeform interactive mode, see docs/agent.md), the --all-repos fleet-mount mechanics, and teardown/reap. Phased breakdown + recommendation. End noting it is a plan to verify. Repo voice rules (no em-dashes, no semicolons in prose, name the actor).


Implementation spec: ward agent talk (ward#219)

This is a plan to verify, not a landed design. It is written from a shallow read of cmd/ward/agent.go, agent_advisor.go, agent_director.go, agent_route.go, container_compute.go, the entrypoint, and docs/agent-*.md / docs/container-*.md. The single biggest finding reshapes the issue's own framing, so read the What already exists section first.


1. What already exists (the reuse map)

The issue treats multi-repo-into-one-container as "the one piece of new engineering." It is mostly already built. Three subsystems land almost the whole feature.

  • Multi-tree layout into one container - the substrate mechanism. Every ward container run already lays down N repos from the warm ward-gitcache into one container. preclone-repos.txt is a fixed manifest of ~8 cross-cutting repos; the entrypoint warms each as a bare mirror (TTL-gated fetch, flock-serialised, WARD_SUBSTRATE_TTL default 600s) and drops a working copy under /substrate/<name> (container_compute.go containerSubstrateDest, parseSubstrateManifest; docs/container-substrate.md). This is the "lay down N trees from cache" step the issue asks for. It exists, it is flock-safe, it hydrates image-tier repos from a baked seed with no network. talk needs a bigger, dispatch-computed manifest, not a new layout engine.
  • Canonical Forgejo enumeration. cl.listOwnerRepos(ctx, owner) already enumerates an org's repos from canonical Forgejo, used by the director (expandOrgScopes in agent_director.go) and the engineer route survey (surveyRepoCatalog in agent_route.go). --all-repos is the union of listOwnerRepos across r.primaryOrgs(), de-duped with the existing mergeScopeRepos. The "enumerate from canonical Forgejo, never the local checkout" requirement falls out of code that already ships. Nothing new.
  • Interactive, read-only, reaper-swept attached session. The advisor freeform mode (ward#388) already drops a human into a live claude <seed> session inside a fresh ephemeral container, seeded by interactivePrompt, read-only, with a --oneshot / no-TTY fallback to claude -p (agent_advisor.go:110, container_compute.go:563 WARD_ASK). talk's "interactive attach" is this exact path.

So talk is not new machinery. It is advisor-freeform-interactive, with the substrate manifest swapped for the enumerated fleet and mounted as the primary read surface.


2. Roster placement - the one genuine fork

Roles today are engineer | director | advisor (docs/agent.md, agentCommand() in agent.go). The roster was deliberately pruned - ward#353 folded architect/explore/sandbox away into the director surface. Adding a fourth noun to a curated roster is a real decision, so name it explicitly rather than defaulting.

Two shapes, and the codebase pulls toward the first:

  • (A) New thin role talk, implemented as a specialization of the advisor interactive path. agentTalkCommand() beside agentAdvisorCommand(), reusing interactivePrompt (retitled/re-seeded) and the advisor container plan, differing in: --all-repos default, a fleet manifest, /repos mount, and a cross-fleet-search seed. Recommended, because talk's default shape (read across the whole fleet, no single context repo) is genuinely distinct from advisor (one context repo, answer-this-question), and burying it as a flag hides a headline capability behind advisor --all-repos --interactive.
  • (B) A flag on advisor. warded advisor --all-repos in interactive mode. Smaller surface, no new roster noun, but it overloads advisor's "one context repo" mental model and makes the fleet-hunt use case undiscoverable.

Recommendation: (A), a new role that is a thin wrapper over advisor's interactive internals - the same relationship engineer freeform has to its seeded-container path. This is the one spot where Kai should confirm the roster grows before code lands.


3. Container lifecycle: reuse vs differs

Concern ward agent work/engineer ward agent talk
Ephemeral container, one per run reuse reuse
Image, entrypoint, gitcache warm-loop, flock reuse reuse
Attach shape detached only (ward#356) interactive TTY (reuse advisor ward#388 path); --oneshot/no-TTY falls back to claude -p
Trees in container one target under /workspace/<repo> (+ --repo grants), read-only substrate under /substrate N fleet trees under /repos/<org>/<name>, read-leaning
Seed carry-this-issue-to-merge (agentSeedPrompt) search-across-the-mounted-trees (new seed, see §5)
Push posture writable origin, pushes to main read-only (WARD_READONLY=1, no-push origin) - see §6
Pre-flight GO/NO-GO yes (runPreflight) skip - there is no single issue to gate; talk is interactive and human-driven

Everything above the attach/seed/push rows is untouched reuse.


4. The cardinality piece is a manifest change, not a clone change

The issue's open decision - "extend container up --repos" vs "talk-only path" - is moot on two counts. First, ward container up was retired (ward#263); there is no container up surface to extend. Second, the N-tree layout engine already exists as the substrate warm-loop. So the actual seam is narrower and better:

Generalize the substrate manifest from a fixed embedded file to a dispatch-computed one. Concretely:

  • At dispatch, talk enumerates the fleet (listOwnerRepos across primaryOrgs()), builds an in-memory manifest in the same owner/name tier format parseSubstrateManifest already reads (tier derives from org: coilyco-flight-deck/coilysiren -> image, coilyco-bridge -> cache, matching the existing public/private split).
  • Pass it to the container the same way the static manifest flows today (via a WARD_SUBSTRATE_MANIFEST-shaped path or an inline env), and point WARD_SUBSTRATE_DEST (or a parallel var) at /repos, laid out /repos/<org>/<name> as the issue specifies.
  • The entrypoint warm-loop, TTL gate, flock, and baked-seed hydration all work unchanged. "Second run is fast (warm cache)" is free - it is the same gitcache the substrate loop already reuses.

This is reusable in exactly the way the issue wanted --repos to be (a future work could take a computed manifest too), without reviving a retired verb.


5. --all-repos fleet-mount mechanics

  • Default --all-repos. With no repo arg, talk mounts the whole enumerated fleet. ward agent talk <repo> narrows to a single tree (still under /repos/<org>/<name> for a uniform seed).
  • Enumeration source. listOwnerRepos per primary org, unioned via mergeScopeRepos. Same-answer-on-every-machine falls out because the source is canonical Forgejo, never the cwd. Depth-1 shallow is the natural default (the issue asks for no history); note that the substrate loop mirrors full today, so confirm whether a shallow-mirror path exists or must be added - this is a real sub-task, not free.
  • Seed. A new talkPrompt(...) in the advisor family: read-only, conversational, and explicitly oriented to search across /repos/<org>/<name> ("you have the whole fleet mounted read-only; grep and read across trees to answer detail-hunting questions; you are not carrying anything to merge"). Model it on interactivePrompt plus the surface-session read-only block (ward#293).
  • Scale caveat to surface, not hide. ~25 shallow clones on a cold volume is a real first-run cost. log() the count and cold-vs-warm state so a slow first run reads as expected, not hung. WARD_SUBSTRATE_SKIP-style escape should still work per the existing loop.

6. Teardown / reap - the real tension, and the codebase-consistent answer

The issue says "land if clean, else salvage branch + filed issue." That reuses the reaper's single-target contract ($WARD_REAP_WORK, one closes #N), but talk has N trees and no canonical target issue. "Land if clean" across N repos reintroduces exactly the cross-repo push failure mode the codebase already warns against (grantedRepoDoneClause, ward#291: a secondary push silently rejected while the primary succeeds).

The established, safer pattern is the director read-only surface (docs/agent-surface.md): WARD_READONLY=1 short-circuits the reaper's push, origin push URL is stripped to no-push://, a pre-push hook walls it, and crucially the doctrine (ward#374) says prefer a sibling dispatch over in-session writes - delegable edits go out as warded engineer #N against the one specific repo, which lands a durable artifact and sidesteps the N-tree push problem entirely.

Recommendation: talk adopts the read-only surface posture, not the land-if-clean engineer posture. Trees mount read-only; a change talk wants made gets dispatched as an engineer run against that repo (talk is interactive, so a human is present to approve). Teardown then reduces to the surface session's existing behavior: reaper short-circuits, nothing pushes, container reaped on exit, only the shared gitcache persists. This satisfies every acceptance bullet ("exit leaves no idle checkout") without inventing N-target reap semantics. The issue's original "land if clean" is the alternative to weigh, but it is the more failure-prone path and contradicts the read-leaning goal.


7. Phased breakdown

  1. Fleet enumeration -> manifest. Add a helper that unions listOwnerRepos across primaryOrgs() and emits a parseSubstrateManifest-compatible manifest (tier by org). Unit-test the org->tier mapping and de-dup. No container work yet. Unblocks everything below.
  2. Dynamic substrate manifest + /repos dest. Let the container plan accept a computed manifest and an alternate dest, mounted /repos/<org>/<name>. Verify the entrypoint warm-loop, TTL, flock, and baked-seed hydration work against the computed set. Add a shallow-mirror path if one does not already exist. Depends on phase 1.
  3. The talk role. agentTalkCommand() + talkPrompt(), reusing the advisor interactive container plan (WARD_ASK/claude -p fallback preserved), --all-repos default, single-repo narrowing, --print. Wire into agentCommand().Commands and the roster generator (agent_roster.go). Depends on phases 1-2.
  4. Read-only teardown posture. Set WARD_READONLY=1 + no-push origin for talk, confirm the reaper short-circuits with N trees, seed the dispatch-a-sibling-for-writes doctrine. Depends on phase 3.
  5. Docs + FEATURES. docs/agent-talk.md, roster/agent.md/agent-subcommands.md updates, docs/FEATURES.md entry (house rule: same commit as the feature).

8. Bottom line

talk is ~80% assembly of shipped parts: substrate warm-loop (N-tree layout), listOwnerRepos (canonical enumeration), advisor interactive path (attach + read-only + reaper), director surface posture (read-only teardown). The genuinely new code is (a) fleet-enumeration-to-manifest, (b) making the substrate manifest dispatch-computed with a /repos dest, and possibly (c) a shallow-mirror path. The issue's stated "one piece of new engineering" (cardinality) is smaller than framed, and its two open options are both superseded by the substrate seam.

Two decisions for Kai before code lands: (1) roster grows by one noun (talk) vs a flag on advisor - recommend the new role; (2) teardown is read-only-surface + dispatch-for-writes vs land-if-clean-per-tree - recommend read-only-surface.

To verify against live code before building: that listOwnerRepos unions cleanly across all primary orgs with the right visibility, that the substrate entrypoint loop can be repointed at a computed manifest and /repos without disturbing the existing /substrate warm, whether a depth-1 shallow-mirror path already exists or must be added, and that WARD_READONLY short-circuits the reaper with no single WARD_REAP_WORK target set.


Researched and posted automatically by ward agent advisor --driver claude (ward#179). This is one-shot research, not a carried change - verify before acting on it.

— Claude (she/her), via ward agent

### 🔎 ward agent advisor `ward agent advisor --driver claude` ran a one-shot **standard** research pass on this question: > Turn ward#219 (ward agent talk - interactive fleet-mounted read sibling to ward agent work) into a concrete implementation spec and post it as a comment. Read-only, no code. The issue already has the motivation + shape (interactive, --all-repos default, read-leaning, reuse the ephemeral-container / fresh-clone-inside / gitcache / reap machinery). Specify: how talk reuses the ward agent work container lifecycle vs what differs (interactive TTY, read-leaning default, multi-repo mount), where it slots in the role roster (agent.go roles engineer|director|advisor - is talk a new role or a generalization of advisor freeform interactive mode, see docs/agent.md), the --all-repos fleet-mount mechanics, and teardown/reap. Phased breakdown + recommendation. End noting it is a plan to verify. Repo voice rules (no em-dashes, no semicolons in prose, name the actor). --- ## Implementation spec: `ward agent talk` (ward#219) This is a plan to verify, not a landed design. It is written from a shallow read of `cmd/ward/agent.go`, `agent_advisor.go`, `agent_director.go`, `agent_route.go`, `container_compute.go`, the entrypoint, and `docs/agent-*.md` / `docs/container-*.md`. The single biggest finding reshapes the issue's own framing, so read the **What already exists** section first. --- ### 1. What already exists (the reuse map) The issue treats multi-repo-into-one-container as "the one piece of new engineering." It is mostly already built. Three subsystems land almost the whole feature. * **Multi-tree layout into one container - the substrate mechanism.** Every `ward container` run already lays down N repos from the warm `ward-gitcache` into one container. `preclone-repos.txt` is a fixed manifest of ~8 cross-cutting repos; the entrypoint warms each as a bare mirror (TTL-gated fetch, `flock`-serialised, `WARD_SUBSTRATE_TTL` default 600s) and drops a working copy under `/substrate/<name>` (`container_compute.go` `containerSubstrateDest`, `parseSubstrateManifest`; `docs/container-substrate.md`). This **is** the "lay down N trees from cache" step the issue asks for. It exists, it is `flock`-safe, it hydrates image-tier repos from a baked seed with no network. talk needs a **bigger, dispatch-computed manifest**, not a new layout engine. * **Canonical Forgejo enumeration.** `cl.listOwnerRepos(ctx, owner)` already enumerates an org's repos from canonical Forgejo, used by the director (`expandOrgScopes` in `agent_director.go`) and the engineer route survey (`surveyRepoCatalog` in `agent_route.go`). `--all-repos` is the union of `listOwnerRepos` across `r.primaryOrgs()`, de-duped with the existing `mergeScopeRepos`. The "enumerate from canonical Forgejo, never the local checkout" requirement falls out of code that already ships. Nothing new. * **Interactive, read-only, reaper-swept attached session.** The advisor freeform mode (ward#388) already drops a human into a live `claude <seed>` session inside a fresh ephemeral container, seeded by `interactivePrompt`, read-only, with a `--oneshot` / no-TTY fallback to `claude -p` (`agent_advisor.go:110`, `container_compute.go:563` `WARD_ASK`). talk's "interactive attach" is this exact path. So talk is not new machinery. It is **advisor-freeform-interactive, with the substrate manifest swapped for the enumerated fleet and mounted as the primary read surface.** --- ### 2. Roster placement - the one genuine fork Roles today are `engineer | director | advisor` (`docs/agent.md`, `agentCommand()` in `agent.go`). The roster was deliberately **pruned** - ward#353 folded `architect`/`explore`/`sandbox` away into the director surface. Adding a fourth noun to a curated roster is a real decision, so name it explicitly rather than defaulting. Two shapes, and the codebase pulls toward the first: * **(A) New thin role `talk`, implemented as a specialization of the advisor interactive path.** `agentTalkCommand()` beside `agentAdvisorCommand()`, reusing `interactivePrompt` (retitled/re-seeded) and the advisor container plan, differing in: `--all-repos` default, a fleet manifest, `/repos` mount, and a cross-fleet-search seed. Recommended, because talk's default shape (read across the whole fleet, no single context repo) is genuinely distinct from advisor (one context repo, answer-this-question), and burying it as a flag hides a headline capability behind `advisor --all-repos --interactive`. * **(B) A flag on advisor.** `warded advisor --all-repos` in interactive mode. Smaller surface, no new roster noun, but it overloads advisor's "one context repo" mental model and makes the fleet-hunt use case undiscoverable. **Recommendation: (A), a new role that is a thin wrapper over advisor's interactive internals** - the same relationship `engineer` freeform has to its seeded-container path. This is the one spot where Kai should confirm the roster grows before code lands. --- ### 3. Container lifecycle: reuse vs differs | Concern | `ward agent work`/engineer | `ward agent talk` | | --- | --- | --- | | Ephemeral container, one per run | reuse | reuse | | Image, entrypoint, gitcache warm-loop, `flock` | reuse | reuse | | Attach shape | detached only (ward#356) | **interactive TTY** (reuse advisor ward#388 path); `--oneshot`/no-TTY falls back to `claude -p` | | Trees in container | one target under `/workspace/<repo>` (+ `--repo` grants), read-only substrate under `/substrate` | **N fleet trees under `/repos/<org>/<name>`**, read-leaning | | Seed | carry-this-issue-to-merge (`agentSeedPrompt`) | **search-across-the-mounted-trees** (new seed, see §5) | | Push posture | writable `origin`, pushes to `main` | **read-only** (`WARD_READONLY=1`, no-push origin) - see §6 | | Pre-flight GO/NO-GO | yes (`runPreflight`) | **skip** - there is no single issue to gate; talk is interactive and human-driven | Everything above the attach/seed/push rows is untouched reuse. --- ### 4. The cardinality piece is a manifest change, not a clone change The issue's open decision - "extend `container up --repos`" vs "talk-only path" - is moot on two counts. First, `ward container up` was **retired** (ward#263); there is no `container up` surface to extend. Second, the N-tree layout engine already exists as the substrate warm-loop. So the actual seam is narrower and better: **Generalize the substrate manifest from a fixed embedded file to a dispatch-computed one.** Concretely: * At dispatch, talk enumerates the fleet (`listOwnerRepos` across `primaryOrgs()`), builds an in-memory manifest in the same `owner/name tier` format `parseSubstrateManifest` already reads (tier derives from org: `coilyco-flight-deck`/`coilysiren` -> `image`, `coilyco-bridge` -> `cache`, matching the existing public/private split). * Pass it to the container the same way the static manifest flows today (via a `WARD_SUBSTRATE_MANIFEST`-shaped path or an inline env), and point `WARD_SUBSTRATE_DEST` (or a parallel var) at `/repos`, laid out `/repos/<org>/<name>` as the issue specifies. * The entrypoint warm-loop, TTL gate, `flock`, and baked-seed hydration all work unchanged. "Second run is fast (warm cache)" is free - it is the same gitcache the substrate loop already reuses. This is reusable in exactly the way the issue wanted `--repos` to be (a future `work` could take a computed manifest too), without reviving a retired verb. --- ### 5. `--all-repos` fleet-mount mechanics * **Default `--all-repos`.** With no repo arg, talk mounts the whole enumerated fleet. `ward agent talk <repo>` narrows to a single tree (still under `/repos/<org>/<name>` for a uniform seed). * **Enumeration source.** `listOwnerRepos` per primary org, unioned via `mergeScopeRepos`. Same-answer-on-every-machine falls out because the source is canonical Forgejo, never the cwd. Depth-1 shallow is the natural default (the issue asks for no history); note that the substrate loop mirrors full today, so confirm whether a shallow-mirror path exists or must be added - this is a real sub-task, not free. * **Seed.** A new `talkPrompt(...)` in the advisor family: read-only, conversational, and explicitly **oriented to search across `/repos/<org>/<name>`** ("you have the whole fleet mounted read-only; grep and read across trees to answer detail-hunting questions; you are not carrying anything to merge"). Model it on `interactivePrompt` plus the surface-session read-only block (ward#293). * **Scale caveat to surface, not hide.** ~25 shallow clones on a cold volume is a real first-run cost. `log()` the count and cold-vs-warm state so a slow first run reads as expected, not hung. `WARD_SUBSTRATE_SKIP`-style escape should still work per the existing loop. --- ### 6. Teardown / reap - the real tension, and the codebase-consistent answer The issue says "land if clean, else salvage branch + filed issue." That reuses the reaper's **single-target** contract (`$WARD_REAP_WORK`, one `closes #N`), but talk has **N trees and no canonical target issue**. "Land if clean" across N repos reintroduces exactly the cross-repo push failure mode the codebase already warns against (`grantedRepoDoneClause`, ward#291: a secondary push silently rejected while the primary succeeds). The established, safer pattern is the **director read-only surface** (`docs/agent-surface.md`): `WARD_READONLY=1` short-circuits the reaper's push, `origin` push URL is stripped to `no-push://`, a `pre-push` hook walls it, and crucially the doctrine (ward#374) says **prefer a sibling dispatch over in-session writes** - delegable edits go out as `warded engineer #N` against the one specific repo, which lands a durable artifact and sidesteps the N-tree push problem entirely. **Recommendation: talk adopts the read-only surface posture, not the land-if-clean engineer posture.** Trees mount read-only; a change talk wants made gets **dispatched as an engineer run against that repo** (talk is interactive, so a human is present to approve). Teardown then reduces to the surface session's existing behavior: reaper short-circuits, nothing pushes, container reaped on exit, only the shared gitcache persists. This satisfies every acceptance bullet ("exit leaves no idle checkout") without inventing N-target reap semantics. The issue's original "land if clean" is the alternative to weigh, but it is the more failure-prone path and contradicts the read-leaning goal. --- ### 7. Phased breakdown 1. **Fleet enumeration -> manifest.** Add a helper that unions `listOwnerRepos` across `primaryOrgs()` and emits a `parseSubstrateManifest`-compatible manifest (tier by org). Unit-test the org->tier mapping and de-dup. No container work yet. Unblocks everything below. 2. **Dynamic substrate manifest + `/repos` dest.** Let the container plan accept a computed manifest and an alternate dest, mounted `/repos/<org>/<name>`. Verify the entrypoint warm-loop, TTL, `flock`, and baked-seed hydration work against the computed set. Add a shallow-mirror path if one does not already exist. Depends on phase 1. 3. **The `talk` role.** `agentTalkCommand()` + `talkPrompt()`, reusing the advisor interactive container plan (`WARD_ASK`/`claude -p` fallback preserved), `--all-repos` default, single-repo narrowing, `--print`. Wire into `agentCommand().Commands` and the roster generator (`agent_roster.go`). Depends on phases 1-2. 4. **Read-only teardown posture.** Set `WARD_READONLY=1` + no-push origin for talk, confirm the reaper short-circuits with N trees, seed the dispatch-a-sibling-for-writes doctrine. Depends on phase 3. 5. **Docs + FEATURES.** `docs/agent-talk.md`, roster/`agent.md`/`agent-subcommands.md` updates, `docs/FEATURES.md` entry (house rule: same commit as the feature). --- ### 8. Bottom line talk is **~80% assembly of shipped parts**: substrate warm-loop (N-tree layout), `listOwnerRepos` (canonical enumeration), advisor interactive path (attach + read-only + reaper), director surface posture (read-only teardown). The genuinely new code is (a) fleet-enumeration-to-manifest, (b) making the substrate manifest dispatch-computed with a `/repos` dest, and possibly (c) a shallow-mirror path. The issue's stated "one piece of new engineering" (cardinality) is smaller than framed, and its two open options are both superseded by the substrate seam. **Two decisions for Kai before code lands:** (1) roster grows by one noun (`talk`) vs a flag on advisor - recommend the new role; (2) teardown is read-only-surface + dispatch-for-writes vs land-if-clean-per-tree - recommend read-only-surface. **To verify against live code before building:** that `listOwnerRepos` unions cleanly across all primary orgs with the right visibility, that the substrate entrypoint loop can be repointed at a computed manifest and `/repos` without disturbing the existing `/substrate` warm, whether a depth-1 shallow-mirror path already exists or must be added, and that `WARD_READONLY` short-circuits the reaper with no single `WARD_REAP_WORK` target set. --- Researched and posted automatically by `ward agent advisor --driver claude` (ward#179). This is one-shot research, not a carried change - verify before acting on it. <!-- ward-agent-reply --> <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-ward-219 on host KAI-DESKTOP-TOWER is carrying this issue (reserved 2026-07-06T18:12:08Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

run seed context — what this run is carrying (ward#609)
  • Resolved: coilyco-flight-deck/ward#219 · branch issue-219 · driver claude · workflow direct-main
  • Run: engineer-claude-ward-219 · ward v0.414.0 · dispatched 2026-07-06T18:12:08Z
  • Comment thread: 4 included in the pre-flight read, 1 stripped (ward's own automated comments).

Issue body as seeded:

## `ward agent talk` - interactive, fleet-mounted sibling to `ward agent work`

### Motivation

Detail-hunting across the fleet ("which repo has X", "what does that config default to") is a common need, but the only way to have that conversation today is to keep ~25 repos checked out across ~5 dev machines and grep locally. That idle-checkout state is the actual pain - not the bytes, the tracking and cross-machine drift. `ward agent work` already solved the hard part of the lifecycle (ephemeral container, fresh-clone-inside, warm gitcache, reap-on-teardown, canonical-clone-so-identical-everywhere). What's missing is an interactive, read-leaning, multi-repo entry point that reuses that machinery so no repo has to idle in a workspace.

### Shape

`ward agent talk` - interactive sibling to `work`:

- Defaults to `--all-repos` (the common case is hunting across the fleet, not deep in one repo).
- Enumerates the fleet from **canonical Forgejo**, never from whatever happens to be checked out on the current box, so the same verb gives the same answer on every machine.
- Shallow-clones (`--depth 1`, no history) all enumerated repos into the **existing warm gitcache** volume, parallel fetch-refresh on start.
- Serves N working trees into **one** ephemeral interactive container at `/repos/<org>/<name>`.
- Agent is seeded to **search across** the mounted trees, not to carry a feature to merge.
- Teardown rides existing `reap`: read-leaning by default, so a dirty tree is the exception - land if clean, else salvage branch + filed issue.

### The one piece of new engineering: cardinality

`ward container` is architecturally **one repo per container** (fresh-clone the single target inside). `talk --all-repos` needs N trees in one container. This is **not** new cloning logic - the warm gitcache already holds per-repo clones - it is a new **layout** step: lay down N trees from cache into `/repos/<org>/<name>`.

Open decision: where that layout step lives.

- **Extend `container up` 

… (truncated to 2000 chars; full body is on this issue)

Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.414.0).

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-ward-219` on host `KAI-DESKTOP-TOWER` is carrying this issue (reserved 2026-07-06T18:12:08Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh** — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <details><summary>run seed context — what this run is carrying (ward#609)</summary> - **Resolved:** `coilyco-flight-deck/ward#219` · branch `issue-219` · driver `claude` · workflow `direct-main` - **Run:** `engineer-claude-ward-219` · ward `v0.414.0` · dispatched `2026-07-06T18:12:08Z` - **Comment thread:** 4 included in the pre-flight read, 1 stripped (ward's own automated comments). - included: @coilyco-ops (2026-06-24T17:27:23Z), @coilyco-ops (2026-06-24T17:29:43Z), @coilyco-ops (2026-07-02T00:21:28Z), @coilyco-ops (2026-07-02T17:48:21Z) - stripped: @coilyco-ops (2026-06-19T07:35:10Z) **Issue body as seeded:** ``` ## `ward agent talk` - interactive, fleet-mounted sibling to `ward agent work` ### Motivation Detail-hunting across the fleet ("which repo has X", "what does that config default to") is a common need, but the only way to have that conversation today is to keep ~25 repos checked out across ~5 dev machines and grep locally. That idle-checkout state is the actual pain - not the bytes, the tracking and cross-machine drift. `ward agent work` already solved the hard part of the lifecycle (ephemeral container, fresh-clone-inside, warm gitcache, reap-on-teardown, canonical-clone-so-identical-everywhere). What's missing is an interactive, read-leaning, multi-repo entry point that reuses that machinery so no repo has to idle in a workspace. ### Shape `ward agent talk` - interactive sibling to `work`: - Defaults to `--all-repos` (the common case is hunting across the fleet, not deep in one repo). - Enumerates the fleet from **canonical Forgejo**, never from whatever happens to be checked out on the current box, so the same verb gives the same answer on every machine. - Shallow-clones (`--depth 1`, no history) all enumerated repos into the **existing warm gitcache** volume, parallel fetch-refresh on start. - Serves N working trees into **one** ephemeral interactive container at `/repos/<org>/<name>`. - Agent is seeded to **search across** the mounted trees, not to carry a feature to merge. - Teardown rides existing `reap`: read-leaning by default, so a dirty tree is the exception - land if clean, else salvage branch + filed issue. ### The one piece of new engineering: cardinality `ward container` is architecturally **one repo per container** (fresh-clone the single target inside). `talk --all-repos` needs N trees in one container. This is **not** new cloning logic - the warm gitcache already holds per-repo clones - it is a new **layout** step: lay down N trees from cache into `/repos/<org>/<name>`. Open decision: where that layout step lives. - **Extend `container up` ``` … (truncated to 2000 chars; full body is on this issue) Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.414.0). </details> <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Owner

This idea has been wrapped into "advisor"

This idea has been wrapped into "advisor"
Owner

Closing: superseded. 'ward agent talk' has been folded into the advisor role - per Kai's 2026-07-06 comment and the advisor design pass on this issue, talk is ~80% the existing advisor freeform-interactive path. NOTE: this issue still shows engineer reservations (18:12 and 18:37) - if a container is still carrying it on the host, stop it (docker container stop engineer-claude-ward-219); it should not land. Re-triage 2026-07-06.

Closing: superseded. 'ward agent talk' has been folded into the advisor role - per Kai's 2026-07-06 comment and the advisor design pass on this issue, talk is ~80% the existing advisor freeform-interactive path. NOTE: this issue still shows engineer reservations (18:12 and 18:37) - if a container is still carrying it on the host, stop it (docker container stop engineer-claude-ward-219); it should not land. Re-triage 2026-07-06.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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#219
No description provided.