Session summary

default

scored

afi / afi · cli reviewed proxy at 9b51f95e with Default profile / Anthropic · opus 5 high on afi 0.30.0

Measurements

Unavailable values retain their exported state and reason

Quality

Recall 45%
Model-judged precision N/A
F1 N/A
Tier 1 recall 66.7%
Tier 2 recall 28.6%
Tier 3 recall 50%
Tier 4 recall 50%
Security recall 50%
Defect recall 53.8%
Maintainability recall 0%
Performance recall 33.3%
Median anchor distance 0
Worst anchor distance 0
Refusals a judge overturned Not recorded

Review outcomes

Distinct matched defects 9
Defects missed 11
Findings 9
Unkeyed findings 0
Intended findings 0
Carried findings Not recorded

Cost, usage, and speed

Total bill $0.7546
Review bill $0.7546
Judge bill N/A
Tokens 105889
Duration 2m 9s

Execution and identity

Lifecycle, reviewer, build, settings, and normalized execution limits

Lifecycle
completed
Lifecycle reason
Not recorded
Started
Finished
Reviewer
afi
Reviewer tool
afi · cli
Configuration
default / a5df80571ddb
Configuration ID
config-a1911a53d2684bd1
Build
afi 0.30.0 / b0f313c0b59a66ecc7612396dc8db0ea5da13a7a
Comparison
comparison-5481b1a296f22243
Build ID
build-30153468219856e4
Cohort ID
cohort-07363c4dd57b37b6
Harness
bench 1 / 3c151df56e62cfdea90b84dd8b265f7e0f4bebff
Adapter
afi 1 / sha256:a1a935298956020ee6d767a756847abb4c00694c88c2eb80d454886ebc4acf8c
Exit code
0
Wall time
129
Deadline exceeded
No
Budget
$12.00
Billing limit
$12.00

Usage

Normalized reviewer-tool usage projection

Input tokens
103695
Output tokens
2194
Cached input
0
Reasoning tokens
7249
Requests
3
Models counted
Not recorded

Judge

Normalized assessment summary

Judge data is N/A for this run

Findings

Review text is escaped; normalized decisions follow each finding

Finding f1

bug

forget` runs unconditionally, so `POST /v1/admin/cache/sweep?dry_run=true` deletes every index entry naming a blob it merely *reported* — `gone` is populated at line 137 regardless of `dry_run`. The advertised "reports what one would reclaim without removing anything" (docs/operations.md) silently un-caches the store, and the next real sweep then deletes those now-unreferenced blobs.

bug
Category: bug Confidence: Not recorded

services/proxy/src/sweep.rs:140

Evidence quote

Not recorded

Suggested fix

Not recorded

matchedDefect 12 (locality)
Finding f2

bug

values()` returns bytes-reclaimed where `COUNTERS[4]` is `cairn_proxy_sweep_blobs_removed_total` and blobs-removed where `COUNTERS[5]` is `cairn_proxy_sweep_bytes_reclaimed_total`; the two sweep… values()` returns bytes-reclaimed where `COUNTERS[4]` is `cairn_proxy_sweep_blobs_removed_total` and blobs-removed where `COUNTERS[5]` is `cairn_proxy_sweep_bytes_reclaimed_total`; the two sweep counters are exposed under each other's names, so the alert docs/operations.md tells operators to build on `cairn_proxy_sweep_bytes_reclaimed_total` reads a blob count. Swap lines 94 and 95.

bug
Category: bug Confidence: Not recorded

services/proxy/src/metrics.rs:94

Evidence quote

Not recorded

Suggested fix

Not recorded

matchedDefect 13 (locality)
Finding f3

bug

sort_by_key(|c| c.age)` orders *ascending* age — newest first — so the over-ceiling loop evicts the blobs most recently fetched, exactly the opposite of the comment on line 100 and of the… sort_by_key(|c| c.age)` orders *ascending* age — newest first — so the over-ceiling loop evicts the blobs most recently fetched, exactly the opposite of the comment on line 100 and of the `CACHE_MIN_AGE` grace period's purpose (a fresh blob just past `min_age` goes before a year-old one). Sort by `std::cmp::Reverse(candidate.age)`.

bug
Category: bug Confidence: Not recorded

services/proxy/src/sweep.rs:102

Evidence quote

Not recorded

Suggested fix

Not recorded

matchedDefect 1 (locality)
Finding f4

bug

clear_partials` unlinks every file in `incoming` with no age or liveness test, but that directory holds the *in-flight* temp files of `BlobStore::writer` (store.rs:201) and of `BlobStore::link`… clear_partials` unlinks every file in `incoming` with no age or liveness test, but that directory holds the *in-flight* temp files of `BlobStore::writer` (store.rs:201) and of `BlobStore::link` (store.rs:147). A sweep firing during a 200 MiB download deletes the file under the open handle, and `BlobWriter::commit`'s rename (store.rs:259) fails with ENOENT, turning a healthy fetch into a 500. Only files older than `min_age` (or than `fetch_timeout`) are abandoned.

bug
Category: bug Confidence: Not recorded

services/proxy/src/sweep.rs:232

Evidence quote

Not recorded

Suggested fix

Not recorded

matchedDefect 17 (locality)
Finding f5

bug

the "nothing points at it" branch ignores age, but a freshly committed blob is unreferenced for the whole of the registry round trip — cache.rs commits at line 151 and only calls `store.link` at… the "nothing points at it" branch ignores age, but a freshly committed blob is unreferenced for the whole of the registry round trip — cache.rs commits at line 151 and only calls `store.link` at line 163. A sweep in that window removes the blob, and the reopen at cache.rs:90 fails with "a blob committed by this request is already missing from the store". Unreferenced blobs must also be older than `min_age` before they are removed.

bug
Category: bug Confidence: Not recorded

services/proxy/src/sweep.rs:119

Evidence quote

Not recorded

Suggested fix

Not recorded

matchedDefect 18 (locality)
Finding f6

performance

sweep` is a synchronous full-tree `read_dir`/`metadata`/`unlink` walk, and both callers run it directly on a runtime worker — `Sweeper::run` at line 84 and the handler at routes/admin.rs:76. On the large store the module doc contemplates this parks a worker thread for the whole walk, stalling the async tasks (including in-flight artifact streams) scheduled on it. It needs `tokio::task::spawn_blocking`.

performance
Category: performance Confidence: Not recorded

services/proxy/src/sweep.rs:90

Evidence quote

Not recorded

Suggested fix

Not recorded

matchedDefect 11 (locality)
Finding f7

bug

the route calls `sweeper.sweep(...)` and not `sweeper.run()`, bypassing the `running` mutex (sweep.rs:67) that the module doc at sweep.rs:10 says makes one-sweep-at-a-time a property of the type. Two concurrent `POST /v1/admin/cache/sweep` calls, or one racing the background tick, each size their removals against a total the other is shrinking and take the store far under the ceiling.

bug
Category: bug Confidence: Not recorded

services/proxy/src/routes/admin.rs:76

Evidence quote

Not recorded

Suggested fix

Not recorded

matchedDefect 6 (locality)
Finding f8

bug

fs::metadata` follows symlinks, contradicting the comment on lines 160-163 — a symlink is counted at its *target's* size, and a symlinked directory inside the blob dir makes `collect` recurse… fs::metadata` follows symlinks, contradicting the comment on lines 160-163 — a symlink is counted at its *target's* size, and a symlinked directory inside the blob dir makes `collect` recurse outside the store, where its files are then unreferenced and unlinked at line 126. Use `fs::symlink_metadata` and skip non-regular files.

bug
Category: bug Confidence: Not recorded

services/proxy/src/sweep.rs:164

Evidence quote

Not recorded

Suggested fix

Not recorded

matchedDefect 4 (locality)
Finding f9

bug

remaining >= self.max_bytes` removes one more blob when the store is exactly at the ceiling; the ceiling is documented as the most the store *may* hold, so this should be `>`.

bug
Category: bug Confidence: Not recorded

services/proxy/src/sweep.rs:120

Evidence quote

Not recorded

Suggested fix

Not recorded

matchedDefect 3 (locality)

Source artifacts

Open a row to inspect or download evidence from this atomically published local run

findings present

Open verified source artifact

Open this path at the exported commit

The artifact is present; its content is available through the verified source link

meta present

Open verified source artifact

Open this path at the exported commit

The artifact is present; its content is available through the verified source link

record present

Open verified source artifact

Open this path at the exported commit

The artifact is present; its content is available through the verified source link

result present

Open verified source artifact

Open this path at the exported commit

The artifact is present; its content is available through the verified source link

spend present

Open verified source artifact

Open this path at the exported commit

The artifact is present; its content is available through the verified source link

stderr present

Open verified source artifact

Open this path at the exported commit

The artifact is present; its content is available through the verified source link

summary present

Open verified source artifact

Open this path at the exported commit

The artifact is present; its content is available through the verified source link

traffic present

Open verified source artifact

Open this path at the exported commit

The artifact is present; its content is available through the verified source link

benchee benchee-dashboard-1 built from 10f4ec58 Static benchmark evidence ·