Cross-platform: route POSIX calls through platform_compat

September 6, 2026 ยท View on GitHub

Kiro Crew runs on macOS, Linux (x86_64 and ARM), and Windows (native). fcntl, termios, resource and pty do not exist on Windows, and os.kill(pid, 0) TERMINATES the target there: it is not a liveness probe.

kiro_crew.platform_compat owns one helper per POSIX call the codebase needs. Reach for the helper, not the stdlib call, even in code you believe only runs on POSIX โ€” the import alone is enough to break a Windows install, and the failure lands at import time in a module a Windows user cannot avoid.

This is the contract. The Windows install and runtime story a user follows is windows-install.md.

Why a table rather than a rule

Half of these are not "the POSIX call is missing on Windows". They are cases where the stdlib call exists and answers wrongly: it silently no-ops, it returns a high-water mark where a live reading was wanted, its unit differs per platform, or it follows a link planted at the name. A rule of the form "guard it with IS_POSIX" produces exactly those silent failures, which is why the helper is named per call.

The helper for each call

NeedUse (platform_compat)NOT
File lockfile_lock(fd, exclusive=) / acquire_lock+release_lock / try_acquire_lockfcntl.flock
Liveness probepid_exists(pid) / pid_liveness(pid)os.kill(pid, 0) (kills on Windows!)
Kill a processkill_pid(pid, sig)os.kill(pid, sig)
Kill a treekill_process_tree(pid, sig)os.killpg(os.getpgid(pid), sig)
Parent PIDget_ppid(pid)/proc read / libproc
Match process cmdlineprocess_matches(pid, needles)/proc/<pid>/cmdline / ps
Process start time (PID-reuse guard)process_start_time(pid)/proc/<pid>/stat / ps -o lstart= (both answer None on Windows, so the guard silently never confirms)
Signalsplatform_compat.SIGKILL / SIGTERMsignal.SIGKILL (undefined on Windows)
Spawn isolationstart_new_session=IS_POSIX + creationflags=CREATE_NEW_PROCESS_GROUPbare start_new_session=True
Re-exec the current Python modulereexec_python_module(module, args)os.execv(sys.executable, [sys.executable, ...]) (breaks when the Windows interpreter path contains spaces)
Race-free Job object assignmentcreationflags |= CREATE_SUSPENDED, then apply_job_limits, then resume_process_main_threadassigning a job to an already-running child (descendants it already spawned escape)
Fork-bomb / memory ceiling on a spawned treesandbox.apply_windows_resource_ceiling(pid) after the spawn, alongside cgroup_scope_argvcgroup_scope_argv alone (a no-op on Windows, so no ceiling at all)
File modechmod_safe(path, mode) / fchmod_safe(fd, mode)os.chmod / os.fchmod (no os.fchmod on Windows)
Owner-only secret (fail-loud)restrict_to_owner(path)os.chmod(path, 0o600) under if IS_POSIX (silent no-op leaves secrets world-readable)
Owner-only secret directory (fail-loud, inheritable)restrict_dir_to_owner(path); make_owner_only_dir(path) to also create it (its tighten step is best-effort)restrict_to_owner(path) on a directory (its Windows grants carry no (OI)(CI), so files created inside land on the default DACL, not owner-only; its 0o600 also drops the execute bit a directory needs)
Directory linksymlink_or_junction(target, link)os.symlink (WinError 1314 without elevation)
Detect/remove a dir linkis_link_or_junction(path) / unlink_link_or_junction(path)path.is_symlink() (misses a Windows junction)
Hold a directory in place while a child writes into it by pathpin_directory(path) (then os.close)os.open(dir, O_RDONLY) (EACCES on Windows, and even where it opens it follows a link planted at the name)
Process RSS (live) / peak RSS / CPUproc_rss_bytes() / proc_peak_rss_bytes() / proc_cpu_seconds()resource.getrusage (ru_maxrss is a high-water mark, never a live reading, and its unit is KiB on Linux but bytes on macOS)
Available host memoryhost_available_mib() (0 = unknown, never 0 = no memory)/proc/meminfo directly (Linux-only, so the bound built on it silently vanishes on macOS and Windows)
FD soft limitraise_nofile_soft_limit(n)resource.setrlimit
Port to PIDfind_listening_pids(port) / listening_pid_tool_available(); find_port_listeners(port) when ownership must be scoped to the local address actually probedlsof directly
Spawn a system tool (ps, lsof, netstat, taskkill)trusted_system_bin(name), treating None as "unavailable"a bare argv name (resolved through a PATH that can lead with same-uid-writable dirs)
strftime no-padstrftime(dt, "%-I")bare dt.strftime("%-I") (ValueError on Windows)

Verifying a change

Verify process, signal, file-lock and metrics changes on macOS and Linux; the Windows shards in CI cover the third. A test that only ever runs on the author's platform is how a silent no-op ships.

Frontend support is Chrome, Firefox, Safari and Edge, using standard Web APIs and guarding the rest (typeof Notification !== 'undefined').