Skip to content

Prune old runs after each build poll so stale branches stop keeping rows [patch] - #398

Open
matt-edmondson wants to merge 1 commit into
mainfrom
fix/313-prune-runs
Open

matt-edmondson wants to merge 1 commit into
mainfrom
fix/313-prune-runs

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #313

What was wrong

Both providers add the newest 10 runs on every poll (Runs.GetOrCreate), and nothing removed them except a GitHub 404 or File → Clear Data. Every Dependabot or feature branch that ever ran therefore kept its own row in the table indefinitely. Build.Runs is persisted, so the app data file grew without bound and was re-serialized on every run update.

Change

New Build.PruneRuns(now), called from BuildSync.UpdateBuildAsync right after the provider poll, so it covers both GitHub and Azure DevOps. It keeps a run when any of these holds:

  • it is still pending or running, so it is never dropped;
  • it is one of the build's 50 newest runs (Build.RetainedNewestRuns);
  • it is one of the 20 newest successful runs (DurationEstimator.MaxSamplesToConsider, now internal). The estimator keeps its full sample even when every recent run failed;
  • it is the newest run on a branch that ran within 30 days (Build.BranchRetention).

Everything else is removed. When anything is pruned, a save is queued. A merged and deleted branch drops out of the table once it is outside both the newest 50 and the 30-day window. RunSyncs for removed runs were already cleaned up through IsOrphaned, and only finished runs are pruned anyway.

I also updated the "Entity Update Strategy" section of CLAUDE.md with a Run Retention rule, and corrected the "No data accumulation from stale entities" line to say it now covers runs.

Tests

New RunRetentionTests:

  • MoreRunsThanTheCapAreTrimmedToTheNewest: 80 runs → the 50 newest are kept.
  • AnOldBranchOutsideTheNewestRunsLosesItsRow: the issue's Dependabot scenario.
  • ABranchThatRanWithinTheRetentionWindowKeepsItsNewestRun: only the branch's newest run is kept.
  • TheEstimatorKeepsItsFullSampleWhenRecentRunsFailed: the estimate is unchanged after pruning.
  • RepeatedPollsOfABusyWorkflowStayBounded: a simulated year of daily polls stays under the bound.
  • ABuildUnderTheCapKeepsEveryRun and AnOngoingRunIsNeverDropped: guards on what must be kept.

Verified locally on Linux / .NET 10:

  • With the change: all 176 tests pass, including the UI tests.
  • With PruneRuns stubbed to return 0: the five behaviour tests fail, and the two guard tests pass as expected.

Worth a look

The thresholds (50 newest, 30 days) are constants chosen inside the ranges the issue suggests. Adjust them if you'd rather keep more history.


Generated by Claude Code

…ows [patch]

Every poll added the newest runs a provider reported and nothing removed them, so every branch
that ever ran kept its own table row and the app data file grew without bound. After each build
poll, Build.PruneRuns keeps ongoing runs, the 50 newest runs, the estimator's 20 newest
successful runs, and the newest run of each branch that ran within 30 days, and drops the rest.

Fixes #313

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW2iZzaJLpHHri8a4vS
@sonarqubecloud

sonarqubecloud Bot commented Nov 4, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Runs are never pruned, so every branch that ever ran keeps its own table row forever and the app data file grows without bound

2 participants