codecortex 0.25.0__tar.gz → 0.27.0__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 (180) hide show
  1. {codecortex-0.25.0/src/codecortex.egg-info → codecortex-0.27.0}/PKG-INFO +1 -1
  2. {codecortex-0.25.0 → codecortex-0.27.0/src/codecortex.egg-info}/PKG-INFO +1 -1
  3. {codecortex-0.25.0 → codecortex-0.27.0}/src/codecortex.egg-info/SOURCES.txt +6 -0
  4. codecortex-0.27.0/src/codeintel/__init__.py +1 -0
  5. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/changed_range.py +5 -1
  6. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/gateway.py +41 -2
  7. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/graph_answer.py +341 -46
  8. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/graph_changed.py +102 -12
  9. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/graph_confidence.py +118 -7
  10. codecortex-0.27.0/src/codeintel/graph_dispatch.py +1245 -0
  11. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/graph_edges.py +13 -1
  12. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/graph_ops.py +124 -47
  13. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/graph_render.py +5 -0
  14. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/provider.py +6 -4
  15. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/providers/graph.py +10 -0
  16. codecortex-0.27.0/src/codeintel/qualifier_scan.py +388 -0
  17. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/server.py +17 -6
  18. codecortex-0.27.0/tests/test_bench_graphify.py +277 -0
  19. codecortex-0.27.0/tests/test_bench_qualified.py +157 -0
  20. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_changed_range.py +175 -0
  21. codecortex-0.27.0/tests/test_dispatch_callers.py +1800 -0
  22. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_docs_trust_claims.py +100 -1
  23. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_edge_confidence.py +656 -2
  24. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_gateway.py +66 -0
  25. codecortex-0.27.0/tests/test_qualifier_scan.py +596 -0
  26. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_summary_integrity.py +30 -1
  27. codecortex-0.25.0/src/codeintel/__init__.py +0 -1
  28. {codecortex-0.25.0 → codecortex-0.27.0}/LICENSE +0 -0
  29. {codecortex-0.25.0 → codecortex-0.27.0}/README.md +0 -0
  30. {codecortex-0.25.0 → codecortex-0.27.0}/pyproject.toml +0 -0
  31. {codecortex-0.25.0 → codecortex-0.27.0}/setup.cfg +0 -0
  32. {codecortex-0.25.0 → codecortex-0.27.0}/src/codecortex.egg-info/dependency_links.txt +0 -0
  33. {codecortex-0.25.0 → codecortex-0.27.0}/src/codecortex.egg-info/entry_points.txt +0 -0
  34. {codecortex-0.25.0 → codecortex-0.27.0}/src/codecortex.egg-info/requires.txt +0 -0
  35. {codecortex-0.25.0 → codecortex-0.27.0}/src/codecortex.egg-info/top_level.txt +0 -0
  36. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/__main__.py +0 -0
  37. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/agent_prompt.py +0 -0
  38. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/auth.py +0 -0
  39. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/c4.py +0 -0
  40. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/c4_check.py +0 -0
  41. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/c4_layers.py +0 -0
  42. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/cache.py +0 -0
  43. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/__init__.py +0 -0
  44. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/_common.py +0 -0
  45. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/c4.py +0 -0
  46. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/doctor.py +0 -0
  47. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/gen_token.py +0 -0
  48. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/graph.py +0 -0
  49. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/index.py +0 -0
  50. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/install.py +0 -0
  51. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/map.py +0 -0
  52. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/prompt.py +0 -0
  53. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/query.py +0 -0
  54. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/reset.py +0 -0
  55. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/serve.py +0 -0
  56. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/serve_http.py +0 -0
  57. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/setup.py +0 -0
  58. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/status.py +0 -0
  59. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/commands/uninstall.py +0 -0
  60. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/config.py +0 -0
  61. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/containment.py +0 -0
  62. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/doctor.py +0 -0
  63. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/filelock.py +0 -0
  64. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/graph_backend.py +0 -0
  65. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/graph_resolution.py +0 -0
  66. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/graph_targets.py +0 -0
  67. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/grapher.py +0 -0
  68. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/http_server.py +0 -0
  69. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/indexer.py +0 -0
  70. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/injector.py +0 -0
  71. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/installer.py +0 -0
  72. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/lang_config.py +0 -0
  73. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/loc.py +0 -0
  74. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/logconfig.py +0 -0
  75. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/mapper.py +0 -0
  76. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/metrics.py +0 -0
  77. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/onboarding.py +0 -0
  78. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/outcome.py +0 -0
  79. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/paths.py +0 -0
  80. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/per_thread.py +0 -0
  81. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/policy.py +0 -0
  82. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/progress.py +0 -0
  83. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/providers/__init__.py +0 -0
  84. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/providers/lsp.py +0 -0
  85. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/providers/none.py +0 -0
  86. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/providers/semantic.py +0 -0
  87. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/py.typed +0 -0
  88. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/query_ops.py +0 -0
  89. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/redact.py +0 -0
  90. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/reindexer.py +0 -0
  91. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/reset.py +0 -0
  92. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/searcher.py +0 -0
  93. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/semantic_db.py +0 -0
  94. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/source_kind.py +0 -0
  95. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/symbol_diff.py +0 -0
  96. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/term.py +0 -0
  97. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/verify.py +0 -0
  98. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/viewer/__init__.py +0 -0
  99. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/viewer/graph_template.html +0 -0
  100. {codecortex-0.25.0 → codecortex-0.27.0}/src/codeintel/wire_text.py +0 -0
  101. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_agent_prompt.py +0 -0
  102. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_backend_failure_detail.py +0 -0
  103. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_backend_reaper.py +0 -0
  104. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_background_index_failure.py +0 -0
  105. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_bench_corpus_typed.py +0 -0
  106. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_bench_gate.py +0 -0
  107. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_bench_oracle.py +0 -0
  108. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_bench_oracle_ts.py +0 -0
  109. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_c4.py +0 -0
  110. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_c4_check.py +0 -0
  111. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_c4_layers.py +0 -0
  112. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_c4_model.py +0 -0
  113. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_c4_output.py +0 -0
  114. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_cache.py +0 -0
  115. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_chunking.py +0 -0
  116. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_cli_commands.py +0 -0
  117. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_cli_help.py +0 -0
  118. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_cli_install_dry_run.py +0 -0
  119. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_cli_install_offer_injection.py +0 -0
  120. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_cold_process.py +0 -0
  121. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_config.py +0 -0
  122. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_corpus.py +0 -0
  123. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_could_not_ask_semantic.py +0 -0
  124. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_docs_ci_claims.py +0 -0
  125. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_docs_deadcode_withdrawal.py +0 -0
  126. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_doctor.py +0 -0
  127. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_e2e.py +0 -0
  128. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_edge_endpoint_cap.py +0 -0
  129. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_engine_adoption.py +0 -0
  130. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_enterprise.py +0 -0
  131. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_graph_failure_population.py +0 -0
  132. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_graph_provider.py +0 -0
  133. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_graph_real.py +0 -0
  134. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_graph_stdin.py +0 -0
  135. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_grapher.py +0 -0
  136. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_hard_exit.py +0 -0
  137. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_hardening.py +0 -0
  138. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_http_auth.py +0 -0
  139. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_http_server.py +0 -0
  140. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_incompleteness.py +0 -0
  141. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_index_failure_reason.py +0 -0
  142. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_index_incompleteness.py +0 -0
  143. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_index_progress.py +0 -0
  144. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_indexer_binary.py +0 -0
  145. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_installer.py +0 -0
  146. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_integration.py +0 -0
  147. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_lang_config.py +0 -0
  148. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_loc_census.py +0 -0
  149. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_lsp_language_coverage.py +0 -0
  150. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_lsp_provider.py +0 -0
  151. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_lsp_real.py +0 -0
  152. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_mapper.py +0 -0
  153. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_mcp_handshake.py +0 -0
  154. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_mcp_server.py +0 -0
  155. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_missing_target.py +0 -0
  156. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_model_dimension.py +0 -0
  157. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_never_raise.py +0 -0
  158. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_null_reason_and_ripple.py +0 -0
  159. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_onboarding.py +0 -0
  160. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_per_thread.py +0 -0
  161. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_query_budget.py +0 -0
  162. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_rbac.py +0 -0
  163. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_redaction_boundary.py +0 -0
  164. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_reindex_lock.py +0 -0
  165. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_reindexer.py +0 -0
  166. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_rerank.py +0 -0
  167. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_reset.py +0 -0
  168. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_semantic_provider.py +0 -0
  169. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_source_kind.py +0 -0
  170. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_staleness.py +0 -0
  171. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_symbol_context.py +0 -0
  172. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_symbol_diff.py +0 -0
  173. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_term.py +0 -0
  174. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_treesitter.py +0 -0
  175. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_uninstall.py +0 -0
  176. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_upgrade_path.py +0 -0
  177. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_verify.py +0 -0
  178. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_verify_call.py +0 -0
  179. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_version_skew.py +0 -0
  180. {codecortex-0.25.0 → codecortex-0.27.0}/tests/test_wire_text.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecortex
