Skip to content

fix(windows): build resolvable file URIs for artifacts and environments - #918

Merged
devlikebear merged 2 commits into
mainfrom
fix/artifact-file-uri
Aug 22, 2026
Merged

fix(windows): build resolvable file URIs for artifacts and environments#918
devlikebear merged 2 commits into
mainfrom
fix/artifact-file-uri

Conversation

@devlikebear

Copy link
Copy Markdown
Owner

Summary

Artifact and environment URIs were built as url.URL{Scheme: "file", Path: path}. On Windows that renders C:\dir\result.txt as file://C:%5Cdir%5Cresult.txt — the drive letter becomes the URI host and every separator is percent-encoded. Nothing could resolve it: filepathFromURI rejects a URI with a host, so artifact lookup failed outright.

I previously reported this as cosmetic, on the grounds that these are recorded identifiers rather than something opened. That was wrong. The managed execution plane test opens exactly this URI:

open C:%5CUsers%5C...%5Cresult.txt: The system cannot find the file specified

Adds internal/fileuri and uses it at all four build sites and the one parse site:

Site
executionplane/artifact_collector.go build + parse
executionplane/worktree_environment.go build
executionplane/local_environment.go build
workerprotocol/artifact_quarantine.go build

New makes the path absolute, converts to forward slashes, and guarantees the leading slash a file URI needs ahead of a drive letter. Path reverses it, dropping that slash only when a drive letter follows.

Compatibility

Nothing recorded is invalidated:

  • unix — an absolute path already starts with a slash and ToSlash is a no-op, so old and new URIs are byte-identical. Existing records keep resolving.
  • Windows — the old form never resolved, so there is nothing to stay compatible with. Path rejects it explicitly rather than guessing at it, and there is a test case for that.

releasedArtifact now returns an error, since building a URI can fail.

The test that hid this

filepathFromArtifactURI in the tarsserver tests stripped "file://" and treated the rest as a path. That is why a malformed URI got as far as open() instead of failing at parse. It now resolves URIs the way production does.

Test plan

  • go build ./... for windows, linux, and darwin
  • go vet on the touched packages under Windows and Linux
  • internal/fileuri — round trip, URI shape, relative paths, and five rejection cases
  • ./scripts/windows_test.sh — exit 0
  • TestManagedWorkExecutionPlaneRunsLifecycleAndPreservesSource passes on Windows (was failing)
  • Linux suite — CI is the gate, no Linux environment here

Measured against the pre-change tree on Windows, comparing normalized failure sets so run-to-run timings do not pollute the diff:

Package Before After
executionplane + workerprotocol 23 failing 21 failing

No new failures. The two that now pass are TestArtifactSanitizersAndURIContracts — the URI contract test itself — and TestFileArtifactCollectorCapturesTrackedAndUntrackedGitPatch. The remaining 21 are the pre-existing Windows issues that keep both packages on the excluded list (symlinks, POSIX file modes, ssh/container policy).

🤖 Generated with Claude Code

devlikebear and others added 2 commits August 22, 2026 12:09
Artifact and environment URIs were built as url.URL{Scheme: "file", Path:
path}. On Windows that renders C:\dir\result.txt as
file://C:%5Cdir%5Cresult.txt: the drive letter becomes the URI host and every
separator is percent-encoded. Nothing could resolve it — filepathFromURI
rejects a URI with a host, so artifact lookup failed outright.

This was reported earlier as cosmetic, on the grounds that these are recorded
identifiers rather than something opened. That was wrong: the managed
execution plane test opens exactly this URI, and it failed with

    open C:%5CUsers%5C...%5Cresult.txt: The system cannot find the file
    specified

Add internal/fileuri and use it at all four build sites and the one parse
site. New makes the path absolute, converts to forward slashes, and
guarantees the leading slash a file URI needs ahead of a drive letter. Path
reverses it, dropping that slash only when a drive letter follows.

Nothing recorded is invalidated. On unix an absolute path already starts with
a slash and ToSlash is a no-op, so old and new URIs are byte-identical; on
Windows the old form never resolved, so there is nothing to stay compatible
with. Path rejects it explicitly rather than guessing.

releasedArtifact now returns an error, since building a URI can fail.

The tarsserver helper that reads these URIs used to strip "file://" and treat
the rest as a path, which is how it accepted a malformed URI and then failed
at open() instead of at parse. It now resolves them the way production does.

Windows: internal/executionplane and internal/workerprotocol lose two
failures and gain none, and the managed execution plane test passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI's diff-coverage gate failed at 74.6%, and the uncovered lines were almost
entirely the error branches this change had just added: four callers each
handling a failure from fileuri.New that no test can reach.

The error was never real. New made the path absolute with filepath.Abs, which
fails only when the working directory cannot be determined, and every caller
already holds an absolute path. An unreachable branch in four places is the
signal that it does not belong in the signature.

New now returns a string. releasedArtifact goes back to returning one value
too. The change is smaller than before it: 39 changed coverable lines rather
than 59, at 97.4% covered rather than 74.6%, because the lines that went away
were the ones nothing could exercise.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@devlikebear
devlikebear merged commit d9f9861 into main Aug 22, 2026
11 checks passed
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