Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a50a8de
fix(sdk): preserve runs across daemon read timeouts
Oct 5, 2026
3ec2aa0
Merge remote-tracking branch 'origin/main' into relayflow/flows-softw…
khaliqgant Oct 8, 2026
0b8f93f
fix(sdk): park on a lost read session and let cancellation interrupt …
khaliqgant Oct 8, 2026
f194b86
fix(sdk): retry a reader session that failed to set up; regenerate mu…
khaliqgant Oct 8, 2026
1a6a9f7
docs(evidence): re-capture focused and live verification at the revie…
khaliqgant Oct 8, 2026
c962fc9
fix(sdk): keep a classified run outcome when the reuse summary read i…
khaliqgant Oct 8, 2026
36ff10e
fix(sdk): a watch connection lost during registration is a read inter…
khaliqgant Oct 8, 2026
9ed7a6e
docs(evidence): commit the mutation and kernel-build transcripts veri…
khaliqgant Oct 8, 2026
a2cff9d
fix(sdk): a completion push aborts the lease snapshot in flight
khaliqgant Oct 8, 2026
fb4524d
fix(sdk): a snapshot aborted by completion ends the wait; verifier re…
khaliqgant Oct 8, 2026
0cfcc0d
fix(sdk): no reuse-summary read after classification reported an unan…
khaliqgant Oct 8, 2026
35c9e76
fix(sdk): fail closed on malformed reader frames; run cancellation re…
khaliqgant Oct 8, 2026
bfcc879
fix(sdk): malformed handshake frames fail closed; the verifier honour…
khaliqgant Oct 8, 2026
90355c9
fix(sdk): cancellation never discards a classified outcome or termina…
khaliqgant Oct 8, 2026
2cdf352
docs(evidence): record the mutation harness command as actually run
khaliqgant Oct 8, 2026
db0326f
refactor(sdk): move budgeted reader sessions out of journal-client; w…
khaliqgant Oct 8, 2026
e3a63ea
fix(sdk): custom workers hand a lost lease back to the kernel
khaliqgant Oct 8, 2026
7baf1b4
fix(sdk): malformed frames reach a registered watch; flow handshakes …
khaliqgant Oct 8, 2026
d4a56c7
fix(sdk): a watch dropped after registration is a read interruption
khaliqgant Oct 8, 2026
89b2c69
fix(sdk): budget the handshake only for post-admission sessions
khaliqgant Oct 8, 2026
65a62fe
fix(sdk): the root lease and a dropped watch both stop in-flight budg…
khaliqgant Oct 8, 2026
8c1d198
fix(sdk): root detection and primary drops keep read interruptions typed
khaliqgant Oct 8, 2026
e4c232a
fix(sdk): hold the lease read scope for the whole Bun attempt; post-d…
khaliqgant Oct 8, 2026
6ccad70
refactor(sdk): split the journal connection from the client; keep com…
khaliqgant Oct 8, 2026
711ae22
fix(sdk): an unread authored result is an unconfirmed verdict, not su…
khaliqgant Oct 8, 2026
c7af060
fix(sdk): only definite daemon answers decide authored-root detection…
khaliqgant Oct 8, 2026
6c85fbf
fix(sdk): canceling a read abandons a reader setup still in progress
khaliqgant Oct 8, 2026
4e50eea
fix(sdk): a completion replayed before a lost watch registration stands
khaliqgant Oct 8, 2026
324aaf3
refactor(sdk): authored-root readback policy in its own module; narro…
khaliqgant Oct 8, 2026
46778f7
fix(sdk): no reuse-summary read after failed-step inspection was inte…
khaliqgant Oct 8, 2026
c6a1cf9
fix(sdk): communication workers keep a retried attempt's history read…
khaliqgant Oct 8, 2026
a54d16c
fix(sdk): communication history reads stop on lease loss and shutdown
khaliqgant Oct 8, 2026
76ff727
fix(sdk): drop a reader whose read was canceled; canceled reads are i…
khaliqgant Oct 8, 2026
59cc8c4
fix(sdk): budget exhaustion aborts the queued read work, not just the…
khaliqgant Oct 8, 2026
850d7d5
fix(sdk): handshake transport errors are read interruptions; timeouts…
khaliqgant Oct 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/SURFACE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1736,6 +1736,32 @@ typed `run_not_found` refusal. A dropped connection, request failure, or
journal may already have changed and the CLI cannot honestly claim the resume
was refused before a write.

A timed-out read is handled separately: flow execution clients (`flows run`
and `flows resume`, authored and declarative) retry only `run.get`,
`journal.read`, `stream.read`, and `subscription.inspect` within a 300-second
total budget. Reads use a separate session, one in flight per body client,
Comment thread
khaliqgant marked this conversation as resolved.
with increasing attempt bounds and jitter. Interactive clients keep the
single-shot 30-second default; writes are never retried by this policy.
Unretried timeouts retain `journal client: <verb> timed out after <ms>ms`.
Exhausted reads instead name the verb, attempts, elapsed time, total read
budget, and possible CPU load.

