Continues Codex's paused in-progress fix in this worktree (confirmed safe to pick up by the human product owner). A missing or malformed branch commit timestamp no longer fails the whole branch page; the latest-branch picker now filters to branches with a known timestamp before selecting the max, and reports Failed (not Empty) when none are known. Adds the one missing test case from the in-progress coverage (all branches with unknown timestamps) plus an explicit test proving the reference screen never depended on the field. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
3.6 KiB
TASK-022: Tolerate incomplete Gitea branch timestamps
Status: implemented; all required checks pass; physical-device retest pending Owner: Claude (picked up from Codex's paused in-progress work in the same worktree, with the human product owner's explicit confirmation to continue, 2026-08-26) Reviewer: Codex
Outcome
A repository remains browsable when Gitea returns a valid branch name and commit ID but omits or malforms that branch commit's optional timestamp. One incomplete branch must not invalidate the repository's entire branch list.
Acceptance criteria
- Missing or malformed branch timestamps map to an unknown timestamp without failing the branch page.
- The latest-branch indicator selects the newest branch among entries with valid timestamps.
- Reference browsing still displays branches whose timestamps are unknown.
- Required branch names and commit IDs remain fail-closed.
- Build, unit tests, lint, and secret scan pass.
- Physical-device reproduction pass with this exact fix. The original crash was reproduced and diagnosed by Codex on-device (
CSOT-T8-iPTN-AMA, see Incident evidence); this fix has been verified only by unit test, not by a live retest against that same repository on a device.
Incident evidence
On the authenticated device, CSOT-T8-iPTN-AMA failed both its row-level latest-branch request and its repository reference screen. Both flows share listBranches, whose mapper previously required every commit.timestamp even though timestamp is not necessary to identify or browse a branch.
Evidence (completion)
domain/model/RepositoryModels.kt:BranchSummary.commitAtEpochMillischanged fromLongtoLong?.data/repositories/GiteaRepositoryBrowserApi.kt:listBranches's mapper now uses a newString?.toEpochMillisOrNull()(parses if present and well-formed, elsenull) instead of.required().toEpochMillisOrInvalid()for the timestamp only —name/commit.idstill go through.required()and remain fail-closed.feature/repositories/RepositoryListViewModel.kt'slaunchRowBranchFetch: the latest-branch picker now filters to branches with a non-null timestamp before takingmaxByOrNull, so an unknown-timestamp branch can never hide a real latest branch. When a repository has branches but none have a resolvable timestamp, the row now reportsLastPushedBranchUiState.Failed(distinct fromEmpty, which stays reserved for a genuinely branch-less repository) rather than silently showing nothing.feature/repositories/RepositoryReferencePresenter.ktneeded no code change — it never readcommitAtEpochMillis— but a test was added (RepositoryReferencePresenterTest) to make that guarantee explicit rather than implicit.- Tests added:
GiteaRepositoryBrowserApiTest(missing/malformed timestamp maps tonull, not a thrown failure),RepositoryReferencePresenterTest(a null-timestamp branch still renders and can be selected),RepositoryListViewModelTest×2 (mixed known/unknown timestamps still surface the true latest; all-unknown reportsFailednotEmpty) — the all-unknown case was the one gap in Codex's in-progress test coverage when this was picked up. ./gradlew assembleDebug testDebugUnitTest lintDebug --rerun-tasks: BUILD SUCCESSFUL, all unit tests passing, 0 lint errors.git diff --cached --check: clean. Secret-pattern scan of the staged diff: no matches.- Not verified: a live retest of repository
CSOT-T8-iPTN-AMA(or any repository with a real missing/malformed branch timestamp) on a physical device with this exact fix — left open per this session's established practice of disclosing rather than silently skipping device-dependent verification.