Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions .claude/agents/proto-change-tracer.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: proto-change-tracer
description: "Use this agent after regenerating protobuf definitions (e.g., after /fetch-protos) to trace the impact of proto changes through the codebase: generated Swift → service wrappers → client extensions → session/controllers → screens/viewmodels → tests.\n\nExamples:\n\n- user: \"what changed in the protos and what needs updating?\"\n assistant: \"I'll trace the proto changes through the service layer to identify what needs updating.\"\n <commentary>The user wants to understand proto change impact. Use the proto-change-tracer agent.</commentary>\n\n- user: \"I just regenerated the protos, what broke?\"\n assistant: \"I'll trace the updated proto definitions through the codebase to find affected code.\"\n <commentary>Proto definitions were updated. Use the proto-change-tracer agent to trace impact.</commentary>"
description: "Use this agent after bumping the contract packages (e.g., after /fetch-protos) to trace the impact of proto changes through the codebase: generated Swift → service wrappers → client extensions → session/controllers → screens/viewmodels → tests.\n\nExamples:\n\n- user: \"what changed in the protos and what needs updating?\"\n assistant: \"I'll trace the proto changes through the service layer to identify what needs updating.\"\n <commentary>The user wants to understand proto change impact. Use the proto-change-tracer agent.</commentary>\n\n- user: \"I just bumped the protos, what broke?\"\n assistant: \"I'll trace the updated proto definitions through the codebase to find affected code.\"\n <commentary>Proto definitions were updated. Use the proto-change-tracer agent to trace impact.</commentary>"
model: sonnet
---

Expand All @@ -16,12 +16,11 @@ identify every file that needs updating. You only analyze — you do not edit.
## Architecture: Proto → Screen Chain

```
FlipcashAPI/Sources/FlipcashAPI/{Core,Payments}/proto/*.proto ← .proto files
[protoc via Scripts/run]
flipcash2-client-protocol / ocp-client-protocol ← published packages,
<domain>_v1_<service>.pb.swift (messages, request/response, result enums) generated
<domain>_v1_<service>.grpc.swift (the <Namespace>.Client wrapper) upstream
FlipcashAPI/Sources/FlipcashAPI/{Core,Payments}/Generated/ ← generated stubs
<domain>_v1_<service>.pb.swift (messages, request/response, result enums)
<domain>_v1_<service>.grpc.swift (the <Namespace>.Client wrapper)
FlipcashAPI/Sources/FlipcashAPI/Exports.swift ← @_exported umbrella
FlipcashCore/.../Clients/{Flip API,Payments API}/Services/*Service.swift
← wraps the generated .Client, builds requests, maps proto result enums to a
Expand All @@ -39,8 +38,8 @@ Flipcash/Core/Session/, Flipcash/Core/Controllers/, Flipcash/Core/Screens/**
- Payments: `Ocp_<Domain>_V1_*` — account, currency, messaging, transaction, common

**Package/tool boundaries:**
- Generated Swift lives in the `FlipcashAPI` package; service wrappers live in `FlipcashCore`; screens live in the `Flipcash` app target.
- The Core and Payments messaging services share basenames — the fetch script renames the Core copy to `flipcash_messaging_v1_*` to avoid a collision in the merged module.
- Generated Swift arrives from the two published packages, which `FlipcashAPI` re-exports; service wrappers live in `FlipcashCore`; screens live in the `Flipcash` app target. Read generated sources from the resolved checkouts under `.build/checkouts/` (or `~/Library/Developer/Xcode/DerivedData/**/SourcePackages/checkouts/`), not from this repo.
- The Core and Payments messaging services share basenames, but they are separate modules now, so the Swift type prefixes (`Flipcash_` vs `Ocp_`) are the only thing keeping them apart.

## Analysis Process

Expand Down Expand Up @@ -124,5 +123,5 @@ Prioritized checklist of files to modify, grouped by layer.
classification — an unmapped case silently degrades to `.error`-level `.unknown`.
- Confirm unary calls use `options: .unaryDefault` and streaming calls use `.defaults`
(never a deadline on a stream).
- Never edit generated files under `Generated/` — flag the wrapping `*Service.swift` instead.
- Never propose edits to generated package sources — they are read-only checkouts. Flag the wrapping `*Service.swift`, or a version bump, instead.
- You are read-only: produce the impact report and checklist; do not modify files.
8 changes: 6 additions & 2 deletions .claude/docs/hard-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,13 @@ case .insufficient(let shortfall):

Existing `ObservableObject` classes (`Client`, `FlipClient`) stay as-is until their dependents are migrated. A single class must use one system — either `ObservableObject` with `@Published`, or `@Observable`. Mixing causes silent observation failures.

## Generated Files
## Generated Protos

**Never modify files under `Generated/` directly** — they're regenerated from upstream protos by the scripts in [Regenerating Protos](technology-stack.md#regenerating-protos), and any local edits will be overwritten. Update the service files that wrap the generated code instead.
**Generated proto code is not in this repo.** `FlipcashAPI` re-exports `OCPClientProtocol` and
`Flipcash2ClientProtocol`, which are published from their own repos (see
[Protos: consumed, not generated here](technology-stack.md#protos-consumed-not-generated-here)).
A contract fix belongs in the package repo and reaches the app as a version bump; anything the
app can fix itself belongs in the service files that wrap the generated code.

## Database Schema Changes

Expand Down
29 changes: 14 additions & 15 deletions .claude/docs/technology-stack.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,23 @@

Open `Code.xcodeproj` in Xcode 16.x. Swift packages resolve automatically on first open. Build and run the `Flipcash` scheme.

## Regenerating Protos
## Protos: consumed, not generated here

Swift gRPC bindings in `FlipcashAPI/Sources/FlipcashAPI/Payments/Generated` and `FlipcashAPI/Sources/FlipcashAPI/Core/Generated` are generated from `.proto` files pulled from the server-protobuf repos. To regenerate:
This repo no longer vendors `.proto` files or runs protoc. The generated Swift ships from two
published packages, and `FlipcashAPI` is a thin umbrella that `@_exported import`s both so
`import FlipcashAPI` keeps working:

```
cd Scripts
./run -a flipcashPayments
./run -a flipcashCore
```

Each invocation clones the latest `.proto` files from the upstream repo, replaces the local `proto/` directory, and regenerates the Swift code in `Generated/`.
| Module | Package | Contract |
|---|---|---|
| `OCPClientProtocol` | [`ocp-client-protocol`](https://github.com/code-payments/ocp-client-protocol) | `ocp-protobuf-api` |
| `Flipcash2ClientProtocol` | [`flipcash2-client-protocol`](https://github.com/code-payments/flipcash2-client-protocol) | `flipcash2-protobuf-api` |

**Required tools** (checked by the script; aborts if missing):
- `protoc` — `brew install protobuf`
- `protoc-gen-swift` — `brew install swift-protobuf`
- `protoc-gen-grpc-swift-2` (grpc-swift **2.x**) — `./Scripts/install-grpc-swift-2-plugin.sh`
Android consumes the Kotlin half of the same two packages, so both apps now generate from one
place instead of each vendoring the contract.

**Never modify files under `Generated/` directly** — changes will be overwritten on the next regen.
**To pick up a contract change:** sync and release it in the client-protocol repo (its README has
the steps), then bump the `exact:` version in `FlipcashAPI/Package.swift`. Nothing in this repo
needs protoc, swift-protobuf, or the grpc-swift plugin installed.

## Required Technologies

Expand All @@ -41,7 +40,7 @@ Each invocation clones the latest `.proto` files from the upstream repo, replace
Flipcash/ # Main app - focus here
FlipcashCore/ # Business logic, models, clients
FlipcashUI/ # UI components, theme
FlipcashAPI/ # gRPC proto definitions + generated v2 bindings (Payments/ + Core/)
FlipcashAPI/ # umbrella over the two published contract packages
CodeCurves/ # Ed25519 cryptography
CodeScanner/ # C++/OpenCV circular code scanning (see below)
```
Expand Down
92 changes: 44 additions & 48 deletions .claude/skills/fetch-protos/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
name: fetch-protos
description: >
Fetch latest protobuf definitions, regenerate Swift bindings, verify the build,
summarize API changes, and scaffold new service stubs. Usage: /fetch-protos [core|payments] [both]
Bump the published contract packages, verify the build, summarize API changes,
and scaffold new service stubs. Usage: /fetch-protos [core|payments] [both]
argument-hint: "[core|payments] (default: both)"
allowed-tools:
- Bash
Expand All @@ -16,74 +16,70 @@ allowed-tools:

# Fetch Protos

Pull `.proto` files from upstream, regenerate the Swift gRPC bindings under
`FlipcashAPI/Sources/FlipcashAPI/{Core,Payments}/Generated`, verify they compile,
summarize the API changes, and scaffold missing service-layer implementations.
Move the app onto newer contract packages, verify they compile, summarize the API changes,
and scaffold missing service-layer implementations.

This repo does not generate protos. `FlipcashAPI` is an umbrella that re-exports two published
packages, and picking up a contract change means bumping their versions — the generation itself
happens in the package repos.

## Pre-flight context

- Core protos: !`find FlipcashAPI/Sources/FlipcashAPI/Core/proto -name "*.proto" 2>/dev/null | wc -l | tr -d ' '`
- Payments protos: !`find FlipcashAPI/Sources/FlipcashAPI/Payments/proto -name "*.proto" 2>/dev/null | wc -l | tr -d ' '`
- Pinned versions: !`grep -E 'client-protocol' FlipcashAPI/Package.swift`
- Git status: !`git status --short FlipcashAPI/`

## Input

Parse `$ARGUMENTS` to determine which domain(s) to fetch.
Parse `$ARGUMENTS` to determine which domain(s) to bump.

**Rules:**
- Known targets: `core` (→ `flipcashCore`), `payments` (→ `flipcashPayments`)
- If no target specified, fetch **both**
- `both` explicitly fetches both
- Known targets: `core` (→ flipcash2), `payments` (→ ocp)
- If no target specified, bump **both**
- `both` explicitly bumps both
- Examples:
- `/fetch-protos` → fetch core + payments
- `/fetch-protos core` → fetch core only
- `/fetch-protos payments` → fetch payments only

Target-to-repo mapping (handled by `Scripts/run`):
- `/fetch-protos` → bump core + payments
- `/fetch-protos core` → bump core only
- `/fetch-protos payments` → bump payments only

| Target | App flag | Upstream repo |
|--------|----------|---------------|
| `core` | `flipcashCore` | `code-payments/flipcash2-protobuf-api` |
| `payments` | `flipcashPayments` | `code-payments/ocp-protobuf-api` |
| Target | Swift module | Package | Upstream contract |
|--------|--------------|---------|-------------------|
| `core` | `Flipcash2ClientProtocol` | `code-payments/flipcash2-client-protocol` | `code-payments/flipcash2-protobuf-api` |
| `payments` | `OCPClientProtocol` | `code-payments/ocp-client-protocol` | `code-payments/ocp-protobuf-api` |

## Steps

### Step 1 — Pre-flight tool check

The script aborts if any generator is missing, but confirm first so the user can
install before anything destructive runs:
### Step 1 — Find the release to move to

```bash
command -v protoc protoc-gen-swift protoc-gen-grpc-swift-2
gh release list --repo code-payments/ocp-client-protocol --limit 5
gh release list --repo code-payments/flipcash2-client-protocol --limit 5
```

If any are missing, install and stop:
- `protoc` → `brew install protobuf`
- `protoc-gen-swift` → `brew install swift-protobuf`
- `protoc-gen-grpc-swift-2` → `./Scripts/install-grpc-swift-2-plugin.sh`
If the contract change you want is not released yet, stop: it has to be synced and published
from the package repo first (see that repo's README — `scripts/sync-protos.sh`, then the
`publish.yml` workflow). Releasing is a deliberate, human-gated step; do not start it from here.

Android pins the same two packages in its `gradle/libs.versions.toml`. The versions are not
required to match across platforms, but a contract change that matters to both should land on
both — flag it if only one side is moving.

### Step 2 — Fetch protos and regenerate
### Step 2 — Bump the pin

For each target, run from the repo root:
Edit the `exact:` requirement in `FlipcashAPI/Package.swift` for each target, then resolve:

```bash
cd Scripts && ./run -a flipcashCore # core
cd Scripts && ./run -a flipcashPayments # payments
xcodebuild -resolvePackageDependencies -project Code.xcodeproj -scheme Flipcash
```

Each invocation clones the latest `.proto` files from upstream, replaces the local
`proto/` directory, copies `proto_deps/` back in, and regenerates the Swift bindings
in `Generated/`. It also drops `validate_validate.pb.swift` (unused client mirror)
and, for core, renames the messaging service files to avoid a basename collision with
the payments messaging service in the merged `FlipcashAPI` module. Show the output.
Show the resulting `Package.resolved` diff — it should change only the bumped package's
`version` and `revision`.

### Step 3 — Diff and summarize changes

The meaningful diff is the regenerated Swift, since `proto/` is wiped and re-cloned:
The packages ship their generated Swift committed, so the API diff is readable directly:

```bash
git diff --stat FlipcashAPI/Sources/FlipcashAPI/Core/Generated FlipcashAPI/Sources/FlipcashAPI/Payments/Generated
git diff FlipcashAPI/Sources/FlipcashAPI/*/proto
gh api repos/code-payments/<package>/compare/<old-tag>...<new-tag> --jq '.files[].filename'
```

For each changed service, summarize:
Expand All @@ -92,19 +88,19 @@ For each changed service, summarize:
- **Removed RPCs**
- **New/modified messages, fields, and enum result cases**

Present a structured change summary. If nothing changed, report that protos are
already up to date and stop here.
Present a structured change summary. If nothing changed, report that the app is already on the
latest release and stop here.

### Step 4 — Build verification

Verify the regenerated code compiles before touching anything else:
Verify the app compiles against the new packages before touching anything else:

```bash
./Scripts/build.sh
```

If the build fails, show errors and stop — a broken generation must be resolved
(usually a proto rename that orphaned a Swift type reference) before proceeding.
If the build fails, show errors and stop — a broken bump must be resolved (usually a proto
rename that orphaned a Swift type reference) before proceeding.

### Step 5 — Trace service-layer impact

Expand Down Expand Up @@ -226,7 +222,7 @@ Show the user a summary of all changes (proto/generated updates + any scaffolded
service code). Offer to commit only after approval, with a conventional message:

```
chore: sync <core|payments> protos
chore: bump <core|payments> client-protocol to <version>
```

If service stubs were scaffolded, suggest a separate commit:
Expand All @@ -237,7 +233,7 @@ feat: scaffold <domain> service for new RPCs

## Never

- Edit generated files under `Generated/` directly — they are overwritten on the next regen. Update the wrapping `*Service.swift` instead.
- Patch generated code locally to work around a contract problem. It lives in the package repos; fix it there and cut a release. Update the wrapping `*Service.swift` instead when the gap is app-side.
- Give a streaming RPC a deadline (`.unaryDefault`). Streaming passes `.defaults`.
- Interpolate variables (especially base58/keys) into log message strings — variables go in `metadata`.
- Skip the build verification in Step 4.
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ and is linked from the map below. **Read the relevant doc before working in that
|---|---|
| About to write/change any code | [Hard Rules](.claude/docs/hard-rules.md) — full text, rationale, examples (checklist below) |
| Working on DI, gRPC, navigation, transport errors, or core concepts | [Architecture & Patterns](.claude/docs/architecture.md) |
| Setting up, building, or regenerating protos; touching SQLite/CodeScanner | [Technology Stack, Setup & Tooling](.claude/docs/technology-stack.md) |
| Setting up, building, or bumping the contract packages; touching SQLite/CodeScanner | [Technology Stack, Setup & Tooling](.claude/docs/technology-stack.md) |
| Writing or running tests | [Testing](.claude/docs/testing.md) |
| Naming, file placement, imports, or committing | [Code Style & Git Workflow](.claude/docs/code-style.md) |
| About to touch cash bills, navigation, dialogs, amounts, or DI | [Common Pitfalls](.claude/docs/common-pitfalls.md) |
Expand Down Expand Up @@ -93,7 +93,7 @@ it before touching the relevant area.
- **Testing framework** — Swift Testing (`import Testing`, `@Suite`/`@Test`), never XCTest.
- **Exhaustive switches** — Prefer `switch` over `if case` for enums so the compiler flags new cases.
- **Modernize incrementally** — Use modern Swift/SwiftUI APIs in net-new/isolated code; don't refactor working code just to modernize. One observation system per class.
- **Generated files** — Never edit files under `Generated/`; change the wrapping service files instead.
- **Generated protos** — `FlipcashAPI` only re-exports the published contract packages; there is no generated code to edit here. Change the wrapping service files instead.
- **Database schema** — Bump `SQLiteVersion` in Info.plist on every schema change (no migrations; DB is rebuilt from server).
- **Logging** — Message string is a constant; every variable goes in structured `metadata`. Never log proto blobs whole.
- **Error reporting** — Call `ErrorReporting.captureError(...)` unconditionally; classify via `ServerError.reportingLevel`, never gate at the call site. Best-effort chatter never reports.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 4 additions & 10 deletions FlipcashAPI/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,15 @@ let package = Package(
),
],
dependencies: [
.package(url: "https://github.com/grpc/grpc-swift-2.git", from: "2.4.0"),
.package(url: "https://github.com/grpc/grpc-swift-protobuf.git", from: "2.0.0"),
.package(url: "https://github.com/code-payments/ocp-client-protocol", exact: "0.1.0"),
.package(url: "https://github.com/code-payments/flipcash2-client-protocol", exact: "0.1.0"),
],
targets: [
.target(
name: "FlipcashAPI",
dependencies: [
.product(name: "GRPCCore", package: "grpc-swift-2"),
.product(name: "GRPCProtobuf", package: "grpc-swift-protobuf"),
],
exclude: [
"Payments/proto",
"Payments/proto_deps",
"Core/proto",
"Core/proto_deps",
.product(name: "OCPClientProtocol", package: "ocp-client-protocol"),
.product(name: "Flipcash2ClientProtocol", package: "flipcash2-client-protocol"),
]
),
]
Expand Down
Loading