warded dispatch fails with flag provided but not defined: -output on ward/ward-kdl version skew #298

Closed
opened 2026-06-25 03:53:39 +00:00 by coilyco-ops · 1 comment
Member

Symptom

A warded <issue-url> dispatch aborts before the container spins:

warded 'https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/293'
Incorrect Usage: flag provided but not defined: -output
ward: flag provided but not defined: -output
ward: ward agent headless --driver claude: resolve issue coilyco-flight-deck/ward#293: forgejo: get issue coilyco-flight-deck/ward#293: exit status 1

Note warded <url> --print resolves the issue fine (it prints the title); only the live (non-print) headless resolve fails. The wrapped exit status 1 is opaque - the real cause is the Incorrect Usage: flag provided but not defined: -output line above it.

Root cause

ward agent resolves the issue through fetchForgejoIssue -> forgejoClient.getIssue (cmd/ward/forgejo_ops.go:89), which shells back to:

ward ops forgejo issue get <owner> <repo> <n> --output json

That issue get verb is provided by the separate ward-kdl binary (the ops guardfile runtime), confirmed by ward ops forgejo describe -> ## ward-kdl ops forgejo issue get. The --output flag on issue get is a ward-kdl-side definition.

When the installed ward binary (which passes --output json) is newer than the installed ward-kdl (whose issue get predates the --output flag), ward-kdl rejects the flag with the urfave/cli flag provided but not defined: -output usage error. So the failure is a version skew between ward and ward-kdl on the issue get --output contract - an undeclared dependency between the two binaries.

It is currently NOT reproducible because brew has aligned the two at 0.138.0 (ward ops forgejo issue get --help now shows --output string). But the binaries can skew again - e.g. a partial ward upgrade, or ward-kdl lagging a ward release - and the failure blocks warded entirely with an opaque message.

Why --print survived but the real run did not

The --print resolve and the live resolve take different paths; only the live path hits getIssue -> ward ops forgejo issue get --output json, so only it tripped the stale ward-kdl flag.

Recommendations

  1. Declare + check the ward<->ward-kdl version contract. ward depends on ward-kdl providing issue get --output (and the other --output json verbs in forgejo_ops.go). A compatibility assertion (or a min-ward-kdl-version check) at the ops seam would fail with a clear message instead of a raw urfave usage error.
  2. Clearer diagnostic on a usage-error exit. When an ops forgejo subprocess exits with flag provided but not defined / Incorrect Usage, forgejoClient.run (or the resolve issue wrapper) should hint ward-kdl may be stale; run \brew upgrade` / `ward upgrade`rather than wrapping it as bareexit status 1`.
  3. Lockstep upgrade. Ensure ward upgrade / the brew formula bumps ward and ward-kdl together so a self-update can't leave them skewed.

Filed from a warded explore (ward#293) session that hit this while trying to dispatch #293.

## Symptom A `warded <issue-url>` dispatch aborts before the container spins: ``` warded 'https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/293' Incorrect Usage: flag provided but not defined: -output ward: flag provided but not defined: -output ward: ward agent headless --driver claude: resolve issue coilyco-flight-deck/ward#293: forgejo: get issue coilyco-flight-deck/ward#293: exit status 1 ``` Note `warded <url> --print` resolves the issue fine (it prints the title); only the live (non-print) headless resolve fails. The wrapped `exit status 1` is opaque - the real cause is the `Incorrect Usage: flag provided but not defined: -output` line above it. ## Root cause `ward agent` resolves the issue through `fetchForgejoIssue` -> `forgejoClient.getIssue` (`cmd/ward/forgejo_ops.go:89`), which shells back to: ``` ward ops forgejo issue get <owner> <repo> <n> --output json ``` That `issue get` verb is provided by the **separate `ward-kdl` binary** (the ops guardfile runtime), confirmed by `ward ops forgejo describe` -> `## ward-kdl ops forgejo issue get`. The `--output` flag on `issue get` is a ward-kdl-side definition. When the installed `ward` binary (which passes `--output json`) is **newer than** the installed `ward-kdl` (whose `issue get` predates the `--output` flag), ward-kdl rejects the flag with the urfave/cli `flag provided but not defined: -output` usage error. So the failure is a **version skew between `ward` and `ward-kdl`** on the `issue get --output` contract - an undeclared dependency between the two binaries. It is currently NOT reproducible because brew has aligned the two at 0.138.0 (`ward ops forgejo issue get --help` now shows `--output string`). But the binaries can skew again - e.g. a partial `ward upgrade`, or ward-kdl lagging a ward release - and the failure blocks `warded` entirely with an opaque message. ## Why `--print` survived but the real run did not The `--print` resolve and the live resolve take different paths; only the live path hits `getIssue` -> `ward ops forgejo issue get --output json`, so only it tripped the stale ward-kdl flag. ## Recommendations 1. **Declare + check the ward<->ward-kdl version contract.** ward depends on ward-kdl providing `issue get --output` (and the other `--output json` verbs in `forgejo_ops.go`). A compatibility assertion (or a min-ward-kdl-version check) at the ops seam would fail with a clear message instead of a raw urfave usage error. 2. **Clearer diagnostic on a usage-error exit.** When an `ops forgejo` subprocess exits with `flag provided but not defined` / `Incorrect Usage`, `forgejoClient.run` (or the `resolve issue` wrapper) should hint `ward-kdl may be stale; run \`brew upgrade\` / \`ward upgrade\`` rather than wrapping it as bare `exit status 1`. 3. **Lockstep upgrade.** Ensure `ward upgrade` / the brew formula bumps `ward` and `ward-kdl` together so a self-update can't leave them skewed. Filed from a `warded explore` (ward#293) session that hit this while trying to dispatch #293.
Author
Member

Closed by 0769ce2 - but the root cause here is wrong, recording the correction.

It is not a ward<->ward-kdl version skew. The ops forgejo runtime is mounted in-binary (ops.go:21), so there is no separate ward-kdl to skew against. Proof on a single aligned binary (both 0.142.0): ward ops forgejo issue get ... --output json succeeds, warded ops forgejo issue get ... --output json fails with the same flag provided but not defined: -output.

Real cause: a warded-invoked dispatch resolves os.Executable() to the warded symlink, and forgejoClient shells <exe> ops forgejo issue get ... --output json. Invoked as warded, that re-enters maybeRewriteWardedShim (warded <args> -> ward agent <args>), which has no ops surface and rejects --output. ward agent <ref> always worked because its basename is already ward. Fix: canonicalWardExe resolves the warded->ward symlink before the ops shell-back.

Closed by `0769ce2` - but the **root cause here is wrong**, recording the correction. It is **not** a ward<->ward-kdl version skew. The `ops forgejo` runtime is mounted **in-binary** (`ops.go:21`), so there is no separate ward-kdl to skew against. Proof on a single aligned binary (both 0.142.0): `ward ops forgejo issue get ... --output json` succeeds, `warded ops forgejo issue get ... --output json` fails with the same `flag provided but not defined: -output`. Real cause: a `warded`-invoked dispatch resolves `os.Executable()` to the **warded** symlink, and `forgejoClient` shells `<exe> ops forgejo issue get ... --output json`. Invoked as `warded`, that re-enters `maybeRewriteWardedShim` (`warded <args>` -> `ward agent <args>`), which has no `ops` surface and rejects `--output`. `ward agent <ref>` always worked because its basename is already `ward`. Fix: `canonicalWardExe` resolves the warded->ward symlink before the ops shell-back.
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#298
No description provided.