Skip to content

Bug: Goal-continuation cadence is bypassed on the within-turn dispatch path #5534

Description

@M-Maciej

Description

#5508 (commit 7eb4650b0a67, "feat(tui): add cancellable goal
continuation cadence") added [goal] continuation_delay_seconds and a
cancellable quiet period, but wired the wait into only one of the two
goal-continuation dispatch paths:

  • The cross-turn scheduler (schedule_goal_continuation in
    crates/tui/src/core/engine.rs, the ready_at quiet period) — used
    when the interactive engine re-dispatches after a completed turn, and
    gated on if !self.host_managed_turns().
  • The within-turn dispatch hook (goal_continuation_message_if_needed
    in crates/tui/src/core/engine/turn_loop.rs) — which has no wait at
    all
    . Whenever a model step ends with the goal still active it
    immediately emits the next continuation prompt.

Consequences:

  1. Within-turn continuation passes never honor the quiet period, on
    any session: up to per_turn_max passes fire back-to-back at model
    latency.
  2. Host-managed sessions never reach the cross-turn scheduler at all
    (the engine yields so the host can create the next durable claim), so
    for those sessions the quiet period is effectively dead — every pass
    is dispatched through the un-waited hook.

Steps to reproduce

  1. Set the quiet period, e.g. [goal] continuation_delay_seconds = 300.
  2. Start a session with an active goal, or resume one via
    codewhale resume --last (a non-host-managed CLI resume).
  3. Watch the continuation passes: they fire at model-latency intervals
    (~9 s observed), never 300 s apart.

Expected behavior

Consecutive goal-continuation passes are separated by the configured quiet
period (cancellable with Esc), on every dispatch path.

Actual behavior

Passes fire instantly through the within-turn dispatch hook; the quiet
period applies only to the cross-turn scheduler path, which is not the
path these sessions dispatch through.

Impact

Every supervised/unattended run that relies on continuation_delay_seconds
to pace autonomous goal work (cost control, provider rate limits,
checkpoint cadence) gets instant-fire passes instead of the configured
cadence. The bypass was measured live in both dispatch modes — host-
injected continuation tokens dispatching immediately (~9 s end→start
gaps), and instant-fire passes on a CLI-resumed session.

Environment

  • OS: Linux
  • codewhale version: 0.9.10
  • Install method: release binary (also reproduced on a source build
    tracking v0.9.10)
  • Model/provider: Deepseek/any
  • Shell: zsh

Logs, screenshots, or recordings

Live measurements against a supervised deployment tracking v0.9.10:

  • Before: host-injected continuation tokens dispatched immediately,
    ~9 s end→start gaps; a CLI-resumed session fired passes instantly.
  • After applying the fix: two consecutive quiet→pass gaps of ~331 s
    and ~323 s, with zero user input in each window. The bypass is gone.

The fix in the accompanying PR applies the same cancellable quiet period
at the within-turn dispatch site, unconditionally (the hook is the
only dispatch site for non-host-managed sessions, so gating the wait on
host-managed turns does not work — a CLI-resumed session would still fall
into the no-wait branch). Semantics preserved: the wait stays cancellable
(the Esc cancel token wins biased over the timer), a pause/clear or a
terminal update_goal observed after the wait cancels the pending pass
before anything is recorded or dispatched, and the
GoalContinuationWaiting / GoalContinuationWaitEnded events are
unchanged. New regression tests cover the non-host-managed wait, the
host-managed wait, and the zero-delay-immediate case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions