Paginated commit history for the current ref, commit detail with
distinguished added/modified/deleted/renamed file status, bounded and
explicitly-truncated patch text, and a ref comparison flow validating
one entered ref against the currently open ref as base.
Reuses TASK-004's listCommits and this session's own TASK-009
HistoryRepository; no shared/domain file touched. Entry point is a new
history button on the existing code browser header.
Self-review caught and fixed a bug before writing tests: a failed
commit-detail fetch discarded which commit had failed, so retry
silently reloaded the whole list instead of re-fetching the commit the
user tried to open.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Implemented directly at human product owner's direction, independent
of Codex's separate uncommitted attempt at the same task (not read or
referenced here).
- CommitDetail (reusing CommitSummary) with per-file added/modified/
deleted/renamed/other status; deleted-author and missing-rename-path
cases handled without crashing or inventing data.
- BoundedPatch for commit and comparison diffs, sourced from Gitea's
.diff raw-text endpoints (JSON commit/compare objects only carry
file status, not diff content); byte limit then line limit, both
constructor-injected and validated positive.
- Comparison uses one combined base...head path segment, built by
concatenating both refs before a single addPathSegment call so each
is escaped exactly once; pagination computed locally since Gitea's
compare endpoint has no server-side paging.
- Reuses Page<T> and ApiFailure; no put/delete/postForm call anywhere
in the file (read-only, as required).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Frozen domain/data contracts for issues, pull requests, reviews, and
comments so TASK-008's UI can be built without touching shared layers.
- Separate immutable IssueSummary/Detail and PullRequestSummary/Detail
models; WorkItemAuthor.Unavailable instead of inventing an identity
for deleted/null authors.
- PullRequestState derived from Gitea's merged flag + open/closed
state (no single field already encodes "merged").
- listIssues drops entries with a non-null pull_request field so
Gitea's issue-list endpoint (which also returns PRs) never
duplicates listPullRequests, done client-side rather than trusting
an unverified query parameter.
- Reuses existing Page<T> and ApiFailure rather than new parallel
types. Read-only: no put/delete/postForm call anywhere in the file.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
TASK-018, human product owner priority-1 request. Adds write:user
OAuth scope (ADR-004) and relaxes OAuthClientConfig's read-only guard
to an explicit, hard-coded write-scope allow-list rather than a
general write-scope allowance. GiteaRepositoryBrowserApi gains star/
unstar/listStarred via a new RepositoryFavorites interface, reusing
its existing repository mapping. RepositoryListViewModel tracks an
ALL/FAVORITES mode; toggling favorite confirms against the server
before updating UI state rather than acting optimistically.
No create/edit/merge/close/comment endpoint is introduced. Favorite
state is never cached locally.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
TASK-017, human product owner priority-1 request. Repository list now
requests sort=updated&order=desc from Gitea, loads 5 repositories per
page with explicit prev/next navigation, and each visible row
independently fetches and shows its most recently committed branch.
Removes the legacy eager-loading RepositoryListApi in favor of the
existing paged GiteaRepositoryBrowser.listRepositories. Fixes two
issues found in self-review: missing error handling on page
navigation, and repositories with no default branch (empty repos)
previously failing the whole page instead of just that row.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Independent read-only review of 8a47d30/bbbff77/16c46a5 per
handoff/claude-master-handoff.md's review-and-freeze step. Verified
build/test/lint independently rather than trusting recorded evidence.
Fixed P2: requireSuccess() collapsed HTTP 409/422 into the generic
InvalidResponse, which would have blocked TASK-007's planned
conflict/unprocessable error states. Added ApiFailure.Conflict and
ApiFailure.Unprocessable with test coverage.
Recorded a P3 (CPU-bound work on the Main dispatcher in
CodeBrowserViewModel) for TASK-016 rather than fixing it now. Closed
TASK-005/006's mutual contract-freeze checkboxes. No P0/P1 found.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Pure-Kotlin classifier for feature/codebrowser/content/ that turns
already-loaded SourceContent bytes into bounded text, binary, Git LFS
pointer, unsupported-encoding, or oversized results with caller-supplied
positive byte/character/line limits, per handoff/task-006a-ready.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>