Skip to content

test(linux): verificación W3 en Linux real — fixtures de sesión, smoke de Secret Service y gate de bundles (#106) - #176

Merged
egdev6 merged 14 commits into
egdev6:mainfrom
sosan:feat-linux-l1-106-retake
Oct 23, 2026
Merged

egdev6 merged 14 commits into
egdev6:mainfrom
sosan:feat-linux-l1-106-retake

Conversation

@sosan

@sosan sosan commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Resumen

Cierra la verificación W3 del ODD de #106 (quorb-linux-local-v1), con evidencia ejecutada en runner Ubuntu 22.04 real:

  • Fixtures de sesión real por proveedor (Claude, Codex, Gemini) con tests deterministas y reloj inyectado — parsean la estructura exacta que escribe cada CLI tras el login oficial, sin credenciales en el runner.
  • Smoke de Secret Service real (dbus-run-session + gnome-keyring-daemon --unlock): guardar → leer → borrar vía backend linux-native.
  • Corrección del gate de bundles: los scripts buscaban quorb_*.deb/quorb_*.AppImage en minúscula pero Tauri produce Quorb_*; ambos checks se saltaban con exit 0 y el orquestador declaraba «OK» sobre un set vacío. Ahora find -iname + fail por tipo.
  • Evidencia documentada en docs/linux-l1-evidence/README.md, con límites honestos.

Cambios

Commit Qué
5ca36e4 linux-ci.yml: smoke de Secret Service (criterio 2)
d7a0f8c fix(ci): búsqueda de bundles case-insensitive + exit 1 si vacío
72b18b8 docs(linux): evidencia W3
43caa59 test(linux): 3 fixtures de sesión real + tests deterministas
99ae704 fix(providers): warning unused variable en token_needs_refresh
2f33507 test(linux): endurecer gate por tipo + evidencia directa
f2e4050 test(linux): fixture Claude fiel al layout v2.1.x + nota macOS Keychain

Evidencia (runs en el fork sosan/quorb, workflow_dispatch)

  • 39698848192 — tests: cargo test --locked (233 passed, 1 ignored = smoke) + smoke Secret Service: 1 passed
  • 39837767677 — tests: incluye los 3 fixtures (todos ok; 249 passed, 0 failed) + smoke Secret Service
  • 39800266204 — bundle: .deb y .AppImage arrancados de verdad en Ubuntu 22.04 limpia (60 s)

Pendiente declarado

  • AC2 migrate_legacy() (migración del servicio legacy): el smoke cubre guardar/leer/borrar, no la migración en sí. Registrado como pendiente en la evidencia con propuesta de validación.
  • Sin llamada autenticada a proveedores (sin credenciales en runner), sin escritorio real, solo ubuntu-22.04, y no se lee CLAUDE_CODE_OAUTH_TOKEN.

Refs #106

sosan and others added 9 commits October 22, 2026 01:46
…6#106)

Recupera el linux-ci.yml consolidado (rama ci/linux-test-manual, commit
4ffd147) y añade el step W3 de la issue egdev6#106: Secret Service smoke real.

- gh gnome-keyring al apt del runner (provee el servicio)
- dbus-run-session crea un session bus aislado; gnome-keyring-daemon
  --unlock levanta el Secret Service con password de prueba propia.
- Corre el test #[ignore] linux_secret_service_round_trip (secrets.rs):
  guarda/lee/borra un valor de prueba via backend linux-native (libsecret)
  — criterio 2 de egdev6#106 ("Secret Service probado en escritorio real").

Los unit tests de login con rutas complejas (split_login_command con
espacios/no-ASCII/fail-closed) ya corren en el cargo test --locked previo
(criterio 3). El workflow sigue manual (workflow_dispatch por ref).

Co-Authored-By: Claude Code <noreply@anthropic.com>
…tifacts

Tauri emits Quorb_<version>_amd64.deb/AppImage with an uppercase Q, while
container verification searched for lowercase quorb_*. Both checks silently
skipped and the orchestrator reported success without launching either
binary. Find bundles case-insensitively and reject an empty artifact set so
bundle verification is a real gate.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Registra los runs 39698848192 (tests + smoke Secret Service) y
39800266204 (bundle + arranque .deb/.AppImage) ejecutados sobre
feat-linux-l1-106-retake en Ubuntu 22.04:

- Smoke linux_secret_service_round_trip (gnome-keyring vía
  dbus-run-session): guardar/leer/borrar OK en el Secret Service real.
- Build de bundles OK y arranque verificado en Ubuntu 22.04 limpia.
- Documenta el bug del glob quorb_*.deb/AppImage (minúscula) que
  saltaba la validación y el fix case-insensitive + fail-on-empty.
- Límites honestos: sin sesiones reales de CLI, sin escritorio real,
  solo ubuntu-22.04.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…tas (egdev6#106)

Cubre el criterio 4 de egdev6#106 (proveedores comprobados en Linux real) de
forma determinista y SIN credenciales reales en CI, alineado con la tabla
de proveedores del README («Login oficial de <CLI>»):

- fixtures/claude-credentials.json: estructura que escribe ~/.claude/.credentials.json
  (claudeAiOauth.accessToken/expiresAt/subscriptionType).
- fixtures/codex-auth.json: estructura que escribe $CODEX_HOME/auth.json
  (tokens.{id_token,access_token,refresh_token,account_id}) + JWT id_token con
  las claims que parse_validity exige (exp > now, chatgpt_account_id, suscripción).
- fixtures/gemini-oauth_creds.json: estructura que escribe ~/.gemini/oauth_creds.json
  (access_token/refresh_token/expiry_date).

Tests (funciones puras existentes, mismo patrón que is_expired/parse_validity):
- claude: parsea el fixture real y decide caducidad con reloj inyectado.
- codex: parsea el fixture real y parse_validity reporta validez (now fijo).
- gemini: extrae token_needs_refresh pura (sin HTTP) y verifica que el fixture
  NO dispara refresh (expiry lejano) ni usa token expirado.

Refactor mínimo necesario: token_needs_refresh(creds, now_ms) en gemini, pura
y testeable; access_token la usa. El refresh HTTP real (expirado) queda
identificado como dependiente de red — gap documentado en docs/linux-l1-evidence.

Co-Authored-By: Claude Code <noreply@anthropic.com>
El warning `unused variable: t` (CI) saltaba al filtrar el access_token:
el binding solo comprobaba presencia. Ahora se expresa como booleano
`usable` con is_some_and; misma semántica, sin warning.

Co-Authored-By: Claude Code <noreply@anthropic.com>
)

Endurece el build-verify tras la revisión del owner: cada verify-*.sh
falla (exit 1) si falta SU propio tipo de bundle, en vez de avisar y
seguir. El gate solo pasa si .deb y .AppImage están ambos y arrancan.

Actualiza la evidencia W3 con el log leído del run 39837767677: los 3
tests de fixture de sesión real y el smoke de Secret Service aparecen
como `ok`. Documenta el pendiente de migrate_legacy (criterio 2) y el
detalle de KEYRING_PASS para reproducir el smoke.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Enriquece el fixture con refreshToken y scopes (campos que escribe el CLI y
Quorb ignora) y documenta en la evidencia dónde vive la sesión de Claude por
plataforma: macOS usa el Keychain (servicio Claude Code-credentials, ya
cubierto por keychain_fallback) y Linux/WSL/Windows el JSON. Registra como
límite que Quorb no lee CLAUDE_CODE_OAUTH_TOKEN.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@egdev6

egdev6 commented Oct 23, 2026

Copy link
Copy Markdown
Owner

@sosan he revisado los commits nuevos (43caa59, 99ae704, 2f33507, f2e4050 y el merge 0fd3dad) y reproducido la verificación sobre el head del PR.

🔴 Bloqueante: el inventario i18n está desactualizado — el CI de upstream fallará

Tras los merges con main (7876cd2/0fd3dad), la rama contiene el módulo src/i18n/ (de #159) y el gate de staleness (#155), pero docs/i18n-inventory.md sigue registrando el árbol pre-i18n (src/ = c64cc86). Verificado:

  • node scripts/i18n-inventory.mjs --check → falla en 43caa59, f2e4050 y en el head del PR.
  • .github/workflows/ci.yml:45 ejecuta ese check → el PR se pondrá en rojo al correr CI upstream.
  • Los runs del fork no lo detectaron porque linux-ci.yml no ejecuta el check de inventario (solo ci.yml).

Fix: node scripts/i18n-inventory.mjs --out docs/i18n-inventory.md y commit. Un cambio de una línea.

✅ Verificación reproducida (head del PR)

Check Resultado
cargo test --locked 256 passed, 0 failed (incluye los 3 fixtures nuevos; el run del fork 39837767677 confirma los 3 parses_real_session_fixture ... ok + smoke Secret Service 1 passed)
cargo clippy --all-targets 0 avisos en gemini/fixtures (99ae704 elimina el unused var correctamente)
vitest run --dir src 658 passed
tsc --noEmit Limpio
Run fork 39837767677 ✅ success en 43caa59, smoke y fixtures visibles en el log

✅ Los commits nuevos, bien

  • 43caa59 — fixtures de sesión real: los 3 fixtures usan valores example-* con campo _note que aclara «NO credenciales reales» — sin secretos. El JWT de codex es fabricado con SIGNATURE_PLACEHOLDER y claims válidas para parse_validity. Los tests usan reloj inyectado (patrón is_expired/parse_validity) — deterministas, sin red.
  • 99ae704: refactor de token_needs_refresh a función pura con now_ms inyectado; semántica idéntica, testable sin HTTP. El gap del refresh real queda documentado.
  • 2f33507 — gate por tipo: exactamente lo propuesto en ci(linux): endurecer verify.sh — fallar si falta cualquiera de los dos bundles, no solo si falta todo #175. verify-deb.sh/verify-appimage.sh ahora exit 1 si falta su bundle; verify.sh simplificado y el mensaje final declara que se validaron los dos. Este PR cierra ci(linux): endurecer verify.sh — fallar si falta cualquiera de los dos bundles, no solo si falta todo #175 — sugiero añadir Closes #175 a la descripción.
  • f2e4050: fixture Claude al layout v2.1.x (refreshToken/scopes que Quorb ignora) + nota honesta de Keychain en macOS y del CLAUDE_CODE_OAUTH_TOKEN no leído.
  • Pendiente de migrate_legacy(): ahora documentado en la evidencia con propuesta de validación — ese pendiente queda registrado en el sitio correcto.

Veredicto

Aprobar con un cambio requerido: regenerar docs/i18n-inventory.md para que el CI upstream pase. Con eso, el PR cumple los criterios de #106 en lo automatizable, cierra #175, y deja documentados honestamente los límites (migración legacy, sin llamadas autenticadas, solo ubuntu-22.04).

Los scripts de build-verify fallan con exit 1 si falta su propio bundle
(commit 2f33507), pero el contrato no estaba protegido contra regresión.
Añade un test node --test que ejercita los tres scripts (bash puro, fallan
en el find antes de instalar/lanzar) contra un ARTIFACTS_DIR temporal:

- falta el .deb  -> exit 1
- falta el .AppImage -> exit 1
- set vacío -> exit 1
- solo llega el .AppImage -> el gate entero en rojo

Los scripts aceptan ARTIFACTS_DIR (default /artifacts) para poder testearlos
sin contenedor; el comportamiento del contenedor no cambia.

Co-Authored-By: Claude Code <noreply@anthropic.com>
npm test también corre en windows-latest (ci.yml), donde bash/find no
existen y el spawnSync fallaría con ENOENT. Los scripts de build-verify
son bash de Linux; el test se autoexcluye fuera de POSIX.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@egdev6

egdev6 commented Oct 23, 2026

Copy link
Copy Markdown
Owner

@sosan re-revisión de los dos commits nuevos (3b20966 + 6b4970b). El test del gate es un buen añadido, pero hay dos bloqueantes, uno arrastrado y uno nuevo que detecté ejecutando el test.

🔴 Bloqueante 1 (arrastrado, sin resolver): inventario i18n stale

Sigue sin regenerarse — node scripts/i18n-inventory.mjs --check falla en el head actual (6b4970b). El CI upstream (ci.yml:45) lo ejecutará y pondrá el PR en rojo. Fix: node scripts/i18n-inventory.mjs --out docs/i18n-inventory.md.

🔴 Bloqueante 2 (nuevo): el test del gate falla en un checkout Linux limpio — exec bit

He ejecutado node --test scripts/verify-linux-bundles.test.mjs en un clone fresco y 2 de los 4 casos fallan:

actual: 126, expected: 1   (ERR_ASSERTION)

Causa: los tres verify-*.sh están en git con modo 100644 (no ejecutables). Los casos que invocan los sub-scripts directamente (bash verify-deb.sh) funcionan, pero verify.sh los llama por nombre vía PATH → bash los encuentra y no puede ejecutarlos → exit 126 (permiso denegado), no 1.

Verificado que es solo eso: con chmod +x .testcontainer/*.sh los 4 casos pasan:

✔ verify-deb.sh falla si falta su propio bundle .deb
✔ verify-appimage.sh falla si falta su propio bundle .AppImage
✔ el gate falla con el set vacío, sin bundles
✔ el gate falla si falta el .deb y solo llega el .AppImage

Fix: git update-index --chmod=+x .testcontainer/verify-deb.sh .testcontainer/verify-appimage.sh .testcontainer/verify.sh. El contenedor no cambia (COPY --chmod=755 ya los hace ejecutables). Ojo: el CI upstream no lo detectará (ci.yml corre npm test solo en windows-latest, donde el test se autoexcluye), pero cualquiera que corra npm test en Linux verá rojo.

✅ Lo bueno de los commits nuevos

  • El test cubre los 4 casos del contrato (falta .deb, falta .AppImage, set vacío, solo .AppImage) — exactamente lo que ci(linux): endurecer verify.sh — fallar si falta cualquiera de los dos bundles, no solo si falta todo #175 pedía, ahora protegido contra regresión.
  • ARTIFACTS_DIR como env var con default /artifacts: testeable sin contenedor, sin root y sin dpkg/xvfb (fallan en el find antes de instalar nada). El comportamiento del contenedor no cambia.
  • El skip POSIX (6b4970b) está bien resuelto: process.platform !== 'win32' con skip razonado.
  • El test está registrado en package.json ✅.

⚠️ Nota de proceso

No hay run de CI en el fork para los dos commits nuevos (la última run es 43caa59). Recomiendo lanzar el linux-ci.yml sobre 6b4970b antes del merge.

Veredicto

Sigue bloqueado, por los dos fixes triviales: (1) regenerar el inventario i18n, (2) chmod +x de los tres scripts en git. Con ambos, el PR queda listo — el contenido de fondo ya está revisado y avalado.

sosan and others added 2 commits October 23, 2026 13:18
…v6#106)

Bloqueantes de la re-revisión del PR egdev6#176 (2026-10-23):

1. Los tres verify-*.sh estaban en git con modo 100644. verify.sh los
   invoca por nombre vía PATH; en un checkout limpio sin el exec bit,
   bash los encuentra y falla con 126 (Permission denied) en vez del
   exit 1 esperado → 2 de 4 casos del test del gate en rojo. Modo
   corregido a 100755 (el Dockerfile ya los hace ejecutables con
   COPY --chmod=755; el contenedor no cambia).

2. docs/i18n-inventory.md quedó stale al añadir
   scripts/verify-linux-bundles.test.mjs: `--check` falla en el head
   y ci.yml (check egdev6#155) pondría el PR en rojo. Regenerado con
   `node scripts/i18n-inventory.mjs --out docs/i18n-inventory.md`.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@egdev6

egdev6 commented Oct 23, 2026

Copy link
Copy Markdown
Owner

@sosan re-revisión del commit 5f54162 — los dos bloqueantes resueltos, listo para merge. ✅

🔴→✅ Bloqueante 1: exec bit

  • Los tres verify-*.sh pasan a modo 100755 en git (confirmado con git ls-files -s).
  • El diff solo toca el modo (0 líneas de contenido) — el contenedor no cambia, como decía el plan (COPY --chmod=755 ya los hacía ejecutables).
  • Test del gate en checkout limpio: 4/4 pasan — ya no hay exit 126.

🔴→✅ Bloqueante 2: inventario i18n

✅ Verificación completa (head 5f54162)

Check Resultado
cargo test --locked 256 passed, 0 failed
vitest run --dir src 658 passed
tsc --noEmit Limpio
i18n-inventory --check ✅
Test del gate (verify-linux-bundles.test.mjs) 4/4 ✅

Nota (no bloquea)

Sigue sin haber run de CI en el fork para el head nuevo (última run: 43caa59). El delta es mínimo y está todo verificado en local — no lo exijo.

Veredicto

Listo para merge. Con esto el PR cierra #175, cubre la verificación W3 de #106 en lo automatizable, deja el pendiente de migrate_legacy() documentado en la evidencia (con mi propuesta de smoke en la issue) y declara los límites honestamente (sin llamadas autenticadas, sin escritorio real, solo ubuntu-22.04).

Buen trabajo de iteración: la evidencia pasó de «verde vacío» a un gate real con regresión protegida.

egdev6
egdev6 previously approved these changes Oct 23, 2026
El merge a56caff (a55caff) resolvió el conflicto de
docs/i18n-inventory.md tomando un lado sin regenerar: `--check` seguía
fallando en el head, dejando vivo el bloqueante 1 de la re-revisión.
Regenerado sobre el árbol ya mergeado con
node scripts/i18n-inventory.mjs --out docs/i18n-inventory.md.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@sosan

sosan commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

Ambos bloqueantes resueltos — head actual b22d472 (con el main de 2610926 mergeado y el inventario i18n regenerado sobre ese árbol).

1 · Inventario i18n — el merge de main al traerlo resolvió el conflicto de docs/i18n-inventory.md tomando un lado sin regenerar, así que --check seguía fallando. Regenerado sobre el árbol ya mergeado (node scripts/i18n-inventory.mjs --out docs/i18n-inventory.md); verificado --check en 0 en el head.

2 · Exec bit — git update-index --chmod=+x → 100755 commiteado (mode change 100644 => 100755 en los 3 scripts). Verificado con git archive que el árbol commiteado trae -rwxr-xr-x. El test del gate pasa 4/4 en checkout sin el hack del PATH; el contenedor no cambia (Dockerfile COPY --chmod=755).

Verificación local sobre el head final (npm test completo):

  • node scripts/i18n-inventory.mjs --check → OK
  • scripts/verify-linux-bundles.test.mjs → 4/4 ✓ (los 2 casos que pasan por verify.sh ya devuelven 1, no 126)
  • suite completa: 668 tests, exit 0

CI en marcha: linux-ci.yml con tests sobre b22d472 (recomendación de lanzarlo sobre el head antes del merge). Aviso con el resultado cuando termine; si pasa, el contenido del PR queda listo para merge.

@egdev6
egdev6 merged commit 31facfc into egdev6:main Oct 23, 2026
5 checks passed
@sosan

sosan commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

CI en marcha — linux-ci.yml con with_bundle=tests sobre b22d472 → ✅ success (los pasos de bundle, skipped por el input; los ejecutados: npm test, npm run build, cargo test --locked y el smoke de Secret Service, todos verdes, sin ningún paso fallido).

El head b22d472 queda verificado en Linux real: inventario i18n OK (--check), exec bits en 100755, gate test 4/4 y suite completa 668 tests. Contenido listo para merge cuando lo veas.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants