mempalace-code 1.13.8__tar.gz → 1.14.2__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 (281) hide show
  1. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/CHANGELOG.md +74 -1
  2. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/PKG-INFO +3 -2
  3. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/README.md +2 -1
  4. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/code_retrieval_bench.py +193 -61
  5. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/data/code_retrieval_queries.json +93 -37
  6. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/OFFLINE_USAGE.md +6 -10
  7. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/RELEASING.md +10 -6
  8. mempalace_code-1.14.2/docs/UPSTREAM_COMPARISON.md +256 -0
  9. mempalace_code-1.14.2/docs/operations/wing-migration.md +303 -0
  10. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/quality/scorecard.json +17 -17
  11. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/quality/scorecard.md +11 -11
  12. mempalace_code-1.14.2/docs/quality/upstream-comparison.json +415 -0
  13. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/release-admission-rulesets.md +1 -0
  14. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/agent_plugin/plugin.json +1 -1
  15. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli.py +18 -2
  16. mempalace_code-1.14.2/mempalace_code/cli_commands/wing_migration.py +12 -0
  17. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/entity_registry.py +30 -191
  18. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp/dispatch.py +66 -26
  19. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp/tools/search.py +10 -3
  20. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mining/orchestrator.py +134 -31
  21. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mining/projects.py +10 -1
  22. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/normalize.py +4 -0
  23. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/search_reranker.py +22 -12
  24. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/searcher.py +23 -8
  25. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/storage.py +175 -41
  26. mempalace_code-1.14.2/mempalace_code/wing_migration.py +5398 -0
  27. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/pyproject.toml +1 -1
  28. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/docs_drift_guard.py +6 -6
  29. mempalace_code-1.14.2/scripts/pytest_failure_summary.py +83 -0
  30. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/release_admission_checks.py +45 -12
  31. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/release_readiness_gate.py +22 -0
  32. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/upstream_comparison_guard.py +2 -2
  33. mempalace_code-1.14.2/scripts/wing_migration.py +7 -0
  34. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_cli_command_modules.py +2 -0
  35. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_code_retrieval_bench.py +295 -30
  36. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_convo_miner.py +31 -0
  37. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_docs_drift_guard.py +6 -7
  38. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_e2e.py +4 -1
  39. mempalace_code-1.14.2/tests/test_entity_registry.py +251 -0
  40. mempalace_code-1.14.2/tests/test_explicit_wing_consistency.py +263 -0
  41. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_mcp_server.py +57 -0
  42. mempalace_code-1.14.2/tests/test_mine_source_exact.py +188 -0
  43. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_miner.py +332 -16
  44. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_normalize.py +32 -0
  45. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_packaging_namespace.py +23 -0
  46. mempalace_code-1.14.2/tests/test_pytest_failure_summary.py +68 -0
  47. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_release_preflight.py +1 -1
  48. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_release_readiness_gate.py +12 -0
  49. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_release_ruleset_contract.py +48 -2
  50. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_release_workflow_admission.py +48 -2
  51. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_search_reranker.py +32 -0
  52. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_searcher.py +232 -0
  53. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_storage.py +314 -1
  54. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_upstream_comparison_guard.py +28 -21
  55. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_watcher.py +1 -1
  56. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_watcher_resource_subprocess.py +23 -11
  57. mempalace_code-1.14.2/tests/test_wing_migration.py +3302 -0
  58. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/uv.lock +1 -1
  59. mempalace_code-1.13.8/docs/UPSTREAM_COMPARISON.md +0 -272
  60. mempalace_code-1.13.8/docs/quality/upstream-comparison.json +0 -316
  61. mempalace_code-1.13.8/tests/test_entity_registry.py +0 -133
  62. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/.gitignore +0 -0
  63. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/.gitleaks.toml +0 -0
  64. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/.pre-commit-config.yaml +0 -0
  65. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/AGENTS.md +0 -0
  66. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/CLAUDE.md +0 -0
  67. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/CONTRIBUTING.md +0 -0
  68. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/LICENSE +0 -0
  69. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/NOTICE +0 -0
  70. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/assets/mempalace_banner.jpg +0 -0
  71. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/BENCHMARKS.md +0 -0
  72. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/HYBRID_MODE.md +0 -0
  73. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/README.md +0 -0
  74. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/convomem_bench.py +0 -0
  75. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/demo_perf_budgets.json +0 -0
  76. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/demo_perf_budgets.py +0 -0
  77. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/dotnet_bench.py +0 -0
  78. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/embed_ab_bench.py +0 -0
  79. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/locomo_bench.py +0 -0
  80. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/longmemeval_bench.py +0 -0
  81. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/membench_bench.py +0 -0
  82. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/minilm_runtime_compatibility_fixture.json +0 -0
  83. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/results_embed_ab_2026-04-09.json +0 -0
  84. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/results_token_delta_mempalace.json +0 -0
  85. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/retrieval_quality_facts.json +0 -0
  86. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/token_delta_bench.py +0 -0
  87. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/benchmarks/token_delta_fixture_facts.json +0 -0
  88. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/AGENT_INSTALL.md +0 -0
  89. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/BACKUP_RESTORE.md +0 -0
  90. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/BENCH_TOKEN_DELTA.md +0 -0
  91. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/COMPARISON_GRAPHIFY.md +0 -0
  92. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/DEPENDENCY_UPGRADE_GATE.md +0 -0
  93. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/HOW_SEARCH_WORKS.md +0 -0
  94. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/LLM_USAGE_RULES.md +0 -0
  95. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/UPDATES.md +0 -0
  96. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/UPSTREAM_HARDENING.md +0 -0
  97. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/WHY_THIS_FORK.md +0 -0
  98. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/demo/code-intelligence-packet.json +0 -0
  99. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/demo/code-intelligence-packet.md +0 -0
  100. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/dependency-audit-allowlist.json +0 -0
  101. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/quality/PALACE-SCOPE-RESOURCE-BOUNDARY.md +0 -0
  102. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/quality/README.md +0 -0
  103. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/quality/autopilot-demo-gate-ledger.json +0 -0
  104. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/quality/autopilot-demo-gate-ledger.md +0 -0
  105. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/quality/workflow-review-protocol.md +0 -0
  106. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/docs/refactoring/REFAC-MINER-MODULES/progress.md +0 -0
  107. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/examples/HOOKS_TUTORIAL.md +0 -0
  108. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/examples/basic_mining.py +0 -0
  109. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/examples/convo_import.py +0 -0
  110. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/examples/gemini_cli_setup.md +0 -0
  111. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/examples/mcp_setup.md +0 -0
  112. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/hooks/README.md +0 -0
  113. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/hooks/mempal_precompact_hook.sh +0 -0
  114. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/hooks/mempal_save_hook.sh +0 -0
  115. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace/__init__.py +0 -0
  116. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace/mcp_server.py +0 -0
  117. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/README.md +0 -0
  118. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/__init__.py +0 -0
  119. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/__main__.py +0 -0
  120. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/_stdio.py +0 -0
  121. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/agent_plugin/mcp.json +0 -0
  122. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/agent_plugin/schemas/1.0.0/mcp.schema.json +0 -0
  123. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/agent_plugin/schemas/1.0.0/plugin.schema.json +0 -0
  124. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/agent_plugin/schemas/SCHEMA-NOTICE.md +0 -0
  125. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/agent_plugin/skills/mempalace/SKILL.md +0 -0
  126. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/agent_plugins.py +0 -0
  127. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/architecture.py +0 -0
  128. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/backup.py +0 -0
  129. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli_commands/__init__.py +0 -0
  130. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli_commands/agent_plugin.py +0 -0
  131. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli_commands/alias.py +0 -0
  132. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli_commands/backup_restore.py +0 -0
  133. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli_commands/common.py +0 -0
  134. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli_commands/diary.py +0 -0
  135. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli_commands/export_import.py +0 -0
  136. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli_commands/ingest.py +0 -0
  137. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli_commands/maintenance.py +0 -0
  138. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli_commands/model.py +0 -0
  139. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli_commands/preflight.py +0 -0
  140. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli_commands/query.py +0 -0
  141. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli_commands/update.py +0 -0
  142. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli_commands/version_check.py +0 -0
  143. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/cli_commands/watch.py +0 -0
  144. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/config.py +0 -0
  145. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/convo_miner.py +0 -0
  146. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/dialect.py +0 -0
  147. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/disk_budget.py +0 -0
  148. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/entity_detector.py +0 -0
  149. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/export.py +0 -0
  150. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/general_extractor.py +0 -0
  151. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/knowledge_graph.py +0 -0
  152. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/language_catalog.py +0 -0
  153. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/layers.py +0 -0
  154. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp/__init__.py +0 -0
  155. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp/protocol_compat.py +0 -0
  156. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp/protocol_text.py +0 -0
  157. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp/registry.py +0 -0
  158. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp/runtime.py +0 -0
  159. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp/tools/__init__.py +0 -0
  160. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp/tools/architecture.py +0 -0
  161. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp/tools/diary.py +0 -0
  162. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp/tools/graph.py +0 -0
  163. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp/tools/kg.py +0 -0
  164. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp/tools/read.py +0 -0
  165. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp/tools/write.py +0 -0
  166. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp_launcher.py +0 -0
  167. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp_server.py +0 -0
  168. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mcp_tool_profiles.py +0 -0
  169. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/miner.py +0 -0
  170. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mining/__init__.py +0 -0
  171. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mining/batching.py +0 -0
  172. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mining/chunkers.py +0 -0
  173. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mining/kg_extract.py +0 -0
  174. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mining/languages.py +0 -0
  175. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mining/scanner.py +0 -0
  176. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mining/symbols.py +0 -0
  177. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/mirror_preflight.py +0 -0
  178. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/onboarding.py +0 -0
  179. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/operation_lock.py +0 -0
  180. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/palace_graph.py +0 -0
  181. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/py.typed +0 -0
  182. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/reader.py +0 -0
  183. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/retrieval_rerank.py +0 -0
  184. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/room_detector_local.py +0 -0
  185. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/source_io.py +0 -0
  186. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/spellcheck.py +0 -0
  187. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/split_mega_files.py +0 -0
  188. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/taxonomy_filters.py +0 -0
  189. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/treesitter.py +0 -0
  190. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/updater.py +0 -0
  191. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/version.py +0 -0
  192. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/version_check.py +0 -0
  193. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/mempalace_code/watcher.py +0 -0
  194. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/pyrightconfig.strict.json +0 -0
  195. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/architecture_guard.py +0 -0
  196. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/bootstrap.sh +0 -0
  197. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/dependency_upgrade_gate.py +0 -0
  198. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/gate_inventory.py +0 -0
  199. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/gen_code_intelligence_packet.py +0 -0
  200. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/gitleaks_scan.py +0 -0
  201. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/nuke_wing.py +0 -0
  202. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/public_safety_scan.py +0 -0
  203. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/quality_scorecard.py +0 -0
  204. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/release_artifact_gate.py +0 -0
  205. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/release_install_metadata_smoke.py +0 -0
  206. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/release_preflight.py +0 -0
  207. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/release_public_read.py +0 -0
  208. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/release_status_gate.py +0 -0
  209. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/scripts/workflow_summary_guard.py +0 -0
  210. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/conftest.py +0 -0
  211. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/fixtures/unreasoned_suppression.py +0 -0
  212. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/release_smoke_support.py +0 -0
  213. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_agent_plugin_package.py +0 -0
  214. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_agent_skill_authority_contract.py +0 -0
  215. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_architecture_extraction.py +0 -0
  216. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_architecture_guard.py +0 -0
  217. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_autopilot_demo_gate_ledger.py +0 -0
  218. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_backup.py +0 -0
  219. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_backup_cli.py +0 -0
  220. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_chunking.py +0 -0
  221. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_cli.py +0 -0
  222. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_cli_golden_scenarios.py +0 -0
  223. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_code_intelligence_packet.py +0 -0
  224. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_config.py +0 -0
  225. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_demo_perf_budgets.py +0 -0
  226. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_dependency_upgrade_gate.py +0 -0
  227. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_dialect.py +0 -0
  228. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_disk_budget.py +0 -0
  229. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_dotnet_bench.py +0 -0
  230. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_dotnet_config.py +0 -0
  231. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_embed_ab_bench.py +0 -0
  232. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_embedding_runtime.py +0 -0
  233. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_entity_detector.py +0 -0
  234. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_export.py +0 -0
  235. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_gate_inventory.py +0 -0
  236. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_general_extractor.py +0 -0
  237. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_gitleaks_scan.py +0 -0
  238. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_installed_artifact_behavior.py +0 -0
  239. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_installed_cli_palace_scope.py +0 -0
  240. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_kg_extract.py +0 -0
  241. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_knowledge_graph.py +0 -0
  242. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_lang_detect.py +0 -0
  243. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_language_catalog.py +0 -0
  244. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_layers.py +0 -0
  245. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_mcp_protocol_compat.py +0 -0
  246. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_mcp_registry.py +0 -0
  247. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_mcp_tool_profiles.py +0 -0
  248. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_miner_modules.py +0 -0
  249. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_offline.py +0 -0
  250. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_onboarding.py +0 -0
  251. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_operation_lock.py +0 -0
  252. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_palace_graph.py +0 -0
  253. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_palace_scope_resource_boundary.py +0 -0
  254. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_public_safety_scan.py +0 -0
  255. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_pyright_optional_dependencies.py +0 -0
  256. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_quality_palace_scope_resource_boundary.py +0 -0
  257. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_quality_scorecard.py +0 -0
  258. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_reader.py +0 -0
  259. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_regular_source_guard.py +0 -0
  260. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_release_artifact_gate.py +0 -0
  261. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_release_install_metadata_smoke.py +0 -0
  262. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_release_public_read.py +0 -0
  263. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_release_skill_contract.py +0 -0
  264. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_release_status_gate.py +0 -0
  265. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_retrieval_rerank.py +0 -0
  266. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_split_mega_files.py +0 -0
  267. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_stdio.py +0 -0
  268. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_storage_lance.py +0 -0
  269. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_symbol_extract.py +0 -0
  270. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_taxonomy_filters.py +0 -0
  271. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_token_delta_bench.py +0 -0
  272. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_treesitter.py +0 -0
  273. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_type_suppressions.py +0 -0
  274. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_updater.py +0 -0
  275. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_version_check.py +0 -0
  276. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_version_consistency.py +0 -0
  277. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_watcher_resource_bounds.py +0 -0
  278. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tests/test_workflow_summary_guard.py +0 -0
  279. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tools/gitleaks/go.mod +0 -0
  280. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tools/gitleaks/go.sum +0 -0
  281. {mempalace_code-1.13.8 → mempalace_code-1.14.2}/tools/gitleaks/tools.go +0 -0
