No task waits on a Codex review; the redesign tasks are confirmed on the product owner's Z Fold7; TASK-016's leftovers are on hold; no biometric reauthentication. Agent: main (opus) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6.0 KiB
TASK-046: Commit history load performance and loading feedback
Status: implemented; all required checks pass; live-confirmed on the human product owner's device Owner: Claude Reviewer: Codex
Outcome
Live-testing TASK-045's redesign surfaced a real, pre-existing bug: opening the 커밋 (commit history)
tab on a real, large company repository (RobotAutomationDevelopment-Dept/LGES-OC5-PtoP-Welder)
consistently failed with "서버에 연결할 수 없습니다" (offline error), even though the device had a
normal network connection throughout. Root-caused, fixed, and confirmed live with the product owner.
Scope
- Included:
GiteaRepositoryBrowserApi.listCommitsnow requestsstat=false;HttpTransport's shared OkHttp client'sreadTimeoutraised 30s -> 60s as a safety margin; a newEstimatedProgressIndicator(percentage progress bar) replaces the plain spinner on History's three loading states. - Excluded: no change to any other API call's stat/file-list behavior (only the commit-list endpoint was touched); no change to commit detail, which legitimately needs per-file data and correctly keeps returning it.
Root cause
Gitea's GET /repos/{owner}/{repo}/commits endpoint computes and returns full per-commit file-change
lists and add/remove line stats by default, regardless of what the client asked for. This particular
repository's history contains one commit (a vendored packages/ folder checked into git) touching
~5.8 million lines. Computing that commit's stats made the entire request -- even a 5-item page --
take 31+ seconds server-side, tripping the client's then-30s read timeout. Live-measured directly
against the real endpoint:
default (stat computed): 31.3s, 678653 bytes
stat=false: 0.4s, 678567 bytes
Note the response size is nearly identical either way -- the cost is server-side compute time, not
payload size. GiteaRepositoryBrowserApi.mapCommit only ever reads sha/commit.message/
commit.author.{name,date} from this response; it never reads files or stats. stat=false is
therefore a pure win: Gitea skips work the app already threw away, with no loss of data the app used.
Design notes
stat=falseis the real fix; the timeout bump and progress bar are defense in depth, not the primary fix -- kept anyway since OkHttp's previous 30s ceiling was already documented (seeHttpTransport.kt's existing comment) as too tight for other genuinely-slow cases against this same home-NAS-hosted Gitea instance (e.g. very large directory listings), and a percentage indicator is a real UX improvement for any request that legitimately takes a while.- The progress bar is a time-based estimate, not a byte-accurate percentage. Confirmed via a live
header check that Gitea serves this endpoint
Transfer-Encoding: chunkedwith noContent-Length, so there is no real total-bytes figure to compute a true percentage from.EstimatedProgressIndicatorramps linearly toward a 94% cap over an assumed ~20s typical duration and holds there until the real response arrives (replacing the whole composable with actual content/error) -- it never visually claims 100% before the data is actually in. This trade-off (an honest estimate, not a precise measurement) was not explicitly re-confirmed with the product owner after implementing, since the underlying request became near-instant oncestat=falsewas added, making the estimate's accuracy largely moot in practice. - Applied to all three of History's loading states (commit list, commit detail, patch), not just the one that was reported, since all three hit the same class of Gitea host and the fix is a same-shape, low-risk composable swap.
Acceptance criteria
listCommitsrequestsstat=false.- Live-measured against the real failing repository: request completes in under a second instead of 30+.
- Loading states show an estimated percentage progress bar instead of a bare spinner.
- No change to what
CommitSummaryexposes or how it's built. - Build, unit tests (including a new query-parameter assertion), and lint pass.
- Live-confirmed on the human product owner's device, against the exact repository that originally failed.
Required checks
./gradlew compileDebugKotlin compileDebugUnitTestKotlin testDebugUnitTest assembleDebug lintDebug
Evidence
data/repositories/GiteaRepositoryBrowserApi.kt:listCommitsadds.addQueryParameter("stat", "false").core/network/HttpTransport.kt:readTimeout30s -> 60s, comment extended with this task's measured numbers.feature/history/HistoryScreen.kt: newEstimatedProgressIndicator+history_loading_progressstring, replacingCircularProgressIndicator()at all three loading call sites (commit list, commit detail, patch).GiteaRepositoryBrowserApiTest.kt: existinglistCommitstest extended to asserturl.queryParameter("stat") == "false".- Direct
curlmeasurement against the live server (see Root cause above): 31.3s -> 0.4s, near-identical response size either way. ./gradlew compileDebugKotlin compileDebugUnitTestKotlin testDebugUnitTest assembleDebug lintDebug: BUILD SUCCESSFUL, all unit tests passing, 0 lint errors.- Live device: installed over wireless
adb. Human product owner navigated to the previously-failing repository's commit tab themselves and confirmed it now works well ("내가 확인했어").
Known limitations
- The progress percentage is a time-based estimate (see Design notes), not derived from real bytes
transferred -- inherent to Gitea serving this endpoint as chunked with no
Content-Length. - Only
listCommits(the list endpoint) was changed. If a future repository has a similarly huge commit and a user opens that commit's detail view, the detail fetch still legitimately needs and requests full file/stat data, and could be slow for the same underlying reason -- not fixable the same way, since that view's entire purpose is showing the file changes.
Review findings
None yet -- no Codex review pending (Codex retired, 2026-09-29).