Current verification status — 2026-09-06
The original incident report below is retained as historical evidence. Subsequent work now separates two branches:
- Failed-turn finalization: 7/7 controlled unchanged-release dependency fault/control cases reproduced the swallowed read/write-failure path and distinguished a committed write with a lost acknowledgement from an absent write.
- Later stranded admission: a separate real Engine/Queue handoff plus one deliberately lost reply reproduced a failed predecessor, new Running step0 successor and two in-flight deliveries. The diagnostic recovered at its shortened2.5s queue deadline. The initiating live outage and an indefinite wedge are not proven by that result.
Executed fixtures, full branch matrix and limitations.
Current main c86b4ddbd414eb43683cc70c6a3245b7e56c0cc3 already propagates failure-finalization errors and holds the session lock. A separate narrow follow-up patch confirms a timed-out terminal write by one exact whole-record readback without repeating work. It has a genuine failing regression baseline,4/4 passing regressions,455/455 passing library tests, formatting and Clippy checks. Tested patch and precise scope.
That patch is not deployed or accepted upstream; PR submission awaits the required matching MOT ticket. The later queue branch and original live initiating fault remain separate. No uncertain tool effects were replayed; final deployment and autonomous acceptance remain blocked.
Original incident report — preserved
Observed failure
An unmodified released Harness worker left a turn durably running after logging that it was finalising that turn as failed. Its SDK subsequently reconnected to the Engine, but the turn did not reach a terminal state or continue autonomously. harness::stop acknowledged stopping: true; readback showed the abort request but the same running step.
This is an observed integration fault plus a source-level hypothesis, not yet a minimal deterministic reproducer or a verified fix.
Version and scope
- Harness release
harness-v1.8.13, release source deb6f98dbd3c7cbc226234844761430a47b911cf.
- Unmodified worker binary SHA-256:
563f96d9b59fa1a9b93c3b9b1e16aeda4aee5f601a247a622c93bbc5f28a0b03.
- Linux amd64, bounded gVisor sandbox, private local Engine connection.
- The relevant
fail_turn error-handling shape is also present in the inspected 1.8.15 source and current main (00417f34dcee7465d5d4602f4ab497db106f3e55). This does not assert the complete live reproduction was repeated on those versions.
Sanitized timeline (2026-09-06 UTC)
08:54:48.725894 turn started
08:55:05.722388 ERROR turn step failed; finalising turn as failed
error=harness/dependency: session::messages: invocation timed out
08:55:13.556042 WARN expiry sweep skipped: binding store unreadable
error=harness/state: harness::state::list harness_binding: invocation timed out
08:55:53.558216 WARN expiry sweep skipped: binding store unreadable (same timeout)
08:55:55.722347 WARN no frames from engine within idle window; forcing reconnect
idle_timeout=60s
08:56:03.540050 INFO iii connected
After reconnect the same turn remained at running step 1. Readback found no pending function calls or child turns. The only observed work-tool action was read-only function discovery. An ordinary provider request had completed successfully; this is not evidence of a model-generation hang.
A subsequent explicit stop was acknowledged and recorded, but the turn remained running. A single explicit reconciliation wake also failed to terminalize it. Only a later operator-controlled cold restart cancelled/drained the old turn. No uncertain external operation was automatically repeated. That recovery is not a reliable product fix.
Source-level hypothesis
In harness/src/turn_loop.rs, fail_turn converts a state-read error to absence:
let record = crate::state::get_turn(...).await.ok().flatten();
It then converts a finalization failure to skipped(session_id):
finalize_failed(...).await.unwrap_or_else(|_| skipped(session_id))
skipped reports Running, next_step: None, skipped: true. Thus a dependency failure while trying to finalize can become indistinguishable to the caller from a benign no-op, without an established future terminalization step. The observed outage overlaps this path, but fault injection is still needed to prove the exact failure cut and the Engine/SDK cause of the original outage.
Expected behavior / useful regression coverage
- Dependency unavailability during failure finalization should remain an explicit, recoverable failure to terminalize, not a silent running/no-next-step result.
- Test disconnect/read timeout at
get_turn, session finalization, and durable turn-write cuts; restore transport and verify an eventual truthful terminal observation through the owning lifecycle contract.
- Test stop while failure finalization is unavailable, plus reconnect/re-registration and cold restart.
- Preserve exact turn identity and cancellation ordering. Never replay an outcome-unknown external tool action merely to complete a turn.
- Distinguish failed state persistence from a stale/terminal turn legitimately skipped.
No downstream Harness/Engine fork or restart watchdog has been introduced. This remains a reliability blocker for autonomous work through the affected runtime.
Current verification status — 2026-09-06
The original incident report below is retained as historical evidence. Subsequent work now separates two branches:
Executed fixtures, full branch matrix and limitations.
Current main
c86b4ddbd414eb43683cc70c6a3245b7e56c0cc3already propagates failure-finalization errors and holds the session lock. A separate narrow follow-up patch confirms a timed-out terminal write by one exact whole-record readback without repeating work. It has a genuine failing regression baseline,4/4 passing regressions,455/455 passing library tests, formatting and Clippy checks. Tested patch and precise scope.That patch is not deployed or accepted upstream; PR submission awaits the required matching MOT ticket. The later queue branch and original live initiating fault remain separate. No uncertain tool effects were replayed; final deployment and autonomous acceptance remain blocked.
Original incident report — preserved
Observed failure
An unmodified released Harness worker left a turn durably
runningafter logging that it was finalising that turn as failed. Its SDK subsequently reconnected to the Engine, but the turn did not reach a terminal state or continue autonomously.harness::stopacknowledgedstopping: true; readback showed the abort request but the same running step.This is an observed integration fault plus a source-level hypothesis, not yet a minimal deterministic reproducer or a verified fix.
Version and scope
harness-v1.8.13, release sourcedeb6f98dbd3c7cbc226234844761430a47b911cf.563f96d9b59fa1a9b93c3b9b1e16aeda4aee5f601a247a622c93bbc5f28a0b03.fail_turnerror-handling shape is also present in the inspected 1.8.15 source and currentmain(00417f34dcee7465d5d4602f4ab497db106f3e55). This does not assert the complete live reproduction was repeated on those versions.Sanitized timeline (2026-09-06 UTC)
After reconnect the same turn remained at running step 1. Readback found no pending function calls or child turns. The only observed work-tool action was read-only function discovery. An ordinary provider request had completed successfully; this is not evidence of a model-generation hang.
A subsequent explicit stop was acknowledged and recorded, but the turn remained running. A single explicit reconciliation wake also failed to terminalize it. Only a later operator-controlled cold restart cancelled/drained the old turn. No uncertain external operation was automatically repeated. That recovery is not a reliable product fix.
Source-level hypothesis
In
harness/src/turn_loop.rs,fail_turnconverts a state-read error to absence:It then converts a finalization failure to
skipped(session_id):skippedreportsRunning,next_step: None,skipped: true. Thus a dependency failure while trying to finalize can become indistinguishable to the caller from a benign no-op, without an established future terminalization step. The observed outage overlaps this path, but fault injection is still needed to prove the exact failure cut and the Engine/SDK cause of the original outage.Expected behavior / useful regression coverage
get_turn, session finalization, and durable turn-write cuts; restore transport and verify an eventual truthful terminal observation through the owning lifecycle contract.No downstream Harness/Engine fork or restart watchdog has been introduced. This remains a reliability blocker for autonomous work through the affected runtime.