3
- Version: 0.25.0
3
+ Version: 0.27.0
4
4
  Summary: Local-first, MCP-native code-intelligence server — graph, LSP, and semantic search behind one safe code.query tool for coding agents.
5
5
  Author: Shammai Hamilton
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecortex
3
- Version: 0.25.0
3
+ Version: 0.27.0
4
4
  Summary: Local-first, MCP-native code-intelligence server — graph, LSP, and semantic search behind one safe code.query tool for coding agents.
5
5
  Author: Shammai Hamilton
6
6
  License-Expression: MIT
@@ -25,6 +25,7 @@ src/codeintel/graph_answer.py
25
25
  src/codeintel/graph_backend.py
26
26
  src/codeintel/graph_changed.py
27
27
  src/codeintel/graph_confidence.py
28
+ src/codeintel/graph_dispatch.py
28
29
  src/codeintel/graph_edges.py
29
30
  src/codeintel/graph_ops.py
30
31
  src/codeintel/graph_render.py
@@ -48,6 +49,7 @@ src/codeintel/policy.py
48
49
  src/codeintel/progress.py
49
50
  src/codeintel/provider.py
50
51
  src/codeintel/py.typed
52
+ src/codeintel/qualifier_scan.py
51
53
  src/codeintel/query_ops.py
