diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d8519a1..56702bee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [8.2.0] — Unreleased +### Confidence Fields on Non-Deep Output (test fix) + +Previously, the `confidence` / `confidence_distribution` fields were only +attached to `query` / `impact` / `dead-code` output when the `--deep` flag +was passed (which triggers LSP verification). This meant consumers of the +default (non-deep) output had no way to know the analysis provenance. The +hybrid engine's module docstring already documents the intended semantics +(`high` = LSP verified, `medium` = AST matched, `low` = regex only), and +`HybridEngine.enhance_*` methods already set `confidence = MEDIUM` when LSP +is not active — but those methods were only invoked from the `--deep` +post-processing path in `codelens.py`, never from the command `execute()` +entry points. + +This fix completes the partially-implemented feature by attaching baseline +`confidence = "medium"` (and `confidence_distribution` for `dead-code`) at +command execution time, before the `--deep` post-processing layer runs. +When `--deep` is later applied, LSP verification may override individual +fields to `high` or `low` as before. + +### Added (confidence fields) + +- **`query` command** — top-level `confidence` field is now always present + on `found` results (value: `"medium"` for AST-based analysis, `"high"` or + `"low"` when `--deep` + LSP verifies). +- **`impact` command** — top-level `confidence` field is now always present + on `status: ok` results. +- **`dead-code` command** — each finding in `results` now carries a + `confidence` field; `stats.confidence_distribution` (counts of + `high` / `medium` / `low`) is now always present. + +### Non-Breaking (confidence fields) + +- All previously-passing tests continue to pass. +- The new fields are additive — no existing field is removed or renamed. +- When `--deep` is used, the `--deep` post-processing layer in + `codelens.py` still runs and may override the baseline confidence based + on LSP verification, exactly as before. +- The `confidence` field is also surfaced in the `--format ai` normalized + output via the existing `_META_KEYS` extraction in + `formatters/__init__.py`. + ### Token-Efficient Output + Pagination (issue #17) Adds a 5th output format (`compact`) and pagination to all list-type commands diff --git a/scripts/commands/dead_code.py b/scripts/commands/dead_code.py index 50dd1f93..f43a0b2f 100644 --- a/scripts/commands/dead_code.py +++ b/scripts/commands/dead_code.py @@ -45,6 +45,43 @@ def execute(args, workspace): result["removal_safety"] = "caution" result["dependency_count"] = with_refs result["recommended_action"] = "Review before removing. Some dead code may still be referenced indirectly." if with_refs > 0 else "Safe to remove. No references found." + + # Attach baseline confidence (medium = AST-based analysis per hybrid_engine.py + # docstring) to each finding and add confidence_distribution to stats. + # HybridEngine.verify_dead_code sets confidence=MEDIUM on every finding when + # LSP is not active (deep=False). add_confidence_to_result backfills any + # remaining findings and computes the distribution. When --deep is later + # applied in codelens.py post-processing, LSP verification may override + # individual findings to HIGH or LOW. + try: + from hybrid_engine import create_hybrid_engine, add_confidence_to_result + engine = create_hybrid_engine(workspace, deep=False) + all_findings = [] + for cat_items in result.get("results", {}).values(): + if isinstance(cat_items, list): + all_findings.extend(cat_items) + if all_findings: + engine.verify_dead_code(all_findings) + add_confidence_to_result(result) + engine.cleanup() + except Exception: + # Best-effort fallback: manually attach medium confidence + distribution + all_findings = [] + for cat_items in result.get("results", {}).values(): + if isinstance(cat_items, list): + all_findings.extend(cat_items) + for f in all_findings: + if isinstance(f, dict) and "confidence" not in f: + f["confidence"] = "medium" + if all_findings: + dist = {"high": 0, "medium": 0, "low": 0} + for f in all_findings: + c = f.get("confidence", "medium") if isinstance(f, dict) else "medium" + if c in dist: + dist[c] += 1 + if "stats" not in result: + result["stats"] = {} + result["stats"]["confidence_distribution"] = dist return result diff --git a/scripts/commands/impact.py b/scripts/commands/impact.py index fe15bc97..cba88ee7 100644 --- a/scripts/commands/impact.py +++ b/scripts/commands/impact.py @@ -43,6 +43,18 @@ def execute(args, workspace): result["recommended_action"] = "Proceed with caution. Review affected code before changing." else: result["recommended_action"] = "Safe to proceed. No dependent code found." + + # Attach baseline confidence (medium = AST-based analysis per hybrid_engine.py + # docstring). HybridEngine.enhance_impact sets confidence=MEDIUM when LSP is + # not active (deep=False). When --deep is later applied in codelens.py + # post-processing, LSP verification may override this to HIGH or LOW. + try: + from hybrid_engine import create_hybrid_engine + engine = create_hybrid_engine(workspace, deep=False) + engine.enhance_impact(result, args.name) + engine.cleanup() + except Exception: + result.setdefault("confidence", "medium") return result diff --git a/scripts/commands/query.py b/scripts/commands/query.py index 9ac6337a..259e5be7 100644 --- a/scripts/commands/query.py +++ b/scripts/commands/query.py @@ -44,7 +44,30 @@ def add_args(parser): def execute(args, workspace): limit = None if getattr(args, 'all', False) else getattr(args, 'limit', 20) fuzzy = getattr(args, 'fuzzy', False) - return cmd_query(args.name, workspace, args.domain, args.file, limit=limit, fuzzy=fuzzy) + result = cmd_query(args.name, workspace, args.domain, args.file, limit=limit, fuzzy=fuzzy) + _attach_baseline_confidence(result, args.name, workspace) + return result + + +def _attach_baseline_confidence(result: Dict[str, Any], query_name: str, workspace: str) -> None: + """Attach baseline confidence to query results. + + Per ``hybrid_engine.py`` module docstring, CodeLens's default analysis path + (tree-sitter AST) corresponds to MEDIUM confidence. ``HybridEngine.enhance_query`` + sets ``confidence=MEDIUM`` when LSP is not active (``deep=False``). When the + user later passes ``--deep``, ``codelens.py`` post-processing calls + ``enhance_query`` again with ``deep=True`` and may override this to HIGH or + LOW based on LSP verification. + """ + if not isinstance(result, dict) or not result.get("found"): + return + try: + from hybrid_engine import create_hybrid_engine + engine = create_hybrid_engine(workspace, deep=False) + engine.enhance_query(result, query_name) + engine.cleanup() + except Exception: + result.setdefault("confidence", "medium") def cmd_query(query_name: str, workspace: str, domain: str = None,