You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Every job in .github/workflows/ci.yml and publish.yml is runs-on: ubuntu-latest. Nothing verifies that import nooa works on any other platform, so a module-level POSIX-only import reaches PyPI without anything failing first.
That is not hypothetical — it already happened, three times, and each time a user found it rather than CI:
Two independent reporters, three issues, all in the same class. I am not proposing to take any of them — #92's reporter says they have a PR in progress, and #84/#85's reporter offered to submit both. This issue is about the missing guard that lets the class recur.
What I measured
I ran a ladder on native Windows 11 (Python 3.13, no WSL), applying the minimum shim for each blocker to see what the next one is:
STEP 1 bare `import nooa` ModuleNotFoundError: fcntl <- #84
STEP 2 + msvcrt-backed fcntl shim AttributeError: signal has no SIGUSR2 <- #85
STEP 3 + signal.SIGUSR2 = 0 imported OK, version 0.0.1.dev162
STEP 4 SandboxedExecutor(require=False) ModuleNotFoundError: resource <- #92
STEP 5 ShellTools.read()/write_file() OK
STEP 6 ShellTools.run() AssertionError: pass_fds not supported on Windows
STEP 7 eval_pipeline._memory_monitor ModuleNotFoundError: resource
Three things in there are worth separating out from the known issues:
STEP 3 is the important one. Once #84 and #85 are fixed, import nooasucceeds on native Windows today. The two blockers are both one-line platform guards, and nothing deeper is preventing importability. That makes an import smoke test a genuinely cheap gate rather than the first step of a long port.
STEP 6 and 7 are not reported in any open issue.
src/nooa/tools/_bash_session.py:173 passes pass_fds=(ctrl_w,) to create_subprocess_exec, which asserts on Windows. Note this is a different failure from the os.killpg/os.getpgid calls at lines 79-80 and 642-661 — those are downstream and never reached, so an audit that only greps for killpg misses the real blocker.
STEP 5 is a useful negative result: the ShellToolsfile surface (read, replace, write_file) is already platform-clean. Only the shell-session surface is POSIX-bound.
Why a gate, and not just the fixes
The repo already has the right convention — hasattr(signal, "SIGUSR1") guards in packages/nooa-cli/src/nooa_cli/commands/eval.py:47 and util/eval_pipeline/src/eval_pipeline/__main__.py:13. It is applied in two places and missed in four. That is exactly the pattern that a per-PR check fixes and code review does not: nobody reviewing a diff on sqlite.py is thinking about win32.
There's also a contributor cost, which is how I ran into this. On a Windows checkout the test suite cannot start at all — conftest.py imports nooa.storage.sqlite — so uv run pytest fails at collection before a single test runs. Any Windows-based contributor is blocked on their first command.
Proposed design
Deliberately narrow, because full Windows support is a much bigger project and I don't think it's what's being asked for here. Running the whole suite on windows-latest is not viable: BashSession needs bash and pass_fds, and the Landlock/seccomp/RLIMIT sandbox is Linux-only by design.
Tier 1 — import smoke test (this issue). A windows-latest job that installs the built wheels and asserts each distribution imports:
Seconds of runtime, no credentials, and it fails on exactly the bug class that reached 0.0.8. I'd suggest including macos-latest in the same matrix: macOS is POSIX so it wouldn't have caught #84, but it would catch a Linux-only assumption (/proc, landlock), and it's the platform most likely to be used by contributors after Linux.
Tier 2 — a posix marker, later and incrementally.pyproject.toml already carries integration, stress, and sandbox markers, so posix fits the existing convention. POSIX-bound tests get marked, and the Windows job grows to run the unmarked remainder. This should follow the fixes rather than lead them, and is a separate PR.
Sequencing
The Tier 1 job goes red until #84 and #85 land — that's the point of it, but it also means it can't merge before those do. Three options, and I'd like a maintainer call rather than guessing:
I'd suggest (1), with (2) as a fallback if #84/#85 stall.
What I'm offering
Happy to submit Tier 1 — the workflow job, plus a short "Supported platforms" note in CONTRIBUTING.md recording that the suite currently requires a POSIX host, so the next Windows contributor isn't surprised by a collection error. I'll also file the two unreported findings (STEP 6, STEP 7) as their own issue if you'd rather track them separately from this one; say the word and I'll split them out.
Not filing this as a PR first because CONTRIBUTING asks for an issue on anything substantial, and adding runners to CI is a maintainer cost decision rather than a code question.
The gap
Every job in
.github/workflows/ci.ymlandpublish.ymlisruns-on: ubuntu-latest. Nothing verifies thatimport nooaworks on any other platform, so a module-level POSIX-only import reaches PyPI without anything failing first.That is not hypothetical — it already happened, three times, and each time a user found it rather than CI:
import nooafails on native Windows: unconditionalimport fcntlinnooa/storage/sqlite.py#84 — unconditionalimport fcntlinnooa/storage/sqlite.pymakes the entire package unimportable on native Windows. Confirmed shipped innooa==0.0.8, not just onmain.install_debug_handler()crashes at import on Windows:signal.SIGUSR2AttributeError escapes the platform guard #85 —signal.SIGUSR2is read outside its platform guard ininstall_debug_handler(), which__init__.pycalls at import time.import resourceinsandbox/guards.py, plussignal.SIGXCPU/SIGKILLinexecutor.py.Two independent reporters, three issues, all in the same class. I am not proposing to take any of them — #92's reporter says they have a PR in progress, and #84/#85's reporter offered to submit both. This issue is about the missing guard that lets the class recur.
What I measured
I ran a ladder on native Windows 11 (Python 3.13, no WSL), applying the minimum shim for each blocker to see what the next one is:
Three things in there are worth separating out from the known issues:
STEP 3 is the important one. Once #84 and #85 are fixed,
import nooasucceeds on native Windows today. The two blockers are both one-line platform guards, and nothing deeper is preventing importability. That makes an import smoke test a genuinely cheap gate rather than the first step of a long port.STEP 6 and 7 are not reported in any open issue.
src/nooa/tools/_bash_session.py:173passespass_fds=(ctrl_w,)tocreate_subprocess_exec, which asserts on Windows. Note this is a different failure from theos.killpg/os.getpgidcalls at lines 79-80 and 642-661 — those are downstream and never reached, so an audit that only greps forkillpgmisses the real blocker.util/eval_pipeline/src/eval_pipeline/_memory_monitor.py:19importsresourceunconditionally.STEP 5 is a useful negative result: the
ShellToolsfile surface (read,replace,write_file) is already platform-clean. Only the shell-session surface is POSIX-bound.Why a gate, and not just the fixes
The repo already has the right convention —
hasattr(signal, "SIGUSR1")guards inpackages/nooa-cli/src/nooa_cli/commands/eval.py:47andutil/eval_pipeline/src/eval_pipeline/__main__.py:13. It is applied in two places and missed in four. That is exactly the pattern that a per-PR check fixes and code review does not: nobody reviewing a diff onsqlite.pyis thinking about win32.There's also a contributor cost, which is how I ran into this. On a Windows checkout the test suite cannot start at all —
conftest.pyimportsnooa.storage.sqlite— souv run pytestfails at collection before a single test runs. Any Windows-based contributor is blocked on their first command.Proposed design
Deliberately narrow, because full Windows support is a much bigger project and I don't think it's what's being asked for here. Running the whole suite on
windows-latestis not viable:BashSessionneedsbashandpass_fds, and the Landlock/seccomp/RLIMIT sandbox is Linux-only by design.Tier 1 — import smoke test (this issue). A
windows-latestjob that installs the built wheels and asserts each distribution imports:Seconds of runtime, no credentials, and it fails on exactly the bug class that reached
0.0.8. I'd suggest includingmacos-latestin the same matrix: macOS is POSIX so it wouldn't have caught #84, but it would catch a Linux-only assumption (/proc,landlock), and it's the platform most likely to be used by contributors after Linux.Tier 2 — a
posixmarker, later and incrementally.pyproject.tomlalready carriesintegration,stress, andsandboxmarkers, soposixfits the existing convention. POSIX-bound tests get marked, and the Windows job grows to run the unmarked remainder. This should follow the fixes rather than lead them, and is a separate PR.Sequencing
The Tier 1 job goes red until #84 and #85 land — that's the point of it, but it also means it can't merge before those do. Three options, and I'd like a maintainer call rather than guessing:
import nooafails on native Windows: unconditionalimport fcntlinnooa/storage/sqlite.py#84/install_debug_handler()crashes at import on Windows:signal.SIGUSR2AttributeError escapes the platform guard #85 merge. Cleanest, but depends on other contributors' PRs.continue-on-error: true, then flip it once the fixes are in. Visible immediately, no red required check.import nooafails on native Windows: unconditionalimport fcntlinnooa/storage/sqlite.py#84/install_debug_handler()crashes at import on Windows:signal.SIGUSR2AttributeError escapes the platform guard #85's reporter already offered to do — which is why I'm not proposing it.I'd suggest (1), with (2) as a fallback if #84/#85 stall.
What I'm offering
Happy to submit Tier 1 — the workflow job, plus a short "Supported platforms" note in
CONTRIBUTING.mdrecording that the suite currently requires a POSIX host, so the next Windows contributor isn't surprised by a collection error. I'll also file the two unreported findings (STEP 6, STEP 7) as their own issue if you'd rather track them separately from this one; say the word and I'll split them out.Not filing this as a PR first because CONTRIBUTING asks for an issue on anything substantial, and adding runners to CI is a maintainer cost decision rather than a code question.
🤖🤖🤖