Skip to content

fix(runs): persist run status on loop/time-budget halt (#34) - #41

Merged
prashar32 merged 1 commit into
mainfrom
fix/halt-status-persistence
May 31, 2026
Merged

fix(runs): persist run status on loop/time-budget halt (#34)#41
prashar32 merged 1 commit into
mainfrom
fix/halt-status-persistence

Conversation

@prashar32

Copy link
Copy Markdown
Owner

Problem

A run halted on its loop or wall-clock budget kept status: "running" (empty haltReason) in the store — visible in runs list, audit, and GET /v1/runs/{id} — even though it was terminally halted in memory (context cancelled, further calls correctly refused with 402). Only token/dollar halts (which go through RecordCallpersistCall) persisted.

Caught while reviewing the observability story: a run the governor killed showing as running directly contradicts the demo narrative and undercuts the "observability done right" pitch — worth fixing before promo.

Cause

Loop/time halts happen in governor.PreStep (via Run.BeginStep) and the pre-call CanProceed. Both flip the governor to halted and return the HaltError before anything is written through — the gateway's 402 path returns there without recording a call.

Fix

Persist the run on both halt paths (persistHalttouch + persistRun). Best-effort, idempotent, and a no-op for the in-memory-only manager. Mirrors the existing token/dollar behavior.

Tests

  • TestManager_LoopHaltPersistsStatus — loop budget = 1; the 2nd BeginStep halts; asserts the persisted run is halted / loop_budget_exceeded.
  • Existing TestManager_HaltPersistsStatus (token path) still passes. go test ./internal/runs, go vet, gofmt clean.

Scope

Addresses the halt-persistence half of #34. The secondary note — a normally completed run also stays running (there's no run-finished signal from the SDK/governed_run) — is a separate lifecycle design item; leaving #34 open for that follow-up.

A run halted on its loop or wall-clock budget is enforced in BeginStep/CanProceed
(PreStep), which returned the HaltError before writing anything through to the
store. The governor was halted in memory (context cancelled, further calls
correctly refused with 402), but runs list / audit / GET /v1/runs/{id} kept
reporting "running" with an empty halt reason.

Persist the run on both halt paths (mirroring the token/dollar RecordCall path,
which already did). Best-effort and idempotent; no-op for the in-memory-only
manager. Adds TestManager_LoopHaltPersistsStatus covering the loop-budget path.

Addresses the halt-persistence half of #34. (The secondary note — a normally
completed run also stays "running" because there's no run-finished signal — is a
separate lifecycle design item, left for a follow-up.)
@prashar32
prashar32 merged commit 7f0f301 into main May 31, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant