Add durable LocalHarness bootstrap and stock execution

Merged
VA
2d ago
+10319-6750

Problem

The promised LocalHarness composition was missing. Default file tools wrote to scratch, fresh branch checkout lacked its staging parent, and reopen discarded branch selection. Git mutations lacked a publication lease; shell commands repeatedly imported the project. Interrupted uploads and expired command windows also needed recovery.

Design

LocalHarness::open(LocalHarnessConfig::new(path, (model, provider))) assembles the existing filesystem/Streams providers, owner authority and grants, project/scratch/journal volumes, durable admission, and stock executor. Config fields are private; defaults come from tools::defaults(), custom tools are additive, and .without_default_tools() opts out.

File tools and bash/fs use one serialized selected checkout. Configured-volume mode binds the same executor to a persistent private native view of the actual volume: materialize its existing content once before binding, then reuse the lazy view and restore its durable branch on reopen. File publication uses the committed upload generation and its actual parent, including recovery before capture. The existing materialization journal retains completion so retries repair missing checkout edits without overwriting later user edits. Reopen restores the durable branch selection. Staging parents are created and validated before publication.

Git carries an operation-window permit derived from the complete admitted task fence through capture, fork and mutation; native effects recheck ownership. Fork publication checks the exact lease gate atomically with retention and child creation. Expired/interrupted windows reconcile before another lease opens. Both recovery and final close publish the returned reconciled generation before releasing the checkout lock; switching branches never publishes the old workspace over the destination. A recovered pending switch updates the in-memory selected view before returning its retry error. Bash uses the shared native process path and a persistent push watcher; arriving directories are registered before capture, with bounded subtree enumeration.

Deletions

Remove the private scripted provider, interruption wrapper, redundant signing/runtime handles, public project conversions, duplicate git/path helpers and nested fuzz package/lockfile. Preserve main's small git::run/NativeDirectory APIs and consolidate every fuzz target/corpus into harness-fuzz. README edits stay in #509.

Against merged main bf727ef89: +9676/-6684, net +2992; excluding the 3,924-line nested lockfile deletion, net +6916. This includes the contained default toolset and mechanical operation-window module moves.

Decisions

  • Merge main through bf727ef89; commits are signed. No README changes.
  • #515 should drop 876799c2e982a6b0b961e1a454e9b4c097a10cf5 (Bind local coding tools to the selected project). The shared binding lives here once.
  • Credentials live outside the checkout in per-user state keyed by the canonical workspace/volume path. Generate the key with getrandom; use Unix 0700/0600 permissions. dirs/getrandom are optional workspace dependencies selected by filesystem-local.
  • Bootstrap internals remain private. Tests use the shared ScriptedModelProvider. Attach/capture/publication identities are domain separated. Suspended work advances only when wakeable; retained failures return TaskFailed.
  • Model synchronization is enabled only by RUSTFLAGS cfg(acyclic_harness_loom), plus cfg(acyclic_loom) for the actual MemoryStream locks. No Cargo feature/build.rs switches synchronization. Models include broken negative controls and documented lock order. Ordinary builds assert that the model cfg is off.
  • Retain the existing filesystem materialization journal as completion state; add no separate recovery store. Use schema-sized authority/lease buffers and funded future boundaries; never raise stack limits.
  • Decisions 6/10: no new service, gateway, daemon, socket, actor channel or submission protocol. Communication uses provider transactions/follows.
  • Volume mode uses the existing generation materializer for the initial private OS view, then the same lazy view, watcher, publisher and native process path as checkout mode. No second tool executor or per-command full-project materialization is introduced.
  • Reconciliation publication identities bind the operation, phase, source and target generation; a shrinking property checks every field. No lock or lock order changed; the existing instrumented checkout serialization models and broken controls cover that unchanged boundary.
  • Review finding 1 is explicitly delegated to stacked #645 and its immediate follow-up. Task-file writes driving execution and query-visible per-delta chunks/tool records/terminal status are not part of #495. #645 publishes the owner/provider binding; its follow-up binds existing TaskVolume query/observe to durable admission and fenced execution. Complete the GraphCoder bootstrap repointing in #514 afterward. #515 now contains the Pi owner/root-provider/native-authority deletions, so use that shared removal rather than maintaining a competing copy; the runtime admission/native-action binding still needs completion. This PR does not claim those deletions are complete.