After a read budget expires, the CLI probes a fresh connection. A responding
daemon reports `daemon_unresponsive`; a failed probe reports
`daemon_unreachable` (also used for initial attach failures). A failed probe
cannot prove the daemon is dead: its diagnostic names both unreachability and
CPU load. Both reports exit 1, carry the known run/root id, retain
`status: running`, and include `flows resume`. This parks the CLI execution
without manufacturing a terminal journal fact; it does not claim a journaled
human park or verify journal integrity. The root worker session closes so the
kernel can recover its attempt, preserving completed work for resume.

Worker completion waits use `run.watch` pushes with a snapshot every two
seconds for the live lease deadline. A scoped watch session closes after each
wait because the protocol has no unwatch verb. A heartbeat timeout relinquishes
worker ownership as lease loss, leaving recovery to the kernel instead of
recording `worker_error` against the body.

A step that ran and failed is **not** one of those. It reports `step_failed`
with `status: failed`, for every step type and for authored TypeScript flows
as well as declarative ones. `protocol_error` is reserved for an outcome the
Expand Down
17 changes: 17 additions & 0 deletions evidence/run-read-timeout/baseline-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
The handshake comparison used an unmodified detached checkout of
`3c58ee16d10a9e2400db980f5bbafac84e437f20`. The two probe sources are copied
verbatim from the `/tmp` scripts named in their captured output. They use this
session's absolute checkout/build paths; adjust those paths when reproducing
on another machine.

Setup used (from the implementation checkout):

```sh
git worktree add --detach /tmp/relayflow-read-timeout-baseline 3c58ee16d10a9e2400db980f5bbafac84e437f20
ln -s /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/node_modules /tmp/relayflow-read-timeout-baseline/packages/sdk/node_modules
npm run build --prefix /tmp/relayflow-read-timeout-baseline/packages/sdk
```

The baseline probe loads SDK code from that checkout while keeping the daemon
binary and stub fixture path identical to the current-code probe. It compares
that one fixture handshake failure, not the full suite.
75 changes: 75 additions & 0 deletions evidence/run-read-timeout/final-focused.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
$ cd packages/sdk && RELAYFLOWD_BIN=/home/khaliqgant/Projects/AgentWorkforce/flows-worktrees/kernel-target/debug/relayflowd npx vitest run tests/journal-client-read-timeout.test.ts tests/journal-client.test.ts tests/journal-client-completion.test.ts tests/journal-client-subscriptions.test.ts tests/running-step-watch.test.ts tests/heartbeat-timeout.test.ts tests/run-daemon-unresponsive.test.ts tests/authored-root.test.ts tests/classify-outcome.test.ts tests/cli.test.ts tests/direct-run-worker-lease.test.ts tests/resume-worker-lease.test.ts tests/worker-lease.test.ts tests/worker-lease-lost.test.ts tests/worker-lease-lost-live.test.ts tests/worker-lease-sweep.test.ts tests/run-read-load-live.test.ts tests/flow-executor-chain.test.ts tests/agent-transcript-live.test.ts tests/human-live.test.ts tests/reuse-summary-interruption.test.ts tests/memoization.test.ts tests/authored-verifier-read-budget.test.ts tests/communication-worker.test.ts tests/communication.test.ts tests/helper-effect-lease-loss.test.ts tests/authored-helpers.test.ts tests/authored-flow-slack.test.ts tests/effect-channel.test.ts --maxWorkers=1 --minWorkers=1

RUN v2.1.9 /home/khaliqgant/Projects/AgentWorkforce/flows-worktrees/pr613/packages/sdk

