brainlayer 1.5.82__tar.gz → 1.5.83__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. {brainlayer-1.5.82 → brainlayer-1.5.83}/PKG-INFO +1 -1
  2. {brainlayer-1.5.82 → brainlayer-1.5.83}/pyproject.toml +1 -1
  3. {brainlayer-1.5.82 → brainlayer-1.5.83}/server.json +2 -2
  4. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/__init__.py +1 -1
  5. brainlayer-1.5.83/src/brainlayer/_build.py +1 -0
  6. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/backup_daily.py +95 -3
  7. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/index_completeness.py +25 -2
  8. brainlayer-1.5.82/src/brainlayer/_build.py +0 -1
  9. {brainlayer-1.5.82 → brainlayer-1.5.83}/.gitignore +0 -0
  10. {brainlayer-1.5.82 → brainlayer-1.5.83}/LICENSE +0 -0
  11. {brainlayer-1.5.82 → brainlayer-1.5.83}/README.md +0 -0
  12. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/hotlane_brainbar_daemon.py +0 -0
  13. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/backup-daily.sh +0 -0
  14. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/brainlayer-env-run.sh +0 -0
  15. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/brainlayer.env.example +0 -0
  16. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.backup-daily.plist +0 -0
  17. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.daemon.plist +0 -0
  18. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.decay.plist +0 -0
  19. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.drain.plist +0 -0
  20. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.health-check.plist +0 -0
  21. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.hotlane-brainbar.plist +0 -0
  22. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.index.plist +0 -0
  23. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.jsonl-backup.plist +0 -0
  24. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.log-cap.plist +0 -0
  25. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.maintenance-nightly.plist +0 -0
  26. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.maintenance-weekly.plist +0 -0
  27. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.maintenance.plist +0 -0
  28. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.observability.plist +0 -0
  29. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.p0-counter.plist +0 -0
  30. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.repair-fts.plist +0 -0
  31. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.t3-ingest.plist +0 -0
  32. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.throughput-watchdog.plist +0 -0
  33. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.tier0-watchdog.plist +0 -0
  34. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.wal-checkpoint.plist +0 -0
  35. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.brainlayer.watch.plist +0 -0
  36. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/com.etanhey.brainlayer-fleet-watchdog.plist +0 -0
  37. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/fleet-watchdog.sh +0 -0
  38. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/install.sh +0 -0
  39. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/jsonl-backup.sh +0 -0
  40. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/launchd/throughput-watchdog.py +0 -0
  41. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/release-verify-signatures.sh +0 -0
  42. {brainlayer-1.5.82 → brainlayer-1.5.83}/scripts/tier0-watchdog.sh +0 -0
  43. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/__main__.py +0 -0
  44. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/_helpers.py +0 -0
  45. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/agent_profiles.py +0 -0
  46. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/agent_provenance.py +0 -0
  47. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/alarm.py +0 -0
  48. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/archive_collapse.py +0 -0
  49. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/archive_lifecycle.py +0 -0
  50. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/backup_retention_invariant.py +0 -0
  51. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/badge_state.py +0 -0
  52. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/bitemporal.py +0 -0
  53. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/brainbar_activation.py +0 -0
  54. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/brainbar_auto_activation.py +0 -0
  55. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/brainbar_hybrid_helper.py +0 -0
  56. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/brainbar_identity.py +0 -0
  57. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/cachedelete_purge.swift +0 -0
  58. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/calibrate.py +0 -0
  59. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/chunk_origin.py +0 -0
  60. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/chunk_origin_backfill.py +0 -0
  61. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/chunk_origin_wipe.py +0 -0
  62. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/chunk_write.py +0 -0
  63. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/classify.py +0 -0
  64. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/claude_paths.py +0 -0
  65. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/cli/__init__.py +0 -0
  66. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/cli/wizard.py +0 -0
  67. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/cli_jobs.py +0 -0
  68. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/cli_new.py +0 -0
  69. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/cloud_backfill.py +0 -0
  70. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/clustering.py +0 -0
  71. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/component_jobs.py +0 -0
  72. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/config.py +0 -0
  73. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/consolidation_plan.py +0 -0
  74. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/content_class.py +0 -0
  75. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/correction_judge.py +0 -0
  76. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/data/agents_registry.yaml +0 -0
  77. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/data/sandbox_seeds/skill-eval-baseline.json +0 -0
  78. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/db_shrink.py +0 -0
  79. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/decay.py +0 -0
  80. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/decay_backfill.py +0 -0
  81. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/decay_job.py +0 -0
  82. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/dedupe.py +0 -0
  83. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/dedupe_merge.py +0 -0
  84. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/deploy_drift.py +0 -0
  85. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/disk_space.py +0 -0
  86. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/doctor.py +0 -0
  87. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/drain.py +0 -0
  88. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/drain_liveness.py +0 -0
  89. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/drive_credentials.py +0 -0
  90. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/embeddings.py +0 -0
  91. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/engine.py +0 -0
  92. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/enrichment_replay.py +0 -0
  93. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/__init__.py +0 -0
  94. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/abcde_enrich_runner.py +0 -0
  95. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/abcde_variants.py +0 -0
  96. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/abcde_variants.yaml +0 -0
  97. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/benchmark.py +0 -0
  98. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/enrichment_gold.py +0 -0
  99. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/enrichment_graders.py +0 -0
  100. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/enrichment_judge.py +0 -0
  101. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/enrichment_llm_judge.py +0 -0
  102. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/enrichment_quality_benchmark.py +0 -0
  103. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/experiment_store.py +0 -0
  104. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/fixtures/README.md +0 -0
  105. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/labeling/enrichment-labeler.html +0 -0
  106. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/phoenix_gate/__init__.py +0 -0
  107. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/phoenix_gate/__main__.py +0 -0
  108. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/phoenix_gate/baseline_store.py +0 -0
  109. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/phoenix_gate/cli.py +0 -0
  110. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/phoenix_gate/models.py +0 -0
  111. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/phoenix_gate/phoenix_client.py +0 -0
  112. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/phoenix_gate/regression_gate.py +0 -0
  113. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/phoenix_gate/triggers.py +0 -0
  114. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/eval/pilot_artifact_report.py +0 -0
  115. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/fallback_replay.py +0 -0
  116. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/git_learning.py +0 -0
  117. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/health_check.py +0 -0
  118. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/hook_python.py +0 -0
  119. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/hooks/__init__.py +0 -0
  120. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/hooks/indexer.py +0 -0
  121. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/import_sweep.py +0 -0
  122. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/index_admission.py +0 -0
  123. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/index_budget.py +0 -0
  124. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/index_new.py +0 -0
  125. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/index_watchdog.py +0 -0
  126. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/ingest/__init__.py +0 -0
  127. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/ingest/codex.py +0 -0
  128. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/ingest/t3.py +0 -0
  129. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/ingest_denylist.py +0 -0
  130. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/ingest_guard.py +0 -0
  131. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/isolation_proof.py +0 -0
  132. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/job_alerts.py +0 -0
  133. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/job_lifecycle_health.py +0 -0
  134. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/jobs.py +0 -0
  135. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/jsonl_backup.py +0 -0
  136. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/kg/__init__.py +0 -0
  137. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/kg_cleanup.py +0 -0
  138. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/kg_judge.py +0 -0
  139. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/kg_promotion.py +0 -0
  140. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/kg_repo.py +0 -0
  141. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/kg_review_session.py +0 -0
  142. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/kg_session_finish.py +0 -0
  143. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/kg_session_harvest.py +0 -0
  144. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/launchd_consolidation.py +0 -0
  145. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/launchd_install.py +0 -0
  146. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/launchd_primitive.py +0 -0
  147. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/launchd_revert.py +0 -0
  148. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/lexical_defense.py +0 -0
  149. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/lexical_defense_dictionary.json +0 -0
  150. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/linkedin_import.py +0 -0
  151. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/log_cap.py +0 -0
  152. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/maintenance.py +0 -0
  153. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/maintenance_calendar.py +0 -0
  154. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/maintenance_control.py +0 -0
  155. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/mcp/__init__.py +0 -0
  156. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/mcp/_format.py +0 -0
  157. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/mcp/_shared.py +0 -0
  158. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/mcp/enrich_handler.py +0 -0
  159. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/mcp/entity_handler.py +0 -0
  160. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/mcp/palette.py +0 -0
  161. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/mcp/search_handler.py +0 -0
  162. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/mcp/store_handler.py +0 -0
  163. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/mcp/tags_handler.py +0 -0
  164. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/mcp_socket_config.py +0 -0
  165. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/mcp_stdio_bridge.py +0 -0
  166. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/memory_types.py +0 -0
  167. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/migrate.py +0 -0
  168. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/migration_custody.py +0 -0
  169. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/migration_journal.py +0 -0
  170. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/migration_runtime.py +0 -0
  171. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/notification_policy.py +0 -0
  172. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/observability_backup.py +0 -0
  173. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/observability_surface.py +0 -0
  174. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/observability_transport.py +0 -0
  175. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/p0_longitudinal_count.py +0 -0
  176. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/parent_death.py +0 -0
  177. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/paths.py +0 -0
  178. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pause.py +0 -0
  179. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/phonetic.py +0 -0
  180. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/__init__.py +0 -0
  181. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/agent_enrichment.py +0 -0
  182. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/analyze_communication.py +0 -0
  183. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/batch_extraction.py +0 -0
  184. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/brain_graph.py +0 -0
  185. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/chat_tags.py +0 -0
  186. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/chunk.py +0 -0
  187. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/classify.py +0 -0
  188. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/cloud_scrub.py +0 -0
  189. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/cluster_sampling.py +0 -0
  190. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/code_intelligence.py +0 -0
  191. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/correction_detection.py +0 -0
  192. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/correction_mining.py +0 -0
  193. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/digest.py +0 -0
  194. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/enrichment_prompts.py +0 -0
  195. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/enrichment_results.py +0 -0
  196. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/enrichment_tiers.py +0 -0
  197. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/entity_extraction.py +0 -0
  198. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/entity_resolution.py +0 -0
  199. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/extract.py +0 -0
  200. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/extract_claude_desktop.py +0 -0
  201. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/extract_corrections.py +0 -0
  202. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/extract_markdown.py +0 -0
  203. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/extract_whatsapp.py +0 -0
  204. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/frustration_mining.py +0 -0
  205. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/git_overlay.py +0 -0
  206. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/kg_extraction.py +0 -0
  207. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/kg_extraction_groq.py +0 -0
  208. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/longitudinal_analyzer.py +0 -0
  209. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/obsidian_export.py +0 -0
  210. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/operation_grouping.py +0 -0
  211. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/plan_linking.py +0 -0
  212. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/rate_limiter.py +0 -0
  213. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/relation_backfill.py +0 -0
  214. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/relation_verification.py +0 -0
  215. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/sanitize.py +0 -0
  216. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/secret_scrub.py +0 -0
  217. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/semantic_style.py +0 -0
  218. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/sentiment.py +0 -0
  219. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/session_history.py +0 -0
  220. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/style_embed.py +0 -0
  221. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/style_index.py +0 -0
  222. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/tag_entity_promotion.py +0 -0
  223. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/temporal_chains.py +0 -0
  224. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/time_batcher.py +0 -0
  225. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/unified_timeline.py +0 -0
  226. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/pipeline/write_queue.py +0 -0
  227. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/provenance.py +0 -0
  228. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/provenance_autosupersede.py +0 -0
  229. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/provenance_integration.py +0 -0
  230. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/queue_io.py +0 -0
  231. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/queue_merge.py +0 -0
  232. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/reembed_backfill.py +0 -0
  233. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/retired_services.py +0 -0
  234. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/runtime_store.py +0 -0
  235. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/sandbox_db.py +0 -0
  236. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/scoping.py +0 -0
  237. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/scrub_at_rest.py +0 -0
  238. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/search_fanout.py +0 -0
  239. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/search_profile.py +0 -0
  240. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/search_repo.py +0 -0
  241. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/service_supervisor.py +0 -0
  242. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/session_repo.py +0 -0
  243. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/setup.py +0 -0
  244. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/socket_hygiene.py +0 -0
  245. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/source_class_migration.py +0 -0
  246. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/spotlight.py +0 -0
  247. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/storage.py +0 -0
  248. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/store.py +0 -0
  249. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/supervisor_adapters.py +0 -0
  250. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/supervisor_child.py +0 -0
  251. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/supervisor_diagnostics.py +0 -0
  252. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/supervisor_guard.py +0 -0
  253. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/supervisor_jobs.py +0 -0
  254. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/system_prompt_guard.py +0 -0
  255. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/t3_linkage_alarm.py +0 -0
  256. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/t3_provenance.py +0 -0
  257. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/t3_sources.py +0 -0
  258. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/tag_normalization.py +0 -0
  259. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/taxonomy.json +0 -0
  260. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/telemetry.py +0 -0
  261. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/timestamp_iso.py +0 -0
  262. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/types.py +0 -0
  263. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/vector_store.py +0 -0
  264. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/wal_checkpoint.py +0 -0
  265. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/watcher.py +0 -0
  266. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/watcher_bridge.py +0 -0
  267. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/writer_policy.py +0 -0
  268. {brainlayer-1.5.82 → brainlayer-1.5.83}/src/brainlayer/writer_telemetry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: brainlayer
