MINOR. Harness state I/O is backend-aware again. harness_state_dir / read_state_file / write_state_file route to <vault>/projects/<slug>/_harness/ when a live synced backend is active and gracefully degrade to device-local <project_root>/.harness/ otherwise — reversing ADR 0018 DC-1's device-local-only state I/O while keeping every other V5-3 decision (DC-2–DC-7) intact.
The discriminator is backend.capabilities.sync on the backend resolve_project already returns (duck-typed — the kernel never imports the vault plugin), not the presence of a vault_path config key. Precedence: a .harness/.project-mode=local opt-out wins, then a synced backend → vault, else device-local. This is not a return to the pre-V5-3 vault_path() probe-and-prefer tier — it routes through the V5-6 resolve_project seam.
It fixes the surfacing regression where an operator on a synced backend kept their PLAN/ROADMAP/progress in the vault but the kernel read the empty device-local .harness/, so the SessionStart hook never injected them. Storage-seam no-Path-leak and import-direction gates remain green (the three state functions are outside the gate's routing-function set by design).
Changed
-
Backend-aware harness state —
harness_state_dir/read_state_file/write_state_file(ADR 0020, reverses ADR 0018 DC-1). New private helper_state_backend_target(resolution)is the single routing decision: returns(backend, harness_locator, backend_root)when a synced backend should serve state, elseNone(device-local). Gated by_read_project_mode(resolution) == "local"(opt-out wins) →resolution["backend"].capabilities.sync(any exception →None, graceful degradation). Vault-absent / fresh-install / selection-raises all collapse to device-local through the existingresolve_projectgraceful path. -
Session-start hooks co-locate
progress.mdwith the resolvedPLAN.md(.sh+.ps1).harness-context-session-start.{sh,ps1}previously hardcodedprogress.mdto the device-local<cwd>/.harness/, which blocked the singleton injection on a synced backend even after the kernel fix. They now deriveprogress.mdas the sibling of whatever_harness/the bridge resolvedPLAN.mdinto, falling back to the device-local path only when no singleton plan resolved.
Internal
-
ADR 0020 — Backend-aware harness state. New decision record with DC-1–DC-6, full Consequences (positive / negative / load-bearing assumptions with re-audit triggers) and Related. ADR 0018 gains an
Amendment — 2026-06-19section recording that DC-1 is reversed while DC-2–DC-7 stand. -
Tests — backend routing coverage.
test_harness_memory.pygainsTestStateBackendRouting(7 cases) plus a CLI vault-wins-over-stale-repo case withOBSIDIAN_VAULT_SCRIPTSpinned for deterministic plugin selection;test_queue_status_lite.pygainstest_synced_backend_returns_vault. Vault fixtures seed LF-only bytes (write_bytes) so the byte-exact VaultBackend read is stable on Windows. CI green across Linux / Mac / Windows.