Summary
The code-analysis domain exists as two parallel package trees with independent, live implementations of the same analyzers. code_intelligence/ is the modern tree (produced by the #381 god-class refactor); code_analysis/src/ is the older fat implementation. The refactor built the clean tree but never migrated the old consumers off, so both are in production simultaneously — an unfinished rename that has since diverged.
This is the "one concept, two implementations" canonical violation, and it is systematic across at least three analyzers, not a one-off.
The duplication
At the top level of code_intelligence/ sit thin facades that re-export from code_intelligence's own modern subtrees — not from code_analysis:
| concept |
code_analysis/src/ (old, fat) |
code_intelligence/ facade re-exports from |
performance_analyzer |
740 lines |
code_intelligence.performance_analysis |
anti_pattern_detector |
2,320 lines |
code_intelligence.anti_pattern_detection |
security_analyzer |
861 lines |
code_intelligence.security |
So each concept has two full implementations — the old one in code_analysis/src/ (~3,921 lines across the three) and a separate modern one under code_intelligence/*/. The facades make the modern side look consolidated while the old side keeps running underneath.
Both sides have live consumers — including API code
code_analysis/src/performance_analyzer <- code_analysis/scripts/analyze_performance.py
code_analysis/src/code_quality_dashboard.py
code_analysis/src/anti_pattern_detector <- api/codebase_analytics/cross_file_analysis.py ← API path uses the OLD tree
code_analysis/src/remediation_loop.py
code_analysis/src/security_analyzer <- code_analysis/src/code_quality_dashboard.py
code_intelligence/performance_analysis <- api/code_intelligence.py ← API path uses the NEW tree
So two different API modules analyse the same codebase through two different analyzer implementations. Which analyzer runs depends purely on which import path the endpoint happened to be written against.
The two implementations have already diverged
The shared output type, PerformanceIssue, is structurally incompatible between the trees:
# code_analysis/src/performance_analyzer.py:27 # code_intelligence/performance_analysis/types.py:87
class PerformanceIssue: class PerformanceIssue:
file_path: str issue_type: PerformanceIssueType # enum, not str
line_number: int severity: PerformanceSeverity # enum, not str
function_name: str | None file_path: str
issue_type: str # "memory_leak", ... line_start: int # vs line_number
severity: str # "critical", ... line_end: int
code_snippet: str recommendation: str # new field, absent in old
One is stringly-typed with line_number; the other uses enums with line_start/line_end and adds recommendation. Any code that consumes "a PerformanceIssue" gets a different shape depending on which analyzer produced it — the exact failure the single-source rule exists to prevent. The anti-pattern and security analyzers, being independent 2,320- and 861-line bodies, carry the same divergence risk in their detection rules: the two trees can flag different issues on identical code, and neither is authoritative.
Why this matters beyond tidiness
- Inconsistent results. The two analytics surfaces can disagree about the same repository, and there is no defined winner — directly relevant to the "some analytics data looks off / work-in-progress" observations elsewhere.
- Double maintenance. A rule fix or false-positive suppression must be made twice or it silently regresses on one surface.
- ~3,900 lines of parallel logic to keep in sync by hand, with no test asserting the two produce equivalent output (they can't — the types differ).
Recommendation (architecture decision — needs owner sign-off)
Per the canonical rule for "two implementations already exist": pick the canonical tree, migrate every consumer to it, and reduce the other to a re-export shim or delete it — do not add a third.
- Declare
code_intelligence/ canonical (it is the post-refactor tree, is enum-typed, and already backs the primary API). Confirm with whoever owns code-analysis.
- Migrate the remaining
code_analysis/src/ consumers — api/codebase_analytics/cross_file_analysis.py, code_quality_dashboard.py, remediation_loop.py, scripts/analyze_performance.py — onto the code_intelligence implementations and its PerformanceIssue type.
- Reduce
code_analysis/src/{performance_analyzer,anti_pattern_detector,security_analyzer}.py to re-export shims, then delete once no importer remains.
- Record the consolidation as an ADR; this is a module-boundary decision, not a mechanical cleanup.
This is a discovery issue — filing per the marketing-session rule (expose, do not fix). The migration itself is an implementation task for a later session and should not proceed without the canonical-tree decision in step 1.
Secondary observation (lower confidence)
RateLimiter is independently reimplemented in four places with different algorithms — services/gateway/message_queue.py (token bucket, per-platform), agents/web_researcher.py (sliding window), security/threat_intelligence.py (requests-per-minute), api/secrets.py ("simple in-memory"). These may be a legitimate case where "duplication is cheaper than the wrong abstraction" — the algorithms genuinely differ — so this is flagged for a judgement call, not asserted as a fork. Worth a look when someone touches rate limiting; not necessarily worth unifying.
Environment
install.sh deploy at /opt/autobot; deployed tree matches origin/Dev_new_gui. Line counts and imports from the deployed backend source.
Summary
The code-analysis domain exists as two parallel package trees with independent, live implementations of the same analyzers.
code_intelligence/is the modern tree (produced by the #381 god-class refactor);code_analysis/src/is the older fat implementation. The refactor built the clean tree but never migrated the old consumers off, so both are in production simultaneously — an unfinished rename that has since diverged.This is the "one concept, two implementations" canonical violation, and it is systematic across at least three analyzers, not a one-off.
The duplication
At the top level of
code_intelligence/sit thin facades that re-export fromcode_intelligence's own modern subtrees — not fromcode_analysis:code_analysis/src/(old, fat)code_intelligence/facade re-exports fromperformance_analyzercode_intelligence.performance_analysisanti_pattern_detectorcode_intelligence.anti_pattern_detectionsecurity_analyzercode_intelligence.securitySo each concept has two full implementations — the old one in
code_analysis/src/(~3,921 lines across the three) and a separate modern one undercode_intelligence/*/. The facades make the modern side look consolidated while the old side keeps running underneath.Both sides have live consumers — including API code
So two different API modules analyse the same codebase through two different analyzer implementations. Which analyzer runs depends purely on which import path the endpoint happened to be written against.
The two implementations have already diverged
The shared output type,
PerformanceIssue, is structurally incompatible between the trees:One is stringly-typed with
line_number; the other uses enums withline_start/line_endand addsrecommendation. Any code that consumes "aPerformanceIssue" gets a different shape depending on which analyzer produced it — the exact failure the single-source rule exists to prevent. The anti-pattern and security analyzers, being independent 2,320- and 861-line bodies, carry the same divergence risk in their detection rules: the two trees can flag different issues on identical code, and neither is authoritative.Why this matters beyond tidiness
Recommendation (architecture decision — needs owner sign-off)
Per the canonical rule for "two implementations already exist": pick the canonical tree, migrate every consumer to it, and reduce the other to a re-export shim or delete it — do not add a third.
code_intelligence/canonical (it is the post-refactor tree, is enum-typed, and already backs the primary API). Confirm with whoever owns code-analysis.code_analysis/src/consumers —api/codebase_analytics/cross_file_analysis.py,code_quality_dashboard.py,remediation_loop.py,scripts/analyze_performance.py— onto thecode_intelligenceimplementations and itsPerformanceIssuetype.code_analysis/src/{performance_analyzer,anti_pattern_detector,security_analyzer}.pyto re-export shims, then delete once no importer remains.This is a discovery issue — filing per the marketing-session rule (expose, do not fix). The migration itself is an implementation task for a later session and should not proceed without the canonical-tree decision in step 1.
Secondary observation (lower confidence)
RateLimiteris independently reimplemented in four places with different algorithms —services/gateway/message_queue.py(token bucket, per-platform),agents/web_researcher.py(sliding window),security/threat_intelligence.py(requests-per-minute),api/secrets.py("simple in-memory"). These may be a legitimate case where "duplication is cheaper than the wrong abstraction" — the algorithms genuinely differ — so this is flagged for a judgement call, not asserted as a fork. Worth a look when someone touches rate limiting; not necessarily worth unifying.Environment
install.shdeploy at/opt/autobot; deployed tree matchesorigin/Dev_new_gui. Line counts and imports from the deployed backend source.