diff --git a/docs/engineering/acceptance-edge-notes.md b/docs/engineering/acceptance-edge-notes.md new file mode 100644 index 0000000..4b8f313 --- /dev/null +++ b/docs/engineering/acceptance-edge-notes.md @@ -0,0 +1,109 @@ +# J10 snapshot acceptance evidence — historical + +J08 final integration note: the outcomes below describe the original J10 snapshot. +The copied fixture now configures J06's explicit `DurableBindingAuthority`; current +node/fixture/module hashes in the supplement describe final staged owner sources. +The supplement's `inputs` and `all_copied_dependencies` still preserve historical +J10 provenance, not the final composite roster. J08's final full run collected +861 tests: 860 passed, one unchanged gate-schema test failed, zero skipped. +All 26 supplement nodes and all 93 mapped pytest nodes passed every phase. +See [qualification-review.md](qualification-review.md) for current blockers and ownership. +No source specification status has been converted into an execution result. + +This is internal integration evidence for the supervisor's J08 qualification +assembly, not a product feature, separate PR, or final qualification result. +All statuses in `acceptance-supplement.json` intentionally remain `not_run`. +The outcomes below describe the attested J10 snapshot only. + +Only three files are J10 contributions: `tests/test_acceptance_edges.py`, +`docs/engineering/acceptance-supplement.json`, and this note. Product modules and +existing tests were read-only. No copies were refreshed, dependencies installed, +services/providers contacted, commits created, or peer files edited. + +| Stage | Evidence | Final exact-node count | +| --- | --- | ---: | +| J02 / J-01 | New ENOSPC at actual legacy publication, five stores, positive recovery | 5 | +| J02 / J-02 | New native SQLITE_FULL, complete rollback, same-key positive recovery | 1 | +| J04 / J-02 | Reused real process exits before/after commit | 5 | +| J16 / J-06 | Reused actual parser → binding → move/disappearance → open-work fallback | 2 | +| J22 / J-07 | Reused end/start tool cuts and complete-range positive for three terminals | 3 | +| J25 / J-02 | Three reused corrupt sources, three reused import failures, two new process exits | 8 | +| J29 / J-04 | New full parser/binding/lesson/preflight/projection chain, two delivery schedules | 2 | +| J31 / J-04 | Same two compound nodes, primary forget with delayed projections and open task | 2 | + +The table has 28 stage-node associations but **26 unique pytest nodes: 10 new, +16 reused**. Every reused definition was inspected and executed. Exact collected +parameter IDs, including escaped newlines in the existing J16 parameters, are +stored in `collection.exact_node_ids`. No static mapping is counted as a run. + +Final verification: **26 passed, 3 expected optional-provider warnings, 3.56 s**. +Runtime: Python 3.12.12, SQLite 3.51.2, Node v25.2.1; the real adapter uses its +provisioned tree-sitter 0.21.1 / tree-sitter-python 0.21.0 dependencies. +The final own-test run separately passed **10/10**; the reused-node run passed +**16/16**. Ruff check and format check passed. Python compilation and Python 3.10 +syntax parsing passed. JSON/source validation checked all eight stages, every +mapped source/module digest, and all 45 unchanged snapshot dependencies. +`git diff --check` passed. Neither mypy nor pyright is installed; no static type +check is claimed and no installation was attempted. + +Final execution command from J10 (the variables expand to the prescribed paths): + +```sh +ROOT=/path/to/orchestration-root +PYTHONPATH="$PWD" \ +JITMIND_TEST_GRAFT_ADAPTER="$ROOT/worktrees/J04/adapters/graft" \ +"$ROOT/venv/bin/python" - <<'PY' +import json, os, subprocess, sys +from pathlib import Path +spec = json.loads(Path('docs/engineering/acceptance-supplement.json').read_text()) +result = subprocess.run( + [sys.executable, '-m', 'pytest', '-q', *spec['collection']['exact_node_ids']], + env=os.environ, timeout=120, +) +raise SystemExit(result.returncode) +PY +``` + +Collection used the same isolated Python with `-m pytest --collect-only -q` and +the exact selectors in `collection.selectors`: **26 collected**. Lint commands +were `"$ROOT/venv/bin/python" -m ruff check tests/test_acceptance_edges.py` and +`"$ROOT/venv/bin/python" -m ruff format --check tests/test_acceptance_edges.py`. +New spawned children have 20-second joins followed by bounded terminate/kill +and reap; the parser uses its actual bounded process wrapper. Only sanitized +ENOSPC is injected; SQLite capacity and process termination are real. + +The supplement's `all_copied_dependencies` is the complete inventory of **45 +pre-existing copied files**, with owner, initial/current SHA-256, and explicit +non-contribution markers. Every local digest still matches +`ROOT/reports/J10-dependency-snapshot.json`. The J04 adapter is read directly +from its owner and is not copied. Its source/lockfile digests are also recorded. +Tests use `JITMIND_TEST_GRAFT_ADAPTER`, falling back to repository +`adapters/graft`; no test depends on a home path or review-probe script. +Inventory counts: J02 8, J03 7, J04 8, J05 6, J06 7, J07 7, and J09 2. + +At source review, nine owner files had advanced beyond this local snapshot: +J02 `storage/{migration,sqlite}.py` and `test_durable_remediation.py`; +J05 `code_memory/{anchors,binding_storage}.py` and `test_code_anchors.py`; +J06 `code_memory/{lesson_models,preflight}.py` and `test_open_work_bindings.py`. +Their observed owner digests are recorded without copying in-progress changes. +Final assembly must retest its actual versions. In particular, newer J06 work +introduces a binding-authority adapter; its final configuration/schema compatibility +is outside this snapshot run and must be composed explicitly, not bypassed. + +The compound fixture first proves that the memory-only sentinel appears in the +authoritative fact and delivered lesson. It never appears in repository source. +One schedule starts with an existing primary projection; the other keeps the +original add pending until after the delete event is delivered. Both verify +metadata replay, lesson version ordering, source freshness/generation, primary +forget before deletion projection, empty current derived memory outputs, +content-free binding/work/delivery histories, and an obligation still open at +revision 1. **Raw authorized code remains readable after memory forget.** J04 +indexes source bytes, not fact bodies; deleting a memory does not delete the +user's files. Retained primary administrative history and backups can still +contain bodies; no physical erasure or all-history purge is claimed. + +No product defect blocked these 26 snapshot cases. Remaining limits are final +owner-version assembly, cross-platform/power-loss coverage, and the per-node +limits recorded in the supplement. The frozen temporal oracle was read but this +supplement adds no temporal acceptance claims. Reusing 16 exact existing nodes +avoids copying tests or introducing product abstractions. diff --git a/docs/engineering/acceptance-map.json b/docs/engineering/acceptance-map.json new file mode 100644 index 0000000..2df2a9f --- /dev/null +++ b/docs/engineering/acceptance-map.json @@ -0,0 +1,3500 @@ +{ + "schema": "jitmind.acceptance-map/v1", + "target": "JITMIND", + "source_handoff_sha256": "f1b3107ff03f21fb8ccc8c8b90301e4953480e5639bd10a059ec278ecb8cdd51", + "notice": "Derived scenario map, not a copy of full handoff fixtures or execution results. Original case fixtures reviewed by supervisor. All scenarios require actual mapped test evidence. Later-stage repeats are required.", + "cases": [ + { + "case_id": "J01", + "ticket": "J-01", + "scenario": "Cleanup races with append", + "required_assertion": "A committed append survives cleanup; no stale-snapshot overwrite", + "required_in_tickets": [ + "J-01" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-01", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_legacy_persistence.py::test_cleanup_append_schedule[cleanup]", + "kind": "real_storage", + "source": { + "path": "tests/test_legacy_persistence.py", + "sha256": "1516040fc50767e8ca317a7da29e57e1386ac9187657825d3108a136ce079a48" + }, + "fixtures": [ + { + "path": "tests/test_legacy_persistence.py", + "sha256": "1516040fc50767e8ca317a7da29e57e1386ac9187657825d3108a136ce079a48" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_legacy_persistence.py::test_cleanup_append_schedule[append]", + "kind": "real_storage", + "source": { + "path": "tests/test_legacy_persistence.py", + "sha256": "1516040fc50767e8ca317a7da29e57e1386ac9187657825d3108a136ce079a48" + }, + "fixtures": [ + { + "path": "tests/test_legacy_persistence.py", + "sha256": "1516040fc50767e8ca317a7da29e57e1386ac9187657825d3108a136ce079a48" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J02", + "ticket": "J-01", + "scenario": "Disk-full write failure", + "required_assertion": "Durability failure reaches caller; no committed acknowledgement", + "required_in_tickets": [ + "J-01", + "J-02" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-01", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_legacy_persistence.py::test_advanced_save_failure_reaches_caller", + "kind": "injected_storage_fault", + "source": { + "path": "tests/test_legacy_persistence.py", + "sha256": "1516040fc50767e8ca317a7da29e57e1386ac9187657825d3108a136ce079a48" + }, + "fixtures": [ + { + "path": "tests/test_legacy_persistence.py", + "sha256": "1516040fc50767e8ca317a7da29e57e1386ac9187657825d3108a136ce079a48" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[advanced]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Five real legacy store classes in temporary directories; successful initial append, ENOSPC from os.replace after temporary bytes are fsynced, then successful append after fault removal.", + "modules_composed": [ + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/memory.py", + "sha256": "68e5803e6523b41f09fb710656ddfb9c0f2a61bb9ba9335a8fa684173e59a7e9" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + }, + { + "path": "jitmind/schemas/ttl_memory.py", + "sha256": "ff48ac01b5a5a1bacd41d671f48e7cee07a703dcf5b3017eb8b8ca14405e29ca" + }, + { + "path": "jitmind/schemas/ttl_page.py", + "sha256": "2889b4ec309cda92c8eeda1ec624f8a074ae00d791e9d5e7fd11a6e16d9816ca" + }, + { + "path": "jitmind/utils/atomic_io.py", + "sha256": "aa58bbae5ca9f686ad020f7ebe74a27c082400964aebad3614fe46cd4466202d" + }, + { + "path": "jitmind/utils/file_lock.py", + "sha256": "4498fbe42d1a9b8f7224f17d684a8a35fb15dbb815b49de83d1246be5ed6e398" + } + ], + "assertions": [ + "Typed PersistenceError reaches public add caller with ENOSPC cause and outcome_uncertain=False.", + "Previous authoritative bytes unchanged; same-instance and fresh reader contain only prior content; temporary file removed.", + "Positive writes before and after fault prove usable fixture." + ], + "evidence_limits": [ + "Injects only errno.ENOSPC at the actual pre-replacement boundary; does not fill disk or assert post-replacement failure semantics.", + "Covers the five public store add surfaces, not every MemoryAgent/provider path or every filesystem syscall." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[memory]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Five real legacy store classes in temporary directories; successful initial append, ENOSPC from os.replace after temporary bytes are fsynced, then successful append after fault removal.", + "modules_composed": [ + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/memory.py", + "sha256": "68e5803e6523b41f09fb710656ddfb9c0f2a61bb9ba9335a8fa684173e59a7e9" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + }, + { + "path": "jitmind/schemas/ttl_memory.py", + "sha256": "ff48ac01b5a5a1bacd41d671f48e7cee07a703dcf5b3017eb8b8ca14405e29ca" + }, + { + "path": "jitmind/schemas/ttl_page.py", + "sha256": "2889b4ec309cda92c8eeda1ec624f8a074ae00d791e9d5e7fd11a6e16d9816ca" + }, + { + "path": "jitmind/utils/atomic_io.py", + "sha256": "aa58bbae5ca9f686ad020f7ebe74a27c082400964aebad3614fe46cd4466202d" + }, + { + "path": "jitmind/utils/file_lock.py", + "sha256": "4498fbe42d1a9b8f7224f17d684a8a35fb15dbb815b49de83d1246be5ed6e398" + } + ], + "assertions": [ + "Typed PersistenceError reaches public add caller with ENOSPC cause and outcome_uncertain=False.", + "Previous authoritative bytes unchanged; same-instance and fresh reader contain only prior content; temporary file removed.", + "Positive writes before and after fault prove usable fixture." + ], + "evidence_limits": [ + "Injects only errno.ENOSPC at the actual pre-replacement boundary; does not fill disk or assert post-replacement failure semantics.", + "Covers the five public store add surfaces, not every MemoryAgent/provider path or every filesystem syscall." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[ttl_memory]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Five real legacy store classes in temporary directories; successful initial append, ENOSPC from os.replace after temporary bytes are fsynced, then successful append after fault removal.", + "modules_composed": [ + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/memory.py", + "sha256": "68e5803e6523b41f09fb710656ddfb9c0f2a61bb9ba9335a8fa684173e59a7e9" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + }, + { + "path": "jitmind/schemas/ttl_memory.py", + "sha256": "ff48ac01b5a5a1bacd41d671f48e7cee07a703dcf5b3017eb8b8ca14405e29ca" + }, + { + "path": "jitmind/schemas/ttl_page.py", + "sha256": "2889b4ec309cda92c8eeda1ec624f8a074ae00d791e9d5e7fd11a6e16d9816ca" + }, + { + "path": "jitmind/utils/atomic_io.py", + "sha256": "aa58bbae5ca9f686ad020f7ebe74a27c082400964aebad3614fe46cd4466202d" + }, + { + "path": "jitmind/utils/file_lock.py", + "sha256": "4498fbe42d1a9b8f7224f17d684a8a35fb15dbb815b49de83d1246be5ed6e398" + } + ], + "assertions": [ + "Typed PersistenceError reaches public add caller with ENOSPC cause and outcome_uncertain=False.", + "Previous authoritative bytes unchanged; same-instance and fresh reader contain only prior content; temporary file removed.", + "Positive writes before and after fault prove usable fixture." + ], + "evidence_limits": [ + "Injects only errno.ENOSPC at the actual pre-replacement boundary; does not fill disk or assert post-replacement failure semantics.", + "Covers the five public store add surfaces, not every MemoryAgent/provider path or every filesystem syscall." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[pages]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Five real legacy store classes in temporary directories; successful initial append, ENOSPC from os.replace after temporary bytes are fsynced, then successful append after fault removal.", + "modules_composed": [ + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/memory.py", + "sha256": "68e5803e6523b41f09fb710656ddfb9c0f2a61bb9ba9335a8fa684173e59a7e9" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + }, + { + "path": "jitmind/schemas/ttl_memory.py", + "sha256": "ff48ac01b5a5a1bacd41d671f48e7cee07a703dcf5b3017eb8b8ca14405e29ca" + }, + { + "path": "jitmind/schemas/ttl_page.py", + "sha256": "2889b4ec309cda92c8eeda1ec624f8a074ae00d791e9d5e7fd11a6e16d9816ca" + }, + { + "path": "jitmind/utils/atomic_io.py", + "sha256": "aa58bbae5ca9f686ad020f7ebe74a27c082400964aebad3614fe46cd4466202d" + }, + { + "path": "jitmind/utils/file_lock.py", + "sha256": "4498fbe42d1a9b8f7224f17d684a8a35fb15dbb815b49de83d1246be5ed6e398" + } + ], + "assertions": [ + "Typed PersistenceError reaches public add caller with ENOSPC cause and outcome_uncertain=False.", + "Previous authoritative bytes unchanged; same-instance and fresh reader contain only prior content; temporary file removed.", + "Positive writes before and after fault prove usable fixture." + ], + "evidence_limits": [ + "Injects only errno.ENOSPC at the actual pre-replacement boundary; does not fill disk or assert post-replacement failure semantics.", + "Covers the five public store add surfaces, not every MemoryAgent/provider path or every filesystem syscall." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[ttl_pages]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Five real legacy store classes in temporary directories; successful initial append, ENOSPC from os.replace after temporary bytes are fsynced, then successful append after fault removal.", + "modules_composed": [ + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/memory.py", + "sha256": "68e5803e6523b41f09fb710656ddfb9c0f2a61bb9ba9335a8fa684173e59a7e9" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + }, + { + "path": "jitmind/schemas/ttl_memory.py", + "sha256": "ff48ac01b5a5a1bacd41d671f48e7cee07a703dcf5b3017eb8b8ca14405e29ca" + }, + { + "path": "jitmind/schemas/ttl_page.py", + "sha256": "2889b4ec309cda92c8eeda1ec624f8a074ae00d791e9d5e7fd11a6e16d9816ca" + }, + { + "path": "jitmind/utils/atomic_io.py", + "sha256": "aa58bbae5ca9f686ad020f7ebe74a27c082400964aebad3614fe46cd4466202d" + }, + { + "path": "jitmind/utils/file_lock.py", + "sha256": "4498fbe42d1a9b8f7224f17d684a8a35fb15dbb815b49de83d1246be5ed6e398" + } + ], + "assertions": [ + "Typed PersistenceError reaches public add caller with ENOSPC cause and outcome_uncertain=False.", + "Previous authoritative bytes unchanged; same-instance and fresh reader contain only prior content; temporary file removed.", + "Positive writes before and after fault prove usable fixture." + ], + "evidence_limits": [ + "Injects only errno.ENOSPC at the actual pre-replacement boundary; does not fill disk or assert post-replacement failure semantics.", + "Covers the five public store add surfaces, not every MemoryAgent/provider path or every filesystem syscall." + ] + }, + { + "node_id": "tests/test_atomic_io_regressions.py::test_atomic_failure_stages_clean_temp_and_descriptors[mkdir]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + }, + "fixtures": [ + { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + } + ], + "fixture_description": "J01 atomic durability/corruption invariants, using J07-owned v2 checkpoint path adaptation. Generic fault coverage supplements, never replaces, the five explicit J10 ENOSPC cases." + }, + { + "node_id": "tests/test_atomic_io_regressions.py::test_atomic_failure_stages_clean_temp_and_descriptors[mkstemp]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + }, + "fixtures": [ + { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + } + ], + "fixture_description": "J01 atomic durability/corruption invariants, using J07-owned v2 checkpoint path adaptation. Generic fault coverage supplements, never replaces, the five explicit J10 ENOSPC cases." + }, + { + "node_id": "tests/test_atomic_io_regressions.py::test_atomic_failure_stages_clean_temp_and_descriptors[fdopen]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + }, + "fixtures": [ + { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + } + ], + "fixture_description": "J01 atomic durability/corruption invariants, using J07-owned v2 checkpoint path adaptation. Generic fault coverage supplements, never replaces, the five explicit J10 ENOSPC cases." + }, + { + "node_id": "tests/test_atomic_io_regressions.py::test_atomic_failure_stages_clean_temp_and_descriptors[write]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + }, + "fixtures": [ + { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + } + ], + "fixture_description": "J01 atomic durability/corruption invariants, using J07-owned v2 checkpoint path adaptation. Generic fault coverage supplements, never replaces, the five explicit J10 ENOSPC cases." + }, + { + "node_id": "tests/test_atomic_io_regressions.py::test_atomic_failure_stages_clean_temp_and_descriptors[flush]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + }, + "fixtures": [ + { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + } + ], + "fixture_description": "J01 atomic durability/corruption invariants, using J07-owned v2 checkpoint path adaptation. Generic fault coverage supplements, never replaces, the five explicit J10 ENOSPC cases." + }, + { + "node_id": "tests/test_atomic_io_regressions.py::test_atomic_failure_stages_clean_temp_and_descriptors[fsync]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + }, + "fixtures": [ + { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + } + ], + "fixture_description": "J01 atomic durability/corruption invariants, using J07-owned v2 checkpoint path adaptation. Generic fault coverage supplements, never replaces, the five explicit J10 ENOSPC cases." + }, + { + "node_id": "tests/test_atomic_io_regressions.py::test_atomic_failure_stages_clean_temp_and_descriptors[replace]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + }, + "fixtures": [ + { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + } + ], + "fixture_description": "J01 atomic durability/corruption invariants, using J07-owned v2 checkpoint path adaptation. Generic fault coverage supplements, never replaces, the five explicit J10 ENOSPC cases." + }, + { + "node_id": "tests/test_atomic_io_regressions.py::test_atomic_failure_stages_clean_temp_and_descriptors[directory_open]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + }, + "fixtures": [ + { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + } + ], + "fixture_description": "J01 atomic durability/corruption invariants, using J07-owned v2 checkpoint path adaptation. Generic fault coverage supplements, never replaces, the five explicit J10 ENOSPC cases." + }, + { + "node_id": "tests/test_atomic_io_regressions.py::test_atomic_failure_stages_clean_temp_and_descriptors[directory_fsync]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + }, + "fixtures": [ + { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + } + ], + "fixture_description": "J01 atomic durability/corruption invariants, using J07-owned v2 checkpoint path adaptation. Generic fault coverage supplements, never replaces, the five explicit J10 ENOSPC cases." + }, + { + "node_id": "tests/test_atomic_io_regressions.py::test_serialization_failure_is_precommit", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + }, + "fixtures": [ + { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + } + ], + "fixture_description": "J01 atomic durability/corruption invariants, using J07-owned v2 checkpoint path adaptation. Generic fault coverage supplements, never replaces, the five explicit J10 ENOSPC cases." + }, + { + "node_id": "tests/test_atomic_io_regressions.py::test_other_legacy_stores_errors_and_corruption[False-profile]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + }, + "fixtures": [ + { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + } + ], + "fixture_description": "J01 atomic durability/corruption invariants, using J07-owned v2 checkpoint path adaptation. Generic fault coverage supplements, never replaces, the five explicit J10 ENOSPC cases." + }, + { + "node_id": "tests/test_atomic_io_regressions.py::test_other_legacy_stores_errors_and_corruption[False-checkpoint]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + }, + "fixtures": [ + { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + } + ], + "fixture_description": "J01 atomic durability/corruption invariants, using J07-owned v2 checkpoint path adaptation. Generic fault coverage supplements, never replaces, the five explicit J10 ENOSPC cases." + }, + { + "node_id": "tests/test_atomic_io_regressions.py::test_other_legacy_stores_errors_and_corruption[True-profile]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + }, + "fixtures": [ + { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + } + ], + "fixture_description": "J01 atomic durability/corruption invariants, using J07-owned v2 checkpoint path adaptation. Generic fault coverage supplements, never replaces, the five explicit J10 ENOSPC cases." + }, + { + "node_id": "tests/test_atomic_io_regressions.py::test_other_legacy_stores_errors_and_corruption[True-checkpoint]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + }, + "fixtures": [ + { + "path": "tests/test_atomic_io_regressions.py", + "sha256": "2f6e21692241b179ddc59e7c7318abf4eb7bd28ab72351d8bac769f0cd178cc4" + } + ], + "fixture_description": "J01 atomic durability/corruption invariants, using J07-owned v2 checkpoint path adaptation. Generic fault coverage supplements, never replaces, the five explicit J10 ENOSPC cases." + } + ], + "additional_mapping_required": false + }, + { + "stage_id": "J-02", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_acceptance_edges.py::test_real_sqlite_full_rolls_back_every_effect_then_accepts_same_request", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Real temporary SQLite DB; each ingest connection capped to current page_count; 100000-character allowed input triggers native SQLITE_FULL, then cap removed for same-key successful commit.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + } + ], + "assertions": [ + "Observes native sqlite_errorcode == SQLITE_FULL before product error translation; public StorageFailure, no acknowledgement.", + "Fresh reader sees zero revision/facts/pages/operations/outbox; no receipt; raw tables including namespace and projection remain empty; integrity_check=ok.", + "After removing cap, same request commits fact/page/receipt/event; replay is idempotent and event delivery returns the fact." + ], + "evidence_limits": [ + "Connection wrapper configures a real PRAGMA, not a mocked SQLite exception; host disk is not exhausted.", + "Empty temporary database only; no production filesystem or WAL qualification." + ] + } + ], + "additional_mapping_required": false + } + ] + }, + { + "case_id": "J03", + "ticket": "J-01", + "scenario": "Live-holder exclusion and process-death release", + "required_assertion": "Old owner cannot remove successor ownership", + "required_in_tickets": [ + "J-01" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-01", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_file_lock_regressions.py::test_aged_live_owner_kill_successor_and_third_owner", + "kind": "real_storage", + "source": { + "path": "tests/test_file_lock_regressions.py", + "sha256": "23302924a1771800e98c377a166297031a4a9d58d6da1abecd80cb912235359f" + }, + "fixtures": [ + { + "path": "tests/test_file_lock_regressions.py", + "sha256": "23302924a1771800e98c377a166297031a4a9d58d6da1abecd80cb912235359f" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J04", + "ticket": "J-02", + "scenario": "Memory/page interrupted write", + "required_assertion": "Recovery yields a consistent memory/provenance pair", + "required_in_tickets": [ + "J-02" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-02", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_durable_storage.py::test_fault_rolls_back_entire_pair[before_transaction]", + "kind": "injected_storage_fault", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_durable_storage.py::test_fault_rolls_back_entire_pair[after_page_insert]", + "kind": "injected_storage_fault", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_durable_storage.py::test_fault_rolls_back_entire_pair[after_fact_insert]", + "kind": "injected_storage_fault", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_durable_storage.py::test_fault_rolls_back_entire_pair[after_outbox_receipt]", + "kind": "injected_storage_fault", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_durable_storage.py::test_fault_rolls_back_entire_pair[before_commit]", + "kind": "injected_storage_fault", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[before_transaction-False]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Existing J02 spawned child calls os._exit(37) at before_transaction, after_page_insert, after_fact_insert, after_outbox_receipt, or after_commit; fresh SQLite process view and retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + } + ], + "assertions": [ + "Child exits at requested boundary; reopened page/fact/receipt counts are all zero before commit or all one after commit.", + "Retry yields revision 1 and one page; child has bounded join, terminate and kill teardown." + ], + "evidence_limits": [ + "Process exit is real; kill point selected by trusted fault hook. Does not emulate machine power loss.", + "Existing node asserts pair counts and idempotent recovery; no claim of a separately asserted byte-for-byte provenance comparison." + ] + }, + { + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_page_insert-False]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Existing J02 spawned child calls os._exit(37) at before_transaction, after_page_insert, after_fact_insert, after_outbox_receipt, or after_commit; fresh SQLite process view and retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + } + ], + "assertions": [ + "Child exits at requested boundary; reopened page/fact/receipt counts are all zero before commit or all one after commit.", + "Retry yields revision 1 and one page; child has bounded join, terminate and kill teardown." + ], + "evidence_limits": [ + "Process exit is real; kill point selected by trusted fault hook. Does not emulate machine power loss.", + "Existing node asserts pair counts and idempotent recovery; no claim of a separately asserted byte-for-byte provenance comparison." + ] + }, + { + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_fact_insert-False]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Existing J02 spawned child calls os._exit(37) at before_transaction, after_page_insert, after_fact_insert, after_outbox_receipt, or after_commit; fresh SQLite process view and retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + } + ], + "assertions": [ + "Child exits at requested boundary; reopened page/fact/receipt counts are all zero before commit or all one after commit.", + "Retry yields revision 1 and one page; child has bounded join, terminate and kill teardown." + ], + "evidence_limits": [ + "Process exit is real; kill point selected by trusted fault hook. Does not emulate machine power loss.", + "Existing node asserts pair counts and idempotent recovery; no claim of a separately asserted byte-for-byte provenance comparison." + ] + }, + { + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_outbox_receipt-False]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Existing J02 spawned child calls os._exit(37) at before_transaction, after_page_insert, after_fact_insert, after_outbox_receipt, or after_commit; fresh SQLite process view and retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + } + ], + "assertions": [ + "Child exits at requested boundary; reopened page/fact/receipt counts are all zero before commit or all one after commit.", + "Retry yields revision 1 and one page; child has bounded join, terminate and kill teardown." + ], + "evidence_limits": [ + "Process exit is real; kill point selected by trusted fault hook. Does not emulate machine power loss.", + "Existing node asserts pair counts and idempotent recovery; no claim of a separately asserted byte-for-byte provenance comparison." + ] + }, + { + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_commit-True]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Existing J02 spawned child calls os._exit(37) at before_transaction, after_page_insert, after_fact_insert, after_outbox_receipt, or after_commit; fresh SQLite process view and retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + } + ], + "assertions": [ + "Child exits at requested boundary; reopened page/fact/receipt counts are all zero before commit or all one after commit.", + "Retry yields revision 1 and one page; child has bounded join, terminate and kill teardown." + ], + "evidence_limits": [ + "Process exit is real; kill point selected by trusted fault hook. Does not emulate machine power loss.", + "Existing node asserts pair counts and idempotent recovery; no claim of a separately asserted byte-for-byte provenance comparison." + ] + } + ], + "additional_mapping_required": false + } + ] + }, + { + "case_id": "J05", + "ticket": "J-02", + "scenario": "Duplicate ingest event", + "required_assertion": "One logical operation is committed for the idempotency key", + "required_in_tickets": [ + "J-02" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-02", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_durable_storage.py::test_multiprocess_duplicate_keys_and_namespace_isolation", + "kind": "real_storage", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_durable_storage.py::test_public_facade_lost_ack_restarts_without_provider_or_side_effects", + "kind": "real_storage", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J06", + "ticket": "J-04", + "scenario": "Same-path source edit", + "required_assertion": "Hash-based freshness detects changed content", + "required_in_tickets": [ + "J-04" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-04", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_code_context.py::test_same_size_edit_new_removed_and_old_generation", + "kind": "real_parser", + "source": { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + }, + "fixtures": [ + { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J07", + "ticket": "J-04", + "scenario": "New source file", + "required_assertion": "Coverage records the newly unindexed file", + "required_in_tickets": [ + "J-04" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-04", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_code_context.py::test_same_size_edit_new_removed_and_old_generation", + "kind": "real_parser", + "source": { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + }, + "fixtures": [ + { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J08", + "ticket": "J-04", + "scenario": "Removed source file", + "required_assertion": "Removal is distinguished from unreadable/unavailable state", + "required_in_tickets": [ + "J-04" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-04", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_code_context.py::test_same_size_edit_new_removed_and_old_generation", + "kind": "real_parser", + "source": { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + }, + "fixtures": [ + { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_code_context.py::test_unsupported_encoding_parse_and_unreadable_unknown", + "kind": "real_parser", + "source": { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + }, + "fixtures": [ + { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J09", + "ticket": "J-03", + "scenario": "Workspace path scope", + "required_assertion": "Results remain in the requested authorized repository/path scope", + "required_in_tickets": [ + "J-03", + "J-04" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-03", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_scoped_research.py::test_repository_ids_same_basename_and_branch_snapshots_stay_distinct", + "kind": "public_api", + "source": { + "path": "tests/test_scoped_research.py", + "sha256": "428d4c17df53220a76f4e38c1be91f50d025418063613eac58dc59ec5bd17108" + }, + "fixtures": [ + { + "path": "tests/test_scoped_research.py", + "sha256": "428d4c17df53220a76f4e38c1be91f50d025418063613eac58dc59ec5bd17108" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + }, + { + "stage_id": "J-04", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_code_context.py::test_real_parser_cycle_scope_depth_workspace", + "kind": "real_parser", + "source": { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + }, + "fixtures": [ + { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J10", + "ticket": "J-04", + "scenario": "Workspace traversal depth", + "required_assertion": "Single-repo and workspace depth contracts agree", + "required_in_tickets": [ + "J-04" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-04", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_code_context.py::test_real_parser_cycle_scope_depth_workspace", + "kind": "real_parser", + "source": { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + }, + "fixtures": [ + { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J11", + "ticket": "J-05", + "scenario": "Pure symbol rename", + "required_assertion": "An evidenced new binding preserves historical memory", + "required_in_tickets": [ + "J-05" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-05", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_code_anchors.py::test_j11_samefile_rename_persists_fact_and_revision", + "kind": "real_parser", + "source": { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + }, + "fixtures": [ + { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J12", + "ticket": "J-05", + "scenario": "Pure file/symbol move", + "required_assertion": "Correct symbol binding follows the move with provenance", + "required_in_tickets": [ + "J-05" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-05", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_code_anchors.py::test_j12_move_and_rename_move[False]", + "kind": "real_parser", + "source": { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + }, + "fixtures": [ + { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_code_anchors.py::test_j12_move_and_rename_move[True]", + "kind": "real_parser", + "source": { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + }, + "fixtures": [ + { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J13", + "ticket": "J-05", + "scenario": "Ambiguous similar symbols", + "required_assertion": "No arbitrary reassignment; state remains unknown", + "required_in_tickets": [ + "J-05" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-05", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_code_anchors.py::test_j13_all_identical_getters_are_ambiguous_and_evidence_bounded", + "kind": "real_parser", + "source": { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + }, + "fixtures": [ + { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J14", + "ticket": "J-05", + "scenario": "Index behind worktree", + "required_assertion": "Stale graph absence does not prove fact deletion", + "required_in_tickets": [ + "J-05" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-05", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_code_anchors.py::test_j14_stale_snapshot_and_shifted_lines_never_read_current_disk", + "kind": "real_parser", + "source": { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + }, + "fixtures": [ + { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J15", + "ticket": "J-05", + "scenario": "Unavailable worktree", + "required_assertion": "No mass expiry from inability to inspect the repository", + "required_in_tickets": [ + "J-05" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-05", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_code_anchors.py::test_j15_missing_checkout_preserves_facts_and_history", + "kind": "real_parser", + "source": { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + }, + "fixtures": [ + { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J16", + "ticket": "J-06", + "scenario": "Unfinished work loses anchor", + "required_assertion": "Obligation remains open and can fall back to broader scope", + "required_in_tickets": [ + "J-06" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-06", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_open_work_bindings.py::test_j16_j31_real_binding_move_disappearance_forget_keep_obligation[# code removed\\n-orphaned_confirmed]", + "kind": "real_parser", + "status": "not_run", + "source": { + "path": "tests/test_open_work_bindings.py", + "sha256": "78c0d4fc7465734165d2de960acd5a9815e624ae96856d024c2e65bf9e12a65d" + }, + "fixtures": [ + { + "path": "tests/test_open_work_bindings.py", + "sha256": "78c0d4fc7465734165d2de960acd5a9815e624ae96856d024c2e65bf9e12a65d" + } + ], + "fixture_description": "Reviewed existing J06 test: temporary Git checkout parsed by actual J04 Node/tree-sitter, J02 fact, J05 logical binding, J06 obligation; file move then confirmed removal or malformed-source uncertainty.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/code_context/__init__.py", + "sha256": "0bdda619ccabbb7f4894b106572885cf15b5c86232bfd6f333f6ca1b8b75f211" + }, + { + "path": "jitmind/code_context/models.py", + "sha256": "84686d3cdb1f34e6111652956e65b90b01fd19487308a268022a66b14ea44878" + }, + { + "path": "jitmind/code_context/process.py", + "sha256": "ef9baf49a5bbe761fbffa1f60ef172b8f53c587eaffc13c4e179f2700462f77a" + }, + { + "path": "jitmind/code_context/retriever.py", + "sha256": "822dc84804992b8bd9fe7de62e535843585091350f8ff5a2311a05eaf51150f4" + }, + { + "path": "jitmind/code_context/service.py", + "sha256": "16daa9b16b9a55f130ca61204c96d40e8abb279709dbd4c36b81e4c99afbe873" + }, + { + "path": "jitmind/code_context/snapshot.py", + "sha256": "45a35d8a3455cde10816ff3e2feef3a7a21174bbec41326635d94394fec27272" + }, + { + "path": "jitmind/code_memory/anchors.py", + "sha256": "98d9715ad0faba844638859e5f49d8ccb58cd9a7e67c56c7376b81b08c24e31a" + }, + { + "path": "jitmind/code_memory/binding_models.py", + "sha256": "39c9e9676f58314e4e3d9073efd19f0072023440cdb6fc61f9d802fc9a9f203c" + }, + { + "path": "jitmind/code_memory/binding_storage.py", + "sha256": "5684d3bf4269703f87f4232f0d3fd32482d90be704170f57e1cc6670ab539c1f" + }, + { + "path": "jitmind/code_memory/revalidation.py", + "sha256": "ada66aa4f8d553d8930eb37aff00662544d7216bdfae941d643485ca8cdd92d5" + }, + { + "path": "jitmind/code_memory/open_work.py", + "sha256": "330b670fa75dfc33bd0013a4af3293fed415c29ef4f1df8df2f3295642e3a23c" + }, + { + "path": "jitmind/code_memory/work_storage.py", + "sha256": "f0a2775db9046f8b01007c946a586e9a4bd1ab3763fff5fef87df0d5555b8362" + }, + { + "path": "jitmind/code_memory/lesson_models.py", + "sha256": "447721bf5d7c7b972a4d3b1bda171c674a107d3aba50c90fa0033403f4c5b787" + }, + { + "path": "jitmind/code_memory/preflight.py", + "sha256": "7119d160107db1f0828fa31affc3aba18bcd716163df562a8c77c45cfebc6258" + } + ], + "assertions": [ + "Binding starts at source.py, follows real move to moved.py, then becomes orphaned_confirmed or unknown.", + "OpenWorkService.binding_locations falls back to exact authorized repo; obligation stays open at revision 1 through primary forget.", + "Real lesson projection/preflight and binding history run in same chain." + ], + "evidence_limits": [ + "Only the two exact collected parameters are reused; current tests and modules are the attested J10 snapshot.", + "Broader repository scope is a location fallback, not task completion or proof of code deletion in malformed input." + ] + }, + { + "node_id": "tests/test_open_work_bindings.py::test_j16_j31_real_binding_move_disappearance_forget_keep_obligation[invalid syntax ???\\n-unknown]", + "kind": "real_parser", + "status": "not_run", + "source": { + "path": "tests/test_open_work_bindings.py", + "sha256": "78c0d4fc7465734165d2de960acd5a9815e624ae96856d024c2e65bf9e12a65d" + }, + "fixtures": [ + { + "path": "tests/test_open_work_bindings.py", + "sha256": "78c0d4fc7465734165d2de960acd5a9815e624ae96856d024c2e65bf9e12a65d" + } + ], + "fixture_description": "Reviewed existing J06 test: temporary Git checkout parsed by actual J04 Node/tree-sitter, J02 fact, J05 logical binding, J06 obligation; file move then confirmed removal or malformed-source uncertainty.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/code_context/__init__.py", + "sha256": "0bdda619ccabbb7f4894b106572885cf15b5c86232bfd6f333f6ca1b8b75f211" + }, + { + "path": "jitmind/code_context/models.py", + "sha256": "84686d3cdb1f34e6111652956e65b90b01fd19487308a268022a66b14ea44878" + }, + { + "path": "jitmind/code_context/process.py", + "sha256": "ef9baf49a5bbe761fbffa1f60ef172b8f53c587eaffc13c4e179f2700462f77a" + }, + { + "path": "jitmind/code_context/retriever.py", + "sha256": "822dc84804992b8bd9fe7de62e535843585091350f8ff5a2311a05eaf51150f4" + }, + { + "path": "jitmind/code_context/service.py", + "sha256": "16daa9b16b9a55f130ca61204c96d40e8abb279709dbd4c36b81e4c99afbe873" + }, + { + "path": "jitmind/code_context/snapshot.py", + "sha256": "45a35d8a3455cde10816ff3e2feef3a7a21174bbec41326635d94394fec27272" + }, + { + "path": "jitmind/code_memory/anchors.py", + "sha256": "98d9715ad0faba844638859e5f49d8ccb58cd9a7e67c56c7376b81b08c24e31a" + }, + { + "path": "jitmind/code_memory/binding_models.py", + "sha256": "39c9e9676f58314e4e3d9073efd19f0072023440cdb6fc61f9d802fc9a9f203c" + }, + { + "path": "jitmind/code_memory/binding_storage.py", + "sha256": "5684d3bf4269703f87f4232f0d3fd32482d90be704170f57e1cc6670ab539c1f" + }, + { + "path": "jitmind/code_memory/revalidation.py", + "sha256": "ada66aa4f8d553d8930eb37aff00662544d7216bdfae941d643485ca8cdd92d5" + }, + { + "path": "jitmind/code_memory/open_work.py", + "sha256": "330b670fa75dfc33bd0013a4af3293fed415c29ef4f1df8df2f3295642e3a23c" + }, + { + "path": "jitmind/code_memory/work_storage.py", + "sha256": "f0a2775db9046f8b01007c946a586e9a4bd1ab3763fff5fef87df0d5555b8362" + }, + { + "path": "jitmind/code_memory/lesson_models.py", + "sha256": "447721bf5d7c7b972a4d3b1bda171c674a107d3aba50c90fa0033403f4c5b787" + }, + { + "path": "jitmind/code_memory/preflight.py", + "sha256": "7119d160107db1f0828fa31affc3aba18bcd716163df562a8c77c45cfebc6258" + } + ], + "assertions": [ + "Binding starts at source.py, follows real move to moved.py, then becomes orphaned_confirmed or unknown.", + "OpenWorkService.binding_locations falls back to exact authorized repo; obligation stays open at revision 1 through primary forget.", + "Real lesson projection/preflight and binding history run in same chain." + ], + "evidence_limits": [ + "Only the two exact collected parameters are reused; current tests and modules are the attested J10 snapshot.", + "Broader repository scope is a location fallback, not task completion or proof of code deletion in malformed input." + ] + } + ], + "additional_mapping_required": false + } + ] + }, + { + "case_id": "J17", + "ticket": "J-06", + "scenario": "Explicit task resolution", + "required_assertion": "Resolver and supporting evidence are recorded exactly once", + "required_in_tickets": [ + "J-06" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-06", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_open_work.py::test_durable_cas_replay_owner_history_and_explicit_reopen", + "kind": "real_storage", + "source": { + "path": "tests/test_open_work.py", + "sha256": "b0b192f5bd1ecfe39811346eb4ce912a85a15d00c446e31e8f2a01493e957597" + }, + "fixtures": [ + { + "path": "tests/test_open_work.py", + "sha256": "b0b192f5bd1ecfe39811346eb4ce912a85a15d00c446e31e8f2a01493e957597" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_open_work.py::test_competing_resolution_accepts_one", + "kind": "real_storage", + "source": { + "path": "tests/test_open_work.py", + "sha256": "b0b192f5bd1ecfe39811346eb4ce912a85a15d00c446e31e8f2a01493e957597" + }, + "fixtures": [ + { + "path": "tests/test_open_work.py", + "sha256": "b0b192f5bd1ecfe39811346eb4ce912a85a15d00c446e31e8f2a01493e957597" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J18", + "ticket": "J-06", + "scenario": "Preflight repeated edit", + "required_assertion": "Bounded once-per-symbol/session behavior avoids prompt flooding", + "required_in_tickets": [ + "J-06" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-06", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_preflight.py::test_version_dedup_replay_two_lessons_and_wrapper_budget", + "kind": "public_api", + "source": { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + }, + "fixtures": [ + { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J19", + "ticket": "J-06", + "scenario": "Poisoned imported law label", + "required_assertion": "Imported content cannot promote itself to trusted policy", + "required_in_tickets": [ + "J-06" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-06", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_preflight.py::test_imported_law_untrusted_authored_positive_and_spoof_denied", + "kind": "public_api", + "source": { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + }, + "fixtures": [ + { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J20", + "ticket": "J-06", + "scenario": "Preflight storage contention", + "required_assertion": "Bounded latency and explicit degraded behavior", + "required_in_tickets": [ + "J-06" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-06", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_preflight.py::test_independent_process_lock_bounded_deferred_and_release[work-IMMEDIATE]", + "kind": "real_storage", + "source": { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + }, + "fixtures": [ + { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_preflight.py::test_independent_process_lock_bounded_deferred_and_release[work-EXCLUSIVE]", + "kind": "real_storage", + "source": { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + }, + "fixtures": [ + { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_preflight.py::test_independent_process_lock_bounded_deferred_and_release[facts-IMMEDIATE]", + "kind": "real_storage", + "source": { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + }, + "fixtures": [ + { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_preflight.py::test_independent_process_lock_bounded_deferred_and_release[facts-EXCLUSIVE]", + "kind": "real_storage", + "source": { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + }, + "fixtures": [ + { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_preflight.py::test_default_deadline_boundary_with_real_storage[before]", + "kind": "real_storage", + "source": { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + }, + "fixtures": [ + { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + } + ], + "fixture_description": "Actual SQLite and default 150 ms policy with only the monotonic deadline clock advanced to before the cutoff. Checks response content and receipt count; not a wall-clock benchmark.", + "status": "not_run" + }, + { + "node_id": "tests/test_preflight.py::test_default_deadline_boundary_with_real_storage[at]", + "kind": "real_storage", + "source": { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + }, + "fixtures": [ + { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + } + ], + "fixture_description": "Actual SQLite and default 150 ms policy with only the monotonic deadline clock advanced to at the cutoff. Checks response content and receipt count; not a wall-clock benchmark.", + "status": "not_run" + }, + { + "node_id": "tests/test_preflight.py::test_default_deadline_boundary_with_real_storage[after]", + "kind": "real_storage", + "source": { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + }, + "fixtures": [ + { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + } + ], + "fixture_description": "Actual SQLite and default 150 ms policy with only the monotonic deadline clock advanced to after the cutoff. Checks response content and receipt count; not a wall-clock benchmark.", + "status": "not_run" + } + ] + } + ] + }, + { + "case_id": "J21", + "ticket": "J-07", + "scenario": "Checkpoint has event gap", + "required_assertion": "Incomplete event range cannot be published as closed", + "required_in_tickets": [ + "J-07" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-07", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_checkpoint_ledger.py::test_missing_event_and_identity_conflicts", + "kind": "real_storage", + "source": { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + }, + "fixtures": [ + { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J22", + "ticket": "J-07", + "scenario": "Checkpoint cuts tool interaction", + "required_assertion": "Unsafe boundary is rejected or postponed", + "required_in_tickets": [ + "J-07" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-07", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_checkpoint_ledger.py::test_overlapping_calls_and_midrange_closed_call", + "kind": "real_storage", + "source": { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + }, + "fixtures": [ + { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals[tool_result]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + }, + "fixtures": [ + { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + } + ], + "fixture_description": "Existing real SQLiteCheckpointLedger stream: tool_start at 0, message at 1, terminal at 2; all three terminal kinds have exact collected parameters.", + "modules_composed": [ + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/checkpoints/__init__.py", + "sha256": "4d54e3f91114b474c337fbd460653e46210c2c668fcb557c231560e8477c0017" + }, + { + "path": "jitmind/checkpoints/ledger.py", + "sha256": "ba500b7dff478277252df4647e4c9ddd48656c1f58667357cf38a2d5639f88e4" + } + ], + "assertions": [ + "Rejects range 0..1 before and after late terminal (end cut), and 1..2 / 2..2 / 1..1 (start cut or wholly inside interaction).", + "Complete 0..2 publishes revision 1; subsequent 3..3 allowed; repeated/unknown terminals rejected." + ], + "evidence_limits": [ + "Both boundary directions are directly asserted by the existing node. No provider transcript or remote checkpoint store." + ] + }, + { + "node_id": "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals[tool_cancel]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + }, + "fixtures": [ + { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + } + ], + "fixture_description": "Existing real SQLiteCheckpointLedger stream: tool_start at 0, message at 1, terminal at 2; all three terminal kinds have exact collected parameters.", + "modules_composed": [ + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/checkpoints/__init__.py", + "sha256": "4d54e3f91114b474c337fbd460653e46210c2c668fcb557c231560e8477c0017" + }, + { + "path": "jitmind/checkpoints/ledger.py", + "sha256": "ba500b7dff478277252df4647e4c9ddd48656c1f58667357cf38a2d5639f88e4" + } + ], + "assertions": [ + "Rejects range 0..1 before and after late terminal (end cut), and 1..2 / 2..2 / 1..1 (start cut or wholly inside interaction).", + "Complete 0..2 publishes revision 1; subsequent 3..3 allowed; repeated/unknown terminals rejected." + ], + "evidence_limits": [ + "Both boundary directions are directly asserted by the existing node. No provider transcript or remote checkpoint store." + ] + }, + { + "node_id": "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals[tool_timeout]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + }, + "fixtures": [ + { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + } + ], + "fixture_description": "Existing real SQLiteCheckpointLedger stream: tool_start at 0, message at 1, terminal at 2; all three terminal kinds have exact collected parameters.", + "modules_composed": [ + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/checkpoints/__init__.py", + "sha256": "4d54e3f91114b474c337fbd460653e46210c2c668fcb557c231560e8477c0017" + }, + { + "path": "jitmind/checkpoints/ledger.py", + "sha256": "ba500b7dff478277252df4647e4c9ddd48656c1f58667357cf38a2d5639f88e4" + } + ], + "assertions": [ + "Rejects range 0..1 before and after late terminal (end cut), and 1..2 / 2..2 / 1..1 (start cut or wholly inside interaction).", + "Complete 0..2 publishes revision 1; subsequent 3..3 allowed; repeated/unknown terminals rejected." + ], + "evidence_limits": [ + "Both boundary directions are directly asserted by the existing node. No provider transcript or remote checkpoint store." + ] + } + ], + "additional_mapping_required": false + } + ] + }, + { + "case_id": "J23", + "ticket": "J-03", + "scenario": "Cross-scope retrieval/cache", + "required_assertion": "Authorization filters candidates and caches before exposure", + "required_in_tickets": [ + "J-03", + "J-04" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-03", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_scoped_research.py::test_real_stores_are_isolated_before_build_rerank_prompt_and_logs", + "kind": "public_api", + "source": { + "path": "tests/test_scoped_research.py", + "sha256": "428d4c17df53220a76f4e38c1be91f50d025418063613eac58dc59ec5bd17108" + }, + "fixtures": [ + { + "path": "tests/test_scoped_research.py", + "sha256": "428d4c17df53220a76f4e38c1be91f50d025418063613eac58dc59ec5bd17108" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_scoped_research.py::test_cache_rechecks_snapshot_and_regrant_and_canonical_metadata", + "kind": "public_api", + "source": { + "path": "tests/test_scoped_research.py", + "sha256": "428d4c17df53220a76f4e38c1be91f50d025418063613eac58dc59ec5bd17108" + }, + "fixtures": [ + { + "path": "tests/test_scoped_research.py", + "sha256": "428d4c17df53220a76f4e38c1be91f50d025418063613eac58dc59ec5bd17108" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + }, + { + "stage_id": "J-04", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_code_context.py::test_denied_before_read_and_revoked_before_return", + "kind": "real_parser", + "source": { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + }, + "fixtures": [ + { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J24", + "ticket": "J-03", + "scenario": "Temporal boundary and timezone", + "required_assertion": "Valid/transaction-time answers respect normalized boundary semantics", + "required_in_tickets": [ + "J-03" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-03", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_scoped_temporal.py::test_seven_query_reviewed_retry_limit_oracle", + "kind": "temporal_oracle", + "source": { + "path": "tests/test_scoped_temporal.py", + "sha256": "e335712d09b7ecdc5e466b63008fb717dab4b9b772623e6b51df840ec71eb66e" + }, + "fixtures": [ + { + "path": "tests/test_scoped_temporal.py", + "sha256": "e335712d09b7ecdc5e466b63008fb717dab4b9b772623e6b51df840ec71eb66e" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_scoped_temporal.py::test_utc_offsets_and_exact_half_open_boundaries", + "kind": "temporal_oracle", + "source": { + "path": "tests/test_scoped_temporal.py", + "sha256": "e335712d09b7ecdc5e466b63008fb717dab4b9b772623e6b51df840ec71eb66e" + }, + "fixtures": [ + { + "path": "tests/test_scoped_temporal.py", + "sha256": "e335712d09b7ecdc5e466b63008fb717dab4b9b772623e6b51df840ec71eb66e" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_scoped_temporal.py::test_declared_single_valued_overlap_fails_and_multiple_values_opt_in", + "kind": "temporal_oracle", + "source": { + "path": "tests/test_scoped_temporal.py", + "sha256": "e335712d09b7ecdc5e466b63008fb717dab4b9b772623e6b51df840ec71eb66e" + }, + "fixtures": [ + { + "path": "tests/test_scoped_temporal.py", + "sha256": "e335712d09b7ecdc5e466b63008fb717dab4b9b772623e6b51df840ec71eb66e" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_scoped_temporal.py::test_legacy_naive_query_input_compatibility_is_unchanged", + "kind": "temporal_oracle", + "source": { + "path": "tests/test_scoped_temporal.py", + "sha256": "e335712d09b7ecdc5e466b63008fb717dab4b9b772623e6b51df840ec71eb66e" + }, + "fixtures": [ + { + "path": "tests/test_scoped_temporal.py", + "sha256": "e335712d09b7ecdc5e466b63008fb717dab4b9b772623e6b51df840ec71eb66e" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J25", + "ticket": "J-02", + "scenario": "Corrupt store and interrupted staging", + "required_assertion": "Corruption is reported without silently manufacturing an empty successful migration; interrupted staged import does not replace authoritative source", + "required_in_tickets": [ + "J-02" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-02", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_durable_migration.py::test_staging_requires_quiescence_and_detects_changed_source", + "kind": "migration", + "source": { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + "fixtures": [ + { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[truncated]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + "fixtures": [ + { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + }, + { + "node_id": "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[duplicate_json_key]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + "fixtures": [ + { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + }, + { + "node_id": "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[nan]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + "fixtures": [ + { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + }, + { + "node_id": "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged[before_import]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + "fixtures": [ + { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + }, + { + "node_id": "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged[after_import_pages]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + "fixtures": [ + { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + }, + { + "node_id": "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged[before_import_commit]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + "fixtures": [ + { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_real_migration_exit_preserves_source_and_allows_retry[after_import_pages]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_real_migration_exit_preserves_source_and_allows_retry[before_import_commit]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + } + ], + "additional_mapping_required": false + } + ] + }, + { + "case_id": "J26", + "ticket": "J-02", + "scenario": "Legacy migration and post-cutover rollback", + "required_assertion": "IDs, provenance, histories and queries preserved by explicit mapping; unknown custom stores not silently transactional", + "required_in_tickets": [ + "J-02" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-02", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_durable_migration.py::test_migration_preserves_ids_metadata_temporal_history_aliases", + "kind": "migration", + "source": { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + "fixtures": [ + { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_durable_migration.py::test_backup_reopen_restore_preserves_candidate_era_writes", + "kind": "migration", + "source": { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + "fixtures": [ + { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J27", + "ticket": "J-04", + "scenario": "Traversal and context acquisition limits", + "required_assertion": "Work stops within declared bounds and preserves source identities and explicit coverage", + "required_in_tickets": [ + "J-04" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-04", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_code_context.py::test_context_budget_and_retention", + "kind": "real_parser", + "source": { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + }, + "fixtures": [ + { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_code_context.py::test_coverage_limits_and_namespace_preflight", + "kind": "real_parser", + "source": { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + }, + "fixtures": [ + { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J28", + "ticket": "J-03", + "scenario": "Canonical path and scope validation", + "required_assertion": "Reject escapes without colliding distinct repositories/identities in caches or databases", + "required_in_tickets": [ + "J-03", + "J-04" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-03", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_scoped_research.py::test_real_store_roots_with_same_basename_are_distinct_and_alias_is_rejected", + "kind": "public_api", + "source": { + "path": "tests/test_scoped_research.py", + "sha256": "428d4c17df53220a76f4e38c1be91f50d025418063613eac58dc59ec5bd17108" + }, + "fixtures": [ + { + "path": "tests/test_scoped_research.py", + "sha256": "428d4c17df53220a76f4e38c1be91f50d025418063613eac58dc59ec5bd17108" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + }, + { + "stage_id": "J-04", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_code_context.py::test_symlink_sentinel_never_opened_and_basename_identity", + "kind": "real_parser", + "source": { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + }, + "fixtures": [ + { + "path": "tests/test_code_context.py", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J29", + "ticket": "J-02", + "scenario": "Projection outbox redelivery", + "required_assertion": "Primary durability intact, replay idempotent, lag visible; no stale graph evidence declared current", + "required_in_tickets": [ + "J-02", + "J-04", + "J-05" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-02", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_durable_storage.py::test_projection_duplicate_out_of_order_and_forget_filter", + "kind": "projection", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_durable_storage.py::test_projection_failure_does_not_rollback_authority", + "kind": "projection", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_durable_storage.py::test_process_crash_after_projection_is_idempotent", + "kind": "projection", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + }, + { + "stage_id": "J-04", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[projected]", + "kind": "projection", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Temporary unversioned checkout, actual J04 parser, J02 add/NOOP/delete, J05 binding and file move, J06 projected lesson/preflight/open task. Two schedules: primary projection already populated or original add event pending until after delete.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/code_context/__init__.py", + "sha256": "0bdda619ccabbb7f4894b106572885cf15b5c86232bfd6f333f6ca1b8b75f211" + }, + { + "path": "jitmind/code_context/models.py", + "sha256": "84686d3cdb1f34e6111652956e65b90b01fd19487308a268022a66b14ea44878" + }, + { + "path": "jitmind/code_context/process.py", + "sha256": "ef9baf49a5bbe761fbffa1f60ef172b8f53c587eaffc13c4e179f2700462f77a" + }, + { + "path": "jitmind/code_context/retriever.py", + "sha256": "822dc84804992b8bd9fe7de62e535843585091350f8ff5a2311a05eaf51150f4" + }, + { + "path": "jitmind/code_context/service.py", + "sha256": "16daa9b16b9a55f130ca61204c96d40e8abb279709dbd4c36b81e4c99afbe873" + }, + { + "path": "jitmind/code_context/snapshot.py", + "sha256": "45a35d8a3455cde10816ff3e2feef3a7a21174bbec41326635d94394fec27272" + }, + { + "path": "jitmind/code_memory/anchors.py", + "sha256": "98d9715ad0faba844638859e5f49d8ccb58cd9a7e67c56c7376b81b08c24e31a" + }, + { + "path": "jitmind/code_memory/binding_models.py", + "sha256": "39c9e9676f58314e4e3d9073efd19f0072023440cdb6fc61f9d802fc9a9f203c" + }, + { + "path": "jitmind/code_memory/binding_storage.py", + "sha256": "5684d3bf4269703f87f4232f0d3fd32482d90be704170f57e1cc6670ab539c1f" + }, + { + "path": "jitmind/code_memory/revalidation.py", + "sha256": "ada66aa4f8d553d8930eb37aff00662544d7216bdfae941d643485ca8cdd92d5" + }, + { + "path": "jitmind/code_memory/open_work.py", + "sha256": "330b670fa75dfc33bd0013a4af3293fed415c29ef4f1df8df2f3295642e3a23c" + }, + { + "path": "jitmind/code_memory/work_storage.py", + "sha256": "f0a2775db9046f8b01007c946a586e9a4bd1ab3763fff5fef87df0d5555b8362" + }, + { + "path": "jitmind/code_memory/lesson_models.py", + "sha256": "447721bf5d7c7b972a4d3b1bda171c674a107d3aba50c90fa0033403f4c5b787" + }, + { + "path": "jitmind/code_memory/preflight.py", + "sha256": "7119d160107db1f0828fa31affc3aba18bcd716163df562a8c77c45cfebc6258" + } + ], + "assertions": [ + "Fact durable and lesson delivered while primary outbox lag remains visible; NOOP delivered twice ahead of original add leaves watermark 0.", + "Real source move makes prior code freshness nonfresh and binding unknown; new build/revalidation produces higher generation and moved verified binding.", + "Lesson version 2 replay is idempotent; version 1 rejected; fact metadata redelivery rejected.", + "Deletion metadata and event followed by old add cannot resurrect fact/binding/lesson; pending add schedule explicitly delivers delete before still-pending add.", + "Primary watermark reaches delete revision, pending count zero, current code generation remains correct." + ], + "evidence_limits": [ + "J04 raw code index consumes captured source, not fact bodies. MEMORY_ONLY_SENTINEL_83f91c is deliberately absent from source.", + "Raw authorized source remains readable after memory forget; forgetting memory never promises deletion of user source files.", + "J02 primary administrative/include_inactive history and backups may retain bodies. Assertions concern ordinary derived retrieval, receipt reconstruction, and content-free binding/work/delivery histories; no physical erasure claim.", + "Source generation, binding observation and outbox revision are separate; no atomic transaction across checkout and three databases is claimed.", + "Uses supplied J10 snapshot. Owner modules may advance; final supervisor must assemble and retest, configuring newer authority adapters if required. No automatic listener or raw-code outbox consumer is invented." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[add_pending]", + "kind": "projection", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Temporary unversioned checkout, actual J04 parser, J02 add/NOOP/delete, J05 binding and file move, J06 projected lesson/preflight/open task. Two schedules: primary projection already populated or original add event pending until after delete.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/code_context/__init__.py", + "sha256": "0bdda619ccabbb7f4894b106572885cf15b5c86232bfd6f333f6ca1b8b75f211" + }, + { + "path": "jitmind/code_context/models.py", + "sha256": "84686d3cdb1f34e6111652956e65b90b01fd19487308a268022a66b14ea44878" + }, + { + "path": "jitmind/code_context/process.py", + "sha256": "ef9baf49a5bbe761fbffa1f60ef172b8f53c587eaffc13c4e179f2700462f77a" + }, + { + "path": "jitmind/code_context/retriever.py", + "sha256": "822dc84804992b8bd9fe7de62e535843585091350f8ff5a2311a05eaf51150f4" + }, + { + "path": "jitmind/code_context/service.py", + "sha256": "16daa9b16b9a55f130ca61204c96d40e8abb279709dbd4c36b81e4c99afbe873" + }, + { + "path": "jitmind/code_context/snapshot.py", + "sha256": "45a35d8a3455cde10816ff3e2feef3a7a21174bbec41326635d94394fec27272" + }, + { + "path": "jitmind/code_memory/anchors.py", + "sha256": "98d9715ad0faba844638859e5f49d8ccb58cd9a7e67c56c7376b81b08c24e31a" + }, + { + "path": "jitmind/code_memory/binding_models.py", + "sha256": "39c9e9676f58314e4e3d9073efd19f0072023440cdb6fc61f9d802fc9a9f203c" + }, + { + "path": "jitmind/code_memory/binding_storage.py", + "sha256": "5684d3bf4269703f87f4232f0d3fd32482d90be704170f57e1cc6670ab539c1f" + }, + { + "path": "jitmind/code_memory/revalidation.py", + "sha256": "ada66aa4f8d553d8930eb37aff00662544d7216bdfae941d643485ca8cdd92d5" + }, + { + "path": "jitmind/code_memory/open_work.py", + "sha256": "330b670fa75dfc33bd0013a4af3293fed415c29ef4f1df8df2f3295642e3a23c" + }, + { + "path": "jitmind/code_memory/work_storage.py", + "sha256": "f0a2775db9046f8b01007c946a586e9a4bd1ab3763fff5fef87df0d5555b8362" + }, + { + "path": "jitmind/code_memory/lesson_models.py", + "sha256": "447721bf5d7c7b972a4d3b1bda171c674a107d3aba50c90fa0033403f4c5b787" + }, + { + "path": "jitmind/code_memory/preflight.py", + "sha256": "7119d160107db1f0828fa31affc3aba18bcd716163df562a8c77c45cfebc6258" + } + ], + "assertions": [ + "Fact durable and lesson delivered while primary outbox lag remains visible; NOOP delivered twice ahead of original add leaves watermark 0.", + "Real source move makes prior code freshness nonfresh and binding unknown; new build/revalidation produces higher generation and moved verified binding.", + "Lesson version 2 replay is idempotent; version 1 rejected; fact metadata redelivery rejected.", + "Deletion metadata and event followed by old add cannot resurrect fact/binding/lesson; pending add schedule explicitly delivers delete before still-pending add.", + "Primary watermark reaches delete revision, pending count zero, current code generation remains correct." + ], + "evidence_limits": [ + "J04 raw code index consumes captured source, not fact bodies. MEMORY_ONLY_SENTINEL_83f91c is deliberately absent from source.", + "Raw authorized source remains readable after memory forget; forgetting memory never promises deletion of user source files.", + "J02 primary administrative/include_inactive history and backups may retain bodies. Assertions concern ordinary derived retrieval, receipt reconstruction, and content-free binding/work/delivery histories; no physical erasure claim.", + "Source generation, binding observation and outbox revision are separate; no atomic transaction across checkout and three databases is claimed.", + "Uses supplied J10 snapshot. Owner modules may advance; final supervisor must assemble and retest, configuring newer authority adapters if required. No automatic listener or raw-code outbox consumer is invented." + ] + } + ], + "additional_mapping_required": false + }, + { + "stage_id": "J-05", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_code_anchors.py::test_real_j02_projection_redelivery_and_terminal_tombstone", + "kind": "projection", + "source": { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + }, + "fixtures": [ + { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J30", + "ticket": "J-07", + "scenario": "Checkpoint identity collision", + "required_assertion": "Distinct identities cannot overwrite/delete each other; verify payload identity; ambiguous legacy mapping flagged", + "required_in_tickets": [ + "J-07" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-07", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_checkpoint_manager.py::test_legacy_identity_collision_and_migration", + "kind": "real_storage", + "source": { + "path": "tests/test_checkpoint_manager.py", + "sha256": "a85fd91122e6dbde9ddf7b7e5422af622d828192540ef3241d87ef51923f9b65" + }, + "fixtures": [ + { + "path": "tests/test_checkpoint_manager.py", + "sha256": "a85fd91122e6dbde9ddf7b7e5422af622d828192540ef3241d87ef51923f9b65" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + }, + { + "node_id": "tests/test_checkpoint_manager.py::test_requested_identity_verified_even_in_v2_path", + "kind": "real_storage", + "source": { + "path": "tests/test_checkpoint_manager.py", + "sha256": "a85fd91122e6dbde9ddf7b7e5422af622d828192540ef3241d87ef51923f9b65" + }, + "fixtures": [ + { + "path": "tests/test_checkpoint_manager.py", + "sha256": "a85fd91122e6dbde9ddf7b7e5422af622d828192540ef3241d87ef51923f9b65" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J31", + "ticket": "J-06", + "scenario": "Forget and derived retention", + "required_assertion": "Current retrieval excludes forgotten content; obligation remains; retained metadata does not leak body", + "required_in_tickets": [ + "J-02", + "J-04", + "J-05", + "J-06" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-02", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_durable_storage.py::test_projection_duplicate_out_of_order_and_forget_filter", + "kind": "projection", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + }, + { + "stage_id": "J-04", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[projected]", + "kind": "projection", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Same two actual composed parser chains as J29; memory-only sentinel first positively delivered from bound lesson and memory, then primary forget with deletion projection delayed.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/code_context/__init__.py", + "sha256": "0bdda619ccabbb7f4894b106572885cf15b5c86232bfd6f333f6ca1b8b75f211" + }, + { + "path": "jitmind/code_context/models.py", + "sha256": "84686d3cdb1f34e6111652956e65b90b01fd19487308a268022a66b14ea44878" + }, + { + "path": "jitmind/code_context/process.py", + "sha256": "ef9baf49a5bbe761fbffa1f60ef172b8f53c587eaffc13c4e179f2700462f77a" + }, + { + "path": "jitmind/code_context/retriever.py", + "sha256": "822dc84804992b8bd9fe7de62e535843585091350f8ff5a2311a05eaf51150f4" + }, + { + "path": "jitmind/code_context/service.py", + "sha256": "16daa9b16b9a55f130ca61204c96d40e8abb279709dbd4c36b81e4c99afbe873" + }, + { + "path": "jitmind/code_context/snapshot.py", + "sha256": "45a35d8a3455cde10816ff3e2feef3a7a21174bbec41326635d94394fec27272" + }, + { + "path": "jitmind/code_memory/anchors.py", + "sha256": "98d9715ad0faba844638859e5f49d8ccb58cd9a7e67c56c7376b81b08c24e31a" + }, + { + "path": "jitmind/code_memory/binding_models.py", + "sha256": "39c9e9676f58314e4e3d9073efd19f0072023440cdb6fc61f9d802fc9a9f203c" + }, + { + "path": "jitmind/code_memory/binding_storage.py", + "sha256": "5684d3bf4269703f87f4232f0d3fd32482d90be704170f57e1cc6670ab539c1f" + }, + { + "path": "jitmind/code_memory/revalidation.py", + "sha256": "ada66aa4f8d553d8930eb37aff00662544d7216bdfae941d643485ca8cdd92d5" + }, + { + "path": "jitmind/code_memory/open_work.py", + "sha256": "330b670fa75dfc33bd0013a4af3293fed415c29ef4f1df8df2f3295642e3a23c" + }, + { + "path": "jitmind/code_memory/work_storage.py", + "sha256": "f0a2775db9046f8b01007c946a586e9a4bd1ab3763fff5fef87df0d5555b8362" + }, + { + "path": "jitmind/code_memory/lesson_models.py", + "sha256": "447721bf5d7c7b972a4d3b1bda171c674a107d3aba50c90fa0033403f4c5b787" + }, + { + "path": "jitmind/code_memory/preflight.py", + "sha256": "7119d160107db1f0828fa31affc3aba18bcd716163df562a8c77c45cfebc6258" + } + ], + "assertions": [ + "After primary forget, ordinary fact/page/adapters/projected entries/receipt reconstruction exclude sentinel before any delete projection.", + "Both saved preflight receipts return empty payload; candidates empty; bound projection refused; deletion metadata then old events cannot revive content.", + "Purged lesson table empty; binding history, work history/receipts, delivery receipts and tombstones contain no sentinel.", + "Task remains open revision 1 with repo fallback. Raw authorized source remains exact and readable with original SHA-256, current generation and no memory sentinel." + ], + "evidence_limits": [ + "J04 raw code index consumes captured source, not fact bodies. MEMORY_ONLY_SENTINEL_83f91c is deliberately absent from source.", + "Raw authorized source remains readable after memory forget; forgetting memory never promises deletion of user source files.", + "J02 primary administrative/include_inactive history and backups may retain bodies. Assertions concern ordinary derived retrieval, receipt reconstruction, and content-free binding/work/delivery histories; no physical erasure claim.", + "Source generation, binding observation and outbox revision are separate; no atomic transaction across checkout and three databases is claimed.", + "Uses supplied J10 snapshot. Owner modules may advance; final supervisor must assemble and retest, configuring newer authority adapters if required. No automatic listener or raw-code outbox consumer is invented." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[add_pending]", + "kind": "projection", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Same two actual composed parser chains as J29; memory-only sentinel first positively delivered from bound lesson and memory, then primary forget with deletion projection delayed.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/code_context/__init__.py", + "sha256": "0bdda619ccabbb7f4894b106572885cf15b5c86232bfd6f333f6ca1b8b75f211" + }, + { + "path": "jitmind/code_context/models.py", + "sha256": "84686d3cdb1f34e6111652956e65b90b01fd19487308a268022a66b14ea44878" + }, + { + "path": "jitmind/code_context/process.py", + "sha256": "ef9baf49a5bbe761fbffa1f60ef172b8f53c587eaffc13c4e179f2700462f77a" + }, + { + "path": "jitmind/code_context/retriever.py", + "sha256": "822dc84804992b8bd9fe7de62e535843585091350f8ff5a2311a05eaf51150f4" + }, + { + "path": "jitmind/code_context/service.py", + "sha256": "16daa9b16b9a55f130ca61204c96d40e8abb279709dbd4c36b81e4c99afbe873" + }, + { + "path": "jitmind/code_context/snapshot.py", + "sha256": "45a35d8a3455cde10816ff3e2feef3a7a21174bbec41326635d94394fec27272" + }, + { + "path": "jitmind/code_memory/anchors.py", + "sha256": "98d9715ad0faba844638859e5f49d8ccb58cd9a7e67c56c7376b81b08c24e31a" + }, + { + "path": "jitmind/code_memory/binding_models.py", + "sha256": "39c9e9676f58314e4e3d9073efd19f0072023440cdb6fc61f9d802fc9a9f203c" + }, + { + "path": "jitmind/code_memory/binding_storage.py", + "sha256": "5684d3bf4269703f87f4232f0d3fd32482d90be704170f57e1cc6670ab539c1f" + }, + { + "path": "jitmind/code_memory/revalidation.py", + "sha256": "ada66aa4f8d553d8930eb37aff00662544d7216bdfae941d643485ca8cdd92d5" + }, + { + "path": "jitmind/code_memory/open_work.py", + "sha256": "330b670fa75dfc33bd0013a4af3293fed415c29ef4f1df8df2f3295642e3a23c" + }, + { + "path": "jitmind/code_memory/work_storage.py", + "sha256": "f0a2775db9046f8b01007c946a586e9a4bd1ab3763fff5fef87df0d5555b8362" + }, + { + "path": "jitmind/code_memory/lesson_models.py", + "sha256": "447721bf5d7c7b972a4d3b1bda171c674a107d3aba50c90fa0033403f4c5b787" + }, + { + "path": "jitmind/code_memory/preflight.py", + "sha256": "7119d160107db1f0828fa31affc3aba18bcd716163df562a8c77c45cfebc6258" + } + ], + "assertions": [ + "After primary forget, ordinary fact/page/adapters/projected entries/receipt reconstruction exclude sentinel before any delete projection.", + "Both saved preflight receipts return empty payload; candidates empty; bound projection refused; deletion metadata then old events cannot revive content.", + "Purged lesson table empty; binding history, work history/receipts, delivery receipts and tombstones contain no sentinel.", + "Task remains open revision 1 with repo fallback. Raw authorized source remains exact and readable with original SHA-256, current generation and no memory sentinel." + ], + "evidence_limits": [ + "J04 raw code index consumes captured source, not fact bodies. MEMORY_ONLY_SENTINEL_83f91c is deliberately absent from source.", + "Raw authorized source remains readable after memory forget; forgetting memory never promises deletion of user source files.", + "J02 primary administrative/include_inactive history and backups may retain bodies. Assertions concern ordinary derived retrieval, receipt reconstruction, and content-free binding/work/delivery histories; no physical erasure claim.", + "Source generation, binding observation and outbox revision are separate; no atomic transaction across checkout and three databases is claimed.", + "Uses supplied J10 snapshot. Owner modules may advance; final supervisor must assemble and retest, configuring newer authority adapters if required. No automatic listener or raw-code outbox consumer is invented." + ] + } + ], + "additional_mapping_required": false + }, + { + "stage_id": "J-05", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_code_anchors.py::test_deleted_authority_with_no_projection_cannot_return_verified", + "kind": "real_parser", + "source": { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + }, + "fixtures": [ + { + "path": "tests/test_code_anchors.py", + "sha256": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + }, + { + "stage_id": "J-06", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_preflight.py::test_primary_forget_hides_delayed_projection_replay_and_keeps_work", + "kind": "public_api", + "source": { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + }, + "fixtures": [ + { + "path": "tests/test_preflight.py", + "sha256": "b468672ced29b13192e8ec6b03ff4c09ac1d0df6bc872b570a4ffd1d1424690c" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + }, + { + "case_id": "J32", + "ticket": "J-08", + "scenario": "Standalone install qualification", + "required_assertion": "Core works independently; absent optional adapter gives capability error not failed import or remote fallback", + "required_in_tickets": [ + "J-08" + ], + "status": "not_run", + "stages": [ + { + "stage_id": "J-08", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_standalone_install.py::installed_smoke", + "kind": "installed_package", + "source": { + "path": "tests/test_standalone_install.py", + "sha256": "1617be508bd800a323058a8f5975bd54ca3ba6bccd9ca6a7df631586f0b75004" + }, + "fixtures": [ + { + "path": "tests/test_standalone_install.py", + "sha256": "1617be508bd800a323058a8f5975bd54ca3ba6bccd9ca6a7df631586f0b75004" + } + ], + "fixture_description": "Deterministic fixture constructed in this test source; not full original handoff fixtures." + } + ] + } + ] + } + ], + "derived_map_sha256": "752b9ed4b8b2571865a0369699bf0ea069f09f3b638f257c79ac498d97b8f5ba", + "mapping_notice": "Final owner-source mapping retains 32 cases and 41 required stages, original handoff/oracle digests, and every prior node. J10 supplies the eight previously incomplete stages. All statuses remain not_run; staged execution is separate from qualification. J10 evidence kinds are preserved exactly and currently block the unchanged runner schema; see final-map-review.md.", + "temporal_oracle": { + "source_sha256": "3a2bb77dab2790f10ad091d048987ab41064699b5bc4c05820d4b4aa83fe231e", + "year": 2026, + "timezone": "UTC", + "interval": "half-open", + "answers": [ + [ + "Jan20", + "Jan20", + 3 + ], + [ + "Jan20", + "Feb15", + 3 + ], + [ + "Jan20", + "Mar1", + 4 + ], + [ + "Jan10", + "Mar2", + 3 + ], + [ + "Jan15", + "Mar2", + 4 + ], + [ + "Feb1", + "Mar2", + 5 + ], + [ + "Feb10", + "Jan20", + 3 + ] + ], + "latest_Jan20": 4, + "conflicts": "error; never insertion order", + "legacy_naive_api": "unchanged" + } +} diff --git a/docs/engineering/acceptance-supplement.json b/docs/engineering/acceptance-supplement.json new file mode 100644 index 0000000..4ac9410 --- /dev/null +++ b/docs/engineering/acceptance-supplement.json @@ -0,0 +1,2343 @@ +{ + "schema": "jitmind.acceptance-supplement/v1", + "target": "JITMIND", + "status": "not_run", + "notice": "J10 exact-node supplement adapted in J08 to final owner APIs. All statuses remain not_run. Inputs and all_copied_dependencies retain historical J10 provenance; executable node source, fixture and composed-module hashes attest the final staged owner files. See acceptance-edge-notes.md and final-map-review.md.", + "inputs": [ + { + "path": "handoff/acceptance-map.json", + "sha256": "752b9ed4b8b2571865a0369699bf0ea069f09f3b638f257c79ac498d97b8f5ba" + }, + { + "path": "handoff/temporal-oracle.md", + "sha256": "3a2bb77dab2790f10ad091d048987ab41064699b5bc4c05820d4b4aa83fe231e" + }, + { + "path": "worktrees/J08/docs/engineering/acceptance-map.json", + "sha256": "099be53bdf862e4c4ecafc850f218d3db59f7a2a8b1237cc901993ba932fae9a" + }, + { + "path": "reports/J10-dependency-snapshot.json", + "sha256": "ad6e3927b1926f2bafc08c108e92cad54d98447096695b05fdba1aaf74d8cd58" + } + ], + "cases": [ + { + "case_id": "J02", + "status": "not_run", + "stages": [ + { + "stage_id": "J-01", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[advanced]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Five real legacy store classes in temporary directories; successful initial append, ENOSPC from os.replace after temporary bytes are fsynced, then successful append after fault removal.", + "modules_composed": [ + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/memory.py", + "sha256": "68e5803e6523b41f09fb710656ddfb9c0f2a61bb9ba9335a8fa684173e59a7e9" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + }, + { + "path": "jitmind/schemas/ttl_memory.py", + "sha256": "ff48ac01b5a5a1bacd41d671f48e7cee07a703dcf5b3017eb8b8ca14405e29ca" + }, + { + "path": "jitmind/schemas/ttl_page.py", + "sha256": "2889b4ec309cda92c8eeda1ec624f8a074ae00d791e9d5e7fd11a6e16d9816ca" + }, + { + "path": "jitmind/utils/atomic_io.py", + "sha256": "aa58bbae5ca9f686ad020f7ebe74a27c082400964aebad3614fe46cd4466202d" + }, + { + "path": "jitmind/utils/file_lock.py", + "sha256": "4498fbe42d1a9b8f7224f17d684a8a35fb15dbb815b49de83d1246be5ed6e398" + } + ], + "assertions": [ + "Typed PersistenceError reaches public add caller with ENOSPC cause and outcome_uncertain=False.", + "Previous authoritative bytes unchanged; same-instance and fresh reader contain only prior content; temporary file removed.", + "Positive writes before and after fault prove usable fixture." + ], + "evidence_limits": [ + "Injects only errno.ENOSPC at the actual pre-replacement boundary; does not fill disk or assert post-replacement failure semantics.", + "Covers the five public store add surfaces, not every MemoryAgent/provider path or every filesystem syscall." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[memory]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Five real legacy store classes in temporary directories; successful initial append, ENOSPC from os.replace after temporary bytes are fsynced, then successful append after fault removal.", + "modules_composed": [ + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/memory.py", + "sha256": "68e5803e6523b41f09fb710656ddfb9c0f2a61bb9ba9335a8fa684173e59a7e9" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + }, + { + "path": "jitmind/schemas/ttl_memory.py", + "sha256": "ff48ac01b5a5a1bacd41d671f48e7cee07a703dcf5b3017eb8b8ca14405e29ca" + }, + { + "path": "jitmind/schemas/ttl_page.py", + "sha256": "2889b4ec309cda92c8eeda1ec624f8a074ae00d791e9d5e7fd11a6e16d9816ca" + }, + { + "path": "jitmind/utils/atomic_io.py", + "sha256": "aa58bbae5ca9f686ad020f7ebe74a27c082400964aebad3614fe46cd4466202d" + }, + { + "path": "jitmind/utils/file_lock.py", + "sha256": "4498fbe42d1a9b8f7224f17d684a8a35fb15dbb815b49de83d1246be5ed6e398" + } + ], + "assertions": [ + "Typed PersistenceError reaches public add caller with ENOSPC cause and outcome_uncertain=False.", + "Previous authoritative bytes unchanged; same-instance and fresh reader contain only prior content; temporary file removed.", + "Positive writes before and after fault prove usable fixture." + ], + "evidence_limits": [ + "Injects only errno.ENOSPC at the actual pre-replacement boundary; does not fill disk or assert post-replacement failure semantics.", + "Covers the five public store add surfaces, not every MemoryAgent/provider path or every filesystem syscall." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[ttl_memory]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Five real legacy store classes in temporary directories; successful initial append, ENOSPC from os.replace after temporary bytes are fsynced, then successful append after fault removal.", + "modules_composed": [ + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/memory.py", + "sha256": "68e5803e6523b41f09fb710656ddfb9c0f2a61bb9ba9335a8fa684173e59a7e9" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + }, + { + "path": "jitmind/schemas/ttl_memory.py", + "sha256": "ff48ac01b5a5a1bacd41d671f48e7cee07a703dcf5b3017eb8b8ca14405e29ca" + }, + { + "path": "jitmind/schemas/ttl_page.py", + "sha256": "2889b4ec309cda92c8eeda1ec624f8a074ae00d791e9d5e7fd11a6e16d9816ca" + }, + { + "path": "jitmind/utils/atomic_io.py", + "sha256": "aa58bbae5ca9f686ad020f7ebe74a27c082400964aebad3614fe46cd4466202d" + }, + { + "path": "jitmind/utils/file_lock.py", + "sha256": "4498fbe42d1a9b8f7224f17d684a8a35fb15dbb815b49de83d1246be5ed6e398" + } + ], + "assertions": [ + "Typed PersistenceError reaches public add caller with ENOSPC cause and outcome_uncertain=False.", + "Previous authoritative bytes unchanged; same-instance and fresh reader contain only prior content; temporary file removed.", + "Positive writes before and after fault prove usable fixture." + ], + "evidence_limits": [ + "Injects only errno.ENOSPC at the actual pre-replacement boundary; does not fill disk or assert post-replacement failure semantics.", + "Covers the five public store add surfaces, not every MemoryAgent/provider path or every filesystem syscall." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[pages]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Five real legacy store classes in temporary directories; successful initial append, ENOSPC from os.replace after temporary bytes are fsynced, then successful append after fault removal.", + "modules_composed": [ + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/memory.py", + "sha256": "68e5803e6523b41f09fb710656ddfb9c0f2a61bb9ba9335a8fa684173e59a7e9" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + }, + { + "path": "jitmind/schemas/ttl_memory.py", + "sha256": "ff48ac01b5a5a1bacd41d671f48e7cee07a703dcf5b3017eb8b8ca14405e29ca" + }, + { + "path": "jitmind/schemas/ttl_page.py", + "sha256": "2889b4ec309cda92c8eeda1ec624f8a074ae00d791e9d5e7fd11a6e16d9816ca" + }, + { + "path": "jitmind/utils/atomic_io.py", + "sha256": "aa58bbae5ca9f686ad020f7ebe74a27c082400964aebad3614fe46cd4466202d" + }, + { + "path": "jitmind/utils/file_lock.py", + "sha256": "4498fbe42d1a9b8f7224f17d684a8a35fb15dbb815b49de83d1246be5ed6e398" + } + ], + "assertions": [ + "Typed PersistenceError reaches public add caller with ENOSPC cause and outcome_uncertain=False.", + "Previous authoritative bytes unchanged; same-instance and fresh reader contain only prior content; temporary file removed.", + "Positive writes before and after fault prove usable fixture." + ], + "evidence_limits": [ + "Injects only errno.ENOSPC at the actual pre-replacement boundary; does not fill disk or assert post-replacement failure semantics.", + "Covers the five public store add surfaces, not every MemoryAgent/provider path or every filesystem syscall." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[ttl_pages]", + "kind": "injected_storage_fault", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Five real legacy store classes in temporary directories; successful initial append, ENOSPC from os.replace after temporary bytes are fsynced, then successful append after fault removal.", + "modules_composed": [ + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/memory.py", + "sha256": "68e5803e6523b41f09fb710656ddfb9c0f2a61bb9ba9335a8fa684173e59a7e9" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + }, + { + "path": "jitmind/schemas/ttl_memory.py", + "sha256": "ff48ac01b5a5a1bacd41d671f48e7cee07a703dcf5b3017eb8b8ca14405e29ca" + }, + { + "path": "jitmind/schemas/ttl_page.py", + "sha256": "2889b4ec309cda92c8eeda1ec624f8a074ae00d791e9d5e7fd11a6e16d9816ca" + }, + { + "path": "jitmind/utils/atomic_io.py", + "sha256": "aa58bbae5ca9f686ad020f7ebe74a27c082400964aebad3614fe46cd4466202d" + }, + { + "path": "jitmind/utils/file_lock.py", + "sha256": "4498fbe42d1a9b8f7224f17d684a8a35fb15dbb815b49de83d1246be5ed6e398" + } + ], + "assertions": [ + "Typed PersistenceError reaches public add caller with ENOSPC cause and outcome_uncertain=False.", + "Previous authoritative bytes unchanged; same-instance and fresh reader contain only prior content; temporary file removed.", + "Positive writes before and after fault prove usable fixture." + ], + "evidence_limits": [ + "Injects only errno.ENOSPC at the actual pre-replacement boundary; does not fill disk or assert post-replacement failure semantics.", + "Covers the five public store add surfaces, not every MemoryAgent/provider path or every filesystem syscall." + ] + } + ] + }, + { + "stage_id": "J-02", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_acceptance_edges.py::test_real_sqlite_full_rolls_back_every_effect_then_accepts_same_request", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Real temporary SQLite DB; each ingest connection capped to current page_count; 100000-character allowed input triggers native SQLITE_FULL, then cap removed for same-key successful commit.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + } + ], + "assertions": [ + "Observes native sqlite_errorcode == SQLITE_FULL before product error translation; public StorageFailure, no acknowledgement.", + "Fresh reader sees zero revision/facts/pages/operations/outbox; no receipt; raw tables including namespace and projection remain empty; integrity_check=ok.", + "After removing cap, same request commits fact/page/receipt/event; replay is idempotent and event delivery returns the fact." + ], + "evidence_limits": [ + "Connection wrapper configures a real PRAGMA, not a mocked SQLite exception; host disk is not exhausted.", + "Empty temporary database only; no production filesystem or WAL qualification." + ] + } + ] + } + ], + "scenario": "Disk-full write failure", + "required_assertion": "Durability failure reaches caller; no committed acknowledgement" + }, + { + "case_id": "J04", + "status": "not_run", + "stages": [ + { + "stage_id": "J-02", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[before_transaction-False]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Existing J02 spawned child calls os._exit(37) at before_transaction, after_page_insert, after_fact_insert, after_outbox_receipt, or after_commit; fresh SQLite process view and retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + } + ], + "assertions": [ + "Child exits at requested boundary; reopened page/fact/receipt counts are all zero before commit or all one after commit.", + "Retry yields revision 1 and one page; child has bounded join, terminate and kill teardown." + ], + "evidence_limits": [ + "Process exit is real; kill point selected by trusted fault hook. Does not emulate machine power loss.", + "Existing node asserts pair counts and idempotent recovery; no claim of a separately asserted byte-for-byte provenance comparison." + ] + }, + { + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_page_insert-False]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Existing J02 spawned child calls os._exit(37) at before_transaction, after_page_insert, after_fact_insert, after_outbox_receipt, or after_commit; fresh SQLite process view and retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + } + ], + "assertions": [ + "Child exits at requested boundary; reopened page/fact/receipt counts are all zero before commit or all one after commit.", + "Retry yields revision 1 and one page; child has bounded join, terminate and kill teardown." + ], + "evidence_limits": [ + "Process exit is real; kill point selected by trusted fault hook. Does not emulate machine power loss.", + "Existing node asserts pair counts and idempotent recovery; no claim of a separately asserted byte-for-byte provenance comparison." + ] + }, + { + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_fact_insert-False]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Existing J02 spawned child calls os._exit(37) at before_transaction, after_page_insert, after_fact_insert, after_outbox_receipt, or after_commit; fresh SQLite process view and retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + } + ], + "assertions": [ + "Child exits at requested boundary; reopened page/fact/receipt counts are all zero before commit or all one after commit.", + "Retry yields revision 1 and one page; child has bounded join, terminate and kill teardown." + ], + "evidence_limits": [ + "Process exit is real; kill point selected by trusted fault hook. Does not emulate machine power loss.", + "Existing node asserts pair counts and idempotent recovery; no claim of a separately asserted byte-for-byte provenance comparison." + ] + }, + { + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_outbox_receipt-False]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Existing J02 spawned child calls os._exit(37) at before_transaction, after_page_insert, after_fact_insert, after_outbox_receipt, or after_commit; fresh SQLite process view and retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + } + ], + "assertions": [ + "Child exits at requested boundary; reopened page/fact/receipt counts are all zero before commit or all one after commit.", + "Retry yields revision 1 and one page; child has bounded join, terminate and kill teardown." + ], + "evidence_limits": [ + "Process exit is real; kill point selected by trusted fault hook. Does not emulate machine power loss.", + "Existing node asserts pair counts and idempotent recovery; no claim of a separately asserted byte-for-byte provenance comparison." + ] + }, + { + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_commit-True]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + }, + "fixtures": [ + { + "path": "tests/test_durable_storage.py", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00" + } + ], + "fixture_description": "Existing J02 spawned child calls os._exit(37) at before_transaction, after_page_insert, after_fact_insert, after_outbox_receipt, or after_commit; fresh SQLite process view and retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + } + ], + "assertions": [ + "Child exits at requested boundary; reopened page/fact/receipt counts are all zero before commit or all one after commit.", + "Retry yields revision 1 and one page; child has bounded join, terminate and kill teardown." + ], + "evidence_limits": [ + "Process exit is real; kill point selected by trusted fault hook. Does not emulate machine power loss.", + "Existing node asserts pair counts and idempotent recovery; no claim of a separately asserted byte-for-byte provenance comparison." + ] + } + ] + } + ], + "scenario": "Memory/page interrupted write", + "required_assertion": "Recovery yields a consistent memory/provenance pair" + }, + { + "case_id": "J16", + "status": "not_run", + "stages": [ + { + "stage_id": "J-06", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_open_work_bindings.py::test_j16_j31_real_binding_move_disappearance_forget_keep_obligation[# code removed\\n-orphaned_confirmed]", + "kind": "real_parser", + "status": "not_run", + "source": { + "path": "tests/test_open_work_bindings.py", + "sha256": "80f876c3cb1f671b41ecbd68c4f969da5f820c6189d4b1b22d6ac4f164fe87e7" + }, + "fixtures": [ + { + "path": "tests/test_open_work_bindings.py", + "sha256": "80f876c3cb1f671b41ecbd68c4f969da5f820c6189d4b1b22d6ac4f164fe87e7" + } + ], + "fixture_description": "Reviewed existing J06 test: temporary Git checkout parsed by actual J04 Node/tree-sitter, J02 fact, J05 logical binding, J06 obligation; file move then confirmed removal or malformed-source uncertainty.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/code_context/__init__.py", + "sha256": "0bdda619ccabbb7f4894b106572885cf15b5c86232bfd6f333f6ca1b8b75f211" + }, + { + "path": "jitmind/code_context/models.py", + "sha256": "84686d3cdb1f34e6111652956e65b90b01fd19487308a268022a66b14ea44878" + }, + { + "path": "jitmind/code_context/process.py", + "sha256": "ef9baf49a5bbe761fbffa1f60ef172b8f53c587eaffc13c4e179f2700462f77a" + }, + { + "path": "jitmind/code_context/retriever.py", + "sha256": "822dc84804992b8bd9fe7de62e535843585091350f8ff5a2311a05eaf51150f4" + }, + { + "path": "jitmind/code_context/service.py", + "sha256": "16daa9b16b9a55f130ca61204c96d40e8abb279709dbd4c36b81e4c99afbe873" + }, + { + "path": "jitmind/code_context/snapshot.py", + "sha256": "45a35d8a3455cde10816ff3e2feef3a7a21174bbec41326635d94394fec27272" + }, + { + "path": "jitmind/code_memory/anchors.py", + "sha256": "98d9715ad0faba844638859e5f49d8ccb58cd9a7e67c56c7376b81b08c24e31a" + }, + { + "path": "jitmind/code_memory/binding_models.py", + "sha256": "39c9e9676f58314e4e3d9073efd19f0072023440cdb6fc61f9d802fc9a9f203c" + }, + { + "path": "jitmind/code_memory/binding_storage.py", + "sha256": "5684d3bf4269703f87f4232f0d3fd32482d90be704170f57e1cc6670ab539c1f" + }, + { + "path": "jitmind/code_memory/revalidation.py", + "sha256": "ada66aa4f8d553d8930eb37aff00662544d7216bdfae941d643485ca8cdd92d5" + }, + { + "path": "jitmind/code_memory/open_work.py", + "sha256": "330b670fa75dfc33bd0013a4af3293fed415c29ef4f1df8df2f3295642e3a23c" + }, + { + "path": "jitmind/code_memory/work_storage.py", + "sha256": "f0a2775db9046f8b01007c946a586e9a4bd1ab3763fff5fef87df0d5555b8362" + }, + { + "path": "jitmind/code_memory/lesson_models.py", + "sha256": "447721bf5d7c7b972a4d3b1bda171c674a107d3aba50c90fa0033403f4c5b787" + }, + { + "path": "jitmind/code_memory/preflight.py", + "sha256": "7119d160107db1f0828fa31affc3aba18bcd716163df562a8c77c45cfebc6258" + } + ], + "assertions": [ + "Binding starts at source.py, follows real move to moved.py, then becomes orphaned_confirmed or unknown.", + "OpenWorkService.binding_locations falls back to exact authorized repo; obligation stays open at revision 1 through primary forget.", + "Real lesson projection/preflight and binding history run in same chain." + ], + "evidence_limits": [ + "Only the two exact collected parameters are reused; current tests and modules are the attested J10 snapshot.", + "Broader repository scope is a location fallback, not task completion or proof of code deletion in malformed input." + ] + }, + { + "node_id": "tests/test_open_work_bindings.py::test_j16_j31_real_binding_move_disappearance_forget_keep_obligation[invalid syntax ???\\n-unknown]", + "kind": "real_parser", + "status": "not_run", + "source": { + "path": "tests/test_open_work_bindings.py", + "sha256": "80f876c3cb1f671b41ecbd68c4f969da5f820c6189d4b1b22d6ac4f164fe87e7" + }, + "fixtures": [ + { + "path": "tests/test_open_work_bindings.py", + "sha256": "80f876c3cb1f671b41ecbd68c4f969da5f820c6189d4b1b22d6ac4f164fe87e7" + } + ], + "fixture_description": "Reviewed existing J06 test: temporary Git checkout parsed by actual J04 Node/tree-sitter, J02 fact, J05 logical binding, J06 obligation; file move then confirmed removal or malformed-source uncertainty.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/code_context/__init__.py", + "sha256": "0bdda619ccabbb7f4894b106572885cf15b5c86232bfd6f333f6ca1b8b75f211" + }, + { + "path": "jitmind/code_context/models.py", + "sha256": "84686d3cdb1f34e6111652956e65b90b01fd19487308a268022a66b14ea44878" + }, + { + "path": "jitmind/code_context/process.py", + "sha256": "ef9baf49a5bbe761fbffa1f60ef172b8f53c587eaffc13c4e179f2700462f77a" + }, + { + "path": "jitmind/code_context/retriever.py", + "sha256": "822dc84804992b8bd9fe7de62e535843585091350f8ff5a2311a05eaf51150f4" + }, + { + "path": "jitmind/code_context/service.py", + "sha256": "16daa9b16b9a55f130ca61204c96d40e8abb279709dbd4c36b81e4c99afbe873" + }, + { + "path": "jitmind/code_context/snapshot.py", + "sha256": "45a35d8a3455cde10816ff3e2feef3a7a21174bbec41326635d94394fec27272" + }, + { + "path": "jitmind/code_memory/anchors.py", + "sha256": "98d9715ad0faba844638859e5f49d8ccb58cd9a7e67c56c7376b81b08c24e31a" + }, + { + "path": "jitmind/code_memory/binding_models.py", + "sha256": "39c9e9676f58314e4e3d9073efd19f0072023440cdb6fc61f9d802fc9a9f203c" + }, + { + "path": "jitmind/code_memory/binding_storage.py", + "sha256": "5684d3bf4269703f87f4232f0d3fd32482d90be704170f57e1cc6670ab539c1f" + }, + { + "path": "jitmind/code_memory/revalidation.py", + "sha256": "ada66aa4f8d553d8930eb37aff00662544d7216bdfae941d643485ca8cdd92d5" + }, + { + "path": "jitmind/code_memory/open_work.py", + "sha256": "330b670fa75dfc33bd0013a4af3293fed415c29ef4f1df8df2f3295642e3a23c" + }, + { + "path": "jitmind/code_memory/work_storage.py", + "sha256": "f0a2775db9046f8b01007c946a586e9a4bd1ab3763fff5fef87df0d5555b8362" + }, + { + "path": "jitmind/code_memory/lesson_models.py", + "sha256": "447721bf5d7c7b972a4d3b1bda171c674a107d3aba50c90fa0033403f4c5b787" + }, + { + "path": "jitmind/code_memory/preflight.py", + "sha256": "7119d160107db1f0828fa31affc3aba18bcd716163df562a8c77c45cfebc6258" + } + ], + "assertions": [ + "Binding starts at source.py, follows real move to moved.py, then becomes orphaned_confirmed or unknown.", + "OpenWorkService.binding_locations falls back to exact authorized repo; obligation stays open at revision 1 through primary forget.", + "Real lesson projection/preflight and binding history run in same chain." + ], + "evidence_limits": [ + "Only the two exact collected parameters are reused; current tests and modules are the attested J10 snapshot.", + "Broader repository scope is a location fallback, not task completion or proof of code deletion in malformed input." + ] + } + ] + } + ], + "scenario": "Unfinished work loses anchor", + "required_assertion": "Obligation remains open and can fall back to broader scope" + }, + { + "case_id": "J22", + "status": "not_run", + "stages": [ + { + "stage_id": "J-07", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals[tool_result]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + }, + "fixtures": [ + { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + } + ], + "fixture_description": "Existing real SQLiteCheckpointLedger stream: tool_start at 0, message at 1, terminal at 2; all three terminal kinds have exact collected parameters.", + "modules_composed": [ + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/checkpoints/__init__.py", + "sha256": "4d54e3f91114b474c337fbd460653e46210c2c668fcb557c231560e8477c0017" + }, + { + "path": "jitmind/checkpoints/ledger.py", + "sha256": "ba500b7dff478277252df4647e4c9ddd48656c1f58667357cf38a2d5639f88e4" + } + ], + "assertions": [ + "Rejects range 0..1 before and after late terminal (end cut), and 1..2 / 2..2 / 1..1 (start cut or wholly inside interaction).", + "Complete 0..2 publishes revision 1; subsequent 3..3 allowed; repeated/unknown terminals rejected." + ], + "evidence_limits": [ + "Both boundary directions are directly asserted by the existing node. No provider transcript or remote checkpoint store." + ] + }, + { + "node_id": "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals[tool_cancel]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + }, + "fixtures": [ + { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + } + ], + "fixture_description": "Existing real SQLiteCheckpointLedger stream: tool_start at 0, message at 1, terminal at 2; all three terminal kinds have exact collected parameters.", + "modules_composed": [ + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/checkpoints/__init__.py", + "sha256": "4d54e3f91114b474c337fbd460653e46210c2c668fcb557c231560e8477c0017" + }, + { + "path": "jitmind/checkpoints/ledger.py", + "sha256": "ba500b7dff478277252df4647e4c9ddd48656c1f58667357cf38a2d5639f88e4" + } + ], + "assertions": [ + "Rejects range 0..1 before and after late terminal (end cut), and 1..2 / 2..2 / 1..1 (start cut or wholly inside interaction).", + "Complete 0..2 publishes revision 1; subsequent 3..3 allowed; repeated/unknown terminals rejected." + ], + "evidence_limits": [ + "Both boundary directions are directly asserted by the existing node. No provider transcript or remote checkpoint store." + ] + }, + { + "node_id": "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals[tool_timeout]", + "kind": "real_storage", + "status": "not_run", + "source": { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + }, + "fixtures": [ + { + "path": "tests/test_checkpoint_ledger.py", + "sha256": "a9ff3ffe41f0cba68e0e7b86ee425c69e9ee912c169fa2323dc510c5a40313b8" + } + ], + "fixture_description": "Existing real SQLiteCheckpointLedger stream: tool_start at 0, message at 1, terminal at 2; all three terminal kinds have exact collected parameters.", + "modules_composed": [ + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/checkpoints/__init__.py", + "sha256": "4d54e3f91114b474c337fbd460653e46210c2c668fcb557c231560e8477c0017" + }, + { + "path": "jitmind/checkpoints/ledger.py", + "sha256": "ba500b7dff478277252df4647e4c9ddd48656c1f58667357cf38a2d5639f88e4" + } + ], + "assertions": [ + "Rejects range 0..1 before and after late terminal (end cut), and 1..2 / 2..2 / 1..1 (start cut or wholly inside interaction).", + "Complete 0..2 publishes revision 1; subsequent 3..3 allowed; repeated/unknown terminals rejected." + ], + "evidence_limits": [ + "Both boundary directions are directly asserted by the existing node. No provider transcript or remote checkpoint store." + ] + } + ] + } + ], + "scenario": "Checkpoint cuts tool interaction", + "required_assertion": "Unsafe boundary is rejected or postponed" + }, + { + "case_id": "J25", + "status": "not_run", + "stages": [ + { + "stage_id": "J-02", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[truncated]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + "fixtures": [ + { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + }, + { + "node_id": "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[duplicate_json_key]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + "fixtures": [ + { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + }, + { + "node_id": "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[nan]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + "fixtures": [ + { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + }, + { + "node_id": "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged[before_import]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + "fixtures": [ + { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + }, + { + "node_id": "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged[after_import_pages]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + "fixtures": [ + { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + }, + { + "node_id": "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged[before_import_commit]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + }, + "fixtures": [ + { + "path": "tests/test_durable_migration.py", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_real_migration_exit_preserves_source_and_allows_retry[after_import_pages]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_real_migration_exit_preserves_source_and_allows_retry[before_import_commit]", + "kind": "migration", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Existing truncated/duplicate-key/NaN legacy-source rejection and three exception rollback nodes; new real child exit(73) after import pages or before import commit; source JSON byte snapshots and clean retry.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/storage/migration.py", + "sha256": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a" + }, + { + "path": "jitmind/schemas/advanced_memory.py", + "sha256": "b56e5807477630000e1dc7c2166080343a1e5eceecdb643cf9d6240c98512633" + }, + { + "path": "jitmind/schemas/page.py", + "sha256": "4f47392d633fd71f3a7f4b758ce16d5c65e2d1aa82809d410214196632b00d0e" + } + ], + "assertions": [ + "Corrupt source raises exact MigrationError without creating a candidate or changing original JSON.", + "Existing three injected exception nodes leave source and candidate empty and permit positive retry.", + "Real child exits roll back namespaces/pages/facts/operations/outbox/aliases/imports; original source bytes unchanged.", + "Restaging and retry preserve fact ID, page provenance and alias; duplicate import keeps revision 1." + ], + "evidence_limits": [ + "Corrupt cases and exception nodes are reused unchanged; real process-exit supplement owns its own small fixture.", + "No concurrent legacy writer or machine-power-loss simulation; staging is explicitly quiesced." + ] + } + ] + } + ], + "scenario": "Corrupt store and interrupted staging", + "required_assertion": "Corruption is reported without silently manufacturing an empty successful migration; interrupted staged import does not replace authoritative source" + }, + { + "case_id": "J29", + "status": "not_run", + "stages": [ + { + "stage_id": "J-04", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[projected]", + "kind": "projection", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Temporary unversioned checkout, actual J04 parser, J02 add/NOOP/delete, J05 binding and file move, J06 projected lesson/preflight/open task. Two schedules: primary projection already populated or original add event pending until after delete.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/code_context/__init__.py", + "sha256": "0bdda619ccabbb7f4894b106572885cf15b5c86232bfd6f333f6ca1b8b75f211" + }, + { + "path": "jitmind/code_context/models.py", + "sha256": "84686d3cdb1f34e6111652956e65b90b01fd19487308a268022a66b14ea44878" + }, + { + "path": "jitmind/code_context/process.py", + "sha256": "ef9baf49a5bbe761fbffa1f60ef172b8f53c587eaffc13c4e179f2700462f77a" + }, + { + "path": "jitmind/code_context/retriever.py", + "sha256": "822dc84804992b8bd9fe7de62e535843585091350f8ff5a2311a05eaf51150f4" + }, + { + "path": "jitmind/code_context/service.py", + "sha256": "16daa9b16b9a55f130ca61204c96d40e8abb279709dbd4c36b81e4c99afbe873" + }, + { + "path": "jitmind/code_context/snapshot.py", + "sha256": "45a35d8a3455cde10816ff3e2feef3a7a21174bbec41326635d94394fec27272" + }, + { + "path": "jitmind/code_memory/anchors.py", + "sha256": "98d9715ad0faba844638859e5f49d8ccb58cd9a7e67c56c7376b81b08c24e31a" + }, + { + "path": "jitmind/code_memory/binding_models.py", + "sha256": "39c9e9676f58314e4e3d9073efd19f0072023440cdb6fc61f9d802fc9a9f203c" + }, + { + "path": "jitmind/code_memory/binding_storage.py", + "sha256": "5684d3bf4269703f87f4232f0d3fd32482d90be704170f57e1cc6670ab539c1f" + }, + { + "path": "jitmind/code_memory/revalidation.py", + "sha256": "ada66aa4f8d553d8930eb37aff00662544d7216bdfae941d643485ca8cdd92d5" + }, + { + "path": "jitmind/code_memory/open_work.py", + "sha256": "330b670fa75dfc33bd0013a4af3293fed415c29ef4f1df8df2f3295642e3a23c" + }, + { + "path": "jitmind/code_memory/work_storage.py", + "sha256": "f0a2775db9046f8b01007c946a586e9a4bd1ab3763fff5fef87df0d5555b8362" + }, + { + "path": "jitmind/code_memory/lesson_models.py", + "sha256": "447721bf5d7c7b972a4d3b1bda171c674a107d3aba50c90fa0033403f4c5b787" + }, + { + "path": "jitmind/code_memory/preflight.py", + "sha256": "7119d160107db1f0828fa31affc3aba18bcd716163df562a8c77c45cfebc6258" + } + ], + "assertions": [ + "Fact durable and lesson delivered while primary outbox lag remains visible; NOOP delivered twice ahead of original add leaves watermark 0.", + "Real source move makes prior code freshness nonfresh and binding unknown; new build/revalidation produces higher generation and moved verified binding.", + "Lesson version 2 replay is idempotent; version 1 rejected; fact metadata redelivery rejected.", + "Deletion metadata and event followed by old add cannot resurrect fact/binding/lesson; pending add schedule explicitly delivers delete before still-pending add.", + "Primary watermark reaches delete revision, pending count zero, current code generation remains correct." + ], + "evidence_limits": [ + "J04 raw code index consumes captured source, not fact bodies. MEMORY_ONLY_SENTINEL_83f91c is deliberately absent from source.", + "Raw authorized source remains readable after memory forget; forgetting memory never promises deletion of user source files.", + "J02 primary administrative/include_inactive history and backups may retain bodies. Assertions concern ordinary derived retrieval, receipt reconstruction, and content-free binding/work/delivery histories; no physical erasure claim.", + "Source generation, binding observation and outbox revision are separate; no atomic transaction across checkout and three databases is claimed.", + "Uses supplied J10 snapshot. Owner modules may advance; final supervisor must assemble and retest, configuring newer authority adapters if required. No automatic listener or raw-code outbox consumer is invented." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[add_pending]", + "kind": "projection", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Temporary unversioned checkout, actual J04 parser, J02 add/NOOP/delete, J05 binding and file move, J06 projected lesson/preflight/open task. Two schedules: primary projection already populated or original add event pending until after delete.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/code_context/__init__.py", + "sha256": "0bdda619ccabbb7f4894b106572885cf15b5c86232bfd6f333f6ca1b8b75f211" + }, + { + "path": "jitmind/code_context/models.py", + "sha256": "84686d3cdb1f34e6111652956e65b90b01fd19487308a268022a66b14ea44878" + }, + { + "path": "jitmind/code_context/process.py", + "sha256": "ef9baf49a5bbe761fbffa1f60ef172b8f53c587eaffc13c4e179f2700462f77a" + }, + { + "path": "jitmind/code_context/retriever.py", + "sha256": "822dc84804992b8bd9fe7de62e535843585091350f8ff5a2311a05eaf51150f4" + }, + { + "path": "jitmind/code_context/service.py", + "sha256": "16daa9b16b9a55f130ca61204c96d40e8abb279709dbd4c36b81e4c99afbe873" + }, + { + "path": "jitmind/code_context/snapshot.py", + "sha256": "45a35d8a3455cde10816ff3e2feef3a7a21174bbec41326635d94394fec27272" + }, + { + "path": "jitmind/code_memory/anchors.py", + "sha256": "98d9715ad0faba844638859e5f49d8ccb58cd9a7e67c56c7376b81b08c24e31a" + }, + { + "path": "jitmind/code_memory/binding_models.py", + "sha256": "39c9e9676f58314e4e3d9073efd19f0072023440cdb6fc61f9d802fc9a9f203c" + }, + { + "path": "jitmind/code_memory/binding_storage.py", + "sha256": "5684d3bf4269703f87f4232f0d3fd32482d90be704170f57e1cc6670ab539c1f" + }, + { + "path": "jitmind/code_memory/revalidation.py", + "sha256": "ada66aa4f8d553d8930eb37aff00662544d7216bdfae941d643485ca8cdd92d5" + }, + { + "path": "jitmind/code_memory/open_work.py", + "sha256": "330b670fa75dfc33bd0013a4af3293fed415c29ef4f1df8df2f3295642e3a23c" + }, + { + "path": "jitmind/code_memory/work_storage.py", + "sha256": "f0a2775db9046f8b01007c946a586e9a4bd1ab3763fff5fef87df0d5555b8362" + }, + { + "path": "jitmind/code_memory/lesson_models.py", + "sha256": "447721bf5d7c7b972a4d3b1bda171c674a107d3aba50c90fa0033403f4c5b787" + }, + { + "path": "jitmind/code_memory/preflight.py", + "sha256": "7119d160107db1f0828fa31affc3aba18bcd716163df562a8c77c45cfebc6258" + } + ], + "assertions": [ + "Fact durable and lesson delivered while primary outbox lag remains visible; NOOP delivered twice ahead of original add leaves watermark 0.", + "Real source move makes prior code freshness nonfresh and binding unknown; new build/revalidation produces higher generation and moved verified binding.", + "Lesson version 2 replay is idempotent; version 1 rejected; fact metadata redelivery rejected.", + "Deletion metadata and event followed by old add cannot resurrect fact/binding/lesson; pending add schedule explicitly delivers delete before still-pending add.", + "Primary watermark reaches delete revision, pending count zero, current code generation remains correct." + ], + "evidence_limits": [ + "J04 raw code index consumes captured source, not fact bodies. MEMORY_ONLY_SENTINEL_83f91c is deliberately absent from source.", + "Raw authorized source remains readable after memory forget; forgetting memory never promises deletion of user source files.", + "J02 primary administrative/include_inactive history and backups may retain bodies. Assertions concern ordinary derived retrieval, receipt reconstruction, and content-free binding/work/delivery histories; no physical erasure claim.", + "Source generation, binding observation and outbox revision are separate; no atomic transaction across checkout and three databases is claimed.", + "Uses supplied J10 snapshot. Owner modules may advance; final supervisor must assemble and retest, configuring newer authority adapters if required. No automatic listener or raw-code outbox consumer is invented." + ] + } + ] + } + ], + "scenario": "Projection outbox redelivery", + "required_assertion": "Primary durability intact, replay idempotent, lag visible; no stale graph evidence declared current" + }, + { + "case_id": "J31", + "status": "not_run", + "stages": [ + { + "stage_id": "J-04", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[projected]", + "kind": "projection", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Same two actual composed parser chains as J29; memory-only sentinel first positively delivered from bound lesson and memory, then primary forget with deletion projection delayed.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/code_context/__init__.py", + "sha256": "0bdda619ccabbb7f4894b106572885cf15b5c86232bfd6f333f6ca1b8b75f211" + }, + { + "path": "jitmind/code_context/models.py", + "sha256": "84686d3cdb1f34e6111652956e65b90b01fd19487308a268022a66b14ea44878" + }, + { + "path": "jitmind/code_context/process.py", + "sha256": "ef9baf49a5bbe761fbffa1f60ef172b8f53c587eaffc13c4e179f2700462f77a" + }, + { + "path": "jitmind/code_context/retriever.py", + "sha256": "822dc84804992b8bd9fe7de62e535843585091350f8ff5a2311a05eaf51150f4" + }, + { + "path": "jitmind/code_context/service.py", + "sha256": "16daa9b16b9a55f130ca61204c96d40e8abb279709dbd4c36b81e4c99afbe873" + }, + { + "path": "jitmind/code_context/snapshot.py", + "sha256": "45a35d8a3455cde10816ff3e2feef3a7a21174bbec41326635d94394fec27272" + }, + { + "path": "jitmind/code_memory/anchors.py", + "sha256": "98d9715ad0faba844638859e5f49d8ccb58cd9a7e67c56c7376b81b08c24e31a" + }, + { + "path": "jitmind/code_memory/binding_models.py", + "sha256": "39c9e9676f58314e4e3d9073efd19f0072023440cdb6fc61f9d802fc9a9f203c" + }, + { + "path": "jitmind/code_memory/binding_storage.py", + "sha256": "5684d3bf4269703f87f4232f0d3fd32482d90be704170f57e1cc6670ab539c1f" + }, + { + "path": "jitmind/code_memory/revalidation.py", + "sha256": "ada66aa4f8d553d8930eb37aff00662544d7216bdfae941d643485ca8cdd92d5" + }, + { + "path": "jitmind/code_memory/open_work.py", + "sha256": "330b670fa75dfc33bd0013a4af3293fed415c29ef4f1df8df2f3295642e3a23c" + }, + { + "path": "jitmind/code_memory/work_storage.py", + "sha256": "f0a2775db9046f8b01007c946a586e9a4bd1ab3763fff5fef87df0d5555b8362" + }, + { + "path": "jitmind/code_memory/lesson_models.py", + "sha256": "447721bf5d7c7b972a4d3b1bda171c674a107d3aba50c90fa0033403f4c5b787" + }, + { + "path": "jitmind/code_memory/preflight.py", + "sha256": "7119d160107db1f0828fa31affc3aba18bcd716163df562a8c77c45cfebc6258" + } + ], + "assertions": [ + "After primary forget, ordinary fact/page/adapters/projected entries/receipt reconstruction exclude sentinel before any delete projection.", + "Both saved preflight receipts return empty payload; candidates empty; bound projection refused; deletion metadata then old events cannot revive content.", + "Purged lesson table empty; binding history, work history/receipts, delivery receipts and tombstones contain no sentinel.", + "Task remains open revision 1 with repo fallback. Raw authorized source remains exact and readable with original SHA-256, current generation and no memory sentinel." + ], + "evidence_limits": [ + "J04 raw code index consumes captured source, not fact bodies. MEMORY_ONLY_SENTINEL_83f91c is deliberately absent from source.", + "Raw authorized source remains readable after memory forget; forgetting memory never promises deletion of user source files.", + "J02 primary administrative/include_inactive history and backups may retain bodies. Assertions concern ordinary derived retrieval, receipt reconstruction, and content-free binding/work/delivery histories; no physical erasure claim.", + "Source generation, binding observation and outbox revision are separate; no atomic transaction across checkout and three databases is claimed.", + "Uses supplied J10 snapshot. Owner modules may advance; final supervisor must assemble and retest, configuring newer authority adapters if required. No automatic listener or raw-code outbox consumer is invented." + ] + }, + { + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[add_pending]", + "kind": "projection", + "status": "not_run", + "source": { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + }, + "fixtures": [ + { + "path": "tests/test_acceptance_edges.py", + "sha256": "266b393756709ea254eea591001880639c14f1a7e5a967e832db3ebd23f540d8" + } + ], + "fixture_description": "Same two actual composed parser chains as J29; memory-only sentinel first positively delivered from bound lesson and memory, then primary forget with deletion projection delayed.", + "modules_composed": [ + { + "path": "jitmind/storage/__init__.py", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e" + }, + { + "path": "jitmind/storage/models.py", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354" + }, + { + "path": "jitmind/storage/sqlite.py", + "sha256": "0fa4ca4acf0975184a8e3d40a9780e2671136e07a9ff5a9cc71030e792ee013c" + }, + { + "path": "jitmind/scope.py", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a" + }, + { + "path": "jitmind/code_context/__init__.py", + "sha256": "0bdda619ccabbb7f4894b106572885cf15b5c86232bfd6f333f6ca1b8b75f211" + }, + { + "path": "jitmind/code_context/models.py", + "sha256": "84686d3cdb1f34e6111652956e65b90b01fd19487308a268022a66b14ea44878" + }, + { + "path": "jitmind/code_context/process.py", + "sha256": "ef9baf49a5bbe761fbffa1f60ef172b8f53c587eaffc13c4e179f2700462f77a" + }, + { + "path": "jitmind/code_context/retriever.py", + "sha256": "822dc84804992b8bd9fe7de62e535843585091350f8ff5a2311a05eaf51150f4" + }, + { + "path": "jitmind/code_context/service.py", + "sha256": "16daa9b16b9a55f130ca61204c96d40e8abb279709dbd4c36b81e4c99afbe873" + }, + { + "path": "jitmind/code_context/snapshot.py", + "sha256": "45a35d8a3455cde10816ff3e2feef3a7a21174bbec41326635d94394fec27272" + }, + { + "path": "jitmind/code_memory/anchors.py", + "sha256": "98d9715ad0faba844638859e5f49d8ccb58cd9a7e67c56c7376b81b08c24e31a" + }, + { + "path": "jitmind/code_memory/binding_models.py", + "sha256": "39c9e9676f58314e4e3d9073efd19f0072023440cdb6fc61f9d802fc9a9f203c" + }, + { + "path": "jitmind/code_memory/binding_storage.py", + "sha256": "5684d3bf4269703f87f4232f0d3fd32482d90be704170f57e1cc6670ab539c1f" + }, + { + "path": "jitmind/code_memory/revalidation.py", + "sha256": "ada66aa4f8d553d8930eb37aff00662544d7216bdfae941d643485ca8cdd92d5" + }, + { + "path": "jitmind/code_memory/open_work.py", + "sha256": "330b670fa75dfc33bd0013a4af3293fed415c29ef4f1df8df2f3295642e3a23c" + }, + { + "path": "jitmind/code_memory/work_storage.py", + "sha256": "f0a2775db9046f8b01007c946a586e9a4bd1ab3763fff5fef87df0d5555b8362" + }, + { + "path": "jitmind/code_memory/lesson_models.py", + "sha256": "447721bf5d7c7b972a4d3b1bda171c674a107d3aba50c90fa0033403f4c5b787" + }, + { + "path": "jitmind/code_memory/preflight.py", + "sha256": "7119d160107db1f0828fa31affc3aba18bcd716163df562a8c77c45cfebc6258" + } + ], + "assertions": [ + "After primary forget, ordinary fact/page/adapters/projected entries/receipt reconstruction exclude sentinel before any delete projection.", + "Both saved preflight receipts return empty payload; candidates empty; bound projection refused; deletion metadata then old events cannot revive content.", + "Purged lesson table empty; binding history, work history/receipts, delivery receipts and tombstones contain no sentinel.", + "Task remains open revision 1 with repo fallback. Raw authorized source remains exact and readable with original SHA-256, current generation and no memory sentinel." + ], + "evidence_limits": [ + "J04 raw code index consumes captured source, not fact bodies. MEMORY_ONLY_SENTINEL_83f91c is deliberately absent from source.", + "Raw authorized source remains readable after memory forget; forgetting memory never promises deletion of user source files.", + "J02 primary administrative/include_inactive history and backups may retain bodies. Assertions concern ordinary derived retrieval, receipt reconstruction, and content-free binding/work/delivery histories; no physical erasure claim.", + "Source generation, binding observation and outbox revision are separate; no atomic transaction across checkout and three databases is claimed.", + "Uses supplied J10 snapshot. Owner modules may advance; final supervisor must assemble and retest, configuring newer authority adapters if required. No automatic listener or raw-code outbox consumer is invented." + ] + } + ] + } + ], + "scenario": "Forget and derived retention", + "required_assertion": "Current retrieval excludes forgotten content; obligation remains; retained metadata does not leak body" + } + ], + "collection": { + "status": "not_run", + "selectors": [ + "tests/test_acceptance_edges.py", + "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair", + "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[truncated]", + "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[duplicate_json_key]", + "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[nan]", + "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged", + "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals", + "tests/test_open_work_bindings.py::test_j16_j31_real_binding_move_disappearance_forget_keep_obligation" + ], + "exact_node_ids": [ + "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[advanced]", + "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[memory]", + "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[ttl_memory]", + "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[pages]", + "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[ttl_pages]", + "tests/test_acceptance_edges.py::test_real_sqlite_full_rolls_back_every_effect_then_accepts_same_request", + "tests/test_acceptance_edges.py::test_real_migration_exit_preserves_source_and_allows_retry[after_import_pages]", + "tests/test_acceptance_edges.py::test_real_migration_exit_preserves_source_and_allows_retry[before_import_commit]", + "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[projected]", + "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[add_pending]", + "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[before_transaction-False]", + "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_page_insert-False]", + "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_fact_insert-False]", + "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_outbox_receipt-False]", + "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_commit-True]", + "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[truncated]", + "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[duplicate_json_key]", + "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[nan]", + "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged[before_import]", + "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged[after_import_pages]", + "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged[before_import_commit]", + "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals[tool_result]", + "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals[tool_cancel]", + "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals[tool_timeout]", + "tests/test_open_work_bindings.py::test_j16_j31_real_binding_move_disappearance_forget_keep_obligation[# code removed\\n-orphaned_confirmed]", + "tests/test_open_work_bindings.py::test_j16_j31_real_binding_move_disappearance_forget_keep_obligation[invalid syntax ???\\n-unknown]" + ], + "unique_node_count": 26 + }, + "all_copied_dependencies": [ + { + "owner": "J02", + "path": "jitmind/storage/__init__.py", + "snapshot_sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e", + "sha256": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "ff45e7aa0f7985ea7c11adc89e4889bd91b50358907372648fe1eb1766f0313e", + "owner_differs_at_review": false + }, + { + "owner": "J02", + "path": "jitmind/storage/migration.py", + "snapshot_sha256": "966fa1107445aa332122ce9a49844cb61d76ebad85780e87d81ffb71e7ee9b23", + "sha256": "966fa1107445aa332122ce9a49844cb61d76ebad85780e87d81ffb71e7ee9b23", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "26cf90e0911fe6bfae875ef31c50f0b8cbf94b05c2d5a694cd5408622533a15a", + "owner_differs_at_review": true + }, + { + "owner": "J02", + "path": "jitmind/storage/models.py", + "snapshot_sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354", + "sha256": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "db77d142af7c8d8a1c20307be9341b135f1e54e6b3c845e7c8a3da82f9588354", + "owner_differs_at_review": false + }, + { + "owner": "J02", + "path": "jitmind/storage/sqlite.py", + "snapshot_sha256": "a5b890e4fff708c476b8c481437b6d8abbe281abbc5c12857806aa63ddce3514", + "sha256": "a5b890e4fff708c476b8c481437b6d8abbe281abbc5c12857806aa63ddce3514", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "1201fbe05afc29b6452216fb21482cb1b97bb54557f15e7780a639cddcfd55eb", + "owner_differs_at_review": true + }, + { + "owner": "J02", + "path": "jitmind/agents/memory_agent.py", + "snapshot_sha256": "1c665b46b527c24e731850b66181c536e630bdc6cdf461523d32b5c5e6801cd2", + "sha256": "1c665b46b527c24e731850b66181c536e630bdc6cdf461523d32b5c5e6801cd2", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "1c665b46b527c24e731850b66181c536e630bdc6cdf461523d32b5c5e6801cd2", + "owner_differs_at_review": false + }, + { + "owner": "J02", + "path": "tests/test_durable_migration.py", + "snapshot_sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60", + "sha256": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "fd8d059cf599243df5e081203dcab1a6b0eb15cec35c2b2cdfa583f00dfa7c60", + "owner_differs_at_review": false + }, + { + "owner": "J02", + "path": "tests/test_durable_remediation.py", + "snapshot_sha256": "9d71f19f47da8d05775382fd89f1aed918e739b81adc09fac1575aae1e6d9383", + "sha256": "9d71f19f47da8d05775382fd89f1aed918e739b81adc09fac1575aae1e6d9383", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "1e8acdf24b9b98b02d07ee335e10d8e46403b0d52a134501d64b306afe409f47", + "owner_differs_at_review": true + }, + { + "owner": "J02", + "path": "tests/test_durable_storage.py", + "snapshot_sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00", + "sha256": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "ea08af202b8806b8d96fd20a8e5508dca6ef76d3abef8ab37479e6e8f684cc00", + "owner_differs_at_review": false + }, + { + "owner": "J03", + "path": "jitmind/scope.py", + "snapshot_sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a", + "sha256": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "2762e46016b2f4e1a6ea6d95208954724f1cfef8e4e59812a19fc06e97bdf34a", + "owner_differs_at_review": false + }, + { + "owner": "J03", + "path": "jitmind/scoped_research.py", + "snapshot_sha256": "5e6428640b58dbd63fc11d69b8417331fc2e995699447699ff2e655ff1bf4d20", + "sha256": "5e6428640b58dbd63fc11d69b8417331fc2e995699447699ff2e655ff1bf4d20", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "5e6428640b58dbd63fc11d69b8417331fc2e995699447699ff2e655ff1bf4d20", + "owner_differs_at_review": false + }, + { + "owner": "J03", + "path": "jitmind/scoped_temporal.py", + "snapshot_sha256": "cb463c776e743af32c55512a7cdb921f1134a9909e616af235c851d44db76326", + "sha256": "cb463c776e743af32c55512a7cdb921f1134a9909e616af235c851d44db76326", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "cb463c776e743af32c55512a7cdb921f1134a9909e616af235c851d44db76326", + "owner_differs_at_review": false + }, + { + "owner": "J03", + "path": "tests/test_scoped_authority.py", + "snapshot_sha256": "c62b826e6c8b562187dc6f0af7ccfa1961a7c9343d5e1570629f65a1c77e23b2", + "sha256": "c62b826e6c8b562187dc6f0af7ccfa1961a7c9343d5e1570629f65a1c77e23b2", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "c62b826e6c8b562187dc6f0af7ccfa1961a7c9343d5e1570629f65a1c77e23b2", + "owner_differs_at_review": false + }, + { + "owner": "J03", + "path": "tests/test_scoped_durable.py", + "snapshot_sha256": "03b01bf9b54bf379bb30b1ddabf9cddf62a619bb30f6086f48a2c7c864d64282", + "sha256": "03b01bf9b54bf379bb30b1ddabf9cddf62a619bb30f6086f48a2c7c864d64282", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "03b01bf9b54bf379bb30b1ddabf9cddf62a619bb30f6086f48a2c7c864d64282", + "owner_differs_at_review": false + }, + { + "owner": "J03", + "path": "tests/test_scoped_research.py", + "snapshot_sha256": "428d4c17df53220a76f4e38c1be91f50d025418063613eac58dc59ec5bd17108", + "sha256": "428d4c17df53220a76f4e38c1be91f50d025418063613eac58dc59ec5bd17108", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "428d4c17df53220a76f4e38c1be91f50d025418063613eac58dc59ec5bd17108", + "owner_differs_at_review": false + }, + { + "owner": "J03", + "path": "tests/test_scoped_temporal.py", + "snapshot_sha256": "e335712d09b7ecdc5e466b63008fb717dab4b9b772623e6b51df840ec71eb66e", + "sha256": "e335712d09b7ecdc5e466b63008fb717dab4b9b772623e6b51df840ec71eb66e", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "e335712d09b7ecdc5e466b63008fb717dab4b9b772623e6b51df840ec71eb66e", + "owner_differs_at_review": false + }, + { + "owner": "J04", + "path": "jitmind/code_context/__init__.py", + "snapshot_sha256": "0bdda619ccabbb7f4894b106572885cf15b5c86232bfd6f333f6ca1b8b75f211", + "sha256": "0bdda619ccabbb7f4894b106572885cf15b5c86232bfd6f333f6ca1b8b75f211", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "0bdda619ccabbb7f4894b106572885cf15b5c86232bfd6f333f6ca1b8b75f211", + "owner_differs_at_review": false + }, + { + "owner": "J04", + "path": "jitmind/code_context/models.py", + "snapshot_sha256": "84686d3cdb1f34e6111652956e65b90b01fd19487308a268022a66b14ea44878", + "sha256": "84686d3cdb1f34e6111652956e65b90b01fd19487308a268022a66b14ea44878", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "84686d3cdb1f34e6111652956e65b90b01fd19487308a268022a66b14ea44878", + "owner_differs_at_review": false + }, + { + "owner": "J04", + "path": "jitmind/code_context/process.py", + "snapshot_sha256": "ef9baf49a5bbe761fbffa1f60ef172b8f53c587eaffc13c4e179f2700462f77a", + "sha256": "ef9baf49a5bbe761fbffa1f60ef172b8f53c587eaffc13c4e179f2700462f77a", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "ef9baf49a5bbe761fbffa1f60ef172b8f53c587eaffc13c4e179f2700462f77a", + "owner_differs_at_review": false + }, + { + "owner": "J04", + "path": "jitmind/code_context/retriever.py", + "snapshot_sha256": "822dc84804992b8bd9fe7de62e535843585091350f8ff5a2311a05eaf51150f4", + "sha256": "822dc84804992b8bd9fe7de62e535843585091350f8ff5a2311a05eaf51150f4", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "822dc84804992b8bd9fe7de62e535843585091350f8ff5a2311a05eaf51150f4", + "owner_differs_at_review": false + }, + { + "owner": "J04", + "path": "jitmind/code_context/service.py", + "snapshot_sha256": "16daa9b16b9a55f130ca61204c96d40e8abb279709dbd4c36b81e4c99afbe873", + "sha256": "16daa9b16b9a55f130ca61204c96d40e8abb279709dbd4c36b81e4c99afbe873", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "16daa9b16b9a55f130ca61204c96d40e8abb279709dbd4c36b81e4c99afbe873", + "owner_differs_at_review": false + }, + { + "owner": "J04", + "path": "jitmind/code_context/snapshot.py", + "snapshot_sha256": "45a35d8a3455cde10816ff3e2feef3a7a21174bbec41326635d94394fec27272", + "sha256": "45a35d8a3455cde10816ff3e2feef3a7a21174bbec41326635d94394fec27272", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "45a35d8a3455cde10816ff3e2feef3a7a21174bbec41326635d94394fec27272", + "owner_differs_at_review": false + }, + { + "owner": "J04", + "path": "tests/test_code_context.py", + "snapshot_sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09", + "sha256": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "ecf614237e36094ddfcdebe37ec45af1ce92d3df3e433d89703435590ac3fa09", + "owner_differs_at_review": false + }, + { + "owner": "J04", + "path": "tests/test_code_context_process.py", + "snapshot_sha256": "67152ebf0b781d45feba30c232625d7bbb93ded937e2c87ea4811af5b06ffd81", + "sha256": "67152ebf0b781d45feba30c232625d7bbb93ded937e2c87ea4811af5b06ffd81", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "67152ebf0b781d45feba30c232625d7bbb93ded937e2c87ea4811af5b06ffd81", + "owner_differs_at_review": false + }, + { + "owner": "J05", + "path": "jitmind/code_memory/__init__.py", + "snapshot_sha256": "79312b29319783d400b5ce459dabe487e7b7cd2f2fbe0d3594334e2a9b377563", + "sha256": "79312b29319783d400b5ce459dabe487e7b7cd2f2fbe0d3594334e2a9b377563", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "79312b29319783d400b5ce459dabe487e7b7cd2f2fbe0d3594334e2a9b377563", + "owner_differs_at_review": false + }, + { + "owner": "J05", + "path": "jitmind/code_memory/anchors.py", + "snapshot_sha256": "60a778774b250ff761e4cef622ed8f45e5e358079a2c0646a3f718495fe5e2c6", + "sha256": "60a778774b250ff761e4cef622ed8f45e5e358079a2c0646a3f718495fe5e2c6", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "98d9715ad0faba844638859e5f49d8ccb58cd9a7e67c56c7376b81b08c24e31a", + "owner_differs_at_review": true + }, + { + "owner": "J05", + "path": "jitmind/code_memory/binding_models.py", + "snapshot_sha256": "39c9e9676f58314e4e3d9073efd19f0072023440cdb6fc61f9d802fc9a9f203c", + "sha256": "39c9e9676f58314e4e3d9073efd19f0072023440cdb6fc61f9d802fc9a9f203c", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "39c9e9676f58314e4e3d9073efd19f0072023440cdb6fc61f9d802fc9a9f203c", + "owner_differs_at_review": false + }, + { + "owner": "J05", + "path": "jitmind/code_memory/binding_storage.py", + "snapshot_sha256": "aacd73710c97f097f1c5934db142e20bd6ee8d6d6f8ae13d87ae3fbfdd17d9d7", + "sha256": "aacd73710c97f097f1c5934db142e20bd6ee8d6d6f8ae13d87ae3fbfdd17d9d7", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "5684d3bf4269703f87f4232f0d3fd32482d90be704170f57e1cc6670ab539c1f", + "owner_differs_at_review": true + }, + { + "owner": "J05", + "path": "jitmind/code_memory/revalidation.py", + "snapshot_sha256": "ada66aa4f8d553d8930eb37aff00662544d7216bdfae941d643485ca8cdd92d5", + "sha256": "ada66aa4f8d553d8930eb37aff00662544d7216bdfae941d643485ca8cdd92d5", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "ada66aa4f8d553d8930eb37aff00662544d7216bdfae941d643485ca8cdd92d5", + "owner_differs_at_review": false + }, + { + "owner": "J05", + "path": "tests/test_code_anchors.py", + "snapshot_sha256": "cc61bb85ed1b3e528941bfbde1241d0c720e674b1f47cafa4f89831b0eb670b1", + "sha256": "cc61bb85ed1b3e528941bfbde1241d0c720e674b1f47cafa4f89831b0eb670b1", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "8255edab92b341ef41dd5556a68f3f2be8e52b0e940e7e9e98891f2f5cced3cb", + "owner_differs_at_review": true + }, + { + "owner": "J06", + "path": "jitmind/code_memory/open_work.py", + "snapshot_sha256": "330b670fa75dfc33bd0013a4af3293fed415c29ef4f1df8df2f3295642e3a23c", + "sha256": "330b670fa75dfc33bd0013a4af3293fed415c29ef4f1df8df2f3295642e3a23c", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "330b670fa75dfc33bd0013a4af3293fed415c29ef4f1df8df2f3295642e3a23c", + "owner_differs_at_review": false + }, + { + "owner": "J06", + "path": "jitmind/code_memory/work_storage.py", + "snapshot_sha256": "2f06be2fbecfc979bc60d39d5cee6fc6a629d8a11940fd8d6f03d71595a28892", + "sha256": "2f06be2fbecfc979bc60d39d5cee6fc6a629d8a11940fd8d6f03d71595a28892", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "2f06be2fbecfc979bc60d39d5cee6fc6a629d8a11940fd8d6f03d71595a28892", + "owner_differs_at_review": false + }, + { + "owner": "J06", + "path": "jitmind/code_memory/lesson_models.py", + "snapshot_sha256": "17c1547051fb5338fd357615d8c673fc2ccb62d2b23e94110af8b7bd201cf4ff", + "sha256": "17c1547051fb5338fd357615d8c673fc2ccb62d2b23e94110af8b7bd201cf4ff", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "01b070099067132dbb5b9269a2657dcf1f22141d67c4cef5c32717f049ea93c8", + "owner_differs_at_review": true + }, + { + "owner": "J06", + "path": "jitmind/code_memory/preflight.py", + "snapshot_sha256": "dadb006f88ce520cfaf574259f1a577ca544b3d387486ff4e1b75e1c028c3bca", + "sha256": "dadb006f88ce520cfaf574259f1a577ca544b3d387486ff4e1b75e1c028c3bca", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "7119d160107db1f0828fa31affc3aba18bcd716163df562a8c77c45cfebc6258", + "owner_differs_at_review": true + }, + { + "owner": "J06", + "path": "tests/test_open_work.py", + "snapshot_sha256": "b0b192f5bd1ecfe39811346eb4ce912a85a15d00c446e31e8f2a01493e957597", + "sha256": "b0b192f5bd1ecfe39811346eb4ce912a85a15d00c446e31e8f2a01493e957597", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "b0b192f5bd1ecfe39811346eb4ce912a85a15d00c446e31e8f2a01493e957597", + "owner_differs_at_review": false + }, + { + "owner": "J06", + "path": "tests/test_open_work_bindings.py", + "snapshot_sha256": "96d38b318fe0e1dc8fa695c60ed4f2f2e50f6f60c2b02d168583845448cad405", + "sha256": "96d38b318fe0e1dc8fa695c60ed4f2f2e50f6f60c2b02d168583845448cad405", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "8c3f5e7c44588c7bbb4478e3c9b93fdedfbecbdf7496e4c1e4a41ec0fb4576e3", + "owner_differs_at_review": true + }, + { + "owner": "J06", + "path": "tests/test_preflight.py", + "snapshot_sha256": "3b5251f8dcaba3de82112d7760e162263fea96c7a59af70d6a32759c41324f26", + "sha256": "3b5251f8dcaba3de82112d7760e162263fea96c7a59af70d6a32759c41324f26", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "3b5251f8dcaba3de82112d7760e162263fea96c7a59af70d6a32759c41324f26", + "owner_differs_at_review": false + }, + { + "owner": "J07", + "path": ".gitignore", + "snapshot_sha256": "b5febaf8d92c40efc842d22cc481a0d3d9d732ca92891646e3e6b8a569261d68", + "sha256": "b5febaf8d92c40efc842d22cc481a0d3d9d732ca92891646e3e6b8a569261d68", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "b5febaf8d92c40efc842d22cc481a0d3d9d732ca92891646e3e6b8a569261d68", + "owner_differs_at_review": false + }, + { + "owner": "J07", + "path": "jitmind/checkpoints/__init__.py", + "snapshot_sha256": "4d54e3f91114b474c337fbd460653e46210c2c668fcb557c231560e8477c0017", + "sha256": "4d54e3f91114b474c337fbd460653e46210c2c668fcb557c231560e8477c0017", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "4d54e3f91114b474c337fbd460653e46210c2c668fcb557c231560e8477c0017", + "owner_differs_at_review": false + }, + { + "owner": "J07", + "path": "jitmind/checkpoints/ledger.py", + "snapshot_sha256": "c3c5b6805d6959a86ec242a6153818a1b4b2b95cd3789493d59f4ed23a790910", + "sha256": "c3c5b6805d6959a86ec242a6153818a1b4b2b95cd3789493d59f4ed23a790910", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "c3c5b6805d6959a86ec242a6153818a1b4b2b95cd3789493d59f4ed23a790910", + "owner_differs_at_review": false + }, + { + "owner": "J07", + "path": "jitmind/utils/checkpoint.py", + "snapshot_sha256": "84e108f0d5c1b87061425f5218a83b4f47b9cd2b3f805022a326bd059773c3ba", + "sha256": "84e108f0d5c1b87061425f5218a83b4f47b9cd2b3f805022a326bd059773c3ba", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "84e108f0d5c1b87061425f5218a83b4f47b9cd2b3f805022a326bd059773c3ba", + "owner_differs_at_review": false + }, + { + "owner": "J07", + "path": "tests/test_checkpoint_ledger.py", + "snapshot_sha256": "63d3df7dbc975322d1f201b497b70f7bef7829621dbe6fad2c71165a6f603a5c", + "sha256": "63d3df7dbc975322d1f201b497b70f7bef7829621dbe6fad2c71165a6f603a5c", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "63d3df7dbc975322d1f201b497b70f7bef7829621dbe6fad2c71165a6f603a5c", + "owner_differs_at_review": false + }, + { + "owner": "J07", + "path": "tests/test_checkpoint_manager.py", + "snapshot_sha256": "a85fd91122e6dbde9ddf7b7e5422af622d828192540ef3241d87ef51923f9b65", + "sha256": "a85fd91122e6dbde9ddf7b7e5422af622d828192540ef3241d87ef51923f9b65", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "a85fd91122e6dbde9ddf7b7e5422af622d828192540ef3241d87ef51923f9b65", + "owner_differs_at_review": false + }, + { + "owner": "J07", + "path": "tests/test_checkpoint_scope.py", + "snapshot_sha256": "5651f767a662c021f31022eef609cf2edbc39644c34d423c424a2d9e986f2fc8", + "sha256": "5651f767a662c021f31022eef609cf2edbc39644c34d423c424a2d9e986f2fc8", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "5651f767a662c021f31022eef609cf2edbc39644c34d423c424a2d9e986f2fc8", + "owner_differs_at_review": false + }, + { + "owner": "J09", + "path": "jitmind/temporal_history.py", + "snapshot_sha256": "87b2cc854522cd59f40b90eca48470c07511fb0bc5058dd8ea5e5ffbe0c04e33", + "sha256": "87b2cc854522cd59f40b90eca48470c07511fb0bc5058dd8ea5e5ffbe0c04e33", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "87b2cc854522cd59f40b90eca48470c07511fb0bc5058dd8ea5e5ffbe0c04e33", + "owner_differs_at_review": false + }, + { + "owner": "J09", + "path": "tests/test_temporal_history_sqlite.py", + "snapshot_sha256": "d70e74954ca0d86f2ee8400cf0e4e15efb08bc88b6230c9caaef75bc5c97ac23", + "sha256": "d70e74954ca0d86f2ee8400cf0e4e15efb08bc88b6230c9caaef75bc5c97ac23", + "contribution": false, + "refreshed_by_J10": false, + "owner_sha256_at_review": "d70e74954ca0d86f2ee8400cf0e4e15efb08bc88b6230c9caaef75bc5c97ac23", + "owner_differs_at_review": false + } + ], + "adapter": { + "owner": "J04", + "location": "delivery/J04/adapters/graft relative to orchestration ROOT; frozen copy used for J08 staged tests", + "copied": true, + "runtime_configuration": "JITMIND_TEST_GRAFT_ADAPTER; fallback to repository adapters/graft. JITMIND_TEST_NODE optional, otherwise PATH node.", + "source_digests": [ + { + "path": "LICENSE", + "sha256": "d671f52607b002fad21b4a09cf3a7bd6c507e95abfd90040c775fdd4d83e3d7c" + }, + { + "path": "PROVENANCE.md", + "sha256": "ecf27eddc3450bfdb9e447f3b7eef81ed55bc8eb231df50fa9ba4084e9e3f545" + }, + { + "path": "THIRD_PARTY_NOTICES.txt", + "sha256": "baa6d2158b7af0ca465bdf91261f796f826fdc1fdf168cc19dca3870c78e9889" + }, + { + "path": "adapter-requirements.json", + "sha256": "b56a819dd77bae14d2caa4a69f8236201d98d85e43de55ea55cafae3120a5115" + }, + { + "path": "donor-audit.json", + "sha256": "7423fa884bd1fc8843b623fb1c980db9b0e50431f651d873f8a4ba9c3b7b88e7" + }, + { + "path": "extract.mjs", + "sha256": "5eef6876d53933a7d54d2dae7ab99e793543fc2122573fbae1d53a20a9dc7914" + }, + { + "path": "package-lock.json", + "sha256": "87a7b5cca2ee883795063104eb4298aa8a0c841ac3d8a9b44ecc8d0adec3bef8" + }, + { + "path": "package.json", + "sha256": "ae646f9eb985c24480383f2264322a341ba94e82c556f4c8318a1bb060293145" + }, + { + "path": "runner.mjs", + "sha256": "f7f35fc019f64a169a0095a7c6f5013046cc7e79961b3a3696166b1c46bf6946" + } + ], + "installed_package_manifests": [ + { + "path": "node_modules/tree-sitter/package.json", + "sha256": "8cca5044dd3381dbf958dea36ef6d9f81f379886d32ef845919cf07679c6e42c", + "version": "0.21.1" + }, + { + "path": "node_modules/tree-sitter-python/package.json", + "sha256": "7743dc38552ccb2309f8ee7b5d56b0292ba7b8760ce35509d3d729f4f70a50d7", + "version": "0.21.0" + } + ] + }, + "remaining_limits": [ + "The frozen temporal oracle was read; this supplement adds no temporal acceptance claims.", + "No product modules, copied tests or dependency manifests are J10 contributions.", + "Static type checker not installed; no installs performed." + ], + "integration_review": { + "contribution": "J10 fixtures and mappings; J08 binding-authority configuration and final source attestation", + "historical_dependency_inventory": "all_copied_dependencies describes the original J10 run, not the final composite", + "source_owners": { + "J01": "commit b3fe1d51a4cb98ea2ddce7e94fd3f732010b623d", + "J02": "delivery/J02", + "J03": "delivery/J03", + "J04": "delivery/J04", + "J05": "delivery/J05", + "J06": "worktrees/J06", + "J07": "worktrees/J07", + "J09": "worktrees/J09" + }, + "release_qualified": false + } +} diff --git a/docs/engineering/evidence-kind-normalization.json b/docs/engineering/evidence-kind-normalization.json new file mode 100644 index 0000000..6477d5f --- /dev/null +++ b/docs/engineering/evidence-kind-normalization.json @@ -0,0 +1,453 @@ +{ + "notice": "Metadata normalization to the existing fixed runner vocabulary; node identities, assertions, stage requirements and not_run statuses are unchanged. Specific execution descriptions remain attached to each node.", + "changes": [ + { + "file": "acceptance-map.json", + "case": "J02", + "stage": "J-01", + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[advanced]", + "original_kind": "injected_filesystem_publication_fault", + "canonical_kind": "injected_storage_fault" + }, + { + "file": "acceptance-map.json", + "case": "J02", + "stage": "J-01", + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[memory]", + "original_kind": "injected_filesystem_publication_fault", + "canonical_kind": "injected_storage_fault" + }, + { + "file": "acceptance-map.json", + "case": "J02", + "stage": "J-01", + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[ttl_memory]", + "original_kind": "injected_filesystem_publication_fault", + "canonical_kind": "injected_storage_fault" + }, + { + "file": "acceptance-map.json", + "case": "J02", + "stage": "J-01", + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[pages]", + "original_kind": "injected_filesystem_publication_fault", + "canonical_kind": "injected_storage_fault" + }, + { + "file": "acceptance-map.json", + "case": "J02", + "stage": "J-01", + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[ttl_pages]", + "original_kind": "injected_filesystem_publication_fault", + "canonical_kind": "injected_storage_fault" + }, + { + "file": "acceptance-map.json", + "case": "J02", + "stage": "J-02", + "node_id": "tests/test_acceptance_edges.py::test_real_sqlite_full_rolls_back_every_effect_then_accepts_same_request", + "original_kind": "real_sqlite_capacity", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-map.json", + "case": "J04", + "stage": "J-02", + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[before_transaction-False]", + "original_kind": "real_process_exit", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-map.json", + "case": "J04", + "stage": "J-02", + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_page_insert-False]", + "original_kind": "real_process_exit", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-map.json", + "case": "J04", + "stage": "J-02", + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_fact_insert-False]", + "original_kind": "real_process_exit", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-map.json", + "case": "J04", + "stage": "J-02", + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_outbox_receipt-False]", + "original_kind": "real_process_exit", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-map.json", + "case": "J04", + "stage": "J-02", + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_commit-True]", + "original_kind": "real_process_exit", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-map.json", + "case": "J16", + "stage": "J-06", + "node_id": "tests/test_open_work_bindings.py::test_j16_j31_real_binding_move_disappearance_forget_keep_obligation[# code removed\\n-orphaned_confirmed]", + "original_kind": "real_parser_binding_open_work", + "canonical_kind": "real_parser" + }, + { + "file": "acceptance-map.json", + "case": "J16", + "stage": "J-06", + "node_id": "tests/test_open_work_bindings.py::test_j16_j31_real_binding_move_disappearance_forget_keep_obligation[invalid syntax ???\\n-unknown]", + "original_kind": "real_parser_binding_open_work", + "canonical_kind": "real_parser" + }, + { + "file": "acceptance-map.json", + "case": "J22", + "stage": "J-07", + "node_id": "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals[tool_result]", + "original_kind": "real_checkpoint_ledger", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-map.json", + "case": "J22", + "stage": "J-07", + "node_id": "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals[tool_cancel]", + "original_kind": "real_checkpoint_ledger", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-map.json", + "case": "J22", + "stage": "J-07", + "node_id": "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals[tool_timeout]", + "original_kind": "real_checkpoint_ledger", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-map.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[truncated]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-map.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[duplicate_json_key]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-map.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[nan]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-map.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged[before_import]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-map.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged[after_import_pages]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-map.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged[before_import_commit]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-map.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_acceptance_edges.py::test_real_migration_exit_preserves_source_and_allows_retry[after_import_pages]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-map.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_acceptance_edges.py::test_real_migration_exit_preserves_source_and_allows_retry[before_import_commit]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-map.json", + "case": "J29", + "stage": "J-04", + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[projected]", + "original_kind": "real_parser_binding_projection_composition", + "canonical_kind": "projection" + }, + { + "file": "acceptance-map.json", + "case": "J29", + "stage": "J-04", + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[add_pending]", + "original_kind": "real_parser_binding_projection_composition", + "canonical_kind": "projection" + }, + { + "file": "acceptance-map.json", + "case": "J31", + "stage": "J-04", + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[projected]", + "original_kind": "real_parser_binding_preflight_forget_composition", + "canonical_kind": "projection" + }, + { + "file": "acceptance-map.json", + "case": "J31", + "stage": "J-04", + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[add_pending]", + "original_kind": "real_parser_binding_preflight_forget_composition", + "canonical_kind": "projection" + }, + { + "file": "acceptance-supplement.json", + "case": "J02", + "stage": "J-01", + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[advanced]", + "original_kind": "injected_filesystem_publication_fault", + "canonical_kind": "injected_storage_fault" + }, + { + "file": "acceptance-supplement.json", + "case": "J02", + "stage": "J-01", + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[memory]", + "original_kind": "injected_filesystem_publication_fault", + "canonical_kind": "injected_storage_fault" + }, + { + "file": "acceptance-supplement.json", + "case": "J02", + "stage": "J-01", + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[ttl_memory]", + "original_kind": "injected_filesystem_publication_fault", + "canonical_kind": "injected_storage_fault" + }, + { + "file": "acceptance-supplement.json", + "case": "J02", + "stage": "J-01", + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[pages]", + "original_kind": "injected_filesystem_publication_fault", + "canonical_kind": "injected_storage_fault" + }, + { + "file": "acceptance-supplement.json", + "case": "J02", + "stage": "J-01", + "node_id": "tests/test_acceptance_edges.py::test_enospc_at_legacy_publication_preserves_ack_and_bytes[ttl_pages]", + "original_kind": "injected_filesystem_publication_fault", + "canonical_kind": "injected_storage_fault" + }, + { + "file": "acceptance-supplement.json", + "case": "J02", + "stage": "J-02", + "node_id": "tests/test_acceptance_edges.py::test_real_sqlite_full_rolls_back_every_effect_then_accepts_same_request", + "original_kind": "real_sqlite_capacity", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-supplement.json", + "case": "J04", + "stage": "J-02", + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[before_transaction-False]", + "original_kind": "real_process_exit", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-supplement.json", + "case": "J04", + "stage": "J-02", + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_page_insert-False]", + "original_kind": "real_process_exit", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-supplement.json", + "case": "J04", + "stage": "J-02", + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_fact_insert-False]", + "original_kind": "real_process_exit", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-supplement.json", + "case": "J04", + "stage": "J-02", + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_outbox_receipt-False]", + "original_kind": "real_process_exit", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-supplement.json", + "case": "J04", + "stage": "J-02", + "node_id": "tests/test_durable_storage.py::test_process_crash_fresh_reader_sees_zero_or_whole_pair[after_commit-True]", + "original_kind": "real_process_exit", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-supplement.json", + "case": "J16", + "stage": "J-06", + "node_id": "tests/test_open_work_bindings.py::test_j16_j31_real_binding_move_disappearance_forget_keep_obligation[# code removed\\n-orphaned_confirmed]", + "original_kind": "real_parser_binding_open_work", + "canonical_kind": "real_parser" + }, + { + "file": "acceptance-supplement.json", + "case": "J16", + "stage": "J-06", + "node_id": "tests/test_open_work_bindings.py::test_j16_j31_real_binding_move_disappearance_forget_keep_obligation[invalid syntax ???\\n-unknown]", + "original_kind": "real_parser_binding_open_work", + "canonical_kind": "real_parser" + }, + { + "file": "acceptance-supplement.json", + "case": "J22", + "stage": "J-07", + "node_id": "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals[tool_result]", + "original_kind": "real_checkpoint_ledger", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-supplement.json", + "case": "J22", + "stage": "J-07", + "node_id": "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals[tool_cancel]", + "original_kind": "real_checkpoint_ledger", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-supplement.json", + "case": "J22", + "stage": "J-07", + "node_id": "tests/test_checkpoint_ledger.py::test_tool_boundaries_and_explicit_terminals[tool_timeout]", + "original_kind": "real_checkpoint_ledger", + "canonical_kind": "real_storage" + }, + { + "file": "acceptance-supplement.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[truncated]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-supplement.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[duplicate_json_key]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-supplement.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_durable_migration.py::test_invalid_migration_refuses_without_changes[nan]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-supplement.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged[before_import]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-supplement.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged[after_import_pages]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-supplement.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_durable_migration.py::test_staged_failure_leaves_legacy_and_candidate_unchanged[before_import_commit]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-supplement.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_acceptance_edges.py::test_real_migration_exit_preserves_source_and_allows_retry[after_import_pages]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-supplement.json", + "case": "J25", + "stage": "J-02", + "node_id": "tests/test_acceptance_edges.py::test_real_migration_exit_preserves_source_and_allows_retry[before_import_commit]", + "original_kind": "real_migration_with_fault_boundaries", + "canonical_kind": "migration" + }, + { + "file": "acceptance-supplement.json", + "case": "J29", + "stage": "J-04", + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[projected]", + "original_kind": "real_parser_binding_projection_composition", + "canonical_kind": "projection" + }, + { + "file": "acceptance-supplement.json", + "case": "J29", + "stage": "J-04", + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[add_pending]", + "original_kind": "real_parser_binding_projection_composition", + "canonical_kind": "projection" + }, + { + "file": "acceptance-supplement.json", + "case": "J31", + "stage": "J-04", + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[projected]", + "original_kind": "real_parser_binding_preflight_forget_composition", + "canonical_kind": "projection" + }, + { + "file": "acceptance-supplement.json", + "case": "J31", + "stage": "J-04", + "node_id": "tests/test_acceptance_edges.py::test_real_parser_binding_projection_redelivery_and_primary_forget[add_pending]", + "original_kind": "real_parser_binding_preflight_forget_composition", + "canonical_kind": "projection" + } + ] +} diff --git a/docs/engineering/qualification-review.md b/docs/engineering/qualification-review.md new file mode 100644 index 0000000..a53299f --- /dev/null +++ b/docs/engineering/qualification-review.md @@ -0,0 +1,62 @@ +# Final qualification contract alignment + +This is a source-review record, not a passing execution report. The qualification +runner records actual results separately against a committed candidate and wheel. + +## Preserved requirements + +The map retains 32 cases, all 41 required stages, 103 stage-node associations and +97 unique identities (96 pytest nodes and one separately executed installed smoke). +Original case text, node identities, parameter variants and handoff/oracle hashes +remain. The timing-oracle corrections and three added boundary nodes are recorded +below; they are not described as unchanged assertions. Source-controlled statuses +remain `not_run`. + +## Metadata correction + +The J10 supplement used descriptive evidence labels outside the runner's existing +fixed vocabulary. `evidence-kind-normalization.json` records each translation. +Specific fixture descriptions, assertions and limitations remain with the nodes. +The two reused compound workflow nodes now have one consistent `projection` kind. +No runner allowlist, expected-test denominator or release blocker was weakened. + +## Capability contract correction + +The missing-node smoke now requires the actual typed `Unavailable` exception with +`node_executable_unavailable`. New negative controls reject ordinary filesystem +and attribute errors, unrelated typed failures and the obsolete generic code. +The real installed smoke still runs outside the checkout with Node and optional +provider packages absent; checkout helper tests cannot substitute for it. + + +## Native SQLite capacity evidence on Python 3.10 + +The real max_page_count exhaustion fixture now records numeric SQLite error +metadata when exposed and otherwise requires the exact native OperationalError +message. Both paths still require a real SQLite capacity limit, zero committed +effects, a clean integrity check, and successful same-request retry after raising +the limit. No error injection substitutes for the real engine capacity failure. + +## Reviewed CI fixture corrections + +The quota fixture seeds 256 receipt rows in one bounded transaction instead of +256 autocommit fsyncs. The binding IMMEDIATE-lock compatibility control uses a +functional deadline but retains its delivered result and 300 ms assertion. The +EXCLUSIVE control retains the production deadline and deferred-result assertion. +No production source or existing assertion was changed. The acceptance map +refreshes only the two affected fixture file identities; all 32 cases, 41 stages, +node identities and not-run specification statuses remain unchanged. New +qualification evidence must be generated against this committed snapshot. + +## Explicit primary-read deadline oracle correction + +The facts/IMMEDIATE positive lock control now tests delivered content with a +bounded functional budget rather than claiming successful fsync delivery inside +150 ms on shared CI. The three incompatible-lock controls still use the original +production policy and 150 ms ceiling, with empty deferred payloads. Three new +real-storage cases verify before/at/after the default deadline using a controlled +monotonic clock. All original node identities and stages remain, and J20 now +requires the three additional boundary identities. The resulting inventory is +103 stage-node associations and 97 unique identities (96 pytest plus installed +smoke). This is a reviewed test-oracle correction, not unchanged assertions or a +production latency benchmark. Production code is unchanged. diff --git a/docs/release-qualification.md b/docs/release-qualification.md new file mode 100644 index 0000000..6dddb1e --- /dev/null +++ b/docs/release-qualification.md @@ -0,0 +1,453 @@ +# Release qualification and recovery + +This is a reproducible **checks gate**, not a production certificate. The J08 +worktree contains the baseline library plus qualification tooling and reviewed +J10 test contributions. Final owner sources were exercised in a frozen temporary +composite for mapping review; the supervisor must assemble the actual candidate +and rerun qualification before scenarios receive gate credit. +No peer-worktree result, mocked tool test, or generic pytest exit code establishes +acceptance of the combined candidate. + +The original audit baseline is Git +`dc6877e85b4048f81ce801d8700dfb934277e5f3`: Python 3.12.12 on macOS arm64, +38 tests passing with three expected optional-provider warnings, as recorded by +the supervising baseline run. This document does not reinterpret that result as +validation of new components, other platforms, or production durability. + +## Prepare an offline candidate check + +Use an isolated Python with the reviewed core dependencies and pytest already +installed. Wheel building also needs the existing `pyproject.toml` build +requirements, **setuptools >=68 and wheel**, in this interpreter: build isolation +and network resolution are deliberately disabled. Python 3.12's default venv does +not necessarily include setuptools. Missing prerequisites produce a failed check, +not an automatic package installation or a retry. + +The supervisor has supplied setuptools 84, wheel 0.48 and 31 hashed offline +wheels for the current isolated environment. The previous missing-setuptools +blocker is resolved; this remediation does not install anything or claim an +installed-wheel result. The supervisor runs that path after final assembly. +For another environment, separately supply a reviewed wheelhouse containing the complete +core dependency closure for the target Python/platform. Qualification installs +only into its own fresh temporary venv. It never installs a donor whole package, +Node, Cohere, Neo4j, FAISS, or another user product. Do not put provider credentials +into this environment. No extra Python dependency was added for this tool. + +Portable shell recipes (replace the placeholder paths): + +```sh +JITMIND_PYTHON=/path/to/isolated/bin/python +JITMIND_WHEELHOUSE=/path/to/reviewed-core-wheels + +# Optional prerequisite preparation by the supervisor, offline: +"$JITMIND_PYTHON" -m pip install --no-index --only-binary=:all: \ + --find-links "$JITMIND_WHEELHOUSE" 'setuptools>=68' wheel + +# Run from the candidate checkout. Output must be NEW and outside the checkout. +# No --worktree means dirty source/untracked files are refused. +"$JITMIND_PYTHON" scripts/qualify_jitmind.py run \ + --output /path/to/new-candidate-report \ + --wheelhouse "$JITMIND_WHEELHOUSE" \ + --overall-seconds 900 --command-seconds 180 + +# Developer checks explicitly allow and label an uncommitted worktree: +"$JITMIND_PYTHON" scripts/qualify_jitmind.py run --worktree \ + --output /path/to/new-developer-report \ + --wheelhouse "$JITMIND_WHEELHOUSE" +``` + +The output parent must already exist. The output directory is created exclusively +with mode 0700; reruns need new paths. Reports and logs are never overwritten. +Exit 0 means `checks_only_complete`, 1 means failed/incomplete checks, and 2 means +invalid/unavailable qualification input or setup. **No exit code means production +signoff.** `release_qualified` remains false in this version. + +The fixed suite is: + +1. `python -m pytest -q tests -p scripts.qualify_jitmind`, with explicit checkout + `PYTHONPATH` and third-party pytest plugin autoload disabled. +2. `python -m compileall -q jitmind scripts/qualify_jitmind.py`. +3. `python -m pip wheel --no-index --no-deps --no-build-isolation`, building from a + fresh copy of the pinned source snapshot. Ignored stale build outputs do not + enter this copy. The wheel is retained and hashed in the report. +4. `python -m venv` in a private temporary directory outside the checkout. +5. That venv's `python -I -m pip install --no-index --only-binary=:all:` of the built + wheel and reviewed wheelhouse dependencies. A missing dependency fails; no + network fallback or install into the calling interpreter occurs. +6. That venv's `python -I` executes `tests/test_standalone_install.py` from a + temporary working directory, without `PYTHONPATH`. PATH contains only that + venv's bin directory, so Node cannot be accidentally discovered on host PATH. + +All commands use trusted argv constructors with `shell=False`. Acceptance-map and +imported-report fields never become executable commands. The environment is a +small explicit allowlist; API keys, provider configuration, proxy variables, +PYTHONPATH, pip credentials/config, NODE_OPTIONS and pytest injection settings are +not inherited. The runner gives HOME/TMPDIR private scratch locations. It does +not read user credential files. Repository tests/build code are trusted executable +candidate code; process containment is not an OS sandbox for malicious tests. + +## Standalone installed-package contract + +The smoke can also be run against an actual already installed candidate venv: + +```sh +# Set CHECKOUT before leaving it. The result path must not exist. +CHECKOUT=$PWD +cd /path/to/empty-temporary-directory +env -i LANG=C.UTF-8 PATH=/path/to/candidate-venv/bin \ + /path/to/candidate-venv/bin/python -I \ + "$CHECKOUT/tests/test_standalone_install.py" --installed-smoke \ + /path/to/candidate-venv /path/to/new-installed-results.json +``` + +Prefer the complete runner: it also scrubs provider keys and ties the install to +the freshly built wheel hash. Direct users must supply an equally clean environment. +The smoke asserts `sys.flags.isolated`, the expected venv prefix, a jitmind module +origin inside that venv, absence of checkout from cwd/sys.path, and absence of +Cohere, Neo4j, FAISS and discoverable Node. Socket connections are denied inside +the smoke process, including accidental provider connections. + +It executes actual legacy `MemoryAgent.memorize` and `ResearchAgent.research` +using a deterministic generator, reopens the persisted memory/pages, and checks +retrieved evidence, answer and provenance. No language-model quality claim follows +from this fake generator. If the durable backend is installed, its `diagnostics()` +records actual application connection settings; the separate stdlib probe is +explicitly not a product-storage setting. + +The optional adapter must import without Node. Configuring an absent Node/adapter +path must produce its meaningful typed `Unavailable` capability error; arbitrary +`FileNotFoundError`, `AttributeError`, a failed import, or silent remote fallback +is not acceptance. The corrected J04 constructor raises `CapabilityUnavailable`, +a subtype of `Unavailable`, with `node_executable_unavailable` for a missing or +nonexecutable Node, `adapter_directory_unavailable` for an absent adapter directory, +and `adapter_runner_unavailable` for a missing, unreadable or symlink runner. +Relative configuration paths still raise `ValueError`. Construction does not +start Node, import optional providers or fall back remotely; final owner tests +executed these boundaries successfully in the staged composite. + +The unchanged installed-smoke helper currently accepts only `runtime_unavailable` +or `adapter_unavailable`. Executing that helper against final J04 reproduces an +assertion failure on `node_executable_unavailable`; this is a smoke/product +reason-code contract mismatch, not an unfixed constructor. The baseline-only J08 +checkout also lacks `jitmind.code_context`. Neither checkout helper tests nor the +staged helper reproduction establish installed-wheel qualification. No install +was performed in the final mapping task, and J32 remains unrun. The supervisor +must resolve the reason-code contract explicitly and rerun the isolated smoke; +this task does not change its assertions. + +Pytest's two checks in this file verify the guard and the fake-generator fixture +**from the checkout**. They cannot satisfy J32. J32's separate exact node identity +is `tests/test_standalone_install.py::installed_smoke`; only the runner's installed +artifact can supply its result. + +## Acceptance and evidence schema + +`docs/engineering/acceptance-map.json` retains the derived handoff's **32 cases and +41 required ticket stages**. Each case has one owner. Repeated stages remain +required, including J02 failure propagation, J03/J04 scope/path/cache checks, +J02/J04/J05 outbox chains, and J02/J04/J05/J06 forget/retention chains. + +The map carries the supplied `source_handoff_sha256`, the digest of the derived +map itself, and the frozen temporal oracle's digest and seven expected answers: +3, 3, 4, 3, 4, 5, 3. Latest-perspective Jan20 is 4; equal instants with timezone +offsets agree; conflicting single-valued overlaps are errors. The legacy naive +API is unchanged. This derived map is not the original full-fixture handoff and +is not execution evidence or a signature. + +Every case/stage stays `status: not_run` in source control. Nodes contain: + +- `node_id`: exact pytest ID including every parameter suffix, or the distinct + installed-smoke identity. Prefix matches and inferred test names do not count. +- `kind`: real storage, injected storage fault, real parser, migration, projection, + public API, temporal oracle, or installed package. Tool/qualification tests and + mocked-tool records cannot become product acceptance. +- `source`: repository-relative source file and SHA-256. +- `fixtures`: nonempty source-attested artifact references. Existing peer tests + construct synthetic deterministic fixtures in their test source; they are + labeled as such and do not claim original full-fixture execution. + +Final mappings are based on actual owner-source definitions and staged execution, +not inherited peer pass reports. Changing a test source requires reviewing its +assertions and refreshing its hash. `nodes: []` or +`additional_mapping_required: true` still blocks a stage. All eight previous +mapping gaps now have the exact J10 supplement nodes, retaining parameters, +fixture intent, kinds and `not_run` status. The map has 103 stage-node associations +and 97 unique identities: 96 pytest nodes and the distinct installed smoke. +Every original mapped node remains, with 14 additional atomic durability checks +using J07's legitimate v2 checkpoint adaptation. The temporal oracle is unchanged. + +Mapping coverage is separate from schema compatibility. The unchanged runner +rejects eight detailed J10 kinds with `unsupported_evidence_kind`; the two +compound nodes also have different kinds in J29 and J31, triggering the runner's +`conflicting_node_kind` rule. No kind was silently normalized and no runner or +schema was changed to accept them. These are genuine unresolved qualification +contract blockers. The supervisor must review a schema-compatible representation +that retains the fixture distinctions before rerunning the combined candidate. +Never delete a required stage to match a smaller denominator. This gate implements +no implicit feature exclusions: +any proposed exclusion needs a separate reviewed scope record and a reviewed gate +change that preserves the original denominator and states the exclusion. + +The report directory contains: + +| Artifact | Meaning | +| --- | --- | +| `report.json` | `jitmind.qualification/v2`: candidate Git SHA, dirty flag, complete source snapshot digest/roster, mode, host facts, fixed command records, package hash, findings and artifact manifest. | +| `acceptance-map.json` | Exact mapping used for this run, hashed by the report. | +| `pytest-results.json` | `jitmind.pytest/v2`: selected IDs/count, setup/call/teardown outcomes, per-node source digest/kind, pytest exit, phase counts, session counters and independent collection artifact hash. | +| `expected-collection.json` | `jitmind.collection/v1`: candidate identity, every item observed before deselection, final selected IDs, deselected IDs, collection failures/skips and filtering flag. Exclusively written at collection finish, before runtime results. | +| `installed-results.json` | Separate installed-package checks, origin relative to its venv, dependency versions and connection probes, when installation reached this step. | +| `sources/` | Every live entry in the candidate snapshot, including non-code fixtures and configuration. Deleted entries have null digests and no artifact. Peer files are never substituted. | +| `*.log`, `*.whl` | Bounded command logs and the built candidate wheel, if build succeeded. | +| `verdict.json` | Recomputed per-case/stage outcomes, counts, findings and release blockers. | + +Missing, failed, skipped, timed-out, incomplete-phase, wrong-source, or unmapped +nodes cannot pass. Empty pytest collection cannot pass. An unavailable collection +has null counts, not fabricated zeros. Known findings survive a checker failing +to run. Truncated logs block success. Unknown performance measurements are null. +All enabled cases must have all required nodes/stages executed and passed. + +The v2 schemas deliberately reject old bundles lacking independent collection +or snapshot evidence; regenerate evidence rather than upgrading a claim in place. +`snapshot_files` is a nonempty sorted list of unique `[relative_path, sha256_or_null]` +pairs. Paths must already be normalized POSIX relative paths. The verifier +recomputes `snapshot_sha256` from UTF-8 `json.dumps(roster, separators=(",", ":"))` +(with Python's default ASCII escaping), then binds every live entry to a verified +`sources/` artifact. Extra sources outside the roster are rejected. Every +runtime record's source hash must agree with that roster; mapped source paths +must also agree with the node's file and the reviewed acceptance map. + +The plugin observes `pytest_itemcollected`, `pytest_deselected` and +`pytest_collectreport`, preserving collection failures and module-level +`skip`/`importorskip`. `-k` (including `pytest.ini addopts`), `-m`, explicit +ignore/deselect, last-failed selection, collect-only and file/node-targeted +invocations cannot complete the fixed suite. Collection IDs must be unique; +selected/deselected sets must partition the original inventory. Runtime phase +counters and pytest's collected/failed counters are checked against the records. +Deleting an unmapped failing record and shrinking runtime collection cannot +change the independently recorded expected inventory. Missing expected records +remain unknown in the full denominator; `collection_count` reports that expected +count and `selected_count` separately reports executed selection. Unavailable +runtime measurements remain null. A collection artifact missing entirely blocks +verification. These checks detect internal contradictions, not a malicious +author rewriting all artifacts consistently or deliberately deceptive test code. + +Reports are strict bounded JSON: duplicate keys, nonfinite numbers (including +exponent overflow), booleans as integers, duplicate cases/tests/artifacts, invalid +SHA formats, missing stages, path traversal/symlink artifacts and modified bytes +are rejected. Externally supplied JUnit/XML is **not imported**. This version +prefers its own pytest plugin records; adding an XML importer would require +separate hardening and tests. No imported artifact triggers network access or +command execution. + +Validate an existing bundle against the **trusted map in the current checkout** +and independently selected expected candidate identities: + +```sh +"$JITMIND_PYTHON" scripts/qualify_jitmind.py verify /path/to/report \ + --candidate-sha \ + --snapshot-sha +``` + +Input JSON is capped at 8 MiB; each artifact at 128 MiB; source and evidence +rosters at 10,000 files and 512 MiB in aggregate. File hashing/copy loops also +check the overall deadline; blocking host filesystem calls are not hard real-time. +`bounded_copy(source, destination, expected_hash=None, deadline=None)` prechecks +regular-file size, opens without following a symlink, pins inode/device/size and +nanosecond modification/change times, and streams unbuffered reads of at most the +remaining limit plus one byte. Oversize/growth, replacement, metadata changes or +an expected-hash mismatch fail and remove partial output. Destinations are +exclusive. Snapshot/build/evidence copies and wheel acquisition all use this +helper; installation uses the copied, hashed wheel. This is a stable-file check, +not a filesystem snapshot or protection against a malicious privileged writer. + +The API `evaluate(trusted_mapping, report, artifact_root)` likewise expects its +first argument from the reviewer, not an untrusted report. Hashes establish byte +consistency, not authorship. A malicious party can manufacture mutually consistent +JSON and hashes; independent execution and authenticated review remain necessary. +Imported claims of release approval are ignored. Keep raw logs private: they may +contain trusted test diagnostics or local paths. Public errors are fixed codes; +unknown imported findings are retained as a sanitized finding marker. Redacted +sharing copies are new artifacts, not silently edited originals. + +## Runtime bounds and operational limits + +Each command has both a relative timeout and the shared absolute suite deadline. +Stdout/stderr are drained concurrently before either can fill a pipe; logs are +capped during capture (2 MiB per command), not sliced after an unbounded capture. +Overflow fails the command. Cancellation/timeout kills the owned POSIX process +group and reaps the direct child, including when a child inherited pipes after +the leader exited. Both pipes are closed on every cleanup path. `run_command` +returns `status: cancelled` for SIGINT/KeyboardInterrupt and SIGTERM. During a +main-thread call, a scoped SIGTERM handler sets the cancellation event and the +previous handler is restored afterward. A caller in another thread never changes +process-global signal handlers: its owner must forward cancellation through the +explicit `threading.Event`. There is no process-wide handler installed on import. +An active command wrapper can therefore return normally with a cancelled result; +the CLI retains incomplete/cancelled evidence rather than claiming success. +SIGKILL cannot be trapped, and interpreter/host death cannot promise cleanup. +There is also no scoped signal handler between commands. A supervisor requiring +whole-lifetime containment must own that process lifecycle. The OS reaps orphan grandchildren. Commands with unknown +external effects are never retried. A process deliberately escaping its group +is outside this trusted-command containment model. + +The process runner and new legacy locks are POSIX-only. Windows is explicitly +unsupported, not validated production Windows. Existing core Ubuntu Python +3.10/3.11/3.12 CI remains unchanged; no Windows skip matrix or unverified platform +job was introduced. macOS integration/platform matrix signoff remains separate. + +Optional parser qualification requires Node >=20 and exactly tree-sitter 0.21.1 / +tree-sitter-python 0.21.0 in the reviewed extracted `adapters/graft` runtime. Use +`npm ci --ignore-scripts --no-audit --no-fund` only as a separate reviewed runtime +provisioning step; never install/run the whole donor package's lifecycle hooks. +Pass `--node-binary /absolute/path/to/node` to the runner on systems where it is +outside the allowlisted PATH. That runtime is supplied only to checkout parser +tests, never to the standalone smoke. A native parser probe must actually run; +installation success alone is insufficient. Python AST is the first-release +subset; other languages and whole-donor parity are not claimed. + +SQLite 3.51.2 in the supplied environment has the noted WAL-reset issue. The new +local stores use DELETE/FULL; WAL requires a patched version accepted by the +storage component. Do not globally enable WAL or infer safe settings from the +qualification runner's unrelated stdlib connection. Review actual application +`diagnostics()` and failure/recovery tests for each candidate. + +| Component disabled/unavailable | Required behavior and limit | +| --- | --- | +| Durable authority | Legacy format remains available by explicit configuration; arbitrary legacy memory/page pairs do not become transactional. | +| Structural adapter | Ordinary authorized memory continues independently; code evidence reports unavailable, with no other-product or remote fallback. | +| Code bindings | Preserve historical facts; unknown/stale source does not expire a fact or manufacture a current binding. | +| Preflight | Explicit no-match/deferred behavior; obligations remain open; imported law labels confer no trust. | +| Checkpoint ledger | Legacy state remains unvalidated; iteration lists do not become closed event ranges. | +| Providers/optional retrievers | No live-provider qualification in this gate; fake-generator success is only local public-API behavior. | + +## Cutover and rollback drill + +Perform this against disposable copies before any real cutover. The supervisor +records actual commands, candidate hashes, settings, data digests and outcomes in +a new evidence bundle; these instructions themselves are not a successful drill. + +1. Inventory source formats, exact namespace/repository identities, outstanding + operation keys and writer processes. Stop **all** old writers and confirm + quiescence. J01's persistent flock strategy cannot mix with old O_EXCL writers; + never unlink a live lock file or steal ownership based on age. +2. Preserve untouched legacy files and a manifest of their hashes. Corrupt or + ambiguous input is a blocking finding, not an empty successful migration. + Use J02 `stage_legacy(source, quiesced=True)` and explicitly import into an empty + namespace of a new SQLite authority. Quiescence is an operator responsibility, + not a property proven by that boolean. +3. Compare stable fact IDs, page aliases/provenance, temporal/version histories, + namespace visibility, queries, receipts and outbox state. Record the explicit + old-to-new ID mapping; never infer page aliases from basenames/list positions. + Refuse unknown custom stores as transactional backends. +4. Exercise writes before COMMIT and after COMMIT/before acknowledgement with + actual tests. Reconcile uncertain acknowledgements using the same operation + key and payload. Do not allocate a new key or repeat a provider/tool effect + merely because the acknowledgement was lost. +5. Cut over one quiesced configuration to the new authority; do not dual write. + Exercise public ingest/research and scoped current-source filtering. Deliver + duplicate/out-of-order projection events; verify primary durability, current + authority filtering, and visible lag. External consumers need their own + delivery ledger, not the built-in reference projection's delivered bit. +6. Commit a distinct **post-cutover write**. Stop candidate writers, take a fresh + SQLite API backup including receipts/aliases/history/outbox, and restore it + with `SQLiteDurableStore.restore(backup, new_destination)`. Never copy only a + live main database file or overwrite the current authority with an old backup. +7. Verify that the restored candidate-era write and its receipt survive, replay + the same key without regeneration, and compare queries/aliases/counts. Restart + bindings/projections explicitly: code snapshots are process-local and require + rebuilding; retained metadata is not proof of current code validation. +8. A rollback to **old application code** is not equivalent to restoring a new + SQLite backup. Preserve the latest candidate authority, reconcile every + candidate-era write, and require a reviewed export/mapping compatible with the + old format before routing traffic. If none exists, keep writers stopped and + choose a forward repair or compatible candidate restore. Never silently lose + post-cutover writes by switching back to the pre-cutover files. +9. Checkpoint v2 hashed IDs also need explicit mapping for old consumers. Back up + exact stored IDs, stop old/new writers, and do not guess sanitized aliases. + Closed-range replay requires the original events/source digest; summaries do + not replace missing events. Replay is not restoration of external side effects. +10. Record remaining limits: single-host local filesystem only; injected faults + and process kills are not physical power-loss tests; historical data/backups + may remain after logical forget; grants must be reissued after restart. + +## Release decision and performance claims + +`checks_only_complete` and `release_qualified` are separate. Even a fully passing +checks bundle retains independent signoff, platform matrix, provider evaluation, +power-loss drill and benchmark-comparison blockers. This tool has no flag that +silently converts these unrun dimensions into approval. A release owner must +review and scope them explicitly in a separate authenticated decision record. + +No benchmark runner or donor comparison is claimed here. `benchmark_measurement` +is null. Future measurements must freeze datasets, source hashes, configs, +provider/token budgets and the complete unique baseline/candidate grid. Preserve +failed/inconclusive cells and raw bounded logs. Label synthetic workloads; never +convert unauthorized reads, lost writes, or poor anchors into a favorable speed +score. Retrieval correctness, durability and anchor quality stay separate from +performance, and unrun comparisons remain `not_run`. + +## Final mapping review and staged execution + +The earlier worker and remediation observations below are historical; they are +not final assembled-candidate results. The final mapping review closed the eight +source-mapping gaps without removing cases, required stages or existing nodes: + +| Case/stage | Exact added J10 node associations | +| --- | ---: | +| J02 / J-01: real legacy publication ENOSPC | 5 | +| J02 / J-02: native SQLite capacity exhaustion | 1 | +| J04 / J-02: real process-exit boundaries | 5 | +| J16 / J-06: parser, bindings and open-work fallback | 2 | +| J22 / J-07: tool result/cancel/timeout boundaries | 3 | +| J25 / J-02: corrupt migration, faults and process exits | 8 | +| J29 / J-04: parser/binding/projection replay composition | 2 | +| J31 / J-04: forget, retained metadata and open obligation | 2 | + +These are 28 associations for 26 unique nodes, including 10 J10-authored edge +cases and 16 reused owner cases. J08 adapted only the compound fixture's explicit +`DurableBindingAuthority` configuration; all forget, obligation and authorized +raw-source assertions remain. Memory forget hides derived bodies and leaves the +obligation open; authorized source files remain readable. Physical erasure and +backup purge are not asserted. + +The frozen composite used J01 at `b3fe1d51a4cb98ea2ddce7e94fd3f732010b623d`, +corrected `delivery/J02` through `delivery/J05`, and final `worktrees/J06`, +`worktrees/J07`, `worktrees/J09`, relative to the orchestration root. Only owned +files were overlaid; stale dependency copies were excluded. The provisioned local +Node/tree-sitter adapter came from `delivery/J04/adapters/graft`. + +Actual full staged execution: **861 collected; 860 passed, 1 failed, 0 skipped**. +The sole failure is the unchanged qualification test rejecting the supplement's +preserved evidence kinds. All **93 mapped pytest identities** passed all three +phases, including **26/26 supplement nodes**. Independent collection recorded no +filtering, deselection or collection errors/skips. These raw diagnostic records +are not an accepted qualification bundle; their kinds remain `unmapped` because +the preserved kind conflicts cannot be certified by this runner. No passing +case/stage status or release approval was synthesized from the raw outcomes. + +Separate diagnostic reproductions demonstrate the compound kind conflict and +the unchanged smoke reason-code mismatch. At that diagnostic stage, J32 installed execution was +unrun and no full gate result was credited. See [the contract alignment record](engineering/qualification-review.md) +for exact commands, source ownership, copied-test inventory and remaining limits. + +## Historical J08 remediation verification record + +The initial regression run reproduced 11 failures and one already-passing SIGINT +case. Final verification: **96 focused helper tests passed** and **134 full checkout +tests passed**, each with three expected optional-provider warnings. Compile and +Ruff E9/F passed; mypy is unavailable. Exact commands and controlled probe results +are in the private historical remediation record. Prior historical counts were 59 helper tests and 97 full checkout +tests; their missing-setuptools build failure is historical and no longer an +environment blocker. No installation, live provider call, peer write, commit or +publication is part of this remediation. Release requirements remain unchanged. + + +## Final delivery contract corrections + +The final delivery normalizes supplement labels to the existing fixed evidence +vocabulary and binds the missing-runtime smoke to the implemented typed reason. +No case, stage, assertion or release blocker was removed. See +[qualification-review.md](engineering/qualification-review.md) and the adjacent +normalization ledger. Re-run against the actual committed candidate; historical +worker counts are not the final candidate result. diff --git a/scripts/qualify_jitmind.py b/scripts/qualify_jitmind.py new file mode 100644 index 0000000..2cf9a1e --- /dev/null +++ b/scripts/qualify_jitmind.py @@ -0,0 +1,1436 @@ +#!/usr/bin/env python3 +"""Offline, fail-closed candidate checks. Imported evidence never executes commands. + +This module also supplies a pytest plugin recording exact node IDs and all phases. +Only the CLI's fixed command constructors execute tools; map data is never argv. +""" + +from __future__ import annotations + +import argparse +from contextlib import contextmanager +import hashlib +import importlib.metadata +import json +import math +import os +from pathlib import Path +import platform +import re +import selectors +import signal +import sqlite3 +import stat +import subprocess +import sys +import tempfile +import threading +import time +from typing import Any + +MAX_JSON = 8 * 1024 * 1024 +MAX_LOG = 2 * 1024 * 1024 +MAX_ARTIFACT = 128 * 1024 * 1024 +MAX_TOTAL_BYTES = 512 * 1024 * 1024 +MAX_FILES = 10000 +SHA256 = re.compile(r"[0-9a-f]{64}\Z") +GIT_SHA = re.compile(r"[0-9a-f]{40}\Z") +KINDS = { + "real_storage", + "injected_storage_fault", + "real_parser", + "public_api", + "migration", + "projection", + "installed_package", + "temporal_oracle", +} +COMMANDS = {"pytest", "compile", "wheel", "venv", "install", "smoke"} +SMOKE_NODE = "tests/test_standalone_install.py::installed_smoke" +SMOKE_CHECKS = { + "isolated_origin", + "optional_dependencies_absent", + "legacy_memory_research", + "optional_adapter_import", + "missing_adapter_capability", +} +RELEASE_BLOCKERS = [ + "independent_signoff_not_recorded", + "platform_matrix_not_run", + "provider_checks_not_run", + "power_loss_drill_not_run", + "benchmark_comparisons_not_run", +] + + +class EvidenceError(ValueError): + """Messages are fixed reason codes, never input payloads or paths.""" + + +def require(condition: Any, code: str) -> None: + if not condition: + raise EvidenceError(code) + + +def strict_json(path: Path, limit: int = MAX_JSON) -> Any: + def pairs(items): + result = {} + for key, value in items: + require(key not in result, "duplicate_json_key") + result[key] = value + return result + + def constant(_value): + raise EvidenceError("nonfinite_json") + + try: + require(path.is_file() and not path.is_symlink(), "invalid_json_artifact") + with path.open("rb") as stream: + raw = stream.read(limit + 1) + require(len(raw) <= limit, "input_too_large") + value = json.loads(raw, object_pairs_hook=pairs, parse_constant=constant) + + # 1e999 is parsed as infinity without invoking parse_constant. + def finite(item): + if isinstance(item, float): + require(math.isfinite(item), "nonfinite_json") + elif isinstance(item, dict): + for val in item.values(): + finite(val) + elif isinstance(item, list): + for val in item: + finite(val) + + finite(value) + return value + except (OSError, UnicodeError, json.JSONDecodeError, RecursionError): + raise EvidenceError("invalid_json_artifact") from None + + +def digest(path: Path, *, deadline: float | None = None) -> str: + require(path.is_file() and not path.is_symlink(), "artifact_missing_or_symlink") + require(path.stat().st_size <= MAX_ARTIFACT, "artifact_too_large") + result = hashlib.sha256() + total = 0 + with path.open("rb") as stream: + for block in iter(lambda: stream.read(65536), b""): + if deadline is not None: + require(time.monotonic() < deadline, "overall_deadline") + total += len(block) + require(total <= MAX_ARTIFACT, "artifact_too_large") + result.update(block) + return result.hexdigest() + + +def bounded_copy( + source: Path, + destination: Path, + *, + expected_hash: str | None = None, + deadline: float | None = None, +) -> str: + """Exclusive, bounded copy of a stable regular file; remove partial output. + + Pin the opened inode and reject replacement or metadata changes during copying. + Hash checks bind bytes when copying a previously inventoried source. + """ + + def identity(info): + return ( + info.st_dev, + info.st_ino, + info.st_mode, + info.st_size, + info.st_mtime_ns, + info.st_ctime_ns, + ) + + before = source.lstat() + require(stat.S_ISREG(before.st_mode), "artifact_missing_or_symlink") + require(before.st_size <= MAX_ARTIFACT, "artifact_too_large") + created = False + try: + fd = os.open(source, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK) + with os.fdopen(fd, "rb", buffering=0) as src: + opened = os.fstat(src.fileno()) + require(identity(opened) == identity(before), "source_changed_during_copy") + total = 0 + hasher = hashlib.sha256() + with destination.open("xb") as dst: + created = True + while True: + if deadline is not None: + require(time.monotonic() < deadline, "overall_deadline") + block = src.read(min(65536, MAX_ARTIFACT - total + 1)) + if not block: + break + total += len(block) + require(total <= MAX_ARTIFACT, "artifact_too_large") + hasher.update(block) + dst.write(block) + require( + identity(os.fstat(src.fileno())) == identity(opened) + and identity(source.lstat()) == identity(opened), + "source_changed_during_copy", + ) + require(total == opened.st_size, "source_changed_during_copy") + result = hasher.hexdigest() + require( + expected_hash is None or result == expected_hash, + "source_hash_mismatch", + ) + return result + except BaseException: + if created: + destination.unlink(missing_ok=True) + raise + + +@contextmanager +def command_signals(cancel: threading.Event): + """Temporarily translate SIGTERM on the main thread; never change worker signals.""" + installed = threading.current_thread() is threading.main_thread() + if installed: + previous = signal.getsignal(signal.SIGTERM) + signal.signal(signal.SIGTERM, lambda _signum, _frame: cancel.set()) + try: + yield + finally: + if installed: + signal.signal(signal.SIGTERM, previous) + + +def relative_file(root: Path, name: str) -> Path: + require( + isinstance(name, str) and bool(name) and "\\" not in name, + "invalid_artifact_path", + ) + path = Path(name) + require(not path.is_absolute() and ".." not in path.parts, "invalid_artifact_path") + current = root.resolve() + for part in path.parts: + current = current / part + require(not current.is_symlink(), "artifact_symlink") + require(current.is_relative_to(root.resolve()), "artifact_escape") + return current + + +def artifact(root: Path, name: str) -> dict: + path = relative_file(root, name) + return {"path": name, "sha256": digest(path), "bytes": path.stat().st_size} + + +def verify_artifact(root: Path, ref: dict) -> Path: + require(isinstance(ref, dict), "invalid_artifact") + require( + isinstance(ref.get("sha256"), str) and SHA256.fullmatch(ref["sha256"]), + "invalid_artifact_digest", + ) + require( + type(ref.get("bytes")) is int and 0 <= ref["bytes"] <= MAX_ARTIFACT, + "invalid_artifact_size", + ) + path = relative_file(root, ref.get("path")) + require( + digest(path) == ref["sha256"] and path.stat().st_size == ref["bytes"], + "artifact_modified", + ) + return path + + +def write_json(path: Path, value: Any) -> None: + data = json.dumps(value, indent=2, sort_keys=True, allow_nan=False) + "\n" + require(len(data.encode()) <= MAX_JSON, "report_too_large") + with path.open("x", encoding="utf-8") as stream: + stream.write(data) + + +def clean_env(home: Path, python: Path, checkout: Path | None = None) -> dict: + """Allowlist: do not inherit credentials, proxy config, plugins, or PYTHONPATH.""" + env = { + "HOME": str(home), + "TMPDIR": str(home), + "LANG": "C.UTF-8", + "LC_ALL": "C.UTF-8", + "PATH": str(python.parent) + os.pathsep + "/usr/bin:/bin", + "PYTHONNOUSERSITE": "1", + "PYTHONDONTWRITEBYTECODE": "1", + "PIP_CONFIG_FILE": os.devnull, + "PIP_DISABLE_PIP_VERSION_CHECK": "1", + "PIP_NO_INDEX": "1", + "PYTEST_DISABLE_PLUGIN_AUTOLOAD": "1", + } + if checkout is not None: + env["PYTHONPATH"] = str(checkout) + return env + + +def run_command( + argv: list[str], + *, + cwd: Path, + env: dict, + log: Path, + deadline: float, + timeout: float = 120, + max_bytes: int = MAX_LOG, + cancel: threading.Event | None = None, +) -> dict: + """Trusted internal argv only. Drain both pipes; bound storage before capture. + + POSIX owned process groups include children inheriting pipes after leader exit. + Kill the group even on successful leader exit; never retry a command. + """ + require(os.name == "posix", "process_groups_unsupported_platform") + require( + isinstance(argv, list) + and bool(argv) + and all(isinstance(arg, str) for arg in argv), + "invalid_argv", + ) + require(type(max_bytes) is int and max_bytes > 0, "invalid_log_budget") + require( + type(timeout) in (int, float) and math.isfinite(timeout) and timeout > 0, + "invalid_timeout", + ) + require( + type(deadline) in (int, float) and math.isfinite(deadline), "invalid_deadline" + ) + started = time.monotonic() + end = min(deadline, started + timeout) + outcome = "not_run" + emitted = 0 + proc = None + cancel = cancel if cancel is not None else threading.Event() + with command_signals(cancel), log.open("xb") as output: + try: + if cancel is not None and cancel.is_set(): + outcome = "cancelled" + elif started >= end: + outcome = "timeout" + else: + proc = subprocess.Popen( + argv, + cwd=cwd, + env=env, + shell=False, + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + start_new_session=True, + ) + outcome = "passed" + with selectors.DefaultSelector() as selector: + for pipe in (proc.stdout, proc.stderr): + os.set_blocking(pipe.fileno(), False) + selector.register(pipe, selectors.EVENT_READ) + while selector.get_map() or proc.poll() is None: + if cancel is not None and cancel.is_set(): + outcome = "cancelled" + break + if time.monotonic() >= end: + outcome = "timeout" + break + for key, _ in selector.select( + min(0.02, max(0, end - time.monotonic())) + ): + chunk = os.read(key.fileobj.fileno(), 16384) + if not chunk: + selector.unregister(key.fileobj) + continue + room = max_bytes - emitted + output.write(chunk[:room]) + emitted += min(room, len(chunk)) + if len(chunk) > room: + outcome = "output_limit" + break + if outcome != "passed": + break + if outcome == "passed" and proc.poll() != 0: + outcome = "failed" + except KeyboardInterrupt: + outcome = "cancelled" + except OSError: + outcome = "launch_error" + finally: + if proc is not None: + try: + os.killpg(proc.pid, signal.SIGKILL) + except ProcessLookupError: + pass + proc.wait() + proc.stdout.close() + proc.stderr.close() + return { + "status": outcome, + "exit_code": None if proc is None else proc.returncode, + "elapsed_seconds": time.monotonic() - started, + "log_bytes": emitted, + "log_truncated": outcome == "output_limit", + } + + +def candidate_identity( + repo: Path, scratch: Path, python: Path, deadline: float +) -> dict: + def git(name, args): + log = scratch / (name + ".log") + result = run_command( + ["/usr/bin/git", "-c", "core.fsmonitor=false", *args], + cwd=repo, + env=clean_env(scratch, python), + log=log, + deadline=deadline, + timeout=20, + max_bytes=MAX_JSON, + ) + require(result["status"] == "passed", "git_identity_unavailable") + return log.read_bytes() + + sha = git("head", ["rev-parse", "HEAD"]).decode().strip() + require(GIT_SHA.fullmatch(sha), "invalid_candidate_sha") + # --porcelain suppresses warning output only on standard installations; fail closed otherwise. + status = git("status", ["status", "--porcelain=v1", "-z", "--untracked-files=all"]) + paths = git( + "files", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"] + ) + records = [] + source_names = sorted(set(paths.split(b"\0")) - {b""}) + require(len(source_names) <= MAX_FILES, "too_many_source_files") + source_bytes = 0 + for raw in source_names: + require(time.monotonic() < deadline, "overall_deadline") + name = raw.decode("utf-8") + path = relative_file(repo, name) + if path.exists(): + source_bytes += path.stat().st_size + require(source_bytes <= MAX_TOTAL_BYTES, "source_snapshot_too_large") + records.append( + [name, digest(path, deadline=deadline) if path.exists() else None] + ) + snapshot = hashlib.sha256( + json.dumps(records, separators=(",", ":")).encode() + ).hexdigest() + return { + "git_sha": sha, + "dirty": bool(status), + "snapshot_sha256": snapshot, + "snapshot_files": records, + } + + +def snapshot_roster(identity: dict) -> dict: + records = identity.get("snapshot_files") + require( + isinstance(records, list) and bool(records) and len(records) <= MAX_FILES, + "invalid_snapshot_roster", + ) + names = [] + for record in records: + require(isinstance(record, list) and len(record) == 2, "invalid_snapshot_entry") + name, sha = record + require( + isinstance(name, str) + and bool(name) + and "\\" not in name + and not Path(name).is_absolute() + and ".." not in Path(name).parts + and Path(name).as_posix() == name + and name != ".", + "invalid_snapshot_path", + ) + require( + sha is None or isinstance(sha, str) and SHA256.fullmatch(sha), + "invalid_snapshot_source_digest", + ) + names.append(name) + require(names == sorted(set(names)), "snapshot_roster_not_normalized") + expected = hashlib.sha256( + json.dumps(records, separators=(",", ":")).encode() + ).hexdigest() + require(identity["snapshot_sha256"] == expected, "snapshot_digest_mismatch") + return dict(records) + + +def validate_map(mapping: dict) -> list[dict]: + require( + isinstance(mapping, dict) + and mapping.get("schema") == "jitmind.acceptance-map/v1", + "invalid_map_schema", + ) + require( + isinstance(mapping.get("source_handoff_sha256"), str) + and SHA256.fullmatch(mapping["source_handoff_sha256"]), + "invalid_handoff_digest", + ) + cases = mapping.get("cases") + require(isinstance(cases, list) and bool(cases), "empty_cases") + ids = set() + for case in cases: + require( + isinstance(case, dict) + and isinstance(case.get("case_id"), str) + and re.fullmatch(r"J[0-9]{2}", case["case_id"]), + "invalid_case", + ) + require(case["case_id"] not in ids, "duplicate_case") + ids.add(case["case_id"]) + required = case.get("required_in_tickets") + require( + isinstance(required, list) + and required + and all( + isinstance(s, str) and re.fullmatch(r"J-[0-9]{2}", s) for s in required + ) + and len(set(required)) == len(required), + "invalid_required_stages", + ) + require( + case.get("ticket") in required and case.get("status") == "not_run", + "invalid_case_owner_or_status", + ) + stages = case.get("stages") + require( + isinstance(stages, list) and len(stages) == len(required), "missing_stage" + ) + require({s.get("stage_id") for s in stages} == set(required), "missing_stage") + for stage in stages: + require(stage.get("status") == "not_run", "map_is_not_execution_evidence") + require( + type(stage.get("additional_mapping_required", False)) is bool, + "invalid_mapping_gap", + ) + nodes = stage.get("nodes") + require(isinstance(nodes, list), "invalid_nodes") + seen = set() + for node in nodes: + node_id = node.get("node_id") + require( + isinstance(node_id, str) and node_id and node_id not in seen, + "duplicate_or_invalid_node", + ) + require( + node_id.startswith("tests/") + and ".." not in node_id.split("::")[0].split("/"), + "invalid_test_path", + ) + require( + not node_id.startswith("tests/test_qualification") + and ( + not node_id.startswith("tests/test_standalone_install.py::") + or node_id == SMOKE_NODE + ), + "tool_test_is_not_acceptance", + ) + seen.add(node_id) + require(node.get("kind") in KINDS, "unsupported_evidence_kind") + require( + isinstance(node.get("source"), dict) and node.get("fixtures"), + "missing_source_or_fixture", + ) + for ref in [node["source"], *node["fixtures"]]: + require( + isinstance(ref, dict) + and isinstance(ref.get("path"), str) + and isinstance(ref.get("sha256"), str) + and SHA256.fullmatch(ref["sha256"]), + "invalid_source_ref", + ) + return cases + + +def evaluate(mapping: dict, report: dict, root: Path) -> dict: + """Validate evidence bytes and recompute verdict. No command execution or network. + + A digest verifies consistency, not authorship. External input is never signoff. + Structural errors raise; incomplete/failed checks remain in the denominator. + """ + cases = validate_map(mapping) + require(report.get("schema") == "jitmind.qualification/v2", "invalid_report_schema") + identity = report.get("candidate", {}) + require( + isinstance(identity.get("git_sha"), str) + and GIT_SHA.fullmatch(identity["git_sha"]), + "invalid_candidate_sha", + ) + require( + isinstance(identity.get("snapshot_sha256"), str) + and SHA256.fullmatch(identity["snapshot_sha256"]), + "invalid_snapshot_digest", + ) + roster = snapshot_roster(identity) + require(type(identity.get("dirty")) is bool, "invalid_dirty_flag") + require(report.get("mode") in {"worktree", "release"}, "invalid_run_mode") + require(not identity["dirty"] or report["mode"] == "worktree", "dirty_release") + map_path = verify_artifact(root, report.get("acceptance_map")) + require(strict_json(map_path) == mapping, "map_identity_mismatch") + artifacts = report.get("artifacts") + require(isinstance(artifacts, list), "invalid_artifacts") + require(len(artifacts) <= MAX_FILES, "too_many_artifacts") + refs = {} + artifact_bytes = 0 + for ref in artifacts: + require( + isinstance(ref, dict) + and type(ref.get("bytes")) is int + and ref["bytes"] >= 0, + "invalid_artifact_size", + ) + artifact_bytes += ref["bytes"] + require(artifact_bytes <= MAX_TOTAL_BYTES, "artifacts_too_large") + verify_artifact(root, ref) + require(ref["path"] not in refs, "duplicate_artifact") + refs[ref["path"]] = ref + for name, sha in roster.items(): + ref = refs.get("sources/" + name) + require( + (ref is None if sha is None else ref is not None and ref["sha256"] == sha), + "snapshot_source_mismatch", + ) + require( + all(not name.startswith("sources/") or name[8:] in roster for name in refs), + "source_outside_snapshot", + ) + commands = report.get("commands") + require(isinstance(commands, list), "missing_commands") + command_ids = set() + command_ok = bool(commands) + findings = report.get("findings", []) + require( + isinstance(findings, list) and all(isinstance(f, str) for f in findings), + "invalid_findings", + ) + # Preserve the existence of imported findings without echoing arbitrary payloads. + safe_findings = { + "candidate_wheel_missing", + "candidate_changed_during_checks", + "candidate_final_identity_unavailable", + } + findings = [ + f if f in safe_findings else "retained_external_finding" for f in findings + ] + for cmd in commands: + require( + cmd.get("id") in COMMANDS and cmd["id"] not in command_ids, + "invalid_command_id", + ) + command_ids.add(cmd["id"]) + require( + type(cmd.get("exit_code")) is int or cmd.get("exit_code") is None, + "invalid_exit_code", + ) + require(type(cmd.get("log_truncated")) is bool, "invalid_truncation_flag") + require(cmd.get("log") in refs, "missing_command_log") + command_ok &= ( + cmd.get("status") == "passed" + and cmd.get("exit_code") == 0 + and not cmd["log_truncated"] + ) + command_ok &= command_ids == COMMANDS + require(report.get("test_results") in refs, "missing_test_results") + test_data = strict_json(root / report["test_results"]) + require(test_data.get("schema") == "jitmind.pytest/v2", "invalid_test_schema") + require(test_data.get("candidate") == identity, "test_source_identity_mismatch") + expected_name = report.get("expected_collection") + require(expected_name in refs, "missing_expected_collection") + expected = strict_json(root / expected_name) + require( + expected.get("schema") == "jitmind.collection/v1" + and expected.get("candidate") == identity, + "collection_identity_mismatch", + ) + require( + test_data.get("expected_collection_sha256") == refs[expected_name]["sha256"], + "collection_digest_mismatch", + ) + for field in ("nodes", "selected", "deselected"): + values = expected.get(field) + require( + isinstance(values, list) + and all(isinstance(n, str) for n in values) + and len(values) == len(set(values)), + "duplicate_or_invalid_collection", + ) + require(type(expected.get("filtered")) is bool, "invalid_collection_filter") + collection_reports = expected.get("collection_reports") + require( + isinstance(collection_reports, list) + and all( + isinstance(r, dict) + and isinstance(r.get("node_id"), str) + and r.get("outcome") in {"failed", "skipped"} + for r in collection_reports + ), + "invalid_collection_reports", + ) + require( + set(expected["selected"]).isdisjoint(expected["deselected"]) + and set(expected["nodes"]) + == set(expected["selected"]) | set(expected["deselected"]), + "collection_inventory_mismatch", + ) + collected = test_data.get("collected") + require( + isinstance(collected, list) + and all(isinstance(n, str) for n in collected) + and len(collected) == len(set(collected)), + "duplicate_or_invalid_collection", + ) + available = test_data.get("available", True) + require(type(available) is bool, "invalid_test_availability") + require( + ( + available + and type(test_data.get("collection_count")) is int + and test_data["collection_count"] == len(collected) + ) + or ( + not available + and test_data.get("collection_count") is None + and not collected + ), + "invalid_collection_count", + ) + records = test_data.get("records") + require(isinstance(records, list), "missing_test_records") + by_node = {} + counts = {"passed": 0, "failed": 0, "skipped": 0, "unknown": 0} + for record in records: + node_id = record.get("node_id") + require( + isinstance(node_id, str) + and node_id in collected + and node_id not in by_node, + "duplicate_or_uncollected_test", + ) + source_name = node_id.split("::")[0] + require( + source_name in roster + and roster[source_name] is not None + and record.get("source_sha256") == roster[source_name], + "record_source_mismatch", + ) + phases = record.get("phases") + require( + isinstance(phases, dict) and set(phases) <= {"setup", "call", "teardown"}, + "invalid_phases", + ) + require( + all(p in {"passed", "failed", "skipped"} for p in phases.values()), + "invalid_phase_outcome", + ) + status = ( + "failed" + if "failed" in phases.values() + else "skipped" + if "skipped" in phases.values() + else "passed" + if phases == {"setup": "passed", "call": "passed", "teardown": "passed"} + else "unknown" + ) + require(record.get("status") == status, "false_test_pass") + counts[status] += 1 + by_node[node_id] = record + counts["unknown"] += len((set(collected) | set(expected["nodes"])) - set(by_node)) + phase_counts = { + outcome: sum(p == outcome for r in records for p in r["phases"].values()) + for outcome in ("passed", "failed", "skipped") + } + if available: + counters = test_data.get("phase_counts") + require( + isinstance(counters, dict) + and set(counters) == set(phase_counts) + and all(type(v) is int for v in counters.values()) + and counters == phase_counts, + "inconsistent_plugin_counters", + ) + require( + type(test_data.get("session_testscollected")) is int + and test_data["session_testscollected"] == len(collected) + and type(test_data.get("session_testsfailed")) is int + and test_data["session_testsfailed"] + == phase_counts["failed"] + + sum(r["outcome"] == "failed" for r in collection_reports), + "inconsistent_plugin_counters", + ) + else: + require( + all( + test_data.get(k) is None + for k in ( + "phase_counts", + "session_testscollected", + "session_testsfailed", + ) + ), + "inconsistent_plugin_counters", + ) + tests_ok = ( + available + and collected == expected["selected"] + and set(collected) == set(expected["nodes"]) + and not expected["filtered"] + and not expected["deselected"] + and not collection_reports + and bool(collected) + and counts["passed"] == len(collected) + and test_data.get("exit_code") == 0 + ) + require(type(test_data.get("exit_code")) is int, "invalid_pytest_exit") + if not tests_ok: + findings.append("pytest_incomplete_or_failed") + if not command_ok: + findings.append("fixed_suite_incomplete_or_failed") + package = report.get("package_sha256") + package_ok = ( + isinstance(package, str) + and SHA256.fullmatch(package) + and any( + name.endswith(".whl") and ref["sha256"] == package + for name, ref in refs.items() + ) + ) + if not package_ok: + findings.append("candidate_wheel_unverified") + installed = None + if "installed-results.json" in refs: + installed = strict_json(root / "installed-results.json") + require( + installed.get("schema") == "jitmind.installed-smoke/v1", + "invalid_smoke_schema", + ) + checks = installed.get("checks") + require( + isinstance(checks, dict) and set(checks) == SMOKE_CHECKS, + "invalid_smoke_checks", + ) + require(installed.get("node_id") == SMOKE_NODE, "invalid_smoke_identity") + require( + type(installed.get("provider_calls")) is int + and installed["provider_calls"] == 0, + "invalid_smoke_provider_count", + ) + smoke_ok = ( + package_ok + and installed.get("status") == "passed" + and all(s == "passed" for s in checks.values()) + ) + by_node[SMOKE_NODE] = { + "kind": "installed_package", + "source_sha256": installed.get("source_sha256"), + "status": "passed" if smoke_ok else "failed", + } + if installed is None or by_node[SMOKE_NODE]["status"] != "passed": + findings.append("installed_smoke_incomplete") + require(report.get("benchmark_measurement") is None, "unreviewed_benchmark_claim") + outcomes = [] + for case in cases: + stages = [] + for stage in case["stages"]: + statuses = [] + for node in stage["nodes"]: + source_ok = node["node_id"].split("::")[0] == node["source"]["path"] + for ref in [node["source"], *node["fixtures"]]: + name = "sources/" + ref["path"] + source_ok &= name in refs and refs[name]["sha256"] == ref["sha256"] + record = by_node.get(node["node_id"]) + if not source_ok: + status = "source_unverified" + elif not record: + status = "not_run" + elif record.get("kind") != node["kind"]: + status = "evidence_kind_mismatch" + elif record.get("source_sha256") != node["source"]["sha256"]: + status = "source_unverified" + else: + status = record["status"] + statuses.append(status) + stages.append( + { + "stage_id": stage["stage_id"], + "node_results": statuses, + "status": "passed" + if statuses + and not stage.get("additional_mapping_required", False) + and all(s == "passed" for s in statuses) + else "inconclusive", + } + ) + outcomes.append( + { + "case_id": case["case_id"], + "stages": stages, + "status": "passed" + if all(s["status"] == "passed" for s in stages) + else "inconclusive", + } + ) + complete = ( + tests_ok + and command_ok + and not findings + and all(c["status"] == "passed" for c in outcomes) + ) + return { + "checks_only_complete": bool(complete), + "release_qualified": False, + "evidence_authorship": "digest_consistency_only_not_signer_identity", + "case_count": len(cases), + "stage_count": sum(len(c["stages"]) for c in cases), + "passed_cases": sum(c["status"] == "passed" for c in outcomes), + "test_counts": counts if available else dict.fromkeys(counts), + "collection_count": len(expected["nodes"]) if available else None, + "selected_count": len(collected) if available else None, + "cases": outcomes, + "findings": findings, + "release_blockers": RELEASE_BLOCKERS + + (["worktree_candidate"] if report["mode"] == "worktree" else []), + "benchmark_measurement": None, + } + + +# Fixed pytest plugin. These hooks only activate in an authorized runner session. +_SESSION: dict = {} + + +def pytest_sessionstart(session): + context = os.environ.get("JITMIND_QUALIFICATION_CONTEXT") + if context: + _SESSION.clear() + _SESSION.update(strict_json(Path(context))) + _SESSION["records"] = {} + _SESSION["collected"] = [] + _SESSION["nodes"] = [] + _SESSION["deselected"] = [] + _SESSION["collection_reports"] = [] + _SESSION["expected_path"] = str( + Path(_SESSION["output"]).with_name("expected-collection.json") + ) + + +def pytest_itemcollected(item): + if _SESSION: + _SESSION["nodes"].append(item.nodeid) + + +def pytest_deselected(items): + if _SESSION: + _SESSION["deselected"].extend(item.nodeid for item in items) + + +def pytest_collectreport(report): + if _SESSION and report.outcome != "passed": + _SESSION["collection_reports"].append( + {"node_id": report.nodeid, "outcome": report.outcome} + ) + + +def collection_record(session): + options = session.config.option + filtered = any( + getattr(options, name, None) + for name in ( + "keyword", + "markexpr", + "deselect", + "ignore", + "ignore_glob", + "lf", + "ff", + "collectonly", + ) + ) + # Fixed runner invokes the full tests directory. Keep arbitrary diagnostic runs + # observable but never credit a file/node-targeted invocation as the fixed suite. + filtered |= session.config.args != ["tests"] + return { + "schema": "jitmind.collection/v1", + "candidate": _SESSION["candidate"], + "nodes": _SESSION["nodes"], + "selected": _SESSION["collected"], + "deselected": _SESSION["deselected"], + "collection_reports": _SESSION["collection_reports"], + "filtered": bool(filtered), + } + + +def pytest_collection_finish(session): + if _SESSION: + _SESSION["collected"] = [item.nodeid for item in session.items] + # Exclusive artifact captured before execution, independent of runtime records. + write_json(Path(_SESSION["expected_path"]), collection_record(session)) + + +def pytest_runtest_logreport(report): + if _SESSION: + phases = _SESSION["records"].setdefault(report.nodeid, {}) + require(report.when not in phases, "duplicate_test_phase") + phases[report.when] = ( + "skipped" if hasattr(report, "wasxfail") else report.outcome + ) + + +def pytest_sessionfinish(session, exitstatus): + if not _SESSION: + return + expected_path = Path(_SESSION["expected_path"]) + if not expected_path.exists(): + write_json(expected_path, collection_record(session)) + records = [] + for node, phases in _SESSION["records"].items(): + status = ( + "failed" + if "failed" in phases.values() + else "skipped" + if "skipped" in phases.values() + else "passed" + if phases == {"setup": "passed", "call": "passed", "teardown": "passed"} + else "unknown" + ) + source = relative_file(Path(_SESSION["repo"]), node.split("::")[0]) + records.append( + { + "node_id": node, + "phases": phases, + "status": status, + "source_sha256": digest(source), + "kind": _SESSION["kinds"].get(node, "unmapped"), + } + ) + write_json( + Path(_SESSION["output"]), + { + "schema": "jitmind.pytest/v2", + "candidate": _SESSION["candidate"], + "collected": _SESSION["collected"], + "available": True, + "collection_count": len(_SESSION["collected"]), + "expected_collection_sha256": digest(expected_path), + "session_testscollected": session.testscollected, + "session_testsfailed": session.testsfailed, + "phase_counts": { + outcome: sum( + p == outcome for r in records for p in r["phases"].values() + ) + for outcome in ("passed", "failed", "skipped") + }, + "records": records, + "exit_code": int(exitstatus), + }, + ) + + +def host_facts(home: Path) -> dict: + with sqlite3.connect(home / "qualification-probe.sqlite") as conn: + pragmas = { + key: conn.execute("PRAGMA " + key).fetchone()[0] + for key in ("journal_mode", "synchronous", "foreign_keys", "busy_timeout") + } + return { + "python": platform.python_version(), + "implementation": platform.python_implementation(), + "os": platform.system(), + "os_release": platform.release(), + "machine": platform.machine(), + "processor": platform.processor() or None, + "cpu_count": os.cpu_count(), + "sqlite": sqlite3.sqlite_version, + "probe_connection_pragmas": pragmas, + "pragma_scope": "fresh stdlib connection; not product storage settings", + "dependencies": sorted( + [ + [d.metadata["Name"], d.version] + for d in importlib.metadata.distributions() + ] + ), + } + + +def run_suite( + repo: Path, + output: Path, + wheelhouse: Path | None, + worktree: bool, + overall: float, + per_command: float, + node_binary: Path | None = None, +) -> dict: + require(os.name == "posix", "process_groups_unsupported_platform") + require( + math.isfinite(overall) + and overall > 0 + and math.isfinite(per_command) + and per_command > 0, + "invalid_deadline", + ) + repo = repo.resolve() + output = output.absolute() + require( + not output.resolve().is_relative_to(repo), "output_must_be_outside_checkout" + ) + output.mkdir(mode=0o700, parents=False, exist_ok=False) + python = Path(sys.executable).absolute() + deadline = time.monotonic() + overall + with tempfile.TemporaryDirectory(prefix="jitmind-qualification-") as temporary: + scratch = Path(temporary) + identity = candidate_identity(repo, scratch, python, deadline) + require(worktree or not identity["dirty"], "dirty_release") + mapping_path = repo / "docs/engineering/acceptance-map.json" + mapping = strict_json(mapping_path) + cases = validate_map(mapping) + write_json(output / "acceptance-map.json", mapping) + report = { + "schema": "jitmind.qualification/v2", + "mode": "worktree" if worktree else "release", + "candidate": identity, + "host": host_facts(scratch), + "findings": [], + "commands": [], + "artifacts": [], + "test_results": "pytest-results.json", + "expected_collection": "expected-collection.json", + "acceptance_map": artifact(output, "acceptance-map.json"), + "package_sha256": None, + "suite_source": "trusted_fixed_argv_v1", + "checkout_tests_pythonpath": "explicit_checkout_only", + } + build_source = scratch / "build-source" + build_source.mkdir() + for name, expected_hash in identity["snapshot_files"]: + require(time.monotonic() < deadline, "overall_deadline") + if expected_hash is None: + continue + source = relative_file(repo, name) + require( + digest(source, deadline=deadline) == expected_hash, + "candidate_changed_during_snapshot", + ) + destination = build_source / name + destination.parent.mkdir(parents=True, exist_ok=True) + bounded_copy( + source, destination, expected_hash=expected_hash, deadline=deadline + ) + require( + digest(destination, deadline=deadline) == expected_hash, + "candidate_changed_during_snapshot", + ) + report["build_source"] = ( + "fresh_copy_of_pinned_snapshot_without_ignored_build_outputs" + ) + report["node_binary_sha256"] = digest(node_binary) if node_binary else None + kinds = {} + for case in cases: + for stage in case["stages"]: + for node in stage["nodes"]: + previous = kinds.setdefault(node["node_id"], node["kind"]) + require(previous == node["kind"], "conflicting_node_kind") + # Every live snapshot entry is retained, including non-code fixtures/config. + copied_bytes = 0 + for name, expected_hash in identity["snapshot_files"]: + if expected_hash is None: + continue + src = relative_file(build_source, name) + copied_bytes += src.stat().st_size + require(copied_bytes <= MAX_TOTAL_BYTES, "source_snapshot_too_large") + dest = output / "sources" / name + dest.parent.mkdir(parents=True, exist_ok=True) + bounded_copy(src, dest, expected_hash=expected_hash, deadline=deadline) + report["artifacts"].append(artifact(output, "sources/" + name)) + context = scratch / "context.json" + write_json( + context, + { + "repo": str(repo), + "output": str(output / "pytest-results.json"), + "candidate": identity, + "kinds": kinds, + }, + ) + env = clean_env(scratch, python, repo) + env["JITMIND_QUALIFICATION_CONTEXT"] = str(context) + if node_binary is not None: + require( + node_binary.is_absolute() and node_binary.is_file(), + "invalid_node_runtime", + ) + env["JITMIND_TEST_NODE"] = str(node_binary.resolve()) + + cancelled = threading.Event() + + def execute(name, argv, cwd, environment): + record = run_command( + argv, + cwd=cwd, + env=environment, + log=output / (name + ".log"), + deadline=deadline, + timeout=per_command, + cancel=cancelled, + ) + if record["status"] == "cancelled": + cancelled.set() + # Exact argv may contain private local paths; report portable placeholders. + record.update( + { + "id": name, + "argv": [ + arg.replace(str(repo), "") + .replace(str(scratch), "") + .replace(str(output), "") + .replace(str(python), "") + .replace(str(wheelhouse), "") + if wheelhouse + else arg.replace(str(repo), "") + .replace(str(scratch), "") + .replace(str(output), "") + .replace(str(python), "") + for arg in argv + ], + "log": name + ".log", + } + ) + report["commands"].append(record) + report["artifacts"].append(artifact(output, name + ".log")) + return record["status"] == "passed" + + execute( + "pytest", + [ + str(python), + "-m", + "pytest", + "-q", + "tests", + "-p", + "scripts.qualify_jitmind", + ], + repo, + env, + ) + if not (output / "expected-collection.json").exists(): + write_json( + output / "expected-collection.json", + { + "schema": "jitmind.collection/v1", + "candidate": identity, + "nodes": [], + "selected": [], + "deselected": [], + "collection_reports": [], + "filtered": True, + }, + ) + report["artifacts"].append(artifact(output, "expected-collection.json")) + if not (output / "pytest-results.json").exists(): + write_json( + output / "pytest-results.json", + { + "schema": "jitmind.pytest/v2", + "candidate": identity, + "collected": [], + "collection_count": None, + "expected_collection_sha256": digest( + output / "expected-collection.json" + ), + "session_testscollected": None, + "session_testsfailed": None, + "phase_counts": None, + "available": False, + "records": [], + "exit_code": 99, + }, + ) + report["artifacts"].append(artifact(output, "pytest-results.json")) + execute( + "compile", + [ + str(python), + "-m", + "compileall", + "-q", + "jitmind", + "scripts/qualify_jitmind.py", + ], + repo, + env, + ) + wheels = scratch / "wheels" + wheels.mkdir() + build_env = clean_env(scratch, python) + built = execute( + "wheel", + [ + str(python), + "-m", + "pip", + "wheel", + "--no-index", + "--no-deps", + "--no-build-isolation", + "--wheel-dir", + str(wheels), + str(build_source), + ], + scratch, + build_env, + ) + available = list(wheels.glob("jitmind-*.whl")) + if built and len(available) == 1: + wheel = available[0] + dest = output / wheel.name + wheel_hash = bounded_copy(wheel, dest, deadline=deadline) + report["artifacts"].append(artifact(output, wheel.name)) + report["package_sha256"] = wheel_hash + venv = scratch / "installed" + created = execute( + "venv", [str(python), "-m", "venv", str(venv)], scratch, build_env + ) + vpython = venv / "bin/python" + install_env = clean_env(scratch, vpython) + install_argv = [ + str(vpython), + "-I", + "-m", + "pip", + "install", + "--no-index", + "--only-binary=:all:", + ] + if wheelhouse is not None: + install_argv += ["--find-links", str(wheelhouse.resolve())] + if created and execute( + "install", install_argv + [str(dest)], scratch, install_env + ): + install_env["PATH"] = str(vpython.parent) + execute( + "smoke", + [ + str(vpython), + "-I", + str(repo / "tests/test_standalone_install.py"), + "--installed-smoke", + str(venv), + str(output / "installed-results.json"), + ], + scratch, + install_env, + ) + if (output / "installed-results.json").exists(): + report["artifacts"].append( + artifact(output, "installed-results.json") + ) + smoke = strict_json(output / "installed-results.json") + report["installed"] = smoke + else: + report["findings"].append("candidate_wheel_missing") + # Preserve every suite slot even when an upstream prerequisite failed. + for name in sorted(COMMANDS - {c["id"] for c in report["commands"]}): + with (output / (name + ".log")).open("xb"): + pass + report["commands"].append( + { + "id": name, + "status": "not_run", + "exit_code": None, + "elapsed_seconds": None, + "log_bytes": 0, + "log_truncated": False, + "log": name + ".log", + "argv": None, + "reason": "prerequisite_not_passed", + } + ) + report["artifacts"].append(artifact(output, name + ".log")) + # Detect mutations during tests/build; build output is gitignored, source is not. + final_scratch = scratch / "final" + final_scratch.mkdir() + try: + after = candidate_identity(repo, final_scratch, python, deadline) + if after != identity: + report["findings"].append("candidate_changed_during_checks") + except EvidenceError: + report["findings"].append("candidate_final_identity_unavailable") + write_json(output / "report.json", report) + verdict = evaluate(mapping, report, output) + write_json(output / "verdict.json", verdict) + return verdict + + +def main(argv=None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + sub = parser.add_subparsers(dest="action", required=True) + run = sub.add_parser("run") + run.add_argument( + "--output", type=Path, required=True, help="new directory outside checkout" + ) + run.add_argument( + "--wheelhouse", type=Path, help="reviewed offline core dependency wheels" + ) + run.add_argument( + "--node-binary", + type=Path, + help="trusted provisioned Node executable for parser tests only", + ) + run.add_argument( + "--worktree", action="store_true", help="label dirty developer checks" + ) + run.add_argument("--overall-seconds", type=float, default=900) + run.add_argument("--command-seconds", type=float, default=180) + verify = sub.add_parser( + "verify", help="read-only validation; never execute evidence commands" + ) + verify.add_argument("report_dir", type=Path) + verify.add_argument( + "--candidate-sha", required=True, help="expected reviewed Git SHA" + ) + verify.add_argument( + "--snapshot-sha", + required=True, + help="expected reviewed source snapshot SHA-256", + ) + args = parser.parse_args(argv) + try: + if args.action == "run": + verdict = run_suite( + Path(__file__).resolve().parents[1], + args.output, + args.wheelhouse, + args.worktree, + args.overall_seconds, + args.command_seconds, + args.node_binary, + ) + else: + report = strict_json(args.report_dir / "report.json") + require( + report.get("candidate", {}).get("git_sha") == args.candidate_sha, + "unexpected_candidate_sha", + ) + require( + report.get("candidate", {}).get("snapshot_sha256") == args.snapshot_sha, + "unexpected_snapshot_sha", + ) + # Expected mapping is trusted checkout configuration, not imported claims. + mapping = strict_json( + Path(__file__).resolve().parents[1] + / "docs/engineering/acceptance-map.json" + ) + verdict = evaluate(mapping, report, args.report_dir) + print(json.dumps(verdict, sort_keys=True, allow_nan=False)) + return 0 if verdict["checks_only_complete"] else 1 + except (EvidenceError, OSError, KeyError, TypeError, AttributeError, ValueError): + print( + json.dumps( + { + "checks_only_complete": False, + "release_qualified": False, + "error": "qualification_invalid_or_unavailable", + } + ) + ) + return 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_acceptance_edges.py b/tests/test_acceptance_edges.py new file mode 100644 index 0000000..5b933a3 --- /dev/null +++ b/tests/test_acceptance_edges.py @@ -0,0 +1,547 @@ +"""Acceptance supplements using real local storage and the provisioned parser. + +Only ENOSPC is injected. SQLite capacity and child exit are real, bounded faults. +No provider, source execution, package installation, or external fixture is used. +""" + +import errno +import hashlib +import json +import multiprocessing +import os +import shutil +import sqlite3 +from contextlib import contextmanager +from pathlib import Path +from uuid import uuid4 + +import pytest + +from jitmind.code_context import CodeContext, CodeQuery, NodeParser, RepoRegistry +from jitmind.code_memory.anchors import CodeMemoryService +from jitmind.code_memory.lesson_models import ( + DurableBindingAuthority, + DurableFactAuthority, + LessonProjection, + ProposedAction, +) +from jitmind.code_memory.open_work import OpenWorkService +from jitmind.code_memory.preflight import PreflightService +from jitmind.code_memory.work_storage import Budget, WorkDatabase +from jitmind.schemas import MemoryEntry, Page +from jitmind.schemas.advanced_memory import AdvancedMemoryStore +from jitmind.schemas.memory import InMemoryMemoryStore +from jitmind.schemas.page import InMemoryPageStore +from jitmind.schemas.ttl_memory import TTLMemoryStore +from jitmind.schemas.ttl_page import TTLPageStore +from jitmind.scope import ScopeAuthority +from jitmind.storage import ( + DurableMemoryAdapter, + DurablePageAdapter, + IngestRequest, + Proposal, + SQLiteDurableStore, + StorageFailure, + stage_legacy, +) +from jitmind.utils import atomic_io + + +@pytest.mark.parametrize( + "store_class,filename", + [ + (AdvancedMemoryStore, "advanced_memory_state.json"), + (InMemoryMemoryStore, "memory_state.json"), + (TTLMemoryStore, "ttl_memory_state.json"), + (InMemoryPageStore, "pages.json"), + (TTLPageStore, "ttl_pages.json"), + ], + ids=["advanced", "memory", "ttl_memory", "pages", "ttl_pages"], +) +def test_enospc_at_legacy_publication_preserves_ack_and_bytes( + tmp_path, monkeypatch, store_class, filename +): + def reader(): + kwargs = ( + {"enable_auto_cleanup": False} + if store_class in (AdvancedMemoryStore, TTLMemoryStore, TTLPageStore) + else {} + ) + return store_class(str(tmp_path), **kwargs) + + def append(store, body): + store.add( + Page(header="fixture", content=body) + if store_class in (InMemoryPageStore, TTLPageStore) + else body + ) + + def contents(store): + state = store.load() + return ( + state.abstracts + if hasattr(state, "abstracts") + else [p.content for p in state] + ) + + store = reader() + append(store, "previously committed") + path = tmp_path / filename + previous = path.read_bytes() + calls = [] + cause = OSError(errno.ENOSPC, "sanitized_fixture") + + def full(source, destination): + # Exercise the actual post-fsync, pre-publication filesystem boundary. + assert Path(destination) == path + assert Path(source).is_file() + assert b"unacknowledged memory" in Path(source).read_bytes() + calls.append((Path(source).name, Path(destination).name)) + raise cause + + with monkeypatch.context() as fault: + fault.setattr(atomic_io.os, "replace", full) + with pytest.raises(atomic_io.PersistenceError) as raised: + append(store, "unacknowledged memory") + assert len(calls) == 1 + assert raised.value.__cause__ is cause + assert cause.errno == errno.ENOSPC + assert not raised.value.outcome_uncertain + assert "sanitized_fixture" not in str(raised.value) + assert path.read_bytes() == previous + assert contents(store) == contents(reader()) == ["previously committed"] + assert not list(tmp_path.glob("*.tmp")) + append(store, "positive after fault removed") + assert contents(reader()) == [ + "previously committed", + "positive after fault removed", + ] + + +def _proposal(body, operation="add", target=None): + return Proposal( + abstract=body, + header="fixture", + decorated=body, + decision={"operation": operation, "target_id": target}, + ) + + +def test_real_sqlite_full_rolls_back_every_effect_then_accepts_same_request( + tmp_path, monkeypatch +): + store = SQLiteDurableStore(tmp_path / "capacity.sqlite") + original = store._connection + native_errors = [] + capacities = [] + + @contextmanager + def capped(**kwargs): + with original(**kwargs) as connection: + pages = connection.execute("PRAGMA page_count").fetchone()[0] + cap = connection.execute(f"PRAGMA max_page_count={pages}").fetchone()[0] + capacities.append((pages, cap)) + try: + yield connection + except sqlite3.Error as exc: + native_errors.append( + (getattr(exc, "sqlite_errorcode", None), type(exc), str(exc)) + ) + raise + + # Below the public 131072-character input bound; comfortably larger than + # this empty DB's free space. This never attempts to fill the host disk. + request = IngestRequest.create("tenant", "capacity-write", "x" * 100000) + with monkeypatch.context() as fault: + fault.setattr(store, "_connection", capped) + with pytest.raises(StorageFailure, match="^storage_failure$"): + store.ingest(request, lambda _: _proposal("bounded capacity fact")) + assert len(native_errors) == 1 + code, error_type, message = native_errors[0] + if code is None: + # Python 3.10 omits result metadata; retain exact native-engine evidence. + assert error_type is sqlite3.OperationalError + assert message == "database or disk is full" + else: + assert type(code) is int and code & 255 == 13 # SQLite primary SQLITE_FULL + assert capacities and all(count == cap for count, cap in capacities) + fresh = SQLiteDurableStore(store.path) + assert fresh.status("tenant") == { + "revision": 0, + "pending_events": 0, + "delivery_watermark": 0, + "pages": 0, + "facts": 0, + "operations": 0, + } + assert fresh.lookup_receipt(request) is None + assert fresh.snapshot("tenant").entries == () + assert fresh.pending_events("tenant") == [] + assert fresh.projected_entries("tenant") == [] + with sqlite3.connect(store.path) as connection: + for table in ( + "namespaces", + "facts", + "pages", + "operations", + "outbox", + "projection", + ): + assert ( + connection.execute(f"SELECT count(*) FROM {table}").fetchone()[0] == 0 + ) + connection.execute("PRAGMA max_page_count=10000") + assert connection.execute("PRAGMA integrity_check").fetchone()[0] == "ok" + receipt = fresh.ingest(request, lambda _: _proposal("bounded capacity fact")) + assert receipt.revision == 1 + assert ( + fresh.get_entry("tenant", receipt.memory_id).source_page_id == receipt.page_id + ) + assert fresh.get_page("tenant", receipt.page_id).content == request.message + assert fresh.lookup_receipt(request) == receipt + assert len(fresh.pending_events("tenant")) == 1 + assert fresh.ingest(request, lambda _: pytest.fail("replay generated")) == receipt + fresh.deliver_event("tenant", receipt.event_id) + assert [entry.id for entry in fresh.projected_entries("tenant")] == [ + receipt.memory_id + ] + + +def _interrupt_import(source, destination, stage): + staged = stage_legacy(source, quiesced=True) + + def interrupt(current): + if current == stage: + os._exit(73) + + SQLiteDurableStore(destination, fault_hook=interrupt).import_staged( + staged, "tenant" + ) + + +@pytest.mark.parametrize("stage", ["after_import_pages", "before_import_commit"]) +def test_real_migration_exit_preserves_source_and_allows_retry(tmp_path, stage): + source = tmp_path / "legacy" + source.mkdir() + fact = MemoryEntry( + id="legacy-fact", + content="migration fixture fact", + source_page_id="0", + t_created="2026-01-01T00:00:00+00:00", + t_observed="2026-01-01T00:00:00+00:00", + ) + page = Page( + header="fixture", + content="migration provenance", + meta={"page_id": 0, "memory_id": fact.id}, + ) + (source / "advanced_memory_state.json").write_text( + json.dumps({"entries": [fact.model_dump()]}) + ) + (source / "pages.json").write_text(json.dumps([page.model_dump()])) + originals = {p.name: p.read_bytes() for p in source.iterdir()} + path = tmp_path / "candidate.sqlite" + SQLiteDurableStore(path) + child = multiprocessing.get_context("spawn").Process( + target=_interrupt_import, args=(str(source), str(path), stage) + ) + try: + child.start() + child.join(20) + assert child.exitcode == 73, "child must exit at the actual import boundary" + finally: + if child.is_alive(): + child.terminate() + child.join(5) + if child.is_alive(): + child.kill() + child.join(5) + assert not child.is_alive() + child.close() + assert {p.name: p.read_bytes() for p in source.iterdir()} == originals + fresh = SQLiteDurableStore(path) + with sqlite3.connect(path) as connection: + for table in ( + "namespaces", + "pages", + "facts", + "operations", + "outbox", + "aliases", + "imports", + ): + assert ( + connection.execute(f"SELECT count(*) FROM {table}").fetchone()[0] == 0 + ) + assert connection.execute("PRAGMA integrity_check").fetchone()[0] == "ok" + staged = stage_legacy(source, quiesced=True) + report = fresh.import_staged(staged, "tenant") + assert report["page_count"] == report["fact_count"] == 1 + restored = fresh.get_entry("tenant", fact.id) + assert restored.content == fact.content + assert fresh.get_page("tenant", restored.source_page_id).content == page.content + assert fresh.resolve_page_alias("tenant", "0") == restored.source_page_id + assert fresh.import_staged(staged, "tenant") == report + assert fresh.status("tenant")["revision"] == 1 + assert {p.name: p.read_bytes() for p in source.iterdir()} == originals + + +@pytest.mark.parametrize("delay_add", [False, True], ids=["projected", "add_pending"]) +def test_real_parser_binding_projection_redelivery_and_primary_forget( + tmp_path, delay_add +): + sentinel = "MEMORY_ONLY_SENTINEL_83f91c" + source = "def original(x):\n return x + 123\n" + checkout = tmp_path / "checkout" + checkout.mkdir() + path = checkout / "source.py" + path.write_text(source) + assert sentinel not in source + repo = str(uuid4()) + authority = ScopeAuthority() + authority.grant("alice", "tenant", [repo]) + + def scope(): + return authority.context("alice", "tenant", str(uuid4())) + + registry = RepoRegistry() + registry.register(repo, str(checkout.resolve()), "tenant") + adapter = Path( + os.environ.get( + "JITMIND_TEST_GRAFT_ADAPTER", + Path(__file__).resolve().parents[1] / "adapters/graft", + ) + ) + node = os.environ.get("JITMIND_TEST_NODE") or shutil.which("node") + assert node and adapter.is_dir(), ( + "Provision the actual J04 Node/tree-sitter adapter" + ) + context = CodeContext( + authority, + registry, + NodeParser(str(Path(node).resolve()), str(adapter.resolve())), + ) + facts = SQLiteDurableStore(tmp_path / "facts.sqlite") + + def ingest(key, operation="add", target=None): + return facts.ingest( + IngestRequest.create("tenant", key, sentinel), + lambda _: _proposal(sentinel, operation, target), + ) + + fact = ingest("add") + duplicate = ingest("noop", "noop", fact.memory_id) + bindings = CodeMemoryService( + authority, facts, context, tmp_path / "bindings.sqlite" + ) + + def build(): + result = context.build(scope(), repo, deadline_ms=10000) + assert result.status == "ok", result + return result + + first = build() + binding = bindings.create_binding( + scope(), + logical_binding_id="logical", + fact_version_id=fact.memory_id, + repo_id=repo, + snapshot_id=first.snapshot_id, + path="source.py", + qualified_name="original", + ) + assert binding.validation_state == "verified_current" + database = WorkDatabase(tmp_path / "work.sqlite") + work = OpenWorkService(database, authority) + item = work.create( + scope(), + repo, + summary="Complete the outstanding review", + expected_revision=0, + idempotency_key="obligation", + binding_refs=("logical",), + decision_refs=(fact.memory_id,), + ) + assert ( + work.binding_locations(scope(), repo, item.id, bindings)[0]["path"] + == "source.py" + ) + projection = LessonProjection( + database, + authority, + DurableFactAuthority(facts, authority), + binding_authority=DurableBindingAuthority(bindings, authority), + ) + + def project(version): + return projection.project_binding_observation( + scope(), + repo, + bindings, + logical_binding_id="logical", + lesson_id="lesson", + version=version, + source_revision=fact.revision, + body=sentinel, + ) + + assert project(1) + service = PreflightService(projection) + delivery_scope = scope() + session = service.start_session(delivery_scope, repo) + original_target = ProposedAction(repo, "source.py", "original") + before = service.deliver( + delivery_scope, session, original_target, delivery_key="original" + ) + assert before.state == "delivered" and sentinel in before.payload + assert facts.status("tenant")["pending_events"] == 2 + assert facts.status("tenant")["delivery_watermark"] == 0 + assert facts.get_entry("tenant", fact.memory_id).content == sentinel + + # Source generation and fact outbox revision are independent watermarks. + path.rename(checkout / "moved.py") + stale = context.query( + scope(), + CodeQuery( + operation="find_code", + repo_id=repo, + snapshot_id=first.snapshot_id, + query="original", + ), + ) + assert stale.freshness["state"] != "fresh" + assert bindings.get_current(scope(), "logical").validation_state == "unknown" + current = build() + assert ( + current.generation > first.generation + and current.snapshot_id != first.snapshot_id + ) + relocated = bindings.revalidate( + scope(), + "logical", + snapshot_id=current.snapshot_id, + expected_revision=binding.revision, + ) + assert ( + relocated.reason_code == "file_move" + and relocated.validation_state == "verified_current" + ) + assert relocated.source_generation == current.generation + assert ( + work.binding_locations(scope(), repo, item.id, bindings)[0]["path"] + == "moved.py" + ) + assert project(2) + assert project(2) # Same material version is idempotent. + assert not project(1) # Out-of-order lesson cannot replace the moved version. + target = ProposedAction(repo, "moved.py", "original") + lessons = projection.candidates(scope(), target, Budget(2)) + assert len(lessons) == 1 and lessons[0].version == 2 + delivered = service.deliver(delivery_scope, session, target, delivery_key="moved") + assert delivered.state == "delivered" and sentinel in delivered.payload + assert bindings.project_fact( + scope(), fact_version_id=fact.memory_id, event_revision=fact.revision + ) + assert not bindings.project_fact( + scope(), fact_version_id=fact.memory_id, event_revision=fact.revision + ) + facts.deliver_event("tenant", duplicate.event_id) + facts.deliver_event("tenant", duplicate.event_id) + assert facts.status("tenant")["delivery_watermark"] == 0 + if delay_add: + assert facts.status("tenant")["pending_events"] == 1 + assert facts.projected_entries("tenant") == [] + else: + facts.deliver_event("tenant", fact.event_id) + assert facts.status("tenant")["pending_events"] == 0 + assert facts.status("tenant")["delivery_watermark"] == duplicate.revision + assert [entry.id for entry in facts.projected_entries("tenant")] == [ + fact.memory_id + ] + assert ( + bindings.get_current(scope(), "logical").source_generation == current.generation + ) + + deleted = ingest("forget", "delete", fact.memory_id) + # Delay all deletion projections. Current output must already be hidden. + assert facts.status("tenant")["pending_events"] == 1 + int(delay_add) + assert facts.get_entry("tenant", fact.memory_id) is None + assert facts.snapshot("tenant").entries == () + assert facts.projected_entries("tenant") == [] + assert DurableMemoryAdapter(facts, "tenant").load().abstracts == [] + assert DurablePageAdapter(facts, "tenant").load() == [] + for receipt in (fact, duplicate, deleted): + assert facts.get_page("tenant", receipt.page_id) is None + assert sentinel not in repr(facts.memory_update(receipt)) + assert sentinel not in repr(facts.receipt_content(receipt)) + assert sentinel not in receipt.model_dump_json() + assert projection.candidates(scope(), target, Budget(2)) == [] + for action, key in ((original_target, "original"), (target, "moved")): + result = service.deliver(delivery_scope, session, action, delivery_key=key) + assert result.state == "nothing_relevant" and result.payload == "" + assert bindings.get_current(scope(), "logical").reason_code == "fact_unavailable" + assert not project(3) + # New deletion metadata first, then delayed old events: no resurrection. + assert bindings.project_fact( + scope(), fact_version_id=fact.memory_id, event_revision=deleted.revision + ) + assert not bindings.project_fact( + scope(), fact_version_id=fact.memory_id, event_revision=fact.revision + ) + report = projection.forget( + scope(), fact.memory_id, tombstone_revision=deleted.revision + ) + assert report["purge"] == "projection_purged" + assert not project(1) + facts.deliver_event("tenant", deleted.event_id) + if delay_add: + assert facts.status("tenant")["delivery_watermark"] == 0 + assert facts.pending_events("tenant")[0]["event_id"] == fact.event_id + for receipt in (duplicate, fact, deleted): + facts.deliver_event("tenant", receipt.event_id) + assert facts.status("tenant")["pending_events"] == 0 + assert facts.status("tenant")["delivery_watermark"] == deleted.revision + assert facts.projected_entries("tenant") == [] + assert projection.candidates(scope(), target, Budget(2)) == [] + assert work.get(scope(), repo, item.id).status == "open" + assert work.get(scope(), repo, item.id).revision == 1 + assert work.binding_locations(scope(), repo, item.id, bindings) == ( + {"binding_id": "logical", "repo_id": repo, "grain": "repo"}, + ) + history = bindings.history(scope(), "logical") + assert len(history) == 2 and sentinel not in repr(history) + assert sentinel not in repr(work.history(scope(), repo, item.id)) + with database.connect(Budget(2)) as connection: + assert connection.execute("SELECT count(*) FROM lessons").fetchone()[0] == 0 + for table in ( + "history", + "work_receipts", + "deliveries", + "delivery_receipts", + "tombstones", + ): + assert sentinel not in repr( + [tuple(row) for row in connection.execute(f"SELECT * FROM {table}")] + ) + with bindings.store.connection() as connection: + for table in ("history", "requests", "sources", "facts"): + assert sentinel not in repr( + connection.execute(f"SELECT * FROM {table}").fetchall() + ) + # J04 indexes authorized source, not fact bodies. Forgetting a memory does + # not delete source files; raw code remains readable with correct identity. + code = context.query( + scope(), + CodeQuery( + operation="find_code", + repo_id=repo, + snapshot_id=current.snapshot_id, + query="original", + ), + ) + assert code.status == "ok" and code.freshness["state"] == "fresh" + assert len(code.results) == 1 and code.results[0].source == source + assert code.generation == current.generation + assert code.results[0].digest == hashlib.sha256(source.encode()).hexdigest() + assert sentinel not in code.model_dump_json() + assert (checkout / "moved.py").read_text() == source diff --git a/tests/test_qualification.py b/tests/test_qualification.py new file mode 100644 index 0000000..76d4a1e --- /dev/null +++ b/tests/test_qualification.py @@ -0,0 +1,628 @@ +"""Gate/tool tests use controlled evidence, never product-acceptance credit.""" + +from __future__ import annotations + +import copy +import hashlib +import json +from pathlib import Path +import signal +import sys +import threading +import time + +import pytest + +from scripts import qualify_jitmind as q + + +def save(path, data): + path.write_text(json.dumps(data, allow_nan=False)) + + +@pytest.fixture +def bundle(tmp_path): + source = tmp_path / "sources/tests/test_product.py" + source.parent.mkdir(parents=True) + source.write_text("def test_contract():\n assert True\n") + ref = {"path": "tests/test_product.py", "sha256": q.digest(source)} + mapping = { + "schema": "jitmind.acceptance-map/v1", + "source_handoff_sha256": "a" * 64, + "cases": [ + { + "case_id": "J01", + "ticket": "J-01", + "status": "not_run", + "required_in_tickets": ["J-01"], + "stages": [ + { + "stage_id": "J-01", + "status": "not_run", + "nodes": [ + { + "node_id": "tests/test_product.py::test_contract", + "kind": "real_storage", + "source": ref, + "fixtures": [ref], + } + ], + } + ], + } + ], + } + roster = [[ref["path"], ref["sha256"]]] + candidate = { + "git_sha": "b" * 40, + "snapshot_files": roster, + "snapshot_sha256": hashlib.sha256( + json.dumps(roster, separators=(",", ":")).encode() + ).hexdigest(), + "dirty": False, + } + node = mapping["cases"][0]["stages"][0]["nodes"][0]["node_id"] + tests = { + "schema": "jitmind.pytest/v2", + "candidate": copy.deepcopy(candidate), + "collected": [node], + "collection_count": 1, + "exit_code": 0, + "records": [ + { + "node_id": node, + "kind": "real_storage", + "source_sha256": ref["sha256"], + "status": "passed", + "phases": {"setup": "passed", "call": "passed", "teardown": "passed"}, + } + ], + } + expected = { + "schema": "jitmind.collection/v1", + "candidate": copy.deepcopy(candidate), + "nodes": [node], + "selected": [node], + "deselected": [], + "collection_reports": [], + "filtered": False, + } + save(tmp_path / "expected-collection.json", expected) + tests.update( + expected_collection_sha256=q.digest(tmp_path / "expected-collection.json"), + phase_counts={"passed": 3, "failed": 0, "skipped": 0}, + session_testscollected=1, + session_testsfailed=0, + ) + save(tmp_path / "pytest-results.json", tests) + save(tmp_path / "acceptance-map.json", mapping) + (tmp_path / "candidate.whl").write_bytes( + b"controlled-test-fixture-not-an-installable-wheel" + ) + smoke = { + "schema": "jitmind.installed-smoke/v1", + "node_id": q.SMOKE_NODE, + "checks": dict.fromkeys(q.SMOKE_CHECKS, "passed"), + "status": "passed", + "provider_calls": 0, + "source_sha256": "d" * 64, + } + save(tmp_path / "installed-results.json", smoke) + commands = [] + for name in sorted(q.COMMANDS): + (tmp_path / (name + ".log")).write_text( + "controlled fixture, not actual execution\n" + ) + commands.append( + { + "id": name, + "status": "passed", + "exit_code": 0, + "log_truncated": False, + "log": name + ".log", + } + ) + report = { + "schema": "jitmind.qualification/v2", + "candidate": candidate, + "mode": "release", + "commands": commands, + "findings": [], + "test_results": "pytest-results.json", + "expected_collection": "expected-collection.json", + "package_sha256": q.digest(tmp_path / "candidate.whl"), + "acceptance_map": q.artifact(tmp_path, "acceptance-map.json"), + "artifacts": [ + q.artifact(tmp_path, str(p.relative_to(tmp_path))) + for p in tmp_path.rglob("*") + if p.is_file() and p.name != "acceptance-map.json" + ], + } + return tmp_path, mapping, report, tests + + +def refresh(bundle, *, tests=True, mapping=True): + root, expected, report, records = bundle + if tests: + collection = q.strict_json(root / "expected-collection.json") + collection["candidate"] = copy.deepcopy(report["candidate"]) + save(root / "expected-collection.json", collection) + records["expected_collection_sha256"] = q.digest( + root / "expected-collection.json" + ) + if records.get("available", True): + records["phase_counts"] = { + outcome: sum( + p == outcome + for r in records["records"] + for p in r["phases"].values() + ) + for outcome in ("passed", "failed", "skipped") + } + records["session_testscollected"] = records["collection_count"] + records["session_testsfailed"] = records["phase_counts"]["failed"] + sum( + r["outcome"] == "failed" for r in collection["collection_reports"] + ) + else: + records.update( + phase_counts=None, session_testscollected=None, session_testsfailed=None + ) + save(root / "pytest-results.json", records) + if mapping: + save(root / "acceptance-map.json", expected) + report["acceptance_map"] = q.artifact(root, "acceptance-map.json") + report["artifacts"] = [q.artifact(root, ref["path"]) for ref in report["artifacts"]] + + +def verdict(bundle): + root, mapping, report, _ = bundle + return q.evaluate(mapping, report, root) + + +def test_complete_controlled_fixture_is_consistent_but_never_signoff(bundle): + result = verdict(bundle) + assert result["checks_only_complete"] is True + assert result["release_qualified"] is False + assert result["case_count"] == result["stage_count"] == result["passed_cases"] == 1 + assert result["benchmark_measurement"] is None + assert "independent_signoff_not_recorded" in result["release_blockers"] + + +@pytest.mark.parametrize( + "raw", ['{"a":1,"a":2}', '{"n":NaN}', '{"n":Infinity}', '{"n":1e999}', "{"] +) +def test_untrusted_json_rejects_duplicates_nonfinite_truncation(tmp_path, raw): + path = tmp_path / "input.json" + path.write_text(raw) + with pytest.raises(q.EvidenceError): + q.strict_json(path) + + +def test_oversized_json_is_bounded_before_decode(tmp_path): + path = tmp_path / "input.json" + path.write_bytes(b" " * 101) + with pytest.raises(q.EvidenceError, match="input_too_large"): + q.strict_json(path, limit=100) + + +def test_duplicate_case_rejected(bundle): + bundle[1]["cases"].append(copy.deepcopy(bundle[1]["cases"][0])) + refresh(bundle) + with pytest.raises(q.EvidenceError, match="duplicate_case"): + verdict(bundle) + + +def test_missing_repeat_stage_rejected(bundle): + bundle[1]["cases"][0]["required_in_tickets"].append("J-04") + refresh(bundle) + with pytest.raises(q.EvidenceError, match="missing_stage"): + verdict(bundle) + + +def test_unmapped_stage_keeps_denominator(bundle): + case = bundle[1]["cases"][0] + case["required_in_tickets"].append("J-04") + case["stages"].append({"stage_id": "J-04", "nodes": [], "status": "not_run"}) + refresh(bundle) + result = verdict(bundle) + assert result["case_count"] == 1 and result["stage_count"] == 2 + assert result["passed_cases"] == 0 and not result["checks_only_complete"] + + +def test_partial_mapping_cannot_complete_stage(bundle): + bundle[1]["cases"][0]["stages"][0]["additional_mapping_required"] = True + refresh(bundle) + assert not verdict(bundle)["checks_only_complete"] + + +def test_no_checks_false_pass_rejected(bundle): + bundle[2]["commands"] = [] + assert not verdict(bundle)["checks_only_complete"] + + +@pytest.mark.parametrize("field", ["collection_count", "exit_code"]) +def test_boolean_is_not_integer(bundle, field): + bundle[3][field] = True if field == "collection_count" else False + refresh(bundle) + with pytest.raises(q.EvidenceError): + verdict(bundle) + + +def test_modified_artifact_rejected(bundle): + (bundle[0] / "pytest.log").write_text("tampered") + with pytest.raises(q.EvidenceError, match="artifact_modified"): + verdict(bundle) + + +@pytest.mark.parametrize("value", ["main", "a" * 39, "z" * 40, True]) +def test_invalid_candidate_sha(bundle, value): + bundle[2]["candidate"]["git_sha"] = value + with pytest.raises(q.EvidenceError, match="invalid_candidate_sha"): + verdict(bundle) + + +def test_source_identity_mismatch(bundle): + bundle[3]["candidate"]["snapshot_sha256"] = "e" * 64 + refresh(bundle) + with pytest.raises(q.EvidenceError, match="test_source_identity_mismatch"): + verdict(bundle) + + +def test_absent_fixture_never_passes(bundle): + bundle[1]["cases"][0]["stages"][0]["nodes"][0]["fixtures"] = [ + {"path": "fixtures/missing.json", "sha256": "e" * 64} + ] + refresh(bundle) + assert not verdict(bundle)["checks_only_complete"] + + +def test_source_digest_not_current_cannot_pass(bundle): + bundle[1]["cases"][0]["stages"][0]["nodes"][0]["source"]["sha256"] = "e" * 64 + refresh(bundle) + assert not verdict(bundle)["checks_only_complete"] + + +def test_unit_stub_cannot_be_product_acceptance(bundle): + bundle[3]["records"][0]["kind"] = "unit_stub" + refresh(bundle) + assert not verdict(bundle)["checks_only_complete"] + + +def test_synthetic_measurement_cannot_claim_unrun_real_comparison(bundle): + bundle[2]["benchmark_measurement"] = { + "workload": "real_donor", + "synthetic": True, + "latency": 0, + } + with pytest.raises(q.EvidenceError, match="unreviewed_benchmark_claim"): + verdict(bundle) + + +def test_empty_pytest_collection_never_passes(bundle): + bundle[3].update(collected=[], records=[], collection_count=0) + refresh(bundle) + assert not verdict(bundle)["checks_only_complete"] + + +def test_missing_expected_node_never_passes(bundle): + bundle[1]["cases"][0]["stages"][0]["nodes"][0]["node_id"] += "_missing" + refresh(bundle) + assert not verdict(bundle)["checks_only_complete"] + + +@pytest.mark.parametrize( + "phase,outcome", [("call", "failed"), ("setup", "skipped"), ("teardown", "failed")] +) +def test_nonpass_retains_case_and_count(bundle, phase, outcome): + record = bundle[3]["records"][0] + record["phases"][phase] = outcome + record["status"] = outcome + refresh(bundle) + result = verdict(bundle) + assert not result["checks_only_complete"] and result["case_count"] == 1 + assert result["test_counts"][outcome] == 1 + + +def test_missing_teardown_is_unknown(bundle): + record = bundle[3]["records"][0] + del record["phases"]["teardown"] + record["status"] = "unknown" + refresh(bundle) + assert verdict(bundle)["test_counts"]["unknown"] == 1 + + +def test_claimed_pass_with_failed_phase_rejected(bundle): + bundle[3]["records"][0]["phases"]["call"] = "failed" + refresh(bundle) + with pytest.raises(q.EvidenceError, match="false_test_pass"): + verdict(bundle) + + +def test_duplicate_test_record_rejected(bundle): + bundle[3]["records"].append(copy.deepcopy(bundle[3]["records"][0])) + refresh(bundle) + with pytest.raises(q.EvidenceError, match="duplicate_or_uncollected_test"): + verdict(bundle) + + +def test_truncated_logs_never_pass(bundle): + bundle[2]["commands"][0]["log_truncated"] = True + assert not verdict(bundle)["checks_only_complete"] + + +def test_missing_checker_retains_existing_finding(bundle): + bundle[2]["findings"] = ["known_prior_failure"] + bundle[2]["commands"].pop() + result = verdict(bundle) + assert result["findings"] and not result["checks_only_complete"] + + +def test_dirty_release_refused_worktree_labeled(bundle): + bundle[2]["candidate"]["dirty"] = True + bundle[3]["candidate"]["dirty"] = True + refresh(bundle) + with pytest.raises(q.EvidenceError, match="dirty_release"): + verdict(bundle) + bundle[2]["mode"] = "worktree" + assert "worktree_candidate" in verdict(bundle)["release_blockers"] + + +def test_artifact_escape_and_symlink_refused(tmp_path): + (tmp_path / "target").write_text("x") + (tmp_path / "link").symlink_to(tmp_path / "target") + for name in ["../outside", "/etc/passwd", "link", "a\\b"]: + with pytest.raises(q.EvidenceError): + q.artifact(tmp_path, name) + + +def test_exclusive_report_creation(tmp_path): + path = tmp_path / "report.json" + q.write_json(path, {"original": True}) + with pytest.raises(FileExistsError): + q.write_json(path, {"changed": True}) + assert q.strict_json(path) == {"original": True} + + +def child(tmp_path, code, *, seconds=2, max_bytes=4096, cancel=None): + return q.run_command( + [sys.executable, "-I", "-c", code], + cwd=tmp_path, + env=q.clean_env(tmp_path, Path(sys.executable)), + log=tmp_path / "child.log", + deadline=time.monotonic() + seconds, + timeout=seconds, + max_bytes=max_bytes, + cancel=cancel, + ) + + +def test_process_both_pipes_drained_and_output_bounded(tmp_path): + result = child( + tmp_path, + 'import os\nwhile True:\n os.write(1,b"a"*8192)\n os.write(2,b"b"*8192)', + ) + assert result["status"] == "output_limit" + assert result["log_truncated"] and (tmp_path / "child.log").stat().st_size == 4096 + + +def test_process_nonzero_has_no_automatic_retry(tmp_path): + result = child(tmp_path, 'import sys; print("once"); sys.exit(7)') + assert result["status"] == "failed" and result["exit_code"] == 7 + assert (tmp_path / "child.log").read_text() == "once\n" + + +def test_process_timeout_reaps_leader(tmp_path): + result = child(tmp_path, "import time; time.sleep(60)", seconds=0.2) + assert result["status"] == "timeout" and result["exit_code"] == -signal.SIGKILL + assert result["elapsed_seconds"] < 2 + + +def test_process_cancellation(tmp_path): + cancel = threading.Event() + timer = threading.Timer(0.1, cancel.set) + timer.start() + try: + result = child(tmp_path, "import time; time.sleep(60)", cancel=cancel) + finally: + timer.join() + assert result["status"] == "cancelled" + + +def test_exited_parent_with_grandchild_pipes_times_out(tmp_path): + result = child( + tmp_path, + 'import subprocess,sys; subprocess.Popen([sys.executable,"-I","-c","import time; time.sleep(60)"])', + seconds=0.2, + ) + assert result["status"] == "timeout" and result["elapsed_seconds"] < 2 + + +def test_deadline_exhausted_does_not_launch(tmp_path): + result = q.run_command( + [sys.executable, "-c", "raise Exception()"], + cwd=tmp_path, + env={}, + log=tmp_path / "child.log", + deadline=time.monotonic() - 1, + ) + assert result["status"] == "timeout" and result["exit_code"] is None + + +def test_environment_excludes_ambient_keys_and_pythonpath(tmp_path, monkeypatch): + monkeypatch.setenv("OPENAI_API_KEY", "sentinel") + monkeypatch.setenv("PYTHONPATH", "/bad") + monkeypatch.setenv("PYTEST_ADDOPTS", "--bad") + monkeypatch.setenv("PIP_INDEX_URL", "https://secret") + result = child(tmp_path, "import os,json; print(json.dumps(dict(os.environ)))") + assert result["status"] == "passed" + env = json.loads((tmp_path / "child.log").read_text()) + assert ( + not {"OPENAI_API_KEY", "PYTHONPATH", "PYTEST_ADDOPTS", "PIP_INDEX_URL"} + & env.keys() + ) + + +def test_verification_import_never_executes_argv(bundle, monkeypatch): + bundle[2]["commands"][0]["argv"] = ["sh", "-c", "arbitrary effects"] + monkeypatch.setattr( + q.subprocess, "Popen", lambda *a, **k: pytest.fail("executed evidence") + ) + verdict(bundle) + + +def test_installed_smoke_missing_checks_is_not_a_pass(bundle): + root = bundle[0] + smoke = q.strict_json(root / "installed-results.json") + smoke["checks"].pop("missing_adapter_capability") + save(root / "installed-results.json", smoke) + refresh(bundle) + with pytest.raises(q.EvidenceError, match="invalid_smoke_checks"): + verdict(bundle) + + +def test_installed_smoke_failure_blocks_generic_pytest_success(bundle): + root = bundle[0] + smoke = q.strict_json(root / "installed-results.json") + smoke["checks"]["missing_adapter_capability"] = "not_run" + save(root / "installed-results.json", smoke) + refresh(bundle) + assert not verdict(bundle)["checks_only_complete"] + + +def test_real_pytest_plugin_records_exact_nodes_and_teardown(tmp_path): + repo = Path(__file__).resolve().parents[1] + (tmp_path / "test_sample.py").write_text("def test_one():\n assert 2 + 2 == 4\n") + output = tmp_path / "pytest-results.json" + context = tmp_path / "context.json" + q.write_json( + context, + {"repo": str(tmp_path), "output": str(output), "candidate": {}, "kinds": {}}, + ) + env = q.clean_env(tmp_path, Path(sys.executable), repo) + env["JITMIND_QUALIFICATION_CONTEXT"] = str(context) + result = q.run_command( + [ + sys.executable, + "-m", + "pytest", + "-q", + "-p", + "scripts.qualify_jitmind", + "test_sample.py", + ], + cwd=tmp_path, + env=env, + log=tmp_path / "pytest.log", + deadline=time.monotonic() + 10, + ) + assert result["status"] == "passed", (tmp_path / "pytest.log").read_text() + record = q.strict_json(output) + assert record["collected"] == ["test_sample.py::test_one"] + assert record["collection_count"] == 1 + assert record["records"][0]["phases"] == dict.fromkeys( + ["setup", "call", "teardown"], "passed" + ) + assert record["records"][0]["kind"] == "unmapped" + + +def test_shipped_map_preserves_32_cases_41_stages_and_not_run(): + path = Path(__file__).resolve().parents[1] / "docs/engineering/acceptance-map.json" + mapping = q.strict_json(path) + cases = q.validate_map(mapping) + assert len(cases) == 32 and sum(len(c["stages"]) for c in cases) == 41 + assert all(c["status"] == "not_run" for c in cases) + assert [x[-1] for x in mapping["temporal_oracle"]["answers"]] == [ + 3, + 3, + 4, + 3, + 4, + 5, + 3, + ] + + +def test_missing_test_artifact_measurements_are_null(bundle): + bundle[3].update( + available=False, collected=[], collection_count=None, records=[], exit_code=99 + ) + refresh(bundle) + result = verdict(bundle) + assert result["collection_count"] is None + assert result["test_counts"] == dict.fromkeys( + ["passed", "failed", "skipped", "unknown"] + ) + assert not result["checks_only_complete"] + + +def test_tool_tests_cannot_be_mapped_as_product_acceptance(bundle): + bundle[1]["cases"][0]["stages"][0]["nodes"][0]["node_id"] = ( + "tests/test_qualification.py::test_fake" + ) + refresh(bundle) + with pytest.raises(q.EvidenceError, match="tool_test_is_not_acceptance"): + verdict(bundle) + + +def test_unknown_external_finding_is_retained_without_echoing_payload(bundle): + bundle[2]["findings"] = ["private-path API_KEY=negative-sentinel"] + result = verdict(bundle) + assert result["findings"] == ["retained_external_finding"] + assert "negative-sentinel" not in json.dumps(result) + + +def test_timeout_kills_owned_grandchild(tmp_path): + import os + + pid_path = tmp_path / "grandchild.pid" + script = ( + "import pathlib,subprocess,sys,time; " + 'p=subprocess.Popen([sys.executable,"-I","-c","import time; time.sleep(60)"]); ' + f"pathlib.Path({str(pid_path)!r}).write_text(str(p.pid)); time.sleep(60)" + ) + result = child(tmp_path, script, seconds=1) + assert result["status"] == "timeout" + pid = int(pid_path.read_text()) + until = time.monotonic() + 2 + while time.monotonic() < until: + try: + os.kill(pid, 0) + except ProcessLookupError: + return + linux_state = Path(f"/proc/{pid}/stat") + if linux_state.exists() and linux_state.read_text().split()[2] == "Z": + return # direct child is reaped; orphan zombie belongs to OS init + time.sleep(0.01) + pytest.fail("owned grandchild remained live after timeout") + + +@pytest.mark.parametrize("target", ["command_exit", "artifact_bytes"]) +def test_evidence_boolean_numeric_fields_refused(bundle, target): + if target == "command_exit": + bundle[2]["commands"][0]["exit_code"] = False + else: + bundle[2]["artifacts"][0]["bytes"] = True + with pytest.raises(q.EvidenceError): + verdict(bundle) + + +def test_json_symlinks_and_special_files_refused(tmp_path): + import os + + path = tmp_path / "fifo" + os.mkfifo(path) + with pytest.raises(q.EvidenceError): + q.strict_json(path) + target = tmp_path / "real.json" + target.write_text("{}") + link = tmp_path / "link.json" + link.symlink_to(target) + with pytest.raises(q.EvidenceError): + q.strict_json(link) + + +def test_hashing_observes_exhausted_deadline(tmp_path): + path = tmp_path / "source" + path.write_bytes(b"source bytes") + with pytest.raises(q.EvidenceError, match="overall_deadline"): + q.digest(path, deadline=time.monotonic() - 1) diff --git a/tests/test_qualification_contracts.py b/tests/test_qualification_contracts.py new file mode 100644 index 0000000..c9b935c --- /dev/null +++ b/tests/test_qualification_contracts.py @@ -0,0 +1,32 @@ +"""Reporting-contract controls, not installed-product acceptance evidence.""" + +import pytest + +from jitmind.code_context.process import Unavailable +from test_standalone_install import missing_adapter_capability + + +def test_smoke_accepts_real_typed_missing_runtime(tmp_path): + missing_adapter_capability(tmp_path) + + +@pytest.mark.parametrize( + "error,expected", + [ + (Unavailable("unrelated_failure"), AssertionError), + (Unavailable("runtime_unavailable"), AssertionError), + (FileNotFoundError("node_executable_unavailable"), FileNotFoundError), + (AttributeError("node_executable_unavailable"), AttributeError), + ], +) +def test_smoke_does_not_accept_wrong_reason_or_untyped_error( + tmp_path, monkeypatch, error, expected +): + import jitmind.code_context as module + + def fail(**kwargs): + raise error + + monkeypatch.setattr(module, "NodeParser", fail) + with pytest.raises(expected): + missing_adapter_capability(tmp_path) diff --git a/tests/test_qualification_regressions.py b/tests/test_qualification_regressions.py new file mode 100644 index 0000000..e7e12ff --- /dev/null +++ b/tests/test_qualification_regressions.py @@ -0,0 +1,493 @@ +"""Reproduced review defects; controlled tool evidence, never acceptance credit.""" + +import copy +import hashlib +import json +import os +from pathlib import Path +import signal +import subprocess +import sys +import time + +import pytest + +from scripts import qualify_jitmind as q +import test_qualification as helpers +from test_qualification import refresh, verdict + + +@pytest.fixture +def bundle(tmp_path): + return helpers.bundle.__wrapped__(tmp_path) + + +def rejected(bundle): + try: + assert not verdict(bundle)["checks_only_complete"] + except q.EvidenceError: + pass + + +@pytest.mark.parametrize( + "corruption", ["digest", "bytes", "missing", "duplicate", "alias"] +) +def test_snapshot_roster_is_bound(bundle, corruption): + identity = bundle[2]["candidate"] + name = "tests/test_product.py" + roster = [[name, q.digest(bundle[0] / "sources" / name)]] + if corruption == "bytes": + roster[0][1] = "e" * 64 + elif corruption == "missing": + roster.append(["absent.py", "e" * 64]) + elif corruption == "duplicate": + roster *= 2 + elif corruption == "alias": + roster[0][0] = "tests/./test_product.py" + identity["snapshot_files"] = roster + identity["snapshot_sha256"] = hashlib.sha256( + json.dumps(roster, separators=(",", ":")).encode() + ).hexdigest() + if corruption == "digest": + identity["snapshot_sha256"] = "e" * 64 + bundle[3]["candidate"] = copy.deepcopy(identity) + refresh(bundle) + rejected(bundle) + + +@pytest.mark.parametrize("mode", ["filter", "skip", "importorskip", "error"]) +def test_real_collection_omissions_are_recorded(tmp_path, mode): + (tmp_path / "tests").mkdir() + (tmp_path / "tests/test_good.py").write_text( + "def test_expected(): pass\ndef test_other(): pass\n" + ) + if mode == "filter": + (tmp_path / "pytest.ini").write_text("[pytest]\naddopts = -k test_expected\n") + else: + text = { + "skip": 'import pytest\npytest.skip("collection", allow_module_level=True)', + "importorskip": 'import pytest\npytest.importorskip("jitmind_nonexistent_fixture_module")', + "error": 'raise RuntimeError("collection failure")', + }[mode] + (tmp_path / "tests/test_bad.py").write_text(text) + output = tmp_path / "pytest-results.json" + q.write_json( + tmp_path / "context.json", + {"repo": str(tmp_path), "output": str(output), "candidate": {}, "kinds": {}}, + ) + env = q.clean_env( + tmp_path, Path(sys.executable), Path(__file__).resolve().parents[1] + ) + env["JITMIND_QUALIFICATION_CONTEXT"] = str(tmp_path / "context.json") + q.run_command( + [ + sys.executable, + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "-p", + "scripts.qualify_jitmind", + "tests", + ], + cwd=tmp_path, + env=env, + log=tmp_path / "pytest.log", + deadline=time.monotonic() + 10, + ) + data = q.strict_json(output) + expected = q.strict_json(tmp_path / "expected-collection.json") + assert len(expected["nodes"]) == 2 + if mode == "filter": + assert expected["deselected"] == ["tests/test_good.py::test_other"] + assert expected["filtered"] is True + else: + assert expected["collection_reports"][-1]["outcome"] == ( + "failed" if mode == "error" else "skipped" + ) + assert data["expected_collection_sha256"] == q.digest( + tmp_path / "expected-collection.json" + ) + + +def test_oversized_copy_rejected_before_destination(tmp_path, monkeypatch): + monkeypatch.setattr(q, "MAX_ARTIFACT", 4096) + src, dst = tmp_path / "large.whl", tmp_path / "copy.whl" + src.write_bytes(b"x" * 5000) + with pytest.raises(q.EvidenceError, match="artifact_too_large"): + q.bounded_copy(src, dst) + assert not dst.exists() + + +def alive(pid): + try: + os.kill(pid, 0) + except ProcessLookupError: + return False + stat = Path(f"/proc/{pid}/stat") + return not (stat.exists() and stat.read_text().split()[2] == "Z") + + +@pytest.mark.parametrize("sig", [signal.SIGINT, signal.SIGTERM]) +def test_signal_wrapper_cleans_owned_descendants(tmp_path, sig): + repo = Path(__file__).resolve().parents[1] + grand = "import time; time.sleep(60)" + code = f'import os,pathlib,subprocess,sys,time; p=subprocess.Popen([sys.executable,"-c",{grand!r}]); pathlib.Path("pids").write_text(str(os.getpid())+" "+str(p.pid)); time.sleep(60)' + wrapper = f'from scripts import qualify_jitmind as q; from pathlib import Path; import sys,time,json; p=Path.cwd(); r=q.run_command([sys.executable,"-c",{code!r}],cwd=p,env=q.clean_env(p,Path(sys.executable)),log=p/"child.log",deadline=time.monotonic()+10); print(json.dumps(r))' + pids = [] + with (tmp_path / "wrapper.log").open("wb") as log: + proc = subprocess.Popen( + [sys.executable, "-c", wrapper], + cwd=tmp_path, + env=q.clean_env(tmp_path, Path(sys.executable), repo), + stdout=log, + stderr=log, + ) + try: + end = time.monotonic() + 5 + while not (tmp_path / "pids").exists() and time.monotonic() < end: + time.sleep(0.01) + pids = [int(x) for x in (tmp_path / "pids").read_text().split()] + proc.send_signal(sig) + assert proc.wait(timeout=3) == 0 + assert ( + json.loads((tmp_path / "wrapper.log").read_text())["status"] + == "cancelled" + ) + end = time.monotonic() + 2 + while any(alive(pid) for pid in pids) and time.monotonic() < end: + time.sleep(0.01) + assert not any(alive(pid) for pid in pids) + finally: + if proc.poll() is None: + proc.kill() + proc.wait(timeout=3) + for pid in pids: + try: + os.kill(pid, signal.SIGKILL) + except ProcessLookupError: + pass + + +def test_deleting_unmapped_failure_does_not_shrink_expected_collection(bundle): + root, _, _, tests = bundle + extra = "tests/test_product.py::test_unmapped_failure" + expected = q.strict_json(root / "expected-collection.json") + expected["nodes"].append(extra) + expected["selected"].append(extra) + (root / "expected-collection.json").write_text(json.dumps(expected)) + tests["collected"].append(extra) + tests["collection_count"] += 1 + tests["records"].append( + dict( + node_id=extra, + kind="unmapped", + source_sha256=tests["records"][0]["source_sha256"], + status="failed", + phases=dict(setup="passed", call="failed", teardown="passed"), + ) + ) + tests["exit_code"] = 1 + refresh(bundle) + assert not verdict(bundle)["checks_only_complete"] + tests["records"].pop() + tests["collected"].pop() + tests["collection_count"] -= 1 + tests["exit_code"] = 0 + refresh(bundle) + assert not verdict(bundle)["checks_only_complete"] + + +def test_missing_unmapped_record_retains_unknown_denominator(bundle): + root, _, _, tests = bundle + extra = "tests/test_product.py::test_missing_record" + expected = q.strict_json(root / "expected-collection.json") + expected["nodes"].append(extra) + expected["selected"].append(extra) + (root / "expected-collection.json").write_text(json.dumps(expected)) + tests["collected"].append(extra) + tests["collection_count"] += 1 + refresh(bundle) + result = verdict(bundle) + assert not result["checks_only_complete"] + assert result["test_counts"]["unknown"] == 1 + + +@pytest.mark.parametrize("field", ["nodes", "selected", "deselected"]) +def test_duplicate_collection_nodes_refused(bundle, field): + path = bundle[0] / "expected-collection.json" + expected = q.strict_json(path) + expected[field] = expected["nodes"] * 2 + path.write_text(json.dumps(expected)) + refresh(bundle) + with pytest.raises(q.EvidenceError, match="duplicate_or_invalid_collection"): + verdict(bundle) + + +@pytest.mark.parametrize( + "field,value", + [ + ("session_testscollected", 2), + ("session_testsfailed", 1), + ("session_testsfailed", False), + ("phase_counts", {"passed": 4, "failed": 0, "skipped": 0}), + ], +) +def test_inconsistent_plugin_counters_refused(bundle, field, value): + bundle[3][field] = value + (bundle[0] / "pytest-results.json").write_text(json.dumps(bundle[3])) + refresh(bundle, tests=False) + with pytest.raises(q.EvidenceError, match="inconsistent_plugin_counters"): + verdict(bundle) + + +@pytest.mark.parametrize("field", ["filtered", "deselected", "collection_reports"]) +def test_collection_incompleteness_blocks_evaluator(bundle, field): + path = bundle[0] / "expected-collection.json" + expected = q.strict_json(path) + if field == "filtered": + expected[field] = True + elif field == "deselected": + expected["nodes"].append("tests/test_product.py::test_omitted") + expected[field] = ["tests/test_product.py::test_omitted"] + else: + expected[field] = [{"node_id": "tests/test_skip.py", "outcome": "skipped"}] + path.write_text(json.dumps(expected)) + refresh(bundle) + assert not verdict(bundle)["checks_only_complete"] + + +def test_runtime_record_source_must_match_snapshot(bundle): + bundle[3]["records"][0]["source_sha256"] = "e" * 64 + refresh(bundle) + with pytest.raises(q.EvidenceError, match="record_source_mismatch"): + verdict(bundle) + + +def test_expected_collection_identity_and_digest_are_independent(bundle): + bundle[3]["expected_collection_sha256"] = "e" * 64 + (bundle[0] / "pytest-results.json").write_text(json.dumps(bundle[3])) + refresh(bundle, tests=False) + with pytest.raises(q.EvidenceError, match="collection_digest_mismatch"): + verdict(bundle) + + +@pytest.mark.parametrize("change", ["grow", "replace", "same_size"]) +def test_copy_rejects_source_change_during_stream(tmp_path, monkeypatch, change): + source, dest = tmp_path / "source", tmp_path / "dest" + source.write_bytes(b"a" * 32) + monkeypatch.setattr(q, "MAX_ARTIFACT", 64) + real_fdopen = os.fdopen + requests = [] + + class ChangingReader: + def __init__(self, fd, mode, **kwargs): + self.stream = real_fdopen(fd, mode, **kwargs) + self.changed = False + + def __enter__(self): + return self + + def __exit__(self, *args): + self.stream.close() + + def fileno(self): + return self.stream.fileno() + + def read(self, size): + requests.append(size) + if not self.changed: + self.changed = True + if change == "grow": + with source.open("ab") as out: + out.write(b"b" * 64) + elif change == "replace": + other = tmp_path / "replacement" + other.write_bytes(b"a" * 32) + other.replace(source) + else: + source.write_bytes(b"b" * 32) + return self.stream.read(size) + + monkeypatch.setattr(q.os, "fdopen", ChangingReader) + with pytest.raises( + q.EvidenceError, match="artifact_too_large|source_changed_during_copy" + ): + q.bounded_copy(source, dest) + assert max(requests) <= 65 + assert not dest.exists() + + +def test_copy_exclusive_hash_deadline_and_exact_limit(tmp_path, monkeypatch): + source, dest = tmp_path / "source", tmp_path / "dest" + source.write_bytes(b"a" * 64) + monkeypatch.setattr(q, "MAX_ARTIFACT", 64) + assert q.bounded_copy(source, dest, expected_hash=q.digest(source)) == q.digest( + dest + ) + with pytest.raises(FileExistsError): + q.bounded_copy(source, dest) + assert dest.read_bytes() == b"a" * 64 + dest.unlink() + with pytest.raises(q.EvidenceError, match="source_hash_mismatch"): + q.bounded_copy(source, dest, expected_hash="e" * 64) + assert not dest.exists() + with pytest.raises(q.EvidenceError, match="overall_deadline"): + q.bounded_copy(source, dest, deadline=time.monotonic() - 1) + assert not dest.exists() + + +def test_copy_symlink_refused(tmp_path): + source = tmp_path / "source" + source.symlink_to(__file__) + with pytest.raises(q.EvidenceError, match="artifact_missing_or_symlink"): + q.bounded_copy(source, tmp_path / "dest") + + +def test_signals_restored_and_worker_threads_do_not_mutate_signals( + tmp_path, monkeypatch +): + import threading + + previous = signal.getsignal(signal.SIGTERM) + seen = [] + real_signal = signal.signal + + def observed(*args): + seen.append(threading.current_thread()) + return real_signal(*args) + + monkeypatch.setattr(q.signal, "signal", observed) + + def run(name): + return q.run_command( + [sys.executable, "-c", 'print("done")'], + cwd=tmp_path, + env=q.clean_env(tmp_path, Path(sys.executable)), + log=tmp_path / name, + deadline=time.monotonic() + 3, + ) + + assert run("main.log")["status"] == "passed" + assert signal.getsignal(signal.SIGTERM) is previous + assert len(seen) == 2 + results = [] + thread = threading.Thread(target=lambda: results.append(run("thread.log"))) + thread.start() + thread.join(timeout=5) + assert not thread.is_alive() + assert results[0]["status"] == "passed" + assert len(seen) == 2 + + +@pytest.mark.parametrize("mode", ["normal", "timeout", "loglimit"]) +def test_owned_group_cleanup_does_not_kill_unrelated_child(tmp_path, mode): + other = subprocess.Popen([sys.executable, "-c", "import time; time.sleep(30)"]) + pids = [] + grand = "import time; time.sleep(30)" + ending = { + "normal": "sys.exit(0)", + "timeout": "time.sleep(30)", + "loglimit": '\nwhile True: os.write(1,b"x"*8192)', + }[mode] + script = f'import os,pathlib,subprocess,sys,time; p=subprocess.Popen([sys.executable,"-c",{grand!r}],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL); pathlib.Path("pids").write_text(str(os.getpid())+" "+str(p.pid)); {ending}' + try: + result = q.run_command( + [sys.executable, "-c", script], + cwd=tmp_path, + env=q.clean_env(tmp_path, Path(sys.executable)), + log=tmp_path / "child.log", + deadline=time.monotonic() + 1, + max_bytes=100, + ) + pids = list(map(int, (tmp_path / "pids").read_text().split())) + assert ( + result["status"] + == {"normal": "passed", "timeout": "timeout", "loglimit": "output_limit"}[ + mode + ] + ) + end = time.monotonic() + 2 + while any(alive(pid) for pid in pids) and time.monotonic() < end: + time.sleep(0.01) + assert not any(alive(pid) for pid in pids) + assert other.poll() is None + finally: + other.kill() + other.wait(timeout=3) + for pid in pids: + try: + os.kill(pid, signal.SIGKILL) + except ProcessLookupError: + pass + + +def test_real_plugin_result_evaluates_with_attested_source(bundle, tmp_path): + root, _, report, tests = bundle + execution = root / "execution" + execution.mkdir() + q.write_json( + execution / "context.json", + { + "repo": str(root / "sources"), + "output": str(execution / "pytest-results.json"), + "candidate": report["candidate"], + "kinds": {tests["collected"][0]: "real_storage"}, + }, + ) + env = q.clean_env( + execution, Path(sys.executable), Path(__file__).resolve().parents[1] + ) + env["JITMIND_QUALIFICATION_CONTEXT"] = str(execution / "context.json") + result = q.run_command( + [ + sys.executable, + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "-p", + "scripts.qualify_jitmind", + "tests", + ], + cwd=root / "sources", + env=env, + log=execution / "pytest.log", + deadline=time.monotonic() + 10, + ) + assert result["status"] == "passed" + for name in ("pytest-results.json", "expected-collection.json"): + (root / name).write_bytes((execution / name).read_bytes()) + refresh(bundle, tests=False) + assert verdict(bundle)["checks_only_complete"] + + +def test_normal_completion_closes_both_pipes(tmp_path, monkeypatch): + real_popen = q.subprocess.Popen + children = [] + + def capture(*args, **kwargs): + child = real_popen(*args, **kwargs) + children.append(child) + return child + + monkeypatch.setattr(q.subprocess, "Popen", capture) + previous = signal.getsignal(signal.SIGTERM) + handler = lambda _signum, _frame: None + signal.signal(signal.SIGTERM, handler) + try: + result = q.run_command( + [sys.executable, "-c", 'print("ok")'], + cwd=tmp_path, + env=q.clean_env(tmp_path, Path(sys.executable)), + log=tmp_path / "child.log", + deadline=time.monotonic() + 3, + ) + assert result["status"] == "passed" + assert signal.getsignal(signal.SIGTERM) is handler + assert children[0].returncode == 0 + assert children[0].stdout.closed and children[0].stderr.closed + finally: + signal.signal(signal.SIGTERM, previous) diff --git a/tests/test_standalone_install.py b/tests/test_standalone_install.py new file mode 100644 index 0000000..b60f955 --- /dev/null +++ b/tests/test_standalone_install.py @@ -0,0 +1,216 @@ +"""Executable installed-wheel smoke. No pytest dependency in the installed venv. + +Run only with: /bin/python -I /tests/test_standalone_install.py + --installed-smoke +A pytest run of this file tests smoke guards; it is NOT installed acceptance. +""" + +from __future__ import annotations + +import argparse +import hashlib +import importlib.metadata +import importlib.util +import json +import os +from pathlib import Path +import shutil +import socket +import sqlite3 +import sys +import tempfile + +SMOKE_NODE = "tests/test_standalone_install.py::installed_smoke" +CHECKS = { + "isolated_origin", + "optional_dependencies_absent", + "legacy_memory_research", + "optional_adapter_import", + "missing_adapter_capability", +} + + +def verify_isolation(venv: Path, origin: Path) -> None: + assert sys.flags.isolated == 1, "isolated_interpreter_required" + assert "PYTHONPATH" not in os.environ, "pythonpath_must_be_absent" + assert Path(sys.prefix).resolve() == venv.resolve(), "wrong_virtual_environment" + assert sys.prefix != sys.base_prefix, "fresh_virtual_environment_required" + assert origin.resolve().is_relative_to(venv.resolve()), "checkout_import_refused" + checkout = Path(__file__).resolve().parents[1] + assert not Path.cwd().resolve().is_relative_to(checkout), "checkout_cwd_refused" + assert not any(Path(p).resolve() == checkout for p in sys.path if p), ( + "checkout_path_refused" + ) + + +def legacy_roundtrip(directory: Path) -> None: + from jitmind import MemoryAgent, ResearchAgent + from jitmind.schemas import InMemoryMemoryStore, InMemoryPageStore + + class FakeGenerator: + def __init__(self): + self.calls = [] + + def generate_single(self, prompt=None, schema=None, **kwargs): + self.calls.append(schema) + properties = (schema or {}).get("properties", {}) + if "operation" in properties: + return {"json": {"operation": "add"}} + if "info_needs" in properties: + return {"json": {"tools": ["page_index"], "page_index": [0]}} + if "content" in properties: + assert "cobalt" in prompt, "retrieved_page_missing_from_prompt" + return {"json": {"content": "cobalt preference", "sources": ["0"]}} + if "enough" in properties: + return {"json": {"enough": True}} + if schema is None: + return {"text": "cobalt preference"} + raise AssertionError("unexpected_generator_contract") + + generator = FakeGenerator() + memory = InMemoryMemoryStore(dir_path=str(directory)) + pages = InMemoryPageStore(dir_path=str(directory)) + agent = MemoryAgent(memory_store=memory, page_store=pages, generator=generator) + result = agent.memorize("cobalt is the user's preferred color") + assert result.new_state.abstracts == ["cobalt preference"] + memory = InMemoryMemoryStore(dir_path=str(directory)) + pages = InMemoryPageStore(dir_path=str(directory)) + assert memory.load().abstracts == ["cobalt preference"] + assert pages.get(0).content == "cobalt is the user's preferred color" + research = ResearchAgent( + page_store=pages, + memory_store=memory, + generator=generator, + max_iters=1, + enable_hyde=False, + enable_self_rag=False, + ) + answer = research.research("preferred color") + assert answer.integrated_memory == "cobalt preference" + assert answer.raw_memory["temp_memory"]["sources"] == ["0"] + assert len(generator.calls) == 5, "public_api_did_not_use_expected_generator_calls" + + +def missing_adapter_capability(directory: Path) -> None: + """Exercise actual public missing-runtime handling, never a mocked parser. + + This deliberately fails until the assembled adapter supports an absent runtime + with a typed capability failure or an unavailable result. Arbitrary IO/import + exceptions and AttributeError are not meaningful capability errors. + """ + from jitmind.code_context import NodeParser + from jitmind.code_context.process import Unavailable + + try: + NodeParser( + node_binary=str(directory / "absent-node"), + adapter_dir=str(directory / "absent-adapter"), + ) + except Unavailable as error: + assert error.reason == "node_executable_unavailable" + else: + raise AssertionError("missing_runtime_requires_capability_error") + + +def installed_smoke(venv: Path) -> dict: + results = {name: "not_run" for name in sorted(CHECKS)} + report = { + "schema": "jitmind.installed-smoke/v1", + "node_id": SMOKE_NODE, + "checks": results, + "status": "failed", + "kind": "installed_package", + "source_sha256": hashlib.sha256(Path(__file__).read_bytes()).hexdigest(), + "dependencies": None, + "sqlite_pragmas": None, + "product_storage_diagnostics": None, + "provider_calls": 0, + "network_policy": "socket_connections_denied", + } + + def no_network(*args, **kwargs): + raise AssertionError("network_forbidden_in_standalone_smoke") + + # Prevent accidental live provider connections even if a regression adds one. + socket.socket.connect = no_network + socket.socket.connect_ex = no_network + socket.create_connection = no_network + try: + results["isolated_origin"] = "failed" + import jitmind + + verify_isolation(venv, Path(jitmind.__file__)) + results["isolated_origin"] = "passed" + report["origin"] = str( + Path(jitmind.__file__).resolve().relative_to(venv.resolve()) + ) + report["dependencies"] = sorted( + [ + [d.metadata["Name"], d.version] + for d in importlib.metadata.distributions() + ] + ) + results["optional_dependencies_absent"] = "failed" + assert all( + importlib.util.find_spec(name) is None + for name in ("cohere", "neo4j", "faiss") + ) + assert shutil.which("node") is None, "node_must_be_absent_from_smoke_path" + results["optional_dependencies_absent"] = "passed" + with tempfile.TemporaryDirectory(prefix="jitmind-installed-") as temp: + directory = Path(temp) + results["legacy_memory_research"] = "failed" + legacy_roundtrip(directory) + results["legacy_memory_research"] = "passed" + with sqlite3.connect(directory / "probe.sqlite") as connection: + report["sqlite_pragmas"] = { + key: connection.execute("PRAGMA " + key).fetchone()[0] + for key in ("journal_mode", "synchronous", "foreign_keys") + } + if importlib.util.find_spec("jitmind.storage") is not None: + from jitmind.storage import SQLiteDurableStore + + report["product_storage_diagnostics"] = SQLiteDurableStore( + directory / "authority.sqlite" + ).diagnostics() + # Import must work independently of the optional native dependency. + results["optional_adapter_import"] = "failed" + import jitmind.code_context # noqa: F401 + + results["optional_adapter_import"] = "passed" + results["missing_adapter_capability"] = "failed" + missing_adapter_capability(directory) + results["missing_adapter_capability"] = "passed" + report["status"] = "passed" + except Exception: + # Do not expose paths, provider payloads, or arbitrary exception strings. + report["error"] = "installed_contract_failed" + return report + + +def test_checkout_cannot_masquerade_as_installed_package(tmp_path): + import pytest + + with pytest.raises(AssertionError): + verify_isolation(tmp_path, Path(__file__)) + + +def test_legacy_fake_generator_roundtrip_is_a_checkout_unit_check(tmp_path): + legacy_roundtrip(tmp_path) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--installed-smoke", action="store_true", required=True) + parser.add_argument("venv", type=Path) + parser.add_argument("result", type=Path) + args = parser.parse_args() + result = installed_smoke(args.venv) + with args.result.open("x", encoding="utf-8") as stream: + json.dump(result, stream, allow_nan=False, sort_keys=True, indent=2) + print( + json.dumps( + {"status": result["status"], "checks": result["checks"]}, sort_keys=True + ) + ) + raise SystemExit(0 if result["status"] == "passed" else 1)