Skip to content

(MOT-4107) fix(harness): recover calls safely after engine restarts - #630

Merged
ytallo merged 5 commits into
mainfrom
fix/507-crash-recovery-v2
Jul 31, 2026
Merged

(MOT-4107) fix(harness): recover calls safely after engine restarts#630
ytallo merged 5 commits into
mainfrom
fix/507-crash-recovery-v2

Conversation

@ytallo

@ytallo ytallo commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Refs MOT-4107

Fixes #507.

Supersedes #625.

An engine restart during an in-flight Harness function call now closes the interrupted call without replaying arbitrary queue consumers or reconstructing incomplete turn state.

What changed

  • Capture the engine boot epoch immediately before each restart-sensitive invocation, so an idle restart cannot poison the next call with a stale global baseline.
  • Add an explicit, default-off queue option for bounded restart redelivery and enable it only for the checkpointed harness-turn queue.
  • Preserve the authoritative durable turn record, including policy, budgets, parent linkage, and output contract; missing records remain stale deliveries instead of being reconstructed from transcript fragments.
  • Retry only safe boot-time dependency races and keep ordinary queue timeout, retry, and DLQ behavior for other consumers.
  • Add INT-010, which SIGKILLs and respawns the pinned engine while a controlled function is in flight, then verifies one side effect, one structured engine_restart result, no duplicate messages, and a completed session.

Verification

  • cargo test --lib in harness (295 passed)
  • cargo test --lib in queue (125 passed)
  • Deterministic INT-010 boot-race coverage: context::assemble is held out while the restored turn retries
  • cargo test --manifest-path tests/integration/Cargo.toml
  • cargo clippy --all-targets -- -D warnings in harness and queue
  • cargo clippy --manifest-path tests/integration/Cargo.toml --all-targets -- -D warnings
  • INT-010 against pinned engine revision 15dc993ebfdbfcabe5d299cf4cae4dd676db4c06 (passed)

Summary by CodeRabbit

  • New Features

    • Added automatic recovery for interrupted function calls when the engine restarts.
    • Enabled configurable redelivery of in-flight queue messages after engine connection loss, with bounded retry behavior.
    • Added retry handling for transient dependency and state-connection errors.
    • Added support for configuring engine-restart faults and scenario timeouts in integration scenarios.
  • Bug Fixes

    • Prevented duplicate calls and messages during engine restart recovery.
    • Improved resilience when enqueueing turn steps fails temporarily.
  • Tests

    • Added crash-recovery coverage for interrupted function execution and process termination.

@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
workers Ready Ready Preview Jul 31, 2026 3:47am
workers-tech-spec Ready Ready Preview Jul 31, 2026 3:47am

Request Review

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

skill-check — worker

0 verified, 50 skipped (no docs/).

Layer Result
structure
vale
ai
render

Four for four. Nicely done.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ytallo, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 33 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a61ee7ef-f1f9-40eb-a30c-bbb980c517e3

📥 Commits

Reviewing files that changed from the base of the PR and between 0618a4c and ed92240.

📒 Files selected for processing (4)
  • harness/tests/integration/README.md
  • harness/tests/integration/src/scenario/phases/execution.rs
  • harness/tests/integration/src/scenarios/engine_restart_recovery.rs
  • harness/tests/integration/src/stack/supervisor.rs
📝 Walkthrough

Walkthrough

The change adds engine-epoch restart detection, bounded transient retries, configurable queue redelivery, engine fault injection, and INT-010 integration coverage for recovering interrupted turns.

Changes

Engine restart recovery

