0025 Latest-Release Self-Update And Human-Directed Conflicts
Date: 2026-07-22
Status
Accepted and implemented on the feature branch; release availability follows merge and core publication.
Context
The installed harness executable previously compared consumer files only with the core embedded in that same executable. An installed v0.1.4 therefore could not discover v0.1.8 even though the product documentation presented harness update as the future-upgrade command. Re-running the mutable remote bootstrap selected a newer binary, but that made the installed update command misleading.
The transactional updater also stopped safely on overlapping edits but retained no resolution workspace. A coding agent could explain a semantic conflict, yet there was no resumable path for a human to authorize the choice and the agent to provide the accepted merged document.
Decision
- The application layer owns one self-update use case. Infrastructure ports provide release download, checksum verification, candidate execution, durable retention, and executable replacement;
main.rsonly composes them. harness updateresolves the publishedharness-v*core-release pointer for the current platform, downloads the binary and SHA-256 sidecar from that exact versioned GitHub release, verifies the bytes, requires the candidate's reported version to equal the pointer version, and rejects a version older than either the installed core or executing binary. It does not use GitHub's global latest release because only the dedicated core pointer defines the expected asset inventory.- The candidate owns interpretation and installation of its embedded payload. Direct version jumps use the exact installed base and the candidate payload; a future incompatible provenance schema must reject explicitly rather than invent an upgrade graph in advance.
harness statusandharness doctorremain local and network-free. Status reportsexecutable_outdatedwhen provenance is newer than the executable's embedded core.- A clean update uses the existing backup, journal, activation, and provenance transaction, then replaces the repository-local executing binary last. A normal self-update may mutate only when the repository executable and its repository path components are regular files rather than symlinks. The checksum-verified candidate subprocess and platform installer are explicit internal handoff paths.
- An overlapping merge changes no managed file, installed provenance, or executable. It retains BASE, LOCAL, UPSTREAM, RESOLVED, and the complete frozen managed-file input set under
.harness-core/update/; the candidate is retained separately under ignored.harness-core/update-candidate/. - Harness does not choose conflict semantics. An agent explains concrete behavioral differences, obtains human direction when a material choice exists, and edits the RESOLVED file.
- A normal
harness updatealways plans directly from installed provenance to the current release pointer. If a resolution is already pending, a mutating normal update replaces it with the new plan; dry-run previews the new plan without removing the pending session.harness update --continue --dry-runpreviews the accepted resolution for its exact pinned candidate;--continueremotely re-verifies the retained candidate and rejects remaining conflict markers, malformed staged inputs, or drift in any managed workspace path before verification and application under one lock.--abortremoves only the staged session. - Platform installers run a staged candidate before replacing the installed binary. On conflict they retain the candidate and leave the old executable; rerunning the installer after resolution automatically continues the exact pending version. This also supports the one-time bootstrap boundary.
- A verified candidate remains durable until executable replacement succeeds. If core activation succeeds but replacement fails or the parent process stops, the next update repairs the executable from that exact remotely re-verified core version before starting another core update.
Alternatives Considered
- A custom signed channel with delegated keys and an upgrade graph. Deferred because the current manually invoked, GitHub-hosted product does not yet justify that operational complexity. It remains a possible response to a stronger publisher-compromise threat model.
- Require users to rerun
curl | bashfor every upgrade. Rejected because it keeps the installed update command unable to perform its documented job and repeatedly executes a mutable remote script. - Write conflict markers into live managed files. Rejected because a partially resolved
AGENTS.mdorWORKFLOW.mdcould immediately corrupt agent authority and would weaken the all-or-nothing update contract. - Build an interactive merge editor into Harness. Rejected because coding agents already have strong diff/edit tools; Harness should preserve inputs, enforce authority boundaries, and apply the result safely.
Consequences
Positive:
- An installed executable can discover and install later releases directly.
- Conflicts become explainable and resumable without exposing partial policy files to agents or other repository users.
- Human judgment governs semantic choices while mechanical staging, verification, drift detection, and activation remain automated.
- The existing release assets, embedded payload, merge rules, and transaction engine remain the primary implementation.
Tradeoffs:
- Existing executables released before this decision cannot discover the first self-updating release and require one final platform-installer refresh.
- Updates require
curland network access; status and diagnostics do not. - SHA-256 sidecars protect integrity relative to the selected GitHub release but do not establish an independent trust root against publisher compromise.
- Core activation and executable replacement remain two ordered filesystem phases, but the verified candidate is durable and a later update or installer run recovers version skew before applying another core version.
- Structural conflicts such as missing managed files are not representable as text resolution packets and must be corrected before restarting.