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.
- {roam_code-12.26/src/roam_code.egg-info → roam_code-12.26.1}/PKG-INFO +1 -1
- {roam_code-12.26 → roam_code-12.26.1}/pyproject.toml +1 -1
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_audit_trail_conformance.py +69 -2
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_metrics_push.py +75 -62
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_pr_analyze.py +160 -87
- {roam_code-12.26 → roam_code-12.26.1/src/roam_code.egg-info}/PKG-INFO +1 -1
- {roam_code-12.26 → roam_code-12.26.1}/src/roam_code.egg-info/SOURCES.txt +1 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_audit_trail_conformance.py +69 -0
- roam_code-12.26.1/tests/test_v2_integration.py +237 -0
- {roam_code-12.26 → roam_code-12.26.1}/LICENSE +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/README.md +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/setup.cfg +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/__main__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/analysis/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/analysis/effects.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/analysis/taint.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/api.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/ask/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/ask/classifier.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/ask/recipes.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/ask/runner.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/ask/workflow.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/attest/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/attest/cga.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/base.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/bridge_config.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/bridge_django.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/bridge_protobuf.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/bridge_rest_api.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/bridge_salesforce.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/bridge_template.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/bridges/registry.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/catalog/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/catalog/detectors.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/catalog/fixes.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/catalog/python_idioms.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/catalog/smells.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/catalog/tasks.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/cli.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/audit_trail_helpers.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/changed_files.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_adrs.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_adversarial.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_affected.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_affected_tests.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_agent_context.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_agent_export.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_agent_plan.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_ai_ratio.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_ai_readiness.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_alerts.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_annotate.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_api.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_api_changes.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_api_drift.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_ask.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_attest.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_audit.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_audit_trail_export.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_audit_trail_verify.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_auth_gaps.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_bisect.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_breaking.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_budget.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_bus_factor.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_capsule.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_cga.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_changelog.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_check_rules.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_ci_setup.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_clean.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_clones.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_closure.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_clusters.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_codeowners.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_complexity.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_config.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_congestion.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_context.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_conventions.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_coupling.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_coverage_gaps.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_critique.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_cut.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_dark_matter.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_dashboard.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_dead.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_debt.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_deps.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_describe.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_dev_profile.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_diagnose.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_diff.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_disambiguate.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_doc_staleness.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_docs_coverage.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_doctor.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_dogfood.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_drift.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_duplicates.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_effects.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_endpoints.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_entry_points.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_eval_retrieve.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_exit_codes.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_fan.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_file.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_fingerprint.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_fitness.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_flag_dead.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_fleet.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_fn_coupling.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_forecast.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_graph_export.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_graph_stats.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_grep.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_guard.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_health.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_help_search.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_hooks.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_hotspots.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_hover.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_impact.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_index.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_index_bundle.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_index_stats.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_ingest_trace.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_init.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_intent.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_invariants.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_layers.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_map.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_math.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_mcp_setup.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_mcp_status.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_metrics.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_migration_safety.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_minimap.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_missing_index.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_module.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_mutate.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_n1.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_oracle.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_orchestrate.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_orphan_imports.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_orphan_routes.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_over_fetch.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_owner.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_partition.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_path_coverage.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_patterns.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_plan.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_plan_refactor.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_plugins.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_pr_comment_render.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_pr_diff.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_pr_prep.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_pr_risk.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_pre_commit.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_preflight.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_py_modern.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_py_types.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_pytest_fixtures.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_recipes.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_recommend.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_relate.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_report.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_reset.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_retrieve.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_risk.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_rules.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_rules_validate.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_safe_delete.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_safe_zones.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_sbom.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_schema.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_search.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_search_semantic.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_secrets.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_semantic_diff.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_simulate.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_simulate_departure.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_sketch.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_smells.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_spectral.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_split.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_stats.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_suggest_refactoring.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_suggest_reviewers.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_supply_chain.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_symbol.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_syntax_check.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_taint.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_telemetry.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_test_gaps.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_test_impact.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_test_pyramid.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_test_scaffold.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_testmap.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_timeline.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_tour.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_trace.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_trends.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_triage.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_understand.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_uses.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_verify.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_verify_imports.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_version.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_vibe_check.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_visualize.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_vuln_map.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_vuln_reach.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_vulns.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_watch.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_weather.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_why.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_why_fail.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_workflow.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_ws.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/cmd_xlang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/codeowners_helpers.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/context_helpers.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/gate_presets.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/git_helpers.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/graph_helpers.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/metrics_history.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/next_steps.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/resolve.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/commands/suppression.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/competitor_site_data.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/config.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/coverage_reports.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/critique/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/critique/aggregator.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/critique/checks.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/db/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/db/connection.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/db/queries.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/db/schema.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/eval/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/eval/harness.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/exit_codes.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/fleet/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/fleet/adapters.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/fleet/manifest.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/git_utils.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/anomaly.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/builder.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/clone_detect.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/clusters.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/cycles.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/dark_matter.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/diff.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/fingerprint.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/layers.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/pagerank.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/partition.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/pathfinding.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/propagation.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/simulate.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/spectral.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/graph/stats.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/complexity.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/discovery.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/django_post.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/file_roles.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/git_stats.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/gitignore.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/incremental.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/indexer.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/parser.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/pytest_fixtures.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/registry_dispatch.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/relations.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/symbols.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/index/test_conventions.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/apex_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/aura_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/base.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/c_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/csharp_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/extractor_schema.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/extractors/kotlin.yaml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/foxpro_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/generic_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/go_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/hcl_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/java_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/javascript_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/kotlin_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/php_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/python_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/query_engine.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/registry.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/ruby_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/rust_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/scala_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/sfxml_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/sql_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/swift_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/typescript_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/visualforce_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/languages/yaml_lang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp-server-card.json +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_extras/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_extras/completions.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_extras/concurrency.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_extras/progress.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_extras/sampling.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_extras/session.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_extras/watcher.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/mcp_server.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/observability.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/confidence.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/errors.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/file_role_hints.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/formatter.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/framework_filter.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/mermaid.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/project_shape.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/sarif.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/output/schema_registry.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/plugins.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/refactor/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/refactor/codegen.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/refactor/transforms.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/retrieve/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/retrieve/learned_ranker.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/retrieve/pipeline.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/retrieve/rerank.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/retrieve/seeds.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/retrieve/semantic.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/rules/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/rules/ast_match.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/rules/builtin.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/rules/dataflow.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/rules/engine.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/runtime/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/runtime/daemon.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/runtime/graph_backend.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/runtime/hotspots.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/runtime/lock_modes.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/runtime/lockmgr.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/runtime/trace_ingest.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/search/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/search/framework_packs.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/search/index_embeddings.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/search/onnx_embeddings.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/search/tfidf.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/aibom_extension.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_classifier.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_engine.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/api_error_leak.yaml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/java_fileupload_path_traversal.yaml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/js_insecure_jwt_decode.yaml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/js_localstorage_secrets.yaml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/js_prototype_pollution.yaml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/js_ssrf.yaml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/js_xss.yaml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/python_basic.yaml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/python_deserialization.yaml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/python_path_traversal.yaml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/python_socketio_remote_source.yaml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/python_sqli.yaml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/python_urllib_open_redirect.yaml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/taint_rules/vue_v_html.yaml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/vuln_reach.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/security/vuln_store.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/surface_counts.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/telemetry.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/templates/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/templates/ci/Jenkinsfile +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/templates/ci/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/templates/ci/agent-review.yml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/templates/ci/azure-pipelines.yml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/templates/ci/bitbucket-pipelines.yml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/templates/ci/gitlab-ci.yml +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/workspace/__init__.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/workspace/aggregator.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/workspace/api_scanner.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/workspace/config.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam/workspace/db.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam_code.egg-info/dependency_links.txt +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam_code.egg-info/entry_points.txt +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam_code.egg-info/requires.txt +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/src/roam_code.egg-info/top_level.txt +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_adrs.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_adversarial.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_affected.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_agent_export.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_agent_mode.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_agent_plan_context.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_ai_ratio.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_ai_readiness.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_alerts_cmd.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_annotations.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_anomaly.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_api_changes.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_api_drift.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_ask.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_attest.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_audit_trail_aggregate.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_audit_trail_sequence.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_audit_trail_verify.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_auth_gaps.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_backend_fixes_round2.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_backend_fixes_round3.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_basic.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_batch_mcp.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_bisect.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_bridge_django.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_bridges.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_bridges_extended.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_budget.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_budget_flag.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_budget_phase2.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_bus_factor.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_capsule.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_cga.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_check_rules.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_ci_gate_eval.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_ci_sarif_guard.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_ci_setup.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_clones.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_closure.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_codeowners.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_commands_architecture.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_commands_exploration.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_commands_health.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_commands_refactoring.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_commands_workflow.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_competitor_site_data.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_comprehensive.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_config.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_congestion.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_context_propagation.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_conventions_cmd.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_coverage_gaps_cmd.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_coverage_ingestion.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_critique.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_cut.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_dark_matter.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_dark_matter_helpers.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_dashboard.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_dataflow_dead.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_dead_aging.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_defer_loading.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_demo_gif_asset.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_describe.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_detail_flag_hints.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_detector_precision.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_deterministic_output.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_dev_profile.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_difficulty_scoring.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_doc_consistency.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_doc_staleness.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_docker_assets.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_docs_coverage.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_docs_site_quality.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_doctor.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_dogfood.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_drift.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_drift_by_team.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_duplicates.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_effects.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_effects_propagation.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_endpoints.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_entry_points_cmd.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_eval_retrieve.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_except_pass_narrow.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_exclude_patterns.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_exit_codes.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_extractor_grammar_drift.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_fallback_contracts.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_file_roles.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_fingerprint.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_fixes.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_flag_dead.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_fleet.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_fn_coupling.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_forecast.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_formatters.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_foxpro.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_framework_detection.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_gate_presets.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_git_helpers.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_git_utils.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_guard.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_health_gate.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_hooks.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_hotspots.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_hover.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_index.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_index_bundle.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_init_cmd.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_install_check.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_intent.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_invariants.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_json_contracts.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_json_error_envelope.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_kotlin_swift_extractors.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_language_corpus.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_languages.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_library_api.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_math.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_math_tips.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_mcp_extras.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_mcp_server.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_mcp_setup.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_mermaid.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_metrics_cmd.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_metrics_push.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_migration_safety.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_minimap.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_missing_index.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_mutate.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_n1.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_next_steps.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_onboard.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_oracle.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_orchestrate.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_orphan_routes.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_oss_bench_harness.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_over_fetch.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_pagerank_truncation.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_partition.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_path_coverage.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_patterns_cmd.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_performance.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_personalized_pagerank.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_plan.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_plugin_discovery.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_analyze.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_analyze_cache.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_analyze_edge_cases.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_analyze_v2_signals.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_comment_render.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_comment_script.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_diff.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_pr_risk_author.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_progress.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_progressive_disclosure.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_properties.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_pytest_fixtures.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_python_extractor_v2.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_python_idioms_e2e.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_python_pivot.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_readme_surface_consistency.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_realworld_feedback.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_refactoring_intelligence.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_registry_dispatch.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_relate.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_report.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_reset_clean.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_resolve.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_retrieve.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_retrieve_cross_repo.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_retrieve_seeds.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_risk.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_ruby.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_rule_profiles.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_rules.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_rules_ast_match.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_rules_community_pack.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_rules_dataflow.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_rules_symbol_requirements.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_rules_validate.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_runtime.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_runtime_score.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_salesforce.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_sarif_flag.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_sbom.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_scala.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_schema_versioning.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_search_explain.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_secrets.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_secrets_v2.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_semantic_diff.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_semantic_onnx.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_semantic_search.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_simulate.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_simulate_departure.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_sketch.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_smells.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_smoke.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_sna_metrics.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_spectral.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_split_cmd.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_sql.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_suggest_reviewers.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_supply_chain.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_surface_counts.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_syntax_check.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_taint.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_taint_analysis.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_taint_classifier.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_taint_intraprocedural.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_test_conventions.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_test_gaps.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_test_scaffold.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_testmap.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_top_flag_consistency.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_tour_cmd.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_trends.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_trends_cohort.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_triage.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_uses_cmd.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1215_passes.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1216_passes.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1216_passes_41_50.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1216_passes_51_60.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1217_passes_61_80.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1218_passes_81_90.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1219_passes_91_100.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1220_passes_101_110.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1221_query_timeout.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v1221_untested_commands.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v12_2.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v2_edge_cases.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v6_features.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v71_features.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v7_features.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_v82_features.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_verify.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_verify_imports.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_vibe_check.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_visualize.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_vuln.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_vulns_cmd.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_watch.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_why.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_workspace.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_ws_resolve_fixes.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_xlang.py +0 -0
- {roam_code-12.26 → roam_code-12.26.1}/tests/test_yaml_hcl.py +0 -0
|
@@ -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
|
|
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
|
-
|
|
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
|
|
106
|
-
|
|
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
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
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":
|
|
221
|
+
"metrics": _extract_metrics(audit_envelope),
|
|
166
222
|
}
|
|
167
|
-
|
|
168
223
|
if include_hotspots:
|
|
169
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
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)))
|
|
@@ -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
|