@@ -2,6 +2,79 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## v1.14.2 — 2026-09-21
6
+
7
+ Recovery release for the accumulated v1.14.1 changes. The immutable v1.14.1
8
+ tag stopped before artifact build when the pinned upstream head moved, so no
9
+ v1.14.1 package or GitHub Release exists.
10
+
11
+ ### Changed
12
+
13
+ - Refreshed the reviewed upstream snapshot through `e038af97`; the MCP import
14
+ fix already has an equivalent entry-point boundary in this fork, while the
15
+ upstream sync identity and JSON audit-WAL changes belong to unsupported
16
+ runtime owners.
17
+ - Added the failed v1.14.1 tag to the immutable orphan-tag evidence registry.
18
+ - Kept the live upstream check at the canonical pre-tag boundary so later
19
+ upstream movement cannot strand another immutable public tag.
20
+
21
+ ## v1.14.1 — 2026-09-20 (tagged, not published)
22
+
23
+ The tag remains immutable public evidence. Publication stopped before artifact
24
+ build because the reviewed upstream head changed; these changes ship in v1.14.2.
25
+
26
+ ### Fixed
27
+
28
+ - Wing migration now binds live evidence to the qualified runner, updates the
29
+ destination runtime hash after the merge, bounds retained qualification storage,
30
+ and preserves exact recovery for the original v16 receipt.
31
+ - Wing migration receipts use the owner-controlled rule `retain until verified
32
+ recovery or owner disposition`; the obsolete calendar deadline is rejected before
33
+ any evidence or migration state is written.
34
+ - Installed-wheel qualification now treats the installation-wide MCP lock files as
35
+ stable protected anchors and directly exercises the `wing-migration` CLI refusal
36
+ without an action.
37
+ - Runtime provenance checks now accept platform-managed symlink ancestors outside
38
+ the owned installation and source roots while still refusing symlinks inside them.
39
+
40
+ ## v1.14.0 — 2026-09-18
41
+
42
+ ### Added
43
+
44
+ - Added the installed wing migration operator with exact-union, snapshot, retry,
45
+ and recovery qualification. Synthetic mining uses an independent offline copy of
46
+ the validated canonical model cache. Exact-host live authority adds MCP maintenance
47
+ fencing, retained recovery evidence, and an original-path zero-write runtime check.
48
+
49
+ ### Changed
50
+
51
+ - Code retrieval qualification now checks current declaration owners and same-hit
52
+ symbols across storage, default code search, and hybrid code search while exposing
53
+ additive ranking evidence.
54
+ - Configured non-.NET wing identifiers now preserve hyphens consistently across
55
+ standalone mining, mine-all, and fresh watcher startup. Git/folder-derived names,
56
+ .NET solution precedence, explicit CLI overrides, historical rows, and active
57
+ watcher snapshots retain their existing behavior.
58
+
59
+ ### Fixed
60
+
61
+ - Recognized Codex rollout files that contain no complete supported conversation now fail before
62
+ raw JSON can be filed as memory and remain eligible for retry after the transcript completes.
63
+ - Entity-registry atomic saves now bound temporary-name collision retries and surface directory
64
+ permission failures immediately on every supported Python version.
65
+ - Existing entity registries with malformed JSON, a non-object root, or a read failure now stop
66
+ before onboarding or programmatic setup can replace their non-regenerable contents.
67
+ - Stale-file deletion and dependent knowledge-graph and tiny-hash expiry now require
68
+ proven regenerable sources owned by the selected project.
69
+ - Full-copy wing qualification now removes each recovered descendant trial before creating the
70
+ next one, caps retained baseline snapshots at one, and refuses stale trial or snapshot
71
+ generations before writing new evidence.
72
+
73
+ ### Security
74
+
75
+ - Removed the unused Wikipedia lookup API from the entity registry. Legacy cached entries remain
76
+ readable, while current packages contain no unconfigured entity-research network path.
77
+
5
78
  ## v1.13.8 — 2026-09-02
