Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Detects time and space complexity hotspots via AST scan. Use when code feels slow, before performance-sensitive merges, or to find O(n²) regressions.
.claude/skills/athola-performance-review/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 78% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 83% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 43% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 134% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 44% | 0% |
Static-analysis review of time and space complexity hotspots.
The skill runs in three escalating tiers. Tier 1 uses Python's stdlib ast and always runs. Tier 2 uses gauntlet's tree-sitter parser to extend detection across languages when gauntlet is installed. Tier 3 uses the gauntlet code graph to upgrade severity when hotspots reach other hotspots transitively. If gauntlet is missing, Tiers 2 and 3 no-op and Tier 1 still produces useful findings on Python source.
bash/performance-review # scan changed files /performance-review path/to/file.py # scan one file /performance-review --tier 1 # force Tier 1 only
Programmatic use:
pythonfrom pensive.skills.performance_review import PerformanceReviewSkill skill = PerformanceReviewSkill() result = skill.analyze(context, "src/module.py") for f in result.issues: print(f"[{f.severity}] {f.file}:{f.line} {f.message}")
profiler.
are common.
time on real data). Use Skill(parseltongue:python-performance) instead: that skill drives cProfile, py-spy, and benchmarks.
Skill(pensive:code-refinement) whose algorithm-efficiency module covers broader optimization patterns. This skill detects; that skill teaches.
SIMD, strength reduction) is worth keeping: use Skill(leyline:loop-optimization) for the hand-vs-compiler rule. This skill flags hotspot shapes, not transformation choices.
queue placement): use Skill(pensive:architecture-review).
perf-review:context-establishedperf-review:scan-completeperf-review:findings-categorizedperf-review:integration-checkedperf-review:report-generatedperf-review:findings-verifiedperf-review:context-established)git diff --name-only. If invoked with a path, scope to that.
files need gauntlet for Tier 2 coverage.
perf-review:scan-complete)Load modules/time-complexity.md for the time-side patterns and modules/space-complexity.md for space-side. Each module documents the AST shape of every detector.
Alongside the automated scan, load modules/memory-allocation-lenses.md and apply its three manual lenses (unbounded external-source collections, hot-path recompute, serial blocking I/O) by reading the target files.
For each Python target file, call:
pythonfrom pensive.skills.performance_review import PerformanceReviewSkill result = PerformanceReviewSkill().analyze(context, path)
The visitor walks the AST once and emits ReviewFinding records.
perf-review:findings-categorized)Group findings by severity:
(T3, T4, S1, S3).
Within a severity, sort by file then line. Suppress findings the user has explicitly marked acceptable (TODO/comment markers) at module-load time of the target.
perf-review:integration-checked)Load modules/gauntlet-integration.md for the contract.
If gauntlet is installed, run Tier 2 on non-Python files that were skipped at Step 2. If a .gauntlet/graph.db exists in the working tree, run Tier 3 to upgrade severities based on transitive hotspot reachability.
If gauntlet is missing, this step is a no-op and the report notes "Tier 2/3 not available: install gauntlet for multi-language and call-chain coverage."
perf-review:report-generated)Emit a markdown report:
## Performance Review: <target>
### HIGH (<count>)
- src/foo.py:42: Nested loop over the same iterable 'items'.
Suggestion: sort + two pointers, or hash-set membership.
### MEDIUM (<count>)
- ...
### LOW (<count>)
- ...
Tier coverage: 1 (always) | 2 (gauntlet ✓/✗) | 3 (graph ✓/✗)The report is informational. Apply fixes via Skill(pensive:code-refinement) or hand-merge.
| Tier | Source | When it runs | What it covers | |------|--------|--------------|----------------| | 1 | stdlib ast | Always (Python source only) | T1-T6, S1-S3 | | 2 | gauntlet.treesitter_parser | When gauntlet importable | Same patterns adapted to JS/TS, Go, Rust, Java, C/C++ | | 3 | gauntlet.graph.GraphStore | When .gauntlet/graph.db exists | Severity upgrade via transitive call chains |
Findings use the shared ReviewFinding dataclass from pensive.skills.base:
pythonReviewFinding( file="src/module.py", line=42, severity="HIGH", # LOW | MEDIUM | HIGH | CRITICAL category="time", # time | space message="Nested loop over the same iterable 'items'.", suggestion="Sort + two pointers, or hash-set membership.", anchor="verbatim source text at file:line", code_snippet="", )
This shape matches every other pensive review skill, so the findings can flow into Skill(pensive:unified-review) without translation.
| Dependency | Required? | Effect when missing | |------------|-----------|---------------------| | gauntlet.treesitter_parser | Optional | Tier 2 returns ]; Python coverage unchanged | | gauntlet.graph.GraphStore | Optional | Tier 3 returns ]; severities are not upgraded |
The optional-import contract follows the precedent in plugins/leyline/src/leyline/tokens.py:25-32: try-import to a module-level sentinel, then early-return on None inside each tier helper. plugins/gauntlet/hooks/precommit_gate.py:35-40 is the boolean-flag variant of the same shape. See modules/gauntlet-integration.md for the exact code shape.
modules/time-complexity.md: T1-T6 detector patterns and ASTshapes.
modules/space-complexity.md: S1-S3 detector patterns.modules/gauntlet-integration.md: Tier 2/3 contract,fallback semantics, examples.
modules/kuva-visualization.md: Rendering benchmark data ascharts with kuva (criterion, pytest-benchmark, ad-hoc tables). Covers when chart evidence satisfies proof-of-work requirements.
modules/memory-allocation-lenses.md: Manual review lenses(not AST detectors) for unbounded collections fed from external sources, hot-path recompute that should be memoized, and serial blocking I/O over unbounded sets. Apply by reading the code; the detector-test rule in Testing does not cover these because nothing is automated.
A perf-review finding is only useful if the caller can confirm it is real. Use this checklist before treating any finding as worth fixing:
cProfile, py-spy, or thelanguage-specific equivalent on the hotspot. The findings pinpoint AST shapes; the profiler validates the runtime impact.
benches/ exists, thehotspot should show up in numbers, not just AST scans.
is wrong if numbers do not move. Capture both timings as evidence references like [E1] (before) and [E2] (after). When 3+ data points exist, render a kuva chart and attach it to the PR (see modules/kuva-visualization.md).
be true at the AST level and false at the call-graph level when callers short-circuit. Manual sampling catches that.
The Skill(imbue:proof-of-work) discipline applies: claims like "the hotspot is fixed" require evidence, not assertion.
A test file already lives at plugins/pensive/tests/skills/test_performance_review.py covering the AST-shape detectors. Two rules for changes here:
added to the modules ships with a test that has the smallest AST sample exercising it.
the skill stops firing on a shape that used to look hot, the reason should appear as a test case so the regression is discoverable later.
The Iron Law applies: a new detector without a failing test first is a request to skip TDD on a code-analysis component, which is exactly the place where TDD pays off most.
perf-review:findings-verified)Every finding must cite a real location and a verbatim anchor. Write findings to .review/findings.json and confirm each citation resolves:
bashpython plugins/imbue/scripts/citation_verifier.py \ --findings .review/findings.json --repo-root .
Drop or label UNVERIFIED any finding the verifier fails (exit 1); only verified findings enter the report. See Skill(imbue:review-core) Step 5 and Skill(imbue:structured-output) for the schema.
suggestion the caller can act on.
detectors have been run; tier coverage is reported.
contracts honor the optional-import sentinel: missing modules return [] rather than raising.
fails before the detector exists; each removed false positive ships with a regression test.
Skill(pensive:unified-review) withouttranslation when invoked from the unified entry point.
Location + verbatim Anchorconfirmed by citation_verifier.py (exit 0), or unverified findings were dropped or labeled UNVERIFIED
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-22 | pass→pass | 13,332 | 7,645 | -43% | 1 | 1 | 0% | 2,768 | 4,165 | +50% | 0 | 0 | — |
case-23 | pass→pass | 20,646 | 10,597 | -49% | 1 | 1 | 0% | 3,178 | 4,540 | +43% | 0 | 0 | — |
case-01 | fail→fail | 21,581 | 3,905 | -82% | 1 | 1 | 0% | 3,810 | 3,062 | -20% | 0 | 0 | — |
case-02 | fail→fail | 10,708 | 7,892 | -26% | 1 | 1 | 0% | 1,590 | 3,173 | +100% | 0 | 0 | — |
case-03 | fail→fail | 6,673 | 5,589 | -16% | 1 | 1 | 0% | 1,069 | 3,224 | +202% | 0 | 0 | — |
case-04 | fail→pass | 21,297 | 4,445 | -79% | 1 | 1 | 0% | 1,836 | 3,265 | +78% | 0 | 0 | — |
case-05 | fail→pass | 11,057 | 2,246 | -80% | 1 | 1 | 0% | 1,774 | 3,248 | +83% | 0 | 0 | — |
case-06 | fail→pass | 14,660 | 2,673 | -82% | 1 | 1 | 0% | 2,291 | 3,271 | +43% | 0 | 0 | — |
case-07 | fail→pass | 10,871 | 5,769 | -47% | 1 | 1 | 0% | 1,632 | 3,824 | +134% | 0 | 0 | — |
case-08 | fail→pass | 16,469 | 3,338 | -80% | 1 | 1 | 0% | 2,368 | 3,416 | +44% | 0 | 0 | — |
case-09 | fail→pass | 11,378 | 2,465 | -78% | 1 | 1 | 0% | 1,847 | 3,312 | +79% | 0 | 0 | — |
case-10 | pass→pass | 10,314 | 2,359 | -77% | 1 | 1 | 0% | 1,588 | 3,264 | +106% | 0 | 0 | — |
case-21 | fail→pass | 17,722 | 13,819 | -22% | 1 | 1 | 0% | 2,968 | 5,187 | +75% | 0 | 0 | — |
case-11 | pass→pass | 16,496 | 1,930 | -88% | 1 | 1 | 0% | 1,540 | 3,137 | +104% | 0 | 0 | — |
case-12 | fail→pass | 12,097 | 4,241 | -65% | 1 | 1 | 0% | 1,869 | 3,602 | +93% | 0 | 0 | — |
case-13 | fail→pass | 9,920 | 2,294 | -77% | 1 | 1 | 0% | 1,520 | 3,204 | +111% | 0 | 0 | — |
case-14 | pass→pass | 6,249 | 2,582 | -59% | 1 | 1 | 0% | 1,001 | 3,314 | +231% | 0 | 0 | — |
case-15 | pass→pass | 17,644 | 4,212 | -76% | 1 | 1 | 0% | 2,676 | 3,621 | +35% | 0 | 0 | — |
case-16 | fail→pass | 10,636 | 4,154 | -61% | 1 | 1 | 0% | 1,687 | 3,700 | +119% | 0 | 0 | — |
case-17 | fail→pass | 19,590 | 7,354 | -62% | 1 | 1 | 0% | 2,818 | 4,099 | +45% | 0 | 0 | — |
case-18 | fail→pass | 16,784 | 6,807 | -59% | 1 | 1 | 0% | 2,677 | 3,948 | +47% | 0 | 0 | — |
case-19 | fail→pass | 17,249 | 4,276 | -75% | 1 | 1 | 0% | 2,026 | 3,669 | +81% | 0 | 0 | — |
case-20 | pass→pass | 15,392 | 5,889 | -62% | 1 | 1 | 0% | 2,340 | 3,751 | +60% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted, and 20 counted toward the lift figure. The other 3 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +57 percentage points is the difference between those two pass rates over the 20 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.