fix(ui): make the backing-up bar a true determinate progress bar - #155
Merged
Merged
Conversation
The orchestrator transitions to Executing { progress: ExecProgress::zero() }
exactly once per source and then streams the moving counters as separate
OrchestratorEvent::Progress ticks. The app-shell event bridge classified those
ticks as BridgeAction::Ignore ("not bridged to the webview in M5"), so the
webview only ever saw the zeroed snapshot embedded in the state: the progress
store's percent stayed null and the top-of-app bar ran the indeterminate teal
sweep for the entire upload.
Bridge the ticks on the SPEC s11.7 `sync:source_progress` channel (already
reserved in events.rs) as { account_id, source_id, progress }, and fold them
into the progress store over the state's embedded snapshot.
Edge cases: a tick is only stored/summed while its account is currently
executing (a late final snapshot cannot resurrect a finished bar); any state
change for an account drops its tick, so a new run never inherits the previous
run's 100%; an aggregate payload clears every tick; multi-account runs still sum.
Contributor
Coverage
Gate: passed - no coverage regression (epsilon 0.1 pp). |
pmaxhogan
added a commit
that referenced
this pull request
Jul 25, 2026
🤖 I have created a release *beep* *boop* --- ## [2.3.0](v2.2.0...v2.3.0) (2026-07-25) ### Features * **core:** record a backup_done activity row when a run completes ([#160](#160)) ([90cde5c](90cde5c)) * **ui:** files-uploaded stat card with sparkline and smoother Activity load-in ([#157](#157)) ([2d85c99](2d85c99)) * **ui:** live streaming folder-tree preview for the exclusion editor ([#158](#158)) ([47ebe14](47ebe14)) ### Bug Fixes * **telemetry:** count bundled uploads in the anonymous aggregate ([#159](#159)) ([11af7ea](11af7ea)) * **ui:** label bundle_upload and hook activity event types ([#154](#154)) ([2998c76](2998c76)) * **ui:** make the backing-up bar a true determinate progress bar ([#155](#155)) ([eed80a6](eed80a6)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
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.
Root cause
The top-of-app "Backing up" bar is meant to be determinate during upload, but in production it ran the indeterminate teal sweep for the entire run.
GlobalProgressBar.vuerenders determinate when the progress store'spercentis non-null, andpercentderives from theexecutingOrchestratorState'sExecProgresstotals. But the orchestrator transitions toExecuting { progress: ExecProgress::zero() }exactly once per source (crates/driven-core/src/orchestrator.rs:1521) and then streams the moving counters as a separateOrchestratorEvent::Progress { source_id, progress }.Those ticks never reached the webview:
classify_bridge_eventinsrc-tauri/src/assembly.rsmapped them toBridgeAction::Ignore, commented "not bridged to the webview in M5 (the progress DTO lands with a later milestone)". That milestone never landed. So the webview only ever saw the zeroed snapshot,percentstayed null, and the bar could never leave the indeterminate branch.Worth noting: the executor calls
on_progresswith the real plan totals (files_total,bytes_total) on its very first call, before any op completes (crates/driven-core/src/executor.rs:3922) - so a single bridged tick is enough to make the bar determinate.The fix
Bridge the ticks and fold them into the progress store.
Channel. Rather than invent a new event, this uses the SPEC s11.7 channel
sync:source_progress, whose constant was already reserved (#[allow(dead_code)]) insrc-tauri/src/events.rsand whose typed listener stub already sat unused inui/src/ipc/events.ts.Wire shape:
{ account_id, source_id, progress }, snake_case - matching the rest of the M5 sync DTOs (AccountSyncStatus,account:needs_reauth) andExecProgressitself, so the store parses ticks with the same reader it uses for theexecutingstate's embedded snapshot. Theaccount_idis added by the bridge, which knows it; the core event carries only the source.That pre-existing UI stub is also corrected here: it declared
sourceIdin camelCase and typedprogressasunknown, so it would have silently mismatched the real payload.Edge cases covered
executing, checked both when storing and when summing. The executor's final snapshot can trail the state transition; it is dropped. A tick for an account with no known status is dropped too.executingtransition, which is the head of a new source's execution and carrieszero(). Without this the bar would open at the prior run's 100% before falling back. An aggregate payload (whathydrate()returns) clears every tick.Also
The label now names the file counts once the ticks carry a real total: "Backing up - 50% (1,234 of 3,000 files)", via a new
progress.backingUpPercentFileskey using the existing locale-awareIntl.NumberFormat. It falls back to the bare percent for delete-only plans, which upload no files. Aria attributes are unchanged in structure and now carry the richer label.Tests
assembly.rs: new unit tests for theSourceProgressclassification arm (the tick is forwarded unchanged) and for the payload's snake_case serialization with the account id.progress-store.test.ts: 14 new tests covering the fold-in and all three edge-case families above, plus the two-listener subscribe/teardown and a partial-registration-failure cleanup.global-progress-bar.test.ts: the indeterminate-to-determinate transition on the first live tick, and the new file-count label.Two existing assertions were updated because the store now registers two listeners rather than one (the app-shell boot count, and the store's idempotency/teardown counts).
🤖 Generated with Claude Code