Add GitHub Codespaces ingress to the Windows shell - #410
Merged
coneilen merged 3 commits intoSep 22, 2026
Merged
Conversation
coneilen
force-pushed
the
coneilen-microsoft-windows-codespaces-ingress-parity
branch
2 times, most recently
from
September 22, 2026 18:39
c107bac to
036939a
Compare
The Windows shell could open a local folder, clone over HTTPS, and attach an SSH remote, but a codespace - the source the macOS welcome flow has always offered - had nowhere to go. This adds the fourth ingress with the same shape as the macOS sheet: discover, choose, validate, then open. Codespaces.zig is the client half and is pure logic, so it is testable without a window: it locates gh.exe, asks it for the account's codespaces as JSON, classifies the failures gh actually returns (missing CLI, unauthenticated, missing codespace scope, no network) into remediations a human can act on, redacts any token-shaped run out of gh output before it can reach a status line, and builds both the validation argv and the codespace:// project URI. Validation dials `gh codespace ssh` with BatchMode so it can never block on a prompt, and runs `git rev-parse` in the chosen path so an accepted identity is one whose sessions can actually start. WindowsCodespaceDialog.zig is the sheet. Its state lives in a Model that is exercised directly, so loading, empty, failure/retry, in-flight validation, cancel-while-dialing, and submit gating are all covered without driving Win32. The window itself is standard controls with dark WM_CTLCOLOR* painting, a STATIC label before each control so UIA names them, and IsDialogMessageW for tab and escape - no custom provider, so the existing accessibility harness sees a real control tree. No daemon protocol change was needed. RemoteProjectLocation already parses codespace:// and already dials through gh, so an accepted codespace opens via the same openProject call as every other source; GhLocator only lacked Windows paths for gh.exe, which is the one portable addition here. The recent-folders submenu is now located rather than indexed, because adding this menu item is exactly what would have silently retargeted that rebuild. Signed-off-by: Colin Neilens <coneilen@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The sanitizer test needs token-shaped input to prove it strips it, but a realistic-looking literal is the kind of thing a scanner flags and a reviewer has to stop and verify. Only the prefix and the shape matter to the redactor, so the fixture now says what it is. Signed-off-by: Colin Neilens <coneilen@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The row says exactly what the deterministic suite proves and exactly what it does not: a live connect was never exercised, because the available token has no codespace scope, so only the 403 path met real gh. Calling that Validated would make the ledger a worse signal than no row at all. Also notes that the Add Folder submenu is now located rather than indexed. Signed-off-by: Colin Neilens <coneilen@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
coneilen
force-pushed
the
coneilen-microsoft-windows-codespaces-ingress-parity
branch
from
September 22, 2026 19:07
036939a to
3cd8328
Compare
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.
TDD evidence
RED: zig test src\GraphModel.zig -> FAIL "project identity derives remote and global from Codable paths" at GraphModel.zig:1531, a codespace:// project is not reported as remote (56 passed; 1 failed)
GREEN: zig test src\GraphModel.zig -> All 57 tests passed
REGRESSION: pwsh Tools\windows\Tests\WindowsShell.Tests.ps1 -> All 96 tests passed, "Windows shell scaffold contract: PASS", exit 0
A second RED was taken at the integration point, by reverting only App.zig, MainWindow.zig and InputRouter.zig to their pre-change state while keeping the new harness contracts:
RED: pwsh Tools\windows\Tests\WindowsShell.Tests.ps1 -> throws "Windows shell contract: the Add Folder menu must offer codespace ingress next to the other repository sources"
GREEN: pwsh Tools\windows\Tests\WindowsShell.Tests.ps1 -> All 96 tests passed, scaffold contract PASS
Both REDs are behavior failures of the missing capability, not toolchain or fixture errors: the suites compile and run, and every other test in them passes.
What
Windows could open a local folder, clone over HTTPS, and attach an SSH remote. A codespace - the fourth source the macOS welcome flow has always offered (
CodespaceFormView/WelcomeFeature/CodespaceClient) - had nowhere to go. This adds it with the same shape: discover, choose, validate, open. Existing local/clone/SSH ingress is untouched.How
graphcode-windows/src/Codespaces.zig- the client half, pure logic so it tests without a window:gh.exe(GRAPHCODE_GHoverride, Program Files, WinGet Links/Programs, thenPATH)gh codespace list --limit 500 --json name,displayName,repository,state, parsed tolerantly (malformed rows dropped, unknown state kept as gh words it)codespacescope, no network, unreadable list - each into a remediation a human can act ongho_/ghp_/ghu_/ghs_/ghr_/github_pat_run, plus control characters, out of gh output before it can reach a status linecodespace://project URI%LOCALAPPDATA%\GraphCode\codespace.iniValidation dials
gh codespace sshwithBatchMode=yesso it can never block on a prompt, and runsgit rev-parse --show-toplevelin the chosen path, so an accepted identity is one whose sessions can actually start.graphcode-windows/src/WindowsCodespaceDialog.zig- the sheet. Its state lives in aModelthat is exercised directly, so loading, empty-account (create link instead of retry), failure plus retry, in-flight validation, cancel-while-dialing, inline-failure clearing, and submit gating are all covered without driving Win32. Selecting a codespace prefills its default workspace path and never overwrites a human edit.The window is standard controls with dark
WM_ERASEBKGND/WM_CTLCOLOR*painting, aSTATIClabel before each control in z-order so UIA names them,IsDialogMessageWfor tab, and Escape to cancel - no custom provider, so the accessibility harness sees a real control tree.Daemon protocol: no change needed. Checked first.
RemoteProjectLocationalready parsescodespace://and already dials through the GitHub CLI, so an accepted codespace opens through the sameopenProjectcall every other source uses. The only portable addition is Windowsgh.execandidates inGhLocator, which otherwise listed macOS paths only while still compiling into the Windows target.Also:
Project.isRemote()now acceptscodespace://so it groups with remotes rather than as a filesystem path; and the recent-folders submenu is located rather than indexed, because adding this menu item is precisely what would have silently retargeted that rebuild at the wrong item.Credential handling
No token or device-code secret is logged, echoed, persisted, or committed anywhere in this change.
ghowns the credential store; this code never reads it. Every byte of gh output passes throughsanitizeMessage, which strips token-shaped runs and control characters before it can reach a status line, and that stripping is asserted by test. The only persisted state is the codespace name and repository path. The single token-shaped literal in the tree is a synthetic redaction fixture (gho_EXAMPLENOTAREALTOKEN0000), labelled as such, and exists only so the redactor can be proven to remove it.Validation status - read honestly
Verified, with output quoted above: the client logic, the dialog state model, the menu/command/ingress wiring,
codespace://project identity, the harness contracts, and a cleanzig build.Not verified: a real end-to-end Codespaces connect. The token available here lacks the
codespacescope:So the failure path is exercised for real against live
gh, but discovery-success, selection against a real list, and a real validated dial are not exercised and are not claimed to be. This needs a reviewer with acodespace-scoped token to confirm. The parity ledger should record Codespaces connect as unverified pending that, not as validated.Separately and unrelated to this change: the Swift half of the suite cannot build on this machine (
error: missing required modules: '_complex', 'ucrt', andvcvars64.batreportsVsDevCmd.bat encountered errors). It fails compilingMailroomKit, which this PR does not touch. The Swift sources here parse clean under the 6.3.3 toolchain; the new XCTest inwindows-tests/needs CI to execute.