roam-code 12.2.0__tar.gz → 12.3.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 (537) hide show
  1. {roam_code-12.2.0/src/roam_code.egg-info → roam_code-12.3.0}/PKG-INFO +4 -2
  2. {roam_code-12.2.0 → roam_code-12.3.0}/pyproject.toml +23 -2
  3. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_critique.py +63 -0
  4. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_dead.py +16 -0
  5. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_diagnose.py +9 -0
  6. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_entry_points.py +7 -1
  7. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_fan.py +41 -2
  8. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_health.py +30 -6
  9. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_oracle.py +64 -22
  10. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_preflight.py +45 -0
  11. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_retrieve.py +89 -1
  12. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_rules.py +14 -1
  13. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_search.py +10 -1
  14. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_smells.py +77 -3
  15. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_taint.py +22 -1
  16. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_weather.py +12 -2
  17. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/metrics_history.py +16 -11
  18. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/resolve.py +58 -1
  19. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/pagerank.py +16 -6
  20. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/kotlin_lang.py +92 -1
  21. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/retrieve/pipeline.py +198 -6
  22. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/retrieve/rerank.py +197 -0
  23. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/retrieve/seeds.py +19 -13
  24. {roam_code-12.2.0 → roam_code-12.3.0/src/roam_code.egg-info}/PKG-INFO +4 -2
  25. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam_code.egg-info/SOURCES.txt +3 -0
  26. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam_code.egg-info/requires.txt +3 -1
  27. roam_code-12.3.0/tests/test_extractor_grammar_drift.py +171 -0
  28. roam_code-12.3.0/tests/test_fallback_contracts.py +225 -0
  29. roam_code-12.3.0/tests/test_retrieve_cross_repo.py +249 -0
  30. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_retrieve_seeds.py +12 -4
  31. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_surface_counts.py +2 -0
  32. {roam_code-12.2.0 → roam_code-12.3.0}/LICENSE +0 -0
  33. {roam_code-12.2.0 → roam_code-12.3.0}/README.md +0 -0
  34. {roam_code-12.2.0 → roam_code-12.3.0}/setup.cfg +0 -0
  35. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/__init__.py +0 -0
  36. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/__main__.py +0 -0
  37. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/analysis/__init__.py +0 -0
  38. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/analysis/effects.py +0 -0
  39. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/analysis/taint.py +0 -0
  40. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/api.py +0 -0
  41. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/ask/__init__.py +0 -0
  42. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/ask/classifier.py +0 -0
  43. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/ask/recipes.py +0 -0
  44. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/ask/runner.py +0 -0
  45. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/attest/__init__.py +0 -0
  46. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/attest/cga.py +0 -0
  47. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/bridges/__init__.py +0 -0
  48. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/bridges/base.py +0 -0
  49. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/bridges/bridge_config.py +0 -0
  50. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/bridges/bridge_django.py +0 -0
  51. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/bridges/bridge_protobuf.py +0 -0
  52. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/bridges/bridge_rest_api.py +0 -0
  53. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/bridges/bridge_salesforce.py +0 -0
  54. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/bridges/bridge_template.py +0 -0
  55. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/bridges/registry.py +0 -0
  56. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/catalog/__init__.py +0 -0
  57. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/catalog/detectors.py +0 -0
  58. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/catalog/fixes.py +0 -0
  59. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/catalog/smells.py +0 -0
  60. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/catalog/tasks.py +0 -0
  61. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/cli.py +0 -0
  62. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/__init__.py +0 -0
  63. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/changed_files.py +0 -0
  64. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_adrs.py +0 -0
  65. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_adversarial.py +0 -0
  66. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_affected.py +0 -0
  67. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_affected_tests.py +0 -0
  68. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_agent_context.py +0 -0
  69. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_agent_export.py +0 -0
  70. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_agent_plan.py +0 -0
  71. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_ai_ratio.py +0 -0
  72. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_ai_readiness.py +0 -0
  73. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_alerts.py +0 -0
  74. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_annotate.py +0 -0
  75. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_api_changes.py +0 -0
  76. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_api_drift.py +0 -0
  77. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_ask.py +0 -0
  78. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_attest.py +0 -0
  79. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_auth_gaps.py +0 -0
  80. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_bisect.py +0 -0
  81. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_breaking.py +0 -0
  82. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_budget.py +0 -0
  83. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_bus_factor.py +0 -0
  84. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_capsule.py +0 -0
  85. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_cga.py +0 -0
  86. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_check_rules.py +0 -0
  87. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_ci_setup.py +0 -0
  88. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_clean.py +0 -0
  89. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_clones.py +0 -0
  90. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_closure.py +0 -0
  91. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_clusters.py +0 -0
  92. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_codeowners.py +0 -0
  93. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_complexity.py +0 -0
  94. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_config.py +0 -0
  95. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_congestion.py +0 -0
  96. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_context.py +0 -0
  97. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_conventions.py +0 -0
  98. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_coupling.py +0 -0
  99. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_coverage_gaps.py +0 -0
  100. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_cut.py +0 -0
  101. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_dark_matter.py +0 -0
  102. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_dashboard.py +0 -0
  103. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_debt.py +0 -0
  104. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_deps.py +0 -0
  105. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_describe.py +0 -0
  106. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_dev_profile.py +0 -0
  107. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_diff.py +0 -0
  108. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_doc_staleness.py +0 -0
  109. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_docs_coverage.py +0 -0
  110. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_doctor.py +0 -0
  111. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_drift.py +0 -0
  112. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_duplicates.py +0 -0
  113. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_effects.py +0 -0
  114. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_endpoints.py +0 -0
  115. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_eval_retrieve.py +0 -0
  116. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_file.py +0 -0
  117. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_fingerprint.py +0 -0
  118. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_fitness.py +0 -0
  119. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_flag_dead.py +0 -0
  120. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_fleet.py +0 -0
  121. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_fn_coupling.py +0 -0
  122. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_forecast.py +0 -0
  123. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_grep.py +0 -0
  124. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_guard.py +0 -0
  125. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_hooks.py +0 -0
  126. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_hotspots.py +0 -0
  127. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_impact.py +0 -0
  128. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_index.py +0 -0
  129. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_index_bundle.py +0 -0
  130. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_ingest_trace.py +0 -0
  131. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_init.py +0 -0
  132. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_intent.py +0 -0
  133. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_invariants.py +0 -0
  134. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_layers.py +0 -0
  135. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_map.py +0 -0
  136. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_math.py +0 -0
  137. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_mcp_setup.py +0 -0
  138. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_metrics.py +0 -0
  139. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_migration_safety.py +0 -0
  140. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_minimap.py +0 -0
  141. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_missing_index.py +0 -0
  142. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_module.py +0 -0
  143. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_mutate.py +0 -0
  144. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_n1.py +0 -0
  145. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_orchestrate.py +0 -0
  146. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_orphan_routes.py +0 -0
  147. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_over_fetch.py +0 -0
  148. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_owner.py +0 -0
  149. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_partition.py +0 -0
  150. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_path_coverage.py +0 -0
  151. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_patterns.py +0 -0
  152. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_plan.py +0 -0
  153. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_plan_refactor.py +0 -0
  154. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_pr_diff.py +0 -0
  155. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_pr_risk.py +0 -0
  156. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_relate.py +0 -0
  157. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_report.py +0 -0
  158. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_reset.py +0 -0
  159. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_risk.py +0 -0
  160. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_safe_delete.py +0 -0
  161. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_safe_zones.py +0 -0
  162. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_sbom.py +0 -0
  163. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_schema.py +0 -0
  164. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_search_semantic.py +0 -0
  165. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_secrets.py +0 -0
  166. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_semantic_diff.py +0 -0
  167. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_simulate.py +0 -0
  168. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_simulate_departure.py +0 -0
  169. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_sketch.py +0 -0
  170. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_spectral.py +0 -0
  171. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_split.py +0 -0
  172. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_suggest_refactoring.py +0 -0
  173. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_suggest_reviewers.py +0 -0
  174. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_supply_chain.py +0 -0
  175. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_symbol.py +0 -0
  176. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_syntax_check.py +0 -0
  177. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_test_gaps.py +0 -0
  178. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_test_scaffold.py +0 -0
  179. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_testmap.py +0 -0
  180. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_tour.py +0 -0
  181. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_trace.py +0 -0
  182. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_trends.py +0 -0
  183. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_triage.py +0 -0
  184. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_understand.py +0 -0
  185. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_uses.py +0 -0
  186. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_verify.py +0 -0
  187. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_verify_imports.py +0 -0
  188. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_vibe_check.py +0 -0
  189. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_visualize.py +0 -0
  190. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_vuln_map.py +0 -0
  191. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_vuln_reach.py +0 -0
  192. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_vulns.py +0 -0
  193. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_watch.py +0 -0
  194. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_why.py +0 -0
  195. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_ws.py +0 -0
  196. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/cmd_xlang.py +0 -0
  197. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/codeowners_helpers.py +0 -0
  198. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/context_helpers.py +0 -0
  199. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/gate_presets.py +0 -0
  200. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/graph_helpers.py +0 -0
  201. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/next_steps.py +0 -0
  202. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/commands/suppression.py +0 -0
  203. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/competitor_site_data.py +0 -0
  204. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/config.py +0 -0
  205. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/coverage_reports.py +0 -0
  206. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/critique/__init__.py +0 -0
  207. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/critique/aggregator.py +0 -0
  208. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/critique/checks.py +0 -0
  209. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/db/__init__.py +0 -0
  210. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/db/connection.py +0 -0
  211. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/db/queries.py +0 -0
  212. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/db/schema.py +0 -0
  213. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/eval/__init__.py +0 -0
  214. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/eval/harness.py +0 -0
  215. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/exit_codes.py +0 -0
  216. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/fleet/__init__.py +0 -0
  217. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/fleet/adapters.py +0 -0
  218. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/fleet/manifest.py +0 -0
  219. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/git_utils.py +0 -0
  220. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/__init__.py +0 -0
  221. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/anomaly.py +0 -0
  222. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/builder.py +0 -0
  223. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/clone_detect.py +0 -0
  224. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/clusters.py +0 -0
  225. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/cycles.py +0 -0
  226. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/dark_matter.py +0 -0
  227. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/diff.py +0 -0
  228. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/fingerprint.py +0 -0
  229. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/layers.py +0 -0
  230. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/partition.py +0 -0
  231. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/pathfinding.py +0 -0
  232. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/propagation.py +0 -0
  233. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/simulate.py +0 -0
  234. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/spectral.py +0 -0
  235. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/graph/stats.py +0 -0
  236. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/index/__init__.py +0 -0
  237. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/index/complexity.py +0 -0
  238. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/index/discovery.py +0 -0
  239. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/index/django_post.py +0 -0
  240. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/index/file_roles.py +0 -0
  241. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/index/git_stats.py +0 -0
  242. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/index/gitignore.py +0 -0
  243. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/index/incremental.py +0 -0
  244. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/index/indexer.py +0 -0
  245. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/index/parser.py +0 -0
  246. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/index/relations.py +0 -0
  247. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/index/symbols.py +0 -0
  248. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/index/test_conventions.py +0 -0
  249. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/__init__.py +0 -0
  250. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/apex_lang.py +0 -0
  251. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/aura_lang.py +0 -0
  252. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/base.py +0 -0
  253. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/c_lang.py +0 -0
  254. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/csharp_lang.py +0 -0
  255. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/extractor_schema.py +0 -0
  256. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/foxpro_lang.py +0 -0
  257. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/generic_lang.py +0 -0
  258. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/go_lang.py +0 -0
  259. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/hcl_lang.py +0 -0
  260. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/java_lang.py +0 -0
  261. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/javascript_lang.py +0 -0
  262. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/php_lang.py +0 -0
  263. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/python_lang.py +0 -0
  264. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/query_engine.py +0 -0
  265. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/registry.py +0 -0
  266. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/ruby_lang.py +0 -0
  267. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/rust_lang.py +0 -0
  268. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/scala_lang.py +0 -0
  269. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/sfxml_lang.py +0 -0
  270. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/sql_lang.py +0 -0
  271. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/swift_lang.py +0 -0
  272. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/typescript_lang.py +0 -0
  273. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/visualforce_lang.py +0 -0
  274. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/languages/yaml_lang.py +0 -0
  275. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/mcp_extras/__init__.py +0 -0
  276. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/mcp_extras/completions.py +0 -0
  277. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/mcp_extras/progress.py +0 -0
  278. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/mcp_extras/sampling.py +0 -0
  279. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/mcp_extras/session.py +0 -0
  280. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/mcp_extras/watcher.py +0 -0
  281. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/mcp_server.py +0 -0
  282. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/output/__init__.py +0 -0
  283. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/output/formatter.py +0 -0
  284. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/output/mermaid.py +0 -0
  285. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/output/sarif.py +0 -0
  286. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/output/schema_registry.py +0 -0
  287. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/plugins.py +0 -0
  288. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/refactor/__init__.py +0 -0
  289. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/refactor/codegen.py +0 -0
  290. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/refactor/transforms.py +0 -0
  291. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/retrieve/__init__.py +0 -0
  292. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/retrieve/learned_ranker.py +0 -0
  293. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/retrieve/semantic.py +0 -0
  294. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/rules/__init__.py +0 -0
  295. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/rules/ast_match.py +0 -0
  296. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/rules/builtin.py +0 -0
  297. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/rules/dataflow.py +0 -0
  298. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/rules/engine.py +0 -0
  299. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/runtime/__init__.py +0 -0
  300. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/runtime/daemon.py +0 -0
  301. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/runtime/graph_backend.py +0 -0
  302. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/runtime/hotspots.py +0 -0
  303. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/runtime/lock_modes.py +0 -0
  304. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/runtime/lockmgr.py +0 -0
  305. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/runtime/trace_ingest.py +0 -0
  306. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/search/__init__.py +0 -0
  307. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/search/framework_packs.py +0 -0
  308. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/search/index_embeddings.py +0 -0
  309. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/search/onnx_embeddings.py +0 -0
  310. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/search/tfidf.py +0 -0
  311. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/security/__init__.py +0 -0
  312. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/security/aibom_extension.py +0 -0
  313. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/security/taint_classifier.py +0 -0
  314. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/security/taint_engine.py +0 -0
  315. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/security/vuln_reach.py +0 -0
  316. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/security/vuln_store.py +0 -0
  317. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/surface_counts.py +0 -0
  318. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/templates/__init__.py +0 -0
  319. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/templates/ci/__init__.py +0 -0
  320. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/workspace/__init__.py +0 -0
  321. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/workspace/aggregator.py +0 -0
  322. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/workspace/api_scanner.py +0 -0
  323. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/workspace/config.py +0 -0
  324. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam/workspace/db.py +0 -0
  325. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam_code.egg-info/dependency_links.txt +0 -0
  326. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam_code.egg-info/entry_points.txt +0 -0
  327. {roam_code-12.2.0 → roam_code-12.3.0}/src/roam_code.egg-info/top_level.txt +0 -0
  328. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_adrs.py +0 -0
  329. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_adversarial.py +0 -0
  330. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_affected.py +0 -0
  331. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_agent_export.py +0 -0
  332. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_agent_mode.py +0 -0
  333. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_agent_plan_context.py +0 -0
  334. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_ai_ratio.py +0 -0
  335. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_ai_readiness.py +0 -0
  336. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_alerts_cmd.py +0 -0
  337. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_annotations.py +0 -0
  338. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_anomaly.py +0 -0
  339. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_api_changes.py +0 -0
  340. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_api_drift.py +0 -0
  341. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_ask.py +0 -0
  342. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_attest.py +0 -0
  343. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_auth_gaps.py +0 -0
  344. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_backend_fixes_round2.py +0 -0
  345. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_backend_fixes_round3.py +0 -0
  346. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_basic.py +0 -0
  347. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_batch_mcp.py +0 -0
  348. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_bisect.py +0 -0
  349. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_bridge_django.py +0 -0
  350. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_bridges.py +0 -0
  351. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_bridges_extended.py +0 -0
  352. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_budget.py +0 -0
  353. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_budget_flag.py +0 -0
  354. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_budget_phase2.py +0 -0
  355. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_bus_factor.py +0 -0
  356. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_capsule.py +0 -0
  357. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_cga.py +0 -0
  358. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_check_rules.py +0 -0
  359. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_ci_gate_eval.py +0 -0
  360. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_ci_sarif_guard.py +0 -0
  361. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_ci_setup.py +0 -0
  362. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_clones.py +0 -0
  363. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_closure.py +0 -0
  364. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_codeowners.py +0 -0
  365. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_commands_architecture.py +0 -0
  366. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_commands_exploration.py +0 -0
  367. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_commands_health.py +0 -0
  368. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_commands_refactoring.py +0 -0
  369. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_commands_workflow.py +0 -0
  370. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_competitor_site_data.py +0 -0
  371. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_comprehensive.py +0 -0
  372. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_config.py +0 -0
  373. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_congestion.py +0 -0
  374. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_context_propagation.py +0 -0
  375. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_conventions_cmd.py +0 -0
  376. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_coverage_gaps_cmd.py +0 -0
  377. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_coverage_ingestion.py +0 -0
  378. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_critique.py +0 -0
  379. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_cut.py +0 -0
  380. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_dark_matter.py +0 -0
  381. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_dark_matter_helpers.py +0 -0
  382. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_dashboard.py +0 -0
  383. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_dataflow_dead.py +0 -0
  384. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_dead_aging.py +0 -0
  385. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_defer_loading.py +0 -0
  386. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_demo_gif_asset.py +0 -0
  387. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_describe.py +0 -0
  388. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_detail_flag_hints.py +0 -0
  389. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_deterministic_output.py +0 -0
  390. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_dev_profile.py +0 -0
  391. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_difficulty_scoring.py +0 -0
  392. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_doc_staleness.py +0 -0
  393. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_docker_assets.py +0 -0
  394. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_docs_coverage.py +0 -0
  395. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_docs_site_quality.py +0 -0
  396. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_doctor.py +0 -0
  397. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_drift.py +0 -0
  398. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_duplicates.py +0 -0
  399. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_effects.py +0 -0
  400. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_effects_propagation.py +0 -0
  401. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_endpoints.py +0 -0
  402. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_entry_points_cmd.py +0 -0
  403. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_eval_retrieve.py +0 -0
  404. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_exclude_patterns.py +0 -0
  405. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_exit_codes.py +0 -0
  406. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_file_roles.py +0 -0
  407. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_fingerprint.py +0 -0
  408. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_fixes.py +0 -0
  409. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_flag_dead.py +0 -0
  410. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_fleet.py +0 -0
  411. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_fn_coupling.py +0 -0
  412. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_forecast.py +0 -0
  413. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_formatters.py +0 -0
  414. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_foxpro.py +0 -0
  415. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_framework_detection.py +0 -0
  416. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_gate_presets.py +0 -0
  417. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_git_utils.py +0 -0
  418. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_guard.py +0 -0
  419. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_health_gate.py +0 -0
  420. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_hooks.py +0 -0
  421. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_hotspots.py +0 -0
  422. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_index.py +0 -0
  423. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_index_bundle.py +0 -0
  424. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_init_cmd.py +0 -0
  425. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_install_check.py +0 -0
  426. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_intent.py +0 -0
  427. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_invariants.py +0 -0
  428. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_json_contracts.py +0 -0
  429. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_json_error_envelope.py +0 -0
  430. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_kotlin_swift_extractors.py +0 -0
  431. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_language_corpus.py +0 -0
  432. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_languages.py +0 -0
  433. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_library_api.py +0 -0
  434. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_math.py +0 -0
  435. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_math_tips.py +0 -0
  436. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_mcp_extras.py +0 -0
  437. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_mcp_server.py +0 -0
  438. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_mcp_setup.py +0 -0
  439. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_mermaid.py +0 -0
  440. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_metrics_cmd.py +0 -0
  441. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_migration_safety.py +0 -0
  442. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_minimap.py +0 -0
  443. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_missing_index.py +0 -0
  444. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_mutate.py +0 -0
  445. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_n1.py +0 -0
  446. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_next_steps.py +0 -0
  447. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_onboard.py +0 -0
  448. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_oracle.py +0 -0
  449. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_orchestrate.py +0 -0
  450. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_orphan_routes.py +0 -0
  451. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_oss_bench_harness.py +0 -0
  452. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_over_fetch.py +0 -0
  453. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_pagerank_truncation.py +0 -0
  454. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_partition.py +0 -0
  455. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_path_coverage.py +0 -0
  456. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_patterns_cmd.py +0 -0
  457. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_performance.py +0 -0
  458. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_personalized_pagerank.py +0 -0
  459. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_plan.py +0 -0
  460. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_plugin_discovery.py +0 -0
  461. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_pr_comment_script.py +0 -0
  462. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_pr_diff.py +0 -0
  463. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_pr_risk_author.py +0 -0
  464. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_progress.py +0 -0
  465. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_progressive_disclosure.py +0 -0
  466. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_properties.py +0 -0
  467. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_python_extractor_v2.py +0 -0
  468. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_readme_surface_consistency.py +0 -0
  469. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_refactoring_intelligence.py +0 -0
  470. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_relate.py +0 -0
  471. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_report.py +0 -0
  472. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_reset_clean.py +0 -0
  473. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_resolve.py +0 -0
  474. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_retrieve.py +0 -0
  475. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_risk.py +0 -0
  476. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_ruby.py +0 -0
  477. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_rule_profiles.py +0 -0
  478. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_rules.py +0 -0
  479. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_rules_ast_match.py +0 -0
  480. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_rules_community_pack.py +0 -0
  481. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_rules_dataflow.py +0 -0
  482. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_rules_symbol_requirements.py +0 -0
  483. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_runtime.py +0 -0
  484. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_runtime_score.py +0 -0
  485. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_salesforce.py +0 -0
  486. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_sarif_flag.py +0 -0
  487. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_sbom.py +0 -0
  488. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_scala.py +0 -0
  489. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_schema_versioning.py +0 -0
  490. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_search_explain.py +0 -0
  491. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_secrets.py +0 -0
  492. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_secrets_v2.py +0 -0
  493. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_semantic_diff.py +0 -0
  494. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_semantic_onnx.py +0 -0
  495. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_semantic_search.py +0 -0
  496. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_simulate.py +0 -0
  497. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_simulate_departure.py +0 -0
  498. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_sketch.py +0 -0
  499. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_smells.py +0 -0
  500. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_smoke.py +0 -0
  501. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_sna_metrics.py +0 -0
  502. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_spectral.py +0 -0
  503. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_split_cmd.py +0 -0
  504. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_sql.py +0 -0
  505. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_suggest_reviewers.py +0 -0
  506. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_supply_chain.py +0 -0
  507. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_syntax_check.py +0 -0
  508. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_taint.py +0 -0
  509. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_taint_analysis.py +0 -0
  510. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_taint_classifier.py +0 -0
  511. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_test_conventions.py +0 -0
  512. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_test_gaps.py +0 -0
  513. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_test_scaffold.py +0 -0
  514. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_testmap.py +0 -0
  515. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_top_flag_consistency.py +0 -0
  516. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_tour_cmd.py +0 -0
  517. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_trends.py +0 -0
  518. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_trends_cohort.py +0 -0
  519. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_triage.py +0 -0
  520. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_uses_cmd.py +0 -0
  521. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_v12_2.py +0 -0
  522. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_v6_features.py +0 -0
  523. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_v71_features.py +0 -0
  524. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_v7_features.py +0 -0
  525. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_v82_features.py +0 -0
  526. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_verify.py +0 -0
  527. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_verify_imports.py +0 -0
  528. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_vibe_check.py +0 -0
  529. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_visualize.py +0 -0
  530. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_vuln.py +0 -0
  531. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_vulns_cmd.py +0 -0
  532. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_watch.py +0 -0
  533. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_why.py +0 -0
  534. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_workspace.py +0 -0
  535. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_ws_resolve_fixes.py +0 -0
  536. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_xlang.py +0 -0
  537. {roam_code-12.2.0 → roam_code-12.3.0}/tests/test_yaml_hcl.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: roam-code
