From 52155ed31975c914385f2969dc2c08b5110d5d23 Mon Sep 17 00:00:00 2001 From: Dmitry Teryaev Date: Sun, 14 Jun 2026 21:55:08 +0300 Subject: [PATCH] feat(installer): default cross_service_resolution to brownfield_only on install Auto cross-service resolution is weak and can produce noisy / false-positive edges. Seed `cross_service_resolution: brownfield_only` into the generated .java-codebase-rag.yml during `java-codebase-rag install`, so only evidence-backed cross-service edges survive by default. Uses setdefault so an explicit user choice (e.g. `auto`) is never clobbered on re-run update; existing configs without the key are unaffected until reinstall. Runtime default stays `auto`, so current users see no behavior change and no existing tests require updating. Co-Authored-By: Claude --- java_codebase_rag/installer.py | 5 +++++ tests/test_installer.py | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/java_codebase_rag/installer.py b/java_codebase_rag/installer.py index 9c9ac8d..6e8ec52 100644 --- a/java_codebase_rag/installer.py +++ b/java_codebase_rag/installer.py @@ -759,6 +759,11 @@ def generate_yaml_config( else: config["embedding"].pop("model", None) + # Seed cross-service resolution safe-by-default: only evidence-backed cross-service + # edges survive (see _is_brownfield_sourced in build_ast_graph). setdefault preserves + # an explicit user choice (e.g. `auto`) on re-run update. + config.setdefault("cross_service_resolution", "brownfield_only") + # Keys NOT written by installer (preserved if present): # - source_root (config.py resolves from walk-up discovery) # - index_dir (config.py defaults to /.java-codebase-rag) diff --git a/tests/test_installer.py b/tests/test_installer.py index ed7dd05..62586bd 100644 --- a/tests/test_installer.py +++ b/tests/test_installer.py @@ -696,6 +696,45 @@ def test_rerun_no_config_returns_none(self, tmp_path): assert result is None +class TestGenerateYamlConfigCrossService: + """cross_service_resolution is seeded safe-by-default; an explicit choice is never overridden.""" + + def test_fresh_install_seeds_brownfield_only(self): + import yaml + from java_codebase_rag.installer import generate_yaml_config + + out = generate_yaml_config( + Path("."), model="auto", microservice_roots=None, existing_yaml=None + ) + assert yaml.safe_load(out)["cross_service_resolution"] == "brownfield_only" + + def test_explicit_auto_is_preserved_on_rerun(self): + import yaml + from java_codebase_rag.installer import generate_yaml_config + + out = generate_yaml_config( + Path("."), + model="auto", + microservice_roots=None, + existing_yaml={"cross_service_resolution": "auto"}, + ) + assert yaml.safe_load(out)["cross_service_resolution"] == "auto" + + def test_absent_key_seeded_and_existing_keys_preserved_on_rerun(self): + import yaml + from java_codebase_rag.installer import generate_yaml_config + + out = generate_yaml_config( + Path("."), + model="auto", + microservice_roots=None, + existing_yaml={"brownfield_overrides": {"svc-a": {}}}, + ) + config = yaml.safe_load(out) + assert config["cross_service_resolution"] == "brownfield_only" + assert config["brownfield_overrides"] == {"svc-a": {}} + + class TestInstallIntegration: """Integration tests for install command."""