Add durable harness wire client and shared views
Problem
An admission could be removed from durable pending state before its JSONL write succeeded, permanently losing the reply after stdout failure. Finished turns also discarded tool-step usage because the canonical metadata file contained only the final model step. Earlier review fixes cover ordered predecessor admissions and reopening tool details from canonical files.
Design
Rust owns the durable ordered outbox, cursors and renderer-independent ClientViews. Both submit and outbox flushing publish every admission through one ordered callback; stdin dispatch emits each exact admission frame. An indeterminate predecessor stops the drain and retains later intent. Admission validation and checkpoint reservation precede publication; a sink error leaves the exact command pending. Only after the sink succeeds does the admission record commit and remove retry intent.
Live deliveries and recovery use the same file fold. Bind the platform ContentResidencyVerifier with HarnessClient::with_files. Invocation JSON supplies the name and arguments; the writer's model_projection attachment supplies typed ToolResultContent; acyclic.model.metadata supplies usage. These reuse the context projection's existing bounded, descriptor-verifying artifact readers. The production writer sums all stored Completed events during its existing verified history scan and writes the whole-turn total to the same canonical metadata file. Live and canonical usage share one checked accumulator; omitted step metadata cannot erase earlier units. TurnOutput keeps its final-step provider metadata contract. Canonical files replace provisional live fields. Per-turn provisional usage is reconciled once, and delayed updates cannot overwrite committed results. File resolution and fallible accounting finish before stdout, append, cursor publication or acknowledgement. Missing read authority fails explicitly rather than publishing partial views.
Checkpoints retain only protocol identity, cursors and pending envelopes. Reopening starts the disposable projection at zero and replays canonical history. Recovery reads at most one checkpoint plus 63 tail records. Canonical stable-lock exclusion, protected journal framing, exclusive sidecar creation and short independent checkpoint names preserve committed data across replacement and aliases.
The headless runner selects stdin, live replay and independent operation-status follows; drains ready sources; writes exact descriptor-based protobuf JSON before cursor commit; and acknowledges after durable commit. Settlement is operation-bound. Exit codes: completed/Succeeded 0, Failed 1, Cancelled 2, Blocked 3, Indeterminate 4.
Decisions
- All model selection is explicit: the dedicated command sets RUSTFLAGS="--cfg acyclic_harness_loom". Cargo features and build.rs never enable the cfg. Ordinary all-features qualification skips the model target cleanly. build.rs only registers cfg names, retaining main's registrations too.
- The journal's only modeled mutex is the inode-lock registry; it is never nested or held across filesystem I/O. The production lock shim uses the OS nonblocking lock normally and loom in the dedicated model. The negative control disables exclusion on that same path.
- Filesystem state is authoritative; views are disposable query folds. There is no persisted transcript cache, new artifact schema, harness server, gateway or actor socket.
- Finish this reviewed path before migrating to gc-query. Final mapping: HarnessWireApi becomes the Streams transaction/follow client port; ServiceConnector becomes platform endpoint/local runtime selection; FileClientState becomes provider-backed outbox/cursor files and is deleted; ViewUpdate becomes followed task-volume files; duplicated TS transport/retry becomes platform I/O over the shared Rust/WASM client.
- Swarm traffic uses streams only. The interest set derives from layout/open-view files: latest whole-swarm status and attention, full-fidelity visible/prefetched agents, and seamless snapshot-at-cursor plus follow. gc-query owns materialized heads; Streams owns multiplexed interest diffs and backpressure; gc-e2e owns the 10k-agent scenario.
Deletions
Remove singular admission-return handling, duplicate artifact-reading logic, persisted transcript snapshots and duplicated assistant identity derivation. Share the production filesystem test fixture. The complete PR against its main merge base is +7818/-246, net +7572 (including regression/property/model/fuzz coverage). The latest two review fixes are +543/-57, net +486; they add 411 lines of scenario, negative-control and property tests.
Tests
- Signed head: d8023341fd7c68c14589bdfe5b3b3ab0e258a9d6. Remote bs client_session qualification: 73/73 in 6.243 s; build 50.17 s. Command: cargo nextest run --profile ci --locked -p acyclic-harness --all-features --test client_session.
- New admission regression covers failed JSONL stdout, an older queued command preceding new stdin input, retry/reopen and checkpoint boundaries. The negative control deliberately commits before publication and the shared retry oracle detects the lost intent. A shrinking property covers all valid admission decisions and checkpoint positions.
- New scripted-provider scenario uses the real filesystem writer for tool-then-answer execution, verifies whole-turn usage through assistant commit, idempotent retry and checkpoint/reopen/replay, including zero or omitted answer-step usage. The negative control substitutes the old final-only metadata file and the accounting oracle detects the loss. A shrinking property compares the exact shared production accumulator against an independent sum.
- client_history: 5/5 passed against the same production changes before the final fixture-only bound adjustment. Prior focused qualification covered client-commands and ordinary all-features model-target gating: 72/72 in 3.627 s.
- Unchanged journal synchronization: dedicated RUSTFLAGS="--cfg acyclic_harness_loom" model command passed 2/2 in 0.012 s, including its negative control; build 4m33s. No new synchronization in these fixes.
- Existing client_files cargo-fuzz target and five checked-in seeds passed ASAN on pinned nightly-2026-07-01: 5,231 runs in 4 s; build 3m56s. This predates the latest accounting/publication fixes; artifact codecs are unchanged. Existing client_recovery target/corpus covers JSONL, journal framing, paths and protobuf roundtrips. No toolchain escape environment is used.
- Existing terminal coverage: 7/7 in 0.01 s. Prior Windows GNU cross-check and focused native Windows stable-lock test passed; these fixes add no platform-specific behavior.
- Remote rustfmt, LF/NUL/source-size checks and git diff --check pass. Runtime suites remain below 10 s.
Live steps
- Now that gc-query is merged, replace the temporary recovery/transport representations with task-volume queries and provider-backed outbox/cursors, then bind the same reducer through WASM. No polling fallback.
- Windows symlink/junction aliases require a host with link-creation privileges.
- Hosted outbound HTTPS, bash compute and secret bindings need platform acceptance; use the in-process runtime until available.