6
79
 
7
80
  Recovery release for the accumulated v1.13.7 changes. The immutable v1.13.7
@@ -882,7 +955,7 @@ Add 4 architecture-oriented MCP tools for .NET type analysis: `mempalace_find_im
882
955
 
883
956
  ## 2026-04-17 · SKILLS-HOOKS
884
957
 
885
- Add Claude Code skills and hooks from wh40k workflow: 12 skills (`/start`, `/status`, `/verify`, `/palace-health`, `/task-plan`, `/task-hardening`, `/doc-refresh`, `/ship`, `/release`, `/entropy-gc`, `/mine`, `/bench`), 3 shared modules (mode-classification, task-state, commit-checkpoint), Codex review integration, pre-commit verification gate, and edit logging hooks.
958
+ Add Claude Code skills and hooks: 12 skills (`/start`, `/status`, `/verify`, `/palace-health`, `/task-plan`, `/task-hardening`, `/doc-refresh`, `/ship`, `/release`, `/entropy-gc`, `/mine`, `/bench`), 3 shared modules (mode-classification, task-state, commit-checkpoint), Codex review integration, pre-commit verification gate, and edit logging hooks.
886
959
 
887
960
  ## 2026-04-17 · BENCH-DOTNET
888
961
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: mempalace-code
3
- Version: 1.13.8
3
+ Version: 1.14.2
4
4
  Summary: Developer memory tool — mine codebases and conversations into a LanceDB-backed searchable palace. No API key required.
5
5
  Project-URL: Homepage, https://github.com/rergards/mempalace-code
6
6
  Project-URL: Repository, https://github.com/rergards/mempalace-code
@@ -1180,6 +1180,7 @@ mempalace-code version-check # show version-check stat
1180
1180
  mempalace-code version-check --check-now # check PyPI now; prints a pip fallback for unmanaged installs
1181
1181
  mempalace-code update status # inspect upgrade eligibility (supported installs)
1182
1182
  mempalace-code agent-plugin path --json # locate the installed Agent Plugins package directory
1183
+ mempalace-code wing-migration qualify --mode synthetic # qualify the receipt-bound copy migration operator
1183
1184
  ```
1184
1185
 
1185
1186
  Plain `status` prints a full wing/room breakdown, so its output grows with palace size. Do not use it as a routine agent bootstrap or machine-readable health check; use `status --summary` for bounded shell-based CLI discovery, task-specific MCP retrieval, or `mempalace-code health --json` for a compact CLI integrity report.
@@ -1275,7 +1276,7 @@ python -m pyright --pythonpath "$(python -c 'import sys; print(sys.executable)')
1275
1276
  Apache 2.0 — see [LICENSE](LICENSE) and [NOTICE](NOTICE).
1276
1277
 
1277
1278
  <!-- Link Definitions -->
1278
- [version-shield]: https://img.shields.io/badge/version-1.13.8-4dc9f6?style=flat-square&labelColor=0a0e14
1279
+ [version-shield]: https://img.shields.io/badge/version-1.14.2-4dc9f6?style=flat-square&labelColor=0a0e14
1279
1280
  [release-link]: https://github.com/rergards/mempalace-code/releases
1280
1281
  [python-shield]: https://img.shields.io/badge/python-3.11+-7dd8f8?style=flat-square&labelColor=0a0e14&logo=python&logoColor=7dd8f8
1281
1282
  [python-link]: https://www.python.org/
@@ -1121,6 +1121,7 @@ mempalace-code version-check # show version-check stat
1121
1121
  mempalace-code version-check --check-now # check PyPI now; prints a pip fallback for unmanaged installs
