From 80e952ca40325001e738ff9a9f3919e55fda62b4 Mon Sep 17 00:00:00 2001 From: joelmitz <1820043398@campus.ouj.ac.jp> Date: Mon, 24 Aug 2026 05:17:30 +0900 Subject: [PATCH 1/3] fix(codex-bridge): give the identity lease a start token on Windows The codex bridge could not start on Windows at all. Every launch died in writeLease() with "cannot determine process start token for identity lease", 20+ times in a row in run/codex-bridge...log. startToken() had exactly two sources and Windows satisfies neither: /proc//stat ENOENT -- Windows has no /proc ps -o lstart= "ps: unknown option -- o" -- the only ps likely to be on PATH there is MSYS's, and it rejects -o outright rather than degrading Both yield an empty token, and writeLease() fails closed on an empty token by design (#906: a bridge that cannot publish an enumerable lease must not go on to arm its network, or it becomes exactly the unreapable orphan the lease exists to prevent). So the fail-closed was correct; the missing source was not. Adds Process.StartTime.Ticks via PowerShell as the Windows source, in both codex-bridge.js startToken() and codex-bridge-launcher.sh _start_token, and admits "pwsh" in the lease schema. ONE source, not a preference list --------------------------------- WMIC's CreationDate is ~2x cheaper (0.36s vs 0.83s measured) and was the first implementation, but it is deprecated and already absent from Windows 11 installs that have dropped the Feature-on-Demand. The problem is not that WMIC may be missing. It is that a per-side "WMIC, else PowerShell" order lets the writer and the reaper resolve DIFFERENT sources for the SAME process whenever only one of the two can reach wmic.exe. Their tokens then differ by FORMAT, so the reaper reads a live bridge's lease as some other process's and never collects the orphan. This was reproduced, not theorised: with wmic shadowed on one side only, the two sides returned node: src=wmic token=20260824045224.102163+540 bash: src=pwsh token=639231439441021632 for one pid. Ticks is the one value both sides can always agree on, so WMIC's speed is not worth the divergence and it is not used at all. Falling back from powershell.exe to pwsh is safe for a reason that does not apply to WMIC: both return the SAME Ticks for a given pid (measured), so the src label names the format, not the executable. Verified that when powershell.exe is unreachable, both sides fall through to pwsh and still agree. Windows must not take the /proc branch -------------------------------------- The Windows branch is taken INSTEAD of the /proc branch, not merely before it. MSYS and Cygwin do expose a working /proc -- field 22 and all -- but it is keyed by the emulation layer's own pid space, while a lease records the Windows pid codex-bridge.js sees as process.pid. Measured on MINGW64: the same shell is MSYS pid 3065729 and winpid 1456. Letting /proc win would silently return the start time of whatever unrelated MSYS process sits at that number, which is the recycled-pid confusion the token exists to prevent, only harder to notice because nothing errors. Verification ------------ Windows 11 26200, MINGW64, node 22 (win32), codex-cli 0.149.0. - node startToken() and shell _start_token return byte-identical "pwsh639231441791462826" for the same live Windows pid; likewise when powershell.exe is unreachable and both fall through to pwsh - _read_lease: 8 cases pass -- pwsh integer accepted, pwsh non-integer and empty rejected, wmic-format rejected, proc/ps regressions still accepted, proc non-numeric and an unknown src still rejected - end to end: bridge starts (alive, armed), publishes start=639231452158110466 startsrc=pwsh, and an agmsg message sent to the role drives a turn and gets an answer back. Before this change the same sequence produced only the token error - tests/test_codex_bridge_launcher.bats gives byte-identical results with and without this change on this machine (10 of 12 fail either way; the suite does not run on MSYS). NOT validated on Linux or macOS -- the POSIX branches are untouched, but that is an argument, not a measurement --- .../types/codex/codex-bridge-launcher.sh | 55 +++++++++++++++++-- scripts/drivers/types/codex/codex-bridge.js | 38 +++++++++++++ 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/scripts/drivers/types/codex/codex-bridge-launcher.sh b/scripts/drivers/types/codex/codex-bridge-launcher.sh index 98fb410ef..4ae470629 100755 --- a/scripts/drivers/types/codex/codex-bridge-launcher.sh +++ b/scripts/drivers/types/codex/codex-bridge-launcher.sh @@ -407,9 +407,50 @@ _REAP_WAIT_TICKS=50 # recycled pid is always distinguishable from the one we leased. # else -> `ps -o lstart=` (second precision). Echoes ""; # returns non-zero (indeterminable) so the caller fails closed. +# win32 -> Process.StartTime.Ticks via PowerShell -- the SAME single source +# codex-bridge.js startToken() writes, so both sides always agree. +# WMIC's CreationDate is cheaper but deprecated (already gone from some +# Windows 11 installs); a "WMIC, else PowerShell" order on each side +# independently would let the two resolve DIFFERENT sources for the +# same process whenever only one of them can reach wmic.exe, and their +# differently-FORMATTED tokens would make the reaper read a live +# bridge's lease as another process's. powershell.exe and pwsh return +# identical Ticks (measured), so falling back between those two +# binaries introduces no such divergence. +# +# ★The Windows branch is taken INSTEAD of the /proc branch, not merely before it. +# MSYS/Cygwin do expose a working /proc (field 22 and all), but it is keyed by the +# emulation layer's OWN pid space, while a lease records the Windows pid that +# codex-bridge.js sees as process.pid -- the two are unrelated numbers for the +# same process (measured on MINGW64: MSYS pid 3065729 vs winpid 1456). Letting +# /proc win here would silently return the start time of whatever UNRELATED MSYS +# process happens to sit at that number, and the reaper would then compare a +# well-formed token against the wrong process: exactly the recycled-pid confusion +# the token exists to prevent, only harder to notice because nothing errors. +_agmsg_is_windows() { + case "${_AGMSG_UNAME_S:=$(uname -s 2>/dev/null || echo unknown)}" in + MINGW*|MSYS*|CYGWIN*) return 0 ;; + *) return 1 ;; + esac +} + _start_token() { - local pid="$1" s r tok + local pid="$1" s r tok bin local -a a + if _agmsg_is_windows; then + for bin in powershell.exe pwsh; do + # `tr -d '\r'` because PowerShell writes CRLF and Node's .trim() on the + # writer side strips it there, so both end up with the bare digits. + tok="$("$bin" -NoProfile -NonInteractive -Command \ + "(Get-Process -Id $pid).StartTime.Ticks" 2>/dev/null | tr -d '\r' | head -n 1)" + tok="${tok#"${tok%%[![:space:]]*}"}" + tok="${tok%"${tok##*[![:space:]]}"}" + case "$tok" in ''|*[!0-9]*) continue ;; esac + printf 'pwsh\t%s' "$tok" + return 0 + done + return 1 + fi if [ -r "/proc/$pid/stat" ]; then s="$(cat "/proc/$pid/stat" 2>/dev/null)" || return 1 r="${s##*)}" @@ -433,7 +474,7 @@ _start_token() { # Parse a lease under an EXACT v=1 schema and fail closed on anything else. Sets # lproj/lpairs/lhost/lpid/lstart/lstartsrc and returns 0 only when the file is # precisely the seven expected keys, each once, no unknown or duplicate or extra -# line, hashes 40-hex, pid numeric, startsrc proc|ps. A malformed, truncated, or +# line, hashes 40-hex, pid numeric, startsrc proc|ps|pwsh. A malformed, truncated, or # tampered lease returns non-zero, so the reaper never kills on a doubtful one. _read_lease() { local file="$1" line k v nlines=0 lv="" @@ -460,10 +501,16 @@ _read_lease() { case "$lproj" in *[!0-9a-f]*|"") return 1 ;; esac; [ "${#lproj}" -eq 40 ] || return 1 case "$lpairs" in *[!0-9a-f]*|"") return 1 ;; esac; [ "${#lpairs}" -eq 40 ] || return 1 case "$lpid" in *[!0-9]*|"") return 1 ;; esac - case "$lstartsrc" in proc|ps) ;; *) return 1 ;; esac + case "$lstartsrc" in proc|ps|pwsh) ;; *) return 1 ;; esac [ -n "$lhost" ] || return 1 [ -n "$lstart" ] || return 1 - if [ "$lstartsrc" = proc ]; then case "$lstart" in *[!0-9]*|"") return 1 ;; esac; fi + # proc's starttime ticks and .NET's StartTime.Ticks are both bare integers, so + # a lease carrying anything else under either label fails closed here. ps stays + # exempt: its lstart is a human date string whose punctuation varies by + # platform, and writer and reader only ever compare it byte for byte. + case "$lstartsrc" in + proc|pwsh) case "$lstart" in *[!0-9]*|"") return 1 ;; esac ;; + esac return 0 } diff --git a/scripts/drivers/types/codex/codex-bridge.js b/scripts/drivers/types/codex/codex-bridge.js index 9cda16b3d..2a50162c3 100755 --- a/scripts/drivers/types/codex/codex-bridge.js +++ b/scripts/drivers/types/codex/codex-bridge.js @@ -1087,7 +1087,45 @@ class CodexBridge { // and the only victim would be a same-(project,pair) bridge in the // sub-ms window before it overwrites this pid's lease, self-corrected // by the launcher respawning it. + // win32 -> Process.StartTime.Ticks, read through PowerShell. Windows has NO + // /proc, and the only `ps` likely to be on PATH there is MSYS's, + // which rejects -o outright ("ps: unknown option -- o") rather than + // degrading -- so BOTH POSIX sources above yield an empty token and + // writeLease() throws, which is why the bridge could never start on + // Windows at all. + // ★ONE source, deliberately, not a preference list. WMIC's + // CreationDate is ~2x cheaper and was the obvious first pick, but it + // is deprecated and already absent from Windows 11 installs that + // have dropped the Feature-on-Demand. A per-side "WMIC, else + // PowerShell" order would then let THIS writer and _start_token + // (codex-bridge-launcher.sh) resolve DIFFERENT sources for the SAME + // process whenever only one of the two can reach wmic.exe -- their + // tokens differ by FORMAT, so the reaper would read a live bridge's + // lease as some other process's and never collect the orphan it + // exists to collect. Ticks is the one value both sides can always + // agree on, so the speed is not worth the divergence. + // Falling back from powershell.exe to pwsh is safe for the same + // reason it is safe here and nowhere else: both return the SAME + // Ticks for a given pid (measured), so the src label names the + // format, not the executable that produced it. startToken() { + if (process.platform === "win32") { + for (const bin of ["powershell.exe", "pwsh"]) { + const r = spawnSync( + bin, + [ + "-NoProfile", + "-NonInteractive", + "-Command", + `(Get-Process -Id ${process.pid}).StartTime.Ticks`, + ], + { encoding: "utf8" }, + ); + const ticks = (r.status === 0 ? (r.stdout || "") : "").trim(); + if (/^\d+$/.test(ticks)) return { src: "pwsh", token: ticks }; + } + return { src: "pwsh", token: "" }; + } try { const stat = fs.readFileSync(`/proc/${process.pid}/stat`, "utf8"); const after = stat.slice(stat.lastIndexOf(")") + 1).trim().split(/\s+/); From 0e64b1358c030e465d0e135eb7e032a474787a77 Mon Sep 17 00:00:00 2001 From: joelmitz <1820043398@campus.ouj.ac.jp> Date: Mon, 24 Aug 2026 08:54:20 +0900 Subject: [PATCH 2/3] test(codex-bridge): pin the lease schema's Windows start token, and trim the rationale to the PR Adds the regression tests the previous commit described but did not commit, and moves the evidence behind the WMIC decision out of the code and into the PR. Tests ----- `_read_lease` is the reaper's only gate on a lease, so its accept/reject set is the contract that widening `proc|ps` to `proc|ps|pwsh` changes. The six cases exercise it directly -- the pattern tests/test_remote.bats already uses for `_remote_endpoint_display` -- rather than through the reaper, which needs a spawnable bridge and a live pid. That is exactly what does not work on Git Bash (#567), and the schema question depends on neither. Only the case that actually runs PowerShell carries `windows-native` in its name, so it lands on the Windows leg; the schema cases carry no such marker and run on every leg. What each case is for, stated plainly rather than as "6 tests pass": | case | with the change | without it | |---|---|---| | a pwsh lease parses | ok | **fails** | | windows-native: a live pid yields an integer pwsh token | ok | **fails** | | a pwsh token that is not an integer is rejected | ok | ok | | an unrecognised startsrc is rejected | ok | ok | | proc and ps still parse; proc non-numeric still rejected | ok | ok | | POSIX: a live pid yields a proc or ps token | skip on Windows | skip | Two detect the change; four are guards. The `wmic` label is in the rejection case deliberately and not as an arbitrary bad value: it pins the decision not to adopt WMIC, so reintroducing it fails loudly. Comments -------- The WMIC rationale ran 22 lines in codex-bridge.js and 20 in the launcher. The conclusion belongs in the code; the measurements behind it (the reproduced divergence, MSYS pid 3065729 vs winpid 1456) belong in the PR, where a reader looking for *why* will be. Trimmed to 10 and 14 lines with no fact dropped from the argument itself. Verification ------------ Windows 11 26200, MINGW64, bats-core 1.14.0 from a clone. - the six cases: all pass; re-run against `dbb9c2c2`'s codex/ tree, the two change-detecting cases fail as tabled above - NOT `bats tests/` green on this machine, and that is not achievable here: .github/workflows/tests.yml shards the suite over `[ubuntu-latest, macos-latest]` only, and the Windows leg runs `filter: "windows-native"`. The full file has never been expected to pass under Git Bash - `launcher: windows-native starts the bridge (#567)` fails here with and without this change. It asserts the half of #567 that is not fixed: on Git Bash the parent-liveness probe asks `tasklist` about an MSYS pid. Measured on this machine -- the same shell is MSYS pid 3994449 and winpid 19568; `tasklist` finds only the latter, `kill -0` only the former. The workflow itself lists `windows runtime (#567)` among its known intermittent reds - ubuntu/macos results are NOT from this machine and are not claimed here --- .../types/codex/codex-bridge-launcher.sh | 28 +++---- scripts/drivers/types/codex/codex-bridge.js | 31 +++---- tests/test_codex_bridge_launcher.bats | 83 +++++++++++++++++++ 3 files changed, 104 insertions(+), 38 deletions(-) diff --git a/scripts/drivers/types/codex/codex-bridge-launcher.sh b/scripts/drivers/types/codex/codex-bridge-launcher.sh index 4ae470629..465f1e52e 100755 --- a/scripts/drivers/types/codex/codex-bridge-launcher.sh +++ b/scripts/drivers/types/codex/codex-bridge-launcher.sh @@ -407,26 +407,20 @@ _REAP_WAIT_TICKS=50 # recycled pid is always distinguishable from the one we leased. # else -> `ps -o lstart=` (second precision). Echoes ""; # returns non-zero (indeterminable) so the caller fails closed. -# win32 -> Process.StartTime.Ticks via PowerShell -- the SAME single source +# win32 -> Process.StartTime.Ticks via PowerShell -- the same single source # codex-bridge.js startToken() writes, so both sides always agree. -# WMIC's CreationDate is cheaper but deprecated (already gone from some -# Windows 11 installs); a "WMIC, else PowerShell" order on each side -# independently would let the two resolve DIFFERENT sources for the -# same process whenever only one of them can reach wmic.exe, and their -# differently-FORMATTED tokens would make the reaper read a live -# bridge's lease as another process's. powershell.exe and pwsh return -# identical Ticks (measured), so falling back between those two +# WMIC is deprecated and already absent from some Windows 11 installs; +# a per-side "WMIC, else PowerShell" order would let the two record +# differently-FORMATTED tokens for the same process. powershell.exe +# and pwsh return identical Ticks, so falling back between those two # binaries introduces no such divergence. # -# ★The Windows branch is taken INSTEAD of the /proc branch, not merely before it. -# MSYS/Cygwin do expose a working /proc (field 22 and all), but it is keyed by the -# emulation layer's OWN pid space, while a lease records the Windows pid that -# codex-bridge.js sees as process.pid -- the two are unrelated numbers for the -# same process (measured on MINGW64: MSYS pid 3065729 vs winpid 1456). Letting -# /proc win here would silently return the start time of whatever UNRELATED MSYS -# process happens to sit at that number, and the reaper would then compare a -# well-formed token against the wrong process: exactly the recycled-pid confusion -# the token exists to prevent, only harder to notice because nothing errors. +# The Windows branch is taken INSTEAD of the /proc branch, not merely before it: +# MSYS/Cygwin do expose a working /proc, but it is keyed by the emulation layer's +# own pid space while a lease records the Windows pid codex-bridge.js sees as +# process.pid. Letting /proc win would return the start time of whatever +# unrelated MSYS process sits at that number -- the recycled-pid confusion this +# token exists to prevent, with nothing to signal it. _agmsg_is_windows() { case "${_AGMSG_UNAME_S:=$(uname -s 2>/dev/null || echo unknown)}" in MINGW*|MSYS*|CYGWIN*) return 0 ;; diff --git a/scripts/drivers/types/codex/codex-bridge.js b/scripts/drivers/types/codex/codex-bridge.js index 2a50162c3..3f0aacf3e 100755 --- a/scripts/drivers/types/codex/codex-bridge.js +++ b/scripts/drivers/types/codex/codex-bridge.js @@ -1087,27 +1087,16 @@ class CodexBridge { // and the only victim would be a same-(project,pair) bridge in the // sub-ms window before it overwrites this pid's lease, self-corrected // by the launcher respawning it. - // win32 -> Process.StartTime.Ticks, read through PowerShell. Windows has NO - // /proc, and the only `ps` likely to be on PATH there is MSYS's, - // which rejects -o outright ("ps: unknown option -- o") rather than - // degrading -- so BOTH POSIX sources above yield an empty token and - // writeLease() throws, which is why the bridge could never start on - // Windows at all. - // ★ONE source, deliberately, not a preference list. WMIC's - // CreationDate is ~2x cheaper and was the obvious first pick, but it - // is deprecated and already absent from Windows 11 installs that - // have dropped the Feature-on-Demand. A per-side "WMIC, else - // PowerShell" order would then let THIS writer and _start_token - // (codex-bridge-launcher.sh) resolve DIFFERENT sources for the SAME - // process whenever only one of the two can reach wmic.exe -- their - // tokens differ by FORMAT, so the reaper would read a live bridge's - // lease as some other process's and never collect the orphan it - // exists to collect. Ticks is the one value both sides can always - // agree on, so the speed is not worth the divergence. - // Falling back from powershell.exe to pwsh is safe for the same - // reason it is safe here and nowhere else: both return the SAME - // Ticks for a given pid (measured), so the src label names the - // format, not the executable that produced it. + // win32 -> Process.StartTime.Ticks, read through PowerShell. Windows has no + // /proc, and MSYS's `ps` rejects -o outright, so both POSIX sources + // yield an empty token and no lease can be published at all. + // ONE source, not a preference list: WMIC is deprecated and already + // absent from some Windows 11 installs, and letting each side choose + // between WMIC and PowerShell independently would let this writer and + // _start_token (codex-bridge-launcher.sh) record differently- + // FORMATTED tokens for the same process. powershell.exe and pwsh + // return identical Ticks, so falling back between those two binaries + // is safe: the src label names the format, not the executable. startToken() { if (process.platform === "win32") { for (const bin of ["powershell.exe", "pwsh"]) { diff --git a/tests/test_codex_bridge_launcher.bats b/tests/test_codex_bridge_launcher.bats index 0e8de0398..5c2761e13 100644 --- a/tests/test_codex_bridge_launcher.bats +++ b/tests/test_codex_bridge_launcher.bats @@ -697,3 +697,86 @@ _fake_alice_lease() { # sets FAKE_PID once its lease file exists kill -0 "$victim" kill "$victim" "$disp" "$parent" 2>/dev/null || true; wait "$disp" 2>/dev/null || true; wait "$victim" 2>/dev/null || true } + +# --- Windows start token: the lease schema must admit the source +# codex-bridge.js writeLease() records on Windows, where /proc does not exist and +# the only `ps` likely to be on PATH (MSYS's) rejects -o outright, so both POSIX +# sources yield an empty token and the bridge can never publish a lease at all. +# +# _read_lease is the reaper's ONLY gate on a lease, so its accept/reject set is +# the contract. These exercise it directly -- the pattern test_remote.bats uses +# for _remote_endpoint_display -- rather than through the reaper: the reaper +# needs a spawnable bridge and a live pid, which is exactly what does not work on +# Git Bash (#567), and the schema question has nothing to do with either. Kept +# out of the `windows-native` filter deliberately: nothing here runs PowerShell, +# so these belong on every leg, not only the Windows one. --- +_lease_verdict() { # -> prints accept|reject + local h40=0123456789abcdef0123456789abcdef01234567 + printf 'v=1\nproject=%s\npairs=%s\nhost=h\npid=123\nstart=%s\nstartsrc=%s\n' \ + "$h40" "$h40" "$2" "$1" > "$TEST_SKILL_DIR/lease-under-test" + bash -c ' + eval "$(sed -n "/^_read_lease() {/,/^}/p" "$1")" + _read_lease "$2" && echo accept || echo reject + ' _ "$LAUNCHER" "$TEST_SKILL_DIR/lease-under-test" 2>/dev/null +} + +@test "launcher: the lease schema admits a pwsh start token" { + [ "$(_lease_verdict pwsh 639231441791462826)" = accept ] +} + +@test "launcher: a pwsh lease whose token is not an integer is rejected, fail-closed" { + # .NET Ticks is a bare integer. Anything else under that label is a lease this + # side did not write, and a doubtful lease must never authorise a kill. + [ "$(_lease_verdict pwsh 6392314.5)" = reject ] + [ "$(_lease_verdict pwsh '')" = reject ] +} + +@test "launcher: an unrecognised startsrc is rejected, fail-closed" { + # wmic is here on purpose, not as an arbitrary bad value: WMIC's CreationDate + # was the faster candidate and was deliberately NOT adopted, because a per-side + # "WMIC, else PowerShell" order lets the writer and the reaper resolve different + # sources for the same process whenever only one of them can reach wmic.exe. + # Rejecting the label pins that decision, so reintroducing it fails loudly. + [ "$(_lease_verdict wmic 20260824041348.411807+540)" = reject ] + [ "$(_lease_verdict bogus 123)" = reject ] +} + +@test "launcher: proc and ps leases still parse (start-token regression)" { + [ "$(_lease_verdict proc 396341883)" = accept ] + [ "$(_lease_verdict ps 'Sun Aug 24 04:00:00 2026')" = accept ] + # ps stays exempt from the integer check (its token is a human date string + # whose punctuation varies by platform); proc does not. + [ "$(_lease_verdict proc abc)" = reject ] +} + +_run_start_token() { # -> runs _start_token in a subshell + run bash -c ' + eval "$(sed -n "/^_agmsg_is_windows() {/,/^}/p;/^_start_token() {/,/^}/p" "$1")" + _start_token "$2" + ' _ "$LAUNCHER" "$1" +} + +@test "launcher: a live pid yields a proc or ps start token on POSIX" { + skip_on_windows "Windows has its own source; see the windows-native case" + _run_start_token $$ + [ "$status" -eq 0 ] + local tab; tab=$(printf '\t') + case "${output%%"$tab"*}" in proc|ps) ;; *) false ;; esac + [ -n "${output#*"$tab"}" ] +} + +@test "launcher: windows-native a live pid yields an integer pwsh start token" { + skip_unless_windows "PowerShell and the Windows pid space are the point" + # The pid must be the WINDOWS one. MSYS/Cygwin number processes in their own + # space -- the same shell is MSYS pid 3994449 and winpid 19568 on our runner -- + # and Get-Process only knows the latter, which is also the pid + # codex-bridge.js records as process.pid. + local winpid; winpid="$(cat /proc/$$/winpid)" + [ -n "$winpid" ] + _run_start_token "$winpid" + [ "$status" -eq 0 ] + local tab; tab=$(printf '\t') + [ "${output%%"$tab"*}" = pwsh ] + local tok="${output#*"$tab"}" + case "$tok" in ''|*[!0-9]*) false ;; esac +} From 489fb4a51018166f9e134085759b139bd0021c05 Mon Sep 17 00:00:00 2001 From: mkmariko Date: Sun, 13 Sep 2026 09:49:53 +0900 Subject: [PATCH 3/3] fix(codex-bridge): cover clangarm and bash 3.2 tests --- .../types/codex/codex-bridge-launcher.sh | 2 +- tests/test_codex_bridge_launcher.bats | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/scripts/drivers/types/codex/codex-bridge-launcher.sh b/scripts/drivers/types/codex/codex-bridge-launcher.sh index 465f1e52e..4eac7ed41 100755 --- a/scripts/drivers/types/codex/codex-bridge-launcher.sh +++ b/scripts/drivers/types/codex/codex-bridge-launcher.sh @@ -423,7 +423,7 @@ _REAP_WAIT_TICKS=50 # token exists to prevent, with nothing to signal it. _agmsg_is_windows() { case "${_AGMSG_UNAME_S:=$(uname -s 2>/dev/null || echo unknown)}" in - MINGW*|MSYS*|CYGWIN*) return 0 ;; + MINGW*|MSYS*|CYGWIN*|CLANGARM*) return 0 ;; *) return 1 ;; esac } diff --git a/tests/test_codex_bridge_launcher.bats b/tests/test_codex_bridge_launcher.bats index 5c2761e13..81437f0e9 100644 --- a/tests/test_codex_bridge_launcher.bats +++ b/tests/test_codex_bridge_launcher.bats @@ -715,7 +715,8 @@ _lease_verdict() { # -> prints accept|reject printf 'v=1\nproject=%s\npairs=%s\nhost=h\npid=123\nstart=%s\nstartsrc=%s\n' \ "$h40" "$h40" "$2" "$1" > "$TEST_SKILL_DIR/lease-under-test" bash -c ' - eval "$(sed -n "/^_read_lease() {/,/^}/p" "$1")" + pattern="/^_read_lease() {/,/^}/p" + eval "$(sed -n "$pattern" "$1")" _read_lease "$2" && echo accept || echo reject ' _ "$LAUNCHER" "$TEST_SKILL_DIR/lease-under-test" 2>/dev/null } @@ -751,7 +752,8 @@ _lease_verdict() { # -> prints accept|reject _run_start_token() { # -> runs _start_token in a subshell run bash -c ' - eval "$(sed -n "/^_agmsg_is_windows() {/,/^}/p;/^_start_token() {/,/^}/p" "$1")" + pattern="/^_agmsg_is_windows() {/,/^}/p;/^_start_token() {/,/^}/p" + eval "$(sed -n "$pattern" "$1")" _start_token "$2" ' _ "$LAUNCHER" "$1" } @@ -765,6 +767,18 @@ _run_start_token() { # -> runs _start_token in a subshell [ -n "${output#*"$tab"}" ] } +@test "launcher: CLANGARM uname selects the Windows start token path" { + local stubdir="$TEST_SKILL_DIR/clangarm-bin" + mkdir -p "$stubdir" + printf '%s\n' '#!/usr/bin/env bash' 'printf "%s\n" CLANGARM64_NT-10.0' > "$stubdir/uname" + printf '%s\n' '#!/usr/bin/env bash' 'printf "%s\n" 639231441791462826' > "$stubdir/powershell.exe" + chmod +x "$stubdir/uname" "$stubdir/powershell.exe" + + PATH="$stubdir:$PATH" _run_start_token 123 + [ "$status" -eq 0 ] + [ "$output" = $'pwsh\t639231441791462826' ] +} + @test "launcher: windows-native a live pid yields an integer pwsh start token" { skip_unless_windows "PowerShell and the Windows pid space are the point" # The pid must be the WINDOWS one. MSYS/Cygwin number processes in their own