Progress reaches the window in batches (BatchedRepoProgress: a few times a second, the latest report per repository), so a sync of any size leaves the window responsive.
A status row above the workspace is there for the whole Sync all pass (SyncAllStatus): which account, which of how many, how far along, a link to that account, Cancel; afterwards it holds the result. It replaces the dialog at the end.
The activity log says when the window did not respond, for how long, and what was running (ResponsivenessMonitor).
The table's default order follows the sync: in progress, failed, finished (latest first), waiting; when the sync is over, failed first, then by name. A column the user chose still wins; a third click on it returns to the default. The row of chips above the table is gone.
Layout: the result bar is one row with one action; Open folder is also a toolbar button; fewer rows are built ahead of the viewport; rows take their new place without animation; two small alignments.
Reported by the owner on a 1.1.1-dev build: nothing said that Sync all was running, the window stopped responding when a sync started, and the first status bar did not look finished.
Measured with a probe build (local bare repositories, the real git client, a watchdog on the window's thread), 281 clones with the syncing account on screen:
Before
After
Progress reports handled by the window
28,662, taking 27.5 s of its thread
about 1,300, taking 2.7 s
Stalls over 200 ms, rows at rest
23, 11.6 s in total, longest 1.1 s
none
Stalls over 200 ms, rows moving (the new order)
n/a
11, 2.9 s in total, longest 0.33 s
Showing an account with 281 repositories
1.0 to 1.3 s
0.6 to 0.8 s
The remaining stalls come from building table rows (about 20 ms each in WinUI.TableView 1.5.0) when several rows change place in one batch; the probe is a worst case, eight identical clones finishing at the same instant.
How it was tested
New: BatchedRepoProgressTests, ResponsivenessMonitorTests (a manual clock and timer, no waits), status and failure-count tests in SyncAllCoordinatorTests, default-order and batched-delivery tests in WorkspaceViewModelTests.
Gates: zero-warning x64 build, 1,034 engine and view-model tests, 212 CLI tests, 100% line coverage on the three gated assemblies, format clean.
Looked at captures of the probe window during and after a pass for layout.
Not run yet: the UI suite. It drives the desktop; it will be run before merge.
Checklist
Commits are signed and signed off (git commit -S -s)
Zero build warnings; format and lint are clean
Tests added or updated; the 100% coverage gate still passes
Docs updated for user-visible changes; threat model updated if the attack surface changed (README updated; no change to the attack surface)
No secrets, credentials, or personal data
New dependencies are justified below, or none were added (none)
Substantial AI assistance, if any, is noted here (written with Claude Code)
## What
- Progress reaches the window in batches (`BatchedRepoProgress`: a few times a second, the latest report per repository), so a sync of any size leaves the window responsive.
- A status row above the workspace is there for the whole Sync all pass (`SyncAllStatus`): which account, which of how many, how far along, a link to that account, Cancel; afterwards it holds the result. It replaces the dialog at the end.
- The activity log says when the window did not respond, for how long, and what was running (`ResponsivenessMonitor`).
- The table's default order follows the sync: in progress, failed, finished (latest first), waiting; when the sync is over, failed first, then by name. A column the user chose still wins; a third click on it returns to the default. The row of chips above the table is gone.
- Layout: the result bar is one row with one action; Open folder is also a toolbar button; fewer rows are built ahead of the viewport; rows take their new place without animation; two small alignments.
## Why
Closes #167
Closes #168
Reported by the owner on a 1.1.1-dev build: nothing said that Sync all was running, the window stopped responding when a sync started, and the first status bar did not look finished.
Measured with a probe build (local bare repositories, the real git client, a watchdog on the window's thread), 281 clones with the syncing account on screen:
| | Before | After |
|---|---|---|
| Progress reports handled by the window | 28,662, taking 27.5 s of its thread | about 1,300, taking 2.7 s |
| Stalls over 200 ms, rows at rest | 23, 11.6 s in total, longest 1.1 s | none |
| Stalls over 200 ms, rows moving (the new order) | n/a | 11, 2.9 s in total, longest 0.33 s |
| Showing an account with 281 repositories | 1.0 to 1.3 s | 0.6 to 0.8 s |
The remaining stalls come from building table rows (about 20 ms each in WinUI.TableView 1.5.0) when several rows change place in one batch; the probe is a worst case, eight identical clones finishing at the same instant.
## How it was tested
- New: `BatchedRepoProgressTests`, `ResponsivenessMonitorTests` (a manual clock and timer, no waits), status and failure-count tests in `SyncAllCoordinatorTests`, default-order and batched-delivery tests in `WorkspaceViewModelTests`.
- Gates: zero-warning x64 build, 1,034 engine and view-model tests, 212 CLI tests, 100% line coverage on the three gated assemblies, format clean.
- Looked at captures of the probe window during and after a pass for layout.
- **Not run yet: the UI suite.** It drives the desktop; it will be run before merge.
## Checklist
- [x] Commits are signed and signed off (`git commit -S -s`)
- [x] Zero build warnings; format and lint are clean
- [x] Tests added or updated; the 100% coverage gate still passes
- [x] Docs updated for user-visible changes; threat model updated if the attack surface changed (README updated; no change to the attack surface)
- [x] No secrets, credentials, or personal data
- [x] New dependencies are justified below, or none were added (none)
- [x] Substantial AI assistance, if any, is noted here (written with Claude Code)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Progress reports were delivered to the window one by one. A clone reports
every whole percent, so 281 clones are 28,662 reports; with the rows on
screen they took 27.5 s of the window's thread in a 60 s pass, and the
window stopped answering for up to a second at a time (23 stalls, 11.6 s).
- BatchedRepoProgress delivers reports a few times a second, the latest per
repository, and what still waits when the run ends is delivered before the
outcome is stated. The same pass now has no stall with the rows at rest.
- SyncAllStatus says for the whole pass which account is syncing, which of
how many, and how far along it is, and holds the result afterwards. The
window shows it in one row above the workspace, with a link to the account
and Cancel. It replaces the dialog at the end of the pass.
- ResponsivenessMonitor writes to the activity log when the window did not
respond, for how long, and what was running.
- The table's default order follows the sync: the repositories in progress,
then the failed ones, then the finished ones (the latest first), then the
ones waiting; when the sync is over, the failed ones first, then by name.
A column the user chose still wins, and a third click on it returns to the
default. The row of chips above the table is gone: those rows are in the
table.
- The result bar is one row (Retry failed when something failed, otherwise
Open folder); Open folder is also a toolbar button. Fewer rows are built
ahead of the viewport, and rows take their new place without animation.
Closes#167Closes#168
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
- The result bar's action is disabled, and does nothing, while a retry cannot
run (a path recovery, a load, a sync): Execute alone does not ask.
- TableOrderTests checks the table against the rule written out in the test,
step by step and over random runs; it fails for an order regression that the
earlier tests let through.
- A row from before a reload is not put back into the table.
- A batch hands over every report even when the handler fails for one.
- The monitor names what was running at the time of a stall, and does not take
a process that was not running (sleep, a debugger) for a window that did not
respond.
- The status never shows more done than there is to do at the start of a run.
- The folder button says so when the folder does not exist yet.
- The UI test for the header cycle follows the third click; stale comments;
the live region is announced once the bar is on screen.
Refs #167
Refs #168
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
Independent review: MERGE AFTER FIXES, fixed in ae81e00
Confirmed defects, fixed
The UI test for the header cycle expected the third click to start over; it now expects "not sorted" (the three-state sort is the intended change).
The result bar's "Retry failed" no longer asked whether it could run. It is disabled, and does nothing, while a path recovery, a load, or a sync is running.
README said the folder button works "at any time"; it is hidden while a sync runs. Corrected. A folder that does not exist yet is now said in the status line.
The order tests did not protect the ordering code (five injected defects left the suite green). TableOrderTests checks the table against the rule written out in the test, in explicit scenarios (several rows in flight, moves down, to the middle and to the end, a second run, a canceled row, a run that stops on an error) and over 8 random runs of 400 steps. Three of the reviewer's mutations, re-injected, each fail 9 of its 11 tests.
The weakened no-Reset assertion: kept, with the reason. The table is re-sorted as a whole when a run ends, because the order then changes from latest-first to by-name; that is the requested behaviour (#168).
The status could show "3 of 1" and a fraction above 1 for a moment at the start of a second run: such counts now read as "starting".
Stale comments, the missing peer fallback, the automation id on a panel that has no peer: corrected.
Suspicions taken up
A handler that fails for one report no longer costs the rest of the batch.
The monitor names the activity at the time of a stall, and drops a question that was in flight while the process was not running (sleep, a debugger).
The activity text is refreshed when a workspace is evicted.
The engine-thread order tests wait for the table itself instead of reading it once.
The live region is announced once the bar is on screen, with the detail.
A row whose recovery dialog was open while the table was reloaded is not put back (pre-existing on main as an exception; here it would have been a ghost row).
Attacked and found sound by the reviewer: the ordering invariant (a fuzz of 400 seeds by 250 steps in four variants), the comparison as a total order, the batching under an 8-thread stress, no stale report across runs, no subscription leak, no deadlock in the monitor.
Not changed, recorded
Keyboard focus and screen-reader position on a row are probably lost when the row moves; the pre-existing hole that lets a reload or a sync start while the recovery dialog is open; an old failed row in a deselected state marks its account as failed in every pass. These go with the workspace state work.
The remaining stalls with rows moving (worst case 0.33 s) are the cost of building table rows.
Gates: build, 1,072 engine and 212 CLI tests, 100% line coverage, format, UI suite 13 of 13 (three runs).
## Independent review: MERGE AFTER FIXES, fixed in ae81e00
**Confirmed defects, fixed**
1. The UI test for the header cycle expected the third click to start over; it now expects "not sorted" (the three-state sort is the intended change).
2. The result bar's "Retry failed" no longer asked whether it could run. It is disabled, and does nothing, while a path recovery, a load, or a sync is running.
3. README said the folder button works "at any time"; it is hidden while a sync runs. Corrected. A folder that does not exist yet is now said in the status line.
4. The order tests did not protect the ordering code (five injected defects left the suite green). `TableOrderTests` checks the table against the rule written out in the test, in explicit scenarios (several rows in flight, moves down, to the middle and to the end, a second run, a canceled row, a run that stops on an error) and over 8 random runs of 400 steps. Three of the reviewer's mutations, re-injected, each fail 9 of its 11 tests.
5. The weakened no-Reset assertion: kept, with the reason. The table is re-sorted as a whole when a run ends, because the order then changes from latest-first to by-name; that is the requested behaviour (#168).
6. The status could show "3 of 1" and a fraction above 1 for a moment at the start of a second run: such counts now read as "starting".
7. Stale comments, the missing peer fallback, the automation id on a panel that has no peer: corrected.
**Suspicions taken up**
- A handler that fails for one report no longer costs the rest of the batch.
- The monitor names the activity at the time of a stall, and drops a question that was in flight while the process was not running (sleep, a debugger).
- The activity text is refreshed when a workspace is evicted.
- The engine-thread order tests wait for the table itself instead of reading it once.
- The live region is announced once the bar is on screen, with the detail.
- A row whose recovery dialog was open while the table was reloaded is not put back (pre-existing on main as an exception; here it would have been a ghost row).
**Attacked and found sound by the reviewer:** the ordering invariant (a fuzz of 400 seeds by 250 steps in four variants), the comparison as a total order, the batching under an 8-thread stress, no stale report across runs, no subscription leak, no deadlock in the monitor.
**Not changed, recorded**
- Keyboard focus and screen-reader position on a row are probably lost when the row moves; the pre-existing hole that lets a reload or a sync start while the recovery dialog is open; an old failed row in a deselected state marks its account as failed in every pass. These go with the workspace state work.
- The remaining stalls with rows moving (worst case 0.33 s) are the cost of building table rows.
**Gates:** build, 1,072 engine and 212 CLI tests, 100% line coverage, format, UI suite 13 of 13 (three runs).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What
BatchedRepoProgress: a few times a second, the latest report per repository), so a sync of any size leaves the window responsive.SyncAllStatus): which account, which of how many, how far along, a link to that account, Cancel; afterwards it holds the result. It replaces the dialog at the end.ResponsivenessMonitor).Why
Closes #167
Closes #168
Reported by the owner on a 1.1.1-dev build: nothing said that Sync all was running, the window stopped responding when a sync started, and the first status bar did not look finished.
Measured with a probe build (local bare repositories, the real git client, a watchdog on the window's thread), 281 clones with the syncing account on screen:
The remaining stalls come from building table rows (about 20 ms each in WinUI.TableView 1.5.0) when several rows change place in one batch; the probe is a worst case, eight identical clones finishing at the same instant.
How it was tested
BatchedRepoProgressTests,ResponsivenessMonitorTests(a manual clock and timer, no waits), status and failure-count tests inSyncAllCoordinatorTests, default-order and batched-delivery tests inWorkspaceViewModelTests.Checklist
git commit -S -s)🤖 Generated with Claude Code
6e50684dddtoae81e0097bIndependent review: MERGE AFTER FIXES, fixed in
ae81e00Confirmed defects, fixed
TableOrderTestschecks the table against the rule written out in the test, in explicit scenarios (several rows in flight, moves down, to the middle and to the end, a second run, a canceled row, a run that stops on an error) and over 8 random runs of 400 steps. Three of the reviewer's mutations, re-injected, each fail 9 of its 11 tests.Suspicions taken up
Attacked and found sound by the reviewer: the ordering invariant (a fuzz of 400 seeds by 250 steps in four variants), the comparison as a total order, the batching under an 8-thread stress, no stale report across runs, no subscription leak, no deadlock in the monitor.
Not changed, recorded
Gates: build, 1,072 engine and 212 CLI tests, 100% line coverage, format, UI suite 13 of 13 (three runs).