Skip to content

feat(progress): redesign install progress UI#501

Merged
jdx merged 4 commits intomainfrom
feat/install-progress-rework
May 3, 2026
Merged

feat(progress): redesign install progress UI#501
jdx merged 4 commits intomainfrom
feat/install-progress-rework

Conversation

@jdx
Copy link
Copy Markdown
Contributor

@jdx jdx commented May 3, 2026

Summary

Reworks the install progress display and fixes two real bookkeeping bugs that surfaced during the redesign.

New visual shape (CI / piped stderr):

aube 1.7.0 by en.dev
░░░░░░░░░░░░░░░ 325 pkgs · resolving · ETA …
█████░░░░░░░░░░ 23/142 pkgs · 4.2 MB / ~13.8 MB · 1.4 MB/s · ETA 5s
███████████████ 1230/1230 pkgs · linking
✓ resolved 1230 · reused 98 · downloaded 1132 (54.6 MB) in 6.8s

Fast-mode (<2s, no heartbeat fires):

aube 1.7.0 by en.dev · ✓ installed 5 packages in 423ms
aube 1.7.0 by en.dev · ✓ Already up to date (1 package)

What changed

  • Bar-left, no frame. Dropped the […] brackets, switched #/- to /, and made the bar a fixed 15 chars on the left. Stats live to the right.
  • Dropped [N/3] phase counter (redundant with cur/total pkgs); replaced with the word linking during phase 3 so the phase is still visible.
  • Replaced elapsed-during-install with ETA. Final summary still carries in Xs.
  • Estimated install size via new dist.unpackedSize field, summed across the resolve stream and adjusted by a 0.30 gzip ratio: 4.2 MB / ~13.8 MB.
  • Fast-mode bypass for installs that finish before the first 2s heartbeat — a single self-identifying summary line, no bar, no header. Phase transitions no longer wake the heartbeat early (the previous behavior produced one frame per phase change on every fast install).
  • TTY mode parity — same ETA, estimated-size, and bytes-segment refresh wiring on the clx-driven animated bar.
  • Color in both modes via existing style::e* helpers. GHA color detection at main.rs:724 already routes through.
  • Homepage demo (HomeLanding.vue) updated to match new bar chars.

Bookkeeping fixes

Two related root-cause bugs in the install progress denominator/numerator pipeline:

  • 2/1 packages overflow. Fresh-resolve streaming pass continued on platform-mismatched non-local packages before inc_total(1). The filter_graph step usually dropped those, but the rare survivor (package declares os/cpu without optional) flowed through the catch-up fetch, which credited the numerator via inc_reused / FetchRow::drop without ever bumping the denominator. Fixed by moving inc_total(1) ahead of the deferred-skip.

  • Stuck at 90% undercount. Streamed inc_total ran for every resolved package, including platform-mismatched optionals that filter_graph subsequently dropped. Denominator inflated, numerator could never catch up. Fixed by reconciling set_total(graph.packages.len()) immediately after filter_graph.

Display clamp (completed = (reused + downloaded).min(resolved)) plus a new WARN_AUBE_PROGRESS_OVERFLOW warning code stay in as defensive guards in case a new code path regresses.

Test plan

  • cargo fmt --check
  • cargo clippy --all-targets -- -D warnings
  • cargo test --workspace (1300+ tests; 6 new render unit tests for the new shapes)
  • Manual verification with fixtures/medium, fixtures/vlt-benchmarks/svelte, and benchmarks/fixture.package.json (cold + warm + no-op cases)
  • CI green

🤖 Generated with Claude Code


Note

Medium Risk
Reworks install progress bookkeeping and output across TTY/CI, including new size/ETA calculations and denominator reconciliation; mistakes could regress progress accuracy or log output, but changes are confined to progress rendering and associated resolver metadata.

Overview
Redesigns the install progress UI for both CI and TTY: CI now prints an append-only left-aligned fixed-width bar with phase-aware labels (resolving/fetching/linking), transfer rate, and ETA; TTY gains matching bytes/rate/ETA segments and more consistent summary lines via a shared aube_prefix_line helper.

