Ubisam-MobileGit/tasks/022-tolerate-branch-timestamp-gaps.md
choiminyeong b46dd3a72b Complete TASK-022: tolerate incomplete Gitea branch timestamps
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>
2026-08-26 22:19:39 +09:00

34 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
- [x] Missing or malformed branch timestamps map to an unknown timestamp without failing the branch page.
- [x] The latest-branch indicator selects the newest branch among entries with valid timestamps.
- [x] Reference browsing still displays branches whose timestamps are unknown.
- [x] Required branch names and commit IDs remain fail-closed.
- [x] 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.commitAtEpochMillis` changed from `Long` to `Long?`.
- `data/repositories/GiteaRepositoryBrowserApi.kt`: `listBranches`'s mapper now uses a new `String?.toEpochMillisOrNull()` (parses if present and well-formed, else `null`) instead of `.required().toEpochMillisOrInvalid()` for the timestamp only — `name`/`commit.id` still go through `.required()` and remain fail-closed.
- `feature/repositories/RepositoryListViewModel.kt`'s `launchRowBranchFetch`: the latest-branch picker now filters to branches with a non-null timestamp before taking `maxByOrNull`, 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 reports `LastPushedBranchUiState.Failed` (distinct from `Empty`, which stays reserved for a genuinely branch-less repository) rather than silently showing nothing.
- `feature/repositories/RepositoryReferencePresenter.kt` needed no code change — it never read `commitAtEpochMillis` — but a test was added (`RepositoryReferencePresenterTest`) to make that guarantee explicit rather than implicit.
- Tests added: `GiteaRepositoryBrowserApiTest` (missing/malformed timestamp maps to `null`, 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 reports `Failed` not `Empty`) — 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.