# Repository instructions ## Gitea issue workflow Use this repository's `gotcha` CLI to list, inspect, create, update, comment on, and close Gitea issues. Run it from the repository root so the configured Git remote selects the server and repository: ```sh cargo run -q -p gotcha-cli -- issue list cargo run -q -p gotcha-cli -- issue list --milestones "first feature complete release" cargo run -q -p gotcha-cli -- issue show 7 cargo run -q -p gotcha-cli -- issue comment 7 < comment.txt cargo run -q -p gotcha-cli -- issue close 7 ``` Use `gotcha issue list --help` for state, kind, keyword, label, milestone, author, assignee, mention, date, and pagination filters. Grant network access before invoking commands that contact Gitea in a sandboxed runner. ## Required pre-commit gates Run every gate below from the repository root before committing. Every command must succeed; do not commit code that is unformatted, fails Clippy, or compiles with warnings. ```sh cargo fmt --all -- --check RUSTFLAGS="-D warnings" cargo check --workspace --all-targets cargo clippy --workspace --all-targets -- -D warnings cargo test --workspace ``` Do not weaken or skip these gates to make a commit pass. Fix the underlying warning, lint, formatting issue, or test failure. ## Gitea, app, CLI, and Swift ownership boundary `gotcha_gitea` owns Gitea networking and authentication plus the behavior of Gitea objects: validation, pagination, name-to-ID resolution, mutations, ownership checks, object relationships, activity targets, commit graphs, and diff parsing. The app and CLI must not import generated Gitea API modules or build generated-client configurations. Add a concrete `Client` operation to `gotcha_gitea` instead. Generated response models may cross the boundary when a frontend only needs to present their fields; the CLI's explicit `api request` command is the sole low-level escape hatch. `crates/app` owns application state, persistence, preferences, favorites, content classification, navigation targets, and view-ready UniFFI records. The CLI owns argument and YAML parsing plus terminal formatting. Do not duplicate Gitea object transformations or relationship handling in either frontend. Swift is the native iOS presentation layer. Limit `ios/Sources` to UIKit and SwiftUI lifecycle, view-controller navigation, native controls, layout, drawing, fonts, colors, symbols, accessibility, task cancellation tied to view lifetime, and Apple presentation integrations such as Quick Look. Swift may map Rust-provided states to visual treatments and maintain transient control state; it must not infer domain state from display strings, duplicate Rust transformations, classify content, or implement persistence and API behavior. When changing iOS functionality: - Trace the complete flow before editing and implement Gitea behavior in `gotcha_gitea` and app behavior in `crates/app` first. - Prefer view-ready Rust records over exposing raw Gitea models or rebuilding titles, summaries, selections, progress, and categories in Swift. - Treat a pure Swift helper that does not require an Apple UI framework as a boundary warning; move it to Rust unless it only calculates view geometry. - Regenerate and commit the UniFFI Swift and C bindings whenever the exported Rust interface changes. - During review, inspect both sides of the bridge and reject new business logic added to Swift merely because its caller is a view controller. ## UI verification conventions `TESTING.md` is the source of truth for iOS visual verification and the release regression suite. - Update `TESTING.md` in the same commit whenever UI appearance, behavior, navigation, or interaction changes so its scenarios remain current. - For each ordinary commit, run every `TESTING.md` scenario relevant to the changed UI in addition to the required pre-commit gates. Report which visual scenarios were exercised. - When asked to perform a release test, run the complete `TESTING.md` checklist. Every scenario must succeed before reporting release sign-off; record failures and resolve release blockers instead of skipping them. ## iOS simulator build and deployment This Apple Silicon project builds the simulator app for `arm64`. Do not disable code signing: the Rust build script consumes Xcode's generated simulator entitlement paths. Do not request an `x86_64` simulator build unless the `x86_64-apple-ios` Rust target has explicitly been installed. Use this sequence from the repository root: ```sh cd ios xcodegen generate xcodebuild \ -project Gotcha.xcodeproj \ -scheme Gotcha \ -configuration Debug \ -sdk iphonesimulator \ -destination 'platform=iOS Simulator,name=iPhone 17 Pro' \ ARCHS=arm64 \ ONLY_ACTIVE_ARCH=YES \ build gotcha_build_dir="$( xcodebuild \ -project Gotcha.xcodeproj \ -scheme Gotcha \ -configuration Debug \ -sdk iphonesimulator \ -destination 'platform=iOS Simulator,name=iPhone 17 Pro' \ -showBuildSettings -json \ ARCHS=arm64 \ ONLY_ACTIVE_ARCH=YES | plutil -extract 0.buildSettings.TARGET_BUILD_DIR raw -o - - )" xcrun simctl install booted "$gotcha_build_dir/Gotcha.app" xcrun simctl launch booted de.rfc1437.gotcha ``` Before deploying, check for a booted simulator with `xcrun simctl list devices available`. If none is booted, boot an available iPhone (currently `xcrun simctl boot "iPhone 17 Pro"`) and open Simulator. CoreSimulator access may require running `xcodebuild` and `simctl` outside the workspace sandbox. ## Post-issue release deployment After an issue is verified, committed, pushed, commented on, and closed, deploy the completed version according to the code it changes: - For CLI changes, build `gotcha-cli` in release mode and install the resulting `gotcha` executable in `~/.local/bin/` so the command is available on `PATH`. - For iOS app changes, install the completed app on the currently paired iPhone. - The Gitea crate is shared by the CLI and iOS app, so changes to that crate also require installing the completed app on the paired iPhone. A purely CLI change does not require an iPhone deployment. If an issue changes both release surfaces, perform both deployments. ## Generated build data Use Xcode's default DerivedData location; do not pass `-derivedDataPath`. Cargo's ignored `target/` directory is a disposable build cache. Cargo does not bound or garbage-collect stale target artifacts, so check it with `du -sh target` and run `cargo clean` when disk space is worth a full rebuild. Do not add a custom cache-pruning tool or disable incremental compilation just to reduce routine cache usage.