TechLens Signal Guide

Auto-generated from signal definitions. Run python scripts/generate_signal_docs.py to regenerate.

Pipeline Overview

The FULL_ANALYSIS pipeline runs 20 stages in order. Dashed borders indicate SLM-dependent stages that are skipped when no local model is available.

graph TB
  subgraph "File Discovery"
    direction LR
    S0["filter_files
Walk repo, select source files"] S1["create_manager
Instantiate per-file score accumulator"] end subgraph "Signal Collection" direction LR S2["ast_analysis
code-analysis structural pipeline (optional)"] S3["git_density
Git density, author count, co-change"] S4["contributor_attribution
Contributor attribution enrichment"] S5["token_metrics
Cyclomatic complexity, operands, operators, fanout"] S6["cognitive_metric
AST cognitive complexity (max + total per file)"] S7["code_metrics
Control flow, definitions, comments, glue, domain tokens"] S8["test_coverage
Map test files to source files"] S9["derived_metrics
Halstead, maintainability index, TIOBE"] S10["cognitive_maintainability
Maintainability index with cognitive complexity in place of Halstead"] S11["moat_signals
API surface, data accumulation, realtime, governance, regulatory"] S12["ast_signals
Dead code, dependency cycles, layer violations"] S13["churn_metric
Per-file near-rewrite churn score"] S14["change_density
Change density (git density x missing protective factors)"] S15["ai_adoption
AI tool adoption detection"] end subgraph "Scoring" direction LR S16["composite_scores
Weighted composite scoring + top-files ranking"] end subgraph "SLM (optional)" direction LR S17["lm_code_value
SLM: uniqueness, difficulty, quality"] S18["file_descriptions
SLM: per-file descriptions + program synthesis"] S19["slm_post_filter
SLM: outlier queries (code value + glue assessment)"] end S0 --> S1 S1 --> S2 S2 --> S3 S3 --> S4 S4 --> S5 S5 --> S6 S6 --> S7 S7 --> S8 S8 --> S9 S9 --> S10 S10 --> S11 S11 --> S12 S12 --> S13 S13 --> S14 S14 --> S15 S15 --> S16 S16 --> S17 S17 --> S18 S18 --> S19 style S17 stroke-dasharray: 5 5 style S18 stroke-dasharray: 5 5 style S19 stroke-dasharray: 5 5

Signal → Composite Score

Signals feed into composite scores with weighted contributions. Dashed arrows indicate inverted signals (lower raw value = higher contribution). ⚛ marks signals requiring a local SLM.

architecturegitheuristicslmstatic_analysis
graph LR
  subgraph " "
    direction LR
    control_flow_density["control_flow_density"]:::heuristic
    definition_density["definition_density"]:::heuristic
    comment_ratio["comment_ratio"]:::heuristic
    test_coverage_boosted["test_coverage_boosted"]:::architecture
    duplication_ratio["duplication_ratio"]:::heuristic
  end
  engineering_score(["engineering_score"]):::composite
  subgraph " "
    direction LR
    lm_uniqueness["lm_uniqueness ⚛"]:::slm
    lm_complexity["lm_complexity ⚛"]:::slm
    lm_quality["lm_quality ⚛"]:::slm
  end
  lm_code_value_score(["lm_code_value_score"]):::composite
  subgraph " "
    direction LR
    domain_match_density["domain_match_density"]:::heuristic
    gluemarker_ratio["gluemarker_ratio"]:::heuristic
    external_call_ratio["external_call_ratio"]:::heuristic
    api_surface_ratio["api_surface_ratio"]:::architecture
    realtime_pattern_density["realtime_pattern_density"]:::architecture
    data_accumulation_pattern["data_accumulation_pattern"]:::architecture
    governance_workflow_density["governance_workflow_density"]:::architecture
    regulatory_token_density["regulatory_token_density"]:::architecture
  end
  moat_score(["moat_score"]):::composite
  control_flow_density -->|15%| engineering_score
  definition_density -->|15%| engineering_score
  comment_ratio -->|15%| engineering_score
  test_coverage_boosted -->|30%| engineering_score
  duplication_ratio -.->|25% inv| engineering_score
  lm_uniqueness -->|33%| lm_code_value_score
  lm_complexity -->|33%| lm_code_value_score
  lm_quality -->|33%| lm_code_value_score
  domain_match_density -->|20%| moat_score
  gluemarker_ratio -.->|15% inv| moat_score
  external_call_ratio -.->|12% inv| moat_score
  api_surface_ratio -->|6%| moat_score
  realtime_pattern_density -->|6%| moat_score
  data_accumulation_pattern -->|10%| moat_score
  governance_workflow_density -->|6%| moat_score
  regulatory_token_density -->|12%| moat_score
  classDef git fill:#4A90D9,color:#fff,stroke:#333
  classDef heuristic fill:#50C878,color:#fff,stroke:#333
  classDef architecture fill:#FF8C42,color:#fff,stroke:#333
  classDef static_analysis fill:#9B59B6,color:#fff,stroke:#333
  classDef slm fill:#E74C3C,color:#fff,stroke:#333
  classDef composite fill:#2C3E50,color:#fff,stroke:#fff,font-size:16px
    

Composite Weight Distribution

pie title engineering_score
  "control_flow_density" : 15
  "definition_density" : 15
  "comment_ratio" : 15
  "test_coverage_boosted" : 30
  "duplication_ratio (inv)" : 25
pie title lm_code_value_score
  "lm_uniqueness" : 33
  "lm_complexity" : 33
  "lm_quality" : 33
pie title moat_score
  "domain_match_density" : 20
  "gluemarker_ratio (inv)" : 15
  "external_call_ratio (inv)" : 12
  "api_surface_ratio" : 6
  "realtime_pattern_density" : 6
  "data_accumulation_pattern" : 10
  "governance_workflow_density" : 6
  "regulatory_token_density" : 12

Signal Glossary

Click a signal to see what it measures. Per-file signals produce one value per source file; repo-level signals produce a single value for the entire repository.

Per-file (31)

  • git_density gitgit
    Normalized change activity score from git history
    Answers the four most common workshop questions at once: Where is active development focused? Who are the critical developers? What happens if they leave? Which components are secretly coupled? These findings directly influence retention clauses, earnout structures, and integration timelines.
  • author_count gitgit
    Normalized count of distinct commit authors per file
    Answers the four most common workshop questions at once: Where is active development focused? Who are the critical developers? What happens if they leave? Which components are secretly coupled? These findings directly influence retention clauses, earnout structures, and integration timelines.
  • cochange gitgit
    Normalized co-change coupling frequency per file
    Answers the four most common workshop questions at once: Where is active development focused? Who are the critical developers? What happens if they leave? Which components are secretly coupled? These findings directly influence retention clauses, earnout structures, and integration timelines.
  • comment_density_hotheuristic
    Per-file interaction signal: comment ratio * git density
    Documentation culture is a leading indicator of post-close maintenance risk. Near-zero comments + high change frequency = institutional knowledge trapped in developers' heads. Directly affects knowledge transfer timelines and key-person retention priorities.
  • test_coveragearchitecture
    Per-file test coverage weight: binary 1.0/0.0 from naming heuristics, replaced by continuous AST coverage ratio (0.0-1.0) when ast_test_coverage_ratio is available
    Reveals engineering discipline. Low test coverage tells an investor the team ships without a safety net. The absence of tests is one of the most common findings that changes rebuild estimates and post-close stabilization budgets.
  • test_coverage_boostedarchitecture
    Churn-weighted boost for tested files (test_coverage * git_density)
    Reveals engineering discipline. Low test coverage tells an investor the team ships without a safety net. The absence of tests is one of the most common findings that changes rebuild estimates and post-close stabilization budgets.
  • slm_value_score SLMslm
    SLM-assessed value score per individual source file
    Enables 'Is this code worth acquiring?' as a scored, benchmarkable answer. Per-file IP identification transforms workshops. Rebuild estimation with AI-assessed complexity.
  • cyclomatic_complexitystatic_analysis
    McCabe cyclomatic complexity per file
    Measures the number of independent execution paths through the code. High cyclomatic complexity means more test cases needed for full coverage and higher probability of latent defects — directly impacting post-close stabilization effort.
  • operands_sumstatic_analysis
    Total operand token count per file
    Raw input for Halstead metrics. Total operand count reflects data volume flowing through the code — higher counts indicate files doing substantive data transformation rather than simple pass-through.
  • operands_uniquestatic_analysis
    Unique operand token count per file
    Vocabulary breadth of data elements. A file with many unique operands is manipulating a rich data model, suggesting domain-specific logic that is harder to replicate or replace with generic tooling.
  • operators_sumstatic_analysis
    Total operator token count per file
    Raw input for Halstead metrics. High operator counts relative to operands indicate dense control logic and data manipulation, contributing to implementation effort estimates.
  • operators_uniquestatic_analysis
    Unique operator token count per file
    Indicates the breadth of language features and constructs used. Files leveraging more unique operators tend to implement more sophisticated algorithms that require deeper expertise to maintain.
  • definitions_countstatic_analysis
    Unique function and class definition count per file
    Counts distinct function and class definitions via Pygments tokens. High definition counts indicate files that declare substantial API surface — more definitions mean more contracts to maintain, test, and document.
  • halstead_volumestatic_analysis
    Halstead program volume
    Measures the information content of the code in bits. High volume files contain more logic to understand, test, and maintain — directly proportional to the knowledge transfer effort required post-acquisition.
  • halstead_difficultystatic_analysis
    Halstead program difficulty
    Quantifies how hard the code is to write or understand. High difficulty files are error-prone to modify and require senior engineers — a factor in retention planning and post-close staffing decisions.
  • halstead_effortstatic_analysis
    Halstead implementation effort
    Enables rebuild estimation and t-shirt sizing. The top 10 files account for 60% of total comprehension effort, giving concrete input for post-close staffing and timeline planning.
  • halstead_bugpropstatic_analysis
    Halstead estimated bug propensity
    Predicts the expected number of delivered defects based on code volume. Files with high bug propensity are where post-close quality issues will concentrate — informing QA resource allocation and stabilization timelines.
  • halstead_timerequiredstatic_analysis
    Halstead estimated implementation time
    Estimates implementation time in seconds per file. High values flag modules that will take longest to rewrite or onboard — giving concrete input for rebuild timelines and staffing plans.
  • maintainability_indexstatic_analysis
    Microsoft's Maintainability Index variant (0-100): a composite of Halstead Volume, Cyclomatic Complexity, and Lines of Code. Originally defined by Oman/Hagemeister (Univ. Idaho/SEI, 1992); the Microsoft formulation popularized by Visual Studio is what most modern tooling — including ours — implements.
    Ready-made quality score on a 0-100 scale that's immediately interpretable. 'This repo scores 35 on maintainability, bottom quartile across 200+ codebases we've assessed' is a finding that lands in investment committee presentations.
  • cognitive_complexitystatic_analysis
    Sonar-style cognitive complexity of the most complex entity in the file (max across functions/methods)
    Estimates the comprehension cost of the hardest function to read in the file. Unlike cyclomatic complexity (which counts independent paths), cognitive complexity penalises nesting and ignores shorthand control flow — a much closer proxy for the cognitive load a reviewer or new hire actually pays. High max scores flag the entities that will dominate post-close onboarding and code-review time.
  • cognitive_complexity_totalstatic_analysis
    Sum of Sonar-style cognitive complexity across all entities in the file
    Aggregates the whole-file comprehension burden, not just the worst entity. A file with 20 moderately complex functions can be harder to carry than one with a single hairy function — this signal surfaces those distributed-complexity files that single-entity maxima miss, and feeds the cognitive maintainability index.
  • maintainability_index_cognitivestatic_analysis
    Cognitive-flavored SEI maintainability index (0-100)
    A 0-100 maintainability score built from comprehension cost rather than Halstead's algebraic volume. Where the classic index can flatter concise-but-dense files (low volume, high difficulty to read), the cognitive variant penalises nested control flow and cumulative entity complexity the way reviewers actually feel it. Pairs with the classic MI so investment-committee narratives can distinguish 'small but tangled' from 'genuinely simple'.
  • fanout_internalstatic_analysis
    Count of unique internal (relative) imports per file
    Measures internal coupling. Files that import many other project files are integration points — complex to modify and test because changes ripple across the dependency chain.
  • fanout_externalstatic_analysis
    Count of unique external imports per file
    Measures external dependency at the file level. Files pulling in many third-party libraries are vulnerable to vendor changes and are often glue code rather than proprietary logic.
  • tiobestatic_analysis
    TIOBE quality index (0-100)
    Industry-standard composite quality score combining complexity, code size, and duplication. Enables cross-repo benchmarking: 'This file scores 45/100, below the threshold for maintainable production code.'
  • pylintstatic_analysis
    Pylint-style quality score (0-100)
    Widely recognized quality score in the Python ecosystem. Provides a familiar benchmark that engineering teams and technical advisors already understand, reducing friction in due-diligence conversations.
  • file_rolestatic_analysis
    Semantic role classification per file (generated, vendor, lock, build_artifact, fixture, test, migration, view, controller, model, service, repository, middleware, config, infra, util, unspecified)
    Enables targeted analysis by distinguishing code that matters (business logic, controllers, models) from code that doesn't (generated, vendored, config). Downstream signals can weight files differently based on their role.
  • churn_score gitgit
    Per-file near-rewrite churn score: sum of log10(loc) * min(change_frac, 1.0) across qualifying near-rewrite events (loc≥200, change_frac≥0.8, deletions>0). Code files only — tests and non-code extensions are excluded.
    Surfaces files that have been rewritten near-completely two or more times — the kind of pathology that signals an unsettled design or AI-thrashing rather than healthy iteration. Targets code files only (test churn is expected; this metric ignores it).
  • is_churning gitgit
    Boolean derived from churn_score: True iff the file has at least EVENT_COUNT_MIN=2 qualifying near-rewrite events in its git history.
    Clean binary view of the churn signal — used for the repo-level ``churn_files`` list that drives at-a-glance hotspot reporting.
  • change_density gitgit
    Per-file score: git_density * (1 - protective_factors). Protective factors: has_tests (0.4), multiple authors (0.3), low complexity (0.3). High density with good tests and multiple authors scores low. High density with no tests, single author, and high complexity scores high. Distinct from `churn_score` — that one counts near-rewrite events on code files; this measures activity-rate risk.
    Activity alone is ambiguous — active development is healthy. Change density isolates files where high change velocity lacks protective factors (tests, shared ownership, manageable complexity), surfacing true maintenance debt and key-person risk.
  • import_indegreearchitecture
    Normalized count of internal imports pointing to this file (from codeanalysis AST edges)
    Identifies architectural bottlenecks and single points of failure. A file imported by dozens of others is both the most valuable and the most dangerous — any bug there cascades across the entire system, and it cannot be replaced without coordinated refactoring.

Repo-level (26)

  • bus_factor_files gitgit
    Count of business-logic files (source, model, service, controller, repository, middleware, util, view) with only one author (bus-factor risk). Excludes vendor, generated, lock, build artifact, fixture, test, migration, config, and infra files.
    Answers the four most common workshop questions at once: Where is active development focused? Who are the critical developers? What happens if they leave? Which components are secretly coupled? These findings directly influence retention clauses, earnout structures, and integration timelines.
  • control_flow_densityheuristic
    Control-flow statements per 100 code lines. Denominator is AST-derived code lines (comments excluded) when AST metadata is available; in the regex fallback path it is non-blank lines (comments included) since the authoritative comment counter lives in stage_token_metrics, not code_metrics.
    Identifies where business rules concentrate. High control flow density in a few files means proprietary decision logic is packed into complex code, driving up rebuild cost. Low density across the repo may indicate the product is mostly glue with little original logic.
  • definition_densityheuristic
    Definitions (functions, classes) per 100 code lines. Denominator follows the same AST-vs-regex-fallback split as control_flow_density — comments-excluded with AST metadata, comments-included in the regex fallback.
    Indicates code modularity and reuse patterns. Low definition density suggests monolithic functions doing too much, increasing maintenance cost.
  • comment_ratioheuristic
    Repo-wide fraction of comment characters (LOC-weighted average of per-file Pygments-tokenized comment-character ratios)
    Documentation culture is a leading indicator of post-close maintenance risk. Near-zero comments + high change frequency = institutional knowledge trapped in developers' heads. Directly affects knowledge transfer timelines and key-person retention priorities.
  • test_coverage_ratioarchitecture
    The active test-coverage measurement: prefers AST-resolved entity reachability when available, falls back to file-pairing.
    Reveals engineering discipline. Low test coverage tells an investor the team ships without a safety net. The absence of tests is one of the most common findings that changes rebuild estimates and post-close stabilization budgets.
  • domain_token_densityheuristic
    Ratio of domain-specific vocabulary to generic tokens
    Unlocks the most strategically important signal. Function names like 'calculate_actuarial_reserve' or 'apply_regulatory_haircut' reveal the depth of proprietary domain knowledge that can't be replicated by hiring generic developers.
  • domain_match_densityheuristic
    Ratio of domain-specific vocabulary matches (stemmed + prefix)
    Measures positive matches against curated domain dictionaries (fintech, healthtech, enterprise). Unlike generic density, this confirms the codebase actually uses specialized vocabulary — a strong indicator of proprietary domain logic.
  • gluemarker_ratioheuristic
    Weighted ratio of glue/boilerplate markers to total lines
    Quantifies how much of the codebase is commodity wiring vs. substantive logic. A product that's 70% glue code has a fundamentally different value proposition than one with deep proprietary processing. Directly answers: 'How much of this is replaceable by off-the-shelf tools or AI?'
  • external_call_ratioheuristic
    Ratio of external library calls to total function calls
    Measures how self-contained the product's value is. High external dependency means vulnerability to vendor pricing changes, API deprecation, or third-party outages. Inverse relationship to defensibility.
  • duplication_ratioheuristic
    Ratio of duplicated code blocks across files
    High duplication is a direct indicator of technical debt and rebuild cost. Copy-pasted code means bugs exist in multiple places, refactoring is more expensive than it appears, and the team's engineering practices are weak.
  • lm_uniqueness SLMslm
    LM-assessed code uniqueness/novelty
    Enables 'Is this code worth acquiring?' as a scored, benchmarkable answer. Per-file IP identification transforms workshops. Rebuild estimation with AI-assessed complexity.
  • lm_complexity SLMslm
    LM-assessed implementation difficulty
    Enables 'Is this code worth acquiring?' as a scored, benchmarkable answer. Per-file IP identification transforms workshops. Rebuild estimation with AI-assessed complexity.
  • lm_quality SLMslm
    LM-assessed code quality
    Enables 'Is this code worth acquiring?' as a scored, benchmarkable answer. Per-file IP identification transforms workshops. Rebuild estimation with AI-assessed complexity.
  • program_value_score SLMslm
    SLM-synthesized holistic program value from per-file summaries
    Enables 'Is this code worth acquiring?' as a scored, benchmarkable answer. Per-file IP identification transforms workshops. Rebuild estimation with AI-assessed complexity.
  • assessment_score SLMslm
    Engineered-vs-assembled verdict from chunk+merge SLM pipeline
    Enables 'Is this code worth acquiring?' as a scored, benchmarkable answer. Per-file IP identification transforms workshops. Rebuild estimation with AI-assessed complexity.
  • api_surface_ratioarchitecture
    Ratio of API-exposed files (e.g. controllers, routes/routers, handlers, views, resolvers, resources, api/apis, endpoints). Measures what fraction of source files exposes external endpoints.
    Detects interface control. A product with a large API surface that third parties integrate with creates switching costs proportional to the number of external consumers. More endpoints = more ecosystem dependence = stickier product.
  • realtime_pattern_densityarchitecture
    Ratio of realtime/event-driven dependencies to total dependencies
    Detects a resilient AI moat. Low-latency and high-frequency domains (WebSocket, gRPC, streaming) are among the hardest for AI to replicate because they require real-time state management and performance engineering. Presence signals sticky technical complexity.
  • data_accumulation_pattern gitarchitecture
    Migration/seed files + git history span indicating data accumulation moat
    Detects data flywheel effects. 200+ migration files represent years of schema evolution driven by real-world usage. The accumulated data model and the logic to manage it are often more valuable than the application code itself.
  • governance_workflow_densityarchitecture
    Density of governance/workflow paths and deps indicating process moat
    Detects one of the stickiest moats: governance authority. Products with deep approval workflows, RBAC, and audit trails have switching costs that compound over time. Customers can't easily rip out a system that enforces their compliance processes.
  • regulatory_token_densityarchitecture
    Density of compliance/regulatory paths and deps indicating regulatory moat
    Regulated industries create natural moats because compliance requirements raise the barrier to entry for competitors and AI. A codebase with deep HIPAA, PCI, or SOX implementations reflects years of regulatory domain knowledge that can't be shortcut.
  • cochange_clusters gitgit
    Clusters of files that frequently change together, detected via Louvain community detection
    Reveals hidden architectural coupling. Files that always change together may be tightly coupled even if they have no import relationship. This informs modularization effort estimates and highlights integration risk during platform migrations.
  • dead_code_ratiostatic_analysis
    Ratio of unreachable or unused code to total lines (detects dead branches, unreferenced functions/classes)
    Dead code inflates maintenance burden and obscures the real codebase size. A high ratio signals accumulated technical debt — developers must navigate and reason about code that serves no purpose, increasing onboarding time and defect risk.
  • dependency_cycle_countstatic_analysis
    Count of circular dependency chains (imports that form loops at the module or package level)
    Circular dependencies between files make the codebase resistant to modularisation and safe refactoring. Each cycle is a tightly coupled cluster that must be changed as a unit, increasing coordination cost and merge conflict risk.
  • layer_violation_countstatic_analysis
    Count of imports that cross architectural layer boundaries (e.g., view calling repository directly in MVC). Only applies to MVC-shaped codebases.
    Layer violations (e.g. a controller importing directly from the data layer, bypassing services) indicate architectural erosion. They increase the blast radius of changes and make it harder to replace or refactor individual layers independently.
  • ai_adoption gitai_adoption
    AI tool adoption detection via config files and commit attribution. Covers evidence catalog E017-E028 (config files) and E009-E014 (git metadata).
    Detects AI coding tool usage (Copilot, Claude Code, Cursor, Aider, Devin, Codex, etc.) via config files and git commit attribution patterns. Critical for software due-diligence assessment of AI adoption maturity.
  • md_maintenance gitai_adoption
    Maintenance signal for root-level CLAUDE.md / AGENTS.md: lifetime and 90-day change ratios plus per-author maintainer distribution. Flags 'exists but unmaintained' AI memory files.
    CLAUDE.md / AGENTS.md sit in the AI's context window on every interaction. Stale memory files degrade output quality; this signal distinguishes 'has AI memory' from 'maintains AI memory'.

How compute_composite Works

  1. Collect contributions — iterate all SignalDefinitions, find those with a CompositeContribution matching the target composite name.
  2. Skip missing — if a signal's value is None (e.g. SLM unavailable, no git history), skip it entirely. Its weight is excluded from both numerator and denominator — the score degrades gracefully rather than penalizing.
  3. Normalize — if normalize_divisor is set, clamp: v = min(raw / divisor, 1.0).
  4. Invert — if inverted=True (lower-is-better signals like duplication), flip: v = 1.0 - v.
  5. Accumulateweighted_sum += v * weight, total_weight += weight.
  6. Re-normalize — final score = weighted_sum / total_weight, so the result is always 0.0–1.0 regardless of which signals were available.