roam-code 12.19__tar.gz → 12.20__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 (616) hide show
  1. {roam_code-12.19/src/roam_code.egg-info → roam_code-12.20}/PKG-INFO +1 -1
  2. {roam_code-12.19 → roam_code-12.20}/pyproject.toml +1 -1
  3. {roam_code-12.19 → roam_code-12.20}/src/roam/catalog/python_idioms.py +0 -22
  4. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_context.py +70 -51
  5. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_dead.py +188 -162
  6. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_doctor.py +8 -1
  7. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_health.py +13 -0
  8. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_orphan_imports.py +34 -13
  9. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_testmap.py +13 -1
  10. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_understand.py +8 -4
  11. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/metrics_history.py +28 -14
  12. {roam_code-12.19 → roam_code-12.20}/src/roam/competitor_site_data.py +1 -12
  13. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/query_engine.py +111 -104
  14. {roam_code-12.19 → roam_code-12.20}/src/roam/mcp-server-card.json +1 -1
  15. {roam_code-12.19 → roam_code-12.20}/src/roam/output/errors.py +0 -5
  16. {roam_code-12.19 → roam_code-12.20/src/roam_code.egg-info}/PKG-INFO +1 -1
  17. {roam_code-12.19 → roam_code-12.20}/src/roam_code.egg-info/SOURCES.txt +1 -0
  18. roam_code-12.20/tests/test_v1220_passes_101_110.py +107 -0
  19. {roam_code-12.19 → roam_code-12.20}/LICENSE +0 -0
  20. {roam_code-12.19 → roam_code-12.20}/README.md +0 -0
  21. {roam_code-12.19 → roam_code-12.20}/setup.cfg +0 -0
  22. {roam_code-12.19 → roam_code-12.20}/src/roam/__init__.py +0 -0
  23. {roam_code-12.19 → roam_code-12.20}/src/roam/__main__.py +0 -0
  24. {roam_code-12.19 → roam_code-12.20}/src/roam/analysis/__init__.py +0 -0
  25. {roam_code-12.19 → roam_code-12.20}/src/roam/analysis/effects.py +0 -0
  26. {roam_code-12.19 → roam_code-12.20}/src/roam/analysis/taint.py +0 -0
  27. {roam_code-12.19 → roam_code-12.20}/src/roam/api.py +0 -0
  28. {roam_code-12.19 → roam_code-12.20}/src/roam/ask/__init__.py +0 -0
  29. {roam_code-12.19 → roam_code-12.20}/src/roam/ask/classifier.py +0 -0
  30. {roam_code-12.19 → roam_code-12.20}/src/roam/ask/recipes.py +0 -0
  31. {roam_code-12.19 → roam_code-12.20}/src/roam/ask/runner.py +0 -0
  32. {roam_code-12.19 → roam_code-12.20}/src/roam/ask/workflow.py +0 -0
  33. {roam_code-12.19 → roam_code-12.20}/src/roam/attest/__init__.py +0 -0
  34. {roam_code-12.19 → roam_code-12.20}/src/roam/attest/cga.py +0 -0
  35. {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/__init__.py +0 -0
  36. {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/base.py +0 -0
  37. {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/bridge_config.py +0 -0
  38. {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/bridge_django.py +0 -0
  39. {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/bridge_protobuf.py +0 -0
  40. {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/bridge_rest_api.py +0 -0
  41. {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/bridge_salesforce.py +0 -0
  42. {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/bridge_template.py +0 -0
  43. {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/registry.py +0 -0
  44. {roam_code-12.19 → roam_code-12.20}/src/roam/catalog/__init__.py +0 -0
  45. {roam_code-12.19 → roam_code-12.20}/src/roam/catalog/detectors.py +0 -0
  46. {roam_code-12.19 → roam_code-12.20}/src/roam/catalog/fixes.py +0 -0
  47. {roam_code-12.19 → roam_code-12.20}/src/roam/catalog/smells.py +0 -0
  48. {roam_code-12.19 → roam_code-12.20}/src/roam/catalog/tasks.py +0 -0
  49. {roam_code-12.19 → roam_code-12.20}/src/roam/cli.py +0 -0
  50. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/__init__.py +0 -0
  51. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/changed_files.py +0 -0
  52. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_adrs.py +0 -0
  53. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_adversarial.py +0 -0
  54. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_affected.py +0 -0
  55. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_affected_tests.py +0 -0
  56. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_agent_context.py +0 -0
  57. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_agent_export.py +0 -0
  58. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_agent_plan.py +0 -0
  59. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_ai_ratio.py +0 -0
  60. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_ai_readiness.py +0 -0
  61. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_alerts.py +0 -0
  62. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_annotate.py +0 -0
  63. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_api.py +0 -0
  64. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_api_changes.py +0 -0
  65. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_api_drift.py +0 -0
  66. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_ask.py +0 -0
  67. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_attest.py +0 -0
  68. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_audit.py +0 -0
  69. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_auth_gaps.py +0 -0
  70. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_bisect.py +0 -0
  71. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_breaking.py +0 -0
  72. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_budget.py +0 -0
  73. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_bus_factor.py +0 -0
  74. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_capsule.py +0 -0
  75. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_cga.py +0 -0
  76. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_changelog.py +0 -0
  77. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_check_rules.py +0 -0
  78. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_ci_setup.py +0 -0
  79. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_clean.py +0 -0
  80. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_clones.py +0 -0
  81. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_closure.py +0 -0
  82. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_clusters.py +0 -0
  83. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_codeowners.py +0 -0
  84. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_complexity.py +0 -0
  85. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_config.py +0 -0
  86. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_congestion.py +0 -0
  87. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_conventions.py +0 -0
  88. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_coupling.py +0 -0
  89. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_coverage_gaps.py +0 -0
  90. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_critique.py +0 -0
  91. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_cut.py +0 -0
  92. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_dark_matter.py +0 -0
  93. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_dashboard.py +0 -0
  94. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_debt.py +0 -0
  95. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_deps.py +0 -0
  96. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_describe.py +0 -0
  97. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_dev_profile.py +0 -0
  98. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_diagnose.py +0 -0
  99. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_diff.py +0 -0
  100. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_disambiguate.py +0 -0
  101. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_doc_staleness.py +0 -0
  102. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_docs_coverage.py +0 -0
  103. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_drift.py +0 -0
  104. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_duplicates.py +0 -0
  105. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_effects.py +0 -0
  106. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_endpoints.py +0 -0
  107. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_entry_points.py +0 -0
  108. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_eval_retrieve.py +0 -0
  109. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_exit_codes.py +0 -0
  110. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_fan.py +0 -0
  111. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_file.py +0 -0
  112. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_fingerprint.py +0 -0
  113. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_fitness.py +0 -0
  114. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_flag_dead.py +0 -0
  115. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_fleet.py +0 -0
  116. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_fn_coupling.py +0 -0
  117. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_forecast.py +0 -0
  118. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_graph_export.py +0 -0
  119. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_graph_stats.py +0 -0
  120. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_grep.py +0 -0
  121. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_guard.py +0 -0
  122. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_help_search.py +0 -0
  123. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_hooks.py +0 -0
  124. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_hotspots.py +0 -0
  125. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_hover.py +0 -0
  126. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_impact.py +0 -0
  127. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_index.py +0 -0
  128. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_index_bundle.py +0 -0
  129. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_index_stats.py +0 -0
  130. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_ingest_trace.py +0 -0
  131. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_init.py +0 -0
  132. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_intent.py +0 -0
  133. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_invariants.py +0 -0
  134. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_layers.py +0 -0
  135. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_map.py +0 -0
  136. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_math.py +0 -0
  137. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_mcp_setup.py +0 -0
  138. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_mcp_status.py +0 -0
  139. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_metrics.py +0 -0
  140. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_migration_safety.py +0 -0
  141. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_minimap.py +0 -0
  142. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_missing_index.py +0 -0
  143. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_module.py +0 -0
  144. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_mutate.py +0 -0
  145. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_n1.py +0 -0
  146. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_oracle.py +0 -0
  147. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_orchestrate.py +0 -0
  148. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_orphan_routes.py +0 -0
  149. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_over_fetch.py +0 -0
  150. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_owner.py +0 -0
  151. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_partition.py +0 -0
  152. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_path_coverage.py +0 -0
  153. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_patterns.py +0 -0
  154. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_plan.py +0 -0
  155. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_plan_refactor.py +0 -0
  156. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_plugins.py +0 -0
  157. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_pr_diff.py +0 -0
  158. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_pr_prep.py +0 -0
  159. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_pr_risk.py +0 -0
  160. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_pre_commit.py +0 -0
  161. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_preflight.py +0 -0
  162. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_py_modern.py +0 -0
  163. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_py_types.py +0 -0
  164. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_pytest_fixtures.py +0 -0
  165. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_recipes.py +0 -0
  166. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_recommend.py +0 -0
  167. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_relate.py +0 -0
  168. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_report.py +0 -0
  169. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_reset.py +0 -0
  170. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_retrieve.py +0 -0
  171. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_risk.py +0 -0
  172. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_rules.py +0 -0
  173. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_safe_delete.py +0 -0
  174. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_safe_zones.py +0 -0
  175. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_sbom.py +0 -0
  176. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_schema.py +0 -0
  177. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_search.py +0 -0
  178. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_search_semantic.py +0 -0
  179. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_secrets.py +0 -0
  180. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_semantic_diff.py +0 -0
  181. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_simulate.py +0 -0
  182. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_simulate_departure.py +0 -0
  183. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_sketch.py +0 -0
  184. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_smells.py +0 -0
  185. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_spectral.py +0 -0
  186. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_split.py +0 -0
  187. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_stats.py +0 -0
  188. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_suggest_refactoring.py +0 -0
  189. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_suggest_reviewers.py +0 -0
  190. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_supply_chain.py +0 -0
  191. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_symbol.py +0 -0
  192. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_syntax_check.py +0 -0
  193. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_taint.py +0 -0
  194. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_telemetry.py +0 -0
  195. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_test_gaps.py +0 -0
  196. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_test_impact.py +0 -0
  197. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_test_pyramid.py +0 -0
  198. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_test_scaffold.py +0 -0
  199. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_timeline.py +0 -0
  200. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_tour.py +0 -0
  201. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_trace.py +0 -0
  202. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_trends.py +0 -0
  203. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_triage.py +0 -0
  204. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_uses.py +0 -0
  205. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_verify.py +0 -0
  206. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_verify_imports.py +0 -0
  207. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_version.py +0 -0
  208. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_vibe_check.py +0 -0
  209. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_visualize.py +0 -0
  210. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_vuln_map.py +0 -0
  211. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_vuln_reach.py +0 -0
  212. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_vulns.py +0 -0
  213. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_watch.py +0 -0
  214. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_weather.py +0 -0
  215. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_why.py +0 -0
  216. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_why_fail.py +0 -0
  217. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_workflow.py +0 -0
  218. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_ws.py +0 -0
  219. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_xlang.py +0 -0
  220. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/codeowners_helpers.py +0 -0
  221. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/context_helpers.py +0 -0
  222. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/gate_presets.py +0 -0
  223. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/graph_helpers.py +0 -0
  224. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/next_steps.py +0 -0
  225. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/resolve.py +0 -0
  226. {roam_code-12.19 → roam_code-12.20}/src/roam/commands/suppression.py +0 -0
  227. {roam_code-12.19 → roam_code-12.20}/src/roam/config.py +0 -0
  228. {roam_code-12.19 → roam_code-12.20}/src/roam/coverage_reports.py +0 -0
  229. {roam_code-12.19 → roam_code-12.20}/src/roam/critique/__init__.py +0 -0
  230. {roam_code-12.19 → roam_code-12.20}/src/roam/critique/aggregator.py +0 -0
  231. {roam_code-12.19 → roam_code-12.20}/src/roam/critique/checks.py +0 -0
  232. {roam_code-12.19 → roam_code-12.20}/src/roam/db/__init__.py +0 -0
  233. {roam_code-12.19 → roam_code-12.20}/src/roam/db/connection.py +0 -0
  234. {roam_code-12.19 → roam_code-12.20}/src/roam/db/queries.py +0 -0
  235. {roam_code-12.19 → roam_code-12.20}/src/roam/db/schema.py +0 -0
  236. {roam_code-12.19 → roam_code-12.20}/src/roam/eval/__init__.py +0 -0
  237. {roam_code-12.19 → roam_code-12.20}/src/roam/eval/harness.py +0 -0
  238. {roam_code-12.19 → roam_code-12.20}/src/roam/exit_codes.py +0 -0
  239. {roam_code-12.19 → roam_code-12.20}/src/roam/fleet/__init__.py +0 -0
  240. {roam_code-12.19 → roam_code-12.20}/src/roam/fleet/adapters.py +0 -0
  241. {roam_code-12.19 → roam_code-12.20}/src/roam/fleet/manifest.py +0 -0
  242. {roam_code-12.19 → roam_code-12.20}/src/roam/git_utils.py +0 -0
  243. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/__init__.py +0 -0
  244. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/anomaly.py +0 -0
  245. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/builder.py +0 -0
  246. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/clone_detect.py +0 -0
  247. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/clusters.py +0 -0
  248. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/cycles.py +0 -0
  249. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/dark_matter.py +0 -0
  250. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/diff.py +0 -0
  251. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/fingerprint.py +0 -0
  252. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/layers.py +0 -0
  253. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/pagerank.py +0 -0
  254. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/partition.py +0 -0
  255. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/pathfinding.py +0 -0
  256. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/propagation.py +0 -0
  257. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/simulate.py +0 -0
  258. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/spectral.py +0 -0
  259. {roam_code-12.19 → roam_code-12.20}/src/roam/graph/stats.py +0 -0
  260. {roam_code-12.19 → roam_code-12.20}/src/roam/index/__init__.py +0 -0
  261. {roam_code-12.19 → roam_code-12.20}/src/roam/index/complexity.py +0 -0
  262. {roam_code-12.19 → roam_code-12.20}/src/roam/index/discovery.py +0 -0
  263. {roam_code-12.19 → roam_code-12.20}/src/roam/index/django_post.py +0 -0
  264. {roam_code-12.19 → roam_code-12.20}/src/roam/index/file_roles.py +0 -0
  265. {roam_code-12.19 → roam_code-12.20}/src/roam/index/git_stats.py +0 -0
  266. {roam_code-12.19 → roam_code-12.20}/src/roam/index/gitignore.py +0 -0
  267. {roam_code-12.19 → roam_code-12.20}/src/roam/index/incremental.py +0 -0
  268. {roam_code-12.19 → roam_code-12.20}/src/roam/index/indexer.py +0 -0
  269. {roam_code-12.19 → roam_code-12.20}/src/roam/index/parser.py +0 -0
  270. {roam_code-12.19 → roam_code-12.20}/src/roam/index/pytest_fixtures.py +0 -0
  271. {roam_code-12.19 → roam_code-12.20}/src/roam/index/registry_dispatch.py +0 -0
  272. {roam_code-12.19 → roam_code-12.20}/src/roam/index/relations.py +0 -0
  273. {roam_code-12.19 → roam_code-12.20}/src/roam/index/symbols.py +0 -0
  274. {roam_code-12.19 → roam_code-12.20}/src/roam/index/test_conventions.py +0 -0
  275. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/__init__.py +0 -0
  276. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/apex_lang.py +0 -0
  277. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/aura_lang.py +0 -0
  278. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/base.py +0 -0
  279. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/c_lang.py +0 -0
  280. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/csharp_lang.py +0 -0
  281. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/extractor_schema.py +0 -0
  282. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/extractors/kotlin.yaml +0 -0
  283. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/foxpro_lang.py +0 -0
  284. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/generic_lang.py +0 -0
  285. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/go_lang.py +0 -0
  286. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/hcl_lang.py +0 -0
  287. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/java_lang.py +0 -0
  288. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/javascript_lang.py +0 -0
  289. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/kotlin_lang.py +0 -0
  290. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/php_lang.py +0 -0
  291. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/python_lang.py +0 -0
  292. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/registry.py +0 -0
  293. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/ruby_lang.py +0 -0
  294. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/rust_lang.py +0 -0
  295. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/scala_lang.py +0 -0
  296. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/sfxml_lang.py +0 -0
  297. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/sql_lang.py +0 -0
  298. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/swift_lang.py +0 -0
  299. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/typescript_lang.py +0 -0
  300. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/visualforce_lang.py +0 -0
  301. {roam_code-12.19 → roam_code-12.20}/src/roam/languages/yaml_lang.py +0 -0
  302. {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_extras/__init__.py +0 -0
  303. {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_extras/completions.py +0 -0
  304. {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_extras/concurrency.py +0 -0
  305. {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_extras/progress.py +0 -0
  306. {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_extras/sampling.py +0 -0
  307. {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_extras/session.py +0 -0
  308. {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_extras/watcher.py +0 -0
  309. {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_server.py +0 -0
  310. {roam_code-12.19 → roam_code-12.20}/src/roam/observability.py +0 -0
  311. {roam_code-12.19 → roam_code-12.20}/src/roam/output/__init__.py +0 -0
  312. {roam_code-12.19 → roam_code-12.20}/src/roam/output/confidence.py +0 -0
  313. {roam_code-12.19 → roam_code-12.20}/src/roam/output/file_role_hints.py +0 -0
  314. {roam_code-12.19 → roam_code-12.20}/src/roam/output/formatter.py +0 -0
  315. {roam_code-12.19 → roam_code-12.20}/src/roam/output/framework_filter.py +0 -0
  316. {roam_code-12.19 → roam_code-12.20}/src/roam/output/mermaid.py +0 -0
  317. {roam_code-12.19 → roam_code-12.20}/src/roam/output/project_shape.py +0 -0
  318. {roam_code-12.19 → roam_code-12.20}/src/roam/output/sarif.py +0 -0
  319. {roam_code-12.19 → roam_code-12.20}/src/roam/output/schema_registry.py +0 -0
  320. {roam_code-12.19 → roam_code-12.20}/src/roam/plugins.py +0 -0
  321. {roam_code-12.19 → roam_code-12.20}/src/roam/refactor/__init__.py +0 -0
  322. {roam_code-12.19 → roam_code-12.20}/src/roam/refactor/codegen.py +0 -0
  323. {roam_code-12.19 → roam_code-12.20}/src/roam/refactor/transforms.py +0 -0
  324. {roam_code-12.19 → roam_code-12.20}/src/roam/retrieve/__init__.py +0 -0
  325. {roam_code-12.19 → roam_code-12.20}/src/roam/retrieve/learned_ranker.py +0 -0
  326. {roam_code-12.19 → roam_code-12.20}/src/roam/retrieve/pipeline.py +0 -0
  327. {roam_code-12.19 → roam_code-12.20}/src/roam/retrieve/rerank.py +0 -0
  328. {roam_code-12.19 → roam_code-12.20}/src/roam/retrieve/seeds.py +0 -0
  329. {roam_code-12.19 → roam_code-12.20}/src/roam/retrieve/semantic.py +0 -0
  330. {roam_code-12.19 → roam_code-12.20}/src/roam/rules/__init__.py +0 -0
  331. {roam_code-12.19 → roam_code-12.20}/src/roam/rules/ast_match.py +0 -0
  332. {roam_code-12.19 → roam_code-12.20}/src/roam/rules/builtin.py +0 -0
  333. {roam_code-12.19 → roam_code-12.20}/src/roam/rules/dataflow.py +0 -0
  334. {roam_code-12.19 → roam_code-12.20}/src/roam/rules/engine.py +0 -0
  335. {roam_code-12.19 → roam_code-12.20}/src/roam/runtime/__init__.py +0 -0
  336. {roam_code-12.19 → roam_code-12.20}/src/roam/runtime/daemon.py +0 -0
  337. {roam_code-12.19 → roam_code-12.20}/src/roam/runtime/graph_backend.py +0 -0
  338. {roam_code-12.19 → roam_code-12.20}/src/roam/runtime/hotspots.py +0 -0
  339. {roam_code-12.19 → roam_code-12.20}/src/roam/runtime/lock_modes.py +0 -0
  340. {roam_code-12.19 → roam_code-12.20}/src/roam/runtime/lockmgr.py +0 -0
  341. {roam_code-12.19 → roam_code-12.20}/src/roam/runtime/trace_ingest.py +0 -0
  342. {roam_code-12.19 → roam_code-12.20}/src/roam/search/__init__.py +0 -0
  343. {roam_code-12.19 → roam_code-12.20}/src/roam/search/framework_packs.py +0 -0
  344. {roam_code-12.19 → roam_code-12.20}/src/roam/search/index_embeddings.py +0 -0
  345. {roam_code-12.19 → roam_code-12.20}/src/roam/search/onnx_embeddings.py +0 -0
  346. {roam_code-12.19 → roam_code-12.20}/src/roam/search/tfidf.py +0 -0
  347. {roam_code-12.19 → roam_code-12.20}/src/roam/security/__init__.py +0 -0
  348. {roam_code-12.19 → roam_code-12.20}/src/roam/security/aibom_extension.py +0 -0
  349. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_classifier.py +0 -0
  350. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_engine.py +0 -0
  351. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/api_error_leak.yaml +0 -0
  352. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/java_fileupload_path_traversal.yaml +0 -0
  353. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/js_insecure_jwt_decode.yaml +0 -0
  354. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/js_localstorage_secrets.yaml +0 -0
  355. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/js_prototype_pollution.yaml +0 -0
  356. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/js_ssrf.yaml +0 -0
  357. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/js_xss.yaml +0 -0
  358. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/python_basic.yaml +0 -0
  359. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/python_deserialization.yaml +0 -0
  360. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/python_path_traversal.yaml +0 -0
  361. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/python_socketio_remote_source.yaml +0 -0
  362. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/python_sqli.yaml +0 -0
  363. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/python_urllib_open_redirect.yaml +0 -0
  364. {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/vue_v_html.yaml +0 -0
  365. {roam_code-12.19 → roam_code-12.20}/src/roam/security/vuln_reach.py +0 -0
  366. {roam_code-12.19 → roam_code-12.20}/src/roam/security/vuln_store.py +0 -0
  367. {roam_code-12.19 → roam_code-12.20}/src/roam/surface_counts.py +0 -0
  368. {roam_code-12.19 → roam_code-12.20}/src/roam/telemetry.py +0 -0
  369. {roam_code-12.19 → roam_code-12.20}/src/roam/templates/__init__.py +0 -0
  370. {roam_code-12.19 → roam_code-12.20}/src/roam/templates/ci/Jenkinsfile +0 -0
  371. {roam_code-12.19 → roam_code-12.20}/src/roam/templates/ci/__init__.py +0 -0
  372. {roam_code-12.19 → roam_code-12.20}/src/roam/templates/ci/azure-pipelines.yml +0 -0
  373. {roam_code-12.19 → roam_code-12.20}/src/roam/templates/ci/bitbucket-pipelines.yml +0 -0
  374. {roam_code-12.19 → roam_code-12.20}/src/roam/templates/ci/gitlab-ci.yml +0 -0
  375. {roam_code-12.19 → roam_code-12.20}/src/roam/workspace/__init__.py +0 -0
  376. {roam_code-12.19 → roam_code-12.20}/src/roam/workspace/aggregator.py +0 -0
  377. {roam_code-12.19 → roam_code-12.20}/src/roam/workspace/api_scanner.py +0 -0
  378. {roam_code-12.19 → roam_code-12.20}/src/roam/workspace/config.py +0 -0
  379. {roam_code-12.19 → roam_code-12.20}/src/roam/workspace/db.py +0 -0
  380. {roam_code-12.19 → roam_code-12.20}/src/roam_code.egg-info/dependency_links.txt +0 -0
  381. {roam_code-12.19 → roam_code-12.20}/src/roam_code.egg-info/entry_points.txt +0 -0
  382. {roam_code-12.19 → roam_code-12.20}/src/roam_code.egg-info/requires.txt +0 -0
  383. {roam_code-12.19 → roam_code-12.20}/src/roam_code.egg-info/top_level.txt +0 -0
  384. {roam_code-12.19 → roam_code-12.20}/tests/test_adrs.py +0 -0
  385. {roam_code-12.19 → roam_code-12.20}/tests/test_adversarial.py +0 -0
  386. {roam_code-12.19 → roam_code-12.20}/tests/test_affected.py +0 -0
  387. {roam_code-12.19 → roam_code-12.20}/tests/test_agent_export.py +0 -0
  388. {roam_code-12.19 → roam_code-12.20}/tests/test_agent_mode.py +0 -0
  389. {roam_code-12.19 → roam_code-12.20}/tests/test_agent_plan_context.py +0 -0
  390. {roam_code-12.19 → roam_code-12.20}/tests/test_ai_ratio.py +0 -0
  391. {roam_code-12.19 → roam_code-12.20}/tests/test_ai_readiness.py +0 -0
  392. {roam_code-12.19 → roam_code-12.20}/tests/test_alerts_cmd.py +0 -0
  393. {roam_code-12.19 → roam_code-12.20}/tests/test_annotations.py +0 -0
  394. {roam_code-12.19 → roam_code-12.20}/tests/test_anomaly.py +0 -0
  395. {roam_code-12.19 → roam_code-12.20}/tests/test_api_changes.py +0 -0
  396. {roam_code-12.19 → roam_code-12.20}/tests/test_api_drift.py +0 -0
  397. {roam_code-12.19 → roam_code-12.20}/tests/test_ask.py +0 -0
  398. {roam_code-12.19 → roam_code-12.20}/tests/test_attest.py +0 -0
  399. {roam_code-12.19 → roam_code-12.20}/tests/test_auth_gaps.py +0 -0
  400. {roam_code-12.19 → roam_code-12.20}/tests/test_backend_fixes_round2.py +0 -0
  401. {roam_code-12.19 → roam_code-12.20}/tests/test_backend_fixes_round3.py +0 -0
  402. {roam_code-12.19 → roam_code-12.20}/tests/test_basic.py +0 -0
  403. {roam_code-12.19 → roam_code-12.20}/tests/test_batch_mcp.py +0 -0
  404. {roam_code-12.19 → roam_code-12.20}/tests/test_bisect.py +0 -0
  405. {roam_code-12.19 → roam_code-12.20}/tests/test_bridge_django.py +0 -0
  406. {roam_code-12.19 → roam_code-12.20}/tests/test_bridges.py +0 -0
  407. {roam_code-12.19 → roam_code-12.20}/tests/test_bridges_extended.py +0 -0
  408. {roam_code-12.19 → roam_code-12.20}/tests/test_budget.py +0 -0
  409. {roam_code-12.19 → roam_code-12.20}/tests/test_budget_flag.py +0 -0
  410. {roam_code-12.19 → roam_code-12.20}/tests/test_budget_phase2.py +0 -0
  411. {roam_code-12.19 → roam_code-12.20}/tests/test_bus_factor.py +0 -0
  412. {roam_code-12.19 → roam_code-12.20}/tests/test_capsule.py +0 -0
  413. {roam_code-12.19 → roam_code-12.20}/tests/test_cga.py +0 -0
  414. {roam_code-12.19 → roam_code-12.20}/tests/test_check_rules.py +0 -0
  415. {roam_code-12.19 → roam_code-12.20}/tests/test_ci_gate_eval.py +0 -0
  416. {roam_code-12.19 → roam_code-12.20}/tests/test_ci_sarif_guard.py +0 -0
  417. {roam_code-12.19 → roam_code-12.20}/tests/test_ci_setup.py +0 -0
  418. {roam_code-12.19 → roam_code-12.20}/tests/test_clones.py +0 -0
  419. {roam_code-12.19 → roam_code-12.20}/tests/test_closure.py +0 -0
  420. {roam_code-12.19 → roam_code-12.20}/tests/test_codeowners.py +0 -0
  421. {roam_code-12.19 → roam_code-12.20}/tests/test_commands_architecture.py +0 -0
  422. {roam_code-12.19 → roam_code-12.20}/tests/test_commands_exploration.py +0 -0
  423. {roam_code-12.19 → roam_code-12.20}/tests/test_commands_health.py +0 -0
  424. {roam_code-12.19 → roam_code-12.20}/tests/test_commands_refactoring.py +0 -0
  425. {roam_code-12.19 → roam_code-12.20}/tests/test_commands_workflow.py +0 -0
  426. {roam_code-12.19 → roam_code-12.20}/tests/test_competitor_site_data.py +0 -0
  427. {roam_code-12.19 → roam_code-12.20}/tests/test_comprehensive.py +0 -0
  428. {roam_code-12.19 → roam_code-12.20}/tests/test_config.py +0 -0
  429. {roam_code-12.19 → roam_code-12.20}/tests/test_congestion.py +0 -0
  430. {roam_code-12.19 → roam_code-12.20}/tests/test_context_propagation.py +0 -0
  431. {roam_code-12.19 → roam_code-12.20}/tests/test_conventions_cmd.py +0 -0
  432. {roam_code-12.19 → roam_code-12.20}/tests/test_coverage_gaps_cmd.py +0 -0
  433. {roam_code-12.19 → roam_code-12.20}/tests/test_coverage_ingestion.py +0 -0
  434. {roam_code-12.19 → roam_code-12.20}/tests/test_critique.py +0 -0
  435. {roam_code-12.19 → roam_code-12.20}/tests/test_cut.py +0 -0
  436. {roam_code-12.19 → roam_code-12.20}/tests/test_dark_matter.py +0 -0
  437. {roam_code-12.19 → roam_code-12.20}/tests/test_dark_matter_helpers.py +0 -0
  438. {roam_code-12.19 → roam_code-12.20}/tests/test_dashboard.py +0 -0
  439. {roam_code-12.19 → roam_code-12.20}/tests/test_dataflow_dead.py +0 -0
  440. {roam_code-12.19 → roam_code-12.20}/tests/test_dead_aging.py +0 -0
  441. {roam_code-12.19 → roam_code-12.20}/tests/test_defer_loading.py +0 -0
  442. {roam_code-12.19 → roam_code-12.20}/tests/test_demo_gif_asset.py +0 -0
  443. {roam_code-12.19 → roam_code-12.20}/tests/test_describe.py +0 -0
  444. {roam_code-12.19 → roam_code-12.20}/tests/test_detail_flag_hints.py +0 -0
  445. {roam_code-12.19 → roam_code-12.20}/tests/test_detector_precision.py +0 -0
  446. {roam_code-12.19 → roam_code-12.20}/tests/test_deterministic_output.py +0 -0
  447. {roam_code-12.19 → roam_code-12.20}/tests/test_dev_profile.py +0 -0
  448. {roam_code-12.19 → roam_code-12.20}/tests/test_difficulty_scoring.py +0 -0
  449. {roam_code-12.19 → roam_code-12.20}/tests/test_doc_consistency.py +0 -0
  450. {roam_code-12.19 → roam_code-12.20}/tests/test_doc_staleness.py +0 -0
  451. {roam_code-12.19 → roam_code-12.20}/tests/test_docker_assets.py +0 -0
  452. {roam_code-12.19 → roam_code-12.20}/tests/test_docs_coverage.py +0 -0
  453. {roam_code-12.19 → roam_code-12.20}/tests/test_docs_site_quality.py +0 -0
  454. {roam_code-12.19 → roam_code-12.20}/tests/test_doctor.py +0 -0
  455. {roam_code-12.19 → roam_code-12.20}/tests/test_drift.py +0 -0
  456. {roam_code-12.19 → roam_code-12.20}/tests/test_drift_by_team.py +0 -0
  457. {roam_code-12.19 → roam_code-12.20}/tests/test_duplicates.py +0 -0
  458. {roam_code-12.19 → roam_code-12.20}/tests/test_effects.py +0 -0
  459. {roam_code-12.19 → roam_code-12.20}/tests/test_effects_propagation.py +0 -0
  460. {roam_code-12.19 → roam_code-12.20}/tests/test_endpoints.py +0 -0
  461. {roam_code-12.19 → roam_code-12.20}/tests/test_entry_points_cmd.py +0 -0
  462. {roam_code-12.19 → roam_code-12.20}/tests/test_eval_retrieve.py +0 -0
  463. {roam_code-12.19 → roam_code-12.20}/tests/test_except_pass_narrow.py +0 -0
  464. {roam_code-12.19 → roam_code-12.20}/tests/test_exclude_patterns.py +0 -0
  465. {roam_code-12.19 → roam_code-12.20}/tests/test_exit_codes.py +0 -0
  466. {roam_code-12.19 → roam_code-12.20}/tests/test_extractor_grammar_drift.py +0 -0
  467. {roam_code-12.19 → roam_code-12.20}/tests/test_fallback_contracts.py +0 -0
  468. {roam_code-12.19 → roam_code-12.20}/tests/test_file_roles.py +0 -0
  469. {roam_code-12.19 → roam_code-12.20}/tests/test_fingerprint.py +0 -0
  470. {roam_code-12.19 → roam_code-12.20}/tests/test_fixes.py +0 -0
  471. {roam_code-12.19 → roam_code-12.20}/tests/test_flag_dead.py +0 -0
  472. {roam_code-12.19 → roam_code-12.20}/tests/test_fleet.py +0 -0
  473. {roam_code-12.19 → roam_code-12.20}/tests/test_fn_coupling.py +0 -0
  474. {roam_code-12.19 → roam_code-12.20}/tests/test_forecast.py +0 -0
  475. {roam_code-12.19 → roam_code-12.20}/tests/test_formatters.py +0 -0
  476. {roam_code-12.19 → roam_code-12.20}/tests/test_foxpro.py +0 -0
  477. {roam_code-12.19 → roam_code-12.20}/tests/test_framework_detection.py +0 -0
  478. {roam_code-12.19 → roam_code-12.20}/tests/test_gate_presets.py +0 -0
  479. {roam_code-12.19 → roam_code-12.20}/tests/test_git_utils.py +0 -0
  480. {roam_code-12.19 → roam_code-12.20}/tests/test_guard.py +0 -0
  481. {roam_code-12.19 → roam_code-12.20}/tests/test_health_gate.py +0 -0
  482. {roam_code-12.19 → roam_code-12.20}/tests/test_hooks.py +0 -0
  483. {roam_code-12.19 → roam_code-12.20}/tests/test_hotspots.py +0 -0
  484. {roam_code-12.19 → roam_code-12.20}/tests/test_hover.py +0 -0
  485. {roam_code-12.19 → roam_code-12.20}/tests/test_index.py +0 -0
  486. {roam_code-12.19 → roam_code-12.20}/tests/test_index_bundle.py +0 -0
  487. {roam_code-12.19 → roam_code-12.20}/tests/test_init_cmd.py +0 -0
  488. {roam_code-12.19 → roam_code-12.20}/tests/test_install_check.py +0 -0
  489. {roam_code-12.19 → roam_code-12.20}/tests/test_intent.py +0 -0
  490. {roam_code-12.19 → roam_code-12.20}/tests/test_invariants.py +0 -0
  491. {roam_code-12.19 → roam_code-12.20}/tests/test_json_contracts.py +0 -0
  492. {roam_code-12.19 → roam_code-12.20}/tests/test_json_error_envelope.py +0 -0
  493. {roam_code-12.19 → roam_code-12.20}/tests/test_kotlin_swift_extractors.py +0 -0
  494. {roam_code-12.19 → roam_code-12.20}/tests/test_language_corpus.py +0 -0
  495. {roam_code-12.19 → roam_code-12.20}/tests/test_languages.py +0 -0
  496. {roam_code-12.19 → roam_code-12.20}/tests/test_library_api.py +0 -0
  497. {roam_code-12.19 → roam_code-12.20}/tests/test_math.py +0 -0
  498. {roam_code-12.19 → roam_code-12.20}/tests/test_math_tips.py +0 -0
  499. {roam_code-12.19 → roam_code-12.20}/tests/test_mcp_extras.py +0 -0
  500. {roam_code-12.19 → roam_code-12.20}/tests/test_mcp_server.py +0 -0
  501. {roam_code-12.19 → roam_code-12.20}/tests/test_mcp_setup.py +0 -0
  502. {roam_code-12.19 → roam_code-12.20}/tests/test_mermaid.py +0 -0
  503. {roam_code-12.19 → roam_code-12.20}/tests/test_metrics_cmd.py +0 -0
  504. {roam_code-12.19 → roam_code-12.20}/tests/test_migration_safety.py +0 -0
  505. {roam_code-12.19 → roam_code-12.20}/tests/test_minimap.py +0 -0
  506. {roam_code-12.19 → roam_code-12.20}/tests/test_missing_index.py +0 -0
  507. {roam_code-12.19 → roam_code-12.20}/tests/test_mutate.py +0 -0
  508. {roam_code-12.19 → roam_code-12.20}/tests/test_n1.py +0 -0
  509. {roam_code-12.19 → roam_code-12.20}/tests/test_next_steps.py +0 -0
  510. {roam_code-12.19 → roam_code-12.20}/tests/test_onboard.py +0 -0
  511. {roam_code-12.19 → roam_code-12.20}/tests/test_oracle.py +0 -0
  512. {roam_code-12.19 → roam_code-12.20}/tests/test_orchestrate.py +0 -0
  513. {roam_code-12.19 → roam_code-12.20}/tests/test_orphan_routes.py +0 -0
  514. {roam_code-12.19 → roam_code-12.20}/tests/test_oss_bench_harness.py +0 -0
  515. {roam_code-12.19 → roam_code-12.20}/tests/test_over_fetch.py +0 -0
  516. {roam_code-12.19 → roam_code-12.20}/tests/test_pagerank_truncation.py +0 -0
  517. {roam_code-12.19 → roam_code-12.20}/tests/test_partition.py +0 -0
  518. {roam_code-12.19 → roam_code-12.20}/tests/test_path_coverage.py +0 -0
  519. {roam_code-12.19 → roam_code-12.20}/tests/test_patterns_cmd.py +0 -0
  520. {roam_code-12.19 → roam_code-12.20}/tests/test_performance.py +0 -0
  521. {roam_code-12.19 → roam_code-12.20}/tests/test_personalized_pagerank.py +0 -0
  522. {roam_code-12.19 → roam_code-12.20}/tests/test_plan.py +0 -0
  523. {roam_code-12.19 → roam_code-12.20}/tests/test_plugin_discovery.py +0 -0
  524. {roam_code-12.19 → roam_code-12.20}/tests/test_pr_comment_script.py +0 -0
  525. {roam_code-12.19 → roam_code-12.20}/tests/test_pr_diff.py +0 -0
  526. {roam_code-12.19 → roam_code-12.20}/tests/test_pr_risk_author.py +0 -0
  527. {roam_code-12.19 → roam_code-12.20}/tests/test_progress.py +0 -0
  528. {roam_code-12.19 → roam_code-12.20}/tests/test_progressive_disclosure.py +0 -0
  529. {roam_code-12.19 → roam_code-12.20}/tests/test_properties.py +0 -0
  530. {roam_code-12.19 → roam_code-12.20}/tests/test_pytest_fixtures.py +0 -0
  531. {roam_code-12.19 → roam_code-12.20}/tests/test_python_extractor_v2.py +0 -0
  532. {roam_code-12.19 → roam_code-12.20}/tests/test_python_idioms_e2e.py +0 -0
  533. {roam_code-12.19 → roam_code-12.20}/tests/test_python_pivot.py +0 -0
  534. {roam_code-12.19 → roam_code-12.20}/tests/test_readme_surface_consistency.py +0 -0
  535. {roam_code-12.19 → roam_code-12.20}/tests/test_realworld_feedback.py +0 -0
  536. {roam_code-12.19 → roam_code-12.20}/tests/test_refactoring_intelligence.py +0 -0
  537. {roam_code-12.19 → roam_code-12.20}/tests/test_registry_dispatch.py +0 -0
  538. {roam_code-12.19 → roam_code-12.20}/tests/test_relate.py +0 -0
  539. {roam_code-12.19 → roam_code-12.20}/tests/test_report.py +0 -0
  540. {roam_code-12.19 → roam_code-12.20}/tests/test_reset_clean.py +0 -0
  541. {roam_code-12.19 → roam_code-12.20}/tests/test_resolve.py +0 -0
  542. {roam_code-12.19 → roam_code-12.20}/tests/test_retrieve.py +0 -0
  543. {roam_code-12.19 → roam_code-12.20}/tests/test_retrieve_cross_repo.py +0 -0
  544. {roam_code-12.19 → roam_code-12.20}/tests/test_retrieve_seeds.py +0 -0
  545. {roam_code-12.19 → roam_code-12.20}/tests/test_risk.py +0 -0
  546. {roam_code-12.19 → roam_code-12.20}/tests/test_ruby.py +0 -0
  547. {roam_code-12.19 → roam_code-12.20}/tests/test_rule_profiles.py +0 -0
  548. {roam_code-12.19 → roam_code-12.20}/tests/test_rules.py +0 -0
  549. {roam_code-12.19 → roam_code-12.20}/tests/test_rules_ast_match.py +0 -0
  550. {roam_code-12.19 → roam_code-12.20}/tests/test_rules_community_pack.py +0 -0
  551. {roam_code-12.19 → roam_code-12.20}/tests/test_rules_dataflow.py +0 -0
  552. {roam_code-12.19 → roam_code-12.20}/tests/test_rules_symbol_requirements.py +0 -0
  553. {roam_code-12.19 → roam_code-12.20}/tests/test_runtime.py +0 -0
  554. {roam_code-12.19 → roam_code-12.20}/tests/test_runtime_score.py +0 -0
  555. {roam_code-12.19 → roam_code-12.20}/tests/test_salesforce.py +0 -0
  556. {roam_code-12.19 → roam_code-12.20}/tests/test_sarif_flag.py +0 -0
  557. {roam_code-12.19 → roam_code-12.20}/tests/test_sbom.py +0 -0
  558. {roam_code-12.19 → roam_code-12.20}/tests/test_scala.py +0 -0
  559. {roam_code-12.19 → roam_code-12.20}/tests/test_schema_versioning.py +0 -0
  560. {roam_code-12.19 → roam_code-12.20}/tests/test_search_explain.py +0 -0
  561. {roam_code-12.19 → roam_code-12.20}/tests/test_secrets.py +0 -0
  562. {roam_code-12.19 → roam_code-12.20}/tests/test_secrets_v2.py +0 -0
  563. {roam_code-12.19 → roam_code-12.20}/tests/test_semantic_diff.py +0 -0
  564. {roam_code-12.19 → roam_code-12.20}/tests/test_semantic_onnx.py +0 -0
  565. {roam_code-12.19 → roam_code-12.20}/tests/test_semantic_search.py +0 -0
  566. {roam_code-12.19 → roam_code-12.20}/tests/test_simulate.py +0 -0
  567. {roam_code-12.19 → roam_code-12.20}/tests/test_simulate_departure.py +0 -0
  568. {roam_code-12.19 → roam_code-12.20}/tests/test_sketch.py +0 -0
  569. {roam_code-12.19 → roam_code-12.20}/tests/test_smells.py +0 -0
  570. {roam_code-12.19 → roam_code-12.20}/tests/test_smoke.py +0 -0
  571. {roam_code-12.19 → roam_code-12.20}/tests/test_sna_metrics.py +0 -0
  572. {roam_code-12.19 → roam_code-12.20}/tests/test_spectral.py +0 -0
  573. {roam_code-12.19 → roam_code-12.20}/tests/test_split_cmd.py +0 -0
  574. {roam_code-12.19 → roam_code-12.20}/tests/test_sql.py +0 -0
  575. {roam_code-12.19 → roam_code-12.20}/tests/test_suggest_reviewers.py +0 -0
  576. {roam_code-12.19 → roam_code-12.20}/tests/test_supply_chain.py +0 -0
  577. {roam_code-12.19 → roam_code-12.20}/tests/test_surface_counts.py +0 -0
  578. {roam_code-12.19 → roam_code-12.20}/tests/test_syntax_check.py +0 -0
  579. {roam_code-12.19 → roam_code-12.20}/tests/test_taint.py +0 -0
  580. {roam_code-12.19 → roam_code-12.20}/tests/test_taint_analysis.py +0 -0
  581. {roam_code-12.19 → roam_code-12.20}/tests/test_taint_classifier.py +0 -0
  582. {roam_code-12.19 → roam_code-12.20}/tests/test_taint_intraprocedural.py +0 -0
  583. {roam_code-12.19 → roam_code-12.20}/tests/test_test_conventions.py +0 -0
  584. {roam_code-12.19 → roam_code-12.20}/tests/test_test_gaps.py +0 -0
  585. {roam_code-12.19 → roam_code-12.20}/tests/test_test_scaffold.py +0 -0
  586. {roam_code-12.19 → roam_code-12.20}/tests/test_testmap.py +0 -0
  587. {roam_code-12.19 → roam_code-12.20}/tests/test_top_flag_consistency.py +0 -0
  588. {roam_code-12.19 → roam_code-12.20}/tests/test_tour_cmd.py +0 -0
  589. {roam_code-12.19 → roam_code-12.20}/tests/test_trends.py +0 -0
  590. {roam_code-12.19 → roam_code-12.20}/tests/test_trends_cohort.py +0 -0
  591. {roam_code-12.19 → roam_code-12.20}/tests/test_triage.py +0 -0
  592. {roam_code-12.19 → roam_code-12.20}/tests/test_uses_cmd.py +0 -0
  593. {roam_code-12.19 → roam_code-12.20}/tests/test_v1215_passes.py +0 -0
  594. {roam_code-12.19 → roam_code-12.20}/tests/test_v1216_passes.py +0 -0
  595. {roam_code-12.19 → roam_code-12.20}/tests/test_v1216_passes_41_50.py +0 -0
  596. {roam_code-12.19 → roam_code-12.20}/tests/test_v1216_passes_51_60.py +0 -0
  597. {roam_code-12.19 → roam_code-12.20}/tests/test_v1217_passes_61_80.py +0 -0
  598. {roam_code-12.19 → roam_code-12.20}/tests/test_v1218_passes_81_90.py +0 -0
  599. {roam_code-12.19 → roam_code-12.20}/tests/test_v1219_passes_91_100.py +0 -0
  600. {roam_code-12.19 → roam_code-12.20}/tests/test_v12_2.py +0 -0
  601. {roam_code-12.19 → roam_code-12.20}/tests/test_v6_features.py +0 -0
  602. {roam_code-12.19 → roam_code-12.20}/tests/test_v71_features.py +0 -0
  603. {roam_code-12.19 → roam_code-12.20}/tests/test_v7_features.py +0 -0
  604. {roam_code-12.19 → roam_code-12.20}/tests/test_v82_features.py +0 -0
  605. {roam_code-12.19 → roam_code-12.20}/tests/test_verify.py +0 -0
  606. {roam_code-12.19 → roam_code-12.20}/tests/test_verify_imports.py +0 -0
  607. {roam_code-12.19 → roam_code-12.20}/tests/test_vibe_check.py +0 -0
  608. {roam_code-12.19 → roam_code-12.20}/tests/test_visualize.py +0 -0
  609. {roam_code-12.19 → roam_code-12.20}/tests/test_vuln.py +0 -0
  610. {roam_code-12.19 → roam_code-12.20}/tests/test_vulns_cmd.py +0 -0
  611. {roam_code-12.19 → roam_code-12.20}/tests/test_watch.py +0 -0
  612. {roam_code-12.19 → roam_code-12.20}/tests/test_why.py +0 -0
  613. {roam_code-12.19 → roam_code-12.20}/tests/test_workspace.py +0 -0
  614. {roam_code-12.19 → roam_code-12.20}/tests/test_ws_resolve_fixes.py +0 -0
  615. {roam_code-12.19 → roam_code-12.20}/tests/test_xlang.py +0 -0
  616. {roam_code-12.19 → roam_code-12.20}/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.19
3
+ Version: 12.20
4
4
  Summary: Instant codebase comprehension for AI coding agents
5
5
  Author: CosmoHac
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "roam-code"
7
- version = "12.19"
7
+ version = "12.20"
8
8
  description = "Instant codebase comprehension for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -742,28 +742,6 @@ def detect_dict_keys_iter(conn: sqlite3.Connection) -> list[dict]:
742
742
  return findings
743
743
 
744
744
 
745
- def detect_string_format_old(conn: sqlite3.Connection) -> list[dict]:
746
- """Find ``"..."`` ``.format(...)`` calls — prefer f-strings on Py3.6+.
747
-
748
- Conservative: only flag the explicit ``"literal".format(...)``
749
- pattern, not ``some_var.format(...)`` (which might be a template
750
- method on a non-string type).
751
- """
752
- findings: list[dict] = []
753
- for file_id, path in _python_files(conn):
754
- text = _file_text(conn, file_id)
755
- if text:
756
- text = _strip_strings_and_comments(text)
757
- if not text:
758
- continue
759
- # The string-strip blanks the literal, so we can't match
760
- # ``"...".format(``. Skip entirely — this detector is best
761
- # implemented on the raw text. Falls back to no-op when
762
- # text was stripped. Future: implement an "unstripped scan"
763
- # that's still safe.
764
- return findings # disabled until we have a string-aware variant
765
-
766
-
767
745
  def detect_async_with_missing(conn: sqlite3.Connection) -> list[dict]:
768
746
  """Find ``aiofiles.open(...)`` / ``httpx.AsyncClient(...)`` not
769
747
  inside an ``async with`` — async resource leak.
@@ -476,6 +476,72 @@ def _gather_batch(conn, resolved, task, session_hint, recent_symbols, use_propag
476
476
  # ---------------------------------------------------------------------------
477
477
 
478
478
 
479
+ def _render_async_badge(sym, row_keys) -> None:
480
+ """Pass 102 — single-line async badge above the signature."""
481
+ if "is_async" in row_keys and sym["is_async"]:
482
+ click.echo(" [async coroutine]")
483
+
484
+
485
+ def _render_idiom_badge(sym, sym_kind: str, decorators_str: str, row_keys) -> None:
486
+ """Pass 102 — surface model-class / fixture / param-test badges."""
487
+ try:
488
+ from roam.catalog.python_idioms import fixture_kind, is_model_class
489
+ except Exception:
490
+ return
491
+ if sym_kind == "class":
492
+ sig_text = sym["signature"] if "signature" in row_keys else ""
493
+ try:
494
+ is_model, kind_label = is_model_class(sig_text, decorators_str)
495
+ except Exception:
496
+ return
497
+ if is_model and kind_label:
498
+ click.echo(f" [{kind_label} model]")
499
+ elif sym_kind in ("function", "method"):
500
+ try:
501
+ fkind = fixture_kind(decorators_str)
502
+ except Exception:
503
+ return
504
+ if fkind:
505
+ click.echo(f" [{fkind}]")
506
+
507
+
508
+ def _split_decorators_paren_aware(decorators_str: str) -> list[str]:
509
+ """Pass 102 — paren-aware split.
510
+
511
+ ``@parametrize("a,b", [...])`` has commas inside its argument; the
512
+ naive ``str.split(",")`` would break it into fragments. Track
513
+ bracket depth so we only split on top-level commas.
514
+ """
515
+ decos: list[str] = []
516
+ depth = 0
517
+ current: list[str] = []
518
+ for ch in decorators_str:
519
+ if ch == "," and depth == 0:
520
+ if current:
521
+ decos.append("".join(current).strip())
522
+ current = []
523
+ else:
524
+ current.append(ch)
525
+ if ch in "([{":
526
+ depth += 1
527
+ elif ch in ")]}":
528
+ depth = max(0, depth - 1)
529
+ if current:
530
+ decos.append("".join(current).strip())
531
+ return decos
532
+
533
+
534
+ def _render_decorators_block(decorators_str: str) -> None:
535
+ """Pass 102 — print up to 5 decorator first-lines for the symbol."""
536
+ if not decorators_str:
537
+ return
538
+ for d in _split_decorators_paren_aware(decorators_str)[:5]:
539
+ first_line = d.splitlines()[0] if d else ""
540
+ if len(d.splitlines()) > 1:
541
+ first_line += "..."
542
+ click.echo(f" {first_line}")
543
+
544
+
479
545
  def _render_text(data):
480
546
  """Print text output for any mode."""
481
547
  mode = data["mode"]
@@ -553,60 +619,13 @@ def _render_single_text(data):
553
619
  click.echo(f"VERDICT: {verdict}")
554
620
  click.echo()
555
621
  click.echo(f"=== Context for: {sym['name']}{task_suffix} ===")
556
- # Python pivot v12.4: surface async + decorators above the
557
- # signature so agents reading context know coroutine semantics
558
- # without scanning source. ``sym`` is a sqlite3.Row which doesn't
559
- # expose ``.get`` — guard each access with a key check.
622
+ # Pass 102 header rendering extracted into helpers.
560
623
  _row_keys = sym.keys() if hasattr(sym, "keys") else []
561
- if "is_async" in _row_keys and sym["is_async"]:
562
- click.echo(" [async coroutine]")
563
624
  decorators_str = (sym["decorators"] if "decorators" in _row_keys else "") or ""
564
- # Python pivot v12.4-iter: model-class + fixture badges — agents
565
- # reading context immediately see whether this is "data with
566
- # validation" (Pydantic/dataclass/attrs/etc.) or a pytest fixture
567
- # / parametrized test, without scanning source.
568
625
  sym_kind = sym["kind"] if "kind" in _row_keys else ""
569
- try:
570
- from roam.catalog.python_idioms import fixture_kind, is_model_class
571
-
572
- if sym_kind == "class":
573
- sig_text = sym["signature"] if "signature" in _row_keys else ""
574
- is_model, kind_label = is_model_class(sig_text, decorators_str)
575
- if is_model and kind_label:
576
- click.echo(f" [{kind_label} model]")
577
- elif sym_kind in ("function", "method"):
578
- fkind = fixture_kind(decorators_str)
579
- if fkind:
580
- click.echo(f" [{fkind}]")
581
- except Exception:
582
- pass
583
- if decorators_str:
584
- # Decorators are comma-joined but ``@parametrize("a,b,c", [...])``
585
- # has commas inside its arguments — naive split breaks the
586
- # display into nonsense fragments. Re-tokenise paren-aware.
587
- decos: list[str] = []
588
- depth = 0
589
- current = []
590
- for ch in decorators_str:
591
- if ch == "," and depth == 0:
592
- if current:
593
- decos.append("".join(current).strip())
594
- current = []
595
- else:
596
- current.append(ch)
597
- if ch in "([{":
598
- depth += 1
599
- elif ch in ")]}":
600
- depth = max(0, depth - 1)
601
- if current:
602
- decos.append("".join(current).strip())
603
- for d in decos[:5]:
604
- # Show the first line of the decorator only — keeps
605
- # multi-line decorators (e.g. click.option blocks) compact.
606
- first_line = d.splitlines()[0] if d else ""
607
- if len(d.splitlines()) > 1:
608
- first_line += "..."
609
- click.echo(f" {first_line}")
626
+ _render_async_badge(sym, _row_keys)
627
+ _render_idiom_badge(sym, sym_kind, decorators_str, _row_keys)
628
+ _render_decorators_block(decorators_str)
610
629
  click.echo(
611
630
  f"{abbrev_kind(sym['kind'])} "
612
631
  f"{sym['qualified_name'] or sym['name']}"
@@ -1057,113 +1057,130 @@ def _extended_summary(extended_data):
1057
1057
  }
1058
1058
 
1059
1059
 
1060
- def _analyze_dataflow_dead(conn):
1061
- """Analyze dataflow-based dead code patterns using taint summaries.
1060
+ def _table_exists(conn, name: str) -> bool:
1061
+ """Pass 106 small probe used by the dataflow analyzer.
1062
1062
 
1063
- Returns list of findings: [{type, symbol, file, line, reason, confidence, call_sites}]
1063
+ Replaces the ``try: SELECT ... LIMIT 0; except: pass`` pattern
1064
+ repeated 3 times in the original ``_analyze_dataflow_dead``.
1064
1065
  """
1065
- findings = []
1066
- project_root = find_project_root()
1067
-
1068
- # Check if required tables exist
1069
1066
  try:
1070
- conn.execute("SELECT 1 FROM taint_summaries LIMIT 0")
1067
+ conn.execute(f"SELECT 1 FROM {name} LIMIT 0")
1068
+ return True
1071
1069
  except Exception:
1072
- return findings
1070
+ return False
1073
1071
 
1074
- has_effects = True
1075
- try:
1076
- conn.execute("SELECT 1 FROM symbol_effects LIMIT 0")
1077
- except Exception:
1078
- has_effects = False
1079
1072
 
1080
- has_metrics = True
1081
- try:
1082
- conn.execute("SELECT 1 FROM symbol_metrics LIMIT 0")
1083
- except Exception:
1084
- has_metrics = False
1073
+ def _read_caller_line(project_root, file_cache: dict, file_path: str, line_no: int) -> str | None:
1074
+ """Pass 106 — return the caller's source line text or ``None`` on miss."""
1075
+ if file_path not in file_cache:
1076
+ try:
1077
+ file_cache[file_path] = (
1078
+ (project_root / file_path).read_text(encoding="utf-8", errors="replace").splitlines()
1079
+ )
1080
+ except Exception:
1081
+ file_cache[file_path] = []
1082
+ lines = file_cache.get(file_path, [])
1083
+ if 0 < line_no <= len(lines):
1084
+ return lines[line_no - 1].strip()
1085
+ return None
1085
1086
 
1086
- # A. Unused Return Values
1087
- if has_metrics:
1088
- funcs_with_return = conn.execute(
1089
- "SELECT s.id, s.name, COALESCE(s.qualified_name, s.name) AS qname, "
1090
- "f.path AS file_path, s.line_start, sm.return_count "
1091
- "FROM symbols s "
1092
- "JOIN files f ON s.file_id = f.id "
1093
- "JOIN symbol_metrics sm ON s.id = sm.symbol_id "
1094
- "WHERE sm.return_count > 0 "
1095
- " AND s.kind IN ('function', 'method')"
1096
- ).fetchall()
1097
1087
 
1098
- file_cache: dict[str, list[str]] = {}
1088
+ def _is_return_captured(line_text: str, func_name: str) -> bool:
1089
+ """Pass 106 — `<var> = func(...)` captures, but `== func()` does not."""
1090
+ if func_name not in line_text:
1091
+ return False
1092
+ prefix = line_text.split(func_name)[0]
1093
+ return bool(re.search(r"[A-Za-z_]\w*\s*=(?!=)", prefix))
1099
1094
 
1100
- # Pre-load callers for every candidate in one batched scan to
1101
- # avoid an N+1 against the edges table (one SELECT per function).
1102
- callers_by_target: dict[int, list] = {}
1103
- if funcs_with_return:
1104
- from roam.db.connection import batched_in
1105
1095
 
1106
- target_ids = [f["id"] for f in funcs_with_return]
1107
- for row in batched_in(
1108
- conn,
1109
- "SELECT e.target_id, e.source_id, e.line, s.name AS caller_name, "
1110
- "f.path AS caller_file, s.line_start AS caller_start "
1111
- "FROM edges e "
1112
- "JOIN symbols s ON e.source_id = s.id "
1113
- "JOIN files f ON s.file_id = f.id "
1114
- "WHERE e.target_id IN ({ph}) AND e.kind = 'calls'",
1115
- target_ids,
1116
- ):
1117
- callers_by_target.setdefault(row["target_id"], []).append(row)
1118
-
1119
- for func in funcs_with_return:
1120
- callers = callers_by_target.get(func["id"], [])
1121
- if not callers:
1122
- continue
1096
+ def _detect_unused_returns(conn, project_root) -> list[dict]:
1097
+ """Pass 106 A. functions whose return value every caller discards."""
1098
+ findings: list[dict] = []
1099
+ funcs_with_return = conn.execute(
1100
+ "SELECT s.id, s.name, COALESCE(s.qualified_name, s.name) AS qname, "
1101
+ "f.path AS file_path, s.line_start, sm.return_count "
1102
+ "FROM symbols s "
1103
+ "JOIN files f ON s.file_id = f.id "
1104
+ "JOIN symbol_metrics sm ON s.id = sm.symbol_id "
1105
+ "WHERE sm.return_count > 0 "
1106
+ " AND s.kind IN ('function', 'method')"
1107
+ ).fetchall()
1108
+ if not funcs_with_return:
1109
+ return findings
1123
1110
 
1124
- # Check each call site
1125
- all_discard = True
1126
- call_site_info = []
1127
- for caller in callers:
1128
- call_line = caller["line"]
1129
- if not call_line:
1130
- all_discard = False
1131
- break
1132
- caller_file = caller["caller_file"]
1133
- if caller_file not in file_cache:
1134
- try:
1135
- fpath = project_root / caller_file
1136
- file_cache[caller_file] = fpath.read_text(encoding="utf-8", errors="replace").splitlines()
1137
- except Exception:
1138
- file_cache[caller_file] = []
1139
- lines = file_cache.get(caller_file, [])
1140
- if call_line <= len(lines):
1141
- line_text = lines[call_line - 1].strip()
1142
- # Check if return value is captured (= before function name, but not == or !=)
1143
- prefix = line_text.split(func["name"])[0] if func["name"] in line_text else ""
1144
- if re.search(r"[A-Za-z_]\w*\s*=(?!=)", prefix):
1145
- all_discard = False
1146
- break
1147
- call_site_info.append({"file": caller_file, "line": call_line, "caller": caller["caller_name"]})
1148
- else:
1149
- all_discard = False
1150
- break
1111
+ from roam.db.connection import batched_in
1151
1112
 
1152
- if all_discard and callers:
1153
- findings.append(
1154
- {
1155
- "type": "unused_return",
1156
- "symbol": func["qname"],
1157
- "file": func["file_path"],
1158
- "line": func["line_start"],
1159
- "reason": (f"return value of {func['qname']} is discarded by all {len(callers)} caller(s)"),
1160
- "confidence": 85,
1161
- "call_sites": call_site_info[:5],
1162
- }
1163
- )
1113
+ target_ids = [f["id"] for f in funcs_with_return]
1114
+ callers_by_target: dict[int, list] = {}
1115
+ for row in batched_in(
1116
+ conn,
1117
+ "SELECT e.target_id, e.source_id, e.line, s.name AS caller_name, "
1118
+ "f.path AS caller_file, s.line_start AS caller_start "
1119
+ "FROM edges e "
1120
+ "JOIN symbols s ON e.source_id = s.id "
1121
+ "JOIN files f ON s.file_id = f.id "
1122
+ "WHERE e.target_id IN ({ph}) AND e.kind = 'calls'",
1123
+ target_ids,
1124
+ ):
1125
+ callers_by_target.setdefault(row["target_id"], []).append(row)
1126
+
1127
+ file_cache: dict[str, list[str]] = {}
1128
+ for func in funcs_with_return:
1129
+ callers = callers_by_target.get(func["id"], [])
1130
+ if not callers:
1131
+ continue
1132
+ all_discard = True
1133
+ call_site_info: list[dict] = []
1134
+ for caller in callers:
1135
+ call_line = caller["line"]
1136
+ if not call_line:
1137
+ all_discard = False
1138
+ break
1139
+ line_text = _read_caller_line(project_root, file_cache, caller["caller_file"], call_line)
1140
+ if line_text is None:
1141
+ all_discard = False
1142
+ break
1143
+ if _is_return_captured(line_text, func["name"]):
1144
+ all_discard = False
1145
+ break
1146
+ call_site_info.append({"file": caller["caller_file"], "line": call_line, "caller": caller["caller_name"]})
1147
+ if all_discard and callers:
1148
+ findings.append(
1149
+ {
1150
+ "type": "unused_return",
1151
+ "symbol": func["qname"],
1152
+ "file": func["file_path"],
1153
+ "line": func["line_start"],
1154
+ "reason": f"return value of {func['qname']} is discarded by all {len(callers)} caller(s)",
1155
+ "confidence": 85,
1156
+ "call_sites": call_site_info[:5],
1157
+ }
1158
+ )
1159
+ return findings
1164
1160
 
1165
- # B. Dead Parameter Chains
1166
- param_rows = conn.execute(
1161
+
1162
+ def _parse_param_names(sig: str) -> list[str]:
1163
+ """Pass 106 — extract concrete parameter names from a signature string."""
1164
+ m = re.search(r"\(([^)]*)\)", sig or "")
1165
+ if not m:
1166
+ return []
1167
+ params_str = m.group(1).strip()
1168
+ if not params_str:
1169
+ return []
1170
+ out: list[str] = []
1171
+ for part in params_str.split(","):
1172
+ token = part.strip().split(":")[0].split("=")[0].strip()
1173
+ while token.startswith("*"):
1174
+ token = token[1:]
1175
+ if token and token not in ("self", "cls", "_"):
1176
+ out.append(token)
1177
+ return out
1178
+
1179
+
1180
+ def _detect_dead_param_chains(conn) -> list[dict]:
1181
+ """Pass 106 — B. parameters with no return / sink dataflow effect."""
1182
+ findings: list[dict] = []
1183
+ rows = conn.execute(
1167
1184
  "SELECT ts.symbol_id, ts.param_taints_return, ts.param_to_sink, "
1168
1185
  "s.name, COALESCE(s.qualified_name, s.name) AS qname, "
1169
1186
  "s.signature, f.path AS file_path, s.line_start "
@@ -1173,85 +1190,94 @@ def _analyze_dataflow_dead(conn):
1173
1190
  "WHERE ts.is_sanitizer = 0 "
1174
1191
  " AND s.kind IN ('function', 'method')"
1175
1192
  ).fetchall()
1176
-
1177
- for row in param_rows:
1193
+ for row in rows:
1178
1194
  try:
1179
1195
  ptr = json.loads(row["param_taints_return"] or "{}")
1180
1196
  pts = json.loads(row["param_to_sink"] or "{}")
1181
1197
  except Exception:
1182
1198
  continue
1199
+ param_names = _parse_param_names(row["signature"])
1200
+ for idx, pname in enumerate(param_names):
1201
+ sidx = str(idx)
1202
+ if ptr.get(sidx, False) or bool(pts.get(sidx)):
1203
+ continue
1204
+ findings.append(
1205
+ {
1206
+ "type": "dead_param_chain",
1207
+ "symbol": row["qname"],
1208
+ "file": row["file_path"],
1209
+ "line": row["line_start"],
1210
+ "variable": pname,
1211
+ "reason": (
1212
+ f"parameter '{pname}' of {row['qname']} has no dataflow effect (not returned, not used in sink)"
1213
+ ),
1214
+ "confidence": 75,
1215
+ "call_sites": [],
1216
+ }
1217
+ )
1218
+ return findings
1219
+
1183
1220
 
1184
- # Parse param names from signature
1185
- sig = row["signature"] or ""
1186
- m = re.search(r"\(([^)]*)\)", sig)
1187
- if not m:
1221
+ def _detect_side_effect_only(conn, unused_return_findings: list[dict]) -> list[dict]:
1222
+ """Pass 106 — C. discard-return funcs whose only effects are pure/logging."""
1223
+ findings: list[dict] = []
1224
+ benign = {"pure", "logging"}
1225
+ for f in unused_return_findings:
1226
+ if f["type"] != "unused_return":
1188
1227
  continue
1189
- params_str = m.group(1).strip()
1190
- if not params_str:
1228
+ sym_id_row = conn.execute(
1229
+ "SELECT id FROM symbols WHERE qualified_name = ? OR name = ? LIMIT 1",
1230
+ (f["symbol"], f["symbol"]),
1231
+ ).fetchone()
1232
+ if not sym_id_row:
1191
1233
  continue
1192
- param_names = []
1193
- for part in params_str.split(","):
1194
- token = part.strip().split(":")[0].split("=")[0].strip()
1195
- while token.startswith("*"):
1196
- token = token[1:]
1197
- if token and token not in ("self", "cls", "_"):
1198
- param_names.append(token)
1234
+ sym_id = sym_id_row["id"]
1235
+ effects = conn.execute(
1236
+ "SELECT DISTINCT effect_type FROM symbol_effects WHERE symbol_id = ?",
1237
+ (sym_id,),
1238
+ ).fetchall()
1239
+ effect_types = {e["effect_type"] for e in effects}
1240
+ if effect_types and effect_types <= benign:
1241
+ findings.append(
1242
+ {
1243
+ "type": "side_effect_only",
1244
+ "symbol": f["symbol"],
1245
+ "file": f["file"],
1246
+ "line": f["line"],
1247
+ "reason": (
1248
+ f"{f['symbol']} has only {'/'.join(sorted(effect_types))} effects "
1249
+ "and return is always discarded"
1250
+ ),
1251
+ "confidence": 70,
1252
+ "call_sites": f.get("call_sites", []),
1253
+ }
1254
+ )
1255
+ return findings
1199
1256
 
1200
- for idx, pname in enumerate(param_names):
1201
- sidx = str(idx)
1202
- has_return_effect = ptr.get(sidx, False)
1203
- has_sink_effect = bool(pts.get(sidx))
1204
- if not has_return_effect and not has_sink_effect:
1205
- findings.append(
1206
- {
1207
- "type": "dead_param_chain",
1208
- "symbol": row["qname"],
1209
- "file": row["file_path"],
1210
- "line": row["line_start"],
1211
- "variable": pname,
1212
- "reason": (
1213
- f"parameter '{pname}' of {row['qname']} has no dataflow effect "
1214
- f"(not returned, not used in sink)"
1215
- ),
1216
- "confidence": 75,
1217
- "call_sites": [],
1218
- }
1219
- )
1220
1257
 
1221
- # C. Side-Effect-Only Functions
1258
+ def _analyze_dataflow_dead(conn):
1259
+ """Analyze dataflow-based dead code patterns using taint summaries.
1260
+
1261
+ Returns list of findings: ``[{type, symbol, file, line, reason,
1262
+ confidence, call_sites}]``.
1263
+
1264
+ Pass 106 — orchestrator only; per-pattern logic moved into
1265
+ ``_detect_unused_returns`` / ``_detect_dead_param_chains`` /
1266
+ ``_detect_side_effect_only``. Cognitive complexity dropped from
1267
+ 160 to ~10.
1268
+ """
1269
+ if not _table_exists(conn, "taint_summaries"):
1270
+ return []
1271
+ has_effects = _table_exists(conn, "symbol_effects")
1272
+ has_metrics = _table_exists(conn, "symbol_metrics")
1273
+ project_root = find_project_root()
1274
+
1275
+ findings: list[dict] = []
1276
+ if has_metrics:
1277
+ findings.extend(_detect_unused_returns(conn, project_root))
1278
+ findings.extend(_detect_dead_param_chains(conn))
1222
1279
  if has_effects:
1223
- # Find functions where all callers discard return AND effects are only logging/pure
1224
- for f in findings:
1225
- if f["type"] != "unused_return":
1226
- continue
1227
- sym_id_row = conn.execute(
1228
- "SELECT id FROM symbols WHERE qualified_name = ? OR name = ? LIMIT 1",
1229
- (f["symbol"], f["symbol"]),
1230
- ).fetchone()
1231
- if not sym_id_row:
1232
- continue
1233
- sym_id = sym_id_row["id"]
1234
- effects = conn.execute(
1235
- "SELECT DISTINCT effect_type FROM symbol_effects WHERE symbol_id = ?",
1236
- (sym_id,),
1237
- ).fetchall()
1238
- effect_types = {e["effect_type"] for e in effects}
1239
- benign = {"pure", "logging"}
1240
- if effect_types and effect_types <= benign:
1241
- findings.append(
1242
- {
1243
- "type": "side_effect_only",
1244
- "symbol": f["symbol"],
1245
- "file": f["file"],
1246
- "line": f["line"],
1247
- "reason": (
1248
- f"{f['symbol']} has only {'/'.join(sorted(effect_types))} effects "
1249
- f"and return is always discarded"
1250
- ),
1251
- "confidence": 70,
1252
- "call_sites": f.get("call_sites", []),
1253
- }
1254
- )
1280
+ findings.extend(_detect_side_effect_only(conn, findings))
1255
1281
 
1256
1282
  findings.sort(key=lambda f: (-f["confidence"], f["file"], f.get("line") or 0))
1257
1283
  return findings
@@ -263,8 +263,15 @@ def _check_command_registry() -> dict:
263
263
  removed or renamed leaves the registry mismatch undetected until an
264
264
  agent calls it. Doctor runs the lazy-import for every entry up front.
265
265
  """
266
+ # Pass 104 — break the static cycle (cli ↔ cmd_doctor) by loading
267
+ # ``roam.cli`` via importlib at runtime. This is the only static
268
+ # edge that connected back to cli; the doctor's safety check still
269
+ # verifies every registered command imports.
266
270
  try:
267
- from roam.cli import _COMMANDS
271
+ import importlib
272
+
273
+ cli_mod = importlib.import_module("roam.cli")
274
+ _COMMANDS = cli_mod._COMMANDS
268
275
  except Exception as exc:
269
276
  return {
270
277
  "name": "CLI command registry",
@@ -49,6 +49,14 @@ _UTILITY_PATH_PATTERNS = (
49
49
  "common/",
50
50
  "internal/",
51
51
  "infra/",
52
+ # Pass 105 — infrastructure hubs that are EXPECTED to have high
53
+ # fan-in. Without these patterns the health-score classifier
54
+ # mislabels architectural roots (Click root group, MCP dispatch,
55
+ # graph builder, file-role classifier) as actionable refactor
56
+ # targets, which they are not.
57
+ "graph/",
58
+ "mcp_extras/",
59
+ "languages/",
52
60
  )
53
61
 
54
62
  _UTILITY_FILE_PATTERNS = (
@@ -56,6 +64,11 @@ _UTILITY_FILE_PATTERNS = (
56
64
  "helpers.py",
57
65
  "common.py",
58
66
  "base.py",
67
+ # Pass 105 — single-file architectural hubs. Same reasoning as
68
+ # ``_UTILITY_PATH_PATTERNS`` additions above.
69
+ "cli.py",
70
+ "mcp_server.py",
71
+ "file_roles.py",
59
72
  )
60
73
 
61
74
  # Paths that are NOT production code — treat as expected utilities