Watch hydration keeps missing ancestors in canonical path order, probes proper prefixes without allocating them, and retires ordinary entries in one stable compaction. Shrinking properties compare ancestry and retirement with exhaustive expectations and bound item/copy work. Broken repeated-removal and sibling-scan controls violate those bounds.

Tests run

Focused Linux gates through bs.ps1; built binaries executed directly with RUST_MIN_STACK unset:

  • LocalHarness: 29 passed, 5.25 s; direct binary with RUST_MIN_STACK explicitly unset: 29 passed, 5.46 s, at d03c81abf: write then bash cat, fresh commit/checkout -b, selected branch reopen/capture, lazy echo, arriving/renamed subdirectories, expired/interrupted window reopen, upload recovery and non-destructive replay, original model reservation, grants/defaults, owner and codec/fence properties. New regressions cover configured-volume write/bash reads, commit/child checkout/reopen, close-time and recovery-time parent rebase publication, and a recovered pending switch followed by capture into the selected branch.
  • Watch hydration: 3 shrinking properties passed, 0.11 s, including real repeated-removal and exhaustive-sibling-work negative controls. Existing native capture-policy scenarios: 9 passed, 0.20 s, using the built filesystem binary with RUST_MIN_STACK unset. Both suites ran at d03c81abf through bs.ps1. Reusable filesystem binary: /mnt/bb/targets/sdk/gc-local-readme-ca6640d8/wt-sdk-gc-local-readme-ca6640d8.1/debug/deps/acyclic_fs-7182ea349df94090.
  • Runtime/recovery: 25 passed, 1.18 s. Every required future is below 4 KiB (largest 3,976 B); reconciliation retains exactly its progress allocation. Previously overflowing native-model lost-ack test passes.
  • Completion-identity property and ordinary-sync assertion: 1 passed each, <0.01 s. Native-content regressions: 6 passed, 0.01 s. Shared git integration: 6 passed, 0.04 s. Client usage property/retry/negative control: 3 passed, 0.35 s.
  • Current library built at d03c81abf; unchanged git integration was verified at ebac1a1e2 and client usage at 0809f5ecc. bb1 cache: /mnt/bb/targets/sdk/gc-local-readme-ca6640d8/wt-sdk-gc-local-readme-ca6640d8.0/debug/deps/ (acyclic_harness-e704bd052a096173, git_executor-f625635c6a9a5612, client_session-186d19f95f688ab1).
  • Reused successful models for unchanged serialization/authority/store paths: 4 passed, 0.07 s, including broken controls; MCP real codec/cancellation models: 6 passed, <0.01 s. Updated model-qualification script: 4 passed, 0.095 s after merging main's Actors qualification.
  • Earlier focused gates on unchanged paths: default tools 10/0.03 s, goal files/relocated corpus 12/0.97 s, compaction 13/0.02 s, family authorization 1/<0.01 s, command properties 2/<0.01 s, file codec 1/0.04 s, moved filesystem operation-window suite 17/0.12 s. Plugin consumer compiled at 55814530a; path isolation 4/0.02 s.
  • Windows focused native lazy-checkout regression: 1 passed, 0.97 s at b12b94e5b. No thread-stack overrides or stacker. git diff --check passes.

Review-fix delta from ebac1a1e2: +821/-74, net +747 lines, mostly regression/property coverage. All new suites are below 10 s. No README changes.

Live steps

macOS held-source watcher runtime validation remains pending: last ivar check found 99% disk usage and 2.9 GiB free; no build was started. Finish the TaskVolume binding and #514 deletions above. Hosted Actors outbound HTTP, bash compute and secret bindings remain platform integration work; local mode uses the existing providers. The two pre-existing native-content assertions from #535 were already fixed in #518 and pass here; no duplicate PR is needed.

Conflicts-only main merge: signed head d085c32c140fcef3fa072e6bf0631dbdd034b09f includes origin/main at 5aae2e7a54aae93d5d56388471abfc447d4b7875. Preserved main's bounded authority-path frames and timer qualifier; retained the consolidated harness fuzz package, including main's new targets and corpus. git diff --check, conflict-marker checks, manifest/lock dependency checks and target/include path checks passed. No Rust rebuild for this merge. The focused qualifier script test is queued while bb1 is under maintenance; prior runtime results above are from the pre-merge head.

Reconciliation interruption fix (947937230)

Problem: the operation window could acknowledge a durable rebase before its native checkout publication. Reopen then saw no remaining window work and could capture stale disk bytes over that rebased generation.

Design and Decisions: retain (publication operation, exact source generation) in the existing filesystem provider's Git compatibility state before recovery/close can reconcile. Clear this marker only after the existing native materialization journal acknowledges publication. Open completes marker recovery before exposing tools; subsequent command and project-file capture paths also finish any retained publication first. The original selected workspace, root identity, publication retry identity, task-owner verification, native journal and Git-state CAS remain the authorities. This adds no separate store, transport, global runner or lock. Source generations stay pinned in the durable marker across both ticket acknowledgement and process loss. Completed native journals suppress replay over later user edits.

Deletions: replace the transient-only reconciliation publication path. This fix is +612/-29, net +583 lines, including the crash sweep, negative control, acknowledgement/codec properties and fuzz corpus/target extension. Finding 1 remains delegated to #645 and its follow-up; no task-file runner was added here.

Verification: the crash property exhausts the marker, ticket claim, durable rebase, ticket acknowledgement, every native publication guard and final acknowledgement boundaries for generated content. Each prefix drops LocalHarness, reopens, compares disk with the rebased generation, then exercises the stock bash capture path and asserts that capture did not change that generation. A deliberately premature acknowledgement reproduces the stale-disk invariant failure. Exact-marker acknowledgement and Git-state codec properties use 64 cases; the production record fuzz target now includes the Git state and a checked seed. All synchronization remains on the existing modeled checkout guard/provider CAS; no primitive or lock order was added.

Validation pending bb1 maintenance: one Rust build is queued, followed by separate direct-binary LocalHarness and interruption-probe suites, with RUST_MIN_STACK unset and timings printed. No second Rust build is queued. Existing timings above describe earlier heads, not this unbuilt fix. Local formatting, LF/crash-damage checks, corpus/include and manifest/lock checks, and git diff --check passed. Signed commit; no README changes. Stop here for re-review; post the two suite timings when the queued job completes.

Approved follow-up after merge

Signed conflicts-only main merge: bf564b4e78273ba8a079f1e7d708a7f9b5d3ca7a, including main 87af90a89633315b0f90bd0a3c13abdf3ad6e00a. The merge retains main's borrowed git executor/error types, automatic context binding, plan-tool removal and new fuzz targets/corpus; preserves this PR's checkout publication/fence checks; and repoints cleanup callers to main's native-runtime directory helper.

The maintainer explicitly requested merging #495 with these four known findings deferred to an immediate follow-up PR from main:

  1. P1: resumable directory preparation after durable pending=true, including the crash before directory creation. Re-derive original custody/identities and finish idempotently. Replace the incomplete guard-only crash coverage with an enumeration of every durable write in the publish path and a broken negative control.
  2. P2: carry the pending marker from the existing Git-state load used for selection, skipping recovery's redundant load when absent; add a work-counting assertion.
  3. P1: concurrent first opens of a configured volume must use unique staging directories and atomic winner publication, with concurrent-open model/property and negative control.
  4. P1: native file publication identities must include (operation, staged path), with a two-path/same-operation regression and retry coverage.

The two bot threads were resolved as approved deferrals, not as completed fixes in #495. The queued LocalHarness/interruption batch remains pending bb1 maintenance; it will see this merged worktree at admission. The existing interruption sweep is known incomplete per finding 1, and no new runtime pass is claimed. No further #495 changes are planned. #645 work is preserved and paused until this follow-up.