Skip to content

fix(sling): reject phantom/stale dispatch under --force (gc-dv3psyz)#2

Open
vbtcl wants to merge 1 commit into
fix/control-dispatcher-drain-coalescefrom
fix/gc-dv3psyz-phantom-stale-dispatch
Open

fix(sling): reject phantom/stale dispatch under --force (gc-dv3psyz)#2
vbtcl wants to merge 1 commit into
fix/control-dispatcher-drain-coalescefrom
fix/gc-dv3psyz-phantom-stale-dispatch

Conversation

@vbtcl

@vbtcl vbtcl commented Jun 18, 2026

Copy link
Copy Markdown
Owner

Problem (gc-dv3psyz)

boson/witness flagged the control-dispatcher wasting polecat sessions via three failure modes:

  1. Dot-stripped phantom — a child bead ID bo-oi9tk.11 lost its dot → bo-oi9tk11, and a polecat was dispatched onto an empty branch for a bead that doesn't exist.
  2. Stale-snapshot double-dispatch — an already-routed bead was re-dispatched, putting two polecats on one bead (bo-oi9tk.11: dag + rictus).
  3. Resolved re-dispatch — a closed/resolved bead was dispatched again.

Root cause

internal/sling preflight treated --force as a blanket override that also disabled the bead-existence check and the fresh-state idempotency check. --force is meant to override policy (suspended agent, empty pool, cross-rig, assignee idempotency) — not correctness (does the bead exist? is it already resolved/queued?).

Fix

  • Existence is validated for every real bead-ID route, even under --force. A same-rig ID that doesn't resolve is rejected (no phantom dispatch). A cross-rig bead not visible locally is still allowed under --force (the documented "remote view not yet synced" escape hatch), downgraded to a warning. Inline-text and formula routes are unaffected — they create their own bead.
  • New dispatchSkipReason guard runs for plain --force bead routes and skips — as an idempotent no-op — a bead that is closed (resolved) or already routed to this exact target and awaiting pickup (the stale-snapshot / double-dispatch cases). The assignee-based idempotency that --force is designed to override is unchanged.

Acceptance criteria mapping

Criterion Mechanism
preserve child-bead dots (no phantom) existence check rejects dot-stripped IDs
validate bead exists before spawning shouldValidateExistingBead runs under --force
fresh snapshot, no re-dispatch of resolved/live dispatchSkipReason fresh-reads closed / already-queued

Tests

Rewrote the two tests that locked in the old phantom-allowing behavior; added closed-skip and already-routed-skip coverage; updated the API + CLI analogs and the --force help text.

go test ./internal/sling/ ./internal/api/ ./internal/dispatch/ ./internal/sourceworkflow/ ./cmd/gc/ — all pass. gofmt/go vet clean; full go build ./... green.

🤖 Generated with Claude Code

The control-dispatcher wasted polecat sessions via three failure modes
flagged by boson/witness: (1) a dot-stripped child bead ID (bo-x.11 ->
bo-x11) dispatched a polecat onto an empty branch for a non-existent
bead; (2) a stale snapshot re-dispatched an already-routed bead, putting
two polecats on one bead; (3) a resolved/closed bead was re-dispatched.

Root cause: sling preflight treated --force as a blanket override that
also disabled the bead-existence check and the fresh-state idempotency
check. --force is meant to override *policy* (suspended agent, empty
pool, cross-rig, assignee idempotency) — not *correctness* (does the
bead exist? is it already resolved/queued?).

Fix (internal/sling preflight):
- Existence is validated for every real bead-ID route, even under
  --force. A same-rig ID that does not resolve is rejected (no phantom
  dispatch). A cross-rig bead not visible locally is still allowed under
  --force (the documented "remote view not yet synced" escape hatch),
  downgraded to a warning. Inline-text and formula routes are unaffected
  (they create their own bead).
- New dispatchSkipReason guard runs for plain --force bead routes and
  skips, as an idempotent no-op, a bead that is closed (resolved) or
  already routed to this exact target and awaiting pickup — the stale
  snapshot / double-dispatch cases --force must never bypass. Assignee-
  based idempotency that --force is designed to override is unchanged.

Tests: rewrote the two tests that locked in the old phantom-allowing
behavior; added closed-skip and already-routed-skip coverage; updated
the API + CLI analogs and the --force help text. internal/sling,
internal/api, internal/dispatch, internal/sourceworkflow and cmd/gc all
pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant