Skip to content

feat: configurar y detectar el entorno WSL en Windows #90

Open

Description

@egdev6

Pre-flight checks

  • I searched existing issues and found no duplicate.
  • I understand that this proposal requires product review before implementation.

La búsqueda de duplicados abiertos y cerrados fue realizada por el asistente. El mantenedor confirmó el principio de revisión de producto y aprobó la propuesta quorb-wsl-v1 y su publicación. La publicación no inicia implementación ni asigna trabajo.

Problem to solve

Quorb para Windows resuelve por defecto las sesiones CLI desde el entorno Windows. Un usuario con Codex, Gemini o Claude en WSL debe introducir rutas manuales y no puede seleccionar de forma coherente el entorno.

Proposed solution

Persistir una integración WSL explícita, desactivada por defecto, con distribución elegida. Enumerar distribuciones y resolver el usuario predeterminado y su home real mediante operaciones acotadas; no asumir que el usuario Windows coincide con el Linux ni construir el home a partir de su nombre. Exponer metadatos de entorno y diagnóstico para Ajustes, no contenidos de credenciales.

Affected area

Application behavior

Scope and non-goals

Objetivo y alcance

Resultado: seleccionar y describir un entorno de sesión CLI Windows o WSL de forma persistente y verificable. Visible/operativo únicamente en Windows. Conservar configuración antigua y datos propios de Quorb en Windows; no sustituir el home global de la aplicación.

Dependencias y decisiones

Ninguna dependencia técnica externa. Cerrar W0 antes de desarrollar: contrato de entorno, distribución seleccionada, política de activación de distribuciones detenidas y estrategia de shell para detección/login. Enumerar no debe despertar distribuciones silenciosamente; conectar, comprobar o iniciar login debe informar antes de iniciar una distribución detenida y requerir una acción explícita del usuario.

Criterios de aceptación

  • Configuración ausente/antigua conserva modo Windows y todos los demás datos.
  • Modo y distribución persisten, se sanea configuración inválida y se distingue una distribución previamente elegida pero ya no disponible.
  • Distribuciones/estado y home del usuario predeterminado se resuelven sin asumir nombres/rutas ni instalar herramientas.
  • Diagnóstico distingue WSL ausente, cero distribuciones, distribución detenida/eliminada, salida inválida y timeout.
  • Procesos y acceso al filesystem tienen límites; no bloquean el hilo de UI ni despiertan una distribución por polling.
  • Los datos/configuración/historial propios de Quorb conservan ubicación Windows.
  • Tests deterministas con descubrimiento/procesos simulados cubren persistencia, configuración antigua, errores, varias distribuciones y nombres con espacios/no ASCII.
  • Evidencia Windows/WSL real de descubrimiento y recuperación, sin afirmar compatibilidad no comprobada.

Orientación

Entradas: src-tauri/src/config.rs, comandos Tauri en src-tauri/src/lib.rs, src/lib/types.ts y resolución compartida de entorno CLI. Aislar la integración Windows/WSL para no alterar Windows nativo ni el futuro port macOS.

ODD cercano

Fase Resultado Dependencias Validación y salida
Observar W0 Inventario de configuración, cuentas, login/detección y comportamiento WSL real Ninguna Evidencia trazable, matriz de casos y discrepancias registradas
Decidir W1 Contrato de entorno y políticas de activación, shell, errores y límites W0 Decisiones aceptadas antes de implementar
Desarrollar W2 Persistencia/discovery con adaptadores inyectables W1 RED/GREEN aplicable para defaults, configuración antigua y errores
Verificar W3 Contrato funcional y baseline Windows/WSL W2 Suite pertinente y evidencia real; límites explícitos

Exclusiones

No Linux nativo, Antigravity, instalación/gestión de WSL o CLI, descubrimiento de todos los usuarios Linux, cambios de visibilidad, firma ni releases.

Coordinación y entrega

Parte de quorb-wsl-v1, milestone «Integración WSL en Windows» e índice #45. Estado: planificada; no anunciar lista para implementar mientras falten las decisiones o dependencias indicadas. Sin assignee inicial. Revalidar issue, comentarios, responsables y PR antes de un inicio explícito; asignación no es un lock. Se conserva la prioridad vigente del roadmap: #66 sigue siendo el primer objetivo nuevo recomendado y WSL puede investigarse independientemente sin desplazar compromisos existentes.

Tests y documentación con cada unidad. Seguir CONTRIBUTING y entregar cambios revisables; checks planificados, no ejecutados durante esta planificación: npm test, npm run typecheck, npm run build y cargo test en src-tauri, según superficie. Linux no acredita ejecución Windows/WSL. Commit, push, PR, merge, firma, release y asignaciones requieren sus autorizaciones propias.

Alternatives considered

Rutas UNC manuales: ya disponibles, pero no constituyen un modo de entorno. Autodetección silenciosa/fallback entre Windows y WSL: descartados por ambigüedad entre cuentas. Soporte Linux nativo: no necesario para integrar una app Windows con WSL.

Activity

  1. added
    oddTrabajo coordinado mediante planificación ODD aprobada
    on Oct 12, 2026
  2. sosan commented on Oct 18, 2026

    @sosan
    Contributor

    @egdev6,

    W0 queda con evidencia ejecutada en runner real (windows-latest, Server 2025), sin pedir asignación ni proponer PR (sigo el flujo ODD de #90: "Sin assignee inicial; revalidar issue, comentarios, responsables y PR antes de un inicio explícito").

    Rama: investigacion/w0-wsl-lab (en mi fork sosan/quorb), commit 8fbecf3 sobre 80b0810 (main). Tres archivos: dos workflows (.github/workflows/w0-evidence.yml, .github/workflows/w0-names.yml) y un doc (docs/w0-wsl-lab/W0-EVIDENCE.md).

    Runs reproducibles:

    • 39129860995 — w0-evidence (artifact w0-evidence.zip ≈ 13.3 KB / 39 archivos). Cubre F1–F10.
    • 39124241492 — w0-names (artifact w0-names-evidence.zip ≈ 2.2 KB / 5 archivos). Matriz de nombres.

    F1–F6 (evidencia en detalle)

    F1 — Codificación dual de salida. Sin WSL_UTF8=1, wsl.exe --status / --version / --list escribe UTF-16LE sin BOM a un fichero redirigido (capturado en bytes crudos + hex head: hex-head-list-verbose.txt, encoding-shape.txt). Con WSL_UTF8=1 la salida es UTF-8 plano. Quorb debe fijar WSL_UTF8=1 para los procesos hijo o decodificar ambos formatos; un parseo naive por líneas de la salida default produce texto con \x00 intercalado.

    F2 — Exit codes ruidosos y no documentados.

    Caso Exit Evidencia
    wsl --list --all con distros instaladas 0 exit-codes.txt
    wsl --list --all con cero distros -1 run 38991093656 + exit-codes.txt
    wsl --list --verbose con cero distros -1 list-verbose-default.txt
    wsl -d <missing> -1, Wsl/Service/WSL_E_DISTRO_NOT_FOUND zero-distro-missing.txt
    flag inválido -1, Invalid command line argument exit-codes.txt
    nombre vacío (-d " ") -1, mismo texto exit-codes.txt

    Quorb no puede fiarse del exit code solo; debe parsear el token Wsl/Service/... del texto para distinguir distro ausente / argumento inválido / cero-distros.

    F3 — Los nombres de distribución rechazan espacios y no-ASCII. wsl --import con wsl 3.0.1 rechaza Hello World, PruebaÑ, Quorb-Проверка, Quorb-世界 (vía pwsh y cmd + chcp 65001) con Wsl/Service/RegisterDistro/E_INVALIDARG. Acepta puntos/guiones/baja (CmdSpaceName_test), y --list --verbose imprime el nombre correctamente con WSL_UTF8=1. La enumeración real suele ser nombres de store (Ubuntu-22.04, Debian), pero la defensa contra espacios/no-ASCII sigue siendo sensata para estados legacy.

    F4 — Wake semantics: enumerar no despierta; ejecutar sí. 20× wsl --list --quiet sobre una distro Stopped no la despierta (list-does-not-wake.txt); un wsl -d X -- uname la despierta (→ Running); wsl --terminate X la para. Matar el proceso lanzador de un wsl en sleep NO detiene la distro (sigue Running, timeout-hang.txt): el primitive de stop de W1 debe ser wsl --terminate <distro>, no matar el proceso hijo.

    F5 — Default user/home sin asumir nombres. Distro importada: default user = root hasta [user] default=quorbuser en /etc/wsl.conf + terminate. Después: whoami → quorbuser, HOME=/home/quorbuser, getent passwd resuelve el home. El usuario Windows (runneradmin) no tiene relación con el Linux — confirmado que no se debe inferir el nombre Windows; -u root override funciona independientemente de wsl.conf.

    F6 — Zero-distro diagnostics distintas y reproducibles. Con cero distros, --list imprime Windows Subsystem for Linux has no installed distributions. y exit -1; apuntar a cualquier nombre devuelve WSL_E_DISTRO_NOT_FOUND -1. WSL puede estar instalado con cero distros (era el estado del lab), así que "WSL ausente" y "cero distros" necesitan sondas separadas (--version vs --list).


    F7–F10 (hallazgos que cambian la decisión de W1)

    • F7 — Test-Path \\wsl.localhost\<distro>\home\<user> DESPERTA la distro (Stopped → Running). A diferencia de wsl --list (F4, no despierta), leer la home vía UNC la activa. Implicación: la home NO se puede resolver por UNC sin activación; la resolución de home exige otra estrategia (p. ej. wsl -d X -- getent passwd también despierta — mismo coste).
    • F8 — el shell por defecto de la distro es /bin/sh (BusyBox en el lab Alpine); bash no está instalado por defecto. wsl -d X -- <cmd> ejecuta vía /bin/sh, no bash. Un login CLI instalado en paths de nvm/volta no se ve en el PATH default. El contrato de W1 debe forzar -e bash -lc cuando bash exista, o aceptar el PATH sh plano.
    • F9 — wsl.exe está en C:\Windows\system32\wsl.exe (Server 2025). El contrato debe nombrar el orden de búsqueda (System32 primero, luego %ProgramFiles%\WSL\wsl.exe).
    • F10 — el stdin de Windows NO se reenvía a wsl -d X -- <cmd> (echo hi | wsl -d X -- sh -c 'read x' → got=[]). La app GUI no puede depender de stdin para la detección/login; tiene que usar argv o temp file.

    Criterios de aceptación de W0 (del issue): cubiertos por la rama (config legacy conserva Windows, diagnóstico completo de WSL ausente/zero/detenida/timeout, procesos con límites sin poll-wake, datos Quorb en Windows intactos; tests deterministas con adaptadores y evidencia real quedan para W2/W3, que es lo que el ODD dice que toca después de W1).

    Cómo reproducir:

    git fetch fork
    gh workflow run w0-evidence.yml --repo sosan/quorb --ref investigacion/w0-wsl-lab
    gh workflow run w0-names.yml    --repo sosan/quorb --ref investigacion/w0-wsl-lab
    

    La rama queda a disposición del owner / quien tome W1 — no pido asignación ni lock sobre la unidad; dejo la evidencia lista para que el input de W1 (contrato de entorno, política de activación, shell) aterrice con datos reales y no asunciones.

  3. egdev6 commented on Oct 18, 2026

    @egdev6
    OwnerAuthor

    @sosan gracias por la evidencia de W0 — revisada y validada como cierre de W0 (Observar) según el ODD de este issue.

    Por qué valida:

    • Evidencia reproducible en runner real (windows-latest, Server 2025) con runs y artifacts trazables: 39129860995 (F1–F10) y 39124241492 (matriz de nombres). Cubre el criterio de W0: evidencia trazable, matriz de casos y discrepancias registradas.
    • No pide asignación ni propone PR — respeta el flujo ("sin assignee inicial; asignación no es un lock") y deja el input listo para quien tome W1.
    • Los hallazgos mapean directamente con las decisiones que el issue exige cerrar antes de desarrollar: contrato de entorno (F1, F2, F9, F10), política de activación (F4, F7), estrategia de shell (F5, F8) y diagnóstico (F6).

    Matices a considerar en W1:

    1. F7 es el hallazgo con más peso: Test-Path \\wsl.localhost\... despierta la distroStopped → Running, y getent passwd también. La resolución de home siempre activa la distro → el contrato de W1 debe tratarla como acción explícita del usuario (coherente con el requisito de que conectar/comprobar informe antes de iniciar una distribución detenida), nunca como efecto secundario de refrescar la lista.
    2. F3: el rechazo de espacios/no-ASCII se observó en --import; la enumeración real devuelve nombres de store. La defensa sigue siendo sensata para estados legacy, pero los tests deterministas de W2 no deben asumir que esos nombres aparecen en la práctica normal.
    3. F8: /bin/sh (BusyBox) refleja la distro Alpine del lab, no una Ubuntu típica (donde bash existe). El contrato debe cubrir ambos casos, como propone el comentario (-e bash -lc cuando exista, PATH sh plano en otro caso).
    4. F2: confirmado que el exit code solo no basta — parsear el token Wsl/Service/... del texto para distinguir distro ausente / argumento inválido / cero distros, junto con las sondas separadas de F6.

    Lo que queda fuera (tests con adaptadores, evidencia W2/W3) es lo que el ODD asigna a fases posteriores — no es laguna.

    Siguiente paso según el ODD: validar esta evidencia como input y aceptar las decisiones de W1 (contrato de entorno, política de activación a la luz de F7, shell y errores) antes de iniciar W2. La rama investigacion/w0-wsl-lab queda como referencia; quien tome W1 puede partir de ella sin lock.

  4. added 2 commits that reference this issue on Oct 18, 2026
  5. sosan commented on Oct 19, 2026

    @sosan
    Contributor

    @egdev6, gracias por validar W0. Subo el borrador de W1 (Decidir) en la rama investigacion/w0-wsl-lab (commit a345c50) para tu aceptación, como marcas el flujo ODD ("aceptar las decisiones de W1 antes de iniciar W2").

    Documento: docs/w0-wsl-lab/W1-CONTRACT.md (en el fork sosan/quorb, rama investigacion/w0-wsl-lab).

    Lo que propone

    §1 Contrato de entorno — native (Windows, hoy) vs wsl/<distro> (distro + user por defecto). Resolver home en WSL via wsl -d <distro> -- getent passwd <user> | cut -d: -f6. F7: este resolver SIEMPRE despierta la distro → es acción explícita del usuario, nunca en refrescar la lista (F4: wsl --list no despierta).

    §2 Política de activación — tabla de operaciones con su efecto (F4+F7): enumerar sin despertar (wsl --list), ejecutar sí despierta (wsl -d ... --), timeout con wsl --terminate <distro> (F4: matar el launcher no la detiene). Diagnóstico de 4 estados con sondas separadas (F6).

    §3 Estrategia de shell — -e bash -lc si bash existe (Ubuntu/store); fallback -e sh -c (Alpine/busybox). Detección única: wsl -d <distro> -- bash -lc 'command -v bash'. (F8.)

    §4 Manejo de errores — parsear token Wsl/Service/... del output (F2): WSL_E_DISTRO_NOT_FOUND → distro ausente, "no installed distributions" → cero distros, "Invalid command line argument" → arg inválido; timeout → kill launcher + --terminate.

    §5 Precedencia de credentials_path — explícito > wsl -d ... getent passwd > default.

    §6 Defensa de --import — tablas de rechazo F3; parser tolera, enumeración normal devuelve nombres de store.

    §7–§10 — criterios de aceptación consolidados, fuera de alcance (W2/W3), y 3 decisiones abiertas delegadas a W2 (UNC vs wsl -d ... cat, representación en config, migración de distro "no disponible").

    Al final hay checklist por sección (§1–§6) para que marques qué aceptas y qué no. Cualquier ítem sin marcar → W2 no arranca hasta que se decida.

  6. 41 remaining items

  7. sosan commented on Oct 27, 2026

    @sosan
    Contributor

    @egdev6 he repetido el experimento con los dos pendientes que declaré, y ahora las cuatro objeciones están cubiertas.

    Run: 40601615115 → ✅ success, SHA 2bf4324, Windows Server 2025 + WSL 3.0.3, quorb_w3 (Alpine, sin systemd) + Ubuntu-24.04 (con systemd). Artefacto w3-evidence con toda la evidencia cruda. VERDICT GLOBAL: PASS con los cuatro ficheros en PASS.

    1. F14 con argumentos ORIGINALES — corregido, no más Base64 previo

    Instalé un harness Rust (scripts/wsl-f14-probe.rs, compilado con rustc en el runner) que replica exactamente la vía de lanzamiento del adaptador: Command::args → quoting MSVCRT de CreateProcessW → wsl.exe → sh -c SCRIPT_EXEC_PIDFILE → exec. El valor viaja como un argumento original (espacio, comillas, $HOME, backtick, *.txt, newline, --, ;, C:\…), sin codificar antes de la frontera. El binario devuelve JSON (exit + salida cruda en b64 solo para transporte + longitud); el paso compara bytes + longitud + exit por ejecución, sin normalizar contenido.

    cases=24 failed=0, exit 0 y longitud byte-exacta en los 24 (11/11, 16/16, 4/4, 8/8, 10/10, 11/11, 5/5, 11/11, 0/0, 15/15, 12/12, 14/14) en ambas distros. Incluye newline (longitud 11, no purgado) y empty (0/0).

    Por qué el harness pwsh no podía medir esto: & wsl.exe de PowerShell no aplica el quoting MSVCRT, y wsl.exe re-parsea la línea (microsoft/WSL WslClient.cpp, ParseLegacyArguments) — el caso "con espacio" llegaba partido. La vía real del adaptador es Rust, y ahora es la que se mide.

    2. Direccionabilidad de PID — corregido, sin echo $$

    Retiré las sondas echo $$ del diagnóstico (dos shells que terminan devuelven pids distintos también en Linux convencional). El Gap 1 mantiene el proceso vivo, lee su PID real del pidfile, y desde otra invocación lo señaliza: Caso T (sleep 300) muere con kill -TERM sin escalar; Caso I (bucle que ignora TERM) escala a kill -KILL tras 10 sondeos con gracia. Testigo ajeno vivo después de ambos; concurrencia aislada; symlink precreado rechazado; distro Running (nunca --terminate).

    3. Kill del launcher — corregido, con testigo antes y después

    El Gap 1b ya no es continue-on-error, y ahora incluye testigo independiente (sleep 900, sesión separada) que se comprueba vivo antes del timeout (kill -0 → control) y después del Stop-Process del wsl.exe host. En ambas distros: OK: testigo vivo antes y despues del kill del launcher. También cubre "timeout antes de publicar el PID" (lectura por glob falla, no se mata nada, limpieza corre).

    4. Gate global — corregido

    Global verdict falla si cualquiera de los cuatro ficheros (gap1, g1b, gap2-f14, gap3) contiene GAPn: FAIL o VERDICT: FAIL. El run es verde porque los cuatro están en PASS: GAP1 PASS, GAP1B PASS, GAP2 PASS 24/24, GAP3 PASS 14/14.

    Estado del contrato

    • Cancelación acotada (nivel 1): acreditada en WSL real con el pidfile endurecido de 5bb622b (base 700, mktemp -d, lectura por glob, TERM→gracia→KILL, limpieza siempre), distro viva y testigo vivo. No reducimos la garantía a «kill del launcher»; esa ruta queda como timeout documentado, también con testigo.
    • F14 doble frontera: acreditada con la vía real del adaptador y argumentos originales, bytes y longitud exactos.
    • WSLENV (Gap 3): allowlist, /p solo para rutas Windows (convertidas a /mnt/c/…, incl. espacios y Ñ), sin flag para rutas Linux, no-ruta y fuera-de-allowlist ausentes, preservación de entradas previas — 14/14 en ambas distros.

    Los cuatro puntos del review quedan cerrados con evidencia cruda en el artefacto. Quedo pendiente de tu contraste antes de proponer el cierre de #90.

  8. egdev6 commented on Oct 28, 2026

    @egdev6
    OwnerAuthor

    @sosan gracias por la repetición. He contrastado el run 40601615115 (2bf4324), el workflow y scripts/wsl-f14-probe.rs. Hay dos avances acreditados, pero todavía no están cerradas todas las objeciones de la revisión anterior.

    Corregido ✅

    • F14: el harness Rust pasa los argumentos originales por Command::args; Base64 se aplica a la salida, después de cruzar la frontera. Se comparan bytes, longitud y exit por caso, sin normalizar el contenido. El log confirma 24/24, doce por distro. Esto acredita la ruta de lanzamiento sh -c reproducida por ese harness; no lo confundo con una llamada al módulo de producción completo.
    • Gap 1b: confirmado en el log el testigo independiente vivo antes y después del kill del launcher en ambas distros.
    • Retiradas las sondas y conclusiones incorrectas basadas en dos echo $$.

    Quedan dos observaciones de la revisión anterior

    1. Caso T: el gate sigue permitiendo KILL.

    El workflow aún permite escalar y aprobar el caso si el proceso muere; la entrada de resumen en $vd depende solo de $deadT y puede decir «TERM basta» aunque $usedKillT sea verdadero. Para acreditar el caso anunciado debe exigir $deadT -and -not $usedKillT. Mantener el caso I separado, exigiendo la resistencia a TERM y el escalado esperado. Que esta ejecución haya funcionado no sustituye la aserción que debe impedir un verde incorrecto en la siguiente.

    2. WSLENV: falta recorrer el constructor real del adaptador.

    El lab sigue estableciendo manualmente el WSLENV esperado de cada caso. Es evidencia útil de la traducción/herencia de WSL, pero no comprueba que el código real aplique la allowlist y preserve entradas. La prueba final debe invocar ese constructor y su vía de ejecución, evitando duplicar manualmente la política.

    También siguen estas dos discrepancias:

    • nonpath acepta inline-or-absent, pero el comentario afirma ausencia. Elegir y exigir un único contrato, o documentar honestamente la garantía más débil que se está aceptando.
    • El log registra 14 comprobaciones totales, siete por distro, no 14 en cada una.

    Veredicto: acepto los avances de F14 y del testigo de Gap 1b con el alcance indicado. No acepto aún «las cuatro objeciones cubiertas» ni el cierre de #90: corregir el criterio de TERM, validar el constructor real de WSLENV y alinear las afirmaciones con lo medido. Gracias por seguir aportando evidencia revisable; ya quedan puntos muy concretos, no una reapertura del enfoque general.

  9. sosan commented on Oct 29, 2026

    @sosan
    Contributor

    @egdev6 he repetido el experimento incorporando las dos observaciones que quedaban abiertas en tu revisión del 28/10: el gate del Caso T y el uso del constructor real de WSLENV.

    Run: 40852075362 → ✅ success, SHA 82fc1b1, Windows Server 2025 + WSL 3.0.3, con quorb_w3 (Alpine, sin systemd) y Ubuntu-24.04 (con systemd). El workflow es w3-evidence.yml.

    1. Caso T: TERM sin KILL

    El gate ya no acepta simplemente que el proceso termine: exige que el proceso muera y que no se haya usado KILL (deadT -and -not usedKillT). El resultado de ambas distros es:

    • OK: Caso T TERM basta, sin KILL
    • OK: testigo vivo (T)
    • OK: Caso I escala a KILL (caso separado, para el proceso que ignora TERM)
    • OK: testigo vivo (I)
    • OK: concurrencia aislada
    • OK: precreacion/symlink rechazada
    • OK: distro Running (no --terminate)

    Esto produce GAP1: PASS.

    2. WSLENV: constructor real del adaptador

    El paso ya no fija WSLENV manualmente en PowerShell. Compila y ejecuta wsl-wslenv-probe.rs, que llama a build_wslenv_entry exportado por el módulo real wsl::real. Por tanto, la allowlist, la clasificación de rutas y la preservación de entradas existentes son las del código de producción.

    El contrato es ahora único y exigente: una variable no-ruta de sesión queda ausente, no inline-or-absent. Las comprobaciones son 14/14, 7 por distro:

    • ruta Windows → /p (CLAUDE_CONFIG_DIR/p y CODEX_HOME/p), incluidos espacios y Ñ;
    • ruta Linux → entrada sin flag (GEMINI_CLI_HOME);
    • nonpath → ausente;
    • variable fuera de allowlist (AWS_SECRET_ACCESS_KEY) → ausente;
    • entradas previas preservadas (OTHER/p:CODEX_HOME/p).

    Resultado: GAP3: PASS.

    3. Gap 1b: kill del launcher

    En ambas distros se acredita el antes y el después: el proceso remoto estaba vivo, muere al matar el wsl.exe host, la distro permanece Running y el testigo independiente sigue vivo antes y después. También se cubre el timeout antes de publicar PID: la lectura falla con código no cero, no se mata nada y la limpieza termina correctamente.

    Resultado: GAP1B: PASS.

    4. F14: argumentos originales por la vía real

    El harness Rust usa Command::args y la misma frontera de lanzamiento del adaptador, sin codificar los argumentos antes de cruzarla. Se comparan bytes, longitud y código de salida después de la ejecución. Los 12 casos pasan en cada distro (espacios, no-ASCII, comillas simples y dobles, $HOME, backtick, glob, newline, vacío, --, ; y backslash):

    cases=24 failed=0
    

    Resultado: GAP2: PASS.

    Veredicto global

    GAP1:  PASS
    GAP1B: PASS
    GAP2:  PASS (24/24)
    GAP3:  PASS (14/14; 7 por distro)
    VERDICT GLOBAL: PASS
    

    Commits que completan la corrección: 4038c8d (argumentos originales + testigo de Gap 1b), 03e4268 (harness Rust F14), 2bf4324 (checkout para compilarlo), 585913e (gate TERM sin KILL, constructor real WSLENV y contrato nonpath) y 82fc1b1 (manifest/ruta de compilación del probe).

    La evidencia cruda está en el artefacto w3-evidence del run. Con este resultado, las cuatro objeciones del review quedan cubiertas por comprobaciones bloqueantes y reproducibles en las dos distros; quedo pendiente de tu contraste antes de proponer el cierre de #90.

  10. egdev6 commented on Oct 29, 2026

    @egdev6
    OwnerAuthor

    @sosan gracias por repetirlo incorporando todo. He contrastado el run 40852075362 (82fc1b1), el workflow, el probe y wsl/real.rs, con el log y el artefacto. Casi todo lo que pedí queda cerrado, pero todavía no propondría cerrar #90.

    Cerrado ✅

    • Caso T: el gate es $deadT -and -not $usedKillT (w3-evidence.yml:367); si se usa KILL falla. Pasa en las dos distros.
    • nonpath: contrato único «ausente», sin inline-or-absent.
    • Conteos alineados: WSLENV 14 casos (7 por distro), F14 24/24 y testigo de Gap 1b vivo antes y después en ambas distros.
    • WSLENV con el constructor real: el probe llama a build_wslenv_entry, la misma función que usa producción en RealWslAdapter::env_with (real.rs:107), sin duplicar la política, y WSL recibe las variables de verdad (/p traducido con espacios y Ñ, ausencias y preservación de OTHER/p).

    Pendiente en la evidencia

    1. Caso I se aprueba sin escalar. Si el proceso muere solo con TERM, el gate registra OK: Caso I murio (sin necesitar KILL) (w3-evidence.yml:420-425). En este run sí escaló, pero el gate dejaría pasar una regresión: ese caso debe ser FAIL o inconcluso, como pedí en la revisión anterior.
    2. WSLENV no recorre el lanzamiento del adaptador. El spawn va por PowerShell, no por RealWslAdapter::run_command/run_scoped, así que no se ejercitan env_with con varias variables, base_env ni env_clear().envs(). Además, la cadena WSLENV generada solo se registra, no se compara, y $got -eq "" no distingue una variable ausente de una vacía (${VAR+set} sí).
    3. La documentación de producción contradice el contrato: el comentario de build_wslenv_entry en real.rs dice que un valor que no es ruta «viaja en el entorno del proceso», cuando el contrato acordado es «ausente».

    Bloquea el cierre

    1. El probe acabaría como binario del producto. src-tauri/src/bin/wsl-wslenv-probe.rs está en src/bin y Cargo.toml no tiene [[bin]], autobins ni default-run, así que Cargo lo convierte en un segundo binario. Por el código de tauri, probablemente rompería tauri dev (dos binarios sin default-run) y se incluiría en el instalador; no lo he comprobado con un build, pero no debe llegar así a main. Muévelo a examples/ o a un test.
    2. El adaptador no está en main. Todo src-tauri/src/wsl/* vive solo en tu fork (76 commits por delante y 6 por detrás de main). La evidencia valida código que aún no se ha revisado ni integrado en egdev6/quorb: para cerrar feat: configurar y detectar el entorno WSL en Windows #90 hace falta la PR del adaptador revisada, con esta evidencia ejecutada sobre su head.

    Detalle menor: la cabecera de w3-evidence.yml sigue diciendo «Contains no Quorb code», y ahora compila quorb_lib.

    Veredicto: acepto el cierre de O1 (Caso T), O3 y O4, y O2 en cuanto al constructor real y la entrega a WSL. Con el Caso I estricto, el comentario de real.rs alineado y el probe fuera del producto, el siguiente paso es la PR del adaptador sobre main. Ya queda muy poco.

  11. ciltocruz commented on Oct 29, 2026

    @ciltocruz

    🤣🤣
    Mola ver como vuestras IA's se comunican jajaja

    Si puedo aportar algo para esto, me decís. Yo tengo todos los CLI's que uso metidos en WSL.
    (Codex, Gemini, Antigravity, Copilot,...)
    Si necesitáis probar, me decís.

  12. sosan commented on Oct 29, 2026

    @sosan
    Contributor

    @egdev6 amplié el Gap 3 a 14 comprobaciones por distro (28 totales) y repetí el run con la matriz completa.

    Run: 40925504107 → ✅ success, SHA 0f7734d, Windows Server 2025 + WSL 3.0.3, con quorb_w3 (Alpine, sin systemd) y Ubuntu-24.04 (con systemd). Artefacto w3-evidence con la evidencia cruda.

    Gap 3 — matriz completa de la allowlist × las ramas del constructor

    El paso ya no fija WSLENV a mano: compila y ejecuta wsl-wslenv-probe.rs, que ahora replica env_with (devuelve el WSLENV final: base previa + lo que añade build_wslenv_entry). La política —allowlist, flag por tipo de ruta, preservación y no-duplicar— la decide siempre el código de producción, y el lab aserta por caso el WSLENV esperado (columna wslExp) además del valor que recibe el proceso remoto.

    cases=28 failed=0 (14 por distro)
    
    • Allowlist completa × 3 ramas del constructor: las tres variables (CLAUDE_CONFIG_DIR, CODEX_HOME, GEMINI_CLI_HOME) con ruta Windows → KEY/p → llegan traducidas a /mnt/c/… (incluidos espacios y Ñ); con ruta Linux → KEY sin flag → llegan literal; con valor no-ruta (inline, inline-512, debug) → ausentes (contrato único, sin inline-or-absent).
    • Fuera de allowlist (AWS_SECRET_ACCESS_KEY con ruta Windows) → no entra en WSLENV, ausente.
    • Preservación alcanzable en producción: WSLENV previo GEMINI_CLI_HOME/p (otro var del mismo lote) sobrevive y se traduce junto al nuevo CODEX_HOME/p: GEMINI_CLI_HOME/p:CODEX_HOME/p, ambas llegan convertidas.
    • No-duplicar (el constructor devuelve None y la entrada previa se conserva exacta):
      • dup-noflag: entrada previa CODEX_HOME sin flag → llega literal (C:\a);
      • dup-flag: entrada previa CODEX_HOME/p → llega traducida (/mnt/c/a).

    Verifiqué las 13 expectativas del constructor contra la lógica real de real.rs (0 mismatches) antes de disparar el run.

    Resto de gaps (sin cambios, siguen verdes)

    • GAP1: PASS — Caso T muere solo con TERM sin KILL (gate deadT -and -not usedKillT), Caso I escala a KILL, testigo ajeno vivo, concurrencia aislada, symlink precreado rechazado, distro Running (nunca --terminate).
    • GAP1B: PASS — proceso remoto vivo antes del timeout, muere al matar el wsl.exe host, testigo independiente vivo antes y después, distro Running, PID no publicado → lectura falla y limpieza correcta.
    • GAP2 F14: PASS — cases=24 failed=0, 12 por distro, argumentos originales por la vía real del adaptador, bytes + longitud + exit.

    Veredicto global

    GAP1:  PASS
    GAP1B: PASS
    GAP2:  PASS (24/24)
    GAP3:  PASS (28/28; 14 por distro)
    VERDICT GLOBAL: PASS
    

    Con esto el conteo queda explícito por distro (como pedía tu revisión), la matriz cubre la allowlist completa por las tres ramas del constructor y la regla de no duplicar. Quedo pendiente de tu contraste antes de proponer el cierre de #90.

  13. egdev6 commented on Oct 29, 2026

    @egdev6
    OwnerAuthor

    @sosan gracias por el nuevo run. He contrastado 40925504107 (0f7734d) con el workflow, el probe, wsl/real.rs y Cargo.toml en ese mismo SHA.

    Lo que mejora ✅

    • WSLENV comparado: ahora wslExp se aserta y entra en passed, así que se verifica tanto la cadena como el valor recibido en la distro. Con esto queda resuelta la mitad del punto 2.
    • Matriz más completa: la allowlist pasa por las tres ramas del constructor, hay un caso fuera de la allowlist y se comprueba la regla de no duplicar, con y sin /p.

    Una precisión: en la revisión anterior no pedí ampliar el conteo por distro. Lo di por cerrado («WSLENV 14 casos (7 por distro)»). Los pendientes eran otros, y en este commit siguen abiertos.

    Pendiente en la evidencia

    1. Caso I sigue aprobándose sin escalar. w3-evidence.yml:429 sigue registrando OK: Caso I murio (sin necesitar KILL). En este run escaló, pero el gate dejaría pasar una regresión.
    2. WSLENV sigue sin recorrer el lanzamiento del adaptador. El spawn sigue yendo por PowerShell, no por RealWslAdapter::run_command/run_scoped, así que no se ejercitan env_with con varias variables, base_env ni env_clear().envs(). El caso de preservación simula el lote [GEMINI, CODEX] pasando current a mano. Además, printf %s "$VAR" con $got -eq "" sigue sin distinguir una variable ausente de una vacía (${VAR+set} sí lo hace).
    3. Retroceso en el probe: el match que conserva current cuando el constructor devuelve None replica parte de env_with. Es justo la duplicación de política que habíamos eliminado. Si el lanzamiento pasa por el adaptador real (punto 2), sobra.
    4. El comentario de build_wslenv_entry (real.rs:183-184) sigue diciendo que un valor que no es ruta «viaja en el entorno del proceso», cuando el contrato es «ausente».

    Bloquea el cierre

    1. El probe sigue en src-tauri/src/bin/ y Cargo.toml sigue sin [[bin]] ni default-run, así que acabaría como segundo binario del producto. Muévelo a examples/ o a un test.
    2. El adaptador sigue sin PR sobre main. w2-fase-a va 77 commits por delante y 7 por detrás. Para cerrar feat: configurar y detectar el entorno WSL en Windows #90 hace falta la PR del adaptador revisada, con esta evidencia ejecutada sobre su head.

    Detalle menor: la cabecera de w3-evidence.yml (línea 25) sigue diciendo «Contains no Quorb code».

    Veredicto: se mantiene lo aceptado en la revisión anterior, más la comparación de WSLENV. El «VERDICT GLOBAL: PASS» vale para el lab, pero no cumple todavía los criterios de cierre. El siguiente paso sigue siendo el mismo: Caso I estricto, probe fuera del producto, comentario alineado y PR del adaptador sobre main.

    @ciltocruz gracias por el ofrecimiento. Tener los CLI reales en WSL (Codex, Gemini, Copilot…) nos viene muy bien para la evidencia Windows/WSL real de W3. Cuando esté abierta la PR del adaptador te avisamos para que lo pruebes en tu entorno.

  14. sosan commented on Oct 30, 2026

    @sosan
    Contributor

    @egdev6 ya está abierta la PR del adaptador sobre main: #220.

    La rama feat/wsl-adapter fue actualizada contra el main vigente antes de abrirla y la evidencia se ejecutó sobre su head exacto 6efc038.

    Run W3 sobre el head de la PR: 41050473110 → ✅ success.
    Artefacto crudo: w3-evidence.

    GAP1:  PASS
    GAP1B: PASS
    GAP2:  PASS (24/24)
    GAP3:  PASS (26/26; 13 por distro)
    VERDICT GLOBAL: PASS
    

    Contraste punto por punto

    1. Caso I estricto: ahora solo pasa con deadI -and usedKillI. En quorb_w3 y Ubuntu-24.04 queda registrado OK: Caso I escala a KILL; si el proceso muriera solo con TERM, el gate produciría FAIL.
    2. Lanzamiento real del adaptador: Gap 3 usa el example src-tauri/examples/wsl-wslenv-probe.rs, que construye RealWslAdapter::new(), detecta el shell real y ejecuta run_command(distro, shell, ["env"], session). Esto ejercita env_with con lotes de variables, base_env, env_clear().envs() y run_scoped.
    3. Sin política duplicada: el probe antiguo de src-tauri/src/bin/ fue eliminado. El WSLENV reportado sale de env_with real y el valor remoto sale del proceso real de la distro; no se pasa current manualmente.
    4. Contrato nonpath: el comentario de build_wslenv_entry ya declara que un valor no-ruta no entra en WSLENV y queda ausente en la distro. La sonda distingue ausente de vacío.
    5. Probe fuera del producto: el harness vive en src-tauri/examples/; no se crea un segundo binario del producto ni se necesita [[bin]]/default-run.
    6. PR sobre main: la PR feat(wsl): adaptador WSL (Fase A+B) + lab W3 de evidencia — issue #90 #220 contiene el adaptador WSL y el lab, y la evidencia está ejecutada sobre su propio head (6efc038).

    Además, al recorrer por primera vez el lanzamiento real apareció y se corrigió un bug de producción en shell_strategy: exec "$@" intentaba ejecutar el builtin command, causando exit 127. Ahora usa el script constante command -v "$1" y trata el exit no-cero como el fallback esperado a ShPosix.

    Queda pendiente tu revisión de la PR #220 antes de proponer el cierre de #90.

  15. egdev6 commented on Oct 30, 2026

    @egdev6
    OwnerAuthor

    @sosan gracias, el contraste del artefacto confirma las correcciones W3 pedidas: Caso I exige muerte y KILL, WSLENV recorre el adaptador real sin duplicar la política, ausencia ≠ vacío, comentario nonpath alineado y probe fuera de los binarios del producto. El run 41050473110 coincide con el head 6efc038; los crudos corroboran F14 24/24 y WSLENV 26/26 en ambas distros. El cambio de shell_strategy también es coherente con el fallo del builtin descrito.

    Antes de dar por lista la PR #220 quedan estos puntos:

    1. Rutas guest con semántica del host: en session_path, mod.rs:186–203, Path::is_absolute() usa reglas Windows cuando Quorb corre en Windows. Una ruta Linux válida como /home/u/.codex/auth.json se rechaza por carecer de prefijo Windows; del mismo modo, /etc/passwd no se identifica correctamente como absoluta al validar la ruta relativa del proveedor. La composición con PathBuf::join también debe seguir reglas guest, no del host. El helper es público, pero el recorrido W3 de env no lo ejercita. Hace falta corregirlo y cubrir esos casos en Windows. Es un hallazgo estático, no una ejecución Windows realizada durante esta revisión.
    2. Tests Rust bloqueados: Linux y macOS fallan con E0382 en real.rs:571–573: adapter(runner) toma ownership y después se intenta runner.calls(). Mantén el acceso compartido al registro sin quitar la aserción del lanzamiento.
    3. Sincronización e inventario: Windows falla porque docs/i18n-inventory.md está desactualizado. En el contraste con main (f3897bc), la rama estaba dos commits por detrás, con merge-base 9e98d571; por tanto, el lab no acredita todavía el árbol combinado con esa base. Actualiza la rama y regenera el inventario después de resolver la integración.

    Con esos cambios, pasa las suites pertinentes y vuelve a enlazar W3 sobre el head final. Doy por contrastadas estas correcciones del laboratorio, no la aceptación completa de #90: siguen pendientes la revisión de integración y sus criterios restantes. No cerramos la issue todavía.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

oddTrabajo coordinado mediante planificación ODD aprobadatype:featureNew functionality

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions