A memory leak detection library for Android.
This report summarizes scanner evidence for square/leakcanary, a Kotlin project with 30,003 GitHub stars. SynthScan examined 132,425 lines of code across 889 source files, recording 78 pattern matches distributed across 7 syntactic categories. The legacy adjusted pattern signal is 1.4 (Low pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Payload schema: 1.0.0 · Engine: 2.0.0 · Scoring: heuristic-density-v1 · Scan date: 2026-10-31.
Can maintainers explain intent, assumptions, and trade-offs? 74 finding(s).
Are imports declared and package/API names verified? 4 finding(s).
Are wrappers, guards, and repeated structures necessary? 0 finding(s).
Which changes need focused human review? 78 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 78 distinct pattern matches across 7 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM⚡ | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 3 | # Follows what the harness's agent is doing, live: every call it makes, and everything that comes back. | COMMENT |
| MEDIUM⚡ | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 5 | # What this is for: `start-harness.sh` shows you the agent's *answer* — the text a `--print` run prints when it | COMMENT |
| MEDIUM⚡ | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 14 | # ./watch-transcript.sh follow the newest harness run in this terminal | COMMENT |
| MEDIUM⚡ | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 19 | # harness: 17 lines two seconds in, 36 by the time the client exited eleven seconds later. It is not written | COMMENT |
| MEDIUM⚡ | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 23 | # **Which file it is, is knowable before the run starts, and only because the harness pins the session id.** | COMMENT |
| MEDIUM⚡ | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 26 | # is what `start-harness.sh` passes to close that, and `session-id.txt` beside the prompt is where it writes | COMMENT |
| MEDIUM⚡ | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 29 | # **Don't compute the path from the harness directory — glob for the id.** The client keys the directory on | COMMENT |
| MEDIUM⚡ | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 30 | # the *physical* working directory, and on macOS `$TMPDIR` is a symlink into `/private/var`, so the harness's | COMMENT |
| MEDIUM⚡ | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 32 | # harness ever mentions. A path built by hand is therefore right on Linux and wrong here. The id is unique, so | COMMENT |
| MEDIUM | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 98 | # started. Named explicitly when it isn't: harness directories are never deleted — see `start-harness.sh` on | COMMENT |
| MEDIUM | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 124 | # Empty when the run was staged before `start-harness.sh` wrote one down, which `wait_for_the_transcript` | COMMENT |
| MEDIUM | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 134 | # this globs for the id rather than building the path from the harness directory. | COMMENT |
| MEDIUM | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 216 | # The description before the command for Bash, which on this harness is very nearly every call: an agent | COMMENT |
| MEDIUM⚡ | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 229 | # the person's own `~/.claude`. That is the whole of what keeps a harness run readable on its own: the client | COMMENT |
| MEDIUM⚡ | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 230 | # is already isolated to `$HARNESS_DIRECTORY/claude` — see `run_the_agent` in start-harness.sh — so the | COMMENT |
| MEDIUM⚡ | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 234 | # hand, since nothing pushes. `--port 0` because several harness runs being read at once is the normal case | COMMENT |
| MEDIUM | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 11 | # thing it has to get right — which is the first thing a person has to get right too. A harness that handed | COMMENT |
| MEDIUM | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 13 | # `harness/eval/run-eval.sh` opens nothing. See `write_prompt`. | COMMENT |
| MEDIUM | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 56 | # for the same reason the directory does: two harness runs both tiled `Agent harness` are two windows nobody | COMMENT |
| MEDIUM | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 82 | # harness directories are never deleted and two runs at once is the normal case, so the newest is the right | COMMENT |
| MEDIUM | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 146 | # harness windows at once are indistinguishable on screen. Renaming the copy names the tile; the two plist | COMMENT |
| MEDIUM | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 203 | # harness's own shape breaks: the app is a copy of a build in a temporary directory, in neither of the two | COMMENT |
| MEDIUM | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 241 | # tool would be scored on something else; a harness is not scoring anything. **So this runs a model's shell | COMMENT |
| MEDIUM | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 246 | # answer for a harness that is about the surface rather than about a model — a name written down here is a name | COMMENT |
| MEDIUM | …k/shark-dive/shark-dive-agent/harness/eval/run-eval.sh | 283 | # `start-harness.sh` dropped it, having nothing to score. This comment claimed it was doing that job alone | COMMENT |
| MEDIUM | …k/shark-dive/shark-dive-agent/harness/eval/run-eval.sh | 289 | # a heap dump. `start-harness.sh`'s `run_the_agent` has the measurement and why it is invisible from inside an | COMMENT |
| MEDIUM | …k/shark-dive/shark-dive-agent/harness/eval/run-eval.sh | 316 | # the run is about, which reads like a harness leaking the other scenarios and not this one. And the client | COMMENT |
| MEDIUM | …k/shark-dive/shark-dive-agent/harness/eval/run-eval.sh | 419 | # A copy for the two reasons `start-harness.sh` copies — `build/compose` is deleted by the next Compose task, | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | config/detekt-config.yml | 61 | LabeledExpression: | COMMENT |
| LOW | gradle/libs.versions.toml | 1 | # Copyright (C) 2021 Square, Inc. | COMMENT |
| LOW | .github/workflows/promote-shark-dive.yml | 1 | # Tells every running Shark Dive that a release exists, which is a separate act from publishing it. | COMMENT |
| LOW | .github/workflows/main.yml | 101 | # whether to show error dialogs from the configuration alone and ignore it. | COMMENT |
| LOW | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 1 | #!/bin/bash | COMMENT |
| LOW | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 21 | # it and a reader that starts early blocks until there is more. | COMMENT |
| LOW | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 41 | # dropped by the file, it never arrives. | COMMENT |
| LOW | …hark-dive/shark-dive-agent/harness/watch-transcript.sh | 121 | echo "${newest%/}" | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 1 | #!/bin/bash | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 21 | # and still not a blind one. **So a run that is meant to show whether the surface carries an investigation | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 61 | readonly CLIENT_CONFIG_DIRECTORY="$HARNESS_DIRECTORY/claude" | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 141 | COMMENT | |
| LOW | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 181 | # real name is what somebody reading a staged directory afterwards is looking for. | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 201 | # | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 221 | # second lock whose comment claims to be load-bearing is the kind of thing somebody later reasons from. | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 241 | # tool would be scored on something else; a harness is not scoring anything. **So this runs a model's shell | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/start-harness.sh | 281 | --permission-mode bypassPermissions \ | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/eval/run-eval.sh | 1 | #!/bin/bash | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/eval/run-eval.sh | 21 | # | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/eval/run-eval.sh | 41 | # Every invocation gets a directory of its own, named for when it started, because notes and verdicts are kept | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/eval/run-eval.sh | 181 | # shell: | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/eval/run-eval.sh | 201 | # | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/eval/run-eval.sh | 221 | mkdir -p "$directory/cwd" | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/eval/run-eval.sh | 261 | END | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/eval/run-eval.sh | 281 | # and one on a scratch directory has none. Which makes `--strict-mcp-config` below redundant rather than | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/eval/run-eval.sh | 301 | # and the final answer — and it is **not** a transcript: the model's own turns, and the thinking between two | COMMENT |
| LOW | …k/shark-dive/shark-dive-agent/harness/eval/run-eval.sh | 341 | } | COMMENT |
| LOW | shark/shark-dive/shark-dive-app/icons/render-icons.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | …k-dive-app/src/test/java/shark/dive/app/DiveAppTest.kt | 761 | COMMENT | |
| LOW | …-dive-app/src/main/java/shark/dive/app/HeapDumpDive.kt | 501 | } | COMMENT |
| LOW | …ve/shark-dive-app/src/main/java/shark/dive/app/Main.kt | 81 | // Launched from a terminal, so Shark's own diagnostics and any failure to open a heap dump belong on | COMMENT |
| LOW | …-dive-core/src/main/java/shark/dive/OwnerReferences.kt | 261 | */ | COMMENT |
| LOW | …-dive-core/src/main/java/shark/dive/OwnerReferences.kt | 281 | // | COMMENT |
| LOW | …-dive-core/src/main/java/shark/dive/OwnerReferences.kt | 301 | // | COMMENT |
| LOW | …-dive-core/src/main/java/shark/dive/OwnerReferences.kt | 321 | ownedObjects = InstancesOf(LayoutNodeChildReferenceReader.LAYOUT_NODE_CLASS_NAME), | COMMENT |
| LOW | …-dive-core/src/main/java/shark/dive/OwnerReferences.kt | 341 | // Compose's modifier nodes point back at their coordinators, which point back at their layers and | COMMENT |
| LOW | …-dive-core/src/main/java/shark/dive/OwnerReferences.kt | 361 | // of a Dagger and a Metro component: every scoped instance came out under the root, and under this | COMMENT |
| LOW | …-dive-core/src/main/java/shark/dive/CachedMapValues.kt | 181 | // library's — nine SQLite prepared statement caches, a job cache, a typeface cache, two empty ones | COMMENT |
| LOW | …-core/src/main/java/shark/dive/HeapDominatorTreemap.kt | 1221 | // A section a strength names, or an object nothing reaches: either way there is nothing to fix for these | COMMENT |
| LOW | shark/shark/src/main/java/shark/HeapAnalyzer.kt | 101 | // TODO Callers should add to analysisStartNanoTime | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| CRITICAL | …e-core/src/test/java/shark/dive/DeviceHeapDumpsTest.kt | 299 | at com.android.server.am.ActivityManagerService.enforceDebuggable(ActivityManagerService.java:1) | CODE |
| CRITICAL | …ve/shark-dive-core/src/test/java/shark/dive/AdbTest.kt | 139 | at com.android.server.am.ActivityManagerService.enforceDebuggable(ActivityManagerService.java:1) | CODE |
| CRITICAL | …ve/shark-dive-core/src/test/java/shark/dive/AdbTest.kt | 140 | at com.android.server.am.ActivityManagerService.dumpHeap(ActivityManagerService.java:2) | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | …d-core/src/main/java/leakcanary/internal/tv/TvToast.kt | 22 | * Don't forget to call [Toast.show] to display the toast! | COMMENT |
| LOW | …-release/src/main/java/leakcanary/BackgroundTrigger.kt | 25 | * Defaults to logging to [SharkLog] (don't forget to set [SharkLog.logger] if you do want to see | COMMENT |
| LOW | …d-release/src/main/java/leakcanary/ScreenOffTrigger.kt | 34 | * Defaults to logging to [SharkLog] (don't forget to set [SharkLog.logger] if you do want to see | COMMENT |
| MEDIUM | docs/theme/main.html | 4 | Upgrade to <a href="{{ base_url }}/changelog/">LeakCanary <strong>{{config.extra.leak_canary.release}}</strong></a> 🐤 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | gradle/libs.versions.toml | 59 | # The two dependency injection runtimes Shark Dive has an owner rule about, on the test classpath of | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | .github/workflows/main.yml | 73 | # Create the AVD and boot it from scratch on every run, rather than restoring a cached | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | …rk-dive-core/src/test/java/shark/dive/ReferrersTest.kt | 258 | field["placeholder"] = payload | CODE |