Layer / File(s) Summary
Restart detection and epoch contracts
harness/src/clients/engine.rs, queue/src/trigger.rs
Dispatches detect changed engine epochs and return synthesized engine_restart errors. Queue invokers expose epoch capture and restart-waiting hooks.
Queue redelivery and transient turn recovery
queue/src/adapter.rs, queue/src/runtime.rs, harness/src/queue.rs, harness/src/functions/turn.rs, harness/src/turn_loop.rs
Queues optionally redeliver interrupted messages with bounded attempts. Turn steps and enqueue operations retry selected transient failures with backoff.
Fault configuration and engine process control
harness/tests/integration/src/types/*, harness/tests/integration/src/scenarios/dsl.rs, harness/tests/integration/src/probe.rs, harness/tests/integration/src/process/*, harness/tests/integration/src/stack/supervisor.rs
Integration fixtures support held responses and engine SIGKILL faults. The stack kills, reaps, and respawns the engine.
Recovery phase and INT-010 validation
harness/tests/integration/src/scenario/*, harness/tests/integration/src/scenarios/*, harness/tests/integration/src/fixtures/*, harness/tests/integration/src/runtime.rs, harness/tests/integration/README.md
The fault phase restores bindings and dependencies. INT-010 verifies recovery, interrupted-call closure, call counts, and duplicate-message prevention.

Estimated code review effort: 5 (Critical) | ~90 minutes

Possibly related PRs

  • iii-hq/workers#625: Implements the same engine-restart recovery changes across the harness, queue, and integration tests.
  • iii-hq/workers#424: Shares the queue/src/trigger.rs invoker abstraction extended here.
  • iii-hq/workers#464: Shares durable harness-turn execution and retry paths.

Sequence Diagram(s)

sequenceDiagram
  participant ScenarioRunner
  participant ScenarioProbe
  participant Stack
  participant QueueRuntime
  participant EngineClient
  ScenarioRunner->>ScenarioProbe: wait for target call
  ScenarioProbe-->>ScenarioRunner: target call recorded
  ScenarioRunner->>Stack: kill and respawn engine
  Stack-->>QueueRuntime: engine available
  QueueRuntime->>EngineClient: redeliver interrupted turn
  EngineClient-->>QueueRuntime: engine_restart closure or recovered result
  QueueRuntime-->>ScenarioRunner: completed recovery result
Loading

Poem

A rabbit watched the engine fall,
Then saw the queues recover all.
Held calls waited, faults ran true,
New epochs guided messages through.
“Hop safely onward!” cried the hare.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 67.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address issue #507 by retrying boot-time dependency races and closing interrupted calls with an engine restart result.
Out of Scope Changes check ✅ Passed The implementation and integration coverage remain focused on engine restart recovery, bounded retries, durable turn handling, and interrupted-call closure.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: safe Harness call recovery after engine restarts.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/507-crash-recovery-v2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (10)
queue/src/trigger.rs (1)

164-166: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

connection_epoch can never return Err.

engine_epoch_ms maps every failure to None, so IiiInvoker::connection_epoch always returns Ok. In invoke_checkpointed_message_across_engine_restarts the ? branch is therefore unreachable, and a probe failure is indistinguishable from "restart watching disabled".

That conflation is acceptable for the current callers, because both cases fall back to a plain invoke. If you want the distinction later, propagate the trigger error instead of discarding it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@queue/src/trigger.rs` around lines 164 - 166, Update
IiiInvoker::connection_epoch to use a non-Result return type matching
engine_epoch_ms, removing the unreachable error branch and adjusting
invoke_checkpointed_message_across_engine_restarts to handle the optional epoch
directly. Preserve the existing fallback to plain invocation for both a missing
epoch and a failed probe.
queue/src/runtime.rs (4)

1098-1100: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Make the select! biased so a completed invocation wins over a simultaneous restart signal.

tokio::select! polls its branches in random order. If invoke_message and connection_lost_since both become ready in the same poll, the loop can discard a completed invocation result and re-invoke the target. That duplicates the side effect even though the original call finished.

harness::turn tolerates a duplicate step, so the impact is bounded today. Adding biased; makes the preference deterministic: an available result always wins.

♻️ Proposed change
         tokio::select! {
+            biased;
             result = invoke_message(queue, invoker, message, attempt, timeout_ms) => return result,
             () = invoker.connection_lost_since(baseline) => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@queue/src/runtime.rs` around lines 1098 - 1100, Update the tokio::select! in
the invocation loop around invoke_message and connection_lost_since to use
biased polling, placing the bias directive before the branches so a completed
invoke_message result is selected over a simultaneous restart signal.

954-963: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider grouping the scalar consumer parameters into a struct.

process_standard_message now takes five scalars and process_fifo_message takes six, ending with a positional bool. Call sites pass 3, 1, 1, 1_800_000, true with no field names. A swapped poll_interval_ms and backoff_ms, or an inverted bool, compiles cleanly.

The values all originate from FunctionQueueConfig. Passing a small borrowed settings struct instead of loose scalars would remove that class of mistake.

Also applies to: 992-1003

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@queue/src/runtime.rs` around lines 954 - 963, Group the scalar consumer
settings currently passed to process_standard_message and process_fifo_message
into a small borrowed configuration struct, using the corresponding fields from
FunctionQueueConfig such as retry, polling, timeout, backoff, and redelivery
settings. Update both function signatures and all call sites to pass the struct,
preserving the existing behavior while eliminating positional scalar and boolean
arguments.

1789-1814: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

This test does not cover the opt-in race it appears to model.

RestartSignalButSuccessfulInvoker resolves connection_lost_since after 2 ms while call needs 20 ms. The test passes false for redeliver_on_engine_restart, so neither hook is consulted, and the invoker's restart signal is never observed. The test proves the opt-out path, which matches its name.

No test covers redeliver_on_engine_restart: true with an invocation that succeeds while a restart signal is already pending. That is exactly the select! race noted on invoke_checkpointed_message_across_engine_restarts. Add a case that reuses this invoker with true and asserts calls == 1.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@queue/src/runtime.rs` around lines 1789 - 1814, Update the test around
standard delivery to cover the opt-in restart behavior: pass true for
redeliver_on_engine_restart while reusing RestartSignalButSuccessfulInvoker, and
add a case asserting invoker.calls remains 1 after successful completion.
Preserve the existing false/normal-semantics test and its acknowledgment
assertions.

1079-1090: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document the multiplicative invocation bound for FIFO queues.

process_fifo_message treats an exhausted restart budget as an ordinary invocation failure. It then retries in place, and each in-place retry enters this loop with a fresh budget. For harness-turn with max_retries: 3 and MAX_RESTART_REDELIVERIES: 3, one message can reach (3 + 1) * (3 + 1) = 16 target invocations before the DLQ.

The total is bounded, so a FIFO group cannot be pinned forever. The current doc comment does not state the interaction with the queue retry budget. Add that to the comment so the effective ceiling is clear to the next reader.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@queue/src/runtime.rs` around lines 1079 - 1090, Update the doc comment for
invoke_checkpointed_message_across_engine_restarts to document that exhausting
the restart budget becomes an ordinary invocation failure, which can trigger the
queue’s in-place retry budget and re-enter this loop with a fresh restart
budget; state the resulting multiplicative ceiling as (max_retries + 1) ×
(MAX_RESTART_REDELIVERIES + 1), while preserving the bounded FIFO-group
behavior.
harness/src/clients/engine.rs (3)

61-100: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

Consider caching the engine epoch instead of probing it on every dispatch.

dispatch now performs a blocking engine::workers::list round trip before every invocation, and engine_link_interrupted then issues one more probe per second for the whole dispatch lifetime. Every tool call therefore pays one extra RPC of added latency, and each in-flight call adds sustained 1 Hz load on engine::workers::list. With many concurrent calls and long tool timeouts this cost grows linearly with concurrency.

A single shared epoch watcher per client, or a short-TTL cached epoch, would give the same restart signal at constant cost. The baseline can still be read from the cache because the epoch only changes on restart.

This is a refactor suggestion, not a blocker; the current behavior is correct.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harness/src/clients/engine.rs` around lines 61 - 100, Refactor EngineClient
dispatch restart detection to reuse a shared cached engine epoch instead of
calling engine_epoch_ms before every invocation. Add a single watcher or
short-TTL cache associated with the client, have dispatch read the cached
baseline, and make engine_link_interrupted consume the same cached state while
preserving restart interruption behavior and the existing fallback when no epoch
is available.

165-198: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated engine-epoch contract in two crates. Both files define ENGINE_EPOCH_PROBE_INTERVAL_MS, ENGINE_EPOCH_PROBE_TIMEOUT_MS, and an engine_epoch_ms that queries engine::workers::list and takes the minimum connected_at_ms over workers with runtime == "engine". The restart signal is one shared contract with the engine, implemented twice. If the engine changes the runtime label, the field name, or the response shape, one copy can be updated and the other left behind, and restart detection then differs between the harness and the queue consumer. Only the harness copy has a unit test.

  • harness/src/clients/engine.rs#L165-L198: extract the constants, engine_epoch_ms, and parse_engine_epoch into a shared helper that both crates depend on, and keep engine_epoch_uses_the_oldest_in_process_engine_worker as its test.
  • queue/src/trigger.rs#L32-L60: replace the local constants and the inlined parse with a call into that shared helper, so the queue invoker and EngineClient::dispatch cannot diverge.

If no crate is shared by both today, add a unit test for the parse in queue/src/trigger.rs as the smaller step.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harness/src/clients/engine.rs` around lines 165 - 198, The engine-epoch
contract is duplicated between harness/src/clients/engine.rs#L165-L198 and
queue/src/trigger.rs#L32-L60. Extract ENGINE_EPOCH_PROBE_INTERVAL_MS,
ENGINE_EPOCH_PROBE_TIMEOUT_MS, engine_epoch_ms, and parse_engine_epoch into a
shared helper used by both crates, preserving
engine_epoch_uses_the_oldest_in_process_engine_worker as its test; update
queue/src/trigger.rs#L32-L60 to call that helper and remove its local constants
and parsing. If no shared crate is available, instead add equivalent parse
coverage in queue/src/trigger.rs.

190-198: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Epoch detection silently disables when the worker shape changes.

parse_engine_epoch returns None when no worker reports runtime == "engine" or when connected_at_ms is missing. dispatch then falls back to the plain SDK timeout, and engine_link_interrupted never trips. That is a safe fallback, but the failure is invisible: a future engine that renames the runtime label or the field disables restart recovery with no signal.

Consider logging once at warn level when the preflight epoch cannot be read, so the loss of restart detection is observable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harness/src/clients/engine.rs` around lines 190 - 198, Update the preflight
flow around parse_engine_epoch and dispatch to emit a warn-level log once
whenever the engine epoch cannot be determined because no matching worker or
connected_at_ms value is available. Preserve the existing timeout fallback and
engine_link_interrupted behavior, while avoiding repeated warnings for the same
preflight failure.
harness/tests/integration/src/fixtures/loading.rs (1)

147-166: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: also validate the restart delay against the scenario deadline.

This block catches fault authoring mistakes before the stack boots. One related mistake is still deferred to run time: a restart_delay_ms at or above deadlines.scenario_ms always burns the deadline inside the fault phase. The failure then surfaces as "fault restart delay exceeded scenario deadline" after a full stack boot.

♻️ Proposed additional check
             anyhow::ensure!(
                 target.hold_response,
                 "engine fault target must hold its response until SIGKILL"
             );
+            anyhow::ensure!(
+                fault.restart_delay_ms < self.scenario.deadlines.scenario_ms,
+                "fault restart delay {}ms must be shorter than the scenario deadline {}ms",
+                fault.restart_delay_ms,
+                self.scenario.deadlines.scenario_ms
+            );
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harness/tests/integration/src/fixtures/loading.rs` around lines 147 - 166, In
the fault validation block for the scenario loading flow, add validation that
fault.restart_delay_ms is strictly less than deadlines.scenario_ms, rejecting
values at or above the scenario deadline with a clear authoring error. Keep the
existing target, call-count, function, and hold-response checks unchanged.
harness/tests/integration/src/stack/supervisor.rs (1)

177-185: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider waiting for the engine listener after respawn.

boot_once waits for the TCP listener and checks early_exit before it continues. respawn_engine returns as soon as the child is spawned. If the respawned engine fails to bind the port, the fault phase blocks in bind_observers until the scenario deadline (120s for INT-010) instead of failing fast with the real cause.

Reuse the boot readiness probe, or poll early_exit() for a short bounded period after the respawn.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harness/tests/integration/src/stack/supervisor.rs` around lines 177 - 185,
Update respawn_engine to wait for the respawned engine’s listener readiness
before returning, reusing the boot_once readiness probe and early_exit check
where possible. Ensure startup failures are surfaced promptly while keeping the
wait bounded rather than blocking until the scenario deadline.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@harness/src/functions/turn.rs`:
- Around line 92-107: Narrow the Dependency handling in is_transient_step_error
so “not connected” is considered transient only for the intended boot-race
prefixes, such as context::assemble or session::messages. Preserve the existing
exclusion for “enqueue harness::turn” and the function_not_found behavior, while
preventing unrelated dependency transport errors from triggering a whole-step
retry.

---

Nitpick comments:
In `@harness/src/clients/engine.rs`:
- Around line 61-100: Refactor EngineClient dispatch restart detection to reuse
a shared cached engine epoch instead of calling engine_epoch_ms before every
invocation. Add a single watcher or short-TTL cache associated with the client,
have dispatch read the cached baseline, and make engine_link_interrupted consume
the same cached state while preserving restart interruption behavior and the
existing fallback when no epoch is available.
- Around line 165-198: The engine-epoch contract is duplicated between
harness/src/clients/engine.rs#L165-L198 and queue/src/trigger.rs#L32-L60.
Extract ENGINE_EPOCH_PROBE_INTERVAL_MS, ENGINE_EPOCH_PROBE_TIMEOUT_MS,
engine_epoch_ms, and parse_engine_epoch into a shared helper used by both
crates, preserving engine_epoch_uses_the_oldest_in_process_engine_worker as its
test; update queue/src/trigger.rs#L32-L60 to call that helper and remove its
local constants and parsing. If no shared crate is available, instead add
equivalent parse coverage in queue/src/trigger.rs.
- Around line 190-198: Update the preflight flow around parse_engine_epoch and
dispatch to emit a warn-level log once whenever the engine epoch cannot be
determined because no matching worker or connected_at_ms value is available.
Preserve the existing timeout fallback and engine_link_interrupted behavior,
while avoiding repeated warnings for the same preflight failure.

In `@harness/tests/integration/src/fixtures/loading.rs`:
- Around line 147-166: In the fault validation block for the scenario loading
flow, add validation that fault.restart_delay_ms is strictly less than
deadlines.scenario_ms, rejecting values at or above the scenario deadline with a
clear authoring error. Keep the existing target, call-count, function, and
hold-response checks unchanged.

In `@harness/tests/integration/src/stack/supervisor.rs`:
- Around line 177-185: Update respawn_engine to wait for the respawned engine’s
listener readiness before returning, reusing the boot_once readiness probe and
early_exit check where possible. Ensure startup failures are surfaced promptly
while keeping the wait bounded rather than blocking until the scenario deadline.

In `@queue/src/runtime.rs`:
- Around line 1098-1100: Update the tokio::select! in the invocation loop around
invoke_message and connection_lost_since to use biased polling, placing the bias
directive before the branches so a completed invoke_message result is selected
over a simultaneous restart signal.
- Around line 954-963: Group the scalar consumer settings currently passed to
process_standard_message and process_fifo_message into a small borrowed
configuration struct, using the corresponding fields from FunctionQueueConfig
such as retry, polling, timeout, backoff, and redelivery settings. Update both
function signatures and all call sites to pass the struct, preserving the
existing behavior while eliminating positional scalar and boolean arguments.
- Around line 1789-1814: Update the test around standard delivery to cover the
opt-in restart behavior: pass true for redeliver_on_engine_restart while reusing
RestartSignalButSuccessfulInvoker, and add a case asserting invoker.calls
remains 1 after successful completion. Preserve the existing
false/normal-semantics test and its acknowledgment assertions.
- Around line 1079-1090: Update the doc comment for
invoke_checkpointed_message_across_engine_restarts to document that exhausting
the restart budget becomes an ordinary invocation failure, which can trigger the
queue’s in-place retry budget and re-enter this loop with a fresh restart
budget; state the resulting multiplicative ceiling as (max_retries + 1) ×
(MAX_RESTART_REDELIVERIES + 1), while preserving the bounded FIFO-group
behavior.

In `@queue/src/trigger.rs`:
- Around line 164-166: Update IiiInvoker::connection_epoch to use a non-Result
return type matching engine_epoch_ms, removing the unreachable error branch and
adjusting invoke_checkpointed_message_across_engine_restarts to handle the
optional epoch directly. Preserve the existing fallback to plain invocation for
both a missing epoch and a failed probe.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dca77451-449f-4127-845c-8e3721f625a4

📥 Commits

Reviewing files that changed from the base of the PR and between a1742f8 and 0618a4c.

📒 Files selected for processing (23)
  • harness/src/clients/engine.rs
  • harness/src/functions/turn.rs
  • harness/src/queue.rs
  • harness/src/turn_loop.rs
  • harness/tests/integration/README.md
  • harness/tests/integration/src/fixtures/loading.rs
  • harness/tests/integration/src/fixtures/tests.rs
  • harness/tests/integration/src/probe.rs
  • harness/tests/integration/src/process/child.rs
  • harness/tests/integration/src/process/supervisor.rs
  • harness/tests/integration/src/process/tests.rs
  • harness/tests/integration/src/runtime.rs
  • harness/tests/integration/src/scenario/phases/execution.rs
  • harness/tests/integration/src/scenario/runner.rs
  • harness/tests/integration/src/scenarios/dsl.rs
  • harness/tests/integration/src/scenarios/engine_restart_recovery.rs
  • harness/tests/integration/src/scenarios/mod.rs
  • harness/tests/integration/src/stack/supervisor.rs
  • harness/tests/integration/src/types/probe.rs
  • harness/tests/integration/src/types/scenario/compiled.rs
  • queue/src/adapter.rs
  • queue/src/runtime.rs
  • queue/src/trigger.rs
💤 Files with no reviewable changes (1)
  • harness/tests/integration/src/process/supervisor.rs

Comment thread harness/src/functions/turn.rs
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.

harness: crash recovery leaves the session unusable — restored turn races worker boot, and the interrupted function_call is never closed

1 participant