🐛 Bug Description
bash scripts/verify.sh fails at step 5 (pytest --cov) on a fresh, unmodified Windows checkout, with 18 test failures that trace to exactly two root causes, both Windows-specific test-teardown/file-locking semantics rather than production code defects. Steps 1-4 (ruff format --check, ruff check, basedpyright, lint-imports) all pass cleanly, so the gap is isolated to the pytest step and appears to be a Windows CI coverage gap rather than a setup or code issue.
🔄 Steps to Reproduce
- On Windows, clone the repository and follow the contributor setup in
contexts/dev/setup.md / CONTRIBUTING.md: uv venv, uv pip install -e ".[dev]", install the pre-commit/commit-msg/pre-push hooks.
- Run
bash scripts/verify.sh from a Git Bash shell (or otherwise run pytest directly) without making any code changes.
- Observe 18 failures in
tests/library/test_local.py, tests/library/test_paper.py, tests/library/test_structure.py, and tests/preprocess/format/test_pdf.py.
✅ Expected Behavior
A fresh, unmodified checkout should pass scripts/verify.sh on Windows the same way it does on the CI-covered platform, since the required .github/workflows/ci.yml workflow is meant to reflect the deterministic gate contributors run locally.
❌ Actual Behavior
Two distinct root causes account for all 18 failures:
- SQLite tempdir cleanup race on open connections (17 of 18 failures, in
tests/library/test_local.py, test_paper.py, and test_structure.py): each test opens a sqlite-backed store inside a TemporaryDirectory, then tearDown calls self._temporary_directory.cleanup() while a sqlite connection into that directory is still open. POSIX permits unlinking a file that a process still has open; Windows does not, so cleanup raises PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: '...\library.db'.
- PDF golden test file-locking mismatch with the native parser (
tests/preprocess/format/test_pdf.py::PdfToMarkdownTests::test_golden_preserves_pages_blocks_coordinates_and_artifacts): the test writes the sample PDF to a temp file that Python still holds open, then hands that path to the native liteparse parser via LiteParse.screenshot(). Path.exists() on the same path succeeds, but the native call fails with RuntimeError: PDF error: file not found — again a Windows exclusive-file-locking mismatch, not a missing file.
📋 Environment
- QuantMind Version: 0.2.0 (fresh clone of
master, no local modifications)
- Python Version: 3.12.10
- Operating System: Windows 11 Home (10.0.26200)
- Installation Method:
uv venv + uv pip install -e ".[dev]", per contexts/dev/setup.md
📝 Additional Context
This was found while completing contributor setup, before writing any feature code, so it reflects a fresh-checkout gap rather than a regression introduced by local changes. Filing so it's tracked; treating it as a known local caveat for now rather than blocking on it.
Error Logs
FAILED tests/library/test_local.py::LocalKnowledgeLibraryTests::test_corrupt_canonical_tree_node_fails_rehydration
FAILED tests/library/test_local.py::LocalKnowledgeLibraryTests::test_corrupt_vector_and_query_dimension_mismatch_fail_clearly
FAILED tests/library/test_local.py::LocalKnowledgeLibraryTests::test_delete_removes_canonical_root_and_nodes_transactionally
FAILED tests/library/test_local.py::LocalKnowledgeLibraryTests::test_orphan_and_missing_derived_data_are_reported_as_stale
FAILED tests/library/test_local.py::LocalKnowledgeLibraryTests::test_stale_canonical_get_fails_but_delete_can_recover
FAILED tests/library/test_local.py::LocalKnowledgeLibraryTests::test_tree_root_and_non_root_nodes_use_exact_grain_and_filters
FAILED tests/library/test_paper.py::PaperLibraryTests::test_multiple_chunk_and_summary_versions_coexist
FAILED tests/library/test_paper.py::PaperLibraryTests::test_put_persists_explicit_source_artifact_and_projection_layers
FAILED tests/library/test_paper.py::PaperLibraryTests::test_rehydrate_rejects_asset_metadata_drift
FAILED tests/library/test_paper.py::PaperLibraryTests::test_rehydrate_rejects_missing_summary_lineage
FAILED tests/library/test_paper.py::PaperLibraryTests::test_required_projection_failure_is_atomic
FAILED tests/library/test_paper.py::PaperLibraryTests::test_search_rejects_projection_text_drift
FAILED tests/library/test_structure.py::StructureTreeLibraryTests::test_put_paper_structure_tree_rejects_a_tree_for_another_source
FAILED tests/library/test_structure.py::StructureTreeLibraryTests::test_put_rejects_a_tampered_tree_and_writes_nothing
FAILED tests/library/test_structure.py::StructureTreeLibraryTests::test_put_reopen_get_and_idempotency_without_source_or_chunks
FAILED tests/library/test_structure.py::StructureTreeLibraryTests::test_rehydrate_fails_closed_on_member_metadata_drift
FAILED tests/library/test_structure.py::SchemaMigrationTests::test_version_three_migrates_to_source_free_artifacts
FAILED tests/preprocess/format/test_pdf.py::PdfToMarkdownTests::test_golden_preserves_pages_blocks_coordinates_and_artifacts
E PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\Users\mukes\AppData\Local\Temp\tmpis34r20l\library.db'
E RuntimeError: PDF error: file not found
Configuration
# No custom configuration; default fresh-checkout setup per contexts/dev/setup.md
🛠️ Possible Solution
- For the sqlite cases: close/dispose the store's sqlite connection explicitly in each test's
tearDown (or use a context manager) before TemporaryDirectory.cleanup() runs, so the file handle is released prior to deletion.
- For the PDF case: close the temp file handle (or use
delete=False plus an explicit close()) before passing its path to LiteParse.screenshot(), so the native parser can open it without contention.
- Longer term: add a Windows job to CI (or a Windows-specific note in
docs/README.md) so this class of POSIX-only-semantics assumption is caught before it reaches contributors.
Checklist
🐛 Bug Description
bash scripts/verify.shfails at step 5 (pytest --cov) on a fresh, unmodified Windows checkout, with 18 test failures that trace to exactly two root causes, both Windows-specific test-teardown/file-locking semantics rather than production code defects. Steps 1-4 (ruff format --check,ruff check,basedpyright,lint-imports) all pass cleanly, so the gap is isolated to the pytest step and appears to be a Windows CI coverage gap rather than a setup or code issue.🔄 Steps to Reproduce
contexts/dev/setup.md/CONTRIBUTING.md:uv venv,uv pip install -e ".[dev]", install the pre-commit/commit-msg/pre-push hooks.bash scripts/verify.shfrom a Git Bash shell (or otherwise runpytestdirectly) without making any code changes.tests/library/test_local.py,tests/library/test_paper.py,tests/library/test_structure.py, andtests/preprocess/format/test_pdf.py.✅ Expected Behavior
A fresh, unmodified checkout should pass
scripts/verify.shon Windows the same way it does on the CI-covered platform, since the required.github/workflows/ci.ymlworkflow is meant to reflect the deterministic gate contributors run locally.❌ Actual Behavior
Two distinct root causes account for all 18 failures:
tests/library/test_local.py,test_paper.py, andtest_structure.py): each test opens a sqlite-backed store inside aTemporaryDirectory, thentearDowncallsself._temporary_directory.cleanup()while a sqlite connection into that directory is still open. POSIX permits unlinking a file that a process still has open; Windows does not, so cleanup raisesPermissionError: [WinError 32] The process cannot access the file because it is being used by another process: '...\library.db'.tests/preprocess/format/test_pdf.py::PdfToMarkdownTests::test_golden_preserves_pages_blocks_coordinates_and_artifacts): the test writes the sample PDF to a temp file that Python still holds open, then hands that path to the nativeliteparseparser viaLiteParse.screenshot().Path.exists()on the same path succeeds, but the native call fails withRuntimeError: PDF error: file not found— again a Windows exclusive-file-locking mismatch, not a missing file.📋 Environment
master, no local modifications)uv venv+uv pip install -e ".[dev]", percontexts/dev/setup.md📝 Additional Context
This was found while completing contributor setup, before writing any feature code, so it reflects a fresh-checkout gap rather than a regression introduced by local changes. Filing so it's tracked; treating it as a known local caveat for now rather than blocking on it.
Error Logs
Configuration
# No custom configuration; default fresh-checkout setup per contexts/dev/setup.md🛠️ Possible Solution
tearDown(or use a context manager) beforeTemporaryDirectory.cleanup()runs, so the file handle is released prior to deletion.delete=Falseplus an explicitclose()) before passing its path toLiteParse.screenshot(), so the native parser can open it without contention.docs/README.md) so this class of POSIX-only-semantics assumption is caught before it reaches contributors.Checklist