1122
1122
  mempalace-code update status # inspect upgrade eligibility (supported installs)
1123
1123
  mempalace-code agent-plugin path --json # locate the installed Agent Plugins package directory
1124
+ mempalace-code wing-migration qualify --mode synthetic # qualify the receipt-bound copy migration operator
1124
1125
  ```
1125
1126
 
1126
1127
  Plain `status` prints a full wing/room breakdown, so its output grows with palace size. Do not use it as a routine agent bootstrap or machine-readable health check; use `status --summary` for bounded shell-based CLI discovery, task-specific MCP retrieval, or `mempalace-code health --json` for a compact CLI integrity report.
@@ -1216,7 +1217,7 @@ python -m pyright --pythonpath "$(python -c 'import sys; print(sys.executable)')
1216
1217
  Apache 2.0 — see [LICENSE](LICENSE) and [NOTICE](NOTICE).
1217
1218
 
1218
1219
  <!-- Link Definitions -->
1219
- [version-shield]: https://img.shields.io/badge/version-1.13.8-4dc9f6?style=flat-square&labelColor=0a0e14
1220
+ [version-shield]: https://img.shields.io/badge/version-1.14.2-4dc9f6?style=flat-square&labelColor=0a0e14
1220
1221
  [release-link]: https://github.com/rergards/mempalace-code/releases
1221
1222
  [python-shield]: https://img.shields.io/badge/python-3.11+-7dd8f8?style=flat-square&labelColor=0a0e14&logo=python&logoColor=7dd8f8
1222
1223
  [python-link]: https://www.python.org/
@@ -9,7 +9,9 @@ file-level retrieval quality for naive, smart, and tree-sitter chunking.
9
9
  from __future__ import annotations
10
10
 
11
11
  import argparse
12
+ import ast
12
13
  import contextlib
14
+ import gc
13
15
  import hashlib
14
16
  import importlib
15
17
  import importlib.metadata
@@ -37,10 +39,19 @@ if not _COMPATIBILITY_MODE_REQUESTED:
37
39
  sys.path.insert(0, str(_PROJECT_ROOT))
38
40
 
39
41
  import mempalace_code.miner as miner # noqa: E402
42
+ import mempalace_code.mining.chunkers as mining_chunkers # noqa: E402
43
+ import mempalace_code.searcher as searcher # noqa: E402
40
44
  from mempalace_code.storage import DEFAULT_EMBED_MODEL, open_store # noqa: E402
41
45
  from mempalace_code.version import __version__ # noqa: E402
42
46
 
43
47
  SUPPORTED_MODES = ("naive", "smart", "treesitter")
48
+ SEARCH_PATHS = ("store.query", "code_search", "code_search_hybrid")
49
+ PATH_CANDIDATE_LIMITS = {
50
+ "store.query": 10,
51
+ "code_search": 30,
52
+ "code_search_hybrid": 50,
53
+ }
54
+ REPORT_SCHEMA_VERSION = 2
44
55
  DEFAULT_DATASET = Path(__file__).resolve().parent / "data" / "code_retrieval_queries.json"
45
56
  NAIVE_WINDOW_LINES = 80
46
57
  NAIVE_OVERLAP_LINES = 10
@@ -379,7 +390,8 @@ def load_dataset(path: Path, limit: int | None = None) -> list[dict]:
379
390
  if not records:
380
391
  raise BenchError("dataset must contain at least one record")
381
392
 
382
- required = {"id", "query", "expected_files", "category"}
393
+ required = {"id", "query", "expected_files", "category", "match_kind"}
394
+ seen_ids = set()
383
395
  for idx, record in enumerate(records):
384
396
  if not isinstance(record, dict):
385
397
  raise BenchError(f"dataset record {idx} must be an object")
@@ -388,6 +400,9 @@ def load_dataset(path: Path, limit: int | None = None) -> list[dict]:
388
400
  raise BenchError(f"dataset record {idx} is missing: {', '.join(missing)}")
389
401
  if not isinstance(record["id"], str) or not record["id"]:
390
402
  raise BenchError(f"dataset record {idx} id must be a non-empty string")
403
+ if record["id"] in seen_ids:
404
+ raise BenchError(f"dataset record {record['id']} has a duplicate id")
405
+ seen_ids.add(record["id"])
391
406
  if not isinstance(record["query"], str) or not record["query"]:
392
407
  raise BenchError(f"dataset record {record['id']} query must be a non-empty string")
393
408
  if not isinstance(record["category"], str) or not record["category"]:
@@ -401,6 +416,30 @@ def load_dataset(path: Path, limit: int | None = None) -> list[dict]:
401
416
  raise BenchError(
402
417
  f"dataset record {record['id']} expected_files must be a non-empty list of strings"
403
418
  )
419
+ if len(set(expected_files)) != len(expected_files):
420
+ raise BenchError(f"dataset record {record['id']} expected_files must be unique")
421
+ match_kind = record["match_kind"]
422
+ if match_kind not in {"symbol", "file_only"}:
423
+ raise BenchError(
424
+ f"dataset record {record['id']} match_kind must be 'symbol' or 'file_only'"
425
+ )
426
+ expected_symbols = record.get("expected_symbols")
427
+ if match_kind == "symbol":
428
+ if (
429
+ not isinstance(expected_symbols, list)
430
+ or not expected_symbols
431
+ or any(not isinstance(symbol, str) or not symbol for symbol in expected_symbols)
432
+ ):
433
+ raise BenchError(
434
+ f"dataset record {record['id']} expected_symbols must be a non-empty list "
435
+ "of strings for symbol matches"
436
+ )
437
+ if len(set(expected_symbols)) != len(expected_symbols):
438
+ raise BenchError(f"dataset record {record['id']} expected_symbols must be unique")
439
+ elif "expected_symbols" in record:
440
+ raise BenchError(
441
+ f"dataset record {record['id']} file_only matches must omit expected_symbols"
442
+ )
404
443
  return records
405
444
 
406
445
 
@@ -421,22 +460,37 @@ def _source_identity(source_file: str) -> tuple[str, str]:
421
460
 
422
461
 
423
462
  def file_matches_expected(source_file: str, expected_file: str) -> bool:
424
- """Match expected file by basename equality or repo-relative suffix equality."""
463
+ """Match a basename or complete normalized path suffix on segment boundaries."""
425
464
  source, basename = _source_identity(source_file)
426
- expected = expected_file.replace("\\", "/")
427
- return basename == expected or source.endswith(expected)
428
-
429
-
430
- def hit_at_k(metadatas: list[dict], expected_files: list[str], k: int) -> bool:
465
+ expected = expected_file.replace("\\", "/").strip("/")
466
+ if "/" not in expected:
467
+ return basename == expected
468
+ return source == expected or source.endswith(f"/{expected}")
469
+
470
+
471
+ def hit_at_k(
472
+ metadatas: list[dict],
473
+ expected_files: list[str],
474
+ k: int,
475
+ expected_symbols: list[str] | None = None,
476
+ ) -> bool:
431
477
  """Return True when any expected file appears within top-k result metadata."""
432
- return rank_of_first_hit(metadatas[:k], expected_files) is not None
478
+ return rank_of_first_hit(metadatas[:k], expected_files, expected_symbols) is not None
433
479
 
434
480
 
435
- def rank_of_first_hit(metadatas: list[dict], expected_files: list[str]) -> int | None:
436
- """Return 1-based rank of the first matching source file, if any."""
481
+ def rank_of_first_hit(
482
+ metadatas: list[dict],
483
+ expected_files: list[str],
484
+ expected_symbols: list[str] | None = None,
485
+ ) -> int | None:
486
+ """Return the first same-hit file and optional exact-symbol match."""
437
487
  for rank, meta in enumerate(metadatas, start=1):
438
488
  source_file = meta.get("source_file", "")
439
- if any(file_matches_expected(source_file, expected) for expected in expected_files):
489
+ file_match = any(
490
+ file_matches_expected(source_file, expected) for expected in expected_files
491
+ )
492
+ symbol_match = expected_symbols is None or meta.get("symbol_name") in expected_symbols
493
+ if file_match and symbol_match:
440
494
  return rank
441
495
  return None
442
496
 
@@ -447,16 +501,50 @@ def scan_corpus_files(repo_dir: Path) -> list[Path]:
447
501
 
448
502
 
449
503
  def validate_dataset(repo_dir: Path, records: list[dict]) -> int:
450
- """Validate that every expected file resolves in the scanned corpus."""
504
+ """Validate expected paths and Python declarations against the current corpus."""
451
505
  scanned = [str(path.relative_to(repo_dir)) for path in scan_corpus_files(repo_dir)]
452
506
  failures = []
453
507
  for record in records:
508
+ resolved_files = []
454
509
  for expected in record["expected_files"]:
455
- if not any(file_matches_expected(source, expected) for source in scanned):
510
+ matches = [source for source in scanned if file_matches_expected(source, expected)]
511
+ if not matches:
456
512
  failures.append((record["id"], expected))
457
513
  print(f"FAIL {record['id']}: missing {expected}")
458
514
  else:
515
+ resolved_files.extend(matches)
459
516
  print(f"PASS {record['id']}: {expected}")
517
+ if record["match_kind"] == "symbol" and resolved_files:
518
+ declared = set()
519
+ for source in resolved_files:
520
+ path = repo_dir / source
521
+ if path.suffix != ".py":
522
+ continue
523
+ try:
524
+ tree = ast.parse(path.read_text(encoding="utf-8", errors="replace"))
525
+ except (OSError, SyntaxError) as exc:
526
+ failures.append((record["id"], f"unreadable declaration owner: {source}"))
527
+ print(f"FAIL {record['id']}: cannot parse declarations in {source}: {exc}")
528
+ continue
529
+ declared.update(
530
+ node.name
531
+ for node in ast.walk(tree)
532
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef))
533
+ )
534
+ missing_symbols = [
535
+ symbol for symbol in record["expected_symbols"] if symbol not in declared
536
+ ]
537
+ if len(missing_symbols) == len(record["expected_symbols"]):
538
+ failures.append((record["id"], "expected declaration"))
539
+ print(
540
+ f"FAIL {record['id']}: no expected declaration in owner files: "
541
+ f"{', '.join(record['expected_symbols'])}"
542
+ )
543
+ else:
544
+ print(
545
+ f"PASS {record['id']}: declaration "
546
+ f"{', '.join(symbol for symbol in record['expected_symbols'] if symbol in declared)}"
547
+ )
460
548
  if failures:
461
549
  return 1
462
550
  print(f"PASS dataset: {len(records)} queries")
@@ -469,12 +557,12 @@ def smart_chunking_only(enabled: bool):
469
557
  if not enabled:
470
558
  yield
471
559
  return
472
- original = miner.get_parser
473
- miner.get_parser = lambda _language: None
560
+ original = mining_chunkers.get_parser
561
+ mining_chunkers.get_parser = lambda _language: None
474
562
  try:
475
563
  yield
476
564
  finally:
477
- miner.get_parser = original
565
+ mining_chunkers.get_parser = original
478
566
 
479
567
 
480
568
  def _repo_commit(repo_dir: Path) -> str | None:
@@ -605,26 +693,58 @@ def mine_mode(repo_dir: Path, palace_path: Path, mode: str):
605
693
  return mine_with_miner(repo_dir, palace_path, mode)
606
694
 
607
695
 
608
- def run_queries(store, records: list[dict]) -> tuple[list[dict], list[float]]:
609
- per_query = []
610
- latencies = []
611
- for record in records:
612
- t0 = time.time()
696
+ def _query_path(store, palace_path: Path, record: dict, path: str) -> list[dict]:
697
+ if path == "store.query":
613
698
  results = store.query(
614
699
  query_texts=[record["query"]],
615
700
  n_results=10,
616
701
  include=["documents", "metadatas", "distances"],
617
702
  )
703
+ return results.get("metadatas", [[]])[0] if results.get("metadatas") else []
704
+
705
+ rerank = "hybrid" if path == "code_search_hybrid" else None
706
+ results = searcher.code_search(str(palace_path), record["query"], n_results=10, rerank=rerank)
707
+ if "error" in results:
708
+ raise BenchError(f"{path} failed for {record['id']}: {results['error']}")
709
+ return results.get("results", [])
710
+
711
+
712
+ @contextlib.contextmanager
713
+ def supported_search_store(store):
714
+ """Route supported code_search calls to the lifecycle-owned benchmark store."""
715
+ original = searcher.open_store
716
+
717
+ def use_existing_store(*_args, **_kwargs):
718
+ return store
719
+
720
+ searcher.open_store = use_existing_store
721
+ try:
722
+ yield
723
+ finally:
724
+ searcher.open_store = original
725
+
726
+
727
+ def run_queries(
728
+ store, palace_path: Path, records: list[dict], path: str
729
+ ) -> tuple[list[dict], list[float]]:
730
+ """Evaluate one supported retrieval path without hiding errors or misses."""
731
+ per_query = []
732
+ latencies = []
733
+ for record in records:
734
+ t0 = time.time()
735
+ metas = _query_path(store, palace_path, record, path)
618
736
  latencies.append((time.time() - t0) * 1000)
619
- metas = results.get("metadatas", [[]])[0] if results.get("metadatas") else []
620
- rank = rank_of_first_hit(metas, record["expected_files"])
737
+ symbols = record["expected_symbols"] if record["match_kind"] == "symbol" else None
738
+ rank = rank_of_first_hit(metas, record["expected_files"], symbols)
621
739
  per_query.append(
622
740
  {
623
741
  "id": record["id"],
624
742
  "query": record["query"],
625
743
  "category": record["category"],
626
744
  "expected_files": record["expected_files"],
627
- "expected_symbols": record.get("expected_symbols", []),
745
+ "match_kind": record["match_kind"],
746
+ "expected_symbols": record.get("expected_symbols"),
747
+ "path": path,
628
748
  "rank": rank,
629
749
  "hit_at_5": rank is not None and rank <= 5,
630
750
  "hit_at_10": rank is not None and rank <= 10,
@@ -636,55 +756,61 @@ def run_queries(store, records: list[dict]) -> tuple[list[dict], list[float]]:
636
756
 
637
757
 
638
758
  def aggregate_results(per_query: list[dict], latencies: list[float]) -> dict:
639
- query_count = len(per_query)
640
- r5 = sum(1 for row in per_query if row["hit_at_5"]) / query_count
641
- r10 = sum(1 for row in per_query if row["hit_at_10"]) / query_count
642
- mrr = sum(1 / row["rank"] for row in per_query if row["rank"]) / query_count
643
-
644
- by_category = defaultdict(list)
645
- for row in per_query:
646
- by_category[row["category"]].append(row)
647
-
648
- per_category = {}
649
- for category, rows in sorted(by_category.items()):
759
+ def summarize(rows: list[dict]) -> dict:
650
760
  count = len(rows)
651
- per_category[category] = {
761
+ if not count:
762
+ return {"query_count": 0, "R@5": None, "R@10": None, "MRR": None}
763
+ return {
652
764
  "query_count": count,
653
765
  "R@5": sum(1 for row in rows if row["hit_at_5"]) / count,
654
766
  "R@10": sum(1 for row in rows if row["hit_at_10"]) / count,
655
767
  "MRR": sum(1 / row["rank"] for row in rows if row["rank"]) / count,
656
768
  }
657
769
 
770
+ by_category = defaultdict(list)
771
+ for row in per_query:
772
+ by_category[row["category"]].append(row)
658
773
  return {
659
- "R@5": r5,
660
- "R@10": r10,
661
- "MRR": mrr,
662
774
  "query_latency_avg_ms": sum(latencies) / len(latencies) if latencies else 0.0,
663
- "per_category": per_category,
775
+ "populations": {
776
+ kind: summarize([row for row in per_query if row["match_kind"] == kind])
777
+ for kind in ("symbol", "file_only")
778
+ },
779
+ "per_category": {
780
+ category: summarize(rows) for category, rows in sorted(by_category.items())
781
+ },
664
782
  }
665
783
 
666
784
 
667
785
  def run_mode(repo_dir: Path, mode: str, records: list[dict]) -> dict:
668
786
  tmp_dir = Path(tempfile.mkdtemp(prefix=f"code_bench_{mode}_"))
787
+ store = None
669
788
  try:
670
789
  t0 = time.time()
671
790
  store, chunk_count, mode_meta = mine_mode(repo_dir, tmp_dir, mode)
672
791
  embed_time_s = time.time() - t0
673
- per_query, latencies = run_queries(store, records)
674
- metrics = aggregate_results(per_query, latencies)
792
+ paths = {}
793
+ with supported_search_store(store):
794
+ for path in SEARCH_PATHS:
795
+ per_query, latencies = run_queries(store, tmp_dir, records, path)
796
+ metrics = aggregate_results(per_query, latencies)
797
+ paths[path] = {
798
+ "ordering": "storage-ranked" if path != "code_search_hybrid" else "hybrid",
799
+ "requested_results": 10,
800
+ "candidate_fetch_limit": PATH_CANDIDATE_LIMITS[path],
801
+ **metrics,
802
+ "per_query": per_query,
803
+ }
675
804
  return {
676
805
  "chunk_count": chunk_count,
677
806
  "embed_time_s": embed_time_s,
678
807
  "index_size_mb": _index_size_mb(tmp_dir),
679
- "query_latency_avg_ms": metrics["query_latency_avg_ms"],
680
- "R@5": metrics["R@5"],
681
- "R@10": metrics["R@10"],
682
- "MRR": metrics["MRR"],
683
- "per_category": metrics["per_category"],
684
- "per_query": per_query,
808
+ "paths": paths,
685
809
  **mode_meta,
686
810
  }
687
811
  finally:
812
+ store = None
813
+ gc.collect()
688
814
  shutil.rmtree(tmp_dir, ignore_errors=True)
689
815
 
690
816
 
@@ -692,26 +818,31 @@ def build_comparison(modes: dict) -> dict:
692
818
  """Build compact table-friendly comparison metrics."""
693
819
  return {
694
820
  mode: {
695
- "R@5": result["R@5"],
696
- "R@10": result["R@10"],
697
- "MRR": result["MRR"],
698
- "chunk_count": result["chunk_count"],
699
- "query_latency_avg_ms": result["query_latency_avg_ms"],
821
+ path: {
822
+ "symbol": result["paths"][path]["populations"]["symbol"],
823
+ "file_only": result["paths"][path]["populations"]["file_only"],
824
+ "query_latency_avg_ms": result["paths"][path]["query_latency_avg_ms"],
825
+ }
826
+ for path in SEARCH_PATHS
700
827
  }
701
828
  for mode, result in modes.items()
702
829
  }
703
830
 
704
831
 
705
832
  def print_table(modes: dict) -> None:
706
- print("\nCode retrieval results")
707
- print("mode chunks R@5 R@10 MRR query_ms")
708
- print("------------ ------- ----- ----- ----- --------")
833
+ print(f"\nCode retrieval results (schema={REPORT_SCHEMA_VERSION}, model={DEFAULT_EMBED_MODEL})")
834
+ print("mode path population count R@5 R@10 MRR")
835
+ print("------------ ------------------ ---------- ----- ----- ----- -----")
709
836
  for mode, result in modes.items():
710
- print(
711
- f"{mode:<12} {result['chunk_count']:>7} "
712
- f"{result['R@5']:.3f} {result['R@10']:.3f} "
713
- f"{result['MRR']:.3f} {result['query_latency_avg_ms']:.1f}"
714
- )
837
+ for path, path_result in result["paths"].items():
838
+ for population, metrics in path_result["populations"].items():
839
+ values = [metrics[name] for name in ("R@5", "R@10", "MRR")]
840
+ formatted = ["null" if value is None else f"{value:.3f}" for value in values]
841
+ print(
842
+ f"{mode:<12} {path:<18} {population:<10} "
843
+ f"{metrics['query_count']:>5} {formatted[0]:>5} "
844
+ f"{formatted[1]:>5} {formatted[2]:>5}"
845
+ )
715
846
 
716
847
 
717
848
  def run_benchmark(repo_dir: Path, dataset_path: Path, modes: list[str], limit: int | None) -> dict:
@@ -719,6 +850,7 @@ def run_benchmark(repo_dir: Path, dataset_path: Path, modes: list[str], limit: i
719
850
  mode_results = {mode: run_mode(repo_dir, mode, records) for mode in modes}
720
851
  return {
721
852
  "meta": {
853
+ "report_schema_version": REPORT_SCHEMA_VERSION,
722
854
  "date": datetime.now(UTC).isoformat(),
723
855
  "repo_path": str(repo_dir),
724
856
  "repo_name": repo_dir.name,