From 53d29884d65c1fd80100c7b5289866a68fc40987 Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sat, 24 Oct 2026 21:59:56 +0200 Subject: [PATCH 01/31] ci(wsl): registrar el lab W3 en main para poder despacharlo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit El workflow w3-evidence.yml vive en w2-fase-a; GitHub solo ofrece para dispatch los workflows presentes en la rama por defecto del repo. Se añade aquí (mismo contenido, sin cambios) para que aparezca en el desplegable de "Run workflow" del fork y se pueda lanzar con ref=w2-fase-a. Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 230 ++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 .github/workflows/w3-evidence.yml diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml new file mode 100644 index 0000000..74ae04f --- /dev/null +++ b/.github/workflows/w3-evidence.yml @@ -0,0 +1,230 @@ +name: "W3 evidence — WSL: pidfile/kill acotado (nivel 1) y F14 doble frontera" + +# Evidence-only lab for Quorb issue #90 (W3 phase). +# Run on a hosted Windows runner; validates with a REAL WSL distro the two +# gaps that W2 declared as unproven: +# Gap 1 — the scoped cancellation (level 1): the pidfile is written, the +# remote process of the operation is killed by that PID, and the +# distro stays Running (no --terminate). +# Gap 2 — F14 double frontier: with a constant script and the data as +# POSITIONAL parameters, the value arrives byte for byte. +# Contains no Quorb code and collects no credentials: it drives wsl.exe the +# same way the adapter does (argv nativo, nunca un string de shell). + +on: + workflow_dispatch: + +permissions: + contents: read + +jobs: + wsl-w3: + runs-on: windows-latest + timeout-minutes: 30 + steps: + - name: Host inventory + shell: pwsh + run: | + New-Item -ItemType Directory -Force -Path raw | Out-Null + Get-ComputerInfo -Property OsName, OsVersion, OsArchitecture | + Out-String | Set-Content raw/host-info.txt + & wsl.exe --version > raw\version.txt 2>&1 + "EXIT=$LASTEXITCODE" | Add-Content raw/version.txt + & wsl.exe --list --verbose > raw\list-before.txt 2>&1 + "EXIT=$LASTEXITCODE" | Add-Content raw/list-before.txt + + - name: Prepare pinned Alpine rootfs (sha256-verified) and import + shell: pwsh + run: | + $url = "https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/x86_64/alpine-minirootfs-3.20.3-x86_64.tar.gz" + Invoke-WebRequest -Uri $url -OutFile rootfs.tar.gz + # The sha256 comes from the published .sha256 file of the same + # release: the lab cannot pin a wrong or invented hash. + $expected = (Invoke-WebRequest -Uri "$url.sha256").Content.Trim().Split()[0].ToLower() + $actual = (Get-FileHash rootfs.tar.gz -Algorithm SHA256).Hash.ToLower() + "expected=$expected actual=$actual" | Set-Content raw/rootfs-sha256.txt + if ($expected -ne $actual) { + "sha256 MISMATCH" | Add-Content raw/rootfs-sha256.txt + exit 1 + } + & wsl.exe --import quorb_w3 C:\wsl\q3 rootfs.tar.gz *>&1 | + Set-Content raw/import.txt + "EXIT=$LASTEXITCODE" | Add-Content raw/import.txt + # Ubuntu: el runner de GitHub trae una distro por defecto + # (normalmente Ubuntu). La mantenemos y la usamos tal cual; si no + # hay ninguna distro aparte de la Alpine, lo documentamos. + & wsl.exe --list --verbose > raw\list-after-import.txt 2>&1 + "EXIT=$LASTEXITCODE" | Add-Content raw\list-after-import.txt + $ubuntu = (& wsl.exe --list --quiet 2>$null | Where-Object { $_.Trim() -and $_ -match 'Ubuntu|Debian' } | Select-Object -First 1) + if ($ubuntu) { + "ubuntu=$ubuntu" | Add-Content raw\list-after-import.txt + } else { + "ubuntu=" | Add-Content raw\list-after-import.txt + } + + - name: Select target distros (Alpine lab + Ubuntu si existe) + shell: pwsh + run: | + # Distro objetivo fijada para el lab: la Alpine importada. + "alpine=quorb_w3" | Set-Content raw\distros.txt + $ubuntu = (& wsl.exe --list --quiet 2>$null | Where-Object { $_.Trim() -and $_ -match 'Ubuntu|Debian' } | Select-Object -First 1) + if ($ubuntu) { "ubuntu=$ubuntu" | Add-Content raw\distros.txt } else { "ubuntu=" | Add-Content raw\distros.txt } + Get-Content raw\distros.txt + + # ------------------------------------------------------------------ + # Gap 1 — scoped cancellation (level 1), por distro (Alpine + Ubuntu) + # ------------------------------------------------------------------ + - name: Gap 1 — launch, pidfile, scoped kill y distro viva (por distro) + shell: pwsh + run: | + $lines = Get-Content raw\distros.txt + $distros = @($lines | ForEach-Object { + $parts = $_ -split '=', 2 + if ($parts[1] -and $parts[1] -ne '') { $parts[1] } + }) + if ($distros.Count -eq 0) { "NO DISTRO TO TEST" | Out-File raw\gap1-verdict.txt; exit 1 } + $failed = @() + foreach ($d in $distros) { + $tag = $d -replace '[^A-Za-z0-9]', '' + $nonce = "$([DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds())-$PID" + $dir = "/tmp/quorb-wsl-$tag-$nonce" + "dir=$dir" | Set-Content "raw\gap1-$tag-dir.txt" + # Mismo script constante que el adaptador (SCRIPT_EXEC_PIDFILE): + # dir privado (700) + pidfile + exec del comando colgado. + $script = 'mkdir "$1" && chmod 700 "$1" && umask 077 && echo $$ > "$1/pid" && shift && exec "$@"' + & wsl.exe -d $d -e sh -c $script sh $dir sleep 300 > "raw\gap1-$tag-launch.txt" 2>&1 + "EXIT=$LASTEXITCODE (124/137 al matarlo = esperado)" | Add-Content "raw\gap1-$tag-launch.txt" + # pidfile con PID numérico + & wsl.exe -d $d -- cat $dir/pid > "raw\gap1-$tag-pid.txt" 2>&1 + "EXIT=$LASTEXITCODE" | Add-Content "raw\gap1-$tag-pid.txt" + $pid_ = (Get-Content "raw\gap1-$tag-pid.txt" | Select-Object -First 1).Trim() + & wsl.exe -d $d -- ls -ld $dir > "raw\gap1-$tag-dir-listing.txt" 2>&1 + "remote pid=$pid_" | Add-Content "raw\gap1-$tag-dir-listing.txt" + if ($pid_ -notmatch '^\d+$') { + "NO PID IN PIDFILE" | Add-Content "raw\gap1-$tag-dir-listing.txt"; $failed += $d; continue + } + # Kill acotado: solo ese PID, desde dentro de la distro. + & wsl.exe -d $d -- kill -TERM $pid_ > "raw\gap1-$tag-kill-term.txt" 2>&1 + "EXIT=$LASTEXITCODE" | Add-Content "raw\gap1-$tag-kill-term.txt" + Start-Sleep -Milliseconds 400 + # `kill -0` existe solo mientras el PID está vivo; non-zero = muerto. + & wsl.exe -d $d -- kill -0 $pid_ > "raw\gap1-$tag-kill-alive.txt" 2>&1 + "EXIT=$LASTEXITCODE (non-zero = el proceso remoto ya no existe)" | Add-Content "raw\gap1-$tag-kill-alive.txt" + # La distro debe seguir Running — sin --terminate. + & wsl.exe --list --verbose > "raw\gap1-$tag-list-after.txt" 2>&1 + & wsl.exe -d $d -- ls -ld $dir >> "raw\gap1-$tag-list-after.txt" 2>&1 + # Veredicto por distro + $alive = Get-Content "raw\gap1-$tag-kill-alive.txt" | Select-Object -First 1 + $listing = Get-Content "raw\gap1-$tag-list-after.txt" + $ok = $true + if ($alive -match "^EXIT=0") { "FAIL: remoto vivo tras el kill" | Out-File "raw\gap1-$tag-verdict.txt"; $ok = $false } + else { "OK: proceso remoto murio ($d)" | Out-File "raw\gap1-$tag-verdict.txt" } + if (($listing | Select-String $d | Select-Object -First 1) -match "Running") { + "OK: la distro sigue Running ($d, sin --terminate)" | Add-Content "raw\gap1-$tag-verdict.txt" + } else { "FAIL: la distro ya no esta Running ($d)" | Add-Content "raw\gap1-$tag-verdict.txt"; $ok = $false } + if ($listing | Select-String "drwx------") { "OK: directorio pidfile 700" | Add-Content "raw\gap1-$tag-verdict.txt" } + else { "WARN: modo 700 no confirmado" | Add-Content "raw\gap1-$tag-verdict.txt" } + if ($ok) { "VERDICT: PASS" | Add-Content "raw\gap1-$tag-verdict.txt" } else { "VERDICT: FAIL" | Add-Content "raw\gap1-$tag-verdict.txt"; $failed += $d } + Get-Content "raw\gap1-$tag-verdict.txt" + # Cleanup: matar el posible sleep huérfano y borrar el dir. + & wsl.exe -d $d -- kill -KILL $pid_ 2>&1 | Out-Null + & wsl.exe -d $d -- rm -rf $dir > "raw\gap1-$tag-cleanup.txt" 2>&1 + "cleanup dir=$dir" | Add-Content "raw\gap1-$tag-cleanup.txt" + } + if ($failed.Count -gt 0) { "FAILED DISTROS: $($failed -join ', ')" | Out-File raw\gap1-verdict.txt; Get-Content raw\gap1-verdict.txt; exit 1 } + "TODAS LAS DISTROS: PASS" | Out-File raw\gap1-verdict.txt + + # ------------------------------------------------------------------ + # Gap 2 — F14 double frontier: literal preservation + # ------------------------------------------------------------------ + - name: Gap 2 — F14 literal preservation a traves de las dos fronteras (por distro) + shell: pwsh + run: | + # Contrato: script CONSTANTE, datos como PARAMETROS posicionales. + # El valor viaja pwsh -> (argv nativo a wsl.exe) -> sh -c script + # constante -> proceso final, y debe llegar byte a byte. + # Los valores se pasan como base64 para que la capa de quoting de + # Windows (CREATE_PROCESS, igual que el adaptador) nunca toque los + # caracteres dificiles: lo que se prueba es la doble frontera, no + # el escaping del shell del host. + $script = 'exec "$@"' + $lines = Get-Content raw\distros.txt + $distros = @($lines | ForEach-Object { + $parts = $_ -split '=', 2 + if ($parts[1] -and $parts[1] -ne '') { $parts[1] } + }) + if ($distros.Count -eq 0) { "NO DISTRO TO TEST" | Out-File raw\gap2-f14.txt; exit 1 } + $all = @() + foreach ($d in $distros) { + $tag = $d -replace '[^A-Za-z0-9]', '' + $cases = @( + @{ name = "space"; value = "con espacio" }, + # `acentu-ñ-世界` es deliberado: incluye acento, ñ y CJK en un + # solo valor "mal escrito" que la doble frontera NO debe + # corregir ni transformar — debe llegar byte a byte tal cual. + @{ name = "nonascii"; value = "acentu-ñ-世界" }, + @{ name = "squote"; value = "it's" }, + @{ name = "dquote"; value = 'say "hi"' }, + @{ name = "dollar"; value = 'cost $HOME' }, + @{ name = "backtick"; value = 'id `whoami`' }, + @{ name = "glob"; value = "*.txt" }, + @{ name = "newline"; value = "line1`nline2" }, + @{ name = "empty"; value = "" }, + @{ name = "dash"; value = "--not-an-option" }, + @{ name = "semicolon"; value = "a;echo pwned" }, + @{ name = "backslash"; value = "C:\Users\quorb" } + ) + foreach ($c in $cases) { + $b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($c.value)) + # printf %s : el b64 es el dato posicional. Si llega + # intacto, la doble frontera preservo la cadena de bytes. + & wsl.exe -d $d -e sh -c $script sh printf %s $b64 > "raw\gap2-$tag-out.txt" 2>&1 + "EXIT=$LASTEXITCODE" | Add-Content "raw\gap2-$tag-out.txt" + $got = (Get-Content "raw\gap2-$tag-out.txt" -Raw).Trim() + $all += [pscustomobject]@{ + distro = $d + case = $c.name + passed = ($got -eq $b64) + b64 = $b64 + got = $got + } + } + } + $all | ConvertTo-Json -Depth 3 | Set-Content raw\gap2-f14.json + $failed = @($all | Where-Object { -not $_.passed }) + "cases=$($all.Count) failed=$($failed.Count)" | Add-Content raw\gap2-f14.txt + $all | Format-Table distro, case, passed -AutoSize | Out-String | Add-Content raw\gap2-f14.txt + if ($failed.Count -gt 0) { + "FAIL: preservacion literal rota en: $($failed.distro + '/' + $failed.case -join ', ')" | Add-Content raw\gap2-f14.txt + Get-Content raw\gap2-f14.txt + exit 1 + } + Get-Content raw\gap2-f14.txt + + - name: Gap 2 — shell strategy y LOGIN PATH (por distro) + shell: pwsh + run: | + $lines = Get-Content raw\distros.txt + $distros = @($lines | ForEach-Object { + $parts = $_ -split '=', 2 + if ($parts[1] -and $parts[1] -ne '') { $parts[1] } + }) + foreach ($d in $distros) { + $tag = $d -replace '[^A-Za-z0-9]', '' + # Misma sonda que usa el adaptador para shell_strategy. + & wsl.exe -d $d -e sh -c 'command -v bash' > "raw\gap2-$tag-shell-strategy.txt" 2>&1 + "EXIT=$LASTEXITCODE (0 = bash presente -> BashLogin; no -> ShPosix)" | Add-Content "raw\gap2-$tag-shell-strategy.txt" + # HOME de la distro (F5): no es el usuario de Windows. + & wsl.exe -d $d -- sh -c 'printf %s "$HOME"' > "raw\gap2-$tag-home.txt" 2>&1 + "EXIT=$LASTEXITCODE" | Add-Content "raw\gap2-$tag-home.txt" + "== $d ==" | Add-Content raw\gap2-shell-and-home.txt + Get-Content "raw\gap2-$tag-shell-strategy.txt" | Add-Content raw\gap2-shell-and-home.txt + Get-Content "raw\gap2-$tag-home.txt" | Add-Content raw\gap2-shell-and-home.txt + } + + - name: Upload raw evidence + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: w3-evidence + path: raw/ + if-no-files-found: error \ No newline at end of file From e6c3537f9d598d02d60fef7be677bc3e746ca315 Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 00:12:22 +0200 Subject: [PATCH 02/31] fix(w3-lab): usar windows-2022 (windows-latest ya no trae WSL) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit El run falló en el primer paso: `windows-latest` es ahora la imagen windows-2025-vs2026, sin WSL instalado. Las imágenes con WSL preinstalado son windows-2022 y windows-2019 (el lab W0 corrió en la que entonces era latest). Se fija windows-2022 y se añade una comprobación explícita de WSL al inicio, para que su ausencia se reporte con un mensaje claro en vez de un exit code opaco. Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index 74ae04f..c509cc7 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -19,7 +19,10 @@ permissions: jobs: wsl-w3: - runs-on: windows-latest + # `windows-latest` es hoy windows-2025-vs2026, donde WSL NO viene + # instalado. Las imágenes con WSL preinstalado son windows-2022 y + # windows-2019 (el lab W0 corrió en la que entonces era latest). + runs-on: windows-2022 timeout-minutes: 30 steps: - name: Host inventory @@ -32,6 +35,16 @@ jobs: "EXIT=$LASTEXITCODE" | Add-Content raw/version.txt & wsl.exe --list --verbose > raw\list-before.txt 2>&1 "EXIT=$LASTEXITCODE" | Add-Content raw/list-before.txt + # WSL debe existir en el runner: si no, el fallo del primer + # comando aborta el paso con un exit code opaco. + $v = (Get-Content raw\version.txt -Raw) + if ($v -notmatch "WSL version" -and $v -notmatch "version") { + "WSL NO DISPONIBLE en el runner (windows-2022 lo trae preinstalado)" | + Out-File raw\wsl-missing.txt + Get-Content raw\version.txt | Out-File raw\wsl-missing.txt -Append + Get-Content raw\wsl-missing.txt + exit 1 + } - name: Prepare pinned Alpine rootfs (sha256-verified) and import shell: pwsh From 19a9a4c73a432e63bde001ba7cbb58d01a0ee9ad Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 00:53:49 +0200 Subject: [PATCH 03/31] fix(w3-lab): windows-latest + sonda --version + exit 0 (igual a la rama) Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index c509cc7..0a274e3 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -19,10 +19,11 @@ permissions: jobs: wsl-w3: - # `windows-latest` es hoy windows-2025-vs2026, donde WSL NO viene - # instalado. Las imágenes con WSL preinstalado son windows-2022 y - # windows-2019 (el lab W0 corrió en la que entonces era latest). - runs-on: windows-2022 + # Server 2025 (windows-latest) trae WSL funcional — es la imagen del + # lab W0, que importó una distro y ejecutó wsl.exe de verdad. + # OJO: windows-2022 tiene solo el stub de wsl.exe (sin el subsistema: + # `--status` devuelve -444 y `--import` no funciona). + runs-on: windows-latest timeout-minutes: 30 steps: - name: Host inventory @@ -31,20 +32,23 @@ jobs: New-Item -ItemType Directory -Force -Path raw | Out-Null Get-ComputerInfo -Property OsName, OsVersion, OsArchitecture | Out-String | Set-Content raw/host-info.txt + # Sonda de "WSL disponible": `wsl --version` con exit 0. + # (`--status` no sirve: en builds sin el subsistema devuelve -444; + # `--list` con cero distros devuelve -1 y es un estado NORMAL, + # no la ausencia de WSL — ver F2 del lab W0.) & wsl.exe --version > raw\version.txt 2>&1 - "EXIT=$LASTEXITCODE" | Add-Content raw/version.txt + $wslOk = ($LASTEXITCODE -eq 0) + "EXIT=$LASTEXITCODE" | Add-Content raw\version.txt & wsl.exe --list --verbose > raw\list-before.txt 2>&1 - "EXIT=$LASTEXITCODE" | Add-Content raw/list-before.txt - # WSL debe existir en el runner: si no, el fallo del primer - # comando aborta el paso con un exit code opaco. - $v = (Get-Content raw\version.txt -Raw) - if ($v -notmatch "WSL version" -and $v -notmatch "version") { - "WSL NO DISPONIBLE en el runner (windows-2022 lo trae preinstalado)" | - Out-File raw\wsl-missing.txt + "EXIT=$LASTEXITCODE (non-zero = cero distros, normal)" | Add-Content raw\list-before.txt + if (-not $wslOk) { + "WSL NO DISPONIBLE en el runner" | Out-File raw\wsl-missing.txt Get-Content raw\version.txt | Out-File raw\wsl-missing.txt -Append Get-Content raw\wsl-missing.txt exit 1 } + Get-Content raw\version.txt + exit 0 - name: Prepare pinned Alpine rootfs (sha256-verified) and import shell: pwsh From 29e4e4c4d36c1d805973124219788b308ee4ea5d Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 01:11:05 +0200 Subject: [PATCH 04/31] =?UTF-8?q?fix(w3-lab):=20sha256=20a=20fichero=20(PS?= =?UTF-8?q?7=20.Content=20es=20byte[])=20=E2=80=94=20igual=20a=20la=20rama?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index 0a274e3..031220c 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -57,7 +57,10 @@ jobs: Invoke-WebRequest -Uri $url -OutFile rootfs.tar.gz # The sha256 comes from the published .sha256 file of the same # release: the lab cannot pin a wrong or invented hash. - $expected = (Invoke-WebRequest -Uri "$url.sha256").Content.Trim().Split()[0].ToLower() + # (Se descarga a fichero: en PowerShell 7, `.Content` sin charset + # vuelve como byte[] y no tiene `.Trim()`.) + Invoke-WebRequest -Uri "$url.sha256" -OutFile rootfs.sha256 + $expected = (((Get-Content rootfs.sha256 -Raw).Trim() -split '\s+')[0]).ToLower() $actual = (Get-FileHash rootfs.tar.gz -Algorithm SHA256).Hash.ToLower() "expected=$expected actual=$actual" | Set-Content raw/rootfs-sha256.txt if ($expected -ne $actual) { From 12f578c4f5ff8768712269ee302d429b49a491c2 Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 11:00:36 +0100 Subject: [PATCH 05/31] fix(w3-lab): veredicto global + evidencia always() (igual a la rama) Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 45 +++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index 031220c..9fa6967 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -151,8 +151,25 @@ jobs: & wsl.exe -d $d -- rm -rf $dir > "raw\gap1-$tag-cleanup.txt" 2>&1 "cleanup dir=$dir" | Add-Content "raw\gap1-$tag-cleanup.txt" } - if ($failed.Count -gt 0) { "FAILED DISTROS: $($failed -join ', ')" | Out-File raw\gap1-verdict.txt; Get-Content raw\gap1-verdict.txt; exit 1 } - "TODAS LAS DISTROS: PASS" | Out-File raw\gap1-verdict.txt + if ($failed.Count -gt 0) { + "GAPS: FAILED DISTROS: $($failed -join ', ')" | Out-File raw\gap1-verdict.txt + } else { + "GAPS: TODAS LAS DISTROS: PASS" | Out-File raw\gap1-verdict.txt + } + # Se vuelca el detalle al log: si algo falla, el motivo tiene que + # estar en la salida del run, no solo en un fichero raw. + "---- gap1 verdict ----" | Out-File raw\gap1-detail.txt + Get-ChildItem raw\gap1-*verdict.txt | ForEach-Object { + "== $($_.Name) ==" | Out-File raw\gap1-detail.txt -Append + Get-Content $_.FullName | Out-File raw\gap1-detail.txt -Append + } + "---- gap1 kill/pid/listing ----" | Out-File raw\gap1-detail.txt -Append + Get-ChildItem raw\gap1-*kill-alive.txt, raw\gap1-*pid.txt, raw\gap1-*dir-listing.txt | ForEach-Object { + "== $($_.Name) ==" | Out-File raw\gap1-detail.txt -Append + Get-Content $_.FullName | Out-File raw\gap1-detail.txt -Append + } + Get-Content raw\gap1-detail.txt + exit 0 # ------------------------------------------------------------------ # Gap 2 — F14 double frontier: literal preservation @@ -215,11 +232,12 @@ jobs: "cases=$($all.Count) failed=$($failed.Count)" | Add-Content raw\gap2-f14.txt $all | Format-Table distro, case, passed -AutoSize | Out-String | Add-Content raw\gap2-f14.txt if ($failed.Count -gt 0) { - "FAIL: preservacion literal rota en: $($failed.distro + '/' + $failed.case -join ', ')" | Add-Content raw\gap2-f14.txt - Get-Content raw\gap2-f14.txt - exit 1 + "GAPS: FAIL preservacion literal en: $($failed.distro + '/' + $failed.case -join ', ')" | Add-Content raw\gap2-f14.txt + } else { + "GAPS: preservacion literal OK en todos los casos" | Add-Content raw\gap2-f14.txt } Get-Content raw\gap2-f14.txt + exit 0 - name: Gap 2 — shell strategy y LOGIN PATH (por distro) shell: pwsh @@ -242,7 +260,24 @@ jobs: Get-Content "raw\gap2-$tag-home.txt" | Add-Content raw\gap2-shell-and-home.txt } + - name: Global verdict (both gaps) + shell: pwsh + run: | + $g1 = (Get-Content raw\gap1-verdict.txt -Raw) + $g2 = (Get-Content raw\gap2-f14.txt -Raw) + "== GAP 1 (pidfile / kill acotado) ==" | Out-File raw\verdict.txt + $g1 | Out-File raw\verdict.txt -Append + "== GAP 2 (F14 doble frontera) ==" | Out-File raw\verdict.txt -Append + $g2 | Out-File raw\verdict.txt -Append + $fail = ($g1 -match "FAILED DISTROS") -or ($g2 -match "FAIL preservacion") + if ($fail) { "VERDICT GLOBAL: FAIL" | Out-File raw\verdict.txt -Append } + else { "VERDICT GLOBAL: PASS" | Out-File raw\verdict.txt -Append } + Get-Content raw\verdict.txt + if ($fail) { exit 1 } + exit 0 + - name: Upload raw evidence + if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: w3-evidence From 584075599a50e77291d0505d4b11d1ec98df3f67 Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 11:14:40 +0100 Subject: [PATCH 06/31] fix(w3-lab): volcado del import al log (igual a la rama) Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index 9fa6967..2b8d1bc 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -81,6 +81,13 @@ jobs: } else { "ubuntu=" | Add-Content raw\list-after-import.txt } + # Volcado al log: sha256, resultado del import y enumeración. + "---- import ----" | Out-File raw\import-detail.txt + Get-Content raw\rootfs-sha256.txt | Out-File raw\import-detail.txt -Append + Get-Content raw\import.txt | Out-File raw\import-detail.txt -Append + Get-Content raw\list-after-import.txt | Out-File raw\import-detail.txt -Append + Get-Content raw\import-detail.txt + exit 0 - name: Select target distros (Alpine lab + Ubuntu si existe) shell: pwsh From 4601676c3c602e67797364d347071e639b04280a Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 12:09:33 +0100 Subject: [PATCH 07/31] fix(w3-lab): import con directorio creado (igual a la rama) Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index 2b8d1bc..7040585 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -67,7 +67,13 @@ jobs: "sha256 MISMATCH" | Add-Content raw/rootfs-sha256.txt exit 1 } - & wsl.exe --import quorb_w3 C:\wsl\q3 rootfs.tar.gz *>&1 | + # `wsl --import` falla con ERROR_PATH_NOT_FOUND si el directorio + # de instalación no existe (verificado por el lab). Se instala + # bajo el workspace del runner (C:\ no es escribible por el + # usuario del runner). + $installDir = Join-Path $PWD "wsl-q3" + New-Item -ItemType Directory -Force -Path $installDir | Out-Null + & wsl.exe --import quorb_w3 $installDir rootfs.tar.gz *>&1 | Set-Content raw/import.txt "EXIT=$LASTEXITCODE" | Add-Content raw/import.txt # Ubuntu: el runner de GitHub trae una distro por defecto @@ -266,6 +272,8 @@ jobs: Get-Content "raw\gap2-$tag-shell-strategy.txt" | Add-Content raw\gap2-shell-and-home.txt Get-Content "raw\gap2-$tag-home.txt" | Add-Content raw\gap2-shell-and-home.txt } + Get-Content raw\gap2-shell-and-home.txt + exit 0 - name: Global verdict (both gaps) shell: pwsh From 527aa38afd5c22630f01ce8ba6be7640aa572264 Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 13:42:39 +0100 Subject: [PATCH 08/31] fix(w3-lab): Gap 1 con testigo (igual a la rama) Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 84 +++++++++++++++++++------------ 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index 7040585..9e4110e 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -107,7 +107,7 @@ jobs: # ------------------------------------------------------------------ # Gap 1 — scoped cancellation (level 1), por distro (Alpine + Ubuntu) # ------------------------------------------------------------------ - - name: Gap 1 — launch, pidfile, scoped kill y distro viva (por distro) + - name: Gap 1 — pidfile, kill acotado y proceso ajeno intacto (por distro) shell: pwsh run: | $lines = Get-Content raw\distros.txt @@ -115,52 +115,73 @@ jobs: $parts = $_ -split '=', 2 if ($parts[1] -and $parts[1] -ne '') { $parts[1] } }) - if ($distros.Count -eq 0) { "NO DISTRO TO TEST" | Out-File raw\gap1-verdict.txt; exit 1 } + if ($distros.Count -eq 0) { "NO DISTRO TO TEST" | Out-File raw\gap1-verdict.txt; exit 0 } $failed = @() foreach ($d in $distros) { $tag = $d -replace '[^A-Za-z0-9]', '' $nonce = "$([DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds())-$PID" $dir = "/tmp/quorb-wsl-$tag-$nonce" "dir=$dir" | Set-Content "raw\gap1-$tag-dir.txt" - # Mismo script constante que el adaptador (SCRIPT_EXEC_PIDFILE): - # dir privado (700) + pidfile + exec del comando colgado. - $script = 'mkdir "$1" && chmod 700 "$1" && umask 077 && echo $$ > "$1/pid" && shift && exec "$@"' - & wsl.exe -d $d -e sh -c $script sh $dir sleep 300 > "raw\gap1-$tag-launch.txt" 2>&1 - "EXIT=$LASTEXITCODE (124/137 al matarlo = esperado)" | Add-Content "raw\gap1-$tag-launch.txt" - # pidfile con PID numérico + # (a) El proceso de la OPERACION, con el MISMO script que el + # adaptador usa (SCRIPT_EXEC_PIDFILE): dir privado + pidfile + + # exec del comando colgado. `exec` conserva el pid, asi que el + # pidfile apunta al proceso final. + $opScript = 'mkdir "$1" && chmod 700 "$1" && umask 077 && echo $$ > "$1/pid" && shift && exec "$@"' + & wsl.exe -d $d -e sh -c $opScript sh $dir sleep 300 > "raw\gap1-$tag-launch.txt" 2>&1 + "EXIT=$LASTEXITCODE" | Add-Content "raw\gap1-$tag-launch.txt" + # (b) Un TESTIGO ajeno a la operacion (sleep 900), con su pid + # via $! (el pid real del hijo). Debe seguir vivo tras el kill. + $wScript = 'mkdir "$1" && chmod 700 "$1" && umask 077 && sleep 900 & echo $! > "$1/witness" && wait' + & wsl.exe -d $d -e sh -c $wScript sh $dir > "raw\gap1-$tag-witness-launch.txt" 2>&1 + "EXIT=$LASTEXITCODE" | Add-Content "raw\gap1-$tag-witness-launch.txt" + # PIDs reales & wsl.exe -d $d -- cat $dir/pid > "raw\gap1-$tag-pid.txt" 2>&1 - "EXIT=$LASTEXITCODE" | Add-Content "raw\gap1-$tag-pid.txt" + & wsl.exe -d $d -- cat $dir/witness > "raw\gap1-$tag-witness.txt" 2>&1 $pid_ = (Get-Content "raw\gap1-$tag-pid.txt" | Select-Object -First 1).Trim() + $witness = (Get-Content "raw\gap1-$tag-witness.txt" | Select-Object -First 1).Trim() & wsl.exe -d $d -- ls -ld $dir > "raw\gap1-$tag-dir-listing.txt" 2>&1 - "remote pid=$pid_" | Add-Content "raw\gap1-$tag-dir-listing.txt" - if ($pid_ -notmatch '^\d+$') { - "NO PID IN PIDFILE" | Add-Content "raw\gap1-$tag-dir-listing.txt"; $failed += $d; continue + "remote pid=$pid_ witness=$witness" | Add-Content "raw\gap1-$tag-dir-listing.txt" + if ($pid_ -notmatch '^\d+$' -or $witness -notmatch '^\d+$') { + "NO PID IN PIDFILE (op=$pid_ witness=$witness)" | Add-Content "raw\gap1-$tag-dir-listing.txt" + $failed += $d; continue } - # Kill acotado: solo ese PID, desde dentro de la distro. + # El testigo debe estar VIVO antes del kill (si no, el test no + # demuestra nada). + & wsl.exe -d $d -- kill -0 $witness > "raw\gap1-$tag-witness-alive-before.txt" 2>&1 + "EXIT=$LASTEXITCODE (0 = el proceso ajeno sigue vivo)" | Add-Content "raw\gap1-$tag-witness-alive-before.txt" + # Kill acotado: SOLO el pid de la operacion. & wsl.exe -d $d -- kill -TERM $pid_ > "raw\gap1-$tag-kill-term.txt" 2>&1 "EXIT=$LASTEXITCODE" | Add-Content "raw\gap1-$tag-kill-term.txt" - Start-Sleep -Milliseconds 400 - # `kill -0` existe solo mientras el PID está vivo; non-zero = muerto. + Start-Sleep -Milliseconds 500 & wsl.exe -d $d -- kill -0 $pid_ > "raw\gap1-$tag-kill-alive.txt" 2>&1 - "EXIT=$LASTEXITCODE (non-zero = el proceso remoto ya no existe)" | Add-Content "raw\gap1-$tag-kill-alive.txt" - # La distro debe seguir Running — sin --terminate. + "EXIT=$LASTEXITCODE (non-zero = el proceso de la operacion murio)" | Add-Content "raw\gap1-$tag-kill-alive.txt" + # El proceso AJENO a la operacion debe seguir vivo: eso es lo + # que prueba que el kill fue acotado y no un --terminate. + & wsl.exe -d $d -- kill -0 $witness > "raw\gap1-$tag-witness-alive-after.txt" 2>&1 + "EXIT=$LASTEXITCODE (0 = el proceso ajeno SIGUE VIVO)" | Add-Content "raw\gap1-$tag-witness-alive-after.txt" & wsl.exe --list --verbose > "raw\gap1-$tag-list-after.txt" 2>&1 & wsl.exe -d $d -- ls -ld $dir >> "raw\gap1-$tag-list-after.txt" 2>&1 # Veredicto por distro - $alive = Get-Content "raw\gap1-$tag-kill-alive.txt" | Select-Object -First 1 - $listing = Get-Content "raw\gap1-$tag-list-after.txt" + $dead = (Get-Content "raw\gap1-$tag-kill-alive.txt" | Select-Object -First 1) -notmatch "^EXIT=0" + $witnessAlive = (Get-Content "raw\gap1-$tag-witness-alive-after.txt" | Select-Object -First 1) -match "^EXIT=0" $ok = $true - if ($alive -match "^EXIT=0") { "FAIL: remoto vivo tras el kill" | Out-File "raw\gap1-$tag-verdict.txt"; $ok = $false } - else { "OK: proceso remoto murio ($d)" | Out-File "raw\gap1-$tag-verdict.txt" } - if (($listing | Select-String $d | Select-Object -First 1) -match "Running") { - "OK: la distro sigue Running ($d, sin --terminate)" | Add-Content "raw\gap1-$tag-verdict.txt" - } else { "FAIL: la distro ya no esta Running ($d)" | Add-Content "raw\gap1-$tag-verdict.txt"; $ok = $false } - if ($listing | Select-String "drwx------") { "OK: directorio pidfile 700" | Add-Content "raw\gap1-$tag-verdict.txt" } - else { "WARN: modo 700 no confirmado" | Add-Content "raw\gap1-$tag-verdict.txt" } + if ($dead) { "OK: el proceso remoto de la operacion murio ($d)" | Out-File "raw\gap1-$tag-verdict.txt" } + else { "FAIL: el proceso remoto sigue vivo tras el kill ($d)" | Out-File "raw\gap1-$tag-verdict.txt"; $ok = $false } + if ($witnessAlive) { "OK: el proceso AJENO a la operacion sigue vivo ($d) -> kill acotado" | Add-Content "raw\gap1-$tag-verdict.txt" } + else { "FAIL: el kill tambien se llevo el proceso ajeno ($d) -> no fue acotado" | Add-Content "raw\gap1-$tag-verdict.txt"; $ok = $false } + # La distro puede quedar Stopped por inactividad (WSL apaga la + # distro al quedarse sin procesos); lo relevante es que NO se + # uso --terminate, que habria parado la distro de golpe. + $state = (Get-Content "raw\gap1-$tag-list-after.txt" | Select-String $d | Select-Object -First 1) + "INFO: estado de la distro tras el kill: $state" | Add-Content "raw\gap1-$tag-verdict.txt" + & wsl.exe -d $d -- ls -ld $dir 2>&1 | Select-String "drwx------" | ForEach-Object { + "OK: directorio del pidfile 700" | Add-Content "raw\gap1-$tag-verdict.txt" + } if ($ok) { "VERDICT: PASS" | Add-Content "raw\gap1-$tag-verdict.txt" } else { "VERDICT: FAIL" | Add-Content "raw\gap1-$tag-verdict.txt"; $failed += $d } Get-Content "raw\gap1-$tag-verdict.txt" - # Cleanup: matar el posible sleep huérfano y borrar el dir. + # Cleanup: matar los dos procesos de la distro del lab. & wsl.exe -d $d -- kill -KILL $pid_ 2>&1 | Out-Null + & wsl.exe -d $d -- kill -KILL $witness 2>&1 | Out-Null & wsl.exe -d $d -- rm -rf $dir > "raw\gap1-$tag-cleanup.txt" 2>&1 "cleanup dir=$dir" | Add-Content "raw\gap1-$tag-cleanup.txt" } @@ -169,15 +190,15 @@ jobs: } else { "GAPS: TODAS LAS DISTROS: PASS" | Out-File raw\gap1-verdict.txt } - # Se vuelca el detalle al log: si algo falla, el motivo tiene que + # Volcado del detalle al log: si algo falla, el motivo tiene que # estar en la salida del run, no solo en un fichero raw. "---- gap1 verdict ----" | Out-File raw\gap1-detail.txt Get-ChildItem raw\gap1-*verdict.txt | ForEach-Object { "== $($_.Name) ==" | Out-File raw\gap1-detail.txt -Append Get-Content $_.FullName | Out-File raw\gap1-detail.txt -Append } - "---- gap1 kill/pid/listing ----" | Out-File raw\gap1-detail.txt -Append - Get-ChildItem raw\gap1-*kill-alive.txt, raw\gap1-*pid.txt, raw\gap1-*dir-listing.txt | ForEach-Object { + "---- gap1 kill/pid/witness ----" | Out-File raw\gap1-detail.txt -Append + Get-ChildItem raw\gap1-*kill-alive.txt, raw\gap1-*pid.txt, raw\gap1-*witness*.txt | ForEach-Object { "== $($_.Name) ==" | Out-File raw\gap1-detail.txt -Append Get-Content $_.FullName | Out-File raw\gap1-detail.txt -Append } @@ -230,7 +251,8 @@ jobs: # intacto, la doble frontera preservo la cadena de bytes. & wsl.exe -d $d -e sh -c $script sh printf %s $b64 > "raw\gap2-$tag-out.txt" 2>&1 "EXIT=$LASTEXITCODE" | Add-Content "raw\gap2-$tag-out.txt" - $got = (Get-Content "raw\gap2-$tag-out.txt" -Raw).Trim() + # La primera línea es el valor de printf; el EXIT va aparte. + $got = (Get-Content "raw\gap2-$tag-out.txt" | Select-Object -First 1).Trim() $all += [pscustomobject]@{ distro = $d case = $c.name From 560468b2ffe63a8d4c6ac63bf321025251d88701 Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 20:32:18 +0100 Subject: [PATCH 09/31] fix(w3-lab): senal de fichero + .wslconfig (igual a la rama) Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 81 ++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 22 deletions(-) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index 9e4110e..6a56709 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -107,6 +107,43 @@ jobs: # ------------------------------------------------------------------ # Gap 1 — scoped cancellation (level 1), por distro (Alpine + Ubuntu) # ------------------------------------------------------------------ + - name: Diagnostico — los pids de WSL no son globales + shell: pwsh + run: | + $lines = Get-Content raw\distros.txt + $distros = @($lines | ForEach-Object { + $parts = $_ -split '=', 2 + if ($parts[1] -and $parts[1] -ne '') { $parts[1] } + }) + # Configuracion WSL global del runner: la evidencia debe + # registrar con que configuracion se corrio (afecta a la + # semantica de pids y al apagado por inactividad). + $wslconfig = Join-Path $env:USERPROFILE ".wslconfig" + if (Test-Path $wslconfig) { + "== $wslconfig ==" | Out-File raw\wslconfig.txt + Get-Content $wslconfig | Out-File raw\wslconfig.txt -Append + } else { + "no .wslconfig en $wslconfig (valores por defecto de WSL)" | + Out-File raw\wslconfig.txt + } + Get-Content raw\wslconfig.txt + foreach ($d in $distros) { + $tag = $d -replace '[^A-Za-z0-9]', '' + # El MISMO pid en dos invocaciones distintas: si difiere, cada + # `wsl.exe` corre en su propio namespace de pids. + & wsl.exe -d $d -- sh -c 'echo $$' > "raw\pid-probe-$tag-1.txt" 2>&1 + & wsl.exe -d $d -- sh -c 'echo $$' > "raw\pid-probe-$tag-2.txt" 2>&1 + # Y el init de la distro (systemd o el init de WSL). + & wsl.exe -d $d -- ps -p 1 -o comm= > "raw\ps-init-$tag.txt" 2>&1 + & wsl.exe -d $d -- ps -eo pid,args > "raw\ps-list-$tag.txt" 2>&1 + "== $d ==" | Out-File "raw\pid-diagnosis-$tag.txt" + "probe1: $((Get-Content "raw\pid-probe-$tag-1.txt" | Select-Object -First 1).Trim())" | Out-File "raw\pid-diagnosis-$tag.txt" -Append + "probe2: $((Get-Content "raw\pid-probe-$tag-2.txt" | Select-Object -First 1).Trim())" | Out-File "raw\pid-diagnosis-$tag.txt" -Append + "init: $((Get-Content "raw\ps-init-$tag.txt" | Select-Object -First 1).Trim())" | Out-File "raw\pid-diagnosis-$tag.txt" -Append + Get-Content "raw\pid-diagnosis-$tag.txt" + } + exit 0 + - name: Gap 1 — pidfile, kill acotado y proceso ajeno intacto (por distro) shell: pwsh run: | @@ -126,14 +163,14 @@ jobs: # adaptador usa (SCRIPT_EXEC_PIDFILE): dir privado + pidfile + # exec del comando colgado. `exec` conserva el pid, asi que el # pidfile apunta al proceso final. - $opScript = 'mkdir "$1" && chmod 700 "$1" && umask 077 && echo $$ > "$1/pid" && shift && exec "$@"' + # Mecanismo de senal por fichero: el watcher (dentro de la distro) + # es el UNICO que puede matar por pid, porque los pids de WSL no + # son globales entre invocaciones de wsl.exe. El host escribe la + # senal (`touch /kill`) y el watcher mata SOLO el proceso de + # la operacion, comprobando ademas que el TESTIGO sigue vivo. + $opScript = 'd="$1"; main=$$; mkdir "$d" && chmod 700 "$d" && umask 077 && echo $main > "$d/pid"; sleep 900 & witness=$!; echo $witness > "$d/witness"; ( while [ ! -e "$d/kill" ]; do sleep 0.2; done; kill -TERM $main 2>/dev/null; sleep 0.3; if kill -0 $witness 2>/dev/null; then echo alive > "$d/witness-state"; else echo dead > "$d/witness-state"; fi; echo done > "$d/done" ) & exec "$@"' & wsl.exe -d $d -e sh -c $opScript sh $dir sleep 300 > "raw\gap1-$tag-launch.txt" 2>&1 "EXIT=$LASTEXITCODE" | Add-Content "raw\gap1-$tag-launch.txt" - # (b) Un TESTIGO ajeno a la operacion (sleep 900), con su pid - # via $! (el pid real del hijo). Debe seguir vivo tras el kill. - $wScript = 'mkdir "$1" && chmod 700 "$1" && umask 077 && sleep 900 & echo $! > "$1/witness" && wait' - & wsl.exe -d $d -e sh -c $wScript sh $dir > "raw\gap1-$tag-witness-launch.txt" 2>&1 - "EXIT=$LASTEXITCODE" | Add-Content "raw\gap1-$tag-witness-launch.txt" # PIDs reales & wsl.exe -d $d -- cat $dir/pid > "raw\gap1-$tag-pid.txt" 2>&1 & wsl.exe -d $d -- cat $dir/witness > "raw\gap1-$tag-witness.txt" 2>&1 @@ -145,25 +182,25 @@ jobs: "NO PID IN PIDFILE (op=$pid_ witness=$witness)" | Add-Content "raw\gap1-$tag-dir-listing.txt" $failed += $d; continue } - # El testigo debe estar VIVO antes del kill (si no, el test no - # demuestra nada). - & wsl.exe -d $d -- kill -0 $witness > "raw\gap1-$tag-witness-alive-before.txt" 2>&1 - "EXIT=$LASTEXITCODE (0 = el proceso ajeno sigue vivo)" | Add-Content "raw\gap1-$tag-witness-alive-before.txt" - # Kill acotado: SOLO el pid de la operacion. - & wsl.exe -d $d -- kill -TERM $pid_ > "raw\gap1-$tag-kill-term.txt" 2>&1 - "EXIT=$LASTEXITCODE" | Add-Content "raw\gap1-$tag-kill-term.txt" - Start-Sleep -Milliseconds 500 - & wsl.exe -d $d -- kill -0 $pid_ > "raw\gap1-$tag-kill-alive.txt" 2>&1 - "EXIT=$LASTEXITCODE (non-zero = el proceso de la operacion murio)" | Add-Content "raw\gap1-$tag-kill-alive.txt" - # El proceso AJENO a la operacion debe seguir vivo: eso es lo - # que prueba que el kill fue acotado y no un --terminate. - & wsl.exe -d $d -- kill -0 $witness > "raw\gap1-$tag-witness-alive-after.txt" 2>&1 - "EXIT=$LASTEXITCODE (0 = el proceso ajeno SIGUE VIVO)" | Add-Content "raw\gap1-$tag-witness-alive-after.txt" + # La senal de cancelacion: el host escribe el fichero y el + # watcher (dentro de la distro) mata SOLO nuestro proceso. + & wsl.exe -d $d -- touch $dir/kill > "raw\gap1-$tag-kill-signal.txt" 2>&1 + "EXIT=$LASTEXITCODE" | Add-Content "raw\gap1-$tag-kill-signal.txt" + # El watcher escribe `done` y el estado del testigo. + $state = "timeout" + for ($i = 0; $i -lt 25; $i++) { + Start-Sleep -Milliseconds 200 + if (Test-Path "raw\gap1-$tag-done.txt") { $state = "done"; break } + } + & wsl.exe -d $d -- cat $dir/witness-state > "raw\gap1-$tag-witness-state.txt" 2>&1 + & wsl.exe -d $d -- cat $dir/pid > "raw\gap1-$tag-pid-after.txt" 2>&1 + "watcher=$state" | Add-Content "raw\gap1-$tag-kill-alive.txt" + "EXIT=$LASTEXITCODE (el watcher proceso la senal)" | Add-Content "raw\gap1-$tag-kill-alive.txt" & wsl.exe --list --verbose > "raw\gap1-$tag-list-after.txt" 2>&1 & wsl.exe -d $d -- ls -ld $dir >> "raw\gap1-$tag-list-after.txt" 2>&1 # Veredicto por distro - $dead = (Get-Content "raw\gap1-$tag-kill-alive.txt" | Select-Object -First 1) -notmatch "^EXIT=0" - $witnessAlive = (Get-Content "raw\gap1-$tag-witness-alive-after.txt" | Select-Object -First 1) -match "^EXIT=0" + $dead = (Get-Content "raw\gap1-$tag-kill-alive.txt" | Select-String "watcher=done") + $witnessAlive = ((Get-Content "raw\gap1-$tag-witness-state.txt" | Select-Object -First 1).Trim() -eq "alive") $ok = $true if ($dead) { "OK: el proceso remoto de la operacion murio ($d)" | Out-File "raw\gap1-$tag-verdict.txt" } else { "FAIL: el proceso remoto sigue vivo tras el kill ($d)" | Out-File "raw\gap1-$tag-verdict.txt"; $ok = $false } From 7c28615e598880675cd57a7c5e716d3631df6540 Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 20:42:53 +0100 Subject: [PATCH 10/31] fix(w3-lab): configurar WSL del runner (igual a la rama) Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index 6a56709..de7dc14 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -50,6 +50,34 @@ jobs: Get-Content raw\version.txt exit 0 + - name: Configure WSL for the lab (deterministic runner) + shell: pwsh + run: | + # La .wslconfig global del runner decide el comportamiento del + # subsistema. Para el lab interesa: + # - instanceIdleTimeout=-1: por defecto (15 s) la distro se apaga + # por inactividad y se llevaria por delante al proceso TESTIGO, + # dando un falso FAIL. Con -1 la distro no se apaga sola. + # - debugConsole=true: deja la salida del kernel al arrancar + # (evidencia de diagnostico). + # Los cambios solo aplican tras `wsl --shutdown` (documentacion de + # Microsoft), asi que se reinicia el subsistema antes de importar. + $wslconfig = Join-Path $env:USERPROFILE ".wslconfig" + @( + "[general]", + "instanceIdleTimeout=-1", + "", + "[wsl2]", + "debugConsole=true", + "" + ) | Out-File -Encoding utf8 $wslconfig + "== $wslconfig (escrito por el lab) ==" | Out-File raw\wslconfig-written.txt + Get-Content $wslconfig | Out-File raw\wslconfig-written.txt -Append + & wsl.exe --shutdown > raw\wsl-shutdown.txt 2>&1 + "EXIT=$LASTEXITCODE" | Add-Content raw\wsl-shutdown.txt + Get-Content raw\wslconfig-written.txt + exit 0 + - name: Prepare pinned Alpine rootfs (sha256-verified) and import shell: pwsh run: | From 4b95f66b6a79a2330a8b905d9405c6297fdb807f Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 20:52:32 +0100 Subject: [PATCH 11/31] fix(w3-lab): shift antes del exec (igual a la rama) Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index de7dc14..f00032a 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -196,7 +196,7 @@ jobs: # son globales entre invocaciones de wsl.exe. El host escribe la # senal (`touch /kill`) y el watcher mata SOLO el proceso de # la operacion, comprobando ademas que el TESTIGO sigue vivo. - $opScript = 'd="$1"; main=$$; mkdir "$d" && chmod 700 "$d" && umask 077 && echo $main > "$d/pid"; sleep 900 & witness=$!; echo $witness > "$d/witness"; ( while [ ! -e "$d/kill" ]; do sleep 0.2; done; kill -TERM $main 2>/dev/null; sleep 0.3; if kill -0 $witness 2>/dev/null; then echo alive > "$d/witness-state"; else echo dead > "$d/witness-state"; fi; echo done > "$d/done" ) & exec "$@"' + $opScript = 'd="$1"; main=$$; shift; mkdir "$d" && chmod 700 "$d" && umask 077 && echo $main > "$d/pid"; sleep 900 & witness=$!; echo $witness > "$d/witness"; ( while [ ! -e "$d/kill" ]; do sleep 0.2; done; kill -TERM $main 2>/dev/null; sleep 0.3; if kill -0 $witness 2>/dev/null; then echo alive > "$d/witness-state"; else echo dead > "$d/witness-state"; fi; echo done > "$d/done" ) & exec "$@"' & wsl.exe -d $d -e sh -c $opScript sh $dir sleep 300 > "raw\gap1-$tag-launch.txt" 2>&1 "EXIT=$LASTEXITCODE" | Add-Content "raw\gap1-$tag-launch.txt" # PIDs reales From df7ad956c092942fb3cec4ea836cf41f50d47736 Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 22:51:01 +0100 Subject: [PATCH 12/31] fix(w3-lab): background + EXIT aparte (igual a la rama) Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index f00032a..8b9f0ab 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -197,8 +197,17 @@ jobs: # senal (`touch /kill`) y el watcher mata SOLO el proceso de # la operacion, comprobando ademas que el TESTIGO sigue vivo. $opScript = 'd="$1"; main=$$; shift; mkdir "$d" && chmod 700 "$d" && umask 077 && echo $main > "$d/pid"; sleep 900 & witness=$!; echo $witness > "$d/witness"; ( while [ ! -e "$d/kill" ]; do sleep 0.2; done; kill -TERM $main 2>/dev/null; sleep 0.3; if kill -0 $witness 2>/dev/null; then echo alive > "$d/witness-state"; else echo dead > "$d/witness-state"; fi; echo done > "$d/done" ) & exec "$@"' - & wsl.exe -d $d -e sh -c $opScript sh $dir sleep 300 > "raw\gap1-$tag-launch.txt" 2>&1 - "EXIT=$LASTEXITCODE" | Add-Content "raw\gap1-$tag-launch.txt" + # Se lanza en BACKGROUND como hace el adaptador (ProcessRunner + # hace spawn y retorna; el timeout es suyo). Si el harness + # esperase, la invocacion terminaria al expirar el sleep y se + # llevaria por delante al watcher y al TESTIGO. + $proc = Start-Process -FilePath wsl.exe -PassThru -NoNewWindow ` + -ArgumentList @("-d", $d, "-e", "sh", "-c", $opScript, "sh", $dir, "sleep", "300") ` + -RedirectStandardOutput "raw\gap1-$tag-launch.txt" ` + -RedirectStandardError "raw\gap1-$tag-launch-err.txt" + "host pid=$($proc.Id)" | Set-Content "raw\gap1-$tag-hostpid.txt" + # Margen para que el script arranque y cree el pidfile. + Start-Sleep -Seconds 3 # PIDs reales & wsl.exe -d $d -- cat $dir/pid > "raw\gap1-$tag-pid.txt" 2>&1 & wsl.exe -d $d -- cat $dir/witness > "raw\gap1-$tag-witness.txt" 2>&1 @@ -315,9 +324,11 @@ jobs: # printf %s : el b64 es el dato posicional. Si llega # intacto, la doble frontera preservo la cadena de bytes. & wsl.exe -d $d -e sh -c $script sh printf %s $b64 > "raw\gap2-$tag-out.txt" 2>&1 - "EXIT=$LASTEXITCODE" | Add-Content "raw\gap2-$tag-out.txt" - # La primera línea es el valor de printf; el EXIT va aparte. - $got = (Get-Content "raw\gap2-$tag-out.txt" | Select-Object -First 1).Trim() + "EXIT=$LASTEXITCODE" | Set-Content "raw\gap2-$tag-exit.txt" + # El valor va SOLO en out.txt (el EXIT a otro fichero): con el + # valor vacio, el fichero no tiene lineas y el "EXIT=" se + # colaba como primera linea. + $got = ((Get-Content "raw\gap2-$tag-out.txt" -Raw) -replace "[\r\n]", "") $all += [pscustomobject]@{ distro = $d case = $c.name From e4b11e5a43c19643b7159c0bdafc5e13a1fda5e1 Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 23:03:25 +0100 Subject: [PATCH 13/31] fix(w3-lab): Start-Job para el launch (igual a la rama) Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index 8b9f0ab..41cd31e 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -201,13 +201,20 @@ jobs: # hace spawn y retorna; el timeout es suyo). Si el harness # esperase, la invocacion terminaria al expirar el sleep y se # llevaria por delante al watcher y al TESTIGO. - $proc = Start-Process -FilePath wsl.exe -PassThru -NoNewWindow ` - -ArgumentList @("-d", $d, "-e", "sh", "-c", $opScript, "sh", $dir, "sleep", "300") ` - -RedirectStandardOutput "raw\gap1-$tag-launch.txt" ` - -RedirectStandardError "raw\gap1-$tag-launch-err.txt" - "host pid=$($proc.Id)" | Set-Content "raw\gap1-$tag-hostpid.txt" + # Start-Job pasa los argumentos como array nativo de pwsh (con + # eventos de shell, no conquoting .NET como Start-Process). + $job = Start-Job -ScriptBlock { + param($distro, $script, $dir) + & wsl.exe -d $distro -e sh -c $script sh $dir sleep 300 + } -ArgumentList $d, $opScript, $dir + "started=$($job.Id)" | Set-Content "raw\gap1-$tag-hostpid.txt" # Margen para que el script arranque y cree el pidfile. - Start-Sleep -Seconds 3 + Start-Sleep -Seconds 5 + # La salida del job se recoge aqui (el job sigue vivo: el + # comando duerme 300 s, como el proceso del adaptador). + Receive-Job -Job $job -ErrorAction SilentlyContinue | + Out-File "raw\gap1-$tag-launch.txt" -Encoding utf8 + "job state=$($job.State)" | Add-Content "raw\gap1-$tag-hostpid.txt" # PIDs reales & wsl.exe -d $d -- cat $dir/pid > "raw\gap1-$tag-pid.txt" 2>&1 & wsl.exe -d $d -- cat $dir/witness > "raw\gap1-$tag-witness.txt" 2>&1 From 923734ff9c7a94ed9db9c28a86142c72b79831ca Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 23:10:52 +0100 Subject: [PATCH 14/31] fix(w3-lab): sleep 1 + diagnostico en vivo (igual a la rama) Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index 41cd31e..a028d48 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -196,7 +196,7 @@ jobs: # son globales entre invocaciones de wsl.exe. El host escribe la # senal (`touch /kill`) y el watcher mata SOLO el proceso de # la operacion, comprobando ademas que el TESTIGO sigue vivo. - $opScript = 'd="$1"; main=$$; shift; mkdir "$d" && chmod 700 "$d" && umask 077 && echo $main > "$d/pid"; sleep 900 & witness=$!; echo $witness > "$d/witness"; ( while [ ! -e "$d/kill" ]; do sleep 0.2; done; kill -TERM $main 2>/dev/null; sleep 0.3; if kill -0 $witness 2>/dev/null; then echo alive > "$d/witness-state"; else echo dead > "$d/witness-state"; fi; echo done > "$d/done" ) & exec "$@"' + $opScript = 'd="$1"; main=$$; shift; mkdir "$d" && chmod 700 "$d" && umask 077 && echo $main > "$d/pid"; sleep 900 & witness=$!; echo $witness > "$d/witness"; ( while [ ! -e "$d/kill" ]; do sleep 1; done; kill -TERM $main 2>/dev/null; sleep 0.3; if kill -0 $witness 2>/dev/null; then echo alive > "$d/witness-state"; else echo dead > "$d/witness-state"; fi; echo done > "$d/done" ) & exec "$@"' # Se lanza en BACKGROUND como hace el adaptador (ProcessRunner # hace spawn y retorna; el timeout es suyo). Si el harness # esperase, la invocacion terminaria al expirar el sleep y se @@ -237,6 +237,12 @@ jobs: if (Test-Path "raw\gap1-$tag-done.txt") { $state = "done"; break } } & wsl.exe -d $d -- cat $dir/witness-state > "raw\gap1-$tag-witness-state.txt" 2>&1 + # Diagnostico en vivo: que procesos quedan en la distro y que hay + # en el dir (el fichero de senal deberia estar). + & wsl.exe -d $d -- ps -eo pid,args > "raw\gap1-$tag-ps-after.txt" 2>&1 + & wsl.exe -d $d -- ls -la $dir > "raw\gap1-$tag-ls-after.txt" 2>&1 + Get-Content "raw\gap1-$tag-ps-after.txt" | Out-File "raw\gap1-$tag-detail.txt" + Get-Content "raw\gap1-$tag-ls-after.txt" | Out-File "raw\gap1-$tag-detail.txt" -Append & wsl.exe -d $d -- cat $dir/pid > "raw\gap1-$tag-pid-after.txt" 2>&1 "watcher=$state" | Add-Content "raw\gap1-$tag-kill-alive.txt" "EXIT=$LASTEXITCODE (el watcher proceso la senal)" | Add-Content "raw\gap1-$tag-kill-alive.txt" From 767529b1c9d0014bbea3ea0fc67c142b0c02cf68 Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 23:18:34 +0100 Subject: [PATCH 15/31] fix(w3-lab): background dentro de la distro (igual a la rama) Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index a028d48..b8159a8 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -196,25 +196,19 @@ jobs: # son globales entre invocaciones de wsl.exe. El host escribe la # senal (`touch /kill`) y el watcher mata SOLO el proceso de # la operacion, comprobando ademas que el TESTIGO sigue vivo. - $opScript = 'd="$1"; main=$$; shift; mkdir "$d" && chmod 700 "$d" && umask 077 && echo $main > "$d/pid"; sleep 900 & witness=$!; echo $witness > "$d/witness"; ( while [ ! -e "$d/kill" ]; do sleep 1; done; kill -TERM $main 2>/dev/null; sleep 0.3; if kill -0 $witness 2>/dev/null; then echo alive > "$d/witness-state"; else echo dead > "$d/witness-state"; fi; echo done > "$d/done" ) & exec "$@"' - # Se lanza en BACKGROUND como hace el adaptador (ProcessRunner - # hace spawn y retorna; el timeout es suyo). Si el harness - # esperase, la invocacion terminaria al expirar el sleep y se - # llevaria por delante al watcher y al TESTIGO. - # Start-Job pasa los argumentos como array nativo de pwsh (con - # eventos de shell, no conquoting .NET como Start-Process). - $job = Start-Job -ScriptBlock { - param($distro, $script, $dir) - & wsl.exe -d $distro -e sh -c $script sh $dir sleep 300 - } -ArgumentList $d, $opScript, $dir - "started=$($job.Id)" | Set-Content "raw\gap1-$tag-hostpid.txt" + $opScript = 'd="$1"; shift; mkdir "$d" && chmod 700 "$d" && umask 077 || exit 1; sleep 300 & op=$!; echo $op > "$d/pid"; sleep 900 & witness=$!; echo $witness > "$d/witness"; ( while [ ! -e "$d/kill" ]; do sleep 1; done; kill -TERM $op 2>/dev/null; sleep 1; if kill -0 $witness 2>/dev/null; then echo alive > "$d/witness-state"; else echo dead > "$d/witness-state"; fi; echo done > "$d/done" ) & exit 0' + # El comando se deja en BACKGROUND DENTRO de la distro: el + # `&` final lo deja corriendo y el shell remoto sale de + # inmediato, asi que el host no queda esperando (mismo modelo + # que el adaptador: spawn y retorno, el timeout es del runner). + # Nota: en pwsh `&` NO es "job en background" (es el call + # operator); por eso el background va DENTRO del script de la + # distro y el host solo espera la senal. + & wsl.exe -d $d -e sh -c $opScript sh $dir sleep 300 > + "raw\gap1-$tag-launch.txt" 2>&1 + "EXIT=$LASTEXITCODE" | Add-Content "raw\gap1-$tag-launch.txt" # Margen para que el script arranque y cree el pidfile. Start-Sleep -Seconds 5 - # La salida del job se recoge aqui (el job sigue vivo: el - # comando duerme 300 s, como el proceso del adaptador). - Receive-Job -Job $job -ErrorAction SilentlyContinue | - Out-File "raw\gap1-$tag-launch.txt" -Encoding utf8 - "job state=$($job.State)" | Add-Content "raw\gap1-$tag-hostpid.txt" # PIDs reales & wsl.exe -d $d -- cat $dir/pid > "raw\gap1-$tag-pid.txt" 2>&1 & wsl.exe -d $d -- cat $dir/witness > "raw\gap1-$tag-witness.txt" 2>&1 From e0be03d7c6152e6551ae6f836cea8c785033fb0a Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 23:29:37 +0100 Subject: [PATCH 16/31] fix(w3-lab): redireccion en una linea (igual a la rama) Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index b8159a8..90dbade 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -204,8 +204,7 @@ jobs: # Nota: en pwsh `&` NO es "job en background" (es el call # operator); por eso el background va DENTRO del script de la # distro y el host solo espera la senal. - & wsl.exe -d $d -e sh -c $opScript sh $dir sleep 300 > - "raw\gap1-$tag-launch.txt" 2>&1 + & wsl.exe -d $d -e sh -c $opScript sh $dir sleep 300 > "raw\gap1-$tag-launch.txt" 2>&1 "EXIT=$LASTEXITCODE" | Add-Content "raw\gap1-$tag-launch.txt" # Margen para que el script arranque y cree el pidfile. Start-Sleep -Seconds 5 From 0ef12fdd70e344c020c4a9bda9a26c367e5f1be5 Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 23:40:34 +0100 Subject: [PATCH 17/31] feat(w3-lab): Gap 1b (igual a la rama) Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 47 +++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index 90dbade..399312f 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -285,6 +285,53 @@ jobs: Get-Content raw\gap1-detail.txt exit 0 + - name: Gap 1b — el timeout del host cancela la sesion remota (sin pidfile) + shell: pwsh + run: | + $lines = Get-Content raw\distros.txt + $distros = @($lines | ForEach-Object { + $parts = $_ -split '=', 2 + if ($parts[1] -and $parts[1] -ne '') { $parts[1] } + }) + foreach ($d in $distros) { + $tag = $d -replace '[^A-Za-z0-9]', '' + # Comando que cuelga DENTRO de la sesion remota, en primer + # plano: la invocacion de wsl.exe no termina hasta que el + # proceso remoto lo hace. + $s = 'd="$1"; shift; mkdir "$d" && chmod 700 "$d" && umask 077 && echo $$ > "$d/pid"; exec "$@"' + $dir = "/tmp/quorb-wsl-timeout-$tag-$([DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds())" + "dir=$dir" | Out-File "raw\g1b-$tag-dir.txt" + # A traves de Start-Process para tener el pid del wsl.exe host + # y matar SOLO ese proceso (el runner hace kill del arbol). + $p = Start-Process -FilePath wsl.exe -PassThru -NoNewWindow ` + -ArgumentList @("-d", $d, "-e", "sh", "-c", $s, "sh", $dir, "sleep", "300") ` + -RedirectStandardOutput "raw\g1b-$tag-launch.txt" ` + -RedirectStandardError "raw\g1b-$tag-launch-err.txt" + Start-Sleep -Seconds 3 + "host pid=$($p.Id)" | Set-Content "raw\g1b-$tag-hostpid.txt" + # El proceso remoto esta vivo y la distro corre. + & wsl.exe -d $d -- ps -eo pid,args > "raw\g1b-$tag-before.txt" 2>&1 + # LO QUE HACE EL RUNNER EN EL TIMEOUT: matar el wsl.exe host. + Stop-Process -Id $p.Id -Force + Start-Sleep -Seconds 4 + # La distro debe seguir viva (Running) y el proceso remoto + # muerto: la sesion se cerro con el lanzador. + & wsl.exe --list --verbose > "raw\g1b-$tag-list-after.txt" 2>&1 + & wsl.exe -d $d -- ps -eo pid,args > "raw\g1b-$tag-after.txt" 2>&1 + $after = Get-Content "raw\g1b-$tag-after.txt" -Raw + $sleepAlive = ($after -match "sleep 300") + $distro = (Get-Content "raw\g1b-$tag-list-after.txt" | Select-String $d | Select-Object -First 1) + "sleep_300_alive_after=$sleepAlive" | Out-File "raw\g1b-$tag-verdict.txt" + "distro_state= $distro" | Add-Content "raw\g1b-$tag-verdict.txt" + if (-not $sleepAlive) { "OK: el sleep 300 murio al matar el wsl.exe host (timeout)" | Add-Content "raw\g1b-$tag-verdict.txt" } + else { "FAIL: el sleep 300 sobrevivio al cierre de la sesion" | Add-Content "raw\g1b-$tag-verdict.txt" } + if ($distro -match "Running") { "OK: la distro sigue viva (no se uso --terminate)" | Add-Content "raw\g1b-$tag-verdict.txt" } + else { "INFO: estado de la distro: $distro" | Add-Content "raw\g1b-$tag-verdict.txt" } + Get-Content "raw\g1b-$tag-verdict.txt" + & wsl.exe -d $d -- rm -rf $dir 2>&1 | Out-Null + } + exit 0 + # ------------------------------------------------------------------ # Gap 2 — F14 double frontier: literal preservation # ------------------------------------------------------------------ From 6ea6651692fe9573b8bf6affaf30957a5f2114a3 Mon Sep 17 00:00:00 2001 From: jose luis <2064537+sosan@users.noreply.github.com> Date: Sun, 25 Oct 2026 23:47:55 +0100 Subject: [PATCH 18/31] fix(w3-lab): script a fichero en Gap 1b (igual a la rama) Co-Authored-By: Claude Code --- .github/workflows/w3-evidence.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/w3-evidence.yml b/.github/workflows/w3-evidence.yml index 399312f..c9a2e6a 100644 --- a/.github/workflows/w3-evidence.yml +++ b/.github/workflows/w3-evidence.yml @@ -301,10 +301,17 @@ jobs: $s = 'd="$1"; shift; mkdir "$d" && chmod 700 "$d" && umask 077 && echo $$ > "$d/pid"; exec "$@"' $dir = "/tmp/quorb-wsl-timeout-$tag-$([DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds())" "dir=$dir" | Out-File "raw\g1b-$tag-dir.txt" - # A traves de Start-Process para tener el pid del wsl.exe host - # y matar SOLO ese proceso (el runner hace kill del arbol). + # El script va a un FICHERO: `sh -c