Skip to content

[BUG-02] --deep flag runs HybridEngine twice — LSP findings double-counted #32

Description

@Wolfvin

Summary

The --deep post-processing block in scripts/codelens.py is implemented twice for the same set of commands (dead-code, query, impact, smell, complexity). Both blocks execute in sequence, each instantiating a fresh HybridEngine and overwriting result["deep_analysis"], result["lsp_active"], and related fields.

Evidence

scripts/codelens.py:

Block 1 (~L1010-1045):

if args.deep and args.command in ("dead-code", "query", "impact", "smell", "complexity"):
    try:
        from hybrid_engine import HybridEngine
        hybrid = HybridEngine(workspace, deep=True)
        ...
        result["deep_analysis"] = ...
        result["lsp_active"] = ...

Block 2 (~L1056-1107):

if args.deep and args.command in ("dead-code", "query", "impact", "smell", "complexity"):
    try:
        from hybrid_engine import create_hybrid_engine, add_confidence_to_result
        hybrid = create_hybrid_engine(workspace, deep=True)
        ...
        result = add_confidence_to_result(result)
        result["deep_analysis"] = ...   # overwrites Block 1
        result["lsp_active"] = ...       # overwrites Block 1

Both blocks have the same trigger condition and both run unconditionally when --deep is set.

Impact

  1. Double work: HybridEngine is instantiated twice per --deep invocation, doubling LSP subprocess calls and parse time.
  2. Potential double-counting: Block 2 calls add_confidence_to_result(result) on the already-Block-1-enriched result. If add_confidence_to_result merges rather than replaces LSP-verified findings, the same finding may be counted twice in confidence_distribution.
  3. State divergence: Block 1 uses HybridEngine(workspace, deep=True) directly; Block 2 uses the factory create_hybrid_engine(workspace, deep=True). The two constructors may produce engines with different internal state (caches, LSP server selection), making the final deep_analysis payload depend on which block "wins" rather than on a single source of truth.
  4. Wasted tokens for MCP clients: codelens_<cmd> tool calls with format=compact pay tokens for the (potentially duplicated) deep_analysis block.

Repro

python3 scripts/codelens.py smell <workspace> --deep --format json | \
  python3 -c "import sys,json; r=json.load(sys.stdin); print('lsp_active:', r.get('lsp_active')); print('confidence_distribution:', r.get('confidence_distribution'))"
# Inspect: if the same LSP finding appears twice in confidence_distribution, the bug is confirmed.

Adding a print() or logger.warning("deep block N executed") to each block also shows both firing on a single --deep invocation.

Suggested fix

Pick one implementation and delete the other. Block 2 is strictly more capable (it also calls add_confidence_to_result), so the conservative fix is:

  1. Delete Block 1 entirely (L1010-1045).
  2. Keep Block 2 (L1056-1107).
  3. Add a regression test that runs smell --deep and asserts HybridEngine.__init__ (or create_hybrid_engine) is invoked exactly once — easiest via unittest.mock.patch counting call_args.

Files

  • scripts/codelens.py (~L1010-1107)
  • tests/test_hybrid_engine.py (new test for single-execution invariant)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions