Wire the automation-mode ceiling into ward's real dispatch path (ward agent / warded) #607

Closed
opened 2026-07-04 09:34:03 +00:00 by coilysiren · 2 comments
Owner

Follow-up to coilyco-flight-deck/agentic-os#246 (Phase 2).

The ceiling gate landed in cli-guard v0.72.0 inside resolveDispatchIssue - the chokepoint every Dispatcher surface (headless/interactive/consult/cascade/ci) flows through - and ward's forgejo + GitHub fetchers now populate dispatch.Issue.Labels so it can read the mode label. That fully implements the issue as written.

Gap found during implementation: ward does not construct a cli-guard Dispatcher or mount dispatch.Command(). The only consumer of the Dispatcher (and thus of resolveDispatchIssue) is cli-guard's examples/treebuilders. Ward's real auto-dispatch is ward agent engineer (public warded <ref>), which resolves the issue via its own resolveAgentWork -> fetchIssue -> runPreflight -> detached launch, bypassing resolveDispatchIssue entirely. So today the ceiling does not fire on the fleet's actual headless auto-burndown path - warded will still dispatch a consult/unlabeled issue.

Decision needed (why this is a separate issue):

  1. Enforce the ceiling on ward's ward agent path. The natural mapping is engineer -> headless ceiling (the code-landing auto-queue), refusing an engineer dispatch on a non-headless issue. director/advisor are supervisory/consultative and arguably ungated (director is the one that assigns modes). This role->level mapping is a product decision the parent issue did not specify.
  2. Where to enforce: ward's runPreflight/resolveAgentWork is the natural seam. issueModeLevel is currently unexported in cli-guard; it would need exporting (or reimplementing the tiny label->level map in ward).
  3. Alternative: mount cli-guard's Dispatcher as a real ward dispatch <surface> command (which the #246 acceptance's "ward dispatch headless" wording assumes exists) - but that adds a surface parallel to ward agent, likely unwanted.

The labels + the Dispatcher-side gate are already live and correct; this issue is just about pointing ward's own dispatch path at them.

Refs coilyco-flight-deck/agentic-os#246

