roam-code 12.19__tar.gz → 12.20__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {roam_code-12.19/src/roam_code.egg-info → roam_code-12.20}/PKG-INFO +1 -1
- {roam_code-12.19 → roam_code-12.20}/pyproject.toml +1 -1
- {roam_code-12.19 → roam_code-12.20}/src/roam/catalog/python_idioms.py +0 -22
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_context.py +70 -51
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_dead.py +188 -162
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_doctor.py +8 -1
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_health.py +13 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_orphan_imports.py +34 -13
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_testmap.py +13 -1
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_understand.py +8 -4
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/metrics_history.py +28 -14
- {roam_code-12.19 → roam_code-12.20}/src/roam/competitor_site_data.py +1 -12
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/query_engine.py +111 -104
- {roam_code-12.19 → roam_code-12.20}/src/roam/mcp-server-card.json +1 -1
- {roam_code-12.19 → roam_code-12.20}/src/roam/output/errors.py +0 -5
- {roam_code-12.19 → roam_code-12.20/src/roam_code.egg-info}/PKG-INFO +1 -1
- {roam_code-12.19 → roam_code-12.20}/src/roam_code.egg-info/SOURCES.txt +1 -0
- roam_code-12.20/tests/test_v1220_passes_101_110.py +107 -0
- {roam_code-12.19 → roam_code-12.20}/LICENSE +0 -0
- {roam_code-12.19 → roam_code-12.20}/README.md +0 -0
- {roam_code-12.19 → roam_code-12.20}/setup.cfg +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/__main__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/analysis/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/analysis/effects.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/analysis/taint.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/api.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/ask/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/ask/classifier.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/ask/recipes.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/ask/runner.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/ask/workflow.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/attest/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/attest/cga.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/base.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/bridge_config.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/bridge_django.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/bridge_protobuf.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/bridge_rest_api.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/bridge_salesforce.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/bridge_template.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/bridges/registry.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/catalog/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/catalog/detectors.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/catalog/fixes.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/catalog/smells.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/catalog/tasks.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/cli.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/changed_files.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_adrs.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_adversarial.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_affected.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_affected_tests.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_agent_context.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_agent_export.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_agent_plan.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_ai_ratio.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_ai_readiness.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_alerts.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_annotate.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_api.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_api_changes.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_api_drift.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_ask.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_attest.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_audit.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_auth_gaps.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_bisect.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_breaking.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_budget.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_bus_factor.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_capsule.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_cga.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_changelog.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_check_rules.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_ci_setup.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_clean.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_clones.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_closure.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_clusters.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_codeowners.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_complexity.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_config.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_congestion.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_conventions.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_coupling.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_coverage_gaps.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_critique.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_cut.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_dark_matter.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_dashboard.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_debt.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_deps.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_describe.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_dev_profile.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_diagnose.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_diff.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_disambiguate.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_doc_staleness.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_docs_coverage.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_drift.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_duplicates.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_effects.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_endpoints.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_entry_points.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_eval_retrieve.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_exit_codes.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_fan.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_file.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_fingerprint.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_fitness.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_flag_dead.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_fleet.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_fn_coupling.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_forecast.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_graph_export.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_graph_stats.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_grep.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_guard.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_help_search.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_hooks.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_hotspots.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_hover.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_impact.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_index.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_index_bundle.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_index_stats.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_ingest_trace.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_init.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_intent.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_invariants.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_layers.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_map.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_math.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_mcp_setup.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_mcp_status.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_metrics.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_migration_safety.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_minimap.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_missing_index.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_module.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_mutate.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_n1.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_oracle.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_orchestrate.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_orphan_routes.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_over_fetch.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_owner.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_partition.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_path_coverage.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_patterns.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_plan.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_plan_refactor.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_plugins.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_pr_diff.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_pr_prep.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_pr_risk.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_pre_commit.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_preflight.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_py_modern.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_py_types.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_pytest_fixtures.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_recipes.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_recommend.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_relate.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_report.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_reset.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_retrieve.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_risk.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_rules.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_safe_delete.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_safe_zones.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_sbom.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_schema.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_search.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_search_semantic.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_secrets.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_semantic_diff.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_simulate.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_simulate_departure.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_sketch.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_smells.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_spectral.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_split.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_stats.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_suggest_refactoring.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_suggest_reviewers.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_supply_chain.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_symbol.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_syntax_check.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_taint.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_telemetry.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_test_gaps.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_test_impact.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_test_pyramid.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_test_scaffold.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_timeline.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_tour.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_trace.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_trends.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_triage.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_uses.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_verify.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_verify_imports.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_version.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_vibe_check.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_visualize.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_vuln_map.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_vuln_reach.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_vulns.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_watch.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_weather.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_why.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_why_fail.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_workflow.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_ws.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/cmd_xlang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/codeowners_helpers.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/context_helpers.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/gate_presets.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/graph_helpers.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/next_steps.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/resolve.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/commands/suppression.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/config.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/coverage_reports.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/critique/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/critique/aggregator.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/critique/checks.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/db/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/db/connection.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/db/queries.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/db/schema.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/eval/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/eval/harness.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/exit_codes.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/fleet/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/fleet/adapters.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/fleet/manifest.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/git_utils.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/anomaly.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/builder.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/clone_detect.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/clusters.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/cycles.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/dark_matter.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/diff.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/fingerprint.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/layers.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/pagerank.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/partition.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/pathfinding.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/propagation.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/simulate.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/spectral.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/graph/stats.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/index/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/index/complexity.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/index/discovery.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/index/django_post.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/index/file_roles.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/index/git_stats.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/index/gitignore.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/index/incremental.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/index/indexer.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/index/parser.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/index/pytest_fixtures.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/index/registry_dispatch.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/index/relations.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/index/symbols.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/index/test_conventions.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/apex_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/aura_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/base.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/c_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/csharp_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/extractor_schema.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/extractors/kotlin.yaml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/foxpro_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/generic_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/go_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/hcl_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/java_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/javascript_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/kotlin_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/php_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/python_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/registry.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/ruby_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/rust_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/scala_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/sfxml_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/sql_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/swift_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/typescript_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/visualforce_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/languages/yaml_lang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_extras/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_extras/completions.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_extras/concurrency.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_extras/progress.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_extras/sampling.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_extras/session.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_extras/watcher.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/mcp_server.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/observability.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/output/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/output/confidence.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/output/file_role_hints.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/output/formatter.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/output/framework_filter.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/output/mermaid.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/output/project_shape.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/output/sarif.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/output/schema_registry.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/plugins.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/refactor/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/refactor/codegen.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/refactor/transforms.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/retrieve/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/retrieve/learned_ranker.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/retrieve/pipeline.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/retrieve/rerank.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/retrieve/seeds.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/retrieve/semantic.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/rules/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/rules/ast_match.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/rules/builtin.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/rules/dataflow.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/rules/engine.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/runtime/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/runtime/daemon.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/runtime/graph_backend.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/runtime/hotspots.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/runtime/lock_modes.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/runtime/lockmgr.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/runtime/trace_ingest.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/search/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/search/framework_packs.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/search/index_embeddings.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/search/onnx_embeddings.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/search/tfidf.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/aibom_extension.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_classifier.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_engine.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/api_error_leak.yaml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/java_fileupload_path_traversal.yaml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/js_insecure_jwt_decode.yaml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/js_localstorage_secrets.yaml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/js_prototype_pollution.yaml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/js_ssrf.yaml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/js_xss.yaml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/python_basic.yaml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/python_deserialization.yaml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/python_path_traversal.yaml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/python_socketio_remote_source.yaml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/python_sqli.yaml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/python_urllib_open_redirect.yaml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/taint_rules/vue_v_html.yaml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/vuln_reach.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/security/vuln_store.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/surface_counts.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/telemetry.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/templates/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/templates/ci/Jenkinsfile +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/templates/ci/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/templates/ci/azure-pipelines.yml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/templates/ci/bitbucket-pipelines.yml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/templates/ci/gitlab-ci.yml +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/workspace/__init__.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/workspace/aggregator.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/workspace/api_scanner.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/workspace/config.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam/workspace/db.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam_code.egg-info/dependency_links.txt +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam_code.egg-info/entry_points.txt +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam_code.egg-info/requires.txt +0 -0
- {roam_code-12.19 → roam_code-12.20}/src/roam_code.egg-info/top_level.txt +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_adrs.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_adversarial.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_affected.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_agent_export.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_agent_mode.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_agent_plan_context.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_ai_ratio.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_ai_readiness.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_alerts_cmd.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_annotations.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_anomaly.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_api_changes.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_api_drift.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_ask.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_attest.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_auth_gaps.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_backend_fixes_round2.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_backend_fixes_round3.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_basic.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_batch_mcp.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_bisect.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_bridge_django.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_bridges.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_bridges_extended.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_budget.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_budget_flag.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_budget_phase2.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_bus_factor.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_capsule.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_cga.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_check_rules.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_ci_gate_eval.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_ci_sarif_guard.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_ci_setup.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_clones.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_closure.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_codeowners.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_commands_architecture.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_commands_exploration.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_commands_health.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_commands_refactoring.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_commands_workflow.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_competitor_site_data.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_comprehensive.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_config.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_congestion.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_context_propagation.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_conventions_cmd.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_coverage_gaps_cmd.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_coverage_ingestion.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_critique.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_cut.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_dark_matter.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_dark_matter_helpers.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_dashboard.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_dataflow_dead.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_dead_aging.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_defer_loading.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_demo_gif_asset.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_describe.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_detail_flag_hints.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_detector_precision.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_deterministic_output.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_dev_profile.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_difficulty_scoring.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_doc_consistency.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_doc_staleness.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_docker_assets.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_docs_coverage.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_docs_site_quality.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_doctor.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_drift.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_drift_by_team.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_duplicates.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_effects.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_effects_propagation.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_endpoints.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_entry_points_cmd.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_eval_retrieve.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_except_pass_narrow.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_exclude_patterns.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_exit_codes.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_extractor_grammar_drift.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_fallback_contracts.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_file_roles.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_fingerprint.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_fixes.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_flag_dead.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_fleet.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_fn_coupling.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_forecast.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_formatters.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_foxpro.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_framework_detection.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_gate_presets.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_git_utils.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_guard.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_health_gate.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_hooks.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_hotspots.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_hover.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_index.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_index_bundle.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_init_cmd.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_install_check.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_intent.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_invariants.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_json_contracts.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_json_error_envelope.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_kotlin_swift_extractors.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_language_corpus.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_languages.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_library_api.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_math.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_math_tips.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_mcp_extras.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_mcp_server.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_mcp_setup.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_mermaid.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_metrics_cmd.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_migration_safety.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_minimap.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_missing_index.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_mutate.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_n1.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_next_steps.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_onboard.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_oracle.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_orchestrate.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_orphan_routes.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_oss_bench_harness.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_over_fetch.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_pagerank_truncation.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_partition.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_path_coverage.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_patterns_cmd.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_performance.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_personalized_pagerank.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_plan.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_plugin_discovery.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_pr_comment_script.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_pr_diff.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_pr_risk_author.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_progress.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_progressive_disclosure.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_properties.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_pytest_fixtures.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_python_extractor_v2.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_python_idioms_e2e.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_python_pivot.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_readme_surface_consistency.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_realworld_feedback.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_refactoring_intelligence.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_registry_dispatch.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_relate.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_report.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_reset_clean.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_resolve.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_retrieve.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_retrieve_cross_repo.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_retrieve_seeds.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_risk.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_ruby.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_rule_profiles.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_rules.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_rules_ast_match.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_rules_community_pack.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_rules_dataflow.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_rules_symbol_requirements.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_runtime.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_runtime_score.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_salesforce.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_sarif_flag.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_sbom.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_scala.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_schema_versioning.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_search_explain.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_secrets.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_secrets_v2.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_semantic_diff.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_semantic_onnx.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_semantic_search.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_simulate.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_simulate_departure.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_sketch.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_smells.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_smoke.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_sna_metrics.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_spectral.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_split_cmd.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_sql.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_suggest_reviewers.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_supply_chain.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_surface_counts.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_syntax_check.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_taint.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_taint_analysis.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_taint_classifier.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_taint_intraprocedural.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_test_conventions.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_test_gaps.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_test_scaffold.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_testmap.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_top_flag_consistency.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_tour_cmd.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_trends.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_trends_cohort.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_triage.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_uses_cmd.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_v1215_passes.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_v1216_passes.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_v1216_passes_41_50.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_v1216_passes_51_60.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_v1217_passes_61_80.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_v1218_passes_81_90.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_v1219_passes_91_100.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_v12_2.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_v6_features.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_v71_features.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_v7_features.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_v82_features.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_verify.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_verify_imports.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_vibe_check.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_visualize.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_vuln.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_vulns_cmd.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_watch.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_why.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_workspace.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_ws_resolve_fixes.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_xlang.py +0 -0
- {roam_code-12.19 → roam_code-12.20}/tests/test_yaml_hcl.py +0 -0
|
@@ -742,28 +742,6 @@ def detect_dict_keys_iter(conn: sqlite3.Connection) -> list[dict]:
|
|
|
742
742
|
return findings
|
|
743
743
|
|
|
744
744
|
|
|
745
|
-
def detect_string_format_old(conn: sqlite3.Connection) -> list[dict]:
|
|
746
|
-
"""Find ``"..."`` ``.format(...)`` calls — prefer f-strings on Py3.6+.
|
|
747
|
-
|
|
748
|
-
Conservative: only flag the explicit ``"literal".format(...)``
|
|
749
|
-
pattern, not ``some_var.format(...)`` (which might be a template
|
|
750
|
-
method on a non-string type).
|
|
751
|
-
"""
|
|
752
|
-
findings: list[dict] = []
|
|
753
|
-
for file_id, path in _python_files(conn):
|
|
754
|
-
text = _file_text(conn, file_id)
|
|
755
|
-
if text:
|
|
756
|
-
text = _strip_strings_and_comments(text)
|
|
757
|
-
if not text:
|
|
758
|
-
continue
|
|
759
|
-
# The string-strip blanks the literal, so we can't match
|
|
760
|
-
# ``"...".format(``. Skip entirely — this detector is best
|
|
761
|
-
# implemented on the raw text. Falls back to no-op when
|
|
762
|
-
# text was stripped. Future: implement an "unstripped scan"
|
|
763
|
-
# that's still safe.
|
|
764
|
-
return findings # disabled until we have a string-aware variant
|
|
765
|
-
|
|
766
|
-
|
|
767
745
|
def detect_async_with_missing(conn: sqlite3.Connection) -> list[dict]:
|
|
768
746
|
"""Find ``aiofiles.open(...)`` / ``httpx.AsyncClient(...)`` not
|
|
769
747
|
inside an ``async with`` — async resource leak.
|
|
@@ -476,6 +476,72 @@ def _gather_batch(conn, resolved, task, session_hint, recent_symbols, use_propag
|
|
|
476
476
|
# ---------------------------------------------------------------------------
|
|
477
477
|
|
|
478
478
|
|
|
479
|
+
def _render_async_badge(sym, row_keys) -> None:
|
|
480
|
+
"""Pass 102 — single-line async badge above the signature."""
|
|
481
|
+
if "is_async" in row_keys and sym["is_async"]:
|
|
482
|
+
click.echo(" [async coroutine]")
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def _render_idiom_badge(sym, sym_kind: str, decorators_str: str, row_keys) -> None:
|
|
486
|
+
"""Pass 102 — surface model-class / fixture / param-test badges."""
|
|
487
|
+
try:
|
|
488
|
+
from roam.catalog.python_idioms import fixture_kind, is_model_class
|
|
489
|
+
except Exception:
|
|
490
|
+
return
|
|
491
|
+
if sym_kind == "class":
|
|
492
|
+
sig_text = sym["signature"] if "signature" in row_keys else ""
|
|
493
|
+
try:
|
|
494
|
+
is_model, kind_label = is_model_class(sig_text, decorators_str)
|
|
495
|
+
except Exception:
|
|
496
|
+
return
|
|
497
|
+
if is_model and kind_label:
|
|
498
|
+
click.echo(f" [{kind_label} model]")
|
|
499
|
+
elif sym_kind in ("function", "method"):
|
|
500
|
+
try:
|
|
501
|
+
fkind = fixture_kind(decorators_str)
|
|
502
|
+
except Exception:
|
|
503
|
+
return
|
|
504
|
+
if fkind:
|
|
505
|
+
click.echo(f" [{fkind}]")
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def _split_decorators_paren_aware(decorators_str: str) -> list[str]:
|
|
509
|
+
"""Pass 102 — paren-aware split.
|
|
510
|
+
|
|
511
|
+
``@parametrize("a,b", [...])`` has commas inside its argument; the
|
|
512
|
+
naive ``str.split(",")`` would break it into fragments. Track
|
|
513
|
+
bracket depth so we only split on top-level commas.
|
|
514
|
+
"""
|
|
515
|
+
decos: list[str] = []
|
|
516
|
+
depth = 0
|
|
517
|
+
current: list[str] = []
|
|
518
|
+
for ch in decorators_str:
|
|
519
|
+
if ch == "," and depth == 0:
|
|
520
|
+
if current:
|
|
521
|
+
decos.append("".join(current).strip())
|
|
522
|
+
current = []
|
|
523
|
+
else:
|
|
524
|
+
current.append(ch)
|
|
525
|
+
if ch in "([{":
|
|
526
|
+
depth += 1
|
|
527
|
+
elif ch in ")]}":
|
|
528
|
+
depth = max(0, depth - 1)
|
|
529
|
+
if current:
|
|
530
|
+
decos.append("".join(current).strip())
|
|
531
|
+
return decos
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def _render_decorators_block(decorators_str: str) -> None:
|
|
535
|
+
"""Pass 102 — print up to 5 decorator first-lines for the symbol."""
|
|
536
|
+
if not decorators_str:
|
|
537
|
+
return
|
|
538
|
+
for d in _split_decorators_paren_aware(decorators_str)[:5]:
|
|
539
|
+
first_line = d.splitlines()[0] if d else ""
|
|
540
|
+
if len(d.splitlines()) > 1:
|
|
541
|
+
first_line += "..."
|
|
542
|
+
click.echo(f" {first_line}")
|
|
543
|
+
|
|
544
|
+
|
|
479
545
|
def _render_text(data):
|
|
480
546
|
"""Print text output for any mode."""
|
|
481
547
|
mode = data["mode"]
|
|
@@ -553,60 +619,13 @@ def _render_single_text(data):
|
|
|
553
619
|
click.echo(f"VERDICT: {verdict}")
|
|
554
620
|
click.echo()
|
|
555
621
|
click.echo(f"=== Context for: {sym['name']}{task_suffix} ===")
|
|
556
|
-
#
|
|
557
|
-
# signature so agents reading context know coroutine semantics
|
|
558
|
-
# without scanning source. ``sym`` is a sqlite3.Row which doesn't
|
|
559
|
-
# expose ``.get`` — guard each access with a key check.
|
|
622
|
+
# Pass 102 — header rendering extracted into helpers.
|
|
560
623
|
_row_keys = sym.keys() if hasattr(sym, "keys") else []
|
|
561
|
-
if "is_async" in _row_keys and sym["is_async"]:
|
|
562
|
-
click.echo(" [async coroutine]")
|
|
563
624
|
decorators_str = (sym["decorators"] if "decorators" in _row_keys else "") or ""
|
|
564
|
-
# Python pivot v12.4-iter: model-class + fixture badges — agents
|
|
565
|
-
# reading context immediately see whether this is "data with
|
|
566
|
-
# validation" (Pydantic/dataclass/attrs/etc.) or a pytest fixture
|
|
567
|
-
# / parametrized test, without scanning source.
|
|
568
625
|
sym_kind = sym["kind"] if "kind" in _row_keys else ""
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
if sym_kind == "class":
|
|
573
|
-
sig_text = sym["signature"] if "signature" in _row_keys else ""
|
|
574
|
-
is_model, kind_label = is_model_class(sig_text, decorators_str)
|
|
575
|
-
if is_model and kind_label:
|
|
576
|
-
click.echo(f" [{kind_label} model]")
|
|
577
|
-
elif sym_kind in ("function", "method"):
|
|
578
|
-
fkind = fixture_kind(decorators_str)
|
|
579
|
-
if fkind:
|
|
580
|
-
click.echo(f" [{fkind}]")
|
|
581
|
-
except Exception:
|
|
582
|
-
pass
|
|
583
|
-
if decorators_str:
|
|
584
|
-
# Decorators are comma-joined but ``@parametrize("a,b,c", [...])``
|
|
585
|
-
# has commas inside its arguments — naive split breaks the
|
|
586
|
-
# display into nonsense fragments. Re-tokenise paren-aware.
|
|
587
|
-
decos: list[str] = []
|
|
588
|
-
depth = 0
|
|
589
|
-
current = []
|
|
590
|
-
for ch in decorators_str:
|
|
591
|
-
if ch == "," and depth == 0:
|
|
592
|
-
if current:
|
|
593
|
-
decos.append("".join(current).strip())
|
|
594
|
-
current = []
|
|
595
|
-
else:
|
|
596
|
-
current.append(ch)
|
|
597
|
-
if ch in "([{":
|
|
598
|
-
depth += 1
|
|
599
|
-
elif ch in ")]}":
|
|
600
|
-
depth = max(0, depth - 1)
|
|
601
|
-
if current:
|
|
602
|
-
decos.append("".join(current).strip())
|
|
603
|
-
for d in decos[:5]:
|
|
604
|
-
# Show the first line of the decorator only — keeps
|
|
605
|
-
# multi-line decorators (e.g. click.option blocks) compact.
|
|
606
|
-
first_line = d.splitlines()[0] if d else ""
|
|
607
|
-
if len(d.splitlines()) > 1:
|
|
608
|
-
first_line += "..."
|
|
609
|
-
click.echo(f" {first_line}")
|
|
626
|
+
_render_async_badge(sym, _row_keys)
|
|
627
|
+
_render_idiom_badge(sym, sym_kind, decorators_str, _row_keys)
|
|
628
|
+
_render_decorators_block(decorators_str)
|
|
610
629
|
click.echo(
|
|
611
630
|
f"{abbrev_kind(sym['kind'])} "
|
|
612
631
|
f"{sym['qualified_name'] or sym['name']}"
|
|
@@ -1057,113 +1057,130 @@ def _extended_summary(extended_data):
|
|
|
1057
1057
|
}
|
|
1058
1058
|
|
|
1059
1059
|
|
|
1060
|
-
def
|
|
1061
|
-
"""
|
|
1060
|
+
def _table_exists(conn, name: str) -> bool:
|
|
1061
|
+
"""Pass 106 — small probe used by the dataflow analyzer.
|
|
1062
1062
|
|
|
1063
|
-
|
|
1063
|
+
Replaces the ``try: SELECT ... LIMIT 0; except: pass`` pattern
|
|
1064
|
+
repeated 3 times in the original ``_analyze_dataflow_dead``.
|
|
1064
1065
|
"""
|
|
1065
|
-
findings = []
|
|
1066
|
-
project_root = find_project_root()
|
|
1067
|
-
|
|
1068
|
-
# Check if required tables exist
|
|
1069
1066
|
try:
|
|
1070
|
-
conn.execute("SELECT 1 FROM
|
|
1067
|
+
conn.execute(f"SELECT 1 FROM {name} LIMIT 0")
|
|
1068
|
+
return True
|
|
1071
1069
|
except Exception:
|
|
1072
|
-
return
|
|
1070
|
+
return False
|
|
1073
1071
|
|
|
1074
|
-
has_effects = True
|
|
1075
|
-
try:
|
|
1076
|
-
conn.execute("SELECT 1 FROM symbol_effects LIMIT 0")
|
|
1077
|
-
except Exception:
|
|
1078
|
-
has_effects = False
|
|
1079
1072
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1073
|
+
def _read_caller_line(project_root, file_cache: dict, file_path: str, line_no: int) -> str | None:
|
|
1074
|
+
"""Pass 106 — return the caller's source line text or ``None`` on miss."""
|
|
1075
|
+
if file_path not in file_cache:
|
|
1076
|
+
try:
|
|
1077
|
+
file_cache[file_path] = (
|
|
1078
|
+
(project_root / file_path).read_text(encoding="utf-8", errors="replace").splitlines()
|
|
1079
|
+
)
|
|
1080
|
+
except Exception:
|
|
1081
|
+
file_cache[file_path] = []
|
|
1082
|
+
lines = file_cache.get(file_path, [])
|
|
1083
|
+
if 0 < line_no <= len(lines):
|
|
1084
|
+
return lines[line_no - 1].strip()
|
|
1085
|
+
return None
|
|
1085
1086
|
|
|
1086
|
-
# A. Unused Return Values
|
|
1087
|
-
if has_metrics:
|
|
1088
|
-
funcs_with_return = conn.execute(
|
|
1089
|
-
"SELECT s.id, s.name, COALESCE(s.qualified_name, s.name) AS qname, "
|
|
1090
|
-
"f.path AS file_path, s.line_start, sm.return_count "
|
|
1091
|
-
"FROM symbols s "
|
|
1092
|
-
"JOIN files f ON s.file_id = f.id "
|
|
1093
|
-
"JOIN symbol_metrics sm ON s.id = sm.symbol_id "
|
|
1094
|
-
"WHERE sm.return_count > 0 "
|
|
1095
|
-
" AND s.kind IN ('function', 'method')"
|
|
1096
|
-
).fetchall()
|
|
1097
1087
|
|
|
1098
|
-
|
|
1088
|
+
def _is_return_captured(line_text: str, func_name: str) -> bool:
|
|
1089
|
+
"""Pass 106 — `<var> = func(...)` captures, but `== func()` does not."""
|
|
1090
|
+
if func_name not in line_text:
|
|
1091
|
+
return False
|
|
1092
|
+
prefix = line_text.split(func_name)[0]
|
|
1093
|
+
return bool(re.search(r"[A-Za-z_]\w*\s*=(?!=)", prefix))
|
|
1099
1094
|
|
|
1100
|
-
# Pre-load callers for every candidate in one batched scan to
|
|
1101
|
-
# avoid an N+1 against the edges table (one SELECT per function).
|
|
1102
|
-
callers_by_target: dict[int, list] = {}
|
|
1103
|
-
if funcs_with_return:
|
|
1104
|
-
from roam.db.connection import batched_in
|
|
1105
1095
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
callers = callers_by_target.get(func["id"], [])
|
|
1121
|
-
if not callers:
|
|
1122
|
-
continue
|
|
1096
|
+
def _detect_unused_returns(conn, project_root) -> list[dict]:
|
|
1097
|
+
"""Pass 106 — A. functions whose return value every caller discards."""
|
|
1098
|
+
findings: list[dict] = []
|
|
1099
|
+
funcs_with_return = conn.execute(
|
|
1100
|
+
"SELECT s.id, s.name, COALESCE(s.qualified_name, s.name) AS qname, "
|
|
1101
|
+
"f.path AS file_path, s.line_start, sm.return_count "
|
|
1102
|
+
"FROM symbols s "
|
|
1103
|
+
"JOIN files f ON s.file_id = f.id "
|
|
1104
|
+
"JOIN symbol_metrics sm ON s.id = sm.symbol_id "
|
|
1105
|
+
"WHERE sm.return_count > 0 "
|
|
1106
|
+
" AND s.kind IN ('function', 'method')"
|
|
1107
|
+
).fetchall()
|
|
1108
|
+
if not funcs_with_return:
|
|
1109
|
+
return findings
|
|
1123
1110
|
|
|
1124
|
-
|
|
1125
|
-
all_discard = True
|
|
1126
|
-
call_site_info = []
|
|
1127
|
-
for caller in callers:
|
|
1128
|
-
call_line = caller["line"]
|
|
1129
|
-
if not call_line:
|
|
1130
|
-
all_discard = False
|
|
1131
|
-
break
|
|
1132
|
-
caller_file = caller["caller_file"]
|
|
1133
|
-
if caller_file not in file_cache:
|
|
1134
|
-
try:
|
|
1135
|
-
fpath = project_root / caller_file
|
|
1136
|
-
file_cache[caller_file] = fpath.read_text(encoding="utf-8", errors="replace").splitlines()
|
|
1137
|
-
except Exception:
|
|
1138
|
-
file_cache[caller_file] = []
|
|
1139
|
-
lines = file_cache.get(caller_file, [])
|
|
1140
|
-
if call_line <= len(lines):
|
|
1141
|
-
line_text = lines[call_line - 1].strip()
|
|
1142
|
-
# Check if return value is captured (= before function name, but not == or !=)
|
|
1143
|
-
prefix = line_text.split(func["name"])[0] if func["name"] in line_text else ""
|
|
1144
|
-
if re.search(r"[A-Za-z_]\w*\s*=(?!=)", prefix):
|
|
1145
|
-
all_discard = False
|
|
1146
|
-
break
|
|
1147
|
-
call_site_info.append({"file": caller_file, "line": call_line, "caller": caller["caller_name"]})
|
|
1148
|
-
else:
|
|
1149
|
-
all_discard = False
|
|
1150
|
-
break
|
|
1111
|
+
from roam.db.connection import batched_in
|
|
1151
1112
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1113
|
+
target_ids = [f["id"] for f in funcs_with_return]
|
|
1114
|
+
callers_by_target: dict[int, list] = {}
|
|
1115
|
+
for row in batched_in(
|
|
1116
|
+
conn,
|
|
1117
|
+
"SELECT e.target_id, e.source_id, e.line, s.name AS caller_name, "
|
|
1118
|
+
"f.path AS caller_file, s.line_start AS caller_start "
|
|
1119
|
+
"FROM edges e "
|
|
1120
|
+
"JOIN symbols s ON e.source_id = s.id "
|
|
1121
|
+
"JOIN files f ON s.file_id = f.id "
|
|
1122
|
+
"WHERE e.target_id IN ({ph}) AND e.kind = 'calls'",
|
|
1123
|
+
target_ids,
|
|
1124
|
+
):
|
|
1125
|
+
callers_by_target.setdefault(row["target_id"], []).append(row)
|
|
1126
|
+
|
|
1127
|
+
file_cache: dict[str, list[str]] = {}
|
|
1128
|
+
for func in funcs_with_return:
|
|
1129
|
+
callers = callers_by_target.get(func["id"], [])
|
|
1130
|
+
if not callers:
|
|
1131
|
+
continue
|
|
1132
|
+
all_discard = True
|
|
1133
|
+
call_site_info: list[dict] = []
|
|
1134
|
+
for caller in callers:
|
|
1135
|
+
call_line = caller["line"]
|
|
1136
|
+
if not call_line:
|
|
1137
|
+
all_discard = False
|
|
1138
|
+
break
|
|
1139
|
+
line_text = _read_caller_line(project_root, file_cache, caller["caller_file"], call_line)
|
|
1140
|
+
if line_text is None:
|
|
1141
|
+
all_discard = False
|
|
1142
|
+
break
|
|
1143
|
+
if _is_return_captured(line_text, func["name"]):
|
|
1144
|
+
all_discard = False
|
|
1145
|
+
break
|
|
1146
|
+
call_site_info.append({"file": caller["caller_file"], "line": call_line, "caller": caller["caller_name"]})
|
|
1147
|
+
if all_discard and callers:
|
|
1148
|
+
findings.append(
|
|
1149
|
+
{
|
|
1150
|
+
"type": "unused_return",
|
|
1151
|
+
"symbol": func["qname"],
|
|
1152
|
+
"file": func["file_path"],
|
|
1153
|
+
"line": func["line_start"],
|
|
1154
|
+
"reason": f"return value of {func['qname']} is discarded by all {len(callers)} caller(s)",
|
|
1155
|
+
"confidence": 85,
|
|
1156
|
+
"call_sites": call_site_info[:5],
|
|
1157
|
+
}
|
|
1158
|
+
)
|
|
1159
|
+
return findings
|
|
1164
1160
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1161
|
+
|
|
1162
|
+
def _parse_param_names(sig: str) -> list[str]:
|
|
1163
|
+
"""Pass 106 — extract concrete parameter names from a signature string."""
|
|
1164
|
+
m = re.search(r"\(([^)]*)\)", sig or "")
|
|
1165
|
+
if not m:
|
|
1166
|
+
return []
|
|
1167
|
+
params_str = m.group(1).strip()
|
|
1168
|
+
if not params_str:
|
|
1169
|
+
return []
|
|
1170
|
+
out: list[str] = []
|
|
1171
|
+
for part in params_str.split(","):
|
|
1172
|
+
token = part.strip().split(":")[0].split("=")[0].strip()
|
|
1173
|
+
while token.startswith("*"):
|
|
1174
|
+
token = token[1:]
|
|
1175
|
+
if token and token not in ("self", "cls", "_"):
|
|
1176
|
+
out.append(token)
|
|
1177
|
+
return out
|
|
1178
|
+
|
|
1179
|
+
|
|
1180
|
+
def _detect_dead_param_chains(conn) -> list[dict]:
|
|
1181
|
+
"""Pass 106 — B. parameters with no return / sink dataflow effect."""
|
|
1182
|
+
findings: list[dict] = []
|
|
1183
|
+
rows = conn.execute(
|
|
1167
1184
|
"SELECT ts.symbol_id, ts.param_taints_return, ts.param_to_sink, "
|
|
1168
1185
|
"s.name, COALESCE(s.qualified_name, s.name) AS qname, "
|
|
1169
1186
|
"s.signature, f.path AS file_path, s.line_start "
|
|
@@ -1173,85 +1190,94 @@ def _analyze_dataflow_dead(conn):
|
|
|
1173
1190
|
"WHERE ts.is_sanitizer = 0 "
|
|
1174
1191
|
" AND s.kind IN ('function', 'method')"
|
|
1175
1192
|
).fetchall()
|
|
1176
|
-
|
|
1177
|
-
for row in param_rows:
|
|
1193
|
+
for row in rows:
|
|
1178
1194
|
try:
|
|
1179
1195
|
ptr = json.loads(row["param_taints_return"] or "{}")
|
|
1180
1196
|
pts = json.loads(row["param_to_sink"] or "{}")
|
|
1181
1197
|
except Exception:
|
|
1182
1198
|
continue
|
|
1199
|
+
param_names = _parse_param_names(row["signature"])
|
|
1200
|
+
for idx, pname in enumerate(param_names):
|
|
1201
|
+
sidx = str(idx)
|
|
1202
|
+
if ptr.get(sidx, False) or bool(pts.get(sidx)):
|
|
1203
|
+
continue
|
|
1204
|
+
findings.append(
|
|
1205
|
+
{
|
|
1206
|
+
"type": "dead_param_chain",
|
|
1207
|
+
"symbol": row["qname"],
|
|
1208
|
+
"file": row["file_path"],
|
|
1209
|
+
"line": row["line_start"],
|
|
1210
|
+
"variable": pname,
|
|
1211
|
+
"reason": (
|
|
1212
|
+
f"parameter '{pname}' of {row['qname']} has no dataflow effect (not returned, not used in sink)"
|
|
1213
|
+
),
|
|
1214
|
+
"confidence": 75,
|
|
1215
|
+
"call_sites": [],
|
|
1216
|
+
}
|
|
1217
|
+
)
|
|
1218
|
+
return findings
|
|
1219
|
+
|
|
1183
1220
|
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1221
|
+
def _detect_side_effect_only(conn, unused_return_findings: list[dict]) -> list[dict]:
|
|
1222
|
+
"""Pass 106 — C. discard-return funcs whose only effects are pure/logging."""
|
|
1223
|
+
findings: list[dict] = []
|
|
1224
|
+
benign = {"pure", "logging"}
|
|
1225
|
+
for f in unused_return_findings:
|
|
1226
|
+
if f["type"] != "unused_return":
|
|
1188
1227
|
continue
|
|
1189
|
-
|
|
1190
|
-
|
|
1228
|
+
sym_id_row = conn.execute(
|
|
1229
|
+
"SELECT id FROM symbols WHERE qualified_name = ? OR name = ? LIMIT 1",
|
|
1230
|
+
(f["symbol"], f["symbol"]),
|
|
1231
|
+
).fetchone()
|
|
1232
|
+
if not sym_id_row:
|
|
1191
1233
|
continue
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1234
|
+
sym_id = sym_id_row["id"]
|
|
1235
|
+
effects = conn.execute(
|
|
1236
|
+
"SELECT DISTINCT effect_type FROM symbol_effects WHERE symbol_id = ?",
|
|
1237
|
+
(sym_id,),
|
|
1238
|
+
).fetchall()
|
|
1239
|
+
effect_types = {e["effect_type"] for e in effects}
|
|
1240
|
+
if effect_types and effect_types <= benign:
|
|
1241
|
+
findings.append(
|
|
1242
|
+
{
|
|
1243
|
+
"type": "side_effect_only",
|
|
1244
|
+
"symbol": f["symbol"],
|
|
1245
|
+
"file": f["file"],
|
|
1246
|
+
"line": f["line"],
|
|
1247
|
+
"reason": (
|
|
1248
|
+
f"{f['symbol']} has only {'/'.join(sorted(effect_types))} effects "
|
|
1249
|
+
"and return is always discarded"
|
|
1250
|
+
),
|
|
1251
|
+
"confidence": 70,
|
|
1252
|
+
"call_sites": f.get("call_sites", []),
|
|
1253
|
+
}
|
|
1254
|
+
)
|
|
1255
|
+
return findings
|
|
1199
1256
|
|
|
1200
|
-
for idx, pname in enumerate(param_names):
|
|
1201
|
-
sidx = str(idx)
|
|
1202
|
-
has_return_effect = ptr.get(sidx, False)
|
|
1203
|
-
has_sink_effect = bool(pts.get(sidx))
|
|
1204
|
-
if not has_return_effect and not has_sink_effect:
|
|
1205
|
-
findings.append(
|
|
1206
|
-
{
|
|
1207
|
-
"type": "dead_param_chain",
|
|
1208
|
-
"symbol": row["qname"],
|
|
1209
|
-
"file": row["file_path"],
|
|
1210
|
-
"line": row["line_start"],
|
|
1211
|
-
"variable": pname,
|
|
1212
|
-
"reason": (
|
|
1213
|
-
f"parameter '{pname}' of {row['qname']} has no dataflow effect "
|
|
1214
|
-
f"(not returned, not used in sink)"
|
|
1215
|
-
),
|
|
1216
|
-
"confidence": 75,
|
|
1217
|
-
"call_sites": [],
|
|
1218
|
-
}
|
|
1219
|
-
)
|
|
1220
1257
|
|
|
1221
|
-
|
|
1258
|
+
def _analyze_dataflow_dead(conn):
|
|
1259
|
+
"""Analyze dataflow-based dead code patterns using taint summaries.
|
|
1260
|
+
|
|
1261
|
+
Returns list of findings: ``[{type, symbol, file, line, reason,
|
|
1262
|
+
confidence, call_sites}]``.
|
|
1263
|
+
|
|
1264
|
+
Pass 106 — orchestrator only; per-pattern logic moved into
|
|
1265
|
+
``_detect_unused_returns`` / ``_detect_dead_param_chains`` /
|
|
1266
|
+
``_detect_side_effect_only``. Cognitive complexity dropped from
|
|
1267
|
+
160 to ~10.
|
|
1268
|
+
"""
|
|
1269
|
+
if not _table_exists(conn, "taint_summaries"):
|
|
1270
|
+
return []
|
|
1271
|
+
has_effects = _table_exists(conn, "symbol_effects")
|
|
1272
|
+
has_metrics = _table_exists(conn, "symbol_metrics")
|
|
1273
|
+
project_root = find_project_root()
|
|
1274
|
+
|
|
1275
|
+
findings: list[dict] = []
|
|
1276
|
+
if has_metrics:
|
|
1277
|
+
findings.extend(_detect_unused_returns(conn, project_root))
|
|
1278
|
+
findings.extend(_detect_dead_param_chains(conn))
|
|
1222
1279
|
if has_effects:
|
|
1223
|
-
|
|
1224
|
-
for f in findings:
|
|
1225
|
-
if f["type"] != "unused_return":
|
|
1226
|
-
continue
|
|
1227
|
-
sym_id_row = conn.execute(
|
|
1228
|
-
"SELECT id FROM symbols WHERE qualified_name = ? OR name = ? LIMIT 1",
|
|
1229
|
-
(f["symbol"], f["symbol"]),
|
|
1230
|
-
).fetchone()
|
|
1231
|
-
if not sym_id_row:
|
|
1232
|
-
continue
|
|
1233
|
-
sym_id = sym_id_row["id"]
|
|
1234
|
-
effects = conn.execute(
|
|
1235
|
-
"SELECT DISTINCT effect_type FROM symbol_effects WHERE symbol_id = ?",
|
|
1236
|
-
(sym_id,),
|
|
1237
|
-
).fetchall()
|
|
1238
|
-
effect_types = {e["effect_type"] for e in effects}
|
|
1239
|
-
benign = {"pure", "logging"}
|
|
1240
|
-
if effect_types and effect_types <= benign:
|
|
1241
|
-
findings.append(
|
|
1242
|
-
{
|
|
1243
|
-
"type": "side_effect_only",
|
|
1244
|
-
"symbol": f["symbol"],
|
|
1245
|
-
"file": f["file"],
|
|
1246
|
-
"line": f["line"],
|
|
1247
|
-
"reason": (
|
|
1248
|
-
f"{f['symbol']} has only {'/'.join(sorted(effect_types))} effects "
|
|
1249
|
-
f"and return is always discarded"
|
|
1250
|
-
),
|
|
1251
|
-
"confidence": 70,
|
|
1252
|
-
"call_sites": f.get("call_sites", []),
|
|
1253
|
-
}
|
|
1254
|
-
)
|
|
1280
|
+
findings.extend(_detect_side_effect_only(conn, findings))
|
|
1255
1281
|
|
|
1256
1282
|
findings.sort(key=lambda f: (-f["confidence"], f["file"], f.get("line") or 0))
|
|
1257
1283
|
return findings
|
|
@@ -263,8 +263,15 @@ def _check_command_registry() -> dict:
|
|
|
263
263
|
removed or renamed leaves the registry mismatch undetected until an
|
|
264
264
|
agent calls it. Doctor runs the lazy-import for every entry up front.
|
|
265
265
|
"""
|
|
266
|
+
# Pass 104 — break the static cycle (cli ↔ cmd_doctor) by loading
|
|
267
|
+
# ``roam.cli`` via importlib at runtime. This is the only static
|
|
268
|
+
# edge that connected back to cli; the doctor's safety check still
|
|
269
|
+
# verifies every registered command imports.
|
|
266
270
|
try:
|
|
267
|
-
|
|
271
|
+
import importlib
|
|
272
|
+
|
|
273
|
+
cli_mod = importlib.import_module("roam.cli")
|
|
274
|
+
_COMMANDS = cli_mod._COMMANDS
|
|
268
275
|
except Exception as exc:
|
|
269
276
|
return {
|
|
270
277
|
"name": "CLI command registry",
|
|
@@ -49,6 +49,14 @@ _UTILITY_PATH_PATTERNS = (
|
|
|
49
49
|
"common/",
|
|
50
50
|
"internal/",
|
|
51
51
|
"infra/",
|
|
52
|
+
# Pass 105 — infrastructure hubs that are EXPECTED to have high
|
|
53
|
+
# fan-in. Without these patterns the health-score classifier
|
|
54
|
+
# mislabels architectural roots (Click root group, MCP dispatch,
|
|
55
|
+
# graph builder, file-role classifier) as actionable refactor
|
|
56
|
+
# targets, which they are not.
|
|
57
|
+
"graph/",
|
|
58
|
+
"mcp_extras/",
|
|
59
|
+
"languages/",
|
|
52
60
|
)
|
|
53
61
|
|
|
54
62
|
_UTILITY_FILE_PATTERNS = (
|
|
@@ -56,6 +64,11 @@ _UTILITY_FILE_PATTERNS = (
|
|
|
56
64
|
"helpers.py",
|
|
57
65
|
"common.py",
|
|
58
66
|
"base.py",
|
|
67
|
+
# Pass 105 — single-file architectural hubs. Same reasoning as
|
|
68
|
+
# ``_UTILITY_PATH_PATTERNS`` additions above.
|
|
69
|
+
"cli.py",
|
|
70
|
+
"mcp_server.py",
|
|
71
|
+
"file_roles.py",
|
|
59
72
|
)
|
|
60
73
|
|
|
61
74
|
# Paths that are NOT production code — treat as expected utilities
|