kernel-lore-mcp 0.3.2__tar.gz → 0.3.3__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 (277) hide show
  1. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/Cargo.lock +1 -1
  2. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/Cargo.toml +1 -1
  3. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/PKG-INFO +1 -1
  4. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/pyproject.toml +1 -1
  5. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/bin/sync.rs +74 -8
  6. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/error.rs +3 -0
  7. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/__init__.py +1 -1
  8. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/_core.pyi +8 -0
  9. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/cost_class.py +25 -2
  10. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/health.py +29 -3
  11. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/routes/status.py +16 -8
  12. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/timeout.py +28 -2
  13. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/lib.rs +2 -0
  14. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/python.rs +47 -1
  15. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/reader.rs +6 -0
  16. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/router.rs +4 -0
  17. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/timeout.rs +99 -0
  18. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_cost_class.py +46 -22
  19. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_resources_routes.py +14 -1
  20. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/uv.lock +1 -1
  21. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/.github/workflows/ci.yml +0 -0
  22. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/.github/workflows/release.yml +0 -0
  23. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/.gitignore +0 -0
  24. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/.python-version +0 -0
  25. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/CHANGELOG.md +0 -0
  26. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/CLAUDE.md +0 -0
  27. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/CONTRIBUTING.md +0 -0
  28. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/GOVERNANCE.md +0 -0
  29. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/LEGAL.md +0 -0
  30. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/LICENSE +0 -0
  31. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/README.md +0 -0
  32. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/SECURITY.md +0 -0
  33. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/TODO.md +0 -0
  34. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/README.md +0 -0
  35. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/architecture/data-flow.md +0 -0
  36. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/architecture/deployment-modes.md +0 -0
  37. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/architecture/four-tier-index.md +0 -0
  38. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/architecture/over-db.md +0 -0
  39. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/architecture/overview.md +0 -0
  40. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/architecture/reciprocity.md +0 -0
  41. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/architecture/trade-offs.md +0 -0
  42. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/demos/first-session.md +0 -0
  43. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/indexing/bm25-tier.md +0 -0
  44. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/indexing/compressed-store.md +0 -0
  45. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/indexing/metadata-tier.md +0 -0
  46. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/indexing/path-tier.md +0 -0
  47. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/indexing/tokenizer-spec.md +0 -0
  48. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/indexing/trigram-tier.md +0 -0
  49. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/ingestion/grokmirror.md +0 -0
  50. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/ingestion/mbox-parsing.md +0 -0
  51. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/ingestion/patch-parsing.md +0 -0
  52. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/ingestion/shard-walking.md +0 -0
  53. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/mcp/client-config.md +0 -0
  54. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/mcp/query-routing.md +0 -0
  55. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/mcp/tools.md +0 -0
  56. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/mcp/transport-auth.md +0 -0
  57. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/ops/corpus-coverage.md +0 -0
  58. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/ops/cost-model.md +0 -0
  59. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/ops/ec2-sizing.md +0 -0
  60. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/ops/monitoring.md +0 -0
  61. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/ops/production-hardening.md +0 -0
  62. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/ops/public-launch-checklist.md +0 -0
  63. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/ops/runbook.md +0 -0
  64. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/ops/threat-model.md +0 -0
  65. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/ops/update-cadence.md +0 -0
  66. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/ops/update-frequency.md +0 -0
  67. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/plans/2026-04-14-best-in-class-kernel-mcp.md +0 -0
  68. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/plans/2026-04-15-internalize-grokmirror.md +0 -0
  69. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/plans/2026-04-15-mcp-spec-coverage-and-uplift.md +0 -0
  70. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/plans/2026-04-17-overdb-followups.md +0 -0
  71. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/plans/2026-04-17-overdb-metadata-tier.md +0 -0
  72. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/plans/2026-04-20-v0.3.0-plan.md +0 -0
  73. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/plans/2026-04-22-v0.3.1-plan.md +0 -0
  74. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/research/2026-04-14-agent-ergonomics.md +0 -0
  75. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/research/2026-04-14-best-in-class-mcp-survey.md +0 -0
  76. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/research/2026-04-14-external-data-sources.md +0 -0
  77. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/research/2026-04-14-gix-vs-git2.md +0 -0
  78. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/research/2026-04-14-mcp-python-sdk.md +0 -0
  79. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/research/2026-04-14-pyo3-maturin.md +0 -0
  80. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/research/2026-04-14-search-library-landscape.md +0 -0
  81. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/research/2026-04-14-storage-footprint.md +0 -0
  82. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/research/2026-04-14-tantivy.md +0 -0
  83. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/research/2026-04-14-workflow-gap-analysis.md +0 -0
  84. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/research/2026-04-15-fuzzy-search-design.md +0 -0
  85. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/research/2026-04-15-path-mentions.md +0 -0
  86. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/research/2026-04-17-overdb-validation.md +0 -0
  87. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/research/training-retriever.md +0 -0
  88. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/README.md +0 -0
  89. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/checklists/01-research.md +0 -0
  90. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/checklists/02-design.md +0 -0
  91. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/checklists/03-implement.md +0 -0
  92. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/checklists/04-test.md +0 -0
  93. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/checklists/05-quality.md +0 -0
  94. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/checklists/06-review.md +0 -0
  95. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/checklists/07-commit.md +0 -0
  96. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/checklists/08-debug.md +0 -0
  97. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/checklists/09-optimize.md +0 -0
  98. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/checklists/10-document.md +0 -0
  99. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/checklists/index.md +0 -0
  100. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/code-quality.md +0 -0
  101. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/data-structures.md +0 -0
  102. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/design/boundaries.md +0 -0
  103. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/design/concurrency.md +0 -0
  104. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/design/dependencies.md +0 -0
  105. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/design/errors.md +0 -0
  106. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/design/modules.md +0 -0
  107. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/git.md +0 -0
  108. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/index.md +0 -0
  109. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/language.md +0 -0
  110. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/libraries/fastmcp.md +0 -0
  111. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/libraries/httpx.md +0 -0
  112. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/libraries/pydantic.md +0 -0
  113. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/libraries/structlog.md +0 -0
  114. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/naming.md +0 -0
  115. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/pyo3-maturin.md +0 -0
  116. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/testing.md +0 -0
  117. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/python/uv.md +0 -0
  118. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/cargo.md +0 -0
  119. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/checklists/01-research.md +0 -0
  120. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/checklists/02-design.md +0 -0
  121. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/checklists/03-implement.md +0 -0
  122. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/checklists/04-test.md +0 -0
  123. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/checklists/05-quality.md +0 -0
  124. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/checklists/06-review.md +0 -0
  125. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/checklists/07-commit.md +0 -0
  126. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/checklists/08-debug.md +0 -0
  127. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/checklists/09-optimize.md +0 -0
  128. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/checklists/10-document.md +0 -0
  129. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/checklists/index.md +0 -0
  130. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/code-quality.md +0 -0
  131. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/design/boundaries.md +0 -0
  132. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/design/concurrency.md +0 -0
  133. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/design/data-structures.md +0 -0
  134. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/design/errors.md +0 -0
  135. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/design/modules.md +0 -0
  136. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/ffi.md +0 -0
  137. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/index.md +0 -0
  138. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/language.md +0 -0
  139. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/libraries/arrow-parquet.md +0 -0
  140. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/libraries/gix.md +0 -0
  141. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/libraries/pyo3.md +0 -0
  142. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/libraries/regex-automata.md +0 -0
  143. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/libraries/roaring-fst.md +0 -0
  144. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/libraries/tantivy.md +0 -0
  145. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/libraries/zstd.md +0 -0
  146. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/naming.md +0 -0
  147. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/testing.md +0 -0
  148. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/docs/standards/rust/unsafe.md +0 -0
  149. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/rust-toolchain.toml +0 -0
  150. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/agentic_smoke.sh +0 -0
  151. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/bench/bench_concurrent_mixed.py +0 -0
  152. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/bench/bench_hosted_adversarial.py +0 -0
  153. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/bench/stress_mcp_multiprocess.py +0 -0
  154. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/grokmirror-personal.conf +0 -0
  155. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/grokmirror.conf +0 -0
  156. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/klmcp-doctor.sh +0 -0
  157. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/klmcp-grok-pull.sh +0 -0
  158. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/klmcp-ingest.sh +0 -0
  159. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/post-pull-hook.sh +0 -0
  160. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/rust_call_graph.py +0 -0
  161. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/systemd/etc-kernel-lore-mcp-env.sample +0 -0
  162. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/systemd/klmcp-grokmirror.service +0 -0
  163. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/systemd/klmcp-grokmirror.timer +0 -0
  164. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/systemd/klmcp-ingest.path +0 -0
  165. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/systemd/klmcp-ingest.service +0 -0
  166. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/systemd/klmcp-mcp.service +0 -0
  167. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/systemd/klmcp-sync.service +0 -0
  168. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/scripts/systemd/klmcp-sync.timer +0 -0
  169. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/bin/bench_blob_read.rs +0 -0
  170. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/bin/bench_ingest_stages.rs +0 -0
  171. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/bin/build_git_sidecar.rs +0 -0
  172. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/bin/build_over.rs +0 -0
  173. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/bin/doctor.rs +0 -0
  174. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/bin/ingest.rs +0 -0
  175. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/bin/reindex.rs +0 -0
  176. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/bm25.rs +0 -0
  177. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/embedding.rs +0 -0
  178. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/git_sidecar.rs +0 -0
  179. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/ingest.rs +0 -0
  180. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/__main__.py +0 -0
  181. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/_surface_manifest.py +0 -0
  182. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/cli/__init__.py +0 -0
  183. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/cli/doctor.py +0 -0
  184. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/cli/embed.py +0 -0
  185. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/cli/ingest.py +0 -0
  186. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/cli/sync.py +0 -0
  187. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/config.py +0 -0
  188. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/cursor.py +0 -0
  189. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/embedding.py +0 -0
  190. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/errors.py +0 -0
  191. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/freshness.py +0 -0
  192. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/kwic.py +0 -0
  193. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/logging_.py +0 -0
  194. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/mapping.py +0 -0
  195. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/models.py +0 -0
  196. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/observability.py +0 -0
  197. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/prompts.py +0 -0
  198. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/reader_cache.py +0 -0
  199. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/resources/__init__.py +0 -0
  200. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/resources/blind_spots.py +0 -0
  201. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/resources/coverage_stats.py +0 -0
  202. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/resources/templates.py +0 -0
  203. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/routes/__init__.py +0 -0
  204. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/routes/metrics.py +0 -0
  205. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/sampling.py +0 -0
  206. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/server.py +0 -0
  207. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/time_bounds.py +0 -0
  208. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/__init__.py +0 -0
  209. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/activity.py +0 -0
  210. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/author_footprint.py +0 -0
  211. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/author_profile.py +0 -0
  212. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/corpus_stats.py +0 -0
  213. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/expand_citation.py +0 -0
  214. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/explain_patch.py +0 -0
  215. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/file_timeline.py +0 -0
  216. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/fix_status.py +0 -0
  217. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/maintainer_profile.py +0 -0
  218. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/message.py +0 -0
  219. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/nearest.py +0 -0
  220. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/patch.py +0 -0
  221. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/patch_diff.py +0 -0
  222. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/patch_search.py +0 -0
  223. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/path_mentions.py +0 -0
  224. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/primitives.py +0 -0
  225. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/sampling_tools.py +0 -0
  226. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/search.py +0 -0
  227. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/series.py +0 -0
  228. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/stable_backport.py +0 -0
  229. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/subsystem_churn.py +0 -0
  230. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/thread.py +0 -0
  231. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/kernel_lore_mcp/tools/thread_state.py +0 -0
  232. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/maintainers.rs +0 -0
  233. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/metadata.rs +0 -0
  234. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/over.rs +0 -0
  235. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/parse.rs +0 -0
  236. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/path_tier.rs +0 -0
  237. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/schema.rs +0 -0
  238. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/state.rs +0 -0
  239. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/store.rs +0 -0
  240. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/sync.rs +0 -0
  241. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/tid.rs +0 -0
  242. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/trailer_refs.rs +0 -0
  243. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/src/trigram.rs +0 -0
  244. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/__init__.py +0 -0
  245. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/bin_ingest.rs +0 -0
  246. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/__init__.py +0 -0
  247. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/conftest.py +0 -0
  248. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/fixtures/__init__.py +0 -0
  249. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_annotations.py +0 -0
  250. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_cost_hints.py +0 -0
  251. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_coverage_stats.py +0 -0
  252. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_cursor.py +0 -0
  253. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_embedding_e2e.py +0 -0
  254. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_errors.py +0 -0
  255. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_fix_status.py +0 -0
  256. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_freshness.py +0 -0
  257. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_fuzzy_search.py +0 -0
  258. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_http_transport.py +0 -0
  259. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_ingest_and_reader.py +0 -0
  260. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_kwic.py +0 -0
  261. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_logging_profile.py +0 -0
  262. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_mcp_adversarial.py +0 -0
  263. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_mcp_tools_e2e.py +0 -0
  264. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_observability.py +0 -0
  265. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_path_mentions.py +0 -0
  266. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_primitives_e2e.py +0 -0
  267. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_prompts.py +0 -0
  268. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_regex_hosted.py +0 -0
  269. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_resource_templates.py +0 -0
  270. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_response_format.py +0 -0
  271. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_sampling_tools.py +0 -0
  272. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_smoke.py +0 -0
  273. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_status_subcommand.py +0 -0
  274. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_stdio_subprocess.py +0 -0
  275. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_surface_manifest.py +0 -0
  276. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_sync_cli.py +0 -0
  277. {kernel_lore_mcp-0.3.2 → kernel_lore_mcp-0.3.3}/tests/python/test_thread_patch_explain.py +0 -0
@@ -2638,7 +2638,7 @@ dependencies = [
2638
2638
 
2639
2639
  [[package]]
2640
2640
  name = "kernel_lore_mcp"
2641
- version = "0.3.2"
2641
+ version = "0.3.3"
2642
2642
  dependencies = [
2643
2643
  "aho-corasick",
2644
2644
  "anyhow",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "kernel_lore_mcp"
3
- version = "0.3.2"
3
+ version = "0.3.3"
4
4
  edition = "2024"
5
5
  rust-version = "1.85"
6
6
  authors = ["Michael Bommarito <michael@bommaritollc.com>"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kernel-lore-mcp
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Classifier: Development Status :: 2 - Pre-Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kernel-lore-mcp"
3
- version = "0.3.2"
3
+ version = "0.3.3"
4
4
  description = "MCP server exposing fast, structured search over lore.kernel.org to LLM developer tools."
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -114,6 +114,11 @@ struct PhaseHeartbeat {
114
114
  handle: Option<std::thread::JoinHandle<()>>,
115
115
  }
116
116
 
117
+ struct StageHeartbeat {
118
+ stop: std::sync::Arc<AtomicBool>,
119
+ handle: Option<std::thread::JoinHandle<()>>,
120
+ }
121
+
117
122
  #[derive(Debug, Clone, Serialize)]
118
123
  struct SyncStatusRecord {
119
124
  version: String,
@@ -195,6 +200,23 @@ impl SyncStateReporter {
195
200
  })
196
201
  }
197
202
 
203
+ fn refresh_stage(
204
+ &self,
205
+ stage: &str,
206
+ note: Option<String>,
207
+ elapsed_secs: f64,
208
+ memory: MemorySnapshot,
209
+ ) -> Result<()> {
210
+ self.update(|status| {
211
+ status.stage = stage.to_string();
212
+ status.note = note;
213
+ status.elapsed_secs = elapsed_secs;
214
+ status.process_rss_mb = memory.process_rss_bytes.map(bytes_to_mib);
215
+ status.memory_available_mb = memory.available_bytes.map(bytes_to_mib);
216
+ status.memory_total_mb = memory.total_bytes.map(bytes_to_mib);
217
+ })
218
+ }
219
+
198
220
  fn set_phase_progress(
199
221
  &self,
200
222
  stage: &str,
@@ -331,6 +353,46 @@ impl Drop for PhaseHeartbeat {
331
353
  }
332
354
  }
333
355
 
356
+ impl Drop for StageHeartbeat {
357
+ fn drop(&mut self) {
358
+ self.stop.store(true, Ordering::Relaxed);
359
+ if let Some(handle) = self.handle.take() {
360
+ handle.thread().unpark();
361
+ let _ = handle.join();
362
+ }
363
+ }
364
+ }
365
+
366
+ fn stage_heartbeat(
367
+ sync_state: SyncStateReporter,
368
+ stage: &'static str,
369
+ note: Option<String>,
370
+ run_started: Instant,
371
+ ) -> StageHeartbeat {
372
+ let stop = std::sync::Arc::new(AtomicBool::new(false));
373
+ let stop_for_thread = std::sync::Arc::clone(&stop);
374
+ let stage_name = stage.to_string();
375
+ let handle = std::thread::spawn(move || {
376
+ loop {
377
+ std::thread::park_timeout(PROGRESS_LOG_INTERVAL);
378
+ if stop_for_thread.load(Ordering::Relaxed) {
379
+ break;
380
+ }
381
+ let elapsed_secs = run_started.elapsed().as_secs_f64();
382
+ let memory = current_memory_snapshot();
383
+ if let Err(e) =
384
+ sync_state.refresh_stage(&stage_name, note.clone(), elapsed_secs, memory)
385
+ {
386
+ tracing::warn!(stage = stage_name, error = %e, "failed to write sync status");
387
+ }
388
+ }
389
+ });
390
+ StageHeartbeat {
391
+ stop,
392
+ handle: Some(handle),
393
+ }
394
+ }
395
+
334
396
  fn main() -> Result<()> {
335
397
  tracing_subscriber::fmt()
336
398
  .with_writer(std::io::stderr)
@@ -688,16 +750,15 @@ fn main() -> Result<()> {
688
750
  if let Some(ref bm25_mutex) = bm25 {
689
751
  let memory = current_memory_snapshot();
690
752
  let config = bm25_config.expect("bm25 config must exist when writer exists");
753
+ let note = Some(format!(
754
+ "threads={} memory_budget_mb={}",
755
+ config.num_threads,
756
+ config.memory_budget_mb()
757
+ ));
691
758
  sync_state
692
- .set_stage(
693
- "bm25_commit",
694
- Some(format!(
695
- "threads={} memory_budget_mb={}",
696
- config.num_threads,
697
- config.memory_budget_mb()
698
- )),
699
- )
759
+ .set_stage("bm25_commit", note.clone())
700
760
  .context("update sync status to bm25_commit")?;
761
+ let stage_guard = stage_heartbeat(sync_state.clone(), "bm25_commit", note, start);
701
762
  tracing::info!(
702
763
  bm25_writer_threads = config.num_threads,
703
764
  bm25_memory_budget_mb = config.memory_budget_mb(),
@@ -718,6 +779,7 @@ fn main() -> Result<()> {
718
779
  memory_total_mb = ?memory.total_bytes.map(bytes_to_mib),
719
780
  "bm25 commit done"
720
781
  );
782
+ drop(stage_guard);
721
783
  }
722
784
 
723
785
  // tid rebuild: reasonable to do every sync tick; touches only the
@@ -725,6 +787,7 @@ fn main() -> Result<()> {
725
787
  sync_state
726
788
  .set_stage("tid_rebuild", None)
727
789
  .context("update sync status to tid_rebuild")?;
790
+ let tid_stage_guard = stage_heartbeat(sync_state.clone(), "tid_rebuild", None, start);
728
791
  tracing::info!("tid rebuild starting");
729
792
  let tid_result = _core::rebuild_tid(&data_dir).context("rebuild tid side-table")?;
730
793
  tracing::info!(
@@ -732,9 +795,11 @@ fn main() -> Result<()> {
732
795
  path = %tid_result.0.display(),
733
796
  "tid rebuild done"
734
797
  );
798
+ drop(tid_stage_guard);
735
799
  sync_state
736
800
  .set_stage("path_vocab_rebuild", None)
737
801
  .context("update sync status to path_vocab_rebuild")?;
802
+ let path_stage_guard = stage_heartbeat(sync_state.clone(), "path_vocab_rebuild", None, start);
738
803
  tracing::info!("path vocab rebuild starting");
739
804
  if let Some(path_count) = rebuild_path_vocab_after_sync(&data_dir)? {
740
805
  tracing::info!(
@@ -743,6 +808,7 @@ fn main() -> Result<()> {
743
808
  "path vocab rebuild done"
744
809
  );
745
810
  }
811
+ drop(path_stage_guard);
746
812
 
747
813
  // Tally.
748
814
  let mut total_ingested: u64 = 0;
@@ -18,6 +18,9 @@ pub enum Error {
18
18
  #[error("query exceeded {limit_ms} ms wall-clock limit")]
19
19
  QueryTimeout { limit_ms: u64 },
20
20
 
21
+ #[error("query cancelled by caller")]
22
+ QueryCancelled,
23
+
21
24
  #[error("invalid cursor: {0}")]
22
25
  InvalidCursor(String),
23
26
 
@@ -15,7 +15,7 @@ from typing import Any
15
15
 
16
16
  __all__ = ["__version__", "native_version"]
17
17
 
18
- __version__ = "0.3.2"
18
+ __version__ = "0.3.3"
19
19
 
20
20
 
21
21
  def __getattr__(name: str) -> Any:
@@ -29,6 +29,14 @@ class TidRebuildResult(TypedDict):
29
29
  path: str
30
30
  rows: int
31
31
 
32
+ class _RequestCancelGuard:
33
+ def close(self) -> None: ...
34
+
35
+ class _RequestCancelToken:
36
+ def __init__(self) -> None: ...
37
+ def cancel(self) -> None: ...
38
+ def install(self) -> _RequestCancelGuard: ...
39
+
32
40
  def rebuild_tid(data_dir: str | PathLike[str]) -> TidRebuildResult: ...
33
41
  def rebuild_bm25(data_dir: str | PathLike[str]) -> int: ...
34
42
  def backfill_subject_normalized(data_dir: str | PathLike[str]) -> int: ...
@@ -41,7 +41,7 @@ import structlog
41
41
 
42
42
  from kernel_lore_mcp.config import get_settings
43
43
  from kernel_lore_mcp.errors import LoreError
44
- from kernel_lore_mcp.health import suggest_retry_after_seconds
44
+ from kernel_lore_mcp.health import should_shed_tool_call, suggest_retry_after_seconds
45
45
  from kernel_lore_mcp.logging_ import profiling_thresholds
46
46
 
47
47
  CostClass = Literal["cheap", "moderate", "expensive"]
@@ -147,6 +147,30 @@ def cost_limited(
147
147
  # Pair with a zero-timeout acquire for the "reject fast"
148
148
  # shape — acquire() would block if locked.
149
149
  base_started = current_tool_request_started() or time.monotonic()
150
+ settings = get_settings()
151
+ should_shed, shed_reason = should_shed_tool_call(
152
+ data_dir=settings.data_dir,
153
+ cost_class=cost,
154
+ )
155
+ if should_shed:
156
+ queue_wait = max(0.0, time.monotonic() - base_started)
157
+ record_tool_queue_wait(
158
+ tool_name,
159
+ cost,
160
+ queue_wait,
161
+ "rate_limited",
162
+ )
163
+ log.warning(
164
+ "tool admission shed",
165
+ tool=tool_name,
166
+ cost_class=cost,
167
+ mode=settings.mode,
168
+ queue_wait_ms=round(queue_wait * 1000, 3),
169
+ inflight=current_inflight(cost),
170
+ limit=_LIMITS[cost],
171
+ reason=shed_reason,
172
+ )
173
+ raise _rate_limited(cost, tool_name)
150
174
  try:
151
175
  await asyncio.wait_for(sem.acquire(), timeout=0.001)
152
176
  except TimeoutError as err:
@@ -157,7 +181,6 @@ def cost_limited(
157
181
  queue_wait,
158
182
  "rate_limited",
159
183
  )
160
- settings = get_settings()
161
184
  log.warning(
162
185
  "tool admission rejected",
163
186
  tool=tool_name,
@@ -11,6 +11,14 @@ from pathlib import Path
11
11
  from typing import Any
12
12
 
13
13
  _ACTIVE_SYNC_STALE_AFTER_SECONDS = 30
14
+ _SYNC_SHED_STAGES = {
15
+ "ingest",
16
+ "bm25_commit",
17
+ "tid_rebuild",
18
+ "path_vocab_rebuild",
19
+ "generation_bump",
20
+ "save_manifest",
21
+ }
14
22
 
15
23
 
16
24
  def writer_lock_present(data_dir: Path) -> bool:
@@ -62,9 +70,7 @@ def read_sync_state(data_dir: Path) -> dict[str, Any] | None:
62
70
  active = bool(payload.get("active"))
63
71
  if heartbeat_age_seconds is not None:
64
72
  active = (
65
- active
66
- and writer_active
67
- and heartbeat_age_seconds <= _ACTIVE_SYNC_STALE_AFTER_SECONDS
73
+ active and writer_active and heartbeat_age_seconds <= _ACTIVE_SYNC_STALE_AFTER_SECONDS
68
74
  )
69
75
  else:
70
76
  active = active and writer_active
@@ -122,6 +128,26 @@ def suggest_retry_after_seconds(
122
128
  return retry_after, None
123
129
 
124
130
 
131
+ def should_shed_tool_call(*, data_dir: Path, cost_class: str) -> tuple[bool, str | None]:
132
+ """Whether the server should fast-reject this tool class.
133
+
134
+ Cheap indexed lookups stay available as long as the process is up.
135
+ Moderate and expensive tools shed aggressively while a writer-heavy
136
+ sync stage is active so a serving box does not spend its remaining
137
+ headroom on work we already expect to be slow or likely to timeout.
138
+ """
139
+ if cost_class == "cheap":
140
+ return False, None
141
+
142
+ sync = read_sync_state(data_dir)
143
+ if sync and sync.get("active"):
144
+ stage = str(sync.get("stage") or "").strip()
145
+ if stage in _SYNC_SHED_STAGES:
146
+ return True, f"sync stage `{stage}` is reserving capacity"
147
+
148
+ return False, None
149
+
150
+
125
151
  def _coerce_int(value: object) -> int | None:
126
152
  if isinstance(value, bool):
127
153
  return None
@@ -66,10 +66,9 @@ def _per_list_shards(data_dir: Path) -> dict[str, list[dict[str, str]]]:
66
66
  return out
67
67
 
68
68
 
69
- def _build_status(settings: Settings) -> dict[str, Any]:
69
+ def _build_status(settings: Settings, *, include_per_list: bool) -> dict[str, Any]:
70
70
  data_dir = settings.data_dir
71
71
  generation, last_ingest = _read_generation(data_dir)
72
- per_list = _per_list_shards(data_dir)
73
72
  writer_lock = writer_lock_present(data_dir)
74
73
  sync = read_sync_state(data_dir)
75
74
 
@@ -85,7 +84,7 @@ def _build_status(settings: Settings) -> dict[str, Any]:
85
84
  # that pairs with the Prometheus gauge.
86
85
  freshness_ok = last_ingest_age_seconds < 3 * interval
87
86
 
88
- return {
87
+ body = {
89
88
  "service": "kernel-lore-mcp",
90
89
  "version": _native_version(),
91
90
  "generation": generation,
@@ -96,10 +95,13 @@ def _build_status(settings: Settings) -> dict[str, Any]:
96
95
  "writer_lock_present": writer_lock,
97
96
  "sync_active": bool(sync and sync.get("active")),
98
97
  "sync": sync,
99
- "per_list": per_list,
98
+ "per_list_omitted": not include_per_list,
100
99
  "capabilities": capabilities(data_dir),
101
100
  "blind_spots_ref": "blind-spots://coverage",
102
101
  }
102
+ if include_per_list:
103
+ body["per_list"] = _per_list_shards(data_dir)
104
+ return body
103
105
 
104
106
 
105
107
  def capabilities(data_dir: Path) -> dict[str, bool]:
@@ -158,7 +160,11 @@ def _native_version() -> str:
158
160
  return "unknown"
159
161
 
160
162
 
161
- def get_status(settings: Settings | None = None) -> dict[str, Any]:
163
+ def get_status(
164
+ settings: Settings | None = None,
165
+ *,
166
+ include_per_list: bool = False,
167
+ ) -> dict[str, Any]:
162
168
  """Cached status builder. Used by both the MCP route and tests.
163
169
 
164
170
  Cache is keyed by `data_dir` so multiple in-process servers (or
@@ -169,7 +175,7 @@ def get_status(settings: Settings | None = None) -> dict[str, Any]:
169
175
  from kernel_lore_mcp.config import get_settings
170
176
 
171
177
  settings = get_settings()
172
- cache_key = str(settings.data_dir)
178
+ cache_key = f"{settings.data_dir}|per_list={int(include_per_list)}"
173
179
  ttl = settings.freshness_cache_ttl_seconds
174
180
  now = time.monotonic()
175
181
  live_writer = writer_lock_present(settings.data_dir)
@@ -178,7 +184,7 @@ def get_status(settings: Settings | None = None) -> dict[str, Any]:
178
184
  cached_at, body = _cache[cache_key]
179
185
  if now - cached_at < effective_ttl:
180
186
  return body
181
- body = _build_status(settings)
187
+ body = _build_status(settings, include_per_list=include_per_list)
182
188
  _cache[cache_key] = (now, body)
183
189
  return body
184
190
 
@@ -190,4 +196,6 @@ def clear_cache() -> None:
190
196
 
191
197
  async def status_endpoint(request: Request) -> JSONResponse:
192
198
  """Starlette/FastMCP custom-route handler."""
193
- return JSONResponse(get_status())
199
+ raw = (request.query_params.get("per_list") or "").strip().lower()
200
+ include_per_list = raw in {"1", "true", "yes", "on"}
201
+ return JSONResponse(get_status(include_per_list=include_per_list))
@@ -24,6 +24,7 @@ from kernel_lore_mcp.config import get_settings
24
24
  from kernel_lore_mcp.errors import LoreError
25
25
  from kernel_lore_mcp.health import suggest_retry_after_seconds
26
26
  from kernel_lore_mcp.logging_ import profiling_thresholds
27
+ from kernel_lore_mcp import _core
27
28
 
28
29
  log = structlog.get_logger(__name__)
29
30
 
@@ -45,9 +46,18 @@ async def run_with_timeout[T](
45
46
  ms = timeout_ms or settings.query_wall_clock_ms
46
47
  tool_name = getattr(fn, "__name__", str(fn))
47
48
  started = time.monotonic()
49
+ token = _core._RequestCancelToken()
50
+
51
+ def _invoke_with_cancel() -> T:
52
+ guard = token.install()
53
+ try:
54
+ return fn(*args)
55
+ finally:
56
+ guard.close()
57
+
48
58
  try:
49
59
  result = await asyncio.wait_for(
50
- asyncio.to_thread(fn, *args),
60
+ asyncio.to_thread(_invoke_with_cancel),
51
61
  timeout=ms / 1000.0,
52
62
  )
53
63
  elapsed = time.monotonic() - started
@@ -85,10 +95,24 @@ async def run_with_timeout[T](
85
95
  echoed_input=echoed_input or {},
86
96
  retry_after_seconds=retry_after_seconds,
87
97
  ) from None
98
+ except asyncio.CancelledError:
99
+ elapsed = time.monotonic() - started
100
+ record_tool_runtime(tool_name, elapsed, "cancelled")
101
+ log.warning(
102
+ "tool runtime cancelled",
103
+ operation=tool_name,
104
+ mode=settings.mode,
105
+ runtime_ms=round(elapsed * 1000, 3),
106
+ timeout_ms=ms,
107
+ )
108
+ raise
88
109
  except LoreError as exc:
89
110
  elapsed = time.monotonic() - started
90
111
  record_tool_runtime(tool_name, elapsed, exc.code)
91
- if exc.code != "query_timeout" and elapsed >= profiling_thresholds(settings.mode).tool_seconds:
112
+ if (
113
+ exc.code != "query_timeout"
114
+ and elapsed >= profiling_thresholds(settings.mode).tool_seconds
115
+ ):
92
116
  log.info(
93
117
  "tool runtime completed",
94
118
  operation=tool_name,
@@ -109,6 +133,8 @@ async def run_with_timeout[T](
109
133
  timeout_ms=ms,
110
134
  )
111
135
  raise
136
+ finally:
137
+ token.cancel()
112
138
 
113
139
 
114
140
  __all__ = ["run_with_timeout"]
@@ -113,6 +113,8 @@ fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
113
113
  m.add_function(wrap_pyfunction!(crate::python::py_embedding_meta, m)?)?;
114
114
  m.add_class::<crate::python::PyReader>()?;
115
115
  m.add_class::<crate::python::PyEmbeddingBuilder>()?;
116
+ m.add_class::<crate::python::PyRequestCancelToken>()?;
117
+ m.add_class::<crate::python::PyRequestCancelGuard>()?;
116
118
  Ok(())
117
119
  }
118
120
 
@@ -24,7 +24,53 @@ use crate::ingest;
24
24
  use crate::reader::{DiffMode, EqField, MessageRow, Reader as CoreReader, RegexField};
25
25
  use crate::router;
26
26
  use crate::tid;
27
- use crate::timeout::DeadlineGuard;
27
+ use crate::timeout::{CancelGuard, CancelToken, DeadlineGuard};
28
+
29
+ #[pyclass(
30
+ module = "kernel_lore_mcp._core",
31
+ name = "_RequestCancelToken",
32
+ skip_from_py_object
33
+ )]
34
+ #[derive(Clone)]
35
+ pub struct PyRequestCancelToken {
36
+ inner: CancelToken,
37
+ }
38
+
39
+ #[pymethods]
40
+ impl PyRequestCancelToken {
41
+ #[new]
42
+ fn new() -> Self {
43
+ Self {
44
+ inner: CancelToken::new(),
45
+ }
46
+ }
47
+
48
+ fn cancel(&self) {
49
+ self.inner.cancel();
50
+ }
51
+
52
+ fn install(&self) -> PyRequestCancelGuard {
53
+ PyRequestCancelGuard {
54
+ guard: Some(CancelGuard::install(self.inner.clone())),
55
+ }
56
+ }
57
+ }
58
+
59
+ #[pyclass(
60
+ module = "kernel_lore_mcp._core",
61
+ name = "_RequestCancelGuard",
62
+ skip_from_py_object
63
+ )]
64
+ pub struct PyRequestCancelGuard {
65
+ guard: Option<CancelGuard>,
66
+ }
67
+
68
+ #[pymethods]
69
+ impl PyRequestCancelGuard {
70
+ fn close(&mut self) {
71
+ self.guard.take();
72
+ }
73
+ }
28
74
 
29
75
  /// Install a thread-local deadline for the duration of one reader
30
76
  /// query, matching the router-layer wall-clock cap. Cheap — ~one
@@ -2571,6 +2571,7 @@ fn parallel_confirm_substring(
2571
2571
 
2572
2572
  const CHUNK: usize = 256;
2573
2573
  let deadline = crate::timeout::current_deadline();
2574
+ let cancel = crate::timeout::current_cancel_token();
2574
2575
  let mut confirmed: Vec<MessageRow> = Vec::with_capacity(limit);
2575
2576
  for chunk in date_sorted_hits.chunks(CHUNK) {
2576
2577
  crate::timeout::check_request_deadline()?;
@@ -2584,6 +2585,7 @@ fn parallel_confirm_substring(
2584
2585
  .par_iter()
2585
2586
  .map(|row| -> Result<Option<MessageRow>> {
2586
2587
  let _g = deadline.map(crate::timeout::DeadlineGuard::install);
2588
+ let _c = cancel.clone().map(crate::timeout::CancelGuard::install);
2587
2589
  crate::timeout::check_request_deadline()?;
2588
2590
  let store = reader.store_for(&row.list)?;
2589
2591
  let body = store.read_at(row.body_segment_id, row.body_offset)?;
@@ -2653,9 +2655,11 @@ fn collect_trigram_candidates(
2653
2655
  Mutex::new(std::collections::HashSet::new());
2654
2656
  let overflowed = AtomicBool::new(false);
2655
2657
  let deadline = crate::timeout::current_deadline();
2658
+ let cancel = crate::timeout::current_cancel_token();
2656
2659
 
2657
2660
  all_segs.par_iter().try_for_each(|seg_dir| -> Result<()> {
2658
2661
  let _g = deadline.map(crate::timeout::DeadlineGuard::install);
2662
+ let _c = cancel.clone().map(crate::timeout::CancelGuard::install);
2659
2663
  crate::timeout::check_request_deadline()?;
2660
2664
  if overflowed.load(Ordering::Relaxed) {
2661
2665
  return Ok(());
@@ -2710,6 +2714,7 @@ fn parallel_confirm_fuzzy(
2710
2714
 
2711
2715
  const CHUNK: usize = 256;
2712
2716
  let deadline = crate::timeout::current_deadline();
2717
+ let cancel = crate::timeout::current_cancel_token();
2713
2718
  let mut confirmed: Vec<MessageRow> = Vec::with_capacity(limit);
2714
2719
  for chunk in date_sorted_hits.chunks(CHUNK) {
2715
2720
  crate::timeout::check_request_deadline()?;
@@ -2720,6 +2725,7 @@ fn parallel_confirm_fuzzy(
2720
2725
  .par_iter()
2721
2726
  .map(|row| -> Result<Option<MessageRow>> {
2722
2727
  let _g = deadline.map(crate::timeout::DeadlineGuard::install);
2728
+ let _c = cancel.clone().map(crate::timeout::CancelGuard::install);
2723
2729
  crate::timeout::check_request_deadline()?;
2724
2730
  let store = reader.store_for(&row.list)?;
2725
2731
  let body = store.read_at(row.body_segment_id, row.body_offset)?;
@@ -398,20 +398,24 @@ pub fn dispatch(
398
398
  // DeadlineGuard::install so scan() checks within the tier honor
399
399
  // the same budget.
400
400
  let deadline = crate::timeout::current_deadline();
401
+ let cancel = crate::timeout::current_cancel_token();
401
402
  let mut meta_out: TierOut = Ok((None, None));
402
403
  let mut trigram_out: TierOut = Ok((None, None));
403
404
  let mut bm25_out: TierOut = Ok((None, None));
404
405
  rayon::scope(|s| {
405
406
  s.spawn(|_| {
406
407
  let _g = deadline.map(crate::timeout::DeadlineGuard::install);
408
+ let _c = cancel.clone().map(crate::timeout::CancelGuard::install);
407
409
  meta_out = run_metadata();
408
410
  });
409
411
  s.spawn(|_| {
410
412
  let _g = deadline.map(crate::timeout::DeadlineGuard::install);
413
+ let _c = cancel.clone().map(crate::timeout::CancelGuard::install);
411
414
  trigram_out = run_trigram();
412
415
  });
413
416
  s.spawn(|_| {
414
417
  let _g = deadline.map(crate::timeout::DeadlineGuard::install);
418
+ let _c = cancel.clone().map(crate::timeout::CancelGuard::install);
415
419
  bm25_out = run_bm25();
416
420
  });
417
421
  });