Follow-up to coilyco-flight-deck/agentic-os#246 (Phase 2). The ceiling gate landed in cli-guard v0.72.0 inside `resolveDispatchIssue` - the chokepoint every **Dispatcher** surface (headless/interactive/consult/cascade/ci) flows through - and ward's forgejo + GitHub fetchers now populate `dispatch.Issue.Labels` so it can read the mode label. That fully implements the issue as written. **Gap found during implementation:** ward does **not** construct a cli-guard `Dispatcher` or mount `dispatch.Command()`. The only consumer of the Dispatcher (and thus of `resolveDispatchIssue`) is cli-guard's `examples/treebuilders`. Ward's real auto-dispatch is `ward agent engineer` (public `warded <ref>`), which resolves the issue via its own `resolveAgentWork` -> `fetchIssue` -> `runPreflight` -> detached launch, bypassing `resolveDispatchIssue` entirely. So today the ceiling does not fire on the fleet's actual headless auto-burndown path - `warded` will still dispatch a `consult`/unlabeled issue. **Decision needed (why this is a separate issue):** 1. Enforce the ceiling on ward's `ward agent` path. The natural mapping is engineer -> headless ceiling (the code-landing auto-queue), refusing an engineer dispatch on a non-`headless` issue. director/advisor are supervisory/consultative and arguably ungated (director is the one that *assigns* modes). This role->level mapping is a product decision the parent issue did not specify. 2. Where to enforce: ward's `runPreflight`/`resolveAgentWork` is the natural seam. `issueModeLevel` is currently unexported in cli-guard; it would need exporting (or reimplementing the tiny label->level map in ward). 3. Alternative: mount cli-guard's `Dispatcher` as a real `ward dispatch <surface>` command (which the #246 acceptance's "ward dispatch headless" wording assumes exists) - but that adds a surface parallel to `ward agent`, likely unwanted. The labels + the Dispatcher-side gate are already live and correct; this issue is just about pointing ward's own dispatch path at them. Refs coilyco-flight-deck/agentic-os#246
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-ward-607 on host KAI-DESKTOP-TOWER is carrying this issue (reserved 2026-07-04T17:26:43Z). 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).

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-ward-607` on host `KAI-DESKTOP-TOWER` is carrying this issue (reserved 2026-07-04T17:26:43Z). 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). <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Owner

WARD-OUTCOME: done - the engineer role now refuses a dispatch on any non-headless issue, enforced in resolveAgentWork (Coded exit 7), landed on main in 062b3e9.

This one was cleaner than I expected. The gap the parent issue flagged was real and exactly as described: cli-guard's ceiling only ever sat on the Dispatcher, and ward's actual path (warded -> resolveAgentWork -> preflight -> launch) never touched it. The interesting decision was where to enforce. The issue named runPreflight as a candidate seam, but that would have been the wrong one - the preflight is skipped without a TTY, which is precisely the director's headless auto-burndown path this gate most needs to cover. resolveAgentWork runs on every dispatch, so that's where it went.

I reimplemented cli-guard's tiny label->level map in ward rather than exporting issueModeLevel - partly the product-boundary call the issue raised, partly a hard constraint: cli-guard isn't a granted repo in this container, so I couldn't push it anyway. The map matches the director's existing backlogModeOrder vocabulary and faithfully copies cli-guard's fail-closed-to-consult / most-restrictive-wins semantics.

Confidence is high. The director only ever auto-dispatches from the headless lane, so this can't regress its normal flow - a mislabeled issue that somehow reached dispatch now fails terminally (via isDispatchDecline) instead of requeueing forever. What I could NOT do is exercise the refusal against a live Forgejo issue end to end (no non-headless test issue handy, and the dispatch path wants Docker + a real fetch); the pure gate logic is unit-tested, but the wiring from a real Issue.Labels fetch through the refusal is verified only by reading, not running.

One rough edge worth a possible follow-up: the role->level map only gates engineer. If a future role lands that also lands code, whoever adds it has to remember to gate it in agentSurfaceCeiling - there's no compile-time nudge. Fine for now with three roles, but worth a note.

— Claude (she/her), via ward agent

WARD-OUTCOME: done - the engineer role now refuses a dispatch on any non-`headless` issue, enforced in `resolveAgentWork` (Coded exit 7), landed on `main` in 062b3e9. This one was cleaner than I expected. The gap the parent issue flagged was real and exactly as described: cli-guard's ceiling only ever sat on the Dispatcher, and ward's actual path (`warded` -> `resolveAgentWork` -> preflight -> launch) never touched it. The interesting decision was *where* to enforce. The issue named `runPreflight` as a candidate seam, but that would have been the wrong one - the preflight is skipped without a TTY, which is precisely the director's headless auto-burndown path this gate most needs to cover. `resolveAgentWork` runs on every dispatch, so that's where it went. I reimplemented cli-guard's tiny label->level map in ward rather than exporting `issueModeLevel` - partly the product-boundary call the issue raised, partly a hard constraint: cli-guard isn't a granted repo in this container, so I couldn't push it anyway. The map matches the director's existing `backlogModeOrder` vocabulary and faithfully copies cli-guard's fail-closed-to-consult / most-restrictive-wins semantics. Confidence is high. The director only ever auto-dispatches from the headless lane, so this can't regress its normal flow - a mislabeled issue that somehow reached dispatch now fails terminally (via `isDispatchDecline`) instead of requeueing forever. What I could NOT do is exercise the refusal against a live Forgejo issue end to end (no non-headless test issue handy, and the dispatch path wants Docker + a real fetch); the pure gate logic is unit-tested, but the wiring from a real `Issue.Labels` fetch through the refusal is verified only by reading, not running. One rough edge worth a possible follow-up: the role->level map only gates `engineer`. If a future role lands that also lands code, whoever adds it has to remember to gate it in `agentSurfaceCeiling` - there's no compile-time nudge. Fine for now with three roles, but worth a note. — Claude (she/her), via ward agent
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#607
No description provided.