Scrubby

Findings & the Learning Loop

How every review event reinforces or weakens Scrubby's domain connections — the Hebbian-learning mechanism behind "Scrubby gets smarter over time."

When we say Scrubby “gets smarter over time,” we mean it literally. Every analysis Scrubby does — whether driven by a PR, an MCP call from your editor, or a CI integration — is also a learning event. The connections between domains get reinforced when they produce useful signal and weakened when they don’t.

This page explains the mechanism, why it matters, and how you (and your AI agent) can feed the loop.

The signal

The atomic unit of learning is a finding — a single piece of feedback Scrubby produces during a review. Each finding has:

  • A severity (error, warning, info).
  • A message (the human-readable explanation).
  • An optional domain — the domain that produced the finding.
  • An optional line number for inline-suggestion rendering.

The domain field is what makes a finding load-bearing for learning. When a finding is attributed to a domain, Scrubby knows which connection to reinforce.

How weights move

When a finding lands on a file:

  • The connection between the finding’s domain and the file’s primary domain gets reinforced by a small delta (+0.05).
  • When a connected domain runs a check on a file but produces no findings, that connection gets weakened by a smaller delta (-0.02).
  • Weights are clamped to [0, 1].

The asymmetry is intentional: useful signal counts more than its absence. A connection that’s frequently silent decays slowly; a connection that’s frequently productive strengthens quickly.

Global vs repo-scoped

Repo-scoped domain weights move on every review on that repo. Global-domain weights (Ruby, React, Testing, Security, etc.) move on a slower clock so a single repo’s signal doesn’t move them disproportionately. The aggregate across many repos is what trains a global domain’s connections.

This is why a fresh React app gets reasonable reviews on day one: it inherits global-domain weights that have been trained across the broader ecosystem.

Temporal decay

Even strong connections decay slowly over time (factor 0.995 per activation). This lets the network forget patterns that stop being relevant — if your team rewrites the auth domain and the old connections to it stop firing, those edges fade naturally.

Where learning happens

Three places, all calling the same underlying mechanism:

  1. scrubby_report_findings from your AI editor. When your agent reviews a file and reports its findings, weights update. See scrubby_report_findings.
  2. GitHub App PR reviews. Every PR Scrubby reviews produces a set of findings, which feed the loop automatically.
  3. PR feedback signals. Dismissing a finding on a PR signals that the connection that produced it was wrong; that flows back as a small negative update.

Why this matters in practice

Two effects compound over time:

  • Cross-domain noise fades. Domain pairs that started with weak coupling but never produce useful findings drift to zero. Scrubby stops asking the questions that don’t pay off.
  • The connections that matter get sharper. Domain pairs that consistently produce useful findings strengthen. Scrubby’s reviews become increasingly tuned to your codebase.

After a few weeks of normal development, the network looks the way your senior engineers think about the codebase — with the same hot-spots, the same caution flags, the same strong dependencies.

Inspecting the network

You can see the current state of the connection graph at any time via scrubby_get_network, which returns connections sorted by weight. The dashboard’s repository view also visualizes this.

If a connection looks wrong — too high, too low — the answer is almost always to keep using Scrubby. Repeatedly running reviews and reporting findings is how the network stabilizes.

Last updated