Skip to content

runs: record a real changed-file count so --changes and the TUI fold cover bucket and index runs#184

Merged
mbertschler merged 1 commit into
mainfrom
claude/implement-182-hgd5bc
Jul 25, 2026
Merged

runs: record a real changed-file count so --changes and the TUI fold cover bucket and index runs#184
mbertschler merged 1 commit into
mainfrom
claude/implement-182-hgd5bc

Conversation

@mbertschler

Copy link
Copy Markdown
Owner

Completes the F19 work #179 left half-done.

The gap

runs.file_count means different things per kind. A peer sync records the receiver-verified — i.e. transferred — count, so its no-ops already read as zero; a bucket push records transferred plus already-correct, and an index run records added plus modified plus unchanged, so their no-ops read as the whole volume. No column answered "did this run change anything?" across kinds, so runs --changes and the TUI's f fold — both keyed on status == success && file_count == 0 — could only collapse peer-sync no-ops. In the reference household's steady state most rows are per-destination bucket pushes, so the bulk of the noise stayed on screen.

Schema (v28)

ALTER TABLE runs ADD COLUMN changed_count INTEGER CHECK (changed_count IS NULL OR changed_count >= 0).

Nullable with no default, so every pre-migration row reads back as NULL — "unknown", not a fabricated zero. Purely additive: no existing column changes meaning and no run row is rewritten, so the audit trail stays intact. No index (low cardinality, never a selective predicate). store/schema.sql regenerated.

The issue predates #183, which took v27 for the STRICT bulk conversion, so this lands as v28 against the now-STRICT runs table — the ALTER is unaffected either way.

Population

Each driver records what it already knows:

driver changed_count
rclone bucket push / restore rclone's Transferred (the other half of the already_correct split — F7 and the fold now agree on one number)
content-addressed / packed push the manifest delta since the watermark
peer sync, initiator receiver-verified paths
peer sync, receiver rows committed by /close
index / audit walk added + modified + missing
offload files offloaded
durability pull durability rows applied
remote verify fingerprints newly recorded (the initial capture pass stays visible; cadence re-confirmations fold)
kopia push unset — a snapshot summary reports the whole tree and no changed count

store.FinishRun keeps its signature and now means "no changed count to state" (a runs fail reap, a preflight refusal, a driver whose tool reports no such number); FinishRunChanged is the entry point for drivers that know.

Filters

store.Run.NoOp is the single rule behind both surfaces — the recorded count where there is one, today's conservative file_count == 0 heuristic where there isn't — so the CLI and the TUI hide exactly the same rows, and pre-v28 history keeps its current rendering rather than folding on a guess. tui.runIsNoOp is gone; cmd/squirrel's runIsInteresting delegates.

Tests

  • store: the column round-trips, unknown stays NULL, the CHECK rejects a negative count, NoOp truth table across recorded and fallback rows.
  • store/migrate_v28_test.go: a pre-v28 fixture migrates with both run shapes reading back unknown and rendering exactly as before; a fresh run on the migrated DB records the real count.
  • index: three walks (add → unchanged → edit + delete) record [2, 0, 2], with the no-op walk's file_count still 2 — the case file_count alone could not call.
  • sync: the terminal-state writers, driven without an rclone binary, split an in-sync push from one that transferred, and pin kopia's honest NULL.
  • cmd/squirrel / tui: a bucket no-op collapses and a bucket run that moved content stays visible, on both surfaces.

go vet ./... and go test ./... pass. golangci-lint could not run in this environment (its Go build is older than the module's target version), so CI is the check for it.

design/friction-log.md F19 amended to record the fix and what #179 left open.

Closes #182


Generated by Claude Code

runs.file_count means different things per kind. A peer sync records the
receiver-verified — i.e. transferred — count, so its no-ops already read
as zero; a bucket push records transferred plus already-correct, and an
index run records added plus modified plus unchanged, so their no-ops
read as the whole volume. No column answered "did this run change
anything?" across kinds, which left `runs --changes` and the TUI's `f`
fold able to collapse only peer-sync no-ops — and in the reference
household's steady state most rows are per-destination bucket pushes.

v28 adds runs.changed_count: nullable with no default, so pre-migration
history reads back as "unknown" rather than a fabricated zero. Purely
additive — no existing column changes meaning and no run row is
rewritten.

Each driver populates it from what it already knows: rclone's transferred
count for bucket pushes and restores, the manifest delta for the
content-addressed and packed layouts, the receiver-verified paths for a
peer sync (both sides), added + modified + missing for an index walk, the
offloaded count for an offload, the applied rows for a durability pull,
and the freshly recorded fingerprints for a remote-verify pass. kopia
reports only its total file count, so it stays honestly unknown.

store.Run.NoOp is now the one rule behind both `runs --changes` and the
TUI fold — the recorded count where there is one, today's conservative
file_count heuristic where there isn't — so the CLI and the TUI hide
exactly the same rows.

Closes #182

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QexD2njN4TGvyUSDdgi6qG
Copilot AI review requested due to automatic review settings July 25, 2026 12:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@mbertschler
mbertschler merged commit e5c00c1 into main Jul 25, 2026
3 checks passed
@mbertschler
mbertschler deleted the claude/implement-182-hgd5bc branch July 25, 2026 12:39
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: record a real changed-file count so --changes and the TUI fold cover bucket and index runs

3 participants