Skip to content

P1: #137 made dispatch order to a capacity-1 worker nondeterministic (60/60 → ~15% failure) #155

Description

@kjgbot

Found by the CI coverage change (#153/#154), which made the kernel suite run in CI for the first time. This test has never executed in CI before today.

The failure

worker_capacity::default_capacity_one_reopens_only_after_durable_completion_or_crash
kernel/relayflowd/tests/crash_resume/worker_capacity.rs:149
assertion `left == right` failed
  left: String("lane-a")
 right: "lane-b"

The spec declares two independent llm steps in the order [lane-b, lane-a], at default worker capacity 1. Line 149 asserts the first step.dispatch the worker sees is lane-b — the first-authored step.

Bisected to #137

Run in isolation, --exact --test-threads=1, same machine, same binary path:

commit runs failures
16860d2 (immediately before #137) 60 0
f16b133 (#137 itself) 15 1
current main 12 3

Combined at-or-after #137: 4 failures in 27 runs (~15%). Probability of observing 0 failures in 60 runs at that rate is ≈ 6e-5, so the clean "before" side is not luck.

It also fails on GitHub's runners — run 33816284259 — which is how it surfaced.

Mechanism

#137 replaced first-runnable-step selection:

-    for spec in &state.spec.steps {
-            StepState::Runnable => return start_actions(state, spec, runtime.attempts + 1, now_ms),

with a batch:

+    let starts = parallel::runnable_batch(state)
+        .into_iter()

runnable_batch returns every dependency-free step. Previously exactly one step was dispatched and it was the first in authored order; now both lanes are started, and a capacity-1 worker receives whichever reaches it first.

Note #137 added machine_starts_every_runnable_step_in_authored_order, which asserts authored order ["lane-b", "lane-a"] — but at the action level. The actions are ordered; what races is delivery to the worker. So the ordering contract this PR declared for itself is not the one observed at the socket.

Why this matters beyond a flaky test

RFC-0001's premise is a deterministic, replayable engine. If which step a capacity-bounded worker is offered first is nondeterministic, two replays of the same journal can dispatch in different orders. The exactly-once ledger keys on (step_id, idempotency_key, surface_path) and is not itself threatened, but dispatch order is an observable part of the contract, and a capacity bound that admits a race is the kind of thing that reads as "works" until it doesn't.

Whether the right repair is (a) restore authored-order selection under a capacity bound, or (b) decide ordering was never guaranteed and correct the test and the docs, is a design call — it should not be settled by whoever is closest to the red CI.

Not fixed here

Reported, not repaired, per the #153 work package: a newly-exposed failure is the deliverable, and the correct response is to name it rather than weaken the gate or quietly patch the test.

Reported by the v2 lead session; #137 was merged by me, so the repair wants a second pair of eyes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions