Skip to content

Fix silent hang after pagination — concurrency cap + progress bar for line-number resolution#100

Merged
shouze merged 5 commits intomainfrom
fix/line-resolution-hang
Mar 9, 2026
Merged

Fix silent hang after pagination — concurrency cap + progress bar for line-number resolution#100
shouze merged 5 commits intomainfrom
fix/line-resolution-hang

Conversation

@shouze
Copy link
Contributor

@shouze shouze commented Mar 9, 2026

Closes #99

Root cause

After the pagination bar completes, fetchAllResults fetches the raw content of every unique matched file from raw.githubusercontent.com to compute absolute line numbers. Two issues caused a silent hang of up to 2 minutes on slow/mobile networks:

  1. Promise.all with no concurrency cap — 200+ HTTP requests fired simultaneously, saturating the connection pool.
  2. No progress feedback — the user saw nothing between the end of the pagination bar and the TUI.

Changes

src/api-utils.ts

  • Add concurrentMap<T, R>() — semaphore-based helper, at most N tasks running in parallel (no new dependencies). Results returned in input order.

src/api.ts

  • Import concurrentMap.
  • Add buildLineResolutionProgress(done, total) — pure function, same style as buildFetchProgress, exported for unit testing.
  • Refactor the resolution phase in fetchAllResults:
    • Replace Promise.all with concurrentMap(..., { concurrency: 20 }).
    • Add AbortSignal.timeout(5_000) per request; silent fallback to fragment-relative line numbers on timeout or error.
    • Write Resolving line numbers… ▓▓▓░░░ 12/47 progress on stderr, updated after each file.
    • Erase the line before the TUI starts.

Tests

  • src/api-utils.test.ts — 6 new tests for concurrentMap (order, index, concurrency cap, error propagation, empty input).
  • src/api.test.ts — 6 new tests for buildLineResolutionProgress.

Verification

bun test               # 537 pass, 0 fail
bun run lint           # 0 errors
bun run format:check   # no diff
bun run knip           # no unused exports
bun run build.ts       # binary compiles

Manual: run a query returning many results on tethering — the Resolving line numbers… bar should appear immediately after pagination, TUI opens within seconds.

… for line-number resolution

- Replace Promise.all with concurrentMap (concurrency capped at 20) to avoid
  saturating the connection pool on slow/mobile networks
- Add AbortSignal.timeout(5 s) per raw file fetch with silent fallback to
  fragment-relative line numbers on timeout or network error
- Add buildLineResolutionProgress() progress bar shown on stderr during the
  resolution phase; line is erased before TUI starts
- Add concurrentMap() helper in api-utils.ts (semaphore, no extra deps)
- Add unit tests for buildLineResolutionProgress and concurrentMap

Closes #99
Copilot AI review requested due to automatic review settings March 9, 2026 00:20
@github-actions
Copy link

github-actions bot commented Mar 9, 2026

Coverage after merging fix/line-resolution-hang into main will be

96.65%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src
   aggregate.ts100%100%100%100%
   api-utils.ts98.15%100%100%97.89%52, 60
   api.ts94.24%100%100%93.49%293–297, 353, 370, 63–69
   cache.ts98.08%100%100%97.87%28
   completions.ts99.35%100%100%99.29%252
   group.ts100%100%100%100%
   output.ts99.12%100%94.74%99.52%58
   render.ts94.05%100%88.89%94.33%153, 177–182, 184–186, 188–189, 216, 380–381, 424–427
   upgrade.ts88.46%100%94.44%87.89%127–128, 148–155, 158–164, 169, 174, 210–213
src/render
   filter-match.ts97.44%100%92.31%100%
   filter.ts100%100%100%100%
   highlight.ts96.62%100%90.40%99.31%284–285
   rows.ts97.87%100%100%97.73%54–55
   selection.ts100%100%100%100%
   summary.ts100%100%100%100%

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Addresses a post-pagination “silent hang” in the CLI by bounding the number of concurrent raw-content fetches used to resolve absolute line numbers, and by adding a stderr progress indicator for that phase.

Changes:

  • Added concurrentMap() utility to run async work with a concurrency cap while preserving input order.
  • Refactored fetchAllResults line-number resolution to use concurrentMap (concurrency 20) plus a per-request timeout and progress updates.
  • Added unit tests for concurrentMap and the new buildLineResolutionProgress progress-line formatter.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
src/api.ts Uses bounded concurrency for raw fetches and prints/clears a resolution progress line; adds buildLineResolutionProgress for testing.
src/api.test.ts Adds tests for the new line-resolution progress string builder.
src/api-utils.ts Introduces concurrentMap helper (semaphore/worker pattern) for capped parallelism.
src/api-utils.test.ts Adds tests covering ordering, indices, concurrency cap, and error behavior for concurrentMap.

- Fix concurrentMap docstring: clarify that all items are processed even
  after an error; first error is re-thrown after all work has completed
- Add concurrency input validation: throw RangeError when concurrency is
  <= 0 or NaN, with a descriptive error message
