commit dd9a70839cc6835a2f10cc0e5789690bdc64a06b Author: choiminyeong Date: Tue Aug 25 21:18:26 2026 +0900 chore: initialize UBGitea engineering harness diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e1c3ffb --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.gradle/ +.idea/ +local.properties +**/build/ +captures/ +.externalNativeBuild/ +.cxx/ +*.jks +*.keystore +keystore.properties +secrets.properties +*.log diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..b127c8f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,126 @@ +# UBGitea Engineering Contract + +This file is the canonical instruction source for every coding agent. If another agent-specific file conflicts with this file, this file wins. + +## Product + +UBGitea is an Android client for the company Gitea instance. + +- Gitea base URL: `https://gitea.ubisampaju.synology.me` +- OAuth client ID: `225589df-b992-4676-aae0-b2534ba7452d` +- OAuth redirect URI: `https://gitea.ubisampaju.synology.me/ubisampaju/oauth/callback` +- Authentication: OAuth 2.0 Authorization Code with PKCE (`S256`) +- Client secret: never required, requested, stored, logged, or committed + +Treat the base URL and client ID as configuration, even while they have production defaults. + +## Source of truth + +Before work, read in this order: + +1. `AGENTS.md` +2. The assigned file under `tasks/` +3. Relevant documents under `docs/` +4. Existing code and tests + +Do not implement a feature without an acceptance-criteria task file. If requirements conflict or a security-sensitive choice is missing, stop and record the question rather than inventing behavior. + +## Agent roles + +### Codex — implementer and integrator + +- Owns repository edits unless the task explicitly transfers ownership. +- Inspects existing code before changing it. +- Implements the smallest coherent change satisfying the task. +- Runs required checks and records evidence. +- Resolves review findings and keeps documentation synchronized. + +### Claude — independent reviewer and design critic + +- Starts read-only unless explicitly assigned implementation ownership. +- Reviews requirements, architecture, security, UX, tests, and the current diff. +- Does not rewrite code while reviewing. +- Reports findings with severity, evidence, reproduction, and acceptance condition. +- May implement only after receiving a task that names its writable file scope. + +### Human product owner + +- Decides product behavior, branding, external infrastructure, and release approval. +- Approves destructive operations and any change to OAuth or production configuration. + +## Single-writer protocol + +- Only one agent may write a file during a task. +- Codex and Claude must not edit the same working tree concurrently. +- A handoff must identify task ID, current commit or diff, writable paths, completed checks, open findings, and next action. +- Never accept a review based only on “looks good.” Use acceptance criteria and test evidence. + +## Required work cycle + +1. Restate the task outcome and risks. +2. Inspect relevant files and current git status. +3. Plan a small change with explicit file ownership. +4. Implement. +5. Run the checks listed in the task. +6. Review the diff against acceptance criteria. +7. Ask the independent reviewer to inspect it. +8. Resolve findings and rerun checks. +9. Update task evidence and hand off. + +## Android engineering rules + +- Kotlin and Jetpack Compose are the default UI stack. +- Prefer unidirectional data flow and immutable UI state. +- Keep Android framework details at the edges of domain and data logic. +- Use coroutines with structured concurrency; do not use global scopes. +- Keep network DTOs separate from domain models. +- Never perform network or disk work on the main thread. +- All user-visible text belongs in string resources. +- Accessibility labels, loading, empty, offline, and error states are required. +- Avoid new dependencies unless the task explains why the standard stack is insufficient. + +## Security invariants + +- Use the system browser for OAuth; do not use WebView for credential entry. +- Generate a fresh PKCE verifier, S256 challenge, and cryptographically random `state` per login attempt. +- Reject missing or mismatched `state` and unexpected redirect URIs. +- Never log authorization codes, access tokens, refresh tokens, cookies, or personal data. +- Store tokens using Android Keystore-backed protection. +- Do not disable TLS validation or add trust-all certificates. +- Do not place secrets in source, resources, Gradle properties, screenshots, fixtures, or CI logs. +- Destructive Gitea operations require explicit UI confirmation and dedicated task approval. + +## Verification gates + +Before a task is complete, run all applicable checks: + +- Gradle build +- Unit tests +- Android lint +- Static formatting/check task when configured +- Focused tests for changed behavior +- Secret scan or repository search for sensitive values +- Manual verification steps named by the task + +If a check cannot run, record the exact reason and do not claim the task is complete. + +## Review severity + +- `P0`: credential exposure, data loss, remote compromise, or release blocker +- `P1`: broken primary flow, authorization flaw, crash, or serious regression +- `P2`: incorrect edge case, maintainability problem, missing important test +- `P3`: minor quality or consistency improvement + +Each finding must include file and line, concrete impact, evidence, and the condition that closes it. + +## Git discipline + +- Preserve unrelated user changes. +- Keep commits focused on one task. +- Do not rewrite history, force-push, delete branches, or discard changes without approval. +- Do not commit generated build output, local SDK paths, signing keys, tokens, or secrets. +- Branch names should use `codex/` for Codex-owned work and `claude/` for Claude-owned work. + +## Definition of done + +A task is done only when its acceptance criteria are met, required checks pass, no unresolved P0/P1 findings remain, documentation reflects material decisions, and a reviewer can reproduce the result from the recorded evidence. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..f30d6a3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,7 @@ +# Claude entry point + +Read `AGENTS.md` first. It is the canonical instruction source. + +Your default role in this repository is independent reviewer and design critic. Stay read-only unless the human product owner or an assigned task explicitly grants you implementation ownership and lists writable paths. + +For a review, use `prompts/claude-review.md` and report findings before summaries. Never request or expose the OAuth client secret. diff --git a/README.md b/README.md new file mode 100644 index 0000000..dd3f87b --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# UBGitea + +Android client for the Ubisam Paju Gitea instance. + +The repository currently contains the engineering harness that governs collaborative development by Codex and Claude. Product code will be bootstrapped through the tasks under `tasks/`. + +## Start here + +1. Read `AGENTS.md`. +2. Read `docs/product.md` and `docs/architecture.md`. +3. Select one task from `tasks/`. +4. Assign one writer and one independent reviewer. +5. Record build and test evidence before marking the task complete. + +OAuth uses Authorization Code with PKCE and does not use a client secret. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..7450a18 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,48 @@ +# Architecture guardrails + +## Proposed stack + +- Kotlin +- Jetpack Compose and Material 3 +- Coroutines and Flow +- Hilt for dependency injection +- Retrofit/OkHttp or Ktor Client, selected once in an ADR +- Room only when offline caching is approved +- AndroidX Browser/AppAuth-compatible system-browser OAuth flow + +## Layers + +```text +UI (Compose) + -> presentation state and events + -> domain use cases and models + -> repository interfaces + -> data implementations + -> Gitea HTTP API / secure local storage +``` + +Dependencies point inward. DTOs, HTTP clients, Android contexts, and persistence entities do not leak into domain models. + +## Initial module strategy + +Begin with one application module and package by feature. Split Gradle modules only when build time or ownership boundaries justify the added complexity. + +```text +app/ + core/auth + core/network + core/designsystem + feature/login + feature/repositories + feature/issues + feature/pullrequests + feature/notifications +``` + +## Configuration + +Production defaults may contain the public Gitea URL and OAuth client ID. Environment-specific values must be injectable for tests. Client secrets and signing material must never be committed. + +## Architecture decisions + +Material choices belong in `docs/decisions/` as short ADRs stating context, decision, alternatives, and consequences. diff --git a/docs/collaboration.md b/docs/collaboration.md new file mode 100644 index 0000000..1ed66d6 --- /dev/null +++ b/docs/collaboration.md @@ -0,0 +1,31 @@ +# Codex and Claude collaboration + +## Operating model + +One agent writes; the other reviews. Roles can rotate between tasks, but ownership cannot be ambiguous within a task. + +## Handoff format + +```markdown +Task: TASK-### +Role completed: implementer | reviewer +Commit/diff: +Writable scope used: +Acceptance criteria status: +Checks run: +Open findings: +Known limitations: +Next owner/action: +``` + +## Conflict handling + +1. Product requirements and human decisions win. +2. `AGENTS.md` wins over agent-specific files. +3. Task acceptance criteria win over implementation preference. +4. Automated evidence wins over an unsupported assertion. +5. Security-sensitive ambiguity is escalated to the human owner. + +## Review independence + +The reviewer must inspect the actual diff and tests. Do not provide the implementer's reasoning as a substitute for evidence; it can anchor the reviewer and hide alternative failure modes. diff --git a/docs/oauth.md b/docs/oauth.md new file mode 100644 index 0000000..854e3af --- /dev/null +++ b/docs/oauth.md @@ -0,0 +1,35 @@ +# OAuth and App Link contract + +## Registered values + +```text +Authorization endpoint: https://gitea.ubisampaju.synology.me/login/oauth/authorize +Token endpoint: https://gitea.ubisampaju.synology.me/login/oauth/access_token +Client ID: 225589df-b992-4676-aae0-b2534ba7452d +Redirect URI: https://gitea.ubisampaju.synology.me/ubisampaju/oauth/callback +Grant: Authorization Code with PKCE +PKCE method: S256 +Client secret: not used +``` + +## Required login flow + +1. Create a random state and PKCE verifier. +2. Derive the S256 challenge. +3. Store only the minimum short-lived transaction state. +4. Open the authorization URL in the system browser. +5. Receive the verified HTTPS App Link. +6. Validate scheme, host, path, state, error, and authorization code. +7. Exchange the code using the original verifier. +8. Store tokens with Keystore-backed protection. +9. Clear transaction state on success, cancellation, or error. + +## External prerequisite + +The host must serve a valid Android Digital Asset Links file at: + +```text +https://gitea.ubisampaju.synology.me/.well-known/assetlinks.json +``` + +It must identify the final Android application ID and signing-certificate SHA-256 fingerprint. OAuth end-to-end testing is blocked until those values and hosting are available. diff --git a/docs/product.md b/docs/product.md new file mode 100644 index 0000000..9102564 --- /dev/null +++ b/docs/product.md @@ -0,0 +1,39 @@ +# Product brief + +## Purpose + +UBGitea gives employees a focused Android experience for the company Gitea instance without attempting to reproduce every web feature. + +## MVP outcomes + +1. Sign in through Gitea OAuth 2.0 with PKCE. +2. View the signed-in user and organizations. +3. Browse accessible repositories, branches, directories, and files. +4. View issues, pull requests, and notifications. +5. Sign out and remove locally stored credentials. + +The first release is read-oriented. Creating, merging, deleting, or administering resources is out of scope until explicitly approved. + +## UX principles + +- Korean-first copy with a structure that permits localization. +- Clear loading, empty, offline, authentication-expired, and server-error states. +- Never obscure which Gitea instance and account are active. +- Require confirmation before any future destructive or irreversible operation. + +## Non-goals for MVP + +- Editing source files +- Git clone/push over SSH +- Repository or account administration +- Gitea Actions runner administration +- Package publishing +- Multiple Gitea instances + +## Open product decisions + +- Final Android application ID +- Minimum supported Android version +- Visual identity, icon, and color palette +- Whether biometric reauthentication is required +- Whether repositories must be cached for offline browsing diff --git a/prompts/claude-review.md b/prompts/claude-review.md new file mode 100644 index 0000000..ec019d2 --- /dev/null +++ b/prompts/claude-review.md @@ -0,0 +1,44 @@ +# Claude kickoff prompt for UBGitea + +You are the independent reviewer and design critic for the UBGitea Android repository. + +Before doing anything, read these files completely in order: + +1. `AGENTS.md` +2. `CLAUDE.md` +3. `docs/product.md` +4. `docs/architecture.md` +5. `docs/oauth.md` +6. `docs/collaboration.md` +7. The assigned task under `tasks/` + +Your default mode is read-only. Do not edit files, install dependencies, change external services, or reveal secrets unless the human product owner explicitly changes your role and provides writable paths. + +For the current task: + +1. Inspect the repository, git status, assigned task, actual diff, and relevant tests. +2. Verify every acceptance criterion with concrete evidence. +3. Independently examine architecture, Android lifecycle behavior, concurrency, accessibility, Gitea API correctness, OAuth 2.0 PKCE, App Link validation, token handling, privacy, and missing tests. +4. Do not accept claims that are unsupported by code or executable checks. +5. Report findings first, ordered by `P0` through `P3`. +6. For each finding include: severity, concise title, exact file and line, impact, evidence or reproduction, and the condition that closes it. +7. If there are no actionable findings, say so explicitly and list residual risks or checks you could not run. +8. Finish with a short handoff using the format in `docs/collaboration.md`. + +Security invariants: + +- Never ask for, store, print, or commit the OAuth client secret. +- UBGitea is a public OAuth client using Authorization Code with PKCE S256. +- Login credentials must be entered only in the system browser. +- Reject mismatched state, unexpected callback host/path, and invalid authorization responses. +- Never disable TLS verification or log authorization codes and tokens. + +Current fixed public configuration: + +```text +Gitea URL: https://gitea.ubisampaju.synology.me +OAuth client ID: 225589df-b992-4676-aae0-b2534ba7452d +Redirect URI: https://gitea.ubisampaju.synology.me/ubisampaju/oauth/callback +``` + +Begin by stating which task and commit/diff you are reviewing and whether any required context is missing. Then perform the review without modifying the repository. diff --git a/tasks/001-bootstrap.md b/tasks/001-bootstrap.md new file mode 100644 index 0000000..b721523 --- /dev/null +++ b/tasks/001-bootstrap.md @@ -0,0 +1,42 @@ +# TASK-001: Bootstrap the Android project + +Status: ready +Owner: Codex +Reviewer: Claude + +## Outcome + +Create an Android Studio project that builds a minimal UBGitea Compose application and establishes deterministic local checks. + +## Scope + +- Included: Gradle wrapper, one Android application module, Compose theme, placeholder home screen, unit-test baseline, lint, CI-ready commands. +- Excluded: OAuth implementation, Gitea API calls, final branding assets. + +## Writable paths + +- Root build and configuration files +- `app/` +- `.github/` if CI is selected +- Documentation updates needed to record chosen versions + +## Acceptance criteria + +- [ ] Android Studio can import the project without manual source edits. +- [ ] Debug APK builds successfully. +- [ ] Unit tests and Android lint run successfully. +- [ ] The launcher displays `UBGitea` and a placeholder state. +- [ ] No secret or client secret is present. +- [ ] Final application ID and minimum SDK are recorded in an ADR. + +## Required checks + +```text +./gradlew assembleDebug +./gradlew testDebugUnitTest +./gradlew lintDebug +``` + +## Evidence + +Pending implementation. diff --git a/tasks/002-oauth-spike.md b/tasks/002-oauth-spike.md new file mode 100644 index 0000000..60d1cee --- /dev/null +++ b/tasks/002-oauth-spike.md @@ -0,0 +1,32 @@ +# TASK-002: Prove OAuth PKCE and App Link end to end + +Status: blocked by TASK-001 and external App Link configuration +Owner: Codex +Reviewer: Claude + +## Outcome + +A user can authorize UBGitea through the system browser, return through the verified App Link, exchange the code using PKCE, and view their Gitea identity. + +## Acceptance criteria + +- [ ] Uses a system browser, not WebView. +- [ ] Uses fresh random state and PKCE S256 per attempt. +- [ ] Rejects mismatched state and malformed callbacks. +- [ ] Does not require or contain a client secret. +- [ ] Does not log codes or tokens. +- [ ] Persists tokens with Keystore-backed protection. +- [ ] Handles success, cancellation, denied authorization, network failure, and expired transaction state. +- [ ] Calls the authenticated user endpoint and displays the user identity. +- [ ] Tests cover URL construction, PKCE, callback validation, and error mapping. + +## External prerequisites + +- Final application ID +- Debug and release signing certificate fingerprints +- Correct `assetlinks.json` hosted on the Gitea domain +- OAuth application configured as a public client + +## Evidence + +Pending prerequisites and implementation. diff --git a/tasks/TEMPLATE.md b/tasks/TEMPLATE.md new file mode 100644 index 0000000..a083cb1 --- /dev/null +++ b/tasks/TEMPLATE.md @@ -0,0 +1,42 @@ +# TASK-###: Title + +Status: proposed +Owner: unassigned +Reviewer: unassigned + +## Outcome + +Describe observable user or engineering outcome. + +## Scope + +- Included: +- Excluded: + +## Writable paths + +- `path/` + +## Acceptance criteria + +- [ ] Observable criterion +- [ ] Error/edge-case criterion +- [ ] Test criterion + +## Security and privacy + +List relevant invariants or write `No additional concerns`. + +## Required checks + +```text +Exact commands are added once the build exists. +``` + +## Evidence + +Record command output summaries, screenshots, and manual verification without secrets. + +## Review findings + +Track finding ID, severity, disposition, and closing evidence.