3
- Version: 12.2.0
3
+ Version: 12.3.0
4
4
  Summary: Instant codebase comprehension for AI coding agents
5
5
  Author: CosmoHac
6
6
  License-Expression: MIT
@@ -29,7 +29,7 @@ Description-Content-Type: text/markdown
29
29
  License-File: LICENSE
30
30
  Requires-Dist: click>=8.0
31
31
  Requires-Dist: tree-sitter>=0.23
32
- Requires-Dist: tree-sitter-language-pack>=0.6
32
+ Requires-Dist: tree-sitter-language-pack<1.6.3,>=0.6
33
33
  Requires-Dist: networkx>=3.0
34
34
  Provides-Extra: mcp
35
35
  Requires-Dist: fastmcp>=2.0; extra == "mcp"
@@ -49,6 +49,8 @@ Requires-Dist: lightgbm>=4.0; extra == "learned"
49
49
  Provides-Extra: dev
50
50
  Requires-Dist: pytest>=7.0; extra == "dev"
51
51
  Requires-Dist: pytest-xdist>=3.0; extra == "dev"
52
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
53
+ Requires-Dist: scipy>=1.11; extra == "dev"
52
54
  Requires-Dist: ruff>=0.4; extra == "dev"
53
55
  Requires-Dist: build>=1.0; extra == "dev"
