Skip to content

Windows-native + WSL + Linux compatibility, verified by executed checks#17

Closed
RadioactiveCheese wants to merge 15 commits into
NateBJones-Projects:mainfrom
RadioactiveCheese:feat/win-linux-compat
Closed

Windows-native + WSL + Linux compatibility, verified by executed checks#17
RadioactiveCheese wants to merge 15 commits into
NateBJones-Projects:mainfrom
RadioactiveCheese:feat/win-linux-compat

Conversation

@RadioactiveCheese

Copy link
Copy Markdown

Supersedes #16 (same branch, same commits — reopened from the correct account/fork). The three Codex review findings from #16 are already addressed on this branch: e8cff49 (STILL_ACTIVE check in win32 pid_is_alive), 2db0795 (fix-swarm validator verify commands through Git Bash), 83272b2 (same POSIX-shell routing swept across all seven kit validators).

What

Makes Ringer run natively on Windows (and stay green on WSL, Linux, and macOS), fixing every macOS/POSIX-only assumption found by a 5-scout Ringer review swarm over the whole repo. Also merges fix/open-folder-cross-platform, whose folder_opener_command() (with WSL wslpath → Explorer translation) supersedes the interim endpoint fix.

Key fixes

  • P0 — checks ran under cmd.exe on native Windows (asyncio.create_subprocess_shell → COMSPEC), so every POSIX check died with '{' is not recognized while workers succeeded. Checks now dispatch through Git Bash on win32 (auto-detected; RINGER_CHECK_SHELL override; the WSL launcher in System32 is rejected; loud rc-127 error instead of a silent cmd.exe fallback).
  • P0 — os.kill(pid, 0) liveness probes in hooks/ringer_nudge.py and ringer.py terminate the target on Windows (TerminateProcess); reading active runs could kill a live run. Replaced with non-destructive OpenProcess/GetExitCodeProcess probes (STILL_ACTIVE required).
  • P1 — timeout cleanup used os.killpg (absent on Windows), orphaning worker process trees; now taskkill /T (/F on kill) with graceful fallback.
  • P1 — fix-swarm patch exporter captured git diff in text mode: cp1252 decode corrupted UTF-8 and injected CRLF on Windows. Now byte-faithful.
  • Validators in all kits now run user build/render/proof commands through the same POSIX shell resolution as the core check runner.
  • Ringside open-folder endpoint: macOS open only → folder_opener_command() for macOS/Windows/Linux/WSL (from the merged branch).
  • Dashboard fileHref produced broken file://C:%5C... URLs for drive-letter paths; Finder-specific copy neutralized.
  • Templates: 6 kits hardcoded /opt/homebrew/bin/python3.14; all check commands now use a {{PYTHON}} placeholder.
  • Tests: USERPROFILE isolation (so tests can't touch the real home dir on Windows), sqlite handles closed, /tmp and symlink and Mac-only-path assumptions removed, plus one date time-bomb defused.
  • Docs: native Windows quickstart (python ringer.py, Git Bash requirement, RINGER_CHECK_SHELL); known limitations documented (child-count telemetry on Windows, macOS-only Seatbelt wrapper).

Verification (all executed)

Proof Windows native WSL Ubuntu
Nested ringer run with POSIX-syntax check PASS PASS
Kill helpers terminate a live child PASS PASS
Pid probe leaves live processes running; dead-unreaped reads dead PASS PASS
All 7 kit validators execute POSIX commands via run_user_command PASS POSIX branch unchanged
Full test suite 132 passed (pytest) 132 passed (unittest)
ringer.py demo (3 real codex workers) 3/3 PASS (was 0/3 before the fix) 3/3 PASS

All fixes were produced by Ringer fix swarms in isolated worktrees with executed verify scripts as checks; MODEL-NOTES carries the run evidence and process lessons.

Known out of scope: the parked hud/ Tauri prototype has two documented P2 findings (bash-only beforeBuildCommand, $HOME-scoped asset protocol) left unchanged — not verifiable here without a Tauri build.

🤖 Generated with Claude Code

RadioactiveCheese and others added 10 commits July 8, 2026 13:43
The /api/open-folder endpoint was hardcoded for macOS: every other
platform got HTTP 501, which the frontend surfaces as a button that
silently does nothing — on the very platforms the README points at
("Windows via WSL").

Replace the darwin-only branch with folder_opener_command(), which
picks the platform's opener:

- macOS: open (unchanged)
- native Windows: explorer.exe
- WSL (detected via WSL_DISTRO_NAME / WSL_INTEROP / /proc/version):
  convert the path with wslpath -w and hand it to explorer.exe, so
  the folder opens in Windows Explorer via \wsl.localhost
- other Linux: xdg-open when present

501 is still returned when no opener exists for the platform.

Verified on Windows 11 + WSL2 Ubuntu: endpoint returns 204 and
Explorer opens the artifacts directory.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…xporter

- ringer.py: dispatch manifest checks through Git Bash on win32 (RINGER_CHECK_SHELL
  override; clear 127 error when absent) instead of cmd.exe; taskkill /T for
  process-tree termination; non-destructive ctypes pid probe; os.startfile /
  xdg-open for the open-folder endpoint; skip doomed `ps` spawn on win32;
  platform-aware stdin display strings.
- hooks/ringer_nudge.py: pid_is_alive probed with os.kill(pid, 0), which
  TERMINATES the target on Windows; now uses OpenProcess/GetExitCodeProcess.
- dashboard: fileHref handles drive-letter paths (file:///C:/...), normalizes
  backslashes; Finder-specific copy neutralized.
- templates/fix-swarm/checks/fix-swarm.py: export patches byte-faithfully
  (git diff captured as bytes; cp1252 text capture corrupted UTF-8 and
  injected CRLF on Windows).

Verified by executed checks on native Windows: nested ringer run with a
POSIX-syntax check passes, kill helpers terminate live children, probe leaves
live processes running, node tests for fileHref, scratch-repo export/apply
round-trip, and `ringer.py demo` 3/3 PASS (was 0/3).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ardcodes

- templates: replace /opt/homebrew/bin/python3.14 and bare python3 in check
  commands with the {{PYTHON}} placeholder; cross-platform note in README.
- README: native Windows quickstart (python ringer.py, Git Bash requirement,
  RINGER_CHECK_SHELL), Windows config path, engine caveats.
- config.sample.toml: Windows config location; macOS-only examples labeled.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Isolate USERPROFILE alongside HOME so Path.home() on Windows stays inside
  test temp dirs; absolute ringer.py path instead of a symlink requiring
  Developer Mode; close sqlite handles (Windows file locks); route /tmp
  hardcodes through tempfile.gettempdir(); inline the design-reference tokens
  that previously lived at an absolute path on the original dev machine;
  deterministic API error-path test; date assertion no longer pinned to the
  day it was written.

Suite: 132 passed natively on Windows (pytest) and on WSL Ubuntu (unittest).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… lessons)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
OpenProcess succeeds for exited processes whose handles are still held
(e.g. an unreaped child), which kept dead runs alive in active-runs
reconciliation on Windows. Mirror the nudge hook: GetExitCodeProcess and
treat only STILL_ACTIVE (259) as live. Reproduced before the fix (exited
Popen child read True), verified after (False; live child unharmed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n win32

The validator's run_shell used shell=True (cmd.exe via COMSPEC), so a
POSIX-syntax {{BUILD_OR_TEST_COMMAND}} failed inside the validator even
though ringer.py now runs outer checks under Git Bash. Mirror ringer.py's
shell resolution (RINGER_CHECK_SHELL, PATH bash rejecting System32/WSL,
Git-for-Windows locations); rc 127 with a clear message when none exists.
Verified on Windows: a verify command using test/grep/{ ...; exit 1; }
passes through the validator, and a failing one still fails with output.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…shell

Seven kit validators (bakeoff, asset-swarm, test-hardening, focus-group,
repo-feature, doc-swarm, research-with-proof) ran build/render/proof
commands with shell=True — cmd.exe on native Windows — bypassing the Git
Bash contract that ringer.py's check runner now guarantees. Each script
gets the same _windows_posix_shell resolution and a run_user_command
helper preserving its original cwd/timeout/stderr semantics, with rc 127
and an actionable message when no POSIX shell exists. Verified by
importing all seven and executing test/{ ...; } commands through them
natively on Windows; POSIX branch unchanged; suite 132 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…used

Console processes commonly refuse taskkill without /F; the old fallback
killed only the tracked parent with proc.terminate(), and since the parent
then exited, the timeout path never escalated to kill_process_group — the
worker's children survived. terminate_process_group now retries with
taskkill /T /F before any parent-only fallback. Reproduced (grandchild
survived) and verified fixed (parent + grandchild both die) on Windows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@RadioactiveCheese

Copy link
Copy Markdown
Author

Heads-up for review continuity: a fourth Codex finding landed on #16 just before it was closed (P1 — graceful taskkill /T failure orphaned worker children). Addressed here in 380dc78: escalate to taskkill /T /F before any parent-only fallback. Reproduced and verified with a parent+grandchild tree probe on native Windows and WSL; full suite 132 green on both. All four findings from #16 are now fixed on this branch (e8cff49, 2db0795, 83272b2, 380dc78).

justfinethanku and others added 3 commits July 9, 2026 17:41
…act model attribution

Four changes from Jon's UX review (2026-07-09), built as a 4-task Ringer swarm
(4/4 first-attempt PASS, executed checks) plus orchestrator review:

- Model attribution (the "gpt-5.6 isn't tracked" bug): eval rows and the HUD
  state payload logged model="" for every codex task, and read-time registry
  resolution mis-credited all of them to GPT-5.5. Now the effective model is
  stamped at write time — task.model, else engine model_default, else parsed
  from the actual composed command (effective_model_from_command). New
  {model_args} placeholder makes the manifest "model" field work for codex
  (expands to "-m <model>" only when a model is resolved). Registry gains
  GPT-5.6 Sol/Luna/Terra (cited: OpenAI codex models page).
- scripts/backfill_model_from_logs.py: re-attributes historical empty-model
  rows from "[ringer.py] command:" lines in raw worker logs. Evidence-only
  (no guessing), dry-run, timestamped backup, atomic rewrite, idempotent.
- Work gating: the live status artifact renders "The work" for FINISHED tasks
  only (placeholder until the first finish); no more "view the work log" on
  running tasks. Final report unchanged.
- Ringside UX: "running now" pill buttons appear when 2+ runs are live
  (state=live AND PID-alive in active-runs.json) — click to bounce between
  runs; workers render as a 2-column expandable card grid (engine · model ·
  elapsed · attempts · tokens, activity line only while running, full-row
  expansion with live log stream); machine-strip and legend removed as
  redundant. Historical artifact dropdowns untouched.

132 tests green (full suite minus the two pre-existing env-broken tests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Ringside overhaul moved unfinished-task state out of worker rows and
into the rounds strip's aria-labels; test_plain_english_artifact.py was
migrated in that commit but the identical assertions in
test_design_reference.py were missed (pre-existing failure on the
overhaul branch, surfaced here because the portability work made this
test file runnable off the original dev machine).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@RadioactiveCheese

Copy link
Copy Markdown
Author

This branch now also merges #18 (feat/ringside-overhaul, 9308df1) — merging or closing #18 separately is no longer necessary if this lands. The merge was clean except for one pre-existing issue in #18 itself: the Ringside work-gating change migrated the retry/working assertions in test_plain_english_artifact.py but missed the identical ones in test_design_reference.py (fails on 9308df1 standalone; it only ever passed on machines where that test file couldn't run). Fixed here in 383e7ba by mirroring the same migration. Combined branch verified: 147 tests green on native Windows (pytest) and WSL Ubuntu (unittest), plus the executed portability checks (nested POSIX-check run, full process-tree kills) pass on both.

Fold in the bubblewrap counterpart to the macOS Seatbelt wrapper
(engines/opencode-sandboxed-linux.sh, same contract: network + reads open,
writes confined to taskdir + per-run scratch + OpenCode state dirs,
--no-sandbox wired for the allow_full_access gate) and the wsl.exe -e
bridge (engines/opencode-sandboxed-wsl.sh) that translates C:\ task dirs
to /mnt paths, plus the confinement smoke test. Docs updated; wrappers no
longer described as macOS-only.

Verified by execution: bwrap smoke shows taskdir writable, HOME and /tmp
read-only, network open; a real Windows-side ringer probe run drove
opencode (GLM-5.2) through the bridge inside the sandbox — PASS attempt 1,
5s, 8k tokens, POSIX check green. .gitattributes pins *.sh to LF so
autocrlf checkouts cannot hand WSL a CRLF script.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@RadioactiveCheese

Copy link
Copy Markdown
Author

9ef6e82 closes the last known gap from the PR body: the OpenCode sandbox is no longer macOS-only. New engines/opencode-sandboxed-linux.sh (bubblewrap, same confinement contract as the Seatbelt wrapper) and engines/opencode-sandboxed-wsl.sh (wsl.exe -e bridge translating C:\ task dirs to /mnt) — verified by executing the bwrap confinement smoke (taskdir writable, HOME and /tmp read-only, network open) and a real Windows-side ringer probe run through the full stack (ringer.py on Windows → WSL bridge → bwrap → opencode/GLM-5.2): PASS attempt 1, 5s, 8k tokens, POSIX check green. Also adds .gitattributes pinning *.sh to LF so autocrlf Windows checkouts can't hand WSL a CRLF script. Suite 147 green.

…ox lane)

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

Copy link
Copy Markdown
Contributor

Thanks for the amount of work and testing that went into this. There are several useful changes here, especially the Windows shell and process fixes and the cross-platform sandbox work.

The problem is scope. This PR now combines core Windows runtime behavior, validator execution, template portability, dashboard and open-folder behavior, Linux/WSL sandboxing, documentation, and pieces of other Ringside work. That is too much to review or roll back safely as one change.

Please break this into focused PRs:

  1. Core native-Windows runtime: Git Bash check execution, safe PID inspection, process-tree cleanup, and byte-safe patch export.
  2. Template and validator portability: shared shell execution, interpreter placeholders, and the corresponding tests.
  3. Cross-platform desktop behavior: open-folder commands and Windows file links.
  4. OpenCode sandboxing: the Linux Bubblewrap wrapper, Windows-to-WSL bridge, and LF enforcement.

Keep the relevant tests and documentation with each change. Please also rebase against current main so already-merged Ringside work and accumulated model notes are not carried forward. PR #24 has now landed for the immediate hardcoded interpreter fix, so that should be accounted for during the rebase.

I am going to hold off on merging this version. The work is useful; it needs to arrive in smaller pieces so each change has a clear verification and rollback boundary. Start with the core Windows runtime PR.

@justfinethanku

Copy link
Copy Markdown
Contributor

First, plainly: this work is extremely valuable and we want it in Ringer. You are the only person who has done the real Windows work — actual multi-platform validation on real machines, and fixes that target, with precision, the only three genuinely non-portable chokepoints in this codebase: how checks get shelled (the Git Bash finder), how process trees are controlled (OpenProcess/STILL_ACTIVE liveness, taskkill /T escalation), and the sandbox wrappers. Nearly everything else in Ringer is portable Python already — which means your few hundred lines are the difference between "macOS/Linux tool" and "runs everywhere." That's not a fringe contribution; it's a whole platform of users.

The reason we're closing this branch is composition, not quality: about half its 52 files are a stale copy of pre-#18/#26 main — Ringside, model-log, and eval changes that have since landed in different form — which is why it conflicts and why nobody can audit which lines are yours versus overtaken history. Your work deserves better than being buried in a diff that's half archaeology.

So: please resubmit as small, separately auditable PRs on current main — we will review them fast, and your authorship stays on every commit:

  1. Check-shell selection on Windowsfind_windows_check_shell() + Verifier._run_check() routing, with tests.
  2. Windows process handlingpid_is_alive, terminate/kill process-tree paths, with tests exercising the Windows branches.
  3. Template validator portability — the eight kit-validator sweeps.
  4. Sandbox wrappers (WSL/Linux) — check feat: add verified multi-model worker orchestration #31's competing bubblewrap wrapper first so you two don't collide.

And here's the part we're doing for you: none of us has a Windows machine, so we're adding a GitHub Actions workflow to main — macOS and Linux run the full suite as required checks, plus a windows-latest job on every PR as a non-blocking harness. Right now that Windows job can only compile and attempt a POSIX-only suite. Your PRs are what turn it green — and once green, we flip it to required, which permanently protects your work from every future change. Write your tests to run under windows-latest and each PR carries its own executed proof; no screenshots needed, no trust required.

Thank you for carrying this further than anyone else has. We'll be watching for the split PRs.

justfinethanku added a commit that referenced this pull request Jul 16, 2026
macOS and Linux run the suite on every push/PR. The windows-latest job
is continue-on-error until native-Windows portability PRs (invited in
#17) turn it green, then it flips to required.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

2 participants