52
54
  src/codeintel/redact.py
53
55
  src/codeintel/reindexer.py
@@ -90,8 +92,10 @@ tests/test_backend_reaper.py
90
92
  tests/test_background_index_failure.py
91
93
  tests/test_bench_corpus_typed.py
92
94
  tests/test_bench_gate.py
95
+ tests/test_bench_graphify.py
93
96
  tests/test_bench_oracle.py
94
97
  tests/test_bench_oracle_ts.py
98
+ tests/test_bench_qualified.py
95
99
  tests/test_c4.py
96
100
  tests/test_c4_check.py
97
101
  tests/test_c4_layers.py
@@ -108,6 +112,7 @@ tests/test_cold_process.py
108
112
  tests/test_config.py
109
113
  tests/test_corpus.py
110
114
  tests/test_could_not_ask_semantic.py
115
+ tests/test_dispatch_callers.py
111
116
  tests/test_docs_ci_claims.py
112
117
  tests/test_docs_deadcode_withdrawal.py
113
118
  tests/test_docs_trust_claims.py
@@ -148,6 +153,7 @@ tests/test_never_raise.py
148
153
  tests/test_null_reason_and_ripple.py
149
154
  tests/test_onboarding.py
150
155
  tests/test_per_thread.py
156
+ tests/test_qualifier_scan.py
151
157
  tests/test_query_budget.py
152
158
  tests/test_rbac.py
153
159
  tests/test_redaction_boundary.py
@@ -0,0 +1 @@
1
+ __version__ = "0.27.0"
@@ -28,6 +28,7 @@ from __future__ import annotations
28
28
 
29
29
  import os
30
30
  import re
31
+ import stat
31
32
  import subprocess
32
33
  import tempfile
33
34
  import threading
@@ -323,7 +324,10 @@ def read_new(root: str, path: str) -> str | None:
323
324
  if safe is None:
324
325
  return None
325
326
  try:
326
- if os.path.getsize(safe) > MAX_SOURCE_BYTES:
327
+ info = os.stat(safe)
328
+ # A regular file only. A FIFO planted at a tracked path has one link and a size of zero, passes
329
+ # containment, and blocks `open` forever — which would hang the whole answer.
330
+ if not stat.S_ISREG(info.st_mode) or info.st_size > MAX_SOURCE_BYTES:
327
331
  return None
328
332
  with open(safe, "rb") as fh:
329
333
  return fh.read().decode("utf-8", "replace")
@@ -83,6 +83,41 @@ def _is_retryable(result: Result) -> bool:
83
83
  )
84
84
 
85
85
 
86
+ def _states_live_file_contents(result: Result | dict[str, Result]) -> bool:
87
+ """Whether the answer states something read from the files on disk as it was asked — the qualifier
88
+ scan's verdicts (`qualifier_seen` on a row, or the `qualifier_absent` / `qualifier_present`
89
+ counts).
90
+
91
+ The cache is keyed by the target string and the index's freshness generation, and neither moves
92
+ when a caller file starts or stops writing the qualifier — only a reindex does. Caching such an
93
+ answer would serve a verdict about the files as they WERE, in prose that says "as they are on
94
+ disk": the reason `changed` is never cached, met by a narrower population. An answer whose scan
95
+ judged nothing printed only the command — but if it was eligible and judged nothing because a
96
+ call stalled, the budget ran out or a lookup timed out (`qualifier_checked`), caching that would
97
+ keep the check switched off for the symbol until the next reindex, long after the filesystem
98
+ recovered. So an answer whose check was attempted at all is not cached either.
99
+ Walks the whole answer, because a fan-out answer nests each engine's envelope."""
100
+ def walk(node: object, depth: int) -> bool:
101
+ if depth > 8:
102
+ return False
103
+ if isinstance(node, dict):
104
+ evidence = node.get("evidence")
105
+ if isinstance(evidence, dict) and (evidence.get("qualifier_absent")
106
+ or evidence.get("qualifier_present")
107
+ or evidence.get("qualifier_checked")):
108
+ return True
109
+ rows = node.get("rows")
110
+ if isinstance(rows, list) and any(
111
+ isinstance(r, dict) and r.get("qualifier_seen") is not None for r in rows):
112
+ return True
113
+ return any(walk(v, depth + 1) for v in node.values() if isinstance(v, (dict, list)))
114
+ if isinstance(node, list):
115
+ return any(walk(v, depth + 1) for v in node)
116
+ return False
117
+
118
+ return walk(result, 0)
119
+
120
+
86
121
  class Gateway:
87
122
  def __init__(self, graph=None, lsp=None, semantic=None, policy: TieringPolicy | None = None,
88
123
  reindexer: Reindexer | None = None, oneshot: bool = False):
@@ -682,7 +717,10 @@ class Gateway:
682
717
  engines = ["graph", "lsp"] if engine_str == "both" else ["graph", "lsp", "semantic"]
683
718
  fan_results = self._fan_out(engines, op_str, target_str, budget, project_root)
684
719
  result = self._merge(fan_results, op_str, target_str, engine_str)
685
- if not uncacheable and not _is_retryable(result):
720
+ # The merged answer drops each engine's `rows` and `evidence` but keeps their prose,
721
+ # so whether it states what the files hold now is read from the engines' own answers.
722
+ if (not uncacheable and not _is_retryable(result)
723
+ and not _states_live_file_contents(fan_results)):
686
724
  self._cache.put(op_str, target_str, cache_engine, root_str, result, freshness)
687
725
  return _mark_reindexing(result, reindexing)
688
726
 
@@ -721,7 +759,8 @@ class Gateway:
721
759
  result = self._cross_check_name_resolved(
722
760
  result, op_str, target_str, budget, project_root, was_auto)
723
761
 
724
- if not uncacheable and not _is_retryable(result):
762
+ if (not uncacheable and not _is_retryable(result)
763
+ and not _states_live_file_contents(result)):
725
764
  self._cache.put(op_str, target_str, cache_engine, root_str, result, freshness)
726
765
  return _mark_reindexing(result, reindexing)
727
766