roam-code 12.26__tar.gz → 12.26.1__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 (644) hide show
  1. {roam_code-12.26/src/roam_code.egg-info → roam_code-12.26.1}/PKG-INFO +1 -1
  2. {roam_code-12.26 → roam_code-12.26.1}/pyproject.toml +1 -1
  3. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_audit_trail_conformance.py +69 -2
  4. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_metrics_push.py +75 -62
  5. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_pr_analyze.py +160 -87
  6. {roam_code-12.26 → roam_code-12.26.1/src/roam_code.egg-info}/PKG-INFO +1 -1
  7. {roam_code-12.26 → roam_code-12.26.1}/src/roam_code.egg-info/SOURCES.txt +1 -0
  8. {roam_code-12.26 → roam_code-12.26.1}/tests/test_audit_trail_conformance.py +69 -0
  9. roam_code-12.26.1/tests/test_v2_integration.py +237 -0
  10. {roam_code-12.26 → roam_code-12.26.1}/LICENSE +0 -0
  11. {roam_code-12.26 → roam_code-12.26.1}/README.md +0 -0
  12. {roam_code-12.26 → roam_code-12.26.1}/setup.cfg +0 -0
  13. {roam_code-12.26 → roam_code-12.26.1}/src/roam/__init__.py +0 -0
  14. {roam_code-12.26 → roam_code-12.26.1}/src/roam/__main__.py +0 -0
  15. {roam_code-12.26 → roam_code-12.26.1}/src/roam/analysis/__init__.py +0 -0
  16. {roam_code-12.26 → roam_code-12.26.1}/src/roam/analysis/effects.py +0 -0
  17. {roam_code-12.26 → roam_code-12.26.1}/src/roam/analysis/taint.py +0 -0
  18. {roam_code-12.26 → roam_code-12.26.1}/src/roam/api.py +0 -0
  19. {roam_code-12.26 → roam_code-12.26.1}/src/roam/ask/__init__.py +0 -0
  20. {roam_code-12.26 → roam_code-12.26.1}/src/roam/ask/classifier.py +0 -0
  21. {roam_code-12.26 → roam_code-12.26.1}/src/roam/ask/recipes.py +0 -0
  22. {roam_code-12.26 → roam_code-12.26.1}/src/roam/ask/runner.py +0 -0
  23. {roam_code-12.26 → roam_code-12.26.1}/src/roam/ask/workflow.py +0 -0
  24. {roam_code-12.26 → roam_code-12.26.1}/src/roam/attest/__init__.py +0 -0
  25. {roam_code-12.26 → roam_code-12.26.1}/src/roam/attest/cga.py +0 -0
  26. {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/__init__.py +0 -0
  27. {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/base.py +0 -0
  28. {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/bridge_config.py +0 -0
  29. {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/bridge_django.py +0 -0
  30. {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/bridge_protobuf.py +0 -0
  31. {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/bridge_rest_api.py +0 -0
  32. {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/bridge_salesforce.py +0 -0
  33. {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/bridge_template.py +0 -0
  34. {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/registry.py +0 -0
  35. {roam_code-12.26 → roam_code-12.26.1}/src/roam/catalog/__init__.py +0 -0
  36. {roam_code-12.26 → roam_code-12.26.1}/src/roam/catalog/detectors.py +0 -0
  37. {roam_code-12.26 → roam_code-12.26.1}/src/roam/catalog/fixes.py +0 -0
  38. {roam_code-12.26 → roam_code-12.26.1}/src/roam/catalog/python_idioms.py +0 -0
  39. {roam_code-12.26 → roam_code-12.26.1}/src/roam/catalog/smells.py +0 -0
  40. {roam_code-12.26 → roam_code-12.26.1}/src/roam/catalog/tasks.py +0 -0
  41. {roam_code-12.26 → roam_code-12.26.1}/src/roam/cli.py +0 -0
  42. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/__init__.py +0 -0
  43. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/audit_trail_helpers.py +0 -0
  44. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/changed_files.py +0 -0
  45. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_adrs.py +0 -0
  46. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_adversarial.py +0 -0
  47. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_affected.py +0 -0
  48. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_affected_tests.py +0 -0
  49. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_agent_context.py +0 -0
  50. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_agent_export.py +0 -0
  51. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_agent_plan.py +0 -0
  52. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_ai_ratio.py +0 -0
  53. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_ai_readiness.py +0 -0
  54. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_alerts.py +0 -0
  55. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_annotate.py +0 -0
  56. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_api.py +0 -0
  57. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_api_changes.py +0 -0
  58. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_api_drift.py +0 -0
  59. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_ask.py +0 -0
  60. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_attest.py +0 -0
  61. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_audit.py +0 -0
  62. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_audit_trail_export.py +0 -0
  63. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_audit_trail_verify.py +0 -0
  64. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_auth_gaps.py +0 -0
  65. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_bisect.py +0 -0
  66. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_breaking.py +0 -0
  67. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_budget.py +0 -0
  68. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_bus_factor.py +0 -0
  69. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_capsule.py +0 -0
  70. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_cga.py +0 -0
  71. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_changelog.py +0 -0
  72. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_check_rules.py +0 -0
  73. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_ci_setup.py +0 -0
  74. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_clean.py +0 -0
  75. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_clones.py +0 -0
  76. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_closure.py +0 -0
  77. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_clusters.py +0 -0
  78. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_codeowners.py +0 -0
  79. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_complexity.py +0 -0
  80. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_config.py +0 -0
  81. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_congestion.py +0 -0
  82. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_context.py +0 -0
  83. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_conventions.py +0 -0
  84. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_coupling.py +0 -0
  85. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_coverage_gaps.py +0 -0
  86. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_critique.py +0 -0
  87. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_cut.py +0 -0
  88. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_dark_matter.py +0 -0
  89. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_dashboard.py +0 -0
  90. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_dead.py +0 -0
  91. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_debt.py +0 -0
  92. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_deps.py +0 -0
  93. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_describe.py +0 -0
  94. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_dev_profile.py +0 -0
  95. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_diagnose.py +0 -0
  96. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_diff.py +0 -0
  97. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_disambiguate.py +0 -0
  98. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_doc_staleness.py +0 -0
  99. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_docs_coverage.py +0 -0
  100. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_doctor.py +0 -0
  101. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_dogfood.py +0 -0
  102. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_drift.py +0 -0
  103. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_duplicates.py +0 -0
  104. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_effects.py +0 -0
  105. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_endpoints.py +0 -0
  106. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_entry_points.py +0 -0
  107. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_eval_retrieve.py +0 -0
  108. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_exit_codes.py +0 -0
  109. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_fan.py +0 -0
  110. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_file.py +0 -0
  111. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_fingerprint.py +0 -0
  112. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_fitness.py +0 -0
  113. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_flag_dead.py +0 -0
  114. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_fleet.py +0 -0
  115. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_fn_coupling.py +0 -0
  116. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_forecast.py +0 -0
  117. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_graph_export.py +0 -0
  118. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_graph_stats.py +0 -0
  119. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_grep.py +0 -0
  120. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_guard.py +0 -0
  121. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_health.py +0 -0
  122. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_help_search.py +0 -0
  123. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_hooks.py +0 -0
  124. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_hotspots.py +0 -0
  125. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_hover.py +0 -0
  126. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_impact.py +0 -0
  127. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_index.py +0 -0
  128. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_index_bundle.py +0 -0
  129. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_index_stats.py +0 -0
  130. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_ingest_trace.py +0 -0
  131. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_init.py +0 -0
  132. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_intent.py +0 -0
  133. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_invariants.py +0 -0
  134. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_layers.py +0 -0
  135. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_map.py +0 -0
  136. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_math.py +0 -0
  137. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_mcp_setup.py +0 -0
  138. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_mcp_status.py +0 -0
  139. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_metrics.py +0 -0
  140. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_migration_safety.py +0 -0
  141. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_minimap.py +0 -0
  142. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_missing_index.py +0 -0
  143. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_module.py +0 -0
  144. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_mutate.py +0 -0
  145. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_n1.py +0 -0
  146. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_oracle.py +0 -0
  147. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_orchestrate.py +0 -0
  148. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_orphan_imports.py +0 -0
  149. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_orphan_routes.py +0 -0
  150. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_over_fetch.py +0 -0
  151. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_owner.py +0 -0
  152. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_partition.py +0 -0
  153. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_path_coverage.py +0 -0
  154. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_patterns.py +0 -0
  155. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_plan.py +0 -0
  156. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_plan_refactor.py +0 -0
  157. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_plugins.py +0 -0
  158. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_pr_comment_render.py +0 -0
  159. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_pr_diff.py +0 -0
  160. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_pr_prep.py +0 -0
  161. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_pr_risk.py +0 -0
  162. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_pre_commit.py +0 -0
  163. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_preflight.py +0 -0
  164. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_py_modern.py +0 -0
  165. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_py_types.py +0 -0
  166. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_pytest_fixtures.py +0 -0
  167. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_recipes.py +0 -0
  168. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_recommend.py +0 -0
  169. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_relate.py +0 -0
  170. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_report.py +0 -0
  171. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_reset.py +0 -0
  172. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_retrieve.py +0 -0
  173. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_risk.py +0 -0
  174. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_rules.py +0 -0
  175. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_rules_validate.py +0 -0
  176. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_safe_delete.py +0 -0
  177. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_safe_zones.py +0 -0
  178. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_sbom.py +0 -0
  179. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_schema.py +0 -0
  180. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_search.py +0 -0
  181. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_search_semantic.py +0 -0
  182. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_secrets.py +0 -0
  183. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_semantic_diff.py +0 -0
  184. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_simulate.py +0 -0
  185. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_simulate_departure.py +0 -0
  186. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_sketch.py +0 -0
  187. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_smells.py +0 -0
  188. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_spectral.py +0 -0
  189. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_split.py +0 -0
  190. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_stats.py +0 -0
  191. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_suggest_refactoring.py +0 -0
  192. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_suggest_reviewers.py +0 -0
  193. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_supply_chain.py +0 -0
  194. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_symbol.py +0 -0
  195. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_syntax_check.py +0 -0
  196. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_taint.py +0 -0
  197. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_telemetry.py +0 -0
  198. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_test_gaps.py +0 -0
  199. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_test_impact.py +0 -0
  200. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_test_pyramid.py +0 -0
  201. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_test_scaffold.py +0 -0
  202. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_testmap.py +0 -0
  203. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_timeline.py +0 -0
  204. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_tour.py +0 -0
  205. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_trace.py +0 -0
  206. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_trends.py +0 -0
  207. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_triage.py +0 -0
  208. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_understand.py +0 -0
  209. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_uses.py +0 -0
  210. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_verify.py +0 -0
  211. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_verify_imports.py +0 -0
  212. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_version.py +0 -0
  213. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_vibe_check.py +0 -0
  214. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_visualize.py +0 -0
  215. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_vuln_map.py +0 -0
  216. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_vuln_reach.py +0 -0
  217. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_vulns.py +0 -0
  218. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_watch.py +0 -0
  219. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_weather.py +0 -0
  220. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_why.py +0 -0
  221. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_why_fail.py +0 -0
  222. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_workflow.py +0 -0
  223. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_ws.py +0 -0
  224. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_xlang.py +0 -0
  225. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/codeowners_helpers.py +0 -0
  226. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/context_helpers.py +0 -0
  227. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/gate_presets.py +0 -0
  228. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/git_helpers.py +0 -0
  229. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/graph_helpers.py +0 -0
  230. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/metrics_history.py +0 -0
  231. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/next_steps.py +0 -0
  232. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/resolve.py +0 -0
  233. {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/suppression.py +0 -0
  234. {roam_code-12.26 → roam_code-12.26.1}/src/roam/competitor_site_data.py +0 -0
  235. {roam_code-12.26 → roam_code-12.26.1}/src/roam/config.py +0 -0
  236. {roam_code-12.26 → roam_code-12.26.1}/src/roam/coverage_reports.py +0 -0
  237. {roam_code-12.26 → roam_code-12.26.1}/src/roam/critique/__init__.py +0 -0
  238. {roam_code-12.26 → roam_code-12.26.1}/src/roam/critique/aggregator.py +0 -0
  239. {roam_code-12.26 → roam_code-12.26.1}/src/roam/critique/checks.py +0 -0
  240. {roam_code-12.26 → roam_code-12.26.1}/src/roam/db/__init__.py +0 -0
  241. {roam_code-12.26 → roam_code-12.26.1}/src/roam/db/connection.py +0 -0
  242. {roam_code-12.26 → roam_code-12.26.1}/src/roam/db/queries.py +0 -0
  243. {roam_code-12.26 → roam_code-12.26.1}/src/roam/db/schema.py +0 -0
  244. {roam_code-12.26 → roam_code-12.26.1}/src/roam/eval/__init__.py +0 -0
  245. {roam_code-12.26 → roam_code-12.26.1}/src/roam/eval/harness.py +0 -0
  246. {roam_code-12.26 → roam_code-12.26.1}/src/roam/exit_codes.py +0 -0
  247. {roam_code-12.26 → roam_code-12.26.1}/src/roam/fleet/__init__.py +0 -0
  248. {roam_code-12.26 → roam_code-12.26.1}/src/roam/fleet/adapters.py +0 -0
  249. {roam_code-12.26 → roam_code-12.26.1}/src/roam/fleet/manifest.py +0 -0
  250. {roam_code-12.26 → roam_code-12.26.1}/src/roam/git_utils.py +0 -0
  251. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/__init__.py +0 -0
  252. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/anomaly.py +0 -0
  253. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/builder.py +0 -0
  254. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/clone_detect.py +0 -0
  255. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/clusters.py +0 -0
  256. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/cycles.py +0 -0
  257. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/dark_matter.py +0 -0
  258. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/diff.py +0 -0
  259. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/fingerprint.py +0 -0
  260. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/layers.py +0 -0
  261. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/pagerank.py +0 -0
  262. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/partition.py +0 -0
  263. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/pathfinding.py +0 -0
  264. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/propagation.py +0 -0
  265. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/simulate.py +0 -0
  266. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/spectral.py +0 -0
  267. {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/stats.py +0 -0
  268. {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/__init__.py +0 -0
  269. {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/complexity.py +0 -0
  270. {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/discovery.py +0 -0
  271. {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/django_post.py +0 -0
  272. {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/file_roles.py +0 -0
  273. {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/git_stats.py +0 -0
  274. {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/gitignore.py +0 -0
  275. {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/incremental.py +0 -0
  276. {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/indexer.py +0 -0
  277. {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/parser.py +0 -0
  278. {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/pytest_fixtures.py +0 -0
  279. {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/registry_dispatch.py +0 -0
  280. {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/relations.py +0 -0
  281. {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/symbols.py +0 -0
  282. {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/test_conventions.py +0 -0
  283. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/__init__.py +0 -0
  284. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/apex_lang.py +0 -0
  285. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/aura_lang.py +0 -0
  286. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/base.py +0 -0
  287. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/c_lang.py +0 -0
  288. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/csharp_lang.py +0 -0
  289. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/extractor_schema.py +0 -0
  290. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/extractors/kotlin.yaml +0 -0
  291. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/foxpro_lang.py +0 -0
  292. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/generic_lang.py +0 -0
  293. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/go_lang.py +0 -0
  294. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/hcl_lang.py +0 -0
  295. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/java_lang.py +0 -0
  296. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/javascript_lang.py +0 -0
  297. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/kotlin_lang.py +0 -0
  298. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/php_lang.py +0 -0
  299. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/python_lang.py +0 -0
  300. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/query_engine.py +0 -0
  301. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/registry.py +0 -0
  302. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/ruby_lang.py +0 -0
  303. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/rust_lang.py +0 -0
  304. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/scala_lang.py +0 -0
  305. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/sfxml_lang.py +0 -0
  306. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/sql_lang.py +0 -0
  307. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/swift_lang.py +0 -0
  308. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/typescript_lang.py +0 -0
  309. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/visualforce_lang.py +0 -0
  310. {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/yaml_lang.py +0 -0
  311. {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp-server-card.json +0 -0
  312. {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_extras/__init__.py +0 -0
  313. {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_extras/completions.py +0 -0
  314. {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_extras/concurrency.py +0 -0
  315. {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_extras/progress.py +0 -0
  316. {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_extras/sampling.py +0 -0
  317. {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_extras/session.py +0 -0
  318. {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_extras/watcher.py +0 -0
  319. {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_server.py +0 -0
  320. {roam_code-12.26 → roam_code-12.26.1}/src/roam/observability.py +0 -0
  321. {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/__init__.py +0 -0
  322. {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/confidence.py +0 -0
  323. {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/errors.py +0 -0
  324. {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/file_role_hints.py +0 -0
  325. {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/formatter.py +0 -0
  326. {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/framework_filter.py +0 -0
  327. {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/mermaid.py +0 -0
  328. {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/project_shape.py +0 -0
  329. {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/sarif.py +0 -0
  330. {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/schema_registry.py +0 -0
  331. {roam_code-12.26 → roam_code-12.26.1}/src/roam/plugins.py +0 -0
  332. {roam_code-12.26 → roam_code-12.26.1}/src/roam/refactor/__init__.py +0 -0
  333. {roam_code-12.26 → roam_code-12.26.1}/src/roam/refactor/codegen.py +0 -0
  334. {roam_code-12.26 → roam_code-12.26.1}/src/roam/refactor/transforms.py +0 -0
  335. {roam_code-12.26 → roam_code-12.26.1}/src/roam/retrieve/__init__.py +0 -0
  336. {roam_code-12.26 → roam_code-12.26.1}/src/roam/retrieve/learned_ranker.py +0 -0
  337. {roam_code-12.26 → roam_code-12.26.1}/src/roam/retrieve/pipeline.py +0 -0
  338. {roam_code-12.26 → roam_code-12.26.1}/src/roam/retrieve/rerank.py +0 -0
  339. {roam_code-12.26 → roam_code-12.26.1}/src/roam/retrieve/seeds.py +0 -0
  340. {roam_code-12.26 → roam_code-12.26.1}/src/roam/retrieve/semantic.py +0 -0
  341. {roam_code-12.26 → roam_code-12.26.1}/src/roam/rules/__init__.py +0 -0
  342. {roam_code-12.26 → roam_code-12.26.1}/src/roam/rules/ast_match.py +0 -0
  343. {roam_code-12.26 → roam_code-12.26.1}/src/roam/rules/builtin.py +0 -0
  344. {roam_code-12.26 → roam_code-12.26.1}/src/roam/rules/dataflow.py +0 -0
  345. {roam_code-12.26 → roam_code-12.26.1}/src/roam/rules/engine.py +0 -0
  346. {roam_code-12.26 → roam_code-12.26.1}/src/roam/runtime/__init__.py +0 -0
  347. {roam_code-12.26 → roam_code-12.26.1}/src/roam/runtime/daemon.py +0 -0
  348. {roam_code-12.26 → roam_code-12.26.1}/src/roam/runtime/graph_backend.py +0 -0
  349. {roam_code-12.26 → roam_code-12.26.1}/src/roam/runtime/hotspots.py +0 -0
  350. {roam_code-12.26 → roam_code-12.26.1}/src/roam/runtime/lock_modes.py +0 -0
  351. {roam_code-12.26 → roam_code-12.26.1}/src/roam/runtime/lockmgr.py +0 -0
  352. {roam_code-12.26 → roam_code-12.26.1}/src/roam/runtime/trace_ingest.py +0 -0
  353. {roam_code-12.26 → roam_code-12.26.1}/src/roam/search/__init__.py +0 -0
  354. {roam_code-12.26 → roam_code-12.26.1}/src/roam/search/framework_packs.py +0 -0
  355. {roam_code-12.26 → roam_code-12.26.1}/src/roam/search/index_embeddings.py +0 -0
  356. {roam_code-12.26 → roam_code-12.26.1}/src/roam/search/onnx_embeddings.py +0 -0
  357. {roam_code-12.26 → roam_code-12.26.1}/src/roam/search/tfidf.py +0 -0
  358. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/__init__.py +0 -0
  359. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/aibom_extension.py +0 -0
  360. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_classifier.py +0 -0
  361. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_engine.py +0 -0
  362. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/api_error_leak.yaml +0 -0
  363. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/java_fileupload_path_traversal.yaml +0 -0
  364. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/js_insecure_jwt_decode.yaml +0 -0
  365. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/js_localstorage_secrets.yaml +0 -0
  366. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/js_prototype_pollution.yaml +0 -0
  367. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/js_ssrf.yaml +0 -0
  368. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/js_xss.yaml +0 -0
  369. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/python_basic.yaml +0 -0
  370. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/python_deserialization.yaml +0 -0
  371. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/python_path_traversal.yaml +0 -0
  372. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/python_socketio_remote_source.yaml +0 -0
  373. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/python_sqli.yaml +0 -0
  374. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/python_urllib_open_redirect.yaml +0 -0
  375. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/vue_v_html.yaml +0 -0
  376. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/vuln_reach.py +0 -0
  377. {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/vuln_store.py +0 -0
  378. {roam_code-12.26 → roam_code-12.26.1}/src/roam/surface_counts.py +0 -0
  379. {roam_code-12.26 → roam_code-12.26.1}/src/roam/telemetry.py +0 -0
  380. {roam_code-12.26 → roam_code-12.26.1}/src/roam/templates/__init__.py +0 -0
  381. {roam_code-12.26 → roam_code-12.26.1}/src/roam/templates/ci/Jenkinsfile +0 -0
  382. {roam_code-12.26 → roam_code-12.26.1}/src/roam/templates/ci/__init__.py +0 -0
  383. {roam_code-12.26 → roam_code-12.26.1}/src/roam/templates/ci/agent-review.yml +0 -0
  384. {roam_code-12.26 → roam_code-12.26.1}/src/roam/templates/ci/azure-pipelines.yml +0 -0
  385. {roam_code-12.26 → roam_code-12.26.1}/src/roam/templates/ci/bitbucket-pipelines.yml +0 -0
  386. {roam_code-12.26 → roam_code-12.26.1}/src/roam/templates/ci/gitlab-ci.yml +0 -0
  387. {roam_code-12.26 → roam_code-12.26.1}/src/roam/workspace/__init__.py +0 -0
  388. {roam_code-12.26 → roam_code-12.26.1}/src/roam/workspace/aggregator.py +0 -0
  389. {roam_code-12.26 → roam_code-12.26.1}/src/roam/workspace/api_scanner.py +0 -0
  390. {roam_code-12.26 → roam_code-12.26.1}/src/roam/workspace/config.py +0 -0
  391. {roam_code-12.26 → roam_code-12.26.1}/src/roam/workspace/db.py +0 -0
  392. {roam_code-12.26 → roam_code-12.26.1}/src/roam_code.egg-info/dependency_links.txt +0 -0
  393. {roam_code-12.26 → roam_code-12.26.1}/src/roam_code.egg-info/entry_points.txt +0 -0
  394. {roam_code-12.26 → roam_code-12.26.1}/src/roam_code.egg-info/requires.txt +0 -0
  395. {roam_code-12.26 → roam_code-12.26.1}/src/roam_code.egg-info/top_level.txt +0 -0
  396. {roam_code-12.26 → roam_code-12.26.1}/tests/test_adrs.py +0 -0
  397. {roam_code-12.26 → roam_code-12.26.1}/tests/test_adversarial.py +0 -0
  398. {roam_code-12.26 → roam_code-12.26.1}/tests/test_affected.py +0 -0
  399. {roam_code-12.26 → roam_code-12.26.1}/tests/test_agent_export.py +0 -0
  400. {roam_code-12.26 → roam_code-12.26.1}/tests/test_agent_mode.py +0 -0
  401. {roam_code-12.26 → roam_code-12.26.1}/tests/test_agent_plan_context.py +0 -0
  402. {roam_code-12.26 → roam_code-12.26.1}/tests/test_ai_ratio.py +0 -0
  403. {roam_code-12.26 → roam_code-12.26.1}/tests/test_ai_readiness.py +0 -0
  404. {roam_code-12.26 → roam_code-12.26.1}/tests/test_alerts_cmd.py +0 -0
  405. {roam_code-12.26 → roam_code-12.26.1}/tests/test_annotations.py +0 -0
  406. {roam_code-12.26 → roam_code-12.26.1}/tests/test_anomaly.py +0 -0
  407. {roam_code-12.26 → roam_code-12.26.1}/tests/test_api_changes.py +0 -0
  408. {roam_code-12.26 → roam_code-12.26.1}/tests/test_api_drift.py +0 -0
  409. {roam_code-12.26 → roam_code-12.26.1}/tests/test_ask.py +0 -0
  410. {roam_code-12.26 → roam_code-12.26.1}/tests/test_attest.py +0 -0
  411. {roam_code-12.26 → roam_code-12.26.1}/tests/test_audit_trail_aggregate.py +0 -0
  412. {roam_code-12.26 → roam_code-12.26.1}/tests/test_audit_trail_sequence.py +0 -0
  413. {roam_code-12.26 → roam_code-12.26.1}/tests/test_audit_trail_verify.py +0 -0
  414. {roam_code-12.26 → roam_code-12.26.1}/tests/test_auth_gaps.py +0 -0
  415. {roam_code-12.26 → roam_code-12.26.1}/tests/test_backend_fixes_round2.py +0 -0
  416. {roam_code-12.26 → roam_code-12.26.1}/tests/test_backend_fixes_round3.py +0 -0
  417. {roam_code-12.26 → roam_code-12.26.1}/tests/test_basic.py +0 -0
  418. {roam_code-12.26 → roam_code-12.26.1}/tests/test_batch_mcp.py +0 -0
  419. {roam_code-12.26 → roam_code-12.26.1}/tests/test_bisect.py +0 -0
  420. {roam_code-12.26 → roam_code-12.26.1}/tests/test_bridge_django.py +0 -0
  421. {roam_code-12.26 → roam_code-12.26.1}/tests/test_bridges.py +0 -0
  422. {roam_code-12.26 → roam_code-12.26.1}/tests/test_bridges_extended.py +0 -0
  423. {roam_code-12.26 → roam_code-12.26.1}/tests/test_budget.py +0 -0
  424. {roam_code-12.26 → roam_code-12.26.1}/tests/test_budget_flag.py +0 -0
  425. {roam_code-12.26 → roam_code-12.26.1}/tests/test_budget_phase2.py +0 -0
  426. {roam_code-12.26 → roam_code-12.26.1}/tests/test_bus_factor.py +0 -0
  427. {roam_code-12.26 → roam_code-12.26.1}/tests/test_capsule.py +0 -0
  428. {roam_code-12.26 → roam_code-12.26.1}/tests/test_cga.py +0 -0
  429. {roam_code-12.26 → roam_code-12.26.1}/tests/test_check_rules.py +0 -0
  430. {roam_code-12.26 → roam_code-12.26.1}/tests/test_ci_gate_eval.py +0 -0
  431. {roam_code-12.26 → roam_code-12.26.1}/tests/test_ci_sarif_guard.py +0 -0
  432. {roam_code-12.26 → roam_code-12.26.1}/tests/test_ci_setup.py +0 -0
  433. {roam_code-12.26 → roam_code-12.26.1}/tests/test_clones.py +0 -0
  434. {roam_code-12.26 → roam_code-12.26.1}/tests/test_closure.py +0 -0
  435. {roam_code-12.26 → roam_code-12.26.1}/tests/test_codeowners.py +0 -0
  436. {roam_code-12.26 → roam_code-12.26.1}/tests/test_commands_architecture.py +0 -0
  437. {roam_code-12.26 → roam_code-12.26.1}/tests/test_commands_exploration.py +0 -0
  438. {roam_code-12.26 → roam_code-12.26.1}/tests/test_commands_health.py +0 -0
  439. {roam_code-12.26 → roam_code-12.26.1}/tests/test_commands_refactoring.py +0 -0
  440. {roam_code-12.26 → roam_code-12.26.1}/tests/test_commands_workflow.py +0 -0
  441. {roam_code-12.26 → roam_code-12.26.1}/tests/test_competitor_site_data.py +0 -0
  442. {roam_code-12.26 → roam_code-12.26.1}/tests/test_comprehensive.py +0 -0
  443. {roam_code-12.26 → roam_code-12.26.1}/tests/test_config.py +0 -0
  444. {roam_code-12.26 → roam_code-12.26.1}/tests/test_congestion.py +0 -0
  445. {roam_code-12.26 → roam_code-12.26.1}/tests/test_context_propagation.py +0 -0
  446. {roam_code-12.26 → roam_code-12.26.1}/tests/test_conventions_cmd.py +0 -0
  447. {roam_code-12.26 → roam_code-12.26.1}/tests/test_coverage_gaps_cmd.py +0 -0
  448. {roam_code-12.26 → roam_code-12.26.1}/tests/test_coverage_ingestion.py +0 -0
  449. {roam_code-12.26 → roam_code-12.26.1}/tests/test_critique.py +0 -0
  450. {roam_code-12.26 → roam_code-12.26.1}/tests/test_cut.py +0 -0
  451. {roam_code-12.26 → roam_code-12.26.1}/tests/test_dark_matter.py +0 -0
  452. {roam_code-12.26 → roam_code-12.26.1}/tests/test_dark_matter_helpers.py +0 -0
  453. {roam_code-12.26 → roam_code-12.26.1}/tests/test_dashboard.py +0 -0
  454. {roam_code-12.26 → roam_code-12.26.1}/tests/test_dataflow_dead.py +0 -0
  455. {roam_code-12.26 → roam_code-12.26.1}/tests/test_dead_aging.py +0 -0
  456. {roam_code-12.26 → roam_code-12.26.1}/tests/test_defer_loading.py +0 -0
  457. {roam_code-12.26 → roam_code-12.26.1}/tests/test_demo_gif_asset.py +0 -0
  458. {roam_code-12.26 → roam_code-12.26.1}/tests/test_describe.py +0 -0
  459. {roam_code-12.26 → roam_code-12.26.1}/tests/test_detail_flag_hints.py +0 -0
  460. {roam_code-12.26 → roam_code-12.26.1}/tests/test_detector_precision.py +0 -0
  461. {roam_code-12.26 → roam_code-12.26.1}/tests/test_deterministic_output.py +0 -0
  462. {roam_code-12.26 → roam_code-12.26.1}/tests/test_dev_profile.py +0 -0
  463. {roam_code-12.26 → roam_code-12.26.1}/tests/test_difficulty_scoring.py +0 -0
  464. {roam_code-12.26 → roam_code-12.26.1}/tests/test_doc_consistency.py +0 -0
  465. {roam_code-12.26 → roam_code-12.26.1}/tests/test_doc_staleness.py +0 -0
  466. {roam_code-12.26 → roam_code-12.26.1}/tests/test_docker_assets.py +0 -0
  467. {roam_code-12.26 → roam_code-12.26.1}/tests/test_docs_coverage.py +0 -0
  468. {roam_code-12.26 → roam_code-12.26.1}/tests/test_docs_site_quality.py +0 -0
  469. {roam_code-12.26 → roam_code-12.26.1}/tests/test_doctor.py +0 -0
  470. {roam_code-12.26 → roam_code-12.26.1}/tests/test_dogfood.py +0 -0
  471. {roam_code-12.26 → roam_code-12.26.1}/tests/test_drift.py +0 -0
  472. {roam_code-12.26 → roam_code-12.26.1}/tests/test_drift_by_team.py +0 -0
  473. {roam_code-12.26 → roam_code-12.26.1}/tests/test_duplicates.py +0 -0
  474. {roam_code-12.26 → roam_code-12.26.1}/tests/test_effects.py +0 -0
  475. {roam_code-12.26 → roam_code-12.26.1}/tests/test_effects_propagation.py +0 -0
  476. {roam_code-12.26 → roam_code-12.26.1}/tests/test_endpoints.py +0 -0
  477. {roam_code-12.26 → roam_code-12.26.1}/tests/test_entry_points_cmd.py +0 -0
  478. {roam_code-12.26 → roam_code-12.26.1}/tests/test_eval_retrieve.py +0 -0
  479. {roam_code-12.26 → roam_code-12.26.1}/tests/test_except_pass_narrow.py +0 -0
  480. {roam_code-12.26 → roam_code-12.26.1}/tests/test_exclude_patterns.py +0 -0
  481. {roam_code-12.26 → roam_code-12.26.1}/tests/test_exit_codes.py +0 -0
  482. {roam_code-12.26 → roam_code-12.26.1}/tests/test_extractor_grammar_drift.py +0 -0
  483. {roam_code-12.26 → roam_code-12.26.1}/tests/test_fallback_contracts.py +0 -0
  484. {roam_code-12.26 → roam_code-12.26.1}/tests/test_file_roles.py +0 -0
  485. {roam_code-12.26 → roam_code-12.26.1}/tests/test_fingerprint.py +0 -0
  486. {roam_code-12.26 → roam_code-12.26.1}/tests/test_fixes.py +0 -0
  487. {roam_code-12.26 → roam_code-12.26.1}/tests/test_flag_dead.py +0 -0
  488. {roam_code-12.26 → roam_code-12.26.1}/tests/test_fleet.py +0 -0
  489. {roam_code-12.26 → roam_code-12.26.1}/tests/test_fn_coupling.py +0 -0
  490. {roam_code-12.26 → roam_code-12.26.1}/tests/test_forecast.py +0 -0
  491. {roam_code-12.26 → roam_code-12.26.1}/tests/test_formatters.py +0 -0
  492. {roam_code-12.26 → roam_code-12.26.1}/tests/test_foxpro.py +0 -0
  493. {roam_code-12.26 → roam_code-12.26.1}/tests/test_framework_detection.py +0 -0
  494. {roam_code-12.26 → roam_code-12.26.1}/tests/test_gate_presets.py +0 -0
  495. {roam_code-12.26 → roam_code-12.26.1}/tests/test_git_helpers.py +0 -0
  496. {roam_code-12.26 → roam_code-12.26.1}/tests/test_git_utils.py +0 -0
  497. {roam_code-12.26 → roam_code-12.26.1}/tests/test_guard.py +0 -0
  498. {roam_code-12.26 → roam_code-12.26.1}/tests/test_health_gate.py +0 -0
  499. {roam_code-12.26 → roam_code-12.26.1}/tests/test_hooks.py +0 -0
  500. {roam_code-12.26 → roam_code-12.26.1}/tests/test_hotspots.py +0 -0
  501. {roam_code-12.26 → roam_code-12.26.1}/tests/test_hover.py +0 -0
  502. {roam_code-12.26 → roam_code-12.26.1}/tests/test_index.py +0 -0
  503. {roam_code-12.26 → roam_code-12.26.1}/tests/test_index_bundle.py +0 -0
  504. {roam_code-12.26 → roam_code-12.26.1}/tests/test_init_cmd.py +0 -0
  505. {roam_code-12.26 → roam_code-12.26.1}/tests/test_install_check.py +0 -0
  506. {roam_code-12.26 → roam_code-12.26.1}/tests/test_intent.py +0 -0
  507. {roam_code-12.26 → roam_code-12.26.1}/tests/test_invariants.py +0 -0
  508. {roam_code-12.26 → roam_code-12.26.1}/tests/test_json_contracts.py +0 -0
  509. {roam_code-12.26 → roam_code-12.26.1}/tests/test_json_error_envelope.py +0 -0
  510. {roam_code-12.26 → roam_code-12.26.1}/tests/test_kotlin_swift_extractors.py +0 -0
  511. {roam_code-12.26 → roam_code-12.26.1}/tests/test_language_corpus.py +0 -0
  512. {roam_code-12.26 → roam_code-12.26.1}/tests/test_languages.py +0 -0
  513. {roam_code-12.26 → roam_code-12.26.1}/tests/test_library_api.py +0 -0
  514. {roam_code-12.26 → roam_code-12.26.1}/tests/test_math.py +0 -0
  515. {roam_code-12.26 → roam_code-12.26.1}/tests/test_math_tips.py +0 -0
  516. {roam_code-12.26 → roam_code-12.26.1}/tests/test_mcp_extras.py +0 -0
  517. {roam_code-12.26 → roam_code-12.26.1}/tests/test_mcp_server.py +0 -0
  518. {roam_code-12.26 → roam_code-12.26.1}/tests/test_mcp_setup.py +0 -0
  519. {roam_code-12.26 → roam_code-12.26.1}/tests/test_mermaid.py +0 -0
  520. {roam_code-12.26 → roam_code-12.26.1}/tests/test_metrics_cmd.py +0 -0
  521. {roam_code-12.26 → roam_code-12.26.1}/tests/test_metrics_push.py +0 -0
  522. {roam_code-12.26 → roam_code-12.26.1}/tests/test_migration_safety.py +0 -0
  523. {roam_code-12.26 → roam_code-12.26.1}/tests/test_minimap.py +0 -0
  524. {roam_code-12.26 → roam_code-12.26.1}/tests/test_missing_index.py +0 -0
  525. {roam_code-12.26 → roam_code-12.26.1}/tests/test_mutate.py +0 -0
  526. {roam_code-12.26 → roam_code-12.26.1}/tests/test_n1.py +0 -0
  527. {roam_code-12.26 → roam_code-12.26.1}/tests/test_next_steps.py +0 -0
  528. {roam_code-12.26 → roam_code-12.26.1}/tests/test_onboard.py +0 -0
  529. {roam_code-12.26 → roam_code-12.26.1}/tests/test_oracle.py +0 -0
  530. {roam_code-12.26 → roam_code-12.26.1}/tests/test_orchestrate.py +0 -0
  531. {roam_code-12.26 → roam_code-12.26.1}/tests/test_orphan_routes.py +0 -0
  532. {roam_code-12.26 → roam_code-12.26.1}/tests/test_oss_bench_harness.py +0 -0
  533. {roam_code-12.26 → roam_code-12.26.1}/tests/test_over_fetch.py +0 -0
  534. {roam_code-12.26 → roam_code-12.26.1}/tests/test_pagerank_truncation.py +0 -0
  535. {roam_code-12.26 → roam_code-12.26.1}/tests/test_partition.py +0 -0
  536. {roam_code-12.26 → roam_code-12.26.1}/tests/test_path_coverage.py +0 -0
  537. {roam_code-12.26 → roam_code-12.26.1}/tests/test_patterns_cmd.py +0 -0
  538. {roam_code-12.26 → roam_code-12.26.1}/tests/test_performance.py +0 -0
  539. {roam_code-12.26 → roam_code-12.26.1}/tests/test_personalized_pagerank.py +0 -0
  540. {roam_code-12.26 → roam_code-12.26.1}/tests/test_plan.py +0 -0
  541. {roam_code-12.26 → roam_code-12.26.1}/tests/test_plugin_discovery.py +0 -0
  542. {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_analyze.py +0 -0
  543. {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_analyze_cache.py +0 -0
  544. {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_analyze_edge_cases.py +0 -0
  545. {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_analyze_v2_signals.py +0 -0
  546. {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_comment_render.py +0 -0
  547. {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_comment_script.py +0 -0
  548. {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_diff.py +0 -0
  549. {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_risk_author.py +0 -0
  550. {roam_code-12.26 → roam_code-12.26.1}/tests/test_progress.py +0 -0
  551. {roam_code-12.26 → roam_code-12.26.1}/tests/test_progressive_disclosure.py +0 -0
  552. {roam_code-12.26 → roam_code-12.26.1}/tests/test_properties.py +0 -0
  553. {roam_code-12.26 → roam_code-12.26.1}/tests/test_pytest_fixtures.py +0 -0
  554. {roam_code-12.26 → roam_code-12.26.1}/tests/test_python_extractor_v2.py +0 -0
  555. {roam_code-12.26 → roam_code-12.26.1}/tests/test_python_idioms_e2e.py +0 -0
  556. {roam_code-12.26 → roam_code-12.26.1}/tests/test_python_pivot.py +0 -0
  557. {roam_code-12.26 → roam_code-12.26.1}/tests/test_readme_surface_consistency.py +0 -0
  558. {roam_code-12.26 → roam_code-12.26.1}/tests/test_realworld_feedback.py +0 -0
  559. {roam_code-12.26 → roam_code-12.26.1}/tests/test_refactoring_intelligence.py +0 -0
  560. {roam_code-12.26 → roam_code-12.26.1}/tests/test_registry_dispatch.py +0 -0
  561. {roam_code-12.26 → roam_code-12.26.1}/tests/test_relate.py +0 -0
  562. {roam_code-12.26 → roam_code-12.26.1}/tests/test_report.py +0 -0
  563. {roam_code-12.26 → roam_code-12.26.1}/tests/test_reset_clean.py +0 -0
  564. {roam_code-12.26 → roam_code-12.26.1}/tests/test_resolve.py +0 -0
  565. {roam_code-12.26 → roam_code-12.26.1}/tests/test_retrieve.py +0 -0
  566. {roam_code-12.26 → roam_code-12.26.1}/tests/test_retrieve_cross_repo.py +0 -0
  567. {roam_code-12.26 → roam_code-12.26.1}/tests/test_retrieve_seeds.py +0 -0
  568. {roam_code-12.26 → roam_code-12.26.1}/tests/test_risk.py +0 -0
  569. {roam_code-12.26 → roam_code-12.26.1}/tests/test_ruby.py +0 -0
  570. {roam_code-12.26 → roam_code-12.26.1}/tests/test_rule_profiles.py +0 -0
  571. {roam_code-12.26 → roam_code-12.26.1}/tests/test_rules.py +0 -0
  572. {roam_code-12.26 → roam_code-12.26.1}/tests/test_rules_ast_match.py +0 -0
  573. {roam_code-12.26 → roam_code-12.26.1}/tests/test_rules_community_pack.py +0 -0
  574. {roam_code-12.26 → roam_code-12.26.1}/tests/test_rules_dataflow.py +0 -0
  575. {roam_code-12.26 → roam_code-12.26.1}/tests/test_rules_symbol_requirements.py +0 -0
  576. {roam_code-12.26 → roam_code-12.26.1}/tests/test_rules_validate.py +0 -0
  577. {roam_code-12.26 → roam_code-12.26.1}/tests/test_runtime.py +0 -0
  578. {roam_code-12.26 → roam_code-12.26.1}/tests/test_runtime_score.py +0 -0
  579. {roam_code-12.26 → roam_code-12.26.1}/tests/test_salesforce.py +0 -0
  580. {roam_code-12.26 → roam_code-12.26.1}/tests/test_sarif_flag.py +0 -0
  581. {roam_code-12.26 → roam_code-12.26.1}/tests/test_sbom.py +0 -0
  582. {roam_code-12.26 → roam_code-12.26.1}/tests/test_scala.py +0 -0
  583. {roam_code-12.26 → roam_code-12.26.1}/tests/test_schema_versioning.py +0 -0
  584. {roam_code-12.26 → roam_code-12.26.1}/tests/test_search_explain.py +0 -0
  585. {roam_code-12.26 → roam_code-12.26.1}/tests/test_secrets.py +0 -0
  586. {roam_code-12.26 → roam_code-12.26.1}/tests/test_secrets_v2.py +0 -0
  587. {roam_code-12.26 → roam_code-12.26.1}/tests/test_semantic_diff.py +0 -0
  588. {roam_code-12.26 → roam_code-12.26.1}/tests/test_semantic_onnx.py +0 -0
  589. {roam_code-12.26 → roam_code-12.26.1}/tests/test_semantic_search.py +0 -0
  590. {roam_code-12.26 → roam_code-12.26.1}/tests/test_simulate.py +0 -0
  591. {roam_code-12.26 → roam_code-12.26.1}/tests/test_simulate_departure.py +0 -0
  592. {roam_code-12.26 → roam_code-12.26.1}/tests/test_sketch.py +0 -0
  593. {roam_code-12.26 → roam_code-12.26.1}/tests/test_smells.py +0 -0
  594. {roam_code-12.26 → roam_code-12.26.1}/tests/test_smoke.py +0 -0
  595. {roam_code-12.26 → roam_code-12.26.1}/tests/test_sna_metrics.py +0 -0
  596. {roam_code-12.26 → roam_code-12.26.1}/tests/test_spectral.py +0 -0
  597. {roam_code-12.26 → roam_code-12.26.1}/tests/test_split_cmd.py +0 -0
  598. {roam_code-12.26 → roam_code-12.26.1}/tests/test_sql.py +0 -0
  599. {roam_code-12.26 → roam_code-12.26.1}/tests/test_suggest_reviewers.py +0 -0
  600. {roam_code-12.26 → roam_code-12.26.1}/tests/test_supply_chain.py +0 -0
  601. {roam_code-12.26 → roam_code-12.26.1}/tests/test_surface_counts.py +0 -0
  602. {roam_code-12.26 → roam_code-12.26.1}/tests/test_syntax_check.py +0 -0
  603. {roam_code-12.26 → roam_code-12.26.1}/tests/test_taint.py +0 -0
  604. {roam_code-12.26 → roam_code-12.26.1}/tests/test_taint_analysis.py +0 -0
  605. {roam_code-12.26 → roam_code-12.26.1}/tests/test_taint_classifier.py +0 -0
  606. {roam_code-12.26 → roam_code-12.26.1}/tests/test_taint_intraprocedural.py +0 -0
  607. {roam_code-12.26 → roam_code-12.26.1}/tests/test_test_conventions.py +0 -0
  608. {roam_code-12.26 → roam_code-12.26.1}/tests/test_test_gaps.py +0 -0
  609. {roam_code-12.26 → roam_code-12.26.1}/tests/test_test_scaffold.py +0 -0
  610. {roam_code-12.26 → roam_code-12.26.1}/tests/test_testmap.py +0 -0
  611. {roam_code-12.26 → roam_code-12.26.1}/tests/test_top_flag_consistency.py +0 -0
  612. {roam_code-12.26 → roam_code-12.26.1}/tests/test_tour_cmd.py +0 -0
  613. {roam_code-12.26 → roam_code-12.26.1}/tests/test_trends.py +0 -0
  614. {roam_code-12.26 → roam_code-12.26.1}/tests/test_trends_cohort.py +0 -0
  615. {roam_code-12.26 → roam_code-12.26.1}/tests/test_triage.py +0 -0
  616. {roam_code-12.26 → roam_code-12.26.1}/tests/test_uses_cmd.py +0 -0
  617. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1215_passes.py +0 -0
  618. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1216_passes.py +0 -0
  619. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1216_passes_41_50.py +0 -0
  620. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1216_passes_51_60.py +0 -0
  621. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1217_passes_61_80.py +0 -0
  622. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1218_passes_81_90.py +0 -0
  623. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1219_passes_91_100.py +0 -0
  624. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1220_passes_101_110.py +0 -0
  625. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1221_query_timeout.py +0 -0
  626. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1221_untested_commands.py +0 -0
  627. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v12_2.py +0 -0
  628. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v2_edge_cases.py +0 -0
  629. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v6_features.py +0 -0
  630. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v71_features.py +0 -0
  631. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v7_features.py +0 -0
  632. {roam_code-12.26 → roam_code-12.26.1}/tests/test_v82_features.py +0 -0
  633. {roam_code-12.26 → roam_code-12.26.1}/tests/test_verify.py +0 -0
  634. {roam_code-12.26 → roam_code-12.26.1}/tests/test_verify_imports.py +0 -0
  635. {roam_code-12.26 → roam_code-12.26.1}/tests/test_vibe_check.py +0 -0
  636. {roam_code-12.26 → roam_code-12.26.1}/tests/test_visualize.py +0 -0
  637. {roam_code-12.26 → roam_code-12.26.1}/tests/test_vuln.py +0 -0
  638. {roam_code-12.26 → roam_code-12.26.1}/tests/test_vulns_cmd.py +0 -0
  639. {roam_code-12.26 → roam_code-12.26.1}/tests/test_watch.py +0 -0
  640. {roam_code-12.26 → roam_code-12.26.1}/tests/test_why.py +0 -0
  641. {roam_code-12.26 → roam_code-12.26.1}/tests/test_workspace.py +0 -0
  642. {roam_code-12.26 → roam_code-12.26.1}/tests/test_ws_resolve_fixes.py +0 -0
  643. {roam_code-12.26 → roam_code-12.26.1}/tests/test_xlang.py +0 -0
  644. {roam_code-12.26 → roam_code-12.26.1}/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.26
3
+ Version: 12.26.1
4
4
  Summary: Instant codebase comprehension for AI coding agents
5
5
  Author: CosmoHac
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "roam-code"
7
- version = "12.26"
7
+ version = "12.26.1"
8
8
  description = "Instant codebase comprehension for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -124,6 +124,56 @@ def _check_retention(records: list[dict], retention_days: int) -> tuple[bool, st
124
124
  )
125
125
 
126
126
 
127
+ def _checks_to_sarif(
128
+ checks: list[dict],
129
+ audit_trail_path: Path,
130
+ score: int,
131
+ ) -> dict:
132
+ """Render the 6-check verdict as a SARIF 2.1.0 envelope.
133
+
134
+ Each FAIL becomes a SARIF result; each rule (one per check id) is
135
+ declared in the run's tool.driver.rules. GitHub Code Scanning ingests
136
+ this directly, surfacing the failures as triage items in the Security
137
+ tab — useful for quarterly compliance gates.
138
+ """
139
+ from roam.output.sarif import to_sarif
140
+
141
+ rules = [
142
+ {
143
+ "id": c["id"],
144
+ "shortDescription": f"EU AI Act Article 12 conformance check: {c['id'].replace('_', ' ')}",
145
+ "defaultLevel": "warning",
146
+ "helpUri": "https://artificialintelligenceact.eu/article/12/",
147
+ "properties": {"category": "compliance", "regulation": "EU AI Act Article 12"},
148
+ }
149
+ for c in checks
150
+ ]
151
+ results = []
152
+ for c in checks:
153
+ if c["passed"]:
154
+ continue
155
+ results.append(
156
+ {
157
+ "ruleId": c["id"],
158
+ "level": "error" if score < 67 else "warning",
159
+ "message": {"text": c["message"]},
160
+ "locations": [
161
+ {
162
+ "physicalLocation": {
163
+ "artifactLocation": {"uri": str(audit_trail_path)},
164
+ }
165
+ }
166
+ ],
167
+ }
168
+ )
169
+ return to_sarif(
170
+ tool_name="roam-code",
171
+ version="audit-trail-conformance-check",
172
+ rules=rules,
173
+ results=results,
174
+ )
175
+
176
+
127
177
  @click.command(name="audit-trail-conformance-check")
128
178
  @click.option(
129
179
  "--input",
@@ -144,12 +194,19 @@ def _check_retention(records: list[dict], retention_days: int) -> tuple[bool, st
144
194
  is_flag=True,
145
195
  help="Exit 5 (gate failure) when conformance score < 100; useful for quarterly compliance gates.",
146
196
  )
197
+ @click.option(
198
+ "--sarif-output",
199
+ type=click.Path(),
200
+ default=None,
201
+ help="Write SARIF to this file (default: stdout when global --sarif is set). Requires --sarif.",
202
+ )
147
203
  @click.pass_context
148
204
  def audit_trail_conformance_check(
149
205
  ctx,
150
206
  input_path: str | None,
151
207
  retention_days: int,
152
208
  gate: bool,
209
+ sarif_output: str | None,
153
210
  ) -> None:
154
211
  """Score the audit trail against an EU AI Act Article 12 checklist.
155
212
 
@@ -168,6 +225,7 @@ def audit_trail_conformance_check(
168
225
  triage signal: "would this trail survive a procurement review?".
169
226
  """
170
227
  json_mode = ctx.obj.get("json") if ctx.obj else False
228
+ sarif = ctx.obj.get("sarif") if ctx.obj else False
171
229
 
172
230
  path = Path(input_path) if input_path else DEFAULT_AUDIT_TRAIL_PATH
173
231
  records = _load_records(path)
@@ -211,7 +269,16 @@ def audit_trail_conformance_check(
211
269
  "disclaimer": "Triage signal only — not legal advice.",
212
270
  }
213
271
 
214
- if json_mode:
272
+ if sarif:
273
+ from roam.output.sarif import write_sarif
274
+
275
+ sarif_doc = _checks_to_sarif(checks, path, score)
276
+ sarif_text = write_sarif(sarif_doc, sarif_output)
277
+ if not sarif_output:
278
+ click.echo(sarif_text)
279
+ elif not json_mode:
280
+ click.echo(f"VERDICT: {verdict} — SARIF written to {sarif_output}")
281
+ elif json_mode:
215
282
  click.echo(
216
283
  to_json(
217
284
  json_envelope(
@@ -224,7 +291,7 @@ def audit_trail_conformance_check(
224
291
  )
225
292
  )
226
293
  )
227
- else:
294
+ elif not sarif:
228
295
  click.echo(f"VERDICT: {verdict}")
229
296
  click.echo(f" path: {path}")
230
297
  click.echo(f" records: {len(records)}")
@@ -102,21 +102,12 @@ def _load_last_pr_analysis(path: Path | None = None) -> dict | None:
102
102
  return None
103
103
 
104
104
 
105
- def _build_payload(
106
- audit_envelope: dict,
107
- *,
108
- repo_id: str,
109
- git_meta: dict,
110
- anonymize: bool,
111
- include_hotspots: bool,
112
- last_pr_envelope: dict | None = None,
113
- ) -> dict:
114
- """Compose the metrics-only payload from a ``roam audit`` envelope.
105
+ def _extract_metrics(audit_envelope: dict) -> dict:
106
+ """Pull the allow-listed numeric metrics out of an audit envelope.
115
107
 
116
- Source-code bodies are NEVER included. Only numbers, file paths
117
- (or hashes when --anonymize), bucket counts, and aggregate scores.
118
- Per the Cloud Lite spec, the receiving API rejects any payload
119
- containing keys outside this allow-listed schema.
108
+ Source-code bodies are NEVER included only numbers, bucket counts,
109
+ aggregate scores. The receiving API rejects any payload containing
110
+ keys outside this allow-listed schema.
120
111
  """
121
112
  summary = audit_envelope.get("summary") or {}
122
113
  sections = audit_envelope.get("sections") or {}
@@ -125,9 +116,8 @@ def _build_payload(
125
116
  dead_summary = (sections.get("dead") or {}).get("summary") or {}
126
117
  pyramid_summary = (sections.get("test_pyramid") or {}).get("summary") or {}
127
118
  danger_summary = (sections.get("hotspots_danger") or {}).get("summary") or {}
128
- danger_section = sections.get("hotspots_danger") or {}
129
119
 
130
- metrics = {
120
+ return {
131
121
  "health_score": health_summary.get("health_score") or summary.get("health_score"),
132
122
  "debt_total_minutes": debt_summary.get("total_remediation_minutes") or debt_summary.get("total_minutes"),
133
123
  "debt_total_hours": debt_summary.get("total_remediation_hours"),
@@ -153,6 +143,72 @@ def _build_payload(
153
143
  "tangle_ratio": health_summary.get("tangle_ratio"),
154
144
  }
155
145
 
146
+
147
+ def _extract_hotspots(audit_envelope: dict, *, anonymize: bool, limit: int = 10) -> list[dict]:
148
+ """Pull the top-N danger-zone rows. Path is hashed under anonymize."""
149
+ danger_section = (audit_envelope.get("sections") or {}).get("hotspots_danger") or {}
150
+ danger_zone = danger_section.get("danger_zone") or []
151
+ out: list[dict] = []
152
+ for row in danger_zone[:limit]:
153
+ path = row.get("path", "")
154
+ entry = {
155
+ "danger_score": row.get("danger_score"),
156
+ "churn": row.get("churn"),
157
+ "complexity": row.get("complexity"),
158
+ "max_fan_in": row.get("max_fan_in"),
159
+ }
160
+ if anonymize:
161
+ entry["path_hash"] = _path_hash(path) if path else None
162
+ else:
163
+ entry["path"] = path
164
+ out.append(entry)
165
+ return out
166
+
167
+
168
+ def _build_last_pr_block(last_pr_envelope: dict) -> dict:
169
+ """Compose the last_pr_analysis block from a saved pr-analyze envelope.
170
+
171
+ Folds in only summary numerics + verdict + primary language + timestamp.
172
+ Computes ``age_days`` + ``stale`` (>7 days) so dashboards can grey
173
+ stale entries without needing to compute age client-side.
174
+ """
175
+ pr_summary = last_pr_envelope.get("summary") or {}
176
+ ai_section = last_pr_envelope.get("ai_likelihood") or {}
177
+ ts = (last_pr_envelope.get("_meta") or {}).get("timestamp")
178
+ block = {
179
+ "verdict": pr_summary.get("verdict"),
180
+ "blast_radius": pr_summary.get("blast_radius"),
181
+ "ai_likelihood": pr_summary.get("ai_likelihood"),
182
+ "rule_violations": pr_summary.get("rule_violations"),
183
+ "high_severity_critique": pr_summary.get("high_severity_critique"),
184
+ "primary_language": ai_section.get("primary_language"),
185
+ "timestamp": ts,
186
+ }
187
+ if ts:
188
+ try:
189
+ pr_dt = _dt.datetime.fromisoformat(ts.replace("Z", "+00:00"))
190
+ age_days = (_dt.datetime.now(_dt.timezone.utc) - pr_dt).days
191
+ block["age_days"] = age_days
192
+ block["stale"] = age_days > 7
193
+ except (TypeError, ValueError):
194
+ pass
195
+ return block
196
+
197
+
198
+ def _build_payload(
199
+ audit_envelope: dict,
200
+ *,
201
+ repo_id: str,
202
+ git_meta: dict,
203
+ anonymize: bool,
204
+ include_hotspots: bool,
205
+ last_pr_envelope: dict | None = None,
206
+ ) -> dict:
207
+ """Compose the metrics-only payload from a ``roam audit`` envelope.
208
+
209
+ Refactor (P23): metrics, hotspots, and last-pr-analysis blocks are
210
+ extracted into helpers above. This function is now a flat coordinator.
211
+ """
156
212
  payload: dict = {
157
213
  "schema": "roam-metrics-v1",
158
214
  "schema_version": "1.0.0",
@@ -162,55 +218,12 @@ def _build_payload(
162
218
  "timestamp": utc_timestamp(),
163
219
  "tool_version": detect_roam_version(),
164
220
  "anonymized": bool(anonymize),
165
- "metrics": metrics,
221
+ "metrics": _extract_metrics(audit_envelope),
166
222
  }
167
-
168
223
  if include_hotspots:
169
- danger_zone = danger_section.get("danger_zone") or []
170
- hotspot_rows: list[dict] = []
171
- for row in danger_zone[:10]:
172
- path = row.get("path", "")
173
- entry = {
174
- "danger_score": row.get("danger_score"),
175
- "churn": row.get("churn"),
176
- "complexity": row.get("complexity"),
177
- "max_fan_in": row.get("max_fan_in"),
178
- }
179
- if anonymize:
180
- entry["path_hash"] = _path_hash(path) if path else None
181
- else:
182
- entry["path"] = path
183
- hotspot_rows.append(entry)
184
- payload["hotspots"] = hotspot_rows
185
-
224
+ payload["hotspots"] = _extract_hotspots(audit_envelope, anonymize=anonymize)
186
225
  if last_pr_envelope:
187
- # Fold the latest pr-analyze summary into the payload so Cloud Lite
188
- # can show "last PR verdict" alongside health-trend without a 2nd call.
189
- # No diff text or source bodies — only summary numerics + verdict.
190
- pr_summary = last_pr_envelope.get("summary") or {}
191
- ai_section = last_pr_envelope.get("ai_likelihood") or {}
192
- ts = (last_pr_envelope.get("_meta") or {}).get("timestamp")
193
- last_pr_block = {
194
- "verdict": pr_summary.get("verdict"),
195
- "blast_radius": pr_summary.get("blast_radius"),
196
- "ai_likelihood": pr_summary.get("ai_likelihood"),
197
- "rule_violations": pr_summary.get("rule_violations"),
198
- "high_severity_critique": pr_summary.get("high_severity_critique"),
199
- "primary_language": ai_section.get("primary_language"),
200
- "timestamp": ts,
201
- }
202
- # Synergy C.1.hhh: surface "stale" so dashboards can grey it out
203
- # without needing to compute age client-side.
204
- if ts:
205
- try:
206
- pr_dt = _dt.datetime.fromisoformat(ts.replace("Z", "+00:00"))
207
- age_days = (_dt.datetime.now(_dt.timezone.utc) - pr_dt).days
208
- last_pr_block["age_days"] = age_days
209
- last_pr_block["stale"] = age_days > 7
210
- except (TypeError, ValueError):
211
- pass
212
- payload["last_pr_analysis"] = last_pr_block
213
-
226
+ payload["last_pr_analysis"] = _build_last_pr_block(last_pr_envelope)
214
227
  return payload
215
228
 
216
229
 
@@ -204,9 +204,18 @@ def _load_baseline(path: Path) -> dict | None:
204
204
 
205
205
 
206
206
  def _save_baseline(path: Path, bundle: dict) -> None:
207
- """Write the current envelope to disk for later drift comparison."""
207
+ """Write the current envelope to disk for later drift comparison.
208
+
209
+ Stamps ``_meta.timestamp`` (UTC) at save time so consumers — drift
210
+ detection + ``pr-comment-render --from-baseline`` age line — can
211
+ compute baseline age without needing the original CLI envelope.
212
+ """
208
213
  path.parent.mkdir(parents=True, exist_ok=True)
209
- path.write_text(_json.dumps(bundle, indent=2), encoding="utf-8")
214
+ out = dict(bundle)
215
+ meta = dict(out.get("_meta") or {})
216
+ meta["timestamp"] = utc_timestamp()
217
+ out["_meta"] = meta
218
+ path.write_text(_json.dumps(out, indent=2), encoding="utf-8")
210
219
 
211
220
 
212
221
  def _compute_drift(current: dict, baseline: dict | None) -> dict | None:
@@ -1365,6 +1374,135 @@ def _determine_verdict(
1365
1374
  return "SAFE", ["all signals below review thresholds"]
1366
1375
 
1367
1376
 
1377
+ # ----------------------------------------------------- pr_analyze sub-helpers ---
1378
+ #
1379
+ # Extracted from pr_analyze (Phase P24, 2026-05-06) to drop the
1380
+ # coordinator's cognitive complexity below the project's 99-cc gate.
1381
+
1382
+
1383
+ def _serve_from_cache(
1384
+ diff_text: str,
1385
+ rules_path: Path,
1386
+ block_threshold: int,
1387
+ language_override: str | None,
1388
+ cache_dir_path: Path,
1389
+ *,
1390
+ json_mode: bool,
1391
+ quiet: bool,
1392
+ gate: bool,
1393
+ ) -> bool:
1394
+ """Try the envelope cache. On hit emit the cached output and return True.
1395
+
1396
+ Returns False on miss so the caller continues with the slow pr-prep
1397
+ pipeline. Top-level ``cache_hit`` + ``cache_key`` keys survive the
1398
+ json_envelope() ``_meta`` rebuild.
1399
+ """
1400
+ key = _cache_key(diff_text, rules_path, block_threshold, language_override)
1401
+ cached = _load_cache(cache_dir_path, key)
1402
+ if cached is None:
1403
+ return False
1404
+ cached["cache_hit"] = True
1405
+ cached["cache_key"] = key
1406
+ s = cached.get("summary") or {}
1407
+ if json_mode:
1408
+ click.echo(to_json(json_envelope("pr-analyze", **cached)))
1409
+ elif quiet:
1410
+ click.echo(
1411
+ f"VERDICT: {s.get('verdict', '?')} (cached, blast {s.get('blast_radius', '?')}, "
1412
+ f"ai {s.get('ai_likelihood', '?')}, rules {s.get('rule_violations', 0)})"
1413
+ )
1414
+ else:
1415
+ click.echo(f"VERDICT: {s.get('verdict', '?')} [cache hit]")
1416
+ if gate and s.get("verdict") == "BLOCK":
1417
+ sys.exit(EXIT_GATE_BLOCK)
1418
+ return True
1419
+
1420
+
1421
+ def _apply_drift(
1422
+ bundle: dict,
1423
+ base_path: Path,
1424
+ verdict: str,
1425
+ reasons: list[str],
1426
+ ) -> tuple[str, list[str]]:
1427
+ """Compute drift vs baseline + auto-escalate verdict on regression.
1428
+
1429
+ Mutates ``bundle`` in place to add the drift block and updated summary.
1430
+ Returns the (possibly escalated) ``(verdict, reasons)``.
1431
+ """
1432
+ baseline_envelope = _load_baseline(base_path)
1433
+ drift = _compute_drift(bundle, baseline_envelope)
1434
+ if not drift:
1435
+ return verdict, reasons
1436
+ bundle["drift"] = drift
1437
+ if drift["regression"] and verdict == "SAFE":
1438
+ verdict = "REVIEW"
1439
+ reasons.append(
1440
+ f"Drift regression vs baseline: blast {drift['blast_radius_delta']:+d}, "
1441
+ f"ai {drift['ai_likelihood_delta']:+d}, +{drift['new_violation_count']} violations"
1442
+ )
1443
+ bundle["summary"]["verdict"] = verdict
1444
+ bundle["summary"]["reasons"] = reasons
1445
+ elif (
1446
+ drift["regression"]
1447
+ and verdict == "REVIEW"
1448
+ and (drift["blast_radius_delta"] >= 20 or drift["new_violation_count"] >= 3)
1449
+ ):
1450
+ verdict = "BLOCK"
1451
+ reasons.append("Drift regression severe enough to escalate REVIEW → BLOCK")
1452
+ bundle["summary"]["verdict"] = verdict
1453
+ bundle["summary"]["reasons"] = reasons
1454
+ return verdict, reasons
1455
+
1456
+
1457
+ def _emit_audit_trail(
1458
+ bundle: dict,
1459
+ trail_path: Path,
1460
+ diff_text: str,
1461
+ intent: str | None,
1462
+ reviewers_payload: dict | None,
1463
+ verdict: str,
1464
+ reasons: list[str],
1465
+ ) -> tuple[str, list[str]]:
1466
+ """Pre-verify chain integrity, append the new record, escalate on tampering.
1467
+
1468
+ Mutates ``bundle`` in place to add the audit_trail block. Returns the
1469
+ (possibly escalated) ``(verdict, reasons)``.
1470
+ """
1471
+ # Lazy import to avoid pulling cmd_audit_trail_verify on every pr-analyze.
1472
+ from roam.commands.cmd_audit_trail_verify import _verify_chain
1473
+
1474
+ _, chain_issues = _verify_chain(trail_path)
1475
+ real_issues = [i for i in chain_issues if "not found" not in i.get("issue", "")]
1476
+ chain_was_valid = not real_issues
1477
+
1478
+ audit_record = _emit_audit_trail_record(
1479
+ audit_trail_path=trail_path,
1480
+ diff_text=diff_text,
1481
+ bundle=bundle,
1482
+ intent=intent,
1483
+ reviewers_payload=reviewers_payload,
1484
+ )
1485
+ bundle["audit_trail"] = {
1486
+ "path": str(trail_path),
1487
+ "record": audit_record,
1488
+ "chain_status": {
1489
+ "pre_emission_chain_valid": chain_was_valid,
1490
+ "pre_emission_issues": real_issues,
1491
+ },
1492
+ }
1493
+ if not chain_was_valid:
1494
+ reasons.append(
1495
+ f"Audit-trail chain broken before append ({len(real_issues)} pre-existing issue(s)) — "
1496
+ "tampering or partial-write corruption detected"
1497
+ )
1498
+ if verdict != "BLOCK":
1499
+ bundle["summary"]["verdict_pre_chain_break"] = verdict
1500
+ verdict = "BLOCK"
1501
+ bundle["summary"]["verdict"] = verdict
1502
+ bundle["summary"]["reasons"] = reasons
1503
+ return verdict, reasons
1504
+
1505
+
1368
1506
  # ----------------------------------------------------------- batch processing ---
1369
1507
 
1370
1508
 
@@ -1788,28 +1926,17 @@ def pr_analyze(
1788
1926
  # Cache lookup — bypasses pr-prep (the slow part) on hit.
1789
1927
  cache_dir_path = Path(cache_dir) if cache_dir else DEFAULT_CACHE_DIR
1790
1928
  rules_path = Path(rules_file) if rules_file else (Path(".roam") / "rules.yml")
1791
- cached_bundle: dict | None = None
1792
- if cache:
1793
- key = _cache_key(diff_text, rules_path, block_threshold, language_override)
1794
- cached_bundle = _load_cache(cache_dir_path, key)
1795
- if cached_bundle is not None:
1796
- # Surface as top-level keys so json_envelope's _meta rebuild doesn't strip them.
1797
- cached_bundle["cache_hit"] = True
1798
- cached_bundle["cache_key"] = key
1799
- if json_mode:
1800
- click.echo(to_json(json_envelope("pr-analyze", **cached_bundle)))
1801
- elif quiet:
1802
- s = cached_bundle.get("summary") or {}
1803
- click.echo(
1804
- f"VERDICT: {s.get('verdict', '?')} (cached, blast {s.get('blast_radius', '?')}, "
1805
- f"ai {s.get('ai_likelihood', '?')}, rules {s.get('rule_violations', 0)})"
1806
- )
1807
- else:
1808
- s = cached_bundle.get("summary") or {}
1809
- click.echo(f"VERDICT: {s.get('verdict', '?')} [cache hit]")
1810
- if gate and (cached_bundle.get("summary") or {}).get("verdict") == "BLOCK":
1811
- sys.exit(EXIT_GATE_BLOCK)
1812
- return
1929
+ if cache and _serve_from_cache(
1930
+ diff_text,
1931
+ rules_path,
1932
+ block_threshold,
1933
+ language_override,
1934
+ cache_dir_path,
1935
+ json_mode=json_mode,
1936
+ quiet=quiet,
1937
+ gate=gate,
1938
+ ):
1939
+ return
1813
1940
 
1814
1941
  prep_payload = _capture_pr_prep(commit_range, high_callers)
1815
1942
  ai = _compute_ai_likelihood(diff_text, language_override=language_override)
@@ -1883,28 +2010,7 @@ def pr_analyze(
1883
2010
 
1884
2011
  # --- Baseline drift detection -----------------------------------------
1885
2012
  base_path = Path(baseline_path) if baseline_path else DEFAULT_BASELINE_PATH
1886
- baseline_envelope = _load_baseline(base_path)
1887
- drift = _compute_drift(bundle, baseline_envelope)
1888
- if drift:
1889
- bundle["drift"] = drift
1890
- # Adjust verdict ONE tier upward on regression (SAFE → REVIEW; REVIEW → BLOCK)
1891
- if drift["regression"] and verdict == "SAFE":
1892
- verdict = "REVIEW"
1893
- reasons.append(
1894
- f"Drift regression vs baseline: blast {drift['blast_radius_delta']:+d}, "
1895
- f"ai {drift['ai_likelihood_delta']:+d}, +{drift['new_violation_count']} violations"
1896
- )
1897
- bundle["summary"]["verdict"] = verdict
1898
- bundle["summary"]["reasons"] = reasons
1899
- elif (
1900
- drift["regression"]
1901
- and verdict == "REVIEW"
1902
- and (drift["blast_radius_delta"] >= 20 or drift["new_violation_count"] >= 3)
1903
- ):
1904
- verdict = "BLOCK"
1905
- reasons.append("Drift regression severe enough to escalate REVIEW → BLOCK")
1906
- bundle["summary"]["verdict"] = verdict
1907
- bundle["summary"]["reasons"] = reasons
2013
+ verdict, reasons = _apply_drift(bundle, base_path, verdict, reasons)
1908
2014
 
1909
2015
  if save_baseline:
1910
2016
  # Save AFTER drift logic so saved envelope reflects the post-drift verdict
@@ -1922,50 +2028,17 @@ def pr_analyze(
1922
2028
  _save_cache(cache_dir_path, cache_save_key, bundle)
1923
2029
  bundle.setdefault("_meta", {})["cache_saved_to"] = str(_cache_path(cache_dir_path, cache_save_key))
1924
2030
 
1925
- audit_record: dict | None = None
1926
- audit_chain_status: dict | None = None
1927
2031
  if audit_trail:
1928
2032
  trail_path = Path(audit_trail_path) if audit_trail_path else DEFAULT_AUDIT_TRAIL_PATH
1929
-
1930
- # Pre-emission integrity check — if the existing chain is broken,
1931
- # surface the issue BEFORE appending so we don't compound corruption.
1932
- # Lazy import to avoid pulling cmd_audit_trail_verify on every pr-analyze.
1933
- from roam.commands.cmd_audit_trail_verify import _verify_chain
1934
-
1935
- _, chain_issues = _verify_chain(trail_path)
1936
- # Filter out the "audit trail not found" issue — that's the genesis case.
1937
- real_issues = [i for i in chain_issues if "not found" not in i.get("issue", "")]
1938
- chain_was_valid = not real_issues
1939
- audit_chain_status = {
1940
- "pre_emission_chain_valid": chain_was_valid,
1941
- "pre_emission_issues": real_issues,
1942
- }
1943
-
1944
- audit_record = _emit_audit_trail_record(
1945
- audit_trail_path=trail_path,
1946
- diff_text=diff_text,
1947
- bundle=bundle,
1948
- intent=intent,
1949
- reviewers_payload=reviewers_payload,
2033
+ verdict, reasons = _emit_audit_trail(
2034
+ bundle,
2035
+ trail_path,
2036
+ diff_text,
2037
+ intent,
2038
+ reviewers_payload,
2039
+ verdict,
2040
+ reasons,
1950
2041
  )
1951
- bundle["audit_trail"] = {
1952
- "path": str(trail_path),
1953
- "record": audit_record,
1954
- "chain_status": audit_chain_status,
1955
- }
1956
-
1957
- # Auto-escalate verdict if the chain was tampered with — a broken
1958
- # audit trail in CI is a compliance incident, gate must fire.
1959
- if not chain_was_valid:
1960
- reasons.append(
1961
- f"Audit-trail chain broken before append ({len(real_issues)} pre-existing issue(s)) — "
1962
- "tampering or partial-write corruption detected"
1963
- )
1964
- if verdict != "BLOCK":
1965
- bundle["summary"]["verdict_pre_chain_break"] = verdict
1966
- verdict = "BLOCK"
1967
- bundle["summary"]["verdict"] = verdict
1968
- bundle["summary"]["reasons"] = reasons
1969
2042
 
1970
2043
  if json_mode:
1971
2044
  click.echo(to_json(json_envelope("pr-analyze", **bundle)))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: roam-code
3
- Version: 12.26
3
+ Version: 12.26.1
4
4
  Summary: Instant codebase comprehension for AI coding agents
5
5
  Author: CosmoHac
6
6
  License-Expression: Apache-2.0
@@ -623,6 +623,7 @@ tests/test_v1221_query_timeout.py
623
623
  tests/test_v1221_untested_commands.py
624
624
  tests/test_v12_2.py
625
625
  tests/test_v2_edge_cases.py
626
+ tests/test_v2_integration.py
626
627
  tests/test_v6_features.py
627
628
  tests/test_v71_features.py
628
629
  tests/test_v7_features.py
@@ -257,6 +257,75 @@ def test_cli_gate_exits_5_on_partial_conformance(tmp_path):
257
257
  assert result.exit_code == EXIT_GATE_FAILURE
258
258
 
259
259
 
260
+ def test_cli_sarif_emits_valid_envelope(tmp_path):
261
+ """--sarif emits a valid SARIF 2.1.0 doc with a result per failed check."""
262
+ runner = CliRunner()
263
+ from roam.cli import cli
264
+
265
+ trail = tmp_path / "trail.jsonl"
266
+ rec = _full_record("SAFE", _dt.datetime.now(_dt.timezone.utc).isoformat().replace("+00:00", "Z"))
267
+ _write_chain(trail, [rec])
268
+
269
+ # Global --sarif comes BEFORE the subcommand name
270
+ result = runner.invoke(cli, ["--sarif", "audit-trail-conformance-check", "--input", str(trail)])
271
+ assert result.exit_code == 0
272
+ doc = _json.loads(result.output)
273
+ assert doc["version"] == "2.1.0"
274
+ assert doc["runs"][0]["tool"]["driver"]["name"] == "roam-code"
275
+ # Recent-only timestamps fail retention → at least 1 result
276
+ results = doc["runs"][0]["results"]
277
+ assert len(results) >= 1
278
+ rule_ids = {r["ruleId"] for r in results}
279
+ assert "retention" in rule_ids
280
+ # Each rule has helpUri pointing at the regulation
281
+ rules = doc["runs"][0]["tool"]["driver"]["rules"]
282
+ assert all("artificialintelligenceact.eu" in r.get("helpUri", "") for r in rules)
283
+
284
+
285
+ def test_cli_sarif_writes_to_file(tmp_path):
286
+ """--sarif --sarif-output writes to the file + emits a single VERDICT line."""
287
+ runner = CliRunner()
288
+ from roam.cli import cli
289
+
290
+ trail = tmp_path / "trail.jsonl"
291
+ _write_chain(
292
+ trail,
293
+ [_full_record("SAFE", _dt.datetime.now(_dt.timezone.utc).isoformat().replace("+00:00", "Z"))],
294
+ )
295
+ sarif_path = tmp_path / "out.sarif"
296
+ result = runner.invoke(
297
+ cli,
298
+ [
299
+ "--sarif",
300
+ "audit-trail-conformance-check",
301
+ "--input",
302
+ str(trail),
303
+ "--sarif-output",
304
+ str(sarif_path),
305
+ ],
306
+ )
307
+ assert result.exit_code == 0
308
+ assert sarif_path.exists()
309
+ doc = _json.loads(sarif_path.read_text(encoding="utf-8"))
310
+ assert doc["version"] == "2.1.0"
311
+ assert "VERDICT:" in result.output
312
+
313
+
314
+ def test_cli_sarif_with_perfect_score_emits_no_results(tmp_path):
315
+ """A 100/100 trail produces a SARIF with rules but zero findings."""
316
+ runner = CliRunner()
317
+ from roam.cli import cli
318
+
319
+ trail = tmp_path / "trail.jsonl"
320
+ old_ts = (_dt.datetime.now(_dt.timezone.utc) - _dt.timedelta(days=200)).isoformat().replace("+00:00", "Z")
321
+ recent_ts = _dt.datetime.now(_dt.timezone.utc).isoformat().replace("+00:00", "Z")
322
+ _write_chain(trail, [_full_record("SAFE", old_ts), _full_record("REVIEW", recent_ts)])
323
+
324
+ result = runner.invoke(cli, ["--sarif", "audit-trail-conformance-check", "--input", str(trail)])
325
+ doc = _json.loads(result.output)
326
+ assert doc["runs"][0]["results"] == [] # all 6 checks passed
327
+
328
+
260
329
  def test_cli_custom_retention_days(tmp_path):
261
330
  runner = CliRunner()
262
331
  from roam.cli import cli