Repository navigation
fix(manager): hold a cached agent to the digest its release installed it with - #860
Draft
antoinetoussaint-byte wants to merge 2 commits into
Draft
antoinetoussaint-byte wants to merge 2 commits into
antoinetoussaint-byte wants to merge 2 commits into
Conversation
… it with The cache is keyed by name alone — <name>__<version> — and nothing compared its bytes with the release that name stands for. So a build left under a released version's name, by a developer or by a CI run that installed what it built, was run by every process resolving that version, silently and with nothing in any output to tell it from the release (parse__0.1.1 tonight was a hash-different local build). v0.19.8 (#819) already holds the downloaded archive to the release's digest and exports GitHubReleaseToken as the one credential rule. This completes it on the install side: Download records the installed executable's SHA-256 in a dot-prefixed sidecar beside it, invisible to every cache scan, and Load holds a cached executable to that record. A changed one is refused by name and both digests; one with no record is replaced by the release under remote resolution, or admitted under CODEFLY_AGENT_SOURCE=local, which is how a local build is run deliberately. InstalledReleaseDigest exposes the record for the CLI's version listing and publish verification. Consequence: a pre-existing cache entry has no record, so the first load after this upgrade re-downloads each release-resolved agent once. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
antoinetoussaint-byte
marked this pull request as draft
October 27, 2026 23:17
…Y_AGENT_SOURCE=local, record or not The digest-mismatch refusal ran before the local-source choice, so an agent rebuilt locally over a released name — the sidecar still describing the release's bytes — was refused even with --local-agents, which is the one way a developer runs a local build deliberately. The local choice now comes first: the build is admitted and the stale record removed, so a later remote load sees an entry with no record and fetches the release rather than trusting a record that no longer describes the file. The remote-resolution refusal now also names the local escape. Review finding on the previous head; the extended test fails there. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
v0.19.8 (#819) made the download side verifiable:
Downloadholds the archive to the release's digest (producer*checksums.txtfirst, GitHub's record second, a visible warning when there is neither) andGitHubReleaseTokenis the one, exported credential rule (GITHUB_TOKEN, elseGH_TOKEN, never aghlogin), named in the anonymous-404 error. This PR completes the install side that #819 did not carry:Downloadrecords the installed executable's SHA-256 in a dot-prefixed sidecar beside it (.<name>__<version>.sha256, sha256sum format). The dot keeps it out of every cache scan (findLocalLatestInDir,Installed, the CLI'slocalCacheVersions); tested.Load→admitCachedRelease: a cached executable with a record must match it or is refused by name and both digests. No record +CODEFLY_AGENT_SOURCE=local→ admitted (that is what the developer asked for). No record + remote resolution → the release is downloaded over it; if that fails the cached bytes are refused, naming theCODEFLY_AGENT_SOURCE=localescape. Gated to kinds that resolve from GitHub releases withAutoDownload, so providers and modules are untouched.InstalledReleaseDigest(ctx, agent)exposes the record ("", no error, for a cache entry never installed from a release) for future adoption by the CLI'sagents versionslisting andpublishverification — no cli consumer exists yet (cli main1901fbc6).Why
The cache is keyed by name alone, so a build left under a released version's name was run by every process resolving that version with nothing to tell it from the release.
parse__0.1.1tonight was exactly that: a hash-different local build.Consequence to know before merging
Every pre-existing cache entry has no sidecar, so the first load after this upgrade re-downloads each release-resolved agent once (under remote resolution). Under
CODEFLY_AGENT_SOURCE=localnothing changes.Provenance
Folded from the parked handoff branch
fix/release-token-and-digest@6b804b8a(based onb71ee8cd, pre-#819). Its token-rule and archive-digest halves were already on main through #819 with the same names (GitHubReleaseToken,FetchReleaseAsset/ReleaseAsset,DigestFromChecksums/DigestFromGitHub,checkArchiveDigest), so those files take main's version; this PR is the 195-line remainder (downloader.go,downloader_test.go,loader.go). One adaptation:readReleaseDigestuses main's two-valueparseChecksums. The handoff branch is left in place, unmodified (no force-push).Consumers
codefly-dev/cliwould adoptInstalledReleaseDigestincmd/agents/versions.go localCacheVersionsandcmd/publish/verify.go(future adoption; nothing on cli main consumes it today), pinning core at the tag this lands in, never this branch's SHA.Verified
go build ./...,go vet ./agents/manager/,go test ./agents/manager/ -count=1ok (15 s), includingTestReleaseDigestSidecarIsInvisibleToCacheScansandTestAdmitCachedReleaseHoldsTheCacheToItsRecord. Fullgo test ./...running locally at the time of opening; CI is the gate.🤖 Generated with Claude Code
Draft: CI finding (2026-10-27 23:12Z)
The
Buildjob fails insdk,services,toolbox/lang,toolbox/launch,toolbox/session(locally reproduced;runners/golangred locally only underGOFLAGS=-mod=mod, green without it). Every failure is the same shape: the test plants a locally built stub under a released version's name in a freshCODEFLY_HOMEand loads it under remote resolution;admitCachedReleasefinds no sidecar, tries the release (GET …/releases/tags/v71.0.0: 404) and refuses with theCODEFLY_AGENT_SOURCE=localescape. The admission rule works as designed; what it exposes is that core's own harness, andcodefly agent build→ run without--local-agents, rely on the cache admitting a local build by name.That is a behaviour decision, not a test fix: either (a) tests and
agent buildflows setCODEFLY_AGENT_SOURCE=local(the deliberate-local-build rule, as this PR states it), or (b) a cache entry with no sidecar stays admitted and only a mismatching sidecar refuses, with the re-download reserved for entries that carry a record. Marked draft until the owner picks; (b) keepsagent buildworking unchanged and still closes theparse__0.1.1hole for every agent installed by this downloader.Review finding 1 addressed — head
457a6bd1afe0cd9c6768afb395269b9abe272f86A local rebuild over a released name left the release sidecar beside bytes it no longer described, and the mismatch refusal ran before the local-source choice, so
--local-agentscould not run it. NowCODEFLY_AGENT_SOURCE=localis decided first: the build is admitted and the stale record removed (a later remote load then fetches the release instead of trusting the record). The remote refusal names the local escape.TestAdmitCachedReleaseHoldsTheCacheToItsRecordis extended with that case and fails on434fa934(Received unexpected error … a local rebuild over a release is what CODEFLY_AGENT_SOURCE=local asks for), passes here. The missing-sidecar policy under remote resolution is unchanged; the CI finding above still stands for the owner's call.