Skip to content

Windows: full-suite debt of 194 failing tests (measured 2026-10-10), fix order by root cause #692

Open

Description

@cdeust

Summary

Cortex runs about 9000 tests on Linux and only 20 hand-picked test files on Windows. A one-off measurement of the full suite on Windows found 194 failing tests (188 failed, 6 errors) against 0 on the Linux control and 3 on macOS. This issue tracks the Windows debt as a sequence of root causes; the verified product defects and the bash question have their own issues.

Evidence

Measured on 2026-10-10 at main 18bd25b, SQLite backend, Python 3.12, dependencies from uv.lock, the process-leak guard active, no -x. Two identical runs gave the identical set of failing ids.

Failure classes (mutually exclusive, they sum to 194):

  1. 50 tests: the empty_env fixture of tests_py/scripts/test_launcher_platform_coverage.py (line 92) returns Scripts/python without .exe. Once fixed, uv's cross-platform resolution runs on Windows for the first time and may expose more.
  2. 42 tests: bare bash resolves to the WSL launcher (tracked in its own issue).
  3. 16 tests: default text encoding is cp1252; 4 of them are a product defect in auto_recall (tracked in its own issue), 12 are tests that call read_text() without an encoding or compare mojibake.
  4. 13 tests: benchmarks/lib/machine_load_snapshot.py:54 calls the POSIX-only os.getloadavg.
  5. 21 tests assume a POSIX path or branch: /a.py is not absolute on Windows (test_preemptive_context 9), os.getppid patched while _window_claude_pid takes the Windows branch (session_registry.py:210-212, test_session_registry 5; PR fix(session-registry): resolve the window's claude.exe on Windows and probe liveness without os.kill (#665) #678 made its own tests platform aware but not this file), ~/state under USERPROFILE, is_supported() false so dispatch takes the spool branch (test_capture_dispatch), a path regex over tmp_path (test_validate_memory 4).
  6. 4 tests use a POSIX primitive without a platform guard (os.O_NOFOLLOW, multiprocessing.get_context("fork"), select.select on a pipe).
  7. 2 tests: tests_py/_hermetic_hook_env.py:91 sets HOME only, and Path.home() reads USERPROFILE on Windows.
  8. 23 tests, suspected CRLF translation of fixtures written with write_text (test_migrate_decision_blocks 9, test_wiki_rebucket_file_docs 9, test_wiki_bulk_migrate 5); inferred from the code, not run.
  9. 23 tests not isolated (first error lines recorded): installer rerun fixtures 6, file priming aliases 6 (heat 0.5 against 0.6: a symlinked project alias is scoped differently, possibly a product defect in the ADR-1086 scoping), mcp client spawn 2, a mocked Path.resolve 2, a TTL on a coarse Windows clock 1, test_capture_drain 1 (tracked in its own issue), and 5 singles.

Expected

Fix the classes in this order, one pull request each, re-measuring on Windows after each one with the scratch workflow used for the measurement: 1 (50 tests), then 3 (the product defect first), 2, 4, 8, then 5, 6 and 7 together, then class 9 one by one. After the first four steps 73 ids remain, after 5 to 7 about 20. The required Windows job then grows one directory at a time (not straight to the full suite), and the per-system skip counts (Windows 78, macOS 6 more than the control) are pinned in CI so that a new platform skip is a visible decision.

Activity

  1. cdeust commented on Nov 4, 2026

    @cdeust
    OwnerAuthor

    Tranche classe 4 proposée dans #861 (commit d0f0ab9) : os.getloadavg est Unix-only, l'AttributeError sous Windows échappait au except OSError de benchmarks/lib/machine_load_snapshot.py. Le test qui échouait avant (os sans getloadavg) passe maintenant. Sous Linux, tests_py/benchmarks donne 386 passés et 12 ignorés. Rien n'a tourné sous Windows.

    Tranches restantes, dans l'ordre de l'issue : classe 3 (encodage cp1252, 12 tests ; le défaut produit auto_recall a sa propre issue), classe 8 (CRLF, 23 tests), classes 5 à 7 ensemble, puis la classe 9 une par une. Les classes 1 et 2 sont déjà fusionnées : #824 pour la classe 1, #738 pour la classe 2.


    Generated by Claude Code

  2. cdeust commented on Nov 5, 2026

    @cdeust
    OwnerAuthor

    Tranche classe 3 (encodage) proposée dans #888 (commit 6d31872), Refs #692.

    • Les 7 échecs de classe 3 relevés dans le run 38008915302 (UnicodeDecodeError: 'charmap' dans test_pg_recall_plans, test_codex_plugin_contract, test_generate_repo_badges) lisent déjà en encoding="utf-8" sur main.
    • fix(hooks): read the post-commit cooldown as UTF-8 and copy the plugin trees into the mutation sandbox (#692, class 3) #888 fait lire en UTF-8 le fichier de cooldown de post_commit_reindex (écrit en UTF-8 par gated_files.write_json, relu jusqu'ici dans le code page de la locale), et nomme UTF-8 dans les 6 fichiers de test qui ouvraient encore du texte sans encodage (ruff --preview --select PLW1514).
    • Elle répare aussi le bac à sable du gate de mutation : .claude-plugin et plugins n'étaient pas copiés, donc toute PR touchant post_commit_reindex.py échouait avant le premier mutant.
    • Rien n'a tourné sous Windows. test_subprocess_safe.py::test_encoding_parameter_is_honored ('' == 'café' sous Windows) reste inexpliqué.

    Tranches restantes, dans l'ordre de l'issue : re-mesure Windows pour confirmer que la classe 3 est vide, classe 8 (CRLF, 23 tests), classes 5 à 7 ensemble, puis la classe 9 une par une.


    Generated by Claude Code

  3. added 2 commits that reference this issue on Nov 5, 2026
  4. cdeust commented on Nov 5, 2026

    @cdeust
    OwnerAuthor

    Class 8 slice proposed in #896 (commit d18493a), Refs #692.

    • The Windows log of run 38008915302 shows that class 8 is not one CRLF problem but two causes:
      • Separators (14 tests, a product defect): wiki_bulk_migrate.plan and wiki_rebucket_file_docs.plan recorded str(rel), i.e. adr\_general\… on Windows, which the detectors and redirect_to do not expect. They now use as_posix().
      • Newlines (9 tests, a test defect): the test_migrate_decision_blocks fixture went through write_text (\r\n on Windows), while the block id is a hash of the file's bytes.
    • Windows simulated on Linux: on main, the newline simulation reproduces the 9 failure ids and the separator simulation the 14 ids, plus one (test_apply_is_idempotent). On the branch, all pass. Nothing ran on real Windows.

    Remaining slices, in the issue's order: Windows re-measure (classes 3, 4 and 8), classes 5 to 7 together, then class 9 one by one. The same str(relative_to(...)) pattern exists outside class 8; the list is in the PR, not diagnosed.


    Generated by Claude Code

  5. 16 remaining items

  6. added a commit that references this issue on Nov 9, 2026
  7. cdeust commented on Nov 9, 2026

    @cdeust
    OwnerAuthor

    J'ai ouvert #952 pour la tranche classe 9 « chemins du cache reranker » (2 tests, commits a8e47f2 puis b68a26b pour le CHANGELOG, Refs #692).

    • Cause (log du run 38008915302, job 114084097957) : test_falls_back_to_home_cache comparait \fake\home\.cache\flashrank à un littéral POSIX ; test_cell_model_cache_and_state_restore_without_changing_defaults comparait le chemin joint par / de core/reranker_model._model_path() (core n'importe pas pathlib, ADR-0244) à un str(Path) natif. Le diff ne porte que sur les séparateurs.
    • Correctif : les deux tests comparent des Path ; aucun code de production touché.
    • Tests : 46 passed sous Linux sur les deux fichiers.
    • Non vérifié : aucun run Windows.

    Tranches restantes de la classe 9, à confirmer par une nouvelle mesure Windows : installer rerun (6), file priming aliases (4 + 2), Path.resolve mocké (2), et les tests isolés listés dans le corps de l'issue.


    Generated by Claude Code

  8. cdeust commented on Nov 9, 2026

    @cdeust
    OwnerAuthor

    J'ai ouvert #954 pour la tranche classe 9 « Path.resolve mocké » (2 tests de test_issue_147_write_class_and_grading, commit 2e27ef4, Refs #692).

    • Cause (log du run 38008915302, job 114084097957) : un FileNotFoundError levé depuis <frozen ntpath>:719, dans le Path(db).resolve() du store. Les tests remplaçaient os.getcwd pour tout l'interpréteur, et sous Windows ntpath.realpath lit le cwd même pour un chemin absolu. Windows refuse aussi de supprimer le cwd d'un processus. C'est un défaut du test, pas du produit.
    • Correctif : le défaut n'est injecté que dans le repli os.getcwd() de remember_helpers._grade_content_best_effort. Aucun code de production n'est touché.
    • Tests : 13 passed sur le fichier et 1937 passed sur tests_py/handlers (Linux). En simulant ntpath.realpath, l'ancien test donne 2 failed et le nouveau passe. Le mutant « getcwd ne lève pas » est tué.
    • Non vérifié : aucun run Windows.

    Tranches restantes de la classe 9, à confirmer par une nouvelle mesure Windows : installer rerun (6), file priming aliases (4 + 2, défaut produit possible dans le périmètre ADR-1086), et les tests isolés listés dans le corps de l'issue.


    Generated by Claude Code

  9. cdeust commented on Nov 9, 2026

    @cdeust
    OwnerAuthor

    J'ai ouvert #956 pour la tranche classe 9 « installer rerun » (6 erreurs de setup dans test_installer_rerun_keeps_entries, plus test_fake_uv_pip_filename, commit ed03acf, Refs #692).

    • Cause (log du run 38008915302, job 114084097957) : le stand-in uv des tests était un script sans extension. Sous Windows, shutil.which ne retient que les noms de PATHEXT, donc le lanceur prenait le vrai uv du runner et installait le vrai uv.lock : le répertoire deps contient FlashRank-0… et _yaml, sans probeyaml ni probens. Pour la même raison, CreateProcess refusait de lancer le script directement (WinError 193).
    • Correctif : sous Windows, le stand-in devient un uv.cmd qui lance le même script avec sys.executable. POSIX ne change pas, et aucun code de production n'est touché.
    • Tests : tests_py/scripts sous Linux donne 1922 passed. Les 6 échecs de test_ci_mutation_scope existent aussi sans le diff (mutmut absent de l'environnement). Le nouveau test test_fake_uv_resolution.py vérifie la résolution par PATH et le contenu du wrapper.
    • Non vérifié : aucun run Windows.

    Tranches restantes de la classe 9, à confirmer par une nouvelle mesure Windows : file priming aliases (4 + 2, défaut produit possible dans le périmètre ADR-1086), test_installer_upgrade_single_version (bash résolu vers WSL, une autre cause) et les tests isolés listés dans le corps de l'issue.


    Generated by Claude Code

  10. cdeust commented on Nov 10, 2026

    @cdeust
    OwnerAuthor

    J'ai ouvert #959 pour la tranche classe 9 « file priming aliases » (6 tests : test_file_priming_aliases ×4 et test_file_memory_priming::test_alias_project_read_preserves_stored_scope ×2 ; commit 3ac8699, Refs #692).

    • Cause (log du run 38008915302, job 114084097957, assert 0.5 == 0.6) : project_ancestors normalise la racine en /, et memory_matches_project normalise aussi la colonne. Les prédicats SQL, eux, comparaient la liste à directory_context brut. Sous Windows, une mémoire stockée sous C:\…\alias ne correspondait donc jamais en SQL. Ce n'est pas un défaut de portée ADR-1086 sur les alias, mais une asymétrie de normalisation. Elle touche aussi les requêtes de session-start, d'auto-recall et d'agent-briefing.
    • Correctif : une seule expression RTRIM(REPLACE(directory_context, '\', '/'), '/') (pg_scope_clause.normalized_directory_column), utilisée par les deux clauses et par les 6 requêtes PG brutes des hooks.
    • Tests : le nouveau test_scope_clause_separators.py donne 6 failed sur main et 9 passed avec le correctif, sous Linux. tests_py/infrastructure, hooks et shared ont les mêmes 6 échecs environnementaux que main (torch absent).
    • Non vérifié : aucun run Windows ; les requêtes PG des hooks ne sont pas exercées localement (pas de pgvector ici).

    Tranches restantes de la classe 9, à confirmer par une nouvelle mesure Windows : test_installer_upgrade_single_version (bash résolu vers WSL) et les tests isolés listés dans le corps de l'issue.


    Generated by Claude Code

  11. added
    autopilot:holdCI autopilot stood down: a human decision is required
    on Nov 10, 2026
  12. cdeust commented on Nov 10, 2026

    @cdeust
    OwnerAuthor

    CI autopilot : #692 mise en autopilot:hold — les tranches restantes ne sont plus identifiables sans nouvelle mesure Windows.

    État de la classe 9 relevé sur main (origin/main au 2026-11-10 07:3x UTC) contre le log du run 38008915302 (job 114084097957, 192 lignes FAILED/ERROR) :

    Ce qui bloque : aucun workflow workflow_dispatch ne lance la suite sous Windows (aucun check Windows sur le dernier commit de main, aucun workflow Windows déclenchable dans .github/workflows), et le token de la routine ne peut pas ajouter le workflow de mesure (permission workflows absente).

    À décider par un humain : relancer le workflow scratch de mesure Windows sur le main actuel (ou ajouter un job Windows déclenchable à la main). Avec le nouveau log, la routine reprendra les singles restants un par un ; un commentaire de @cdeust ou l'édition du corps avec la liste mesurée lève ce hold.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Windowsautopilot:holdCI autopilot stood down: a human decision is requiredbugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions