Uh oh!
There was an error while loading. Please reload this page.
feat(builder): enforce BuildRequest.output_directory is absolute (#333) - #335
Merged
antoinetoussaint-byte merged 2 commits intoAug 21, 2026
Merged
Conversation
Expose a shared guard that enforces the BuildRequest.output_directory contract — when set, the destination must be absolute; empty selects the legacy in-agent build. The guard lives next to the recipe contract in core (where the proto is defined) so the CLI, which populates the field in a separate repo, imports and calls it before sending a BuildRequest. The invariant cannot be enforced in BuildDockerBuildPlan: its tree walk is relative-safe, so an IsAbs guard there would reject valid callers rather than catch the real failure at the point the field is set. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fe7p9irTPJPkQBcpRVYJzg
…dary (#333) The prior change only exported ValidateBuildRequestOutputDirectory; nothing in core called it, so merging enforced nothing and closing #333 would have declared victory before any code path guarded the invariant. A relative output_directory would still sail through: the agent resolves it against its own working directory while the caller expects an absolute location it owns, so the recipe handshake breaks silently with no error. Wire the guard into BuilderWrapper.DockerBuildRequest — the universal chokepoint every agent runner routes a BuildRequest through, which already validates the build-context kind. It is BuildRequest-specific, so it rejects only contract-violating callers (relative, non-empty), never a valid one; this refutes the "wrong layer" objection that applies to the generic BuildDockerBuildPlan tree walk. Enforcement is now real on every agent build. Also pin the nil-request boundary case in the validator test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fe7p9irTPJPkQBcpRVYJzg
Uh oh!
There was an error while loading. Please reload this page.
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 freeto 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.
Closes#333.
Summary
BuildRequest.output_directoryas "an absolute destination owned by the caller", but nothing asserted absoluteness. A relative path would resolve against the agent's working directory while the caller expects an absolute location it owns — the recipe handshake would then break silently, with no error.BuilderWrapper.DockerBuildRequest— the universal chokepoint every agent runner routes aBuildRequestthrough (golang + rust), which already validates the build-context kind. So the invariant is guarded on every agent build, not left to a caller that might never call it.BuildRequest-specific, so it rejects only contract-violating callers (relative, non-empty) and never a valid one. Empty stays valid (legacy in-agent build). This is why the check lives here and not inBuildDockerBuildPlan: that helper takes a bare destination and itsWalkDirinventory is relative-safe, so a guard there would reject valid callers of a generic function rather than catch the violation at the request boundary.ValidateBuildRequestOutputDirectoryis exported so the CLI can also resolve-and-validate before sending, but enforcement no longer depends on that.Test plan
go test ./agents/services/passesTestValidateBuildRequestOutputDirectorycovers nil (valid), empty (valid), absolute (valid), relative (rejected)TestDockerBuildRequestEnforcesAbsoluteOutputDirectoryproves the boundary rejects a relative destination and passes absolute + empty throughgo vet ./agents/services/andgo build ./runners/...clean🤖 Generated with Claude Code