Adds a best-effort total-size estimate by threading dist.unpackedSize from registry packuments (aube-registry::Dist) through ResolvedPackage::unpacked_size and summing it during streaming resolve; after filter_graph, the estimate and total package denominator are reconciled against the surviving graph to avoid inflated totals.

Fixes progress bookkeeping edge cases by incrementing the total before platform-deferred skips, clamping completed to resolved, and introducing WARN_AUBE_PROGRESS_OVERFLOW (documented in error-code data) to surface any future numerator>denominator mismatches; progress-line rendering is centralized in new progress/render.rs. Also updates the docs homepage demo bar glyphs to match the new UI.

Reviewed by Cursor Bugbot for commit c9dd5e3. Bugbot is set up for automated code reviews on this repo. Configure here.

Bar-left, no frame, █/░ fill, ETA, estimated download size from
unpackedSize, fast-mode bypass for sub-2s installs, and `linking`
phase word in place of dropped `[N/3]` counter. CI mode and TTY mode
share the same label assembly.

Also fixes two real bookkeeping bugs surfaced while reworking the UI:

* "2/1 packages" overflow: fresh-resolve streamed `inc_total` only
  for non-deferred packages, but the post-`filter_graph` catch-up
  fetch credited deferred-but-survived packages to the numerator
  without a matching denominator bump.
* "Stuck at 90%" undercount: streamed `inc_total` for every resolved
  package including platform-mismatched optionals that
  `filter_graph` then dropped, inflating the denominator past the
  surviving graph size.

Both fixed by moving `inc_total(1)` ahead of the deferred-skip and
reconciling with `set_total(graph.packages.len())` after
`filter_graph`. Display clamp + new `WARN_AUBE_PROGRESS_OVERFLOW`
remain as defensive guards.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 3, 2026

Greptile Summary

Redesigns the install progress UI to a bar-left format with shared CI/TTY rendering, adds fetch-window ETA and best-effort size estimation via dist.unpackedSize, and fixes two real bookkeeping bugs: the "2/1 packages" overflow (denominator incremented after deferred-skip) and the "stuck at 90%" undercount (set_total now reconciles against the post-filter_graph graph). All three issues flagged in prior review threads — the empty-bytes-segment double separator, the ETA using total-install elapsed instead of fetch-window elapsed, and estimated_bytes not being reconciled after filter_graph — are addressed in this revision.

Confidence Score: 5/5

Safe to merge — no P0 or P1 issues found; all previously-flagged concerns have been addressed.

All three prior review-thread bugs are fixed (empty bytes segment guard, fetch-window ETA baseline, reconcile_estimated_bytes after filter_graph). The two root-cause bookkeeping bugs (denominator overrun, undercount) are also fixed with defensive clamp + warning retained as a backstop. The new render.rs module is well-tested with six targeted unit tests covering every documented output shape.

No files require special attention.

Important Files Changed

Filename Overview
crates/aube/src/progress/render.rs New shared rendering module; empty-bytes-segment guard, fetch-window ETA, and overflow clamp all implemented correctly. Tests cover the documented shapes.
crates/aube/src/progress/ci.rs CI heartbeat refactored to delegate rendering to shared render module; phase-transition no-wake behavior and fetch-window baseline capture are correct. Summary output and fast-mode path look correct.
crates/aube/src/progress/mod.rs TTY-side refresh helpers (bytes segment, rate, ETA) correctly mirror CI render logic. reconcile_estimated_bytes and inc_estimated_bytes book-keeping is consistent. FetchRow::drop now bumps the downloaded mirror atomically alongside the clx increment.
crates/aube/src/commands/install/mod.rs Both bookkeeping fixes applied: inc_total moved before the deferred-skip (prevents 2/1 overflow) and set_total + reconcile_estimated_bytes called after filter_graph (prevents stuck-at-90% undercount). Fast-mode CI summary path correctly gates on shown.
crates/aube-registry/src/lib.rs Adds optional unpacked_size field to Dist with serde(default) — backward-compatible deserialization.
crates/aube-resolver/src/types.rs Adds unpacked_size: Option<u64> to ResolvedPackage — well-documented, all construction sites updated to None on paths without packument data.
crates/aube-codes/src/warnings.rs Adds WARN_AUBE_PROGRESS_OVERFLOW constant and Progress UI category; docs/error-codes.data.json updated consistently.
docs/.vitepress/theme/HomeLanding.vue Bar chars updated from #/- to █/░ and pkgs suffix added to the count label — purely visual, matches new CLI output.

