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.2 KiB
TASK-014: Browse organizations and filter repositories by organization
Status: implemented; all required checks pass; no review pending (Codex retired, 2026-09-29) Owner: Claude (human product owner directed Claude to own all subsequent tasks, 2026-08-26) Reviewer: Codex
Outcome
A signed-in user can see the organizations their account belongs to and filter the repository list by one organization while still being able to return to "all repositories." This closes MVP outcome #2 in docs/product.md ("View the signed-in user and organizations").
Scope
- Included:
- Read-only
GET /user/orgsandGET /orgs/{org}/repos, added toGiteaRepositoryBrowserApias a third small interface, reusing its existing repository-mapping code. - An organization filter row on the repository list (ALL mode only — favorites are not org-scoped in Gitea), defaulting to "전체" (all), with the existing loading/empty/offline/auth-expired/server-error states applying to the filtered list the same way they already do for the unfiltered one.
- Read-only
- Excluded:
- Any organization administration or membership write (create/join/leave/manage team).
- Filtering the Favorites tab by organization — starred repos are not org-scoped in Gitea's own model.
Writable paths
app/src/main/java/com/ubisam/ubgitea/domain/organizations/app/src/main/java/com/ubisam/ubgitea/data/repositories/GiteaRepositoryBrowserApi.kt(additive: new interface implementation only)app/src/test/java/com/ubisam/ubgitea/data/repositories/GiteaRepositoryBrowserApiTest.kt(additive)app/src/main/java/com/ubisam/ubgitea/feature/repositories/app/src/test/java/com/ubisam/ubgitea/feature/repositories/app/src/main/res/values/strings_repositories.xmltasks/014-organizations.md
Acceptance criteria
- Organizations list and organization-scoped repository list are both paginated read-only calls; no membership/team-write endpoint is reachable —
GiteaRepositoryBrowserApionly addsget()calls for these two new methods (grep-confirmed, same as every other read-only endpoint in that class). - The repository list can be filtered to one organization and back to "all repositories" without losing the recent-push sort or paging behavior already built (TASK-017) —
listOrganizationRepositoriesrequests the samesort=updated&order=desc, uses the sameREPOSITORY_PAGE_SIZE/page-based navigation, and reusesmapRepositoryso field mapping cannot drift between the two code paths. - Loading, empty, offline, authentication-expired, and server-error states apply to the organization-filtered list the same way they already do for the unfiltered list — the org-scoped call goes through the exact same
loadRepositoryPagetry/catch andRepositoryListUiStatecases as the unfiltered call; there is no separate, parallel state machine for it. - Tests cover organization list mapping, organization-repository mapping (reusing existing repository mapping, not duplicating it), and filter switching — 2 data-layer tests plus 3 ViewModel tests (see Evidence).
Required checks
./gradlew assembleDebug
./gradlew testDebugUnitTest
./gradlew lintDebug
Evidence
Implemented on branch claude/task-014-organizations from commit 7756ec4.
- Domain:
domain/organizations/OrganizationModels.kt(OrganizationSummary(name, displayName)),domain/organizations/OrganizationsRepository.kt. Data:GiteaRepositoryBrowserApigained a third interface implementation (listOrganizations,listOrganizationRepositories), reusing its existingmapRepositoryfor the latter rather than duplicating repository-JSON mapping a third time. - Verified against Gitea's actual
OrganizationGo struct (name/full_namefields) before implementing, not guessed. RepositoryListViewModel:Content/Emptygainedorganization: String?(null = all) andContentgainedorganizations: List<OrganizationSummary>for the filter row. The organizations list is fetched once per session (loadOrganizationsOnce, cached incachedOrganizations, cleared on logout) rather than on every page load — a failure to load it is treated as non-fatal (empty filter row) and does not block showing the repository list itself, since the list is the primary content and the filter is secondary.selectOrganization(String?)only applies in ALL mode (Gitea star is not organization-scoped, so Favorites ignores it, tested explicitly).- UI:
RepositoryListScreengained a horizontally-scrollableFilterChiprow (전체 + one chip per organization) shown only in ALL mode when at least one organization exists, using each organization'sdisplayNamewhen present and falling back toname.
Test coverage: 2 new data-layer tests (organizations map name and full name, organization repositories reuse repository mapping and request recent-push order) plus 3 new ViewModel tests (selecting an organization filters the list and organizations are exposed for the picker, the organizations list is only fetched once and reused across page loads, organization filtering does not apply to the favorites tab).
Checks run from the new worktree (local.properties created locally, not committed):
./gradlew testDebugUnitTest --tests "com.ubisam.ubgitea.feature.repositories.RepositoryListViewModelTest" → BUILD SUCCESSFUL
./gradlew assembleDebug testDebugUnitTest lintDebug --rerun-tasks → BUILD SUCCESSFUL
- 169 unit tests across 28 suites, 0 failures/errors
- 0 lint errors
git diff --check → clean (CRLF notice only)
sensitive-pattern scan of changed files → no matches
Known limitations:
- No physical device was available to verify the filter chip row or an account with real multiple-organization membership interactively.
- Organization membership itself (which orgs the account belongs to) is read once per session and not refreshed mid-session if the user's org membership changes on the server; a manual logout/login or app restart picks up changes. This matches the app's existing no-background-refresh posture elsewhere.
No open P0-P3 findings from self-review. Requesting Codex read-only review.