Skip to content

fix(ahoy): an explicit ABCD_BIN_TARGET is a sandbox, not a hint - #520

Merged
REPPL merged 3 commits into
mainfrom
fix/bin-target-suppresses-adoption
Aug 26, 2026
Merged

fix(ahoy): an explicit ABCD_BIN_TARGET is a sandbox, not a hint#520
REPPL merged 3 commits into
mainfrom
fix/bin-target-suppresses-adoption

Conversation

@REPPL

@REPPL REPPL commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Closes the sandbox escape #515 deliberately left open, and the one it labelled a design decision rather than a bug fix.

The defect

effectiveBinTarget answers which PATH entry a verb acts on. It preferred any owned entry it could find, falling back to binTarget() only when there was none.

In production that is correct: ABCD_BIN_TARGET is unset, and an install that finds an abcd it already owns should update that one rather than leave a second copy elsewhere. Adoption is how ahoy install stays idempotent.

Under test it escapes the sandbox. hermeticRepo's own docstring says it "redirects HOME, the plugin root and the PATH symlink target to temp locations" — sandboxing the install target is its stated contract. The preference silently overrode it, so an ahoy install reached from such a test adopted a real PATH entry and rewrote it to point inside a tempdir the test then deleted. The machines that dogfood the installer are exactly the machines with an owned entry to find.

The fix

An explicitly set ABCD_BIN_TARGET wins. A caller that names a target has asked for a specific file, and the only callers that name one are tests asking for a sandbox. Adoption is untouched when the variable is unset, which is every production path.

The precondition, measured

The naive statement of this bug overclaims, and the record says so rather than repeating it. classifyBinTarget calls an entry owned only for a symlink into the plugin root, a dev shim, or a regular file whose provenance is recorded at ~/.abcd/path-entry and whose bytes still hash to the recorded sha. A plain release binary with no provenance record classifies binTargetForeign and was never adopted.

That is why a release-only install on this machine survived two full go test ./... runs today byte-identical, while a dev-shim install on the same machine sits squarely in the at-risk population. A peer session supplied the survival datum, which is what prompted measuring the precondition instead of asserting it — a test pinning the wrong precondition would have gone green while guarding nothing.

Tests

Two, both halves:

  • TestExplicitBinTargetSuppressesAdoption — watched failing against the old adoption order, which returned the owned entry instead of the named sandbox.
  • TestAdoptionStillWinsWhenBinTargetIsUnset — guards the production behaviour the fix must not change.

Checks

make preflight exit 0, gofmt -l . clean. Branched from current origin/main. Resolves iss-2608261447262355, whose record moves open/resolved/ in this diff, so RS001 is satisfied.

Assisted-by: Claude:claude-opus-5

effectiveBinTarget answers which PATH entry a verb acts on, and it answered
by preferring any owned entry it could find, falling back to binTarget()
only when there was none. In production that is right: ABCD_BIN_TARGET is
unset, and an install that finds an abcd it already owns should update that
one rather than leave a second copy elsewhere.

Under test it escapes the sandbox. hermeticRepo's own docstring says it
"redirects HOME, the plugin root and the PATH symlink target to temp
locations", so sandboxing the install target is its stated contract; the
preference silently overrode it, and an `ahoy install` reached from such a
test adopted a real PATH entry and rewrote it to point inside a tempdir the
test then deleted. The machines that dogfood the installer are exactly the
machines with an owned entry to find.

So an explicitly set ABCD_BIN_TARGET now wins. Adoption is untouched when it
is unset, which is every production path.

The precondition is narrower than the naive statement of the bug, and the
record says so rather than overclaiming: classifyBinTarget calls an entry
owned only for a symlink into the plugin root, a dev shim, or a regular file
whose provenance is recorded at ~/.abcd/path-entry and whose bytes still
hash to the recorded sha. A plain release binary with no provenance record
classifies foreign and was never adopted, which is why a release-only
install on this machine survived two full suite runs untouched while a
dev-shim install on the same machine sits in the at-risk population.

Two tests, both halves. The suppression case was watched failing against the
old adoption order; the unset case guards the production behaviour the fix
must not change.

Resolves: iss-2608261447262355

Assisted-by: Claude:claude-opus-5
@REPPL
REPPL enabled auto-merge August 26, 2026 15:02
@REPPL
REPPL added this pull request to the merge queue Aug 26, 2026
Merged via the queue into main with commit 68ae95c Aug 26, 2026
11 checks passed
@REPPL
REPPL deleted the fix/bin-target-suppresses-adoption branch August 26, 2026 17:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant