You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PR #349 added build-revision self-identification (internal/version.Revision() reads debug.ReadBuildInfo's vcs.revision) so a stale driver binary is loud. A devagent binary built inside a linked git worktree stamps the wrong commit: Go's -buildvcs resolves vcs.revision from the shared common gitdir (the primary checkout's refs/heads/main tip) instead of the worktree's own HEAD. This defeats both consumers of #349's feature — but only for linked-worktree builds (a build in a normal checkout stamps correctly; see the control below).
Evidence — same commit 8ca0688, built two ways (2026-09-12)
Linked worktree (.devagent-worktrees/…, git rev-parse HEAD = 8ca0688) — wrong:
$ go build -o /tmp/dg-wt ./cmd/devagent
$ go version -m /tmp/dg-wt | grep -E 'mod |vcs'
mod github.com/FreePeak/devagent v1.13.6+dirty
build vcs=git
build vcs.revision=50b03e8d72869f066260f704b355fba112deddfa # = primary refs/heads/main (#350 merge), predates #349
build vcs.time=2026-09-12T20:48:05Z # = #350 merge time
build vcs.modified=true # worktree tree differs from the 50b03e8 it stamped
$ /tmp/dg-wt --version
0.1.0 (rev 50b03e8d72869f066260f704b355fba112deddfa)
$ git rev-parse HEAD
8ca0688993bf777ae74f7e11028932e33aee3e9a
Plain clone, detached at the same commit 8ca0688 (not a worktree) — correct:
$ git clone <local> /tmp/clone && git -C /tmp/clone checkout 8ca0688
$ go build -C /tmp/clone -o /tmp/dg-clone ./cmd/devagent
$ go version -m /tmp/dg-clone | grep vcs
build vcs=git
build vcs.revision=8ca0688993bf777ae74f7e11028932e33aee3e9a
build vcs.time=2026-09-12T21:24:20Z
build vcs.modified=false
$ /tmp/dg-clone --version
0.1.0 (rev 8ca0688993bf777ae74f7e11028932e33aee3e9a) # == its own HEAD
Same source commit; non-worktree stamps 8ca0688 (modified=false), linked worktree stamps 50b03e8 (modified=true, module reported +dirty). The rev string is confirmed to originate from vcs.revision in the build-info, so this is a worktree VCS-resolution issue, not a non-buildinfo source.
Impact (scoped to linked-worktree builds)
Every devagent binary built inside this repo's per-task worktrees (task/gate builds, ad-hoc verification like this one, record release if invoked from such a binary):
writes a wrong revision into release-created ledger rows (ReleaseRecord.Revision) — e.g. devagent record release … from the worktree binary above recorded "revision":"50b03e8…".
trips RunLoop's stale-binary guard, which compares version.Revision() against git rev-parse HEAD in the loop's cwd. Built-in-worktree stamp (50b03e8) ≠ worktree HEAD (8ca0688) → false [loop] WARN: stale binary on a current binary. The guard is advisory-only (loop still runs), so the cost is a false alarm that erodes the signal Diagnosis complete: loops 290/291 died on transient infra retries mid-implement #349 was added to provide.
The shipped self-update path is unaffected: scripts/self-update.sh does git pull --ff-only + go build -trimpath ./cmd/devagent in the primary non-worktree checkout, so the live driver stamps correctly (modified=false) and its guard behaves as intended. The live loop does not false-WARN today; the defect is latent for worktree-built binaries.
No user data is corrupted; ReleaseRecord.Tag/SHA are correct (fixed in #351) — only revision is mis-stamped, and the WARN is noise.
Suggested fix direction
Lead: follow the repo's existing -X stamp precedent (#249)..github/workflows/release.yml:134 already stamps -X github.com/FreePeak/devagent/internal/version.Version=${TAG} into every release binary precisely so --version self-reports the published release, and doctor.go:304 already special-cases the unstamped 0.1.0 default. Revision was added as a var of the same stampable shape — so add to the same ldflags:
Applied in release.yml and in scripts/self-update.sh:24 (which currently builds with no -X at all), this is the one-line route that fixes both consumers: release rows name the actual writing commit, and the stale-binary guard compares a trustworthy stamp. When the build injects Revision explicitly, version.Revision() should prefer the injected value over the (possibly mis-resolved) buildinfo vcs.revision. Note RevisionOverride is already the test-only injection seam; the production stamp should ride the same path or a dedicated -X var rather than that name.
Belt-and-braces for the guard (suppresses the WARN only): Go's build-info also exposes vcs.modified, and no git plumbing is needed — go version -m showed vcs.modified=true for the linked-worktree build and false for the clone at the same commit. So the guard can WARN only when Revision() != git rev-parse HEADandvcs.modified == false, staying quiet on worktree/+dirty builds without extra git rev-parse --absolute-git-dir/--git-common-dir calls. This preserves #349's actual target (a clean driver built from an older commit — self-update builds are clean, modified=false, so a genuinely stale seat still trips). Use it alongside the -X fix, not instead of it: on its own it leaves worktree-built release rows still naming the wrong commit.
Test gap:TestRevision only exercises the unstamped/unknown path, and CI's actions/checkout@v4 plain clone never reproduces the mis-stamp, so the suite stays green despite the defect. Add a case pinning the worktree condition (or the -X-stamped path and the modified-aware guard).
Notes
Surfaced while landing/verifying PR #349 (rebased onto 50b03e8, merged as 7aef356). Filed rather than fixed here because the remediation is a design decision for the feature owner; main is green and the tag/sha fix is independent. The 2026-09-13 PRD footer (#353, merged as 68c36cf, head d8d86fd) links this issue as #349's caveat.
Summary
PR #349 added build-revision self-identification (
internal/version.Revision()readsdebug.ReadBuildInfo'svcs.revision) so a stale driver binary is loud. Adevagentbinary built inside a linked git worktree stamps the wrong commit: Go's-buildvcsresolvesvcs.revisionfrom the shared common gitdir (the primary checkout'srefs/heads/maintip) instead of the worktree's own HEAD. This defeats both consumers of #349's feature — but only for linked-worktree builds (a build in a normal checkout stamps correctly; see the control below).Evidence — same commit
8ca0688, built two ways (2026-09-12)Linked worktree (
.devagent-worktrees/…,git rev-parse HEAD = 8ca0688) — wrong:Plain clone, detached at the same commit
8ca0688(not a worktree) — correct:Same source commit; non-worktree stamps
8ca0688(modified=false), linked worktree stamps50b03e8(modified=true, module reported+dirty). Therevstring is confirmed to originate fromvcs.revisionin the build-info, so this is a worktree VCS-resolution issue, not a non-buildinfo source.Impact (scoped to linked-worktree builds)
Every
devagentbinary built inside this repo's per-task worktrees (task/gate builds, ad-hoc verification like this one,record releaseif invoked from such a binary):revisionintorelease-createdledger rows (ReleaseRecord.Revision) — e.g.devagent record release …from the worktree binary above recorded"revision":"50b03e8…".RunLoop's stale-binary guard, which comparesversion.Revision()againstgit rev-parse HEADin the loop's cwd. Built-in-worktree stamp (50b03e8) ≠ worktree HEAD (8ca0688) → false[loop] WARN: stale binaryon a current binary. The guard is advisory-only (loop still runs), so the cost is a false alarm that erodes the signal Diagnosis complete: loops 290/291 died on transient infra retries mid-implement #349 was added to provide.The shipped self-update path is unaffected:
scripts/self-update.shdoesgit pull --ff-only+go build -trimpath ./cmd/devagentin the primary non-worktree checkout, so the live driver stamps correctly (modified=false) and its guard behaves as intended. The live loop does not false-WARN today; the defect is latent for worktree-built binaries.No user data is corrupted;
ReleaseRecord.Tag/SHAare correct (fixed in #351) — onlyrevisionis mis-stamped, and the WARN is noise.Suggested fix direction
Lead: follow the repo's existing
-Xstamp precedent (#249)..github/workflows/release.yml:134already stamps-X github.com/FreePeak/devagent/internal/version.Version=${TAG}into every release binary precisely so--versionself-reports the published release, anddoctor.go:304already special-cases the unstamped0.1.0default.Revisionwas added as a var of the same stampable shape — so add to the same ldflags:Applied in
release.ymland inscripts/self-update.sh:24(which currently builds with no-Xat all), this is the one-line route that fixes both consumers: release rows name the actual writing commit, and the stale-binary guard compares a trustworthy stamp. When the build injectsRevisionexplicitly,version.Revision()should prefer the injected value over the (possibly mis-resolved) buildinfovcs.revision. NoteRevisionOverrideis already the test-only injection seam; the production stamp should ride the same path or a dedicated-Xvar rather than that name.Belt-and-braces for the guard (suppresses the WARN only): Go's build-info also exposes
vcs.modified, and no git plumbing is needed —go version -mshowedvcs.modified=truefor the linked-worktree build andfalsefor the clone at the same commit. So the guard can WARN only whenRevision() != git rev-parse HEADandvcs.modified == false, staying quiet on worktree/+dirtybuilds without extragit rev-parse --absolute-git-dir/--git-common-dircalls. This preserves #349's actual target (a clean driver built from an older commit — self-update builds are clean,modified=false, so a genuinely stale seat still trips). Use it alongside the-Xfix, not instead of it: on its own it leaves worktree-built release rows still naming the wrong commit.Test gap:
TestRevisiononly exercises the unstamped/unknownpath, and CI'sactions/checkout@v4plain clone never reproduces the mis-stamp, so the suite stays green despite the defect. Add a case pinning the worktree condition (or the-X-stamped path and themodified-aware guard).Notes
Surfaced while landing/verifying PR #349 (rebased onto
50b03e8, merged as7aef356). Filed rather than fixed here because the remediation is a design decision for the feature owner; main is green and the tag/sha fix is independent. The2026-09-13PRD footer (#353, merged as68c36cf, headd8d86fd) links this issue as #349's caveat.