54
56
  Requires-Dist: twine>=5.0; extra == "dev"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "roam-code"
7
- version = "12.2.0"
7
+ version = "12.3.0"
8
8
  description = "Instant codebase comprehension for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -32,7 +32,11 @@ classifiers = [
32
32
  dependencies = [
33
33
  "click>=8.0",
34
34
  "tree-sitter>=0.23",
35
- "tree-sitter-language-pack>=0.6",
35
+ # Pinned upper bound: 1.6.3's cp312 wheel ships a manylinux_2_34
36
+ # tarball that fails to register the ``tree_sitter_language_pack``
37
+ # module post-install (CI run 25217131296). 1.6.2 (cp310-abi3) works
38
+ # cleanly across 3.9–3.13.
39
+ "tree-sitter-language-pack>=0.6,<1.6.3",
36
40
  "networkx>=3.0",
37
41
  ]
38
42
 
@@ -81,6 +85,16 @@ learned = [
81
85
  dev = [
82
86
  "pytest>=7.0",
83
87
  "pytest-xdist>=3.0",
88
+ # pytest-asyncio is required by tests/test_taint_classifier.py — those
89
+ # tests use ``@pytest.mark.asyncio`` to verify the MCP-sampling-based
90
+ # taint classifier under a controlled async loop.
91
+ "pytest-asyncio>=0.23",
92
+ # scipy is required by ``nx.pagerank`` on networkx 3.x; without it,
93
+ # the personalized-pagerank tests exercise the degree-based fallback
94
+ # which doesn't honour ``alpha`` and trivially fails the
95
+ # alpha-override / chain-propagation invariants. Production users
96
+ # without scipy still get the (less accurate but correct) fallback.
97
+ "scipy>=1.11",
84
98
  "ruff>=0.4",
85
99
  "build>=1.0",
86
100
  "twine>=5.0",
@@ -92,10 +106,17 @@ where = ["src"]
92
106
  [tool.pytest.ini_options]
93
107
  testpaths = ["tests"]
94
108
  addopts = "-q --tb=short"
109
+ # v12.2: pytest-asyncio strict-mode + auto fixture loop scope. Avoids the
110
+ # 11 PytestUnknownMarkWarning warnings on `@pytest.mark.asyncio` in
111
+ # test_taint_classifier.py when pytest-asyncio is installed (it ships
112
+ # under the `[dev]` extra).
113
+ asyncio_mode = "strict"
114
+ asyncio_default_fixture_loop_scope = "function"
95
115
  markers = [
96
116
  "smoke: fast, high-signal tests for local iteration",
97
117
  "core: broader local suite for pre-push checks",
98
118
  "slow: marks tests as slow (deselect with '-m \"not slow\"')",
119
+ "asyncio: marks async tests requiring pytest-asyncio (skip when missing)",
99
120
  ]
100
121
 
101
122
  [tool.ruff]
@@ -31,6 +31,57 @@ from roam.critique.checks import (
31
31
  from roam.db.connection import open_db
32
32
  from roam.output.formatter import json_envelope, to_json
33
33
 
34
+ # Hot-path → bench command. When a diff touches any of these path
35
+ # prefixes, the default critique rules can pass while the change
36
+ # materially alters retrieval/scoring/graph algorithms. The hint
37
+ # names the bench so the user includes it in their verification
38
+ # loop. Order matters: first match wins (most specific first).
39
+ _BENCH_RELEVANCE_RULES = [
40
+ (
41
+ ("src/roam/retrieve/", "src/roam/eval/"),
42
+ "pytest tests/test_retrieve_cross_repo.py + roam eval-retrieve --tasks bench/retrieve/roam_self.jsonl",
43
+ ),
44
+ (
45
+ ("src/roam/graph/pagerank.py", "src/roam/graph/clusters.py"),
46
+ "pytest tests/test_personalized_pagerank.py tests/test_fallback_contracts.py",
47
+ ),
48
+ (("src/roam/graph/",), "pytest tests/ -k graph_ -m 'not slow'"),
49
+ (
50
+ ("src/roam/languages/", "src/roam/index/parser.py"),
51
+ "pytest tests/test_languages.py tests/test_extractor_grammar_drift.py",
52
+ ),
53
+ (("src/roam/security/taint",), "pytest tests/test_taint_analysis.py tests/test_taint_classifier.py"),
54
+ (("src/roam/critique/",), "pytest tests/test_critique.py"),
55
+ (
56
+ ("src/roam/commands/cmd_oracle.py", "src/roam/commands/cmd_health.py"),
57
+ "pytest tests/test_oracle.py tests/test_commands_health.py",
58
+ ),
59
+ ]
60
+
61
+
62
+ def _bench_relevance_hint(regions) -> str:
63
+ """Return a one-line bench/test suggestion when the diff touches a
64
+ structurally-significant path. ``regions`` is the
65
+ ``critique.checks.ChangedRegion`` list from the diff parser; we
66
+ look at each region's file path and pick the first matching rule.
67
+ """
68
+ paths = []
69
+ for r in regions:
70
+ path = getattr(r, "file_path", None) or getattr(r, "file", None) or ""
71
+ if path:
72
+ paths.append(path.replace("\\", "/"))
73
+ if not paths:
74
+ return ""
75
+ seen: set[str] = set()
76
+ for path in paths:
77
+ for prefixes, hint in _BENCH_RELEVANCE_RULES:
78
+ if any(path.startswith(p) or p in path for p in prefixes):
79
+ if hint in seen:
80
+ return hint
81
+ seen.add(hint)
82
+ return hint
83
+ return ""
84
+
34
85
 
35
86
  @click.command()
36
87
  @click.option(
@@ -163,5 +214,17 @@ def critique(ctx, input_path, high_callers, intent_text):
163
214
  click.echo(f" {line}")
164
215
  click.echo()
165
216
 
217
+ # Bench-relevance hint (dogfood notes 2026-05-01): when the diff
218
+ # touches files in the retrieve / graph / catalog hot path, the
219
+ # default rule set ("clones not edited", "blast radius") can
220
+ # legitimately say "no concerns" while the change quietly
221
+ # alters the structural-rerank scoring formula. Surfacing the
222
+ # bench command makes the verifier conversation include the
223
+ # one validation that actually exercises the modified code.
224
+ bench_hint = _bench_relevance_hint(regions)
225
+ if bench_hint:
226
+ click.echo()
227
+ click.echo(f"BENCH HINT: {bench_hint}")
228
+
166
229
  if result["severity_breakdown"].get("high", 0) > 0:
167
230
  ctx.exit(5)
@@ -358,6 +358,19 @@ def _group_dead(dead_items, by):
358
358
  # ---------------------------------------------------------------------------
359
359
 
360
360
 
361
+ _TOOLING_PATH_HINTS = ("/dev/", "/benchmarks/", "/.github/", "\\dev\\", "\\benchmarks\\", "\\.github\\")
362
+
363
+
364
+ def _is_tooling_path(path: str) -> bool:
365
+ """Same hint set as cmd_fan / cmd_smells. Excluded from dead-code
366
+ headline by default (dogfood notes 2026-05-01) — CI scripts are
367
+ expected to have unreferenced helper functions."""
368
+ if not path:
369
+ return False
370
+ p = "/" + path.replace("\\", "/")
371
+ return any(hint.replace("\\", "/") in p for hint in _TOOLING_PATH_HINTS)
372
+
373
+
361
374
  def _analyze_dead(conn):
362
375
  """Run the full dead code analysis.
363
376
 
@@ -366,6 +379,9 @@ def _analyze_dead(conn):
366
379
  rows = conn.execute(UNREFERENCED_EXPORTS).fetchall()
367
380
  # Exclude test files — their symbols are discovered by pytest, not imported
368
381
  rows = [r for r in rows if not _is_test_path(r["file_path"])]
382
+ # Exclude tooling/CI/benchmarks/dev — same default-exclusion that
383
+ # ``cmd_smells`` and ``cmd_fan`` apply.
384
+ rows = [r for r in rows if not _is_tooling_path(r["file_path"])]
369
385
  if not rows:
370
386
  return [], [], set()
371
387
 
@@ -315,6 +315,15 @@ def diagnose(ctx, name, depth):
315
315
 
316
316
  # Text output
317
317
  click.echo(f"VERDICT: {verdict}")
318
+ # Index-staleness hint (dogfood notes 2026-05-01): when index
319
+ # is older than HEAD, commit_count / churn columns are
320
+ # unreliable — surface this so the user knows whether to trust
321
+ # the numbers or re-index.
322
+ from roam.commands.resolve import index_staleness_hint as _stale_hint
323
+
324
+ _stale = _stale_hint()
325
+ if _stale:
326
+ click.echo(f"NOTE: {_stale}")
318
327
  sym_name = sym["qualified_name"] or sym["name"]
319
328
  click.echo(f"Diagnose: {sym_name}")
320
329
  click.echo(f" {loc(target_metrics['file_path'], sym['line_start'])}")
@@ -103,7 +103,13 @@ _NAME_PATTERNS = [
103
103
  ("Scheduled", re.compile(r"cron|schedule|periodic|tick", re.IGNORECASE)),
104
104
  ("Message", re.compile(r"consume|subscriber|on_message|process_message", re.IGNORECASE)),
105
105
  ("CLI", re.compile(r"^cmd_|_command$|_cmd$", re.IGNORECASE)),
106
- ("HTTP", re.compile(r"_view$|_endpoint$|_controller$|_handler$", re.IGNORECASE)),
106
+ # HTTP name patterns: only the strong endpoint/controller suffixes.
107
+ # ``_view$`` and ``_handler$`` matched too broadly — dogfood notes
108
+ # 2026-05-01 caught ``_extract_create_view`` (a SQL parser method)
109
+ # and ``error_handler`` (signal handler) being classified as HTTP
110
+ # routes. The decorator pattern above already catches genuine
111
+ # ``@app.route`` etc. — name fallback can be conservative.
112
+ ("HTTP", re.compile(r"_endpoint$|_controller$", re.IGNORECASE)),
107
113
  ]
108
114
 
109
115
 
@@ -107,12 +107,43 @@ _FRAMEWORK_NAMES = frozenset(
107
107
  )
108
108
 
109
109
 
110
+ _TOOLING_PATH_HINTS = ("/dev/", "/benchmarks/", "/.github/", "\\dev\\", "\\benchmarks\\", "\\.github\\")
111
+
112
+
113
+ def _is_tooling_path(path: str) -> bool:
114
+ """Return True for paths that belong to tooling/CI/benchmarks/dev.
115
+
116
+ Default-excluded from headline metrics so first-time users aren't
117
+ dominated by ``pr-comment.js`` and ``roam-bench.py`` rows.
118
+ Matches the hint set used by ``cmd_smells._file_role_lookup``.
119
+ """
120
+ if not path:
121
+ return False
122
+ p = "/" + path.replace("\\", "/")
123
+ return any(hint.replace("\\", "/") in p for hint in _TOOLING_PATH_HINTS)
124
+
125
+
126
+ def _filter_tooling_rows(rows):
127
+ """Filter out rows whose ``file_path`` is in a tooling location."""
128
+ return [r for r in rows if not _is_tooling_path(r["file_path"] or "")]
129
+
130
+
110
131
  @click.command()
111
132
  @click.argument("mode", default="symbol", type=click.Choice(["symbol", "file"]))
112
133
  @click.option("-n", "count", default=20, help="Number of items to show")
113
134
  @click.option("--no-framework", is_flag=True, help="Filter out framework/boilerplate symbols")
135
+ @click.option(
136
+ "--include-tooling",
137
+ is_flag=True,
138
+ default=False,
139
+ help=(
140
+ "Include CI scripts, dev tooling, build, and generated files. "
141
+ "Excluded by default — high fan-in in dev/.github/benchmarks "
142
+ "is expected and dominates the headline (dogfood notes 2026-05-01)."
143
+ ),
144
+ )
114
145
  @click.pass_context
115
- def fan(ctx, mode, count, no_framework):
146
+ def fan(ctx, mode, count, no_framework, include_tooling):
116
147
  """Show fan-in/fan-out: most connected symbols or files.
117
148
 
118
149
  Unlike ``coupling`` (which measures temporal co-change frequency), this
@@ -124,6 +155,10 @@ def fan(ctx, mode, count, no_framework):
124
155
  ensure_index()
125
156
 
126
157
  with open_db(readonly=True) as conn:
158
+ # Pull more rows than ``count`` when filtering, so the displayed
159
+ # top-N still has ``count`` entries after exclusions. 5x is a
160
+ # comfortable safety factor for typical tooling shares.
161
+ fetch_limit = count * 5 if not include_tooling else count
127
162
  if mode == "symbol":
128
163
  rows = conn.execute(
129
164
  """
@@ -138,9 +173,13 @@ def fan(ctx, mode, count, no_framework):
138
173
  ORDER BY total DESC
139
174
  LIMIT ?
140
175
  """,
141
- (count,),
176
+ (fetch_limit,),
142
177
  ).fetchall()
143
178
 
179
+ if not include_tooling:
180
+ rows = _filter_tooling_rows(rows)
181
+ rows = rows[:count]
182
+
144
183
  if no_framework:
145
184
  rows = [r for r in rows if r["name"] not in _FRAMEWORK_NAMES]
146
185
 
@@ -473,18 +473,34 @@ def health(ctx, no_framework, gate):
473
473
  """Sigmoid health factor: 1 for no issues, → 0 for many."""
474
474
  return math.exp(-value / scale) if scale > 0 else 1.0
475
475
 
476
- god_critical = sum(1 for g in god_items if g.get("severity") == "CRITICAL")
477
- god_signal = god_critical * 3 + len(god_items) * 0.5
478
- bn_critical = sum(1 for b in bn_items if b.get("severity") == "CRITICAL")
479
- bn_signal = bn_critical * 2 + len(bn_items) * 0.3
476
+ # Score signals count *actionable* items only. Utilities
477
+ # (string/path/datetime helpers) are expected to have high fan-in
478
+ # and would dominate the formula otherwise. Per dogfood notes
479
+ # 2026-05-01: this repo had 50 god components total but 27 were
480
+ # expected utilities; the old formula penalised the score for
481
+ # all 50 and produced a misleading 2/100 verdict. The display
482
+ # already classifies them ("23 actionable, 27 expected utilities");
483
+ # the score should match the display.
484
+ god_actionable = [g for g in god_items if g.get("category") == "actionable"]
485
+ god_critical = sum(1 for g in god_actionable if g.get("severity") == "CRITICAL")
486
+ # Normalise by codebase size so a 14k-symbol repo with 23 actionable
487
+ # god components (0.16%) doesn't score the same as a 100-symbol
488
+ # repo with 23 (23%). 1k symbols is the unit; signal scales linearly.
489
+ size_norm = max(1.0, total_symbols / 1000.0)
490
+ god_signal = (god_critical * 3 + len(god_actionable) * 0.5) / size_norm
491
+ bn_actionable_items = [b for b in bn_items if b.get("category") == "actionable"]
492
+ bn_critical = sum(1 for b in bn_actionable_items if b.get("severity") == "CRITICAL")
493
+ bn_signal = (bn_critical * 2 + len(bn_actionable_items) * 0.3) / size_norm
480
494
 
481
495
  coverage_import = imported_coverage_overview(conn)
482
496
 
483
497
  # Base factors (weights sum to 1.0 before optional imported coverage).
498
+ # Scales tuned post-normalisation so a normal repo (low percent of
499
+ # actionable god components) scores in the 60-90 range.
484
500
  base_factors = [
485
501
  (_health_factor(tangle_ratio, 10), 0.30), # tangle ratio
486
- (_health_factor(god_signal, 5), 0.20), # god components
487
- (_health_factor(bn_signal, 4), 0.15), # bottlenecks
502
+ (_health_factor(god_signal, 1.5), 0.20), # god components (normalised /1k symbols)
503
+ (_health_factor(bn_signal, 1.0), 0.15), # bottlenecks (normalised /1k symbols)
488
504
  (_health_factor(len(violations), 5), 0.15), # layer violations
489
505
  ]
490
506
  # File-level health: map avg [0-10] to a factor
@@ -756,6 +772,14 @@ def health(ctx, no_framework, gate):
756
772
 
757
773
  # --- Text output ---
758
774
  click.echo(f"VERDICT: {verdict}\n")
775
+ # Index-staleness hint — only when relevant. Surfaces here
776
+ # because git_file_changes feed health's tangle/coupling/churn
777
+ # signals; an out-of-date index quietly degrades all of them.
778
+ from roam.commands.resolve import index_staleness_hint as _stale_hint
779
+
780
+ _stale = _stale_hint()
781
+ if _stale:
782
+ click.echo(f"NOTE: {_stale}\n")
759
783
  issue_count = len(cycles) + len(god_items) + len(bn_items) + len(violations)
760
784
  parts = []
761
785
  if cycles:
@@ -87,33 +87,57 @@ def oracle_route_exists(conn: sqlite3.Connection, path: str) -> tuple[bool, str]
87
87
 
88
88
 
89
89
  def oracle_is_test_only(conn: sqlite3.Connection, name: str) -> tuple[bool, str]:
90
- """Are ALL callers of this symbol in test files?
91
-
92
- A symbol with no callers at all returns ``False`` (no evidence either
93
- way). A symbol whose every caller's file role is ``test`` (per
94
- ``files.file_role``) returns ``True``.
90
+ """Is this symbol used only by test code?
91
+
92
+ Resolution order (matches dogfood expectations 2026-05-01):
93
+
94
+ 1. **Symbol lives in a test file** → ``True``. The previous heuristic
95
+ was "all callers in test files", but a test method like
96
+ ``test_check_count`` has zero callers (pytest invokes it by
97
+ reflection), and the heuristic returned ``False`` for the most
98
+ canonically test-only thing in the codebase. We now check the
99
+ symbol's *own* ``file_role`` first — anything in
100
+ ``file_role='test'`` is test-only by definition.
101
+ 2. **Symbol is in a non-test file with all callers in test files**
102
+ → ``True``. Catches helpers that live in production but are
103
+ only ever called from tests (genuinely test-only).
104
+ 3. **Symbol has no callers and is in a non-test file** → ``False``
105
+ with a clearer "orphan" reason than before.
95
106
  """
96
107
  if not name:
97
108
  return False, "empty query"
98
109
  target_rows = conn.execute(
99
- "SELECT id FROM symbols WHERE name = ? OR qualified_name = ?",
110
+ """
111
+ SELECT s.id, COALESCE(f.file_role, 'unknown') AS role
112
+ FROM symbols s
113
+ JOIN files f ON f.id = s.file_id
114
+ WHERE s.name = ? OR s.qualified_name = ?
115
+ """,
100
116
  (name, name),
101
117
  ).fetchall()
102
118
  if not target_rows:
103
119
  return False, f"no symbol named '{name}'"
104
- target_ids = [int(r[0]) for r in target_rows]
105
120
 
121
+ # 1) Symbol's own file_role is 'test' → trivially test-only.
122
+ own_test = [r for r in target_rows if r[1] == "test"]
123
+ if own_test and len(own_test) == len(target_rows):
124
+ return True, "symbol lives in a test file (file_role='test')"
125
+ # Mixed (e.g. same name in test + source) — fall through to caller
126
+ # analysis so the answer reflects actual usage.
127
+
128
+ target_ids = [int(r[0]) for r in target_rows]
106
129
  placeholders = ",".join("?" * len(target_ids))
107
130
  caller_rows = conn.execute(
108
131
  f"SELECT s.id, COALESCE(f.file_role, 'unknown') AS role "
109
132
  f"FROM edges e "
110
133
  f"JOIN symbols s ON s.id = e.source_id "
111
134
  f"JOIN files f ON f.id = s.file_id "
112
- f"WHERE e.target_id IN ({placeholders}) AND e.kind IN ('calls', 'references')",
135
+ f"WHERE e.target_id IN ({placeholders}) AND e.kind IN ('call', 'reference', 'calls', 'references')",
113
136
  target_ids,
114
137
  ).fetchall()
115
138
  if not caller_rows:
116
- return False, f"no callers found for '{name}' (orphan)"
139
+ # 3) No callers AND no callees lives in a test file ⇒ orphan in source.
140
+ return False, f"no callers found for '{name}' (orphan in source)"
117
141
  test_count = sum(1 for r in caller_rows if r[1] == "test")
118
142
  total = len(caller_rows)
119
143
  if test_count == total:
@@ -143,24 +167,42 @@ def oracle_is_reachable_from_entry(conn: sqlite3.Connection, name: str, *, max_h
143
167
  return False, f"no symbol named '{name}'"
144
168
  target_ids = {int(r[0]) for r in target_rows}
145
169
 
146
- # Pull entry symbol ids. Conservative: prefer explicit `is_entry=1`,
147
- # fall back to file_role='entry'.
170
+ # Entry-point definition (dogfood notes 2026-05-01, two iterations):
171
+ #
172
+ # First attempt — files with no incoming ``file_edges``. Caught the
173
+ # previous bug (``no entry-point symbols indexed`` on every query)
174
+ # but mis-classified ``cli`` itself: ``src/roam/cli.py`` IS imported
175
+ # by tests + internal helpers so it had incoming edges and wasn't
176
+ # an entry point. The user expects ``is-reachable cli`` to be True.
177
+ #
178
+ # Second attempt — same import-graph definition PLUS name-based
179
+ # fallback for canonical entry symbols (``cli``, ``main``,
180
+ # ``__main__``, ``run``, ``app``, ``serve``). Anything with one of
181
+ # these names IS treated as an entry point regardless of whether
182
+ # someone imported its file. Catches the "main is the entry point
183
+ # but tests import it" case without false-positives elsewhere.
148
184
  try:
149
- entry_rows = conn.execute("SELECT s.id FROM symbols s WHERE s.is_entry = 1").fetchall()
185
+ entry_rows = conn.execute(
186
+ """
187
+ SELECT s.id FROM symbols s
188
+ JOIN files f ON f.id = s.file_id
189
+ WHERE f.id NOT IN (SELECT DISTINCT target_file_id FROM file_edges)
190
+ """
191
+ ).fetchall()
150
192
  entry_ids = {int(r[0]) for r in entry_rows}
151
193
  except sqlite3.OperationalError:
152
194
  entry_ids = set()
153
- if not entry_ids:
154
- try:
155
- entry_rows = conn.execute(
156
- "SELECT s.id FROM symbols s JOIN files f ON f.id = s.file_id WHERE COALESCE(f.file_role, '') = 'entry'"
157
- ).fetchall()
158
- entry_ids = {int(r[0]) for r in entry_rows}
159
- except sqlite3.OperationalError:
160
- entry_ids = set()
195
+
196
+ try:
197
+ named_entry_rows = conn.execute(
198
+ "SELECT id FROM symbols WHERE name IN ('cli', 'main', '__main__', 'run', 'app', 'serve', 'entrypoint')"
199
+ ).fetchall()
200
+ entry_ids.update(int(r[0]) for r in named_entry_rows)
201
+ except sqlite3.OperationalError:
202
+ pass
161
203
 
162
204
  if not entry_ids:
163
- return False, "no entry-point symbols indexed (run `roam index` first)"
205
+ return False, "no entry-point symbols indexed (run `roam init` to (re)build the index)"
164
206
 
165
207
  if entry_ids & target_ids:
166
208
  return True, "target is itself an entry point"
@@ -176,7 +218,7 @@ def oracle_is_reachable_from_entry(conn: sqlite3.Connection, name: str, *, max_h
176
218
  rows = conn.execute(
177
219
  f"SELECT target_id FROM edges "
178
220
  f"WHERE source_id IN ({placeholders}) "
179
- f" AND kind IN ('calls', 'references')",
221
+ f" AND kind IN ('call', 'reference', 'calls', 'references')",
180
222
  chunk,
181
223
  ).fetchall()
182
224
  for r in rows:
@@ -106,6 +106,42 @@ def _overall_risk(*severities: str) -> str:
106
106
  return "LOW"
107
107
 
108
108
 
109
+ def _risk_driver(blast, tests, compl, coupl, convs, fitns) -> str:
110
+ """Identify the row driving the overall risk verdict.
111
+
112
+ Returns a one-line summary like ``complexity (cc=17, HIGH)`` so
113
+ an agent reading the preflight output knows *why* the overall
114
+ verdict is what it is — it doesn't have to scan all six rows
115
+ looking for the worst severity.
116
+
117
+ Tie-break by actionability: complexity > fitness > tests > coupling
118
+ > blast > conventions. A complexity warning is more actionable than
119
+ a convention warning even at equal severity.
120
+ """
121
+ rows = [
122
+ ("complexity", compl, f"cc={compl['max_cognitive_complexity']:.0f}"),
123
+ ("fitness", fitns, f"{fitns.get('rules_failed', 0)} rules would fail"),
124
+ ("tests", tests, f"{tests.get('direct', 0)} direct, {tests.get('transitive', 0)} transitive"),
125
+ ("coupling", coupl, f"{coupl.get('coupled_files', 0)} coupled files"),
126
+ (
127
+ "blast radius",
128
+ blast,
129
+ f"{blast.get('affected_symbols', 0)} symbols in {blast.get('affected_files', 0)} files",
130
+ ),
131
+ ("conventions", convs, f"{convs.get('violation_count', 0)} violations"),
132
+ ]
133
+ # Find max severity
134
+ worst: tuple[int, str, str] | None = None
135
+ for label, row, detail in rows:
136
+ sev = row.get("severity", "OK").upper()
137
+ order = _SEVERITY_ORDER.get(sev, 0)
138
+ if order <= 1: # OK / LOW — not driving the verdict
139
+ continue
140
+ if worst is None or order > worst[0]:
141
+ worst = (order, label, f"{label} ({detail}, {sev})")
142
+ return worst[2] if worst else ""
143
+
144
+
109
145
  def _severity_tag(sev: str) -> str:
110
146
  return f"[{sev}]"
111
147
 
@@ -708,6 +744,15 @@ def preflight(ctx, target, staged):
708
744
  # Overall
709
745
  click.echo(f"\n Overall risk: {risk}")
710
746
 
747
+ # Risk driver — name the row that's pushing the verdict so an
748
+ # agent doesn't have to deduce it (dogfood notes 2026-05-01).
749
+ # Pick the highest-severity row, breaking ties by category
750
+ # priority (complexity > fitness > tests > coupling > blast >
751
+ # conventions — most actionable first).
752
+ driver = _risk_driver(blast, tests, compl, coupl, convs, fitns)
753
+ if driver:
754
+ click.echo(f" Risk driver: {driver}")
755
+
711
756
  # Suggested tests
712
757
  if tests["pytest_command"]:
713
758
  click.echo(f" Suggested tests: {tests['pytest_command']}")
@@ -22,6 +22,85 @@ from roam.output.formatter import json_envelope, loc, to_json
22
22
  from roam.retrieve.pipeline import run_retrieve
23
23
 
24
24
 
25
+ def _retrieve_confidence(candidates: list[dict], task: str = "") -> str:
26
+ """Classify the confidence of a retrieve result.
27
+
28
+ Returns ``"low"`` when the top result probably doesn't match the
29
+ query, ``"ok"`` otherwise. Heuristic (dogfood notes 2026-05-01,
30
+ two iterations):
31
+
32
+ * **Token coverage** — count distinct query tokens that appear
33
+ across the top-5 candidate paths. If only 1 token is covered
34
+ across all 5 (e.g. "trace login flow" → only "trace" matches
35
+ anywhere), the answer is almost certainly junk: the lexical
36
+ hits are tracking one common word.
37
+ * **Score signals** — also flag when top score is < 0.30 AND
38
+ top-vs-fifth spread is < 0.10 (scores bunched at the noise
39
+ floor).
40
+
41
+ Either signal trips low-confidence; the token-coverage check
42
+ catches the original "trace the login flow" failure mode that
43
+ the score-only heuristic missed (top was 1.1 but covered just
44
+ one query token).
45
+ """
46
+ if not candidates:
47
+ return "low"
48
+ scores = [float(c.get("score") or 0.0) for c in candidates if c.get("score") is not None]
49
+ if not scores:
50
+ return "ok"
51
+
52
+ # Score-based check
53
+ top = scores[0]
54
+ fifth = scores[min(4, len(scores) - 1)]
55
+ if top < 0.30 and (top - fifth) < 0.10:
56
+ return "low"
57
+
58
+ # Token-coverage check across top-10 path+name. Match against both
59
+ # path AND symbol name — a test like ``test_implements_edge`` in
60
+ # ``test_comprehensive.py`` covers "implements" via name even though
61
+ # the path doesn't. Trip low-confidence when ≥3 query tokens were
62
+ # supplied but ≤1 of them surfaces *anywhere* in the top-10. That
63
+ # catches "trace the login flow" (top-10 has "trace" everywhere
64
+ # but never "login" or "flow") while letting "AST clone detection
65
+ # implemented" pass (clone, implements, detect all surface).
66
+ if task and candidates:
67
+ try:
68
+ from roam.retrieve.seeds import extract_tokens
69
+
70
+ tokens = extract_tokens(task)
71
+ except Exception:
72
+ tokens = []
73
+ if len(tokens) >= 2:
74
+ lowered = {t.lower() for t in tokens if len(t) >= 4}
75
+ covered: set[str] = set()
76
+ for c in candidates[:10]:
77
+ surface = (
78
+ (c.get("file_path") or c.get("file") or "")
79
+ + " "
80
+ + (c.get("name") or "")
81
+ + " "
82
+ + (c.get("qualified_name") or "")
83
+ ).lower()
84
+ for tok in lowered:
85
+ if tok in surface:
86
+ covered.add(tok)
87
+ elif len(tok) >= 7:
88
+ # Prefix match for plurals/derivations: "detection"
89
+ # → look for "detect", "implementing" → "implement".
90
+ # Length floor ≥7 means the prefix has ≥4 chars
91
+ # which is past the noise floor for path tokens.
92
+ if tok[:-3] in surface and len(tok[:-3]) >= 4:
93
+ covered.add(tok)
94
+ # Trip when ≥2 query tokens were supplied but ≤1 covered
95
+ # in top-10. Catches "trace login flow" (only "trace"
96
+ # matches, "login"/"flow" never appear) without firing
97
+ # on "AST clone detection implemented" where "clone" and
98
+ # "implemented" both surface in top-10.
99
+ if len(lowered) >= 2 and len(covered) <= 1:
100
+ return "low"
101
+ return "ok"
102
+
103
+
25
104
  @click.command()
26
105
  @click.argument("task", nargs=-1, required=True)
27
106
  @click.option(
@@ -120,13 +199,22 @@ def retrieve(ctx, task, budget, k, rerank, seed_files):
120
199
  )
121
200
 
122
201
  candidates = result["candidates"]
123
- verdict = (
202
+ confidence = _retrieve_confidence(candidates, task_str)
203
+ base_verdict = (
124
204
  f"{len(candidates)} span{'s' if len(candidates) != 1 else ''} "
125
205
  f"({result['budget_used']}/{result['budget']} tokens, "
126
206
  f"{len(result['seeds'])} seed{'s' if len(result['seeds']) != 1 else ''})"
127
207
  if candidates
128
208
  else "No candidates matched the task text"
129
209
  )
210
+ # R.5 (dogfood 2026-05-01): "trace the login flow" against a repo
211
+ # with no login flow returned 20 spans with no warning. The agent
212
+ # had no signal that the answer was junk. We now prepend a
213
+ # confidence tag to the verdict when (a) the top score is below
214
+ # an absolute floor or (b) scores are bunched within a narrow
215
+ # band — both indicators that lexical hits are spread thin
216
+ # rather than concentrated on a real match.
217
+ verdict = f"low confidence — {base_verdict}" if confidence == "low" else base_verdict
130
218
 
131
219
  if json_mode:
132
220
  click.echo(