✓ tests/cli.test.ts (71 tests) 5583ms
✓ flows check CLI > resolves a bare PATH-resolved claude with no declared model, in an isolated PATH 350ms
✓ flows run/resume CLI over the journal protocol > follows a dispatched worker step instead of reporting a protocol error 2045ms
✓ flows run/resume CLI over the journal protocol > follows a worker wait past a locally expired lease until the daemon settles it 2045ms
(node:783010) [FLOWS_ROOT_LEASE_LOST] Warning: authored root run_id=root-run attempt=1: lease_conflict: attempt has no active worker lease. Waiting for the kernel to retry it.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ tests/authored-root.test.ts (36 tests) 458ms
✓ tests/journal-client-read-timeout.test.ts (33 tests) 2247ms
✓ an exhausted budget during reader setup frees the queue for the next read 303ms
✓ tests/journal-client.test.ts (17 tests) 80ms
✓ tests/flow-executor-chain.test.ts (14 tests) 6820ms
✓ flow executor LLM and output-binding chain > runs f.llm -> f.agent -> f.run with schema-verified journal output and the exact allowed model 422ms
✓ flow executor LLM and output-binding chain > runs the exact authored flagship f.llm -> f.agent -> f.run path through the durable CLI root 999ms
✓ flow executor LLM and output-binding chain > resumes an interrupted durable authored root without replaying completed flagship effects 2666ms
✓ flow executor LLM and output-binding chain > passes a declarative verified value through an agent into a deterministic artifact 355ms
✓ flow executor LLM and output-binding chain > flows run consumes YAML bindings and resume reuses the original journal output 889ms
✓ tests/authored-flow-slack.test.ts (7 tests) 1271ms
✓ authored Slack helper effects > replays after SIGKILL before confirm with the same token and one successful completion 458ms
✓ authored Slack helper effects > replays after SIGKILL before complete with the same token and one successful completion 436ms
✓ tests/running-step-watch.test.ts (14 tests) 12309ms
✓ uses pushes for completion with lease-cadence reads and releases its watcher 2115ms
✓ cancels promptly while a lease snapshot read is in flight 2004ms
✓ a completion push ends the wait without waiting out an in-flight snapshot 2053ms
✓ a completion that aborts the snapshot is not an error even if a retry starts at once 2002ms
✓ a malformed watch frame during an in-flight snapshot is an error, not a completion 2002ms
✓ a watch dropped during an in-flight snapshot is reported at once, not after the read budget 2001ms
✓ tests/agent-transcript-live.test.ts (4 tests) 2755ms
✓ the transcript digest through the built CLI, a real daemon and the local agent > preserves structured agent failure details and its completed root index 662ms
✓ the transcript digest through the built CLI, a real daemon and the local agent > preserves structured llm failure details and its completed root index 686ms
✓ the transcript digest through the built CLI, a real daemon and the local agent > journals the digest in trajectory_tail on a successful agent step and writes the file it points at 792ms
✓ the transcript digest through the built CLI, a real daemon and the local agent > on a failed agent step, names the failure and the transcript in the terminal diagnostic, redacted 614ms
✓ tests/authored-helpers.test.ts (6 tests) 4017ms
✓ lowers the named acceptance helpers and Slack to confirmed journal effects 352ms
✓ runs every available provider through the real kernel and resumes completed effects without a second write 2254ms
✓ replays after SIGKILL before confirm with the same token and one successful completion 726ms
✓ replays after SIGKILL before complete with the same token and one successful completion 621ms
✓ tests/classify-outcome.test.ts (11 tests) 7422ms
✓ classifyOutcome > gives up and reports when a running run never becomes classifiable 2010ms
✓ the remedy on a worker park > follows a step through a retry backoff longer than the unclassified bound 3005ms
✓ the remedy on a worker park > follows a run.start outcome that is already running on a retried attempt 2001ms
✓ tests/human-live.test.ts (3 tests) 4840ms
✓ f.human against a real daemon > parks with the question, refuses wrong answers, records one, and resumes to success 2929ms
✓ f.human against a real daemon > a "no" is a value the body branches on: declined, exit 0, no effect 1217ms
✓ f.human against a real daemon > refuses to answer a run the daemon does not know 693ms
✓ tests/communication-worker.test.ts (20 tests) 1826ms
✓ tests/worker-lease.test.ts (8 tests) 12ms
✓ tests/worker-lease-lost.test.ts (17 tests) 20ms
✓ tests/communication.test.ts (10 tests) 14ms
✓ tests/effect-channel.test.ts (5 tests) 282ms
✓ tests/worker-lease-lost-live.test.ts (3 tests) 714ms
✓ tests/run-daemon-unresponsive.test.ts (5 tests) 6ms
✓ tests/memoization.test.ts (58 tests) 66ms
✓ tests/run-read-load-live.test.ts (2 tests) 2979ms
✓ completes a CPU-saturating deterministic flow with reads in flight and preserves its journal 2064ms
✓ drains read and watch promises before an authored flow completes 914ms
✓ tests/reuse-summary-interruption.test.ts (6 tests) 5ms
✓ tests/worker-lease-sweep.test.ts (4 tests) 6ms
✓ tests/journal-client-completion.test.ts (6 tests) 109ms
✓ tests/resume-worker-lease.test.ts (3 tests) 10ms
✓ tests/direct-run-worker-lease.test.ts (3 tests) 11ms
✓ tests/helper-effect-lease-loss.test.ts (1 test) 280ms
✓ tests/journal-client-subscriptions.test.ts (1 test) 10ms
✓ tests/authored-verifier-read-budget.test.ts (2 tests) 59ms
✓ tests/heartbeat-timeout.test.ts (1 test) 14ms

Test Files 29 passed (29)
Tests 371 passed (371)
Start at 06:16:46
Duration 66.56s (transform 1.62s, setup 189ms, collect 8.28s, tests 54.22s, environment 4ms, prepare 1.15s)

exit=0
Loading
Loading