Reviews (4): Last reviewed commit: "fix(progress): emit overflow warning at ..." | Re-trigger Greptile

Comment thread crates/aube/src/progress/render.rs Outdated
Comment thread crates/aube/src/progress/render.rs
Comment thread crates/aube/src/commands/install/mod.rs
Comment thread crates/aube/src/progress/mod.rs
Comment thread crates/aube/src/progress/render.rs
Comment thread crates/aube/src/progress/mod.rs
Comment thread crates/aube/src/progress/mod.rs Outdated
* Skip empty bytes_segment so the phase-2 label doesn't render a
  doubled `·  ·` separator on installs without `dist.unpackedSize`
  (Greptile P1).
* Switch ETA to fetch-window throughput: snapshot completed at the
  set_phase("fetching") boundary, then divide remaining work by
  `fetch_elapsed_ms / (completed - baseline)`. Falls back to `ETA …`
  while the baseline or fetch window are missing. Eliminates the
  pessimistic "110s ETA after a 3s resolve" case (Greptile P2).
* Reconcile estimated_bytes after filter_graph: track per-dep_path
  unpacked_size during streaming, then re-sum surviving dep_paths
  via the new InstallProgress::reconcile_estimated_bytes API.
  Mirrors the package-denominator reconcile applied next to it
  (Greptile P3).
* Apply TARBALL_COMPRESSION_RATIO in TTY refresh_bytes_segment too —
  CI mode applied it; TTY was showing the raw unpacked sum (~3.3×
  inflated) (Cursor #1).
* Refresh ETA on every inc_downloaded_bytes so it doesn't freeze
  during long downloads when no inc_total / inc_reused events fire.
  Documents the trade-off at FetchRow::drop where a refresh would
  require a larger refactor (Cursor #3).
* Style only the ✓ green on the install / no-op summaries instead
  of the whole sentence; aligns with the CI heartbeat summary's
  styling so the success cue stays sharp without bleeding green
  across the line (Cursor #4 + user request).
* Standardize the ✓ prefix on both Already-up-to-date paths
  (`print_install_summary` and `print_already_up_to_date`).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread crates/aube/src/progress/mod.rs
Comment thread crates/aube/src/commands/install/mod.rs
* `inc_estimated_bytes` now subtracts any prior recorded value
  before adding the new one, so a duplicate dep_path stream
  (defensive case the resolver shouldn't produce but isn't
  prevented from producing) keeps the atomic running total in
  lockstep with the HashMap. Without this the running estimate
  overcounts until `reconcile_estimated_bytes` re-syncs at the
  filter_graph boundary, and the bar shows an inflated `~X MB`
  in the meantime.
* `print_already_up_to_date` now goes through the shared
  `aube_prefix_line` helper instead of reconstructing the
  `aube VERSION by en.dev · <msg>` shape inline. Eliminates the
  drift risk between this site and `print_install_summary`'s
  no-op branch — both render identically by construction.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 4b4b106. Configure here.

Comment thread crates/aube/src/progress/render.rs
* Hoist the `clamped_completed` call out of `bar_only` and
  `label_for` and into a single computation at the top of
  `progress_line`, threading the result through as a parameter.
  Previously the clamp ran twice per render tick — once for the
  bar, once for the label — and so did the diagnostic warning.
* Gate `tracing::warn!(WARN_AUBE_PROGRESS_OVERFLOW)` behind a
  process-wide `AtomicBool::swap` latch so the warning fires at
  most once per CLI run. The bookkeeping condition tends to recur
  across heartbeats once tripped; without this gate the CLI would
  log dozens of identical warnings to stderr, drowning out the
  actual install output. One warning per session is enough to
  flag the regression for follow-up.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jdx jdx merged commit 8e259eb into main May 3, 2026
18 checks passed
@jdx jdx deleted the feat/install-progress-rework branch May 3, 2026 18:35
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.

1 participant