Target the gap after an external commit
Cloudflare announced experimental OpenCodeHarness on October 12, 2026, embedding OpenCode 2.0 in a plain Durable Object. It requires Agents SDK 0.28.0 or later, @opencode/sdk@2, and @opencode/plugin@2. Cloudflare assigns the model loop, transcript, tools, and session recovery to OpenCode; Lifecycle supplies storage and durable jobs. To test recovery, consider an agent that creates a remote issue before its host disappears without receiving the receipt. The experiment must distinguish three outcomes: the conversation persisted, execution resumed, and the external action happened the intended number of times. Sources: Cloudflare
The research checked on October 14, 2026, supports a proposed experiment, not a verdict on exactly-once external effects. No eviction test was run, and the reviewed material supplies no confirmed force-eviction command. The fetched source entry point exports interfaces rather than exposing the recovery implementation or a test suite. OTF’s October 13, 2026, walkthrough provides guidance but no measured interruption trace or duplicate-effect count. Treat the procedure below as a test design whose reset mechanism must be established before execution. Sources: Cloudflare, OTF
Separate stored state from completed work
Cloudflare’s reference says sessions, messages, and durable events use the object’s SQLite database, with an opencode_ prefix on tables and indexes. Active sessions have heartbeat jobs, and an alarm wakes the object after eviction or a crash. An unused OpenCode host closes after 30 seconds without work or event subscriptions and reopens on demand. Keep that idle-host behavior separate from an interruption during an active tool call. The latter tests how execution handles unfinished or uncertain work. Sources: Cloudflare
Durable Object storage is private, transactional, and strongly consistent, according to Cloudflare; in-memory state can disappear during eviction or ordinary deployments. Those database properties do not by themselves coordinate a transaction with a remote issue service. OpenCode’s Workerd profile also replaces unavailable local filesystem and process services. Embedding the loop therefore does not establish that a desktop checkout or shell survives alongside it. The table below separates documented facilities from the observations needed to assess recovery. Sources: OpenCode, Cloudflare
Table
Documented recovery facilities and proposed checks
| Facility | Documented behavior | Proposed check |
|---|---|---|
| SQLite persistence | Stores sessions, messages, and durable events | Compare records across interruption |
| Heartbeat jobs | An alarm wakes the object after eviction or a crash | Observe activation before reconnecting |
| Submission identity | operationId deduplication is session-scoped | Resubmit the same operation |
| Durable replay | Omits uncommitted live text deltas | Compare durable cursor and transcript |
| Point-in-time recovery | Restores database contents within the previous 30 days | Distinguish database restoration from task continuation |
Build an observable tool and a controlled pause
Start in a disposable namespace and record the environment before introducing faults. Capture exact package versions, the lockfile, source commit, Wrangler version, compatibility date, and deployed Worker version. Include the model identifier and settings, plus the object, session, and operation identities. Keep those records with each trial’s evidence. This gives later comparisons a stable reference and helps distinguish a recovery problem from an environment change.
Use a separate test service with an append-only build ledger or mock issue-creation API. Give every logical action a key, record each request attempt, and count committed effects separately. After committing an entry, have the service signal the test controller and withhold its response. This creates a known acknowledgement gap; overwriting a file with identical contents could conceal repeated execution. Record evidence independently of shutdown hooks because OpenCode’s documentation says eviction can happen without cleanup. Sources: OpenCode
- Run an uninterrupted baseline and match the receipt to one committed ledger entry.
- Pause the service after its commit but before it returns the receipt.
- Interrupt the host using a verified runtime-reset facility and record the mechanism.
- Keep the client quiet for a predetermined interval while observing object activations externally.
- Reconnect to the original object and session, then reconcile the operation with the ledger.
- Repeat with destination-side uniqueness enforcement on the logical action key.
Observe recovery before reconnecting
Once a reset mechanism is established, label local runtime resets and controlled deployed restarts as separate conditions. Record the exact mechanism for every trial so the results identify what was interrupted. A client reconnect immediately afterward could supply activity that conceals a failed autonomous wake-up. Observe external telemetry first, then reconnect after the predetermined quiet interval. That ordering targets Cloudflare’s documented alarm-based recovery path. Sources: Cloudflare
Treat a client disconnect as a separate control, not proof that the execution host disappeared. Cloudflare distinguishes aborting a wait, which stops the wait, from aborting a session, which can interrupt work. OTF also recommends testing object restart and client disconnect separately and reconnecting to the same object. Run the proposed matrix below against the same observable tool and preserve traces for every trial. Include two successive interruptions to test whether recovery can make progress after another failure. Sources: Cloudflare, OTF
Table
Proposed interruption and control matrix
| Condition | What to assess |
|---|---|
| No interruption | Tool and observation setup |
| Before remote commit | Completion of unfinished work |
| After commit, before response | Repeated effects after an uncertain outcome |
| After durable tool-result recording | Preservation of completed progress |
| Client disconnect only | Transport loss versus execution loss |
| Same operation submitted again | Submission deduplication |
| Two successive interruptions | Recovery after another interruption |
Reconcile SQLite, durable events, and the ledger
Discover the deployed schema before assuming internal table names. The query below lists tables and indexes so inspection can follow the actual database structure. Use the results to identify session records, operation state, durable events, and job state. Capture snapshots before interruption and after recovery, alongside activation identifiers and deployment versions. Keep inspection outside the critical timing window where possible so observation does not disturb the intended pause.
SELECT type, name, tbl_name, sql
FROM sqlite_schema
WHERE type IN ('table', 'index')
ORDER BY type, name;Cloudflare says messages() returns active context after compaction, while history() returns the full transcript. Durable replay uses session.log() and event.durable.seq, excluding uncommitted live text deltas. Record the last durable cursor before interruption, any durable tool result, and the final operation outcome. Cloudflare defines done as answered; unanswered covers failure, interruption, withdrawal, or absence. Plugin tools may appear under the execute wrapper, so account for that representation when examining events. Match these records to external receipts rather than accepting the assistant’s final message as proof of a committed action. Sources: Cloudflare
Count attempts and effects separately
Repeat the experiment with the destination atomically enforcing uniqueness on the logical action key and returning the original receipt for repeats. A repeated request with one committed effect differs from two committed mutations. Cloudflare’s operationId deduplicates submissions within a session and returns accepted: false on reuse. OTF recommends application-level task IDs and API deduplication, which can complement that submission control. Suppressing a retry without resolving its uncertain outcome could instead leave requested work incomplete. Sources: Cloudflare, OTF
For each condition, report trial counts, successful completions, unresolved operations, tool attempts, committed effects, missing transcript records, and additional model usage. Separate restart-to-first-progress from restart-to-completion, and publish raw traces and failures. Report three distinct outcomes: session restored, execution resumed, and effects counted. Keep reliability claims proportional to the sample. Before expanding deployment, examine which identifiers survive recovery, how interrupted tools are resolved, and what happens when retries are exhausted. Repeat critical cases across an SDK or schema upgrade to check recovery against changed state.