- Add 3 unit tests covering the new validation (0, negative, NaN)
- Replace magic-number space padding with ANSI erase-entire-line sequence
  (\x1b[2K\r) to clear the resolution progress line reliably

See: #100
@shouze shouze requested a review from Copilot March 9, 2026 00:25
@github-actions
Copy link

github-actions bot commented Mar 9, 2026

Coverage after merging fix/line-resolution-hang into main will be

96.66%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src
   aggregate.ts100%100%100%100%
   api-utils.ts98.23%100%100%98%52, 60
   api.ts94.24%100%100%93.49%293–297, 353, 370, 63–69
   cache.ts98.08%100%100%97.87%28
   completions.ts99.35%100%100%99.29%252
   group.ts100%100%100%100%
   output.ts99.12%100%94.74%99.52%58
   render.ts94.05%100%88.89%94.33%153, 177–182, 184–186, 188–189, 216, 380–381, 424–427
   upgrade.ts88.46%100%94.44%87.89%127–128, 148–155, 158–164, 169, 174, 210–213
src/render
   filter-match.ts97.44%100%92.31%100%
   filter.ts100%100%100%100%
   highlight.ts96.62%100%90.40%99.31%284–285
   rows.ts97.87%100%100%97.73%54–55
   selection.ts100%100%100%100%
   summary.ts100%100%100%100%

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

- Replace Number.isFinite with Number.isInteger in concurrentMap validation:
  floats like 1.5 now correctly throw RangeError (isInteger implies finite,
  covers NaN/Infinity/floats/negatives in a single check)
- Add test: concurrency=1.5 throws RangeError
- Pass maxRetries=0 to fetchWithRetry for raw file fetches so AbortSignal.timeout
  acts as a strict hard cap; retry delays in fetchWithRetry are not abort-aware
  and would otherwise let a timed-out request silently retry for up to 30 s

See: #100
@shouze shouze requested a review from Copilot March 9, 2026 00:32
@github-actions
Copy link

github-actions bot commented Mar 9, 2026

Coverage after merging fix/line-resolution-hang into main will be

96.66%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src
   aggregate.ts100%100%100%100%
   api-utils.ts98.23%100%100%98%52, 60
   api.ts94.26%100%100%93.52%296–300, 356, 373, 63–69
   cache.ts98.08%100%100%97.87%28
   completions.ts99.35%100%100%99.29%252
   group.ts100%100%100%100%
   output.ts99.12%100%94.74%99.52%58
   render.ts94.05%100%88.89%94.33%153, 177–182, 184–186, 188–189, 216, 380–381, 424–427
   upgrade.ts88.46%100%94.44%87.89%127–128, 148–155, 158–164, 169, 174, 210–213
src/render
   filter-match.ts97.44%100%92.31%100%
   filter.ts100%100%100%100%
   highlight.ts96.62%100%90.40%99.31%284–285
   rows.ts97.87%100%100%97.73%54–55
   selection.ts100%100%100%100%
   summary.ts100%100%100%100%

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

- Extract buildProgressBar() private helper in api.ts to deduplicate the
  ANSI colour + block-character logic shared between buildFetchProgress and
  buildLineResolutionProgress — styling changes now only need one edit
- Fix concurrency-cap test: use originalSetTimeout (real async delay) instead
  of the immediate-call setTimeout shim installed by beforeEach, so worker
  tasks genuinely interleave and the maxActive assertion is meaningful

See: #100
@shouze shouze requested a review from Copilot March 9, 2026 00:39
@github-actions
Copy link

github-actions bot commented Mar 9, 2026

Coverage after merging fix/line-resolution-hang into main will be

96.66%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src
   aggregate.ts100%100%100%100%
   api-utils.ts98.23%100%100%98%52, 60
   api.ts94.29%100%100%93.52%302–306, 362, 379, 63–69
   cache.ts98.08%100%100%97.87%28
   completions.ts99.35%100%100%99.29%252
   group.ts100%100%100%100%
   output.ts99.12%100%94.74%99.52%58
   render.ts94.05%100%88.89%94.33%153, 177–182, 184–186, 188–189, 216, 380–381, 424–427
   upgrade.ts88.46%100%94.44%87.89%127–128, 148–155, 158–164, 169, 174, 210–213
src/render
   filter-match.ts97.44%100%92.31%100%
   filter.ts100%100%100%100%
   highlight.ts96.62%100%90.40%99.31%284–285
   rows.ts97.87%100%100%97.73%54–55
   selection.ts100%100%100%100%
   summary.ts100%100%100%100%

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

@github-actions
Copy link

github-actions bot commented Mar 9, 2026

Coverage after merging fix/line-resolution-hang into main will be

96.66%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src
   aggregate.ts100%100%100%100%
   api-utils.ts98.23%100%100%98%52, 60
   api.ts94.29%100%100%93.52%307–311, 367, 384, 63–69
   cache.ts98.08%100%100%97.87%28
   completions.ts99.35%100%100%99.29%252
   group.ts100%100%100%100%
   output.ts99.12%100%94.74%99.52%58
   render.ts94.05%100%88.89%94.33%153, 177–182, 184–186, 188–189, 216, 380–381, 424–427
   upgrade.ts88.46%100%94.44%87.89%127–128, 148–155, 158–164, 169, 174, 210–213
src/render
   filter-match.ts97.44%100%92.31%100%
   filter.ts100%100%100%100%
   highlight.ts96.62%100%90.40%99.31%284–285
   rows.ts97.87%100%100%97.73%54–55
   selection.ts100%100%100%100%
   summary.ts100%100%100%100%

@shouze shouze merged commit beaae85 into main Mar 9, 2026
6 checks passed
@shouze shouze deleted the fix/line-resolution-hang branch March 9, 2026 00:49
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.

fix: silent hang after pagination bar — add concurrency limit and progress feedback for line-number resolution

2 participants