From a343a444562cb32d599f9996397f686bf6be60a2 Mon Sep 17 00:00:00 2001 From: Bojie Li Date: Sat, 22 Aug 2026 14:24:24 +0800 Subject: [PATCH] release: prepare 0.2.0 A minor rather than a patch: the release adds the target-first form and the Grok Build adapter, and removes --untrusted. Also clears the one thing standing between a tag and a published release. Releases are gated on CI, and CI has been red on main since before either of the last two merges: gosec reads `args[i+1:]` and `args[:i]` in parseFlags as possibly out of range, when i is an index obtained by ranging over args and both slices are in range by construction. gosec does not follow that through the reassignment of args, so the finding is annotated with its reason rather than the parser being reshaped for a linter on the eve of a release. The changelog's 0.2.0 section is also put back into Keep a Changelog order. Merging #20 left a Fixed block ahead of Added and a second Fixed block after Removed; they are now one Fixed section in the conventional position. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01PuwaXDJut6CorENbW1KHPk --- CHANGELOG.md | 58 +++++++++++++++++++++++------------------ cmd/reach/flags.go | 7 +++-- internal/reach/types.go | 2 +- 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ffc031..727132a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,34 +9,15 @@ details in closed harness binaries. Entries therefore name the harness versions a change was verified against: "works with Claude Code" is not a claim this project makes without a version attached. -## [Unreleased] +## [0.2.0] - 2026-08-22 -### Fixed +**A target now names a session.** `reach build-box claude` binds a session to +build-box and starts the agent against it in one line; the two-step `reach up` +form remains for sessions that outlive a single agent. `--untrusted` is gone — +see below for why the guarantee it advertised was always weaker than the flag +implied. -- **A harness on PATH through a relative entry was reported as not installed.** - Since Go 1.19 `exec.LookPath` returns a binary found through a relative PATH - entry together with an error, so that a caller testing `err != nil` refuses to - run it. reach tested exactly that, for every harness, and told the operator - `claude is not installed or not in PATH` about a binary they could launch by - name in the very same shell — false about the one thing the message asserts, - and it sends someone off reinstalling a harness that was never missing. - - The obvious repair did not work either, which is what made it worth fixing - rather than documenting. `LookPath` stops at the first match, so appending an - absolute entry to PATH left the earlier relative one matching first and - failing the same way; the operator watches the fix that should plainly work - change nothing at all. - - A relative PATH entry is an ordinary thing for a person to keep, and bash runs - what it finds there without comment, so reach now does too. The lookup stays - `exec.LookPath` rather than a hand-rolled PATH walk — on Windows executability - is PATHEXT and not a file mode — and only the refusal is dropped. What reach - does not inherit is the ambiguity: the result is resolved to an absolute path - while the working directory is still the one the lookup assumed, because a - path left relative names a different file after any chdir, and this one is - handed to exec and to the seam probe. The probe resolves harness binaries the - same way, so it cannot verify a seam for one binary and hand the operator - another. +Verified against Grok Build 1.0.5, which this release adds an adapter for. ### Added @@ -125,6 +106,31 @@ project makes without a version attached. ### Fixed +- **A harness on PATH through a relative entry was reported as not installed.** + Since Go 1.19 `exec.LookPath` returns a binary found through a relative PATH + entry together with an error, so that a caller testing `err != nil` refuses to + run it. reach tested exactly that, for every harness, and told the operator + `claude is not installed or not in PATH` about a binary they could launch by + name in the very same shell — false about the one thing the message asserts, + and it sends someone off reinstalling a harness that was never missing. + + The obvious repair did not work either, which is what made it worth fixing + rather than documenting. `LookPath` stops at the first match, so appending an + absolute entry to PATH left the earlier relative one matching first and + failing the same way; the operator watches the fix that should plainly work + change nothing at all. + + A relative PATH entry is an ordinary thing for a person to keep, and bash runs + what it finds there without comment, so reach now does too. The lookup stays + `exec.LookPath` rather than a hand-rolled PATH walk — on Windows executability + is PATHEXT and not a file mode — and only the refusal is dropped. What reach + does not inherit is the ambiguity: the result is resolved to an absolute path + while the working directory is still the one the lookup assumed, because a + path left relative names a different file after any chdir, and this one is + handed to exec and to the seam probe. The probe resolves harness binaries the + same way, so it cannot verify a seam for one binary and hand the operator + another. + - **`reach harness verify grok` could not reach a verdict.** Three faults, each of which alone was enough to fail the probe, and together they meant `reach grok` refused to launch at all without `-force`. The probe's diff --git a/cmd/reach/flags.go b/cmd/reach/flags.go index 55f80ec..52be500 100644 --- a/cmd/reach/flags.go +++ b/cmd/reach/flags.go @@ -17,8 +17,11 @@ func parseFlags(fs *flag.FlagSet, args []string) ([]string, error) { var tail []string for i, a := range args { if a == "--" { - tail = args[i+1:] - args = args[:i] + // G602: i is an index into args from ranging over it, so both + // args[i+1:] and args[:i] are in range by construction. gosec + // does not follow that through the reassignment of args. + tail = args[i+1:] //nolint:gosec // i came from range over args + args = args[:i] //nolint:gosec // i came from range over args break } } diff --git a/internal/reach/types.go b/internal/reach/types.go index f0e29e7..79fca9a 100644 --- a/internal/reach/types.go +++ b/internal/reach/types.go @@ -12,7 +12,7 @@ import ( // Version is the reach release. It is part of the remote helper cache path, so // bumping it forces a fresh helper install rather than silent reuse of a stale // binary. -const Version = "0.1.1" +const Version = "0.2.0" // ExecRequest is a command to run on a target. type ExecRequest struct {