ci: run the Go agent E2E suite on Windows, and fail if it skips - #60
Merged
Merged
Conversation
windows-e2e never ran any of the 22 agent E2E tests. Its only go test step ran before the job downloaded the binaries, and findE2EBinary's ../../dist fallback was the literal "..._linux_amd64" at every call site, so on Windows (where build.sh also appends .exe) it looked for a file that could never be there. Every test skipped and the job went green. Nothing in either job would notice the Linux step going the same way either: go test reports skips as a pass. windows-e2e now runs `go test -v ./cmd/meowshell/` in a new step after the artifact is downloaded into its private staging directory, with MEOWSHELL/TAILCAT set explicitly to the Windows binaries (returned unchecked, so a broken binary fails instead of skipping). It runs before the testderp step, which exports TAILCAT_DERPMAP_URL; these tests use TS_DEBUG_TAILCAT_LOCAL_DERP per server, as in the build job. The job timeout goes from 15 to 25 minutes to cover the second run. findE2EBinary now takes the bare binary name and builds the fallback from runtime.GOOS/GOARCH the way build.sh's build() does (armv7 for arm, .exe on Windows). It logs a fixed marker when it resolves a binary and puts one in its skip message. e2e/require-agent-e2e-ran.sh wraps the go test in both E2E steps and fails the step if any test skipped for a missing binary, or if no lookup resolved at all (tests filtered out, -v dropped). *.sh is pinned to eol=lf so the windows-latest autocrlf=true checkout does not hand Git Bash a CRLF script. The .NET E2E suite needs no change for this: it runs only in the Linux dotnet job, and its lookup has no hardcoded platform name. CLAUDE.md now says so, and describes the new Windows state. Verified on linux/amd64 with .tailcat-src set up per CLAUDE.md: go vet ./... and GOOS=windows go vet ./... pass, as does GOOS=windows go test -c ./cmd/meowshell/. go test ./... passes except TestListenUnixAllowsRootOwnedStickyTmpStyleParent, which fails the same way on the parent commit (it only runs as root, and this sandbox runs as root). Through the guard with explicit binaries: 26 lookups resolved, 0 skips, exit 0; again with the env vars unset, going through the new fallback name. Mutations: dist/ moved aside -> 22 skips, go test PASS, guard exit 1; -run limited to the naming test -> guard exit 1 for no lookups; nonexistent explicit binaries -> go test fails and the guard passes that through; the .exe suffix dropped -> TestE2EDistNameMatchesBuildSh fails. actionlint is clean. Nothing here has run on Windows; only a windows-e2e run shows that the suite passes there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013WM5HyhwRLKBDyHR6qitkk
The first windows-e2e run of the agent E2E suite passed all but four subtests, each one a test assuming the server side was Unix: - exec with a nonzero exit read back 1, not 42. The server runs exec commands through its user's shell, which on Windows is `pwsh -Command`; that turns a failing native command's exit code into 1, so `sh -c 'exit 42'` could never report 42. On Windows the test now sends `exit 42`, which PowerShell exits 42 on. - The shell channel saw its marker echoed but never exited: the test typed Enter as \n. A Unix pty maps a terminal's \r to \n, so that worked there; the Windows server's ConPTY does not, and PowerShell took \n as Ctrl+Enter, a new line rather than a submitted command, so "exit" sat unrun until the 30 s frame read timed out. Enter is now \r everywhere, which is what a real terminal (the app's xterm.js included) sends. - Two SFTP mode checks read back 0666 for 0640 and 0600. Windows has no Unix permissions; Go's os.Chmod there only toggles the read-only attribute from the owner write bit. servedFileMode says what a server on this platform reports; the Linux job still checks the exact bits. Verified on linux/amd64: the agent E2E suite through e2e/require-agent-e2e-ran.sh passes (26 lookups resolved, none skipped), so \r works through a Unix pty; GOOS=windows go vet is clean. The Windows-only branches are proven only by windows-e2e itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013WM5HyhwRLKBDyHR6qitkk
Owner
Author
|
Generated by Claude Code |
Sniperlyf3
added a commit
that referenced
this pull request
Sep 25, 2026
A healthy shell could be failed with "channel data is not being consumed fast enough". AgentChannelDataPump queued at most 32 items per channel, whatever their size, so a burst of small PTY output frames (a prompt redraw, a line-at-a-time command) filled it before the pump caught up. dotnet-android-e2e hit exactly that on #60: the probe's SSH session was faulted on a commit whose sibling run passed the same job. The bound is there to cap memory held for a consumer that has stopped, and 32 items capped that at a few hundred bytes for small frames while allowing 1 MiB for 32 KiB ones. The queue is now unbounded as a channel and bounded by what it holds: at most 4 MiB of payload, plus a 16384-item cap so a flood of empty frames cannot grow the queue's own overhead. Bytes are released once an item is delivered, not when it is dequeued, since the item handed to a stalled sink is still in memory. An empty queue always takes the next item, so a single frame larger than the limit is never mistaken for backpressure. Verified: dotnet test dotnet/Meowshell.sln 227/227. New tests: a burst of 4096 three-byte frames into a stalled sink does not fault; a frame over the limit is accepted into an empty queue; a consumer that keeps up receives 3x the limit without faulting. Each rule was mutated and a test failed: item cap back to 32 (burst test), no empty-queue exemption (oversize test), no byte limit (stall test), bytes never released (running-total test). The two existing stall tests now overflow by bytes. Claude-Session: https://claude.ai/code/session_013WM5HyhwRLKBDyHR6qitkk Co-authored-by: Claude <noreply@anthropic.com>
Sniperlyf3
added a commit
that referenced
this pull request
Sep 25, 2026
Five E2E test classes each carried the same binary lookup, and each returned null -- "no binaries, skip" -- when a DOTNET_E2E_* variable was set but named a file that did not exist. The tests treat null as a local run and return early, so a mistyped or not-yet-built path in CI turned the whole real-binary suite into a green no-op. That is the trap the Go agent E2E suite fell into on Windows until #60, and CLAUDE.md listed the .NET half as still open. The five copies now share E2EBinaries.Sources(): both variables unset still skips, as a local run should; one set without the other, or either naming a missing file, throws with the variable and path. CLAUDE.md says so in place of the "still goes quiet" note. Verified: E2EBinariesTests pin all three outcomes. dotnet test dotnet/Meowshell.sln passes 231/231 with the variables unset, and 231/231 against the real linux_amd64 binaries with e2e/start-testderp.sh running, as CI runs it. With DOTNET_E2E_MEOWSHELL_BIN pointed at a missing file, 26 real-binary tests fail with "does not exist"; before this change the same configuration passed. Claude-Session: https://claude.ai/code/session_013WM5HyhwRLKBDyHR6qitkk Co-authored-by: Claude <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.
Problem
windows-e2ehas never run any of the 22 agent E2E tests, and still went green.go teststep ran before the job downloaded the binaries.findE2EBinary's fallback name was hardcoded..._linux_amd64, so on Windows (which also appends.exe) the file could never be found. Every test skipped.go testcounts skips as a pass.Fix
windows-e2estep: it runsgo test -v ./cmd/meowshell/after the artifact download, withMEOWSHELL/TAILCATset explicitly to the Windows binaries.TS_DEBUG_TAILCAT_LOCAL_DERPper server.findE2EBinary: it now builds the fallback name fromGOOS/GOARCHthe waybuild.shnames its output, and prints fixed markers when a lookup resolves or skips.TestE2EDistNameMatchesBuildShpins the naming.e2e/require-agent-e2e-ran.sh: wraps both E2E steps (Linux and Windows). It fails the step if any test skipped for a missing binary, or if no lookup resolved at all..gitattributes:*.shis noweol=lf. The Windows runner checks files out with CRLF line endings (autocrlf), which breaks bash scripts.CLAUDE.md: updated to describe the new Windows state.Verification (linux/amd64)
go vet ./...andGOOS=windows go vet ./cmd/meowshell/pass.TestListenUnixAllowsRootOwnedStickyTmpStyleParentfails only as root, as it does on main. It was excluded from the local run; CI runners aren't root.dist/moved aside: 22 tests skip andgo testpasses, but the guard fails the step.-runfiltered to the naming test: the guard fails because nothing resolved.go testfails and the guard passes the failure through..exesuffix dropped:TestE2EDistNameMatchesBuildShfails.windows-e2erun is the first time the suite runs there, so real platform failures are possible.🤖 Generated with Claude Code
https://claude.ai/code/session_013WM5HyhwRLKBDyHR6qitkk
Generated by Claude Code