runs: record a real changed-file count so --changes and the TUI fold cover bucket and index runs#184
Merged
Merged
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Completes the F19 work #179 left half-done.
The gap
runs.file_countmeans 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, soruns --changesand the TUI'sffold — both keyed onstatus == 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.sqlregenerated.The issue predates #183, which took v27 for the STRICT bulk conversion, so this lands as v28 against the now-STRICT
runstable — theALTERis unaffected either way.Population
Each driver records what it already knows:
Transferred(the other half of thealready_correctsplit — F7 and the fold now agree on one number)/closestore.FinishRunkeeps its signature and now means "no changed count to state" (aruns failreap, a preflight refusal, a driver whose tool reports no such number);FinishRunChangedis the entry point for drivers that know.Filters
store.Run.NoOpis the single rule behind both surfaces — the recorded count where there is one, today's conservativefile_count == 0heuristic 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.runIsNoOpis gone;cmd/squirrel'srunIsInterestingdelegates.Tests
store: the column round-trips, unknown stays NULL, the CHECK rejects a negative count,NoOptruth 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'sfile_countstill 2 — the casefile_countalone 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 ./...andgo test ./...pass.golangci-lintcould 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.mdF19 amended to record the fix and what #179 left open.Closes #182
Generated by Claude Code