3
- Version: 1.5.82
3
+ Version: 1.5.83
4
4
  Summary: Persistent local memory for AI agents — 16 BrainBar MCP tools, semantic search, knowledge graph, SQLite
5
5
  Project-URL: Homepage, https://brainlayer.etanheyman.com
6
6
  Project-URL: Repository, https://github.com/EtanHey/brainlayer
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "brainlayer"
3
- version = "1.5.82"
3
+ version = "1.5.83"
4
4
  description = "Persistent local memory for AI agents — 16 BrainBar MCP tools, semantic search, knowledge graph, SQLite"
5
5
  license = {text = "Apache-2.0"}
6
6
  readme = "README.md"
@@ -3,7 +3,7 @@
3
3
  "name": "io.github.etanhey/brainlayer",
4
4
  "title": "BrainLayer",
5
5
  "description": "Persistent local memory for AI agents \u2014 semantic search, knowledge graph, and 16 BrainBar MCP tools with ToolAnnotations over SQLite.",
6
- "version": "1.5.82",
6
+ "version": "1.5.83",
7
7
  "websiteUrl": "https://brainlayer.etanheyman.com",
8
8
  "repository": {
9
9
  "url": "https://github.com/EtanHey/brainlayer",
@@ -14,7 +14,7 @@
14
14
  "registryType": "pypi",
15
15
  "registryBaseUrl": "https://pypi.org",
16
16
  "identifier": "brainlayer",
17
- "version": "1.5.82",
17
+ "version": "1.5.83",
18
18
  "runtimeHint": "pipx",
19
19
  "transport": {
20
20
  "type": "stdio"
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "1.5.82"
5
+ __version__ = "1.5.83"
6
6
  # Git sha the distributed package was built from; stamped by the release build, None in a source tree.
7
7
  # scripts/sprint_gate.py reads it in "keg" mode to prove the served package is the code under test.
8
8
  # No runtime `str | None` annotation: launchd/install.sh imports this under /usr/bin/python3 (3.9).
@@ -0,0 +1 @@
1
+ BUILD_SHA = "3906bb58f4619f13e8a1785f9a6a305d85e543b8"
@@ -64,6 +64,7 @@ DEFAULT_STAGING_DIR = Path.home() / ".local" / "share" / "brainlayer" / "backups
64
64
  MIN_RAW_FREE_BYTES = 2 * 1024 * 1024 * 1024
65
65
  COPY_CHUNK_BYTES = 1024 * 1024
66
66
  RECLAIM_CHUNK_BYTES = 64 * 1024 * 1024
67
+ CHUNK_TIMING_PROGRESS_INTERVAL = 8
67
68
  RECLAIM_WINDOW_SECONDS = 2.0
68
69
  RECLAIM_MAX_SAMPLES = 5
69
70
  FRAME_OVERHEAD_BYTES = 64 * 1024
@@ -176,6 +177,7 @@ class SQLiteBackupArtifact:
176
177
  compress_duration_seconds: float | None = None
177
178
  raw_sha256: str | None = None
178
179
  reclaim_basis: str | None = None
180
+ chunk_stage_seconds: dict[str, Any] | None = None
179
181
 
180
182
  @property
181
183
  def archive_path(self) -> Path:
@@ -1541,8 +1543,60 @@ def _confirm_raw_reclaim(
1541
1543
  )
1542
1544
 
1543
1545
 
1546
+ class _ChunkStageTimings:
1547
+ """Cumulative seconds; maxima combine repeated checks within each 1-based chunk.
1548
+
1549
+ final_hash covers the whole archive, so its max_chunk is null. Unexecuted stages
1550
+ remain zero with a null max_chunk. Only numeric measurements enter this payload.
1551
+ """
1552
+
1553
+ def __init__(self, clock: Callable[[], float] = time.monotonic) -> None:
1554
+ # Injected, never monkeypatched: patching time.monotonic is process-wide and
1555
+ # kills unrelated background threads (the writer heartbeat) mid-test.
1556
+ self._clock = clock
1557
+ self.data: dict[str, Any] = {"chunks": 0}
1558
+ for stage in (
1559
+ "raw_read_hash",
1560
+ "compress",
1561
+ "archive_write_sync",
1562
+ "raw_punch",
1563
+ "block_reduction",
1564
+ "volume_delta",
1565
+ "free_floor",
1566
+ "final_hash",
1567
+ ):
1568
+ self.data[stage] = {"total": 0.0, "max": 0.0, "max_chunk": None}
1569
+ self._chunk_seconds: dict[str, float] = {}
1570
+
1571
+ def now(self) -> float:
1572
+ """Stage start times come from the same clock record() reads."""
1573
+ return self._clock()
1574
+
1575
+ def start_chunk(self) -> None:
1576
+ self.data["chunks"] += 1
1577
+ self._chunk_seconds = {}
1578
+
1579
+ def record(self, stage: str, started: float) -> None:
1580
+ elapsed = self._clock() - started
1581
+ current = self._chunk_seconds.get(stage, 0.0) + elapsed
1582
+ self._chunk_seconds[stage] = current
1583
+ entry = self.data[stage]
1584
+ entry["total"] += elapsed
1585
+ if entry["max_chunk"] is None or current > entry["max"]:
1586
+ entry.update(max=current, max_chunk=None if stage == "final_hash" else self.data["chunks"])
1587
+
1588
+ def progress(self) -> None:
1589
+ if self.data["chunks"] % CHUNK_TIMING_PROGRESS_INTERVAL == 0:
1590
+ print("Backup chunk timing progress: " + json.dumps({"chunk_stage_seconds": self.data}), flush=True)
1591
+
1592
+
1544
1593
  def _compress_reclaiming_raw(
1545
- raw: Path, archive: Path, *, zstd_binary: str | None, reclaim_basis: str = "volume_delta"
1594
+ raw: Path,
1595
+ archive: Path,
1596
+ *,
1597
+ zstd_binary: str | None,
1598
+ reclaim_basis: str = "volume_delta",
1599
+ timings: _ChunkStageTimings | None = None,
1546
1600
  ) -> str:
1547
1601
  """Write durable independent frames, then release their raw extents; hash before punching.
1548
1602
 
@@ -1550,14 +1604,23 @@ def _compress_reclaiming_raw(
1550
1604
  punch after selection aborts: switching to an unbudgeted full-copy path would be unsafe.
1551
1605
  The caller moves this run's raw into its attempt directory before destructive consumption.
1552
1606
  """
1607
+ timings = timings if timings is not None else _ChunkStageTimings()
1553
1608
  digest = hashlib.sha256()
1554
1609
  with os.fdopen(os.open(raw, os.O_RDWR | os.O_NOFOLLOW), "r+b") as source, archive.open("xb") as sink:
1555
1610
  info = os.fstat(source.fileno())
1556
1611
  if not stat.S_ISREG(info.st_mode) or info.st_nlink != 1:
1557
1612
  raise ValueError("Cannot consume a linked or non-regular raw snapshot")
1558
1613
  offset = 0
1559
- while chunk := source.read(RECLAIM_CHUNK_BYTES):
1614
+ while True:
1615
+ # The raw read is timed too: on a starved disk it can dominate the chunk.
1616
+ started = timings.now()
1617
+ chunk = source.read(RECLAIM_CHUNK_BYTES)
1618
+ if not chunk:
1619
+ break
1620
+ timings.start_chunk()
1560
1621
  digest.update(chunk)
1622
+ timings.record("raw_read_hash", started)
1623
+ started = timings.now()
1561
1624
  if zstd_binary is None:
1562
1625
  frame = gzip.compress(chunk, compresslevel=6)
1563
1626
  else:
@@ -1567,29 +1630,43 @@ def _compress_reclaiming_raw(
1567
1630
  capture_output=True,
1568
1631
  check=True,
1569
1632
  ).stdout
1633
+ timings.record("compress", started)
1570
1634
  if len(frame) > len(chunk) + FRAME_OVERHEAD_BYTES:
1571
1635
  raise ValueError("Compressed frame exceeds budgeted expansion")
1636
+ started = timings.now()
1572
1637
  _require_raw_floor(archive.parent, forthcoming=len(frame))
1638
+ timings.record("free_floor", started)
1639
+ started = timings.now()
1573
1640
  sink.write(frame)
1574
1641
  sink.flush()
1575
1642
  if sys.platform == "darwin":
1576
1643
  fcntl.fcntl(sink.fileno(), fcntl.F_FULLFSYNC)
1577
1644
  else:
1578
1645
  os.fsync(sink.fileno())
1646
+ timings.record("archive_write_sync", started)
1647
+ started = timings.now()
1579
1648
  before_blocks = os.fstat(source.fileno()).st_blocks
1649
+ timings.record("block_reduction", started)
1580
1650
  # Full chunks are allocation-aligned; punching the short final extent is safe
1581
1651
  # because no unread bytes follow it. APFS may retain its final partial block.
1652
+ started = timings.now()
1582
1653
  before_free = shutil.disk_usage(archive.parent).free
1654
+ timings.record("volume_delta", started)
1655
+ started = timings.now()
1583
1656
  _punch_raw_hole(source.fileno(), offset, len(chunk))
1657
+ timings.record("raw_punch", started)
1658
+ started = timings.now()
1584
1659
  after_blocks = os.fstat(source.fileno()).st_blocks
1585
1660
  if after_blocks >= before_blocks:
1586
1661
  raise RuntimeError("punched raw extents not reclaimed (filesystem allocation pin)")
1662
+ timings.record("block_reduction", started)
1587
1663
  # Bracket only the punch: unrelated allocations during compression must not
1588
1664
  # accumulate against reclaim. Allow half a chunk for concurrent volume activity.
1589
1665
  # Short tails can free less than this tolerance due to APFS accounting; they
1590
1666
  # retain the per-file block check above and the raw free-space floor below.
1591
1667
  # With no snapshots, the block drop proves reclaim despite unrelated writers.
1592
1668
  if len(chunk) == RECLAIM_CHUNK_BYTES and reclaim_basis != "block_drop_no_snapshot":
1669
+ started = timings.now()
1593
1670
  _confirm_raw_reclaim(
1594
1671
  archive.parent,
1595
1672
  offset=offset,
@@ -1598,12 +1675,18 @@ def _compress_reclaiming_raw(
1598
1675
  after_blocks=after_blocks,
1599
1676
  before_free=before_free,
1600
1677
  )
1678
+ timings.record("volume_delta", started)
1601
1679
  offset += len(chunk)
1680
+ started = timings.now()
1602
1681
  _require_raw_floor(archive.parent)
1682
+ timings.record("free_floor", started)
1683
+ timings.progress()
1684
+ started = timings.now()
1603
1685
  raw_hash = digest.hexdigest()
1604
1686
  with archive.open("rb") as compressed:
1605
1687
  if _archive_stream_sha256(compressed, zstd=zstd_binary is not None) != raw_hash:
1606
1688
  raise ValueError("Raw/archive snapshot binding failed (SHA-256)")
1689
+ timings.record("final_hash", started)
1607
1690
  return raw_hash
1608
1691
 
1609
1692
 
@@ -1813,6 +1896,7 @@ def create_sqlite_backup_artifact(
1813
1896
  compress_started = time.monotonic()
1814
1897
  raw_sha256 = None
1815
1898
  reclaim_basis = None
1899
+ chunk_timings = None
1816
1900
  if reclaim_raw:
1817
1901
  # Cache once for this backup, before compression starts, never per chunk.
1818
1902
  snapshots = _staging_snapshots_may_exist(output_dir)
@@ -1821,8 +1905,13 @@ def create_sqlite_backup_artifact(
1821
1905
  # Keep consumed extents inside the owned attempt, never at the published raw path.
1822
1906
  consumed_raw = _consume_raw_in_attempt(raw_snapshot, Path(tmp))
1823
1907
  print("Backup consuming verified raw snapshot; a failed attempt requires a fresh snapshot", flush=True)
1908
+ chunk_timings = _ChunkStageTimings()
1824
1909
  raw_sha256 = _compress_reclaiming_raw(
1825
- consumed_raw, temp_archive, zstd_binary=zstd_binary, reclaim_basis=reclaim_basis
1910
+ consumed_raw,
1911
+ temp_archive,
1912
+ zstd_binary=zstd_binary,
1913
+ reclaim_basis=reclaim_basis,
1914
+ timings=chunk_timings,
1826
1915
  )
1827
1916
  elif zstd_binary is not None:
1828
1917
  _compress_zstd(zstd_binary, raw_snapshot, temp_archive, output_dir)
@@ -1868,6 +1957,7 @@ def create_sqlite_backup_artifact(
1868
1957
  compress_duration_seconds=compress_duration,
1869
1958
  raw_sha256=raw_sha256,
1870
1959
  reclaim_basis=reclaim_basis,
1960
+ chunk_stage_seconds=chunk_timings.data if chunk_timings is not None else None,
1871
1961
  )
1872
1962
 
1873
1963
 
@@ -3539,6 +3629,7 @@ def run_backup(
3539
3629
  integrity_duration_seconds=resume.get("integrity_duration_seconds"),
3540
3630
  raw_sha256=resume.get("raw_sha256"),
3541
3631
  reclaim_basis=resume.get("reclaim_basis"),
3632
+ chunk_stage_seconds=resume.get("chunk_stage_seconds"),
3542
3633
  )
3543
3634
  for suffix in DB_SNAPSHOT_SUFFIXES:
3544
3635
  target = Path(staging_dir) / f"{resolved_date_stamp}{suffix}"
@@ -3603,6 +3694,7 @@ def run_backup(
3603
3694
  "integrity_duration_seconds": getattr(artifact, "integrity_duration_seconds", None),
3604
3695
  "raw_sha256": getattr(artifact, "raw_sha256", None),
3605
3696
  "reclaim_basis": getattr(artifact, "reclaim_basis", None),
3697
+ "chunk_stage_seconds": getattr(artifact, "chunk_stage_seconds", None),
3606
3698
  "compressor": getattr(artifact, "compressor", None)
3607
3699
  or ("zstd" if snapshot.name.endswith(ZSTD_SNAPSHOT_SUFFIX) else "gzip"),
3608
3700
  "compress_duration_seconds": getattr(artifact, "compress_duration_seconds", None),
@@ -246,7 +246,6 @@ def _build_scratch(conn: sqlite3.Connection, present: set[str]) -> list[str]:
246
246
  in SQLite's own temp database -- writable even when the main DB is opened
247
247
  `mode=ro` -- so the working set spills to disk and memory stays flat.
248
248
  """
249
- conn.execute("PRAGMA temp_store = FILE")
250
249
  conn.execute("CREATE TEMP TABLE _ic_route (chunk_id TEXT PRIMARY KEY, route TEXT, active INTEGER)")
251
250
  conn.execute(
252
251
  f"""
@@ -296,7 +295,31 @@ def census(
296
295
  verify_vector_payload: bool = False,
297
296
  collect_ids: bool = True,
298
297
  ) -> CompletenessCensus:
299
- """Measure completeness in both directions. Reads only the main database."""
298
+ """Measure both directions in one snapshot; only TEMP tables are written.
299
+
300
+ Autocommit callers (doctor and repair) need an explicit read transaction
301
+ so a concurrent WAL commit cannot split routing, FTS, and vector reads.
302
+ An existing transaction belongs to the caller and must stay open.
303
+ """
304
+ owns_snapshot = not conn.in_transaction
305
+ if owns_snapshot:
306
+ conn.execute("PRAGMA temp_store = FILE")
307
+ conn.execute("BEGIN")
308
+ try:
309
+ return _census_snapshot(conn, verify_vector_payload=verify_vector_payload, collect_ids=collect_ids)
310
+ finally:
311
+ if owns_snapshot:
312
+ # SQL, not conn.rollback(): under Python 3.12+ autocommit=True the
313
+ # method is a no-op and would leave this BEGIN's snapshot open.
314
+ conn.execute("ROLLBACK")
315
+
316
+
317
+ def _census_snapshot(
318
+ conn: sqlite3.Connection,
319
+ *,
320
+ verify_vector_payload: bool,
321
+ collect_ids: bool,
322
+ ) -> CompletenessCensus:
300
323
  present = _table_names(conn)
301
324
  result = CompletenessCensus()
302
325
  staged = _build_scratch(conn, present)
@@ -1 +0,0 @@
1
- BUILD_SHA = "9b6e1c75b4bed7440161989df15eb49ccabca0db"
File without changes
File without changes
File without changes