Skip to content

refactor: keep ETA sampling and estimation together - #60

Merged
stromseng merged 1 commit into
maintenance/task-executionfrom
maintenance/progress-estimation
Sep 6, 2026
Merged

stromseng merged 1 commit into
maintenance/task-executionfrom
maintenance/progress-estimation

Conversation

@stromseng

@stromseng stromseng commented Sep 6, 2026 •

Copy link
Copy Markdown
Owner

Problem

ETA depends on two pieces of one algorithm that live in unrelated places: task-state code retains the sample window, while renderer formatting computes recent throughput. Changing retention requires knowing how the formatter interprets the first and last retained samples. The formatter also duplicates checks for whether any work remains.

Solution

Put sample retention and remaining-time estimation in src/progress-estimation.ts:

  • appendProgressSample keeps the existing 30-second rolling window, one observation just before that window when available, and the 1,000-sample cap.
  • estimateRemainingMillis owns eligibility checks and throughput calculation, returning a duration or undefined.
  • The store still appends observations at the atomic mutation boundary.
  • Renderer formatting consumes the estimate and only turns it into display text; the redundant remaining-work check is removed.

The estimator accepts a task snapshot directly. It returns no estimate for completed/failed tasks, unknown totals, no remaining work, or insufficient positive time/progress deltas. These are the same conditions previously enforced by the formatter and its helper together.

Examples

Recent throughput

For a running task with 12 total units and 11 processed, retained observations at (10,000ms, 1) and (40,000ms, 11) represent 10 units over 30 seconds. The remaining unit is estimated at 3,000ms, which the ETA column formats as ETA: 00:03.

A quiet task crossing the window

Retaining one sample immediately before the rolling window still allows a useful delta when old samples age out. The retention comment and the code that interprets those endpoints now live in the same module.

No available estimate

A task with only its initial observation has no measured throughput. estimateRemainingMillis returns undefined; the standalone ETA remains empty and the combined elapsed/ETA display retains its existing fallback. Unknown totals and terminal task states retain their current output behavior.

Validation

  • bun test: 130 passed, 0 failed, 396 assertions.
  • bun run check: typecheck, lint, formatting, and Knip passed.
  • bun run format applied; staged diff whitespace check passed.

Existing store tests cover unchanged processed counts, the sample cap, and retention across the rolling window. Existing rendered ETA tests cover estimates, completed tasks, long durations, and sample-based throughput. Non-failing TSGO schema suggestions remain. No build or dev server was run.

Review notes

This layer follows the type/API refactors. Review the two functions in progress-estimation.ts, then the store import and the smaller formatter. No timing constants, sampling cadence, public schemas, or public exports change, and the new module has no renderer dependency.

Stack

GitHub stack #63: main → #58 (types) → #59 (execution) → #60 (ETA) → #61 (tests) → #62 (examples). Each PR is based on the layer below it, so its diff contains only that concern. Manage the chain with gh stack; no PR has been merged by this change.

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