Skip to content

kew delegate — Assign-to-kew delegation watcher

kew delegate lets a human hand off a Linear issue to kew by assigning it to the kew seat (or adding a configured label). kew delegate watch runs a foreground loop that detects the assignment, claims the issue, and dispatches it as a one-node ephemeral epic — reusing the same claim → dispatch → close path as kew epic run.

Status — supervised use is safe; unattended use is close. As of #966 the delegation reservation lifecycle is sound (terminal outcomes release their reservation, so the fleet no longer deadlocks after [parallel] max_workers completions), and #971 hardens the refusal path for long unattended runs: kew comments once per refusal episode (not on every cooldown expiry), a failed unclaim leaves a reconcile retry surface instead of stranding the item, and an expired/revoked LINEAR_API_KEY terminates the watcher loudly (exit 2) rather than retrying forever. Running kew delegate watch under supervision is safe today; fully unattended operation is pending the remainder of the delegation-followups epic (driver-side reservation reconcile and epic-driver lock liveness). --dry-run is always safe.

Refusal-episode comment semantics (#971)

When a budget/preflight pre-check refuses a delegated issue, kew writes a terminal refusal row and posts one Linear comment naming the cap that bound and the knob to raise. While the issue stays over budget the watcher keeps re-surfacing it (its trigger label survives the unclaim), but kew comments only once per episode: a repeat refusal updates state silently until the episode ends (a successful dispatch or any non-refusal outcome resets it). Raise the named budget.*_usd limit and the next re-claim proceeds. See [delegation].refusal_cooldown_s.

Requirements

  • [tracker].type = "linear" — delegation is a Linear-only feature.
  • [delegation].assignee set to the kew Linear member name — the watcher resolves this to a member ID at startup and refuses to start if it is blank.
  • LINEAR_API_KEY environment variable set to a Linear personal API key.

kew delegate watch [--dry-run]

Usage: kew delegate watch [OPTIONS]

Options:
  --dry-run    Log 'would claim / would dispatch' without reserving or spending.
kew delegate watch                # foreground watcher — Ctrl-C to stop
kew delegate watch --dry-run      # preview mode: no claims, no spend

Starts a foreground polling loop (like kew ingest serve — no daemon, no autostart). On each tick it reads the local event cursor from the run store (every [delegation].fast_interval_s seconds) and polls Linear (at most once per [delegation].poll_interval_s seconds) to find issues newly assigned to the kew member that carry the configured delegation label. For each candidate it:

  1. Re-verifies the assignee and label against the live Linear API (not a snapshot).
  2. Claims the issue — transitions it to In Progress and reserves a fleet slot.
  3. Dispatches it as a one-node ephemeral epic under the "delegation" pseudo-epic, sharing the [parallel] max_workers fleet cap with kew epic run --all.

Ctrl-C shuts down cleanly.

Configuration

See [delegation] in the configuration reference. Key fields:

Field Default Description
assignee "" The kew Linear member name. Required at watch start.
label "kew:ready" Linear label that signals a delegated issue is ready.
poll_interval_s 20 Throttled Linear API poll floor (seconds).
fast_interval_s 2 Local tracker-events read cadence (seconds).
refusal_cooldown_s 300 Skip re-claiming a budget-refused item for this long, damping the refuse→comment loop (0 disables).

Exit codes

Code Meaning
0 Clean shutdown (Ctrl-C).
1 Misconfiguration (tracker.type not linear, delegation.assignee not set).
2 LINEAR_API_KEY not set at startup, or Linear authentication failed at runtime (expired/revoked key) — the watcher terminates loudly rather than retrying forever (#971).