# 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.
