684f07746d
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
114 lines
4.5 KiB
Markdown
114 lines
4.5 KiB
Markdown
# Solitaire Quest — Claude Code Instructions
|
|
|
|
See @ARCHITECTURE.md for full project design, crate responsibilities, data models, and API reference.
|
|
|
|
---
|
|
|
|
## Project Layout
|
|
|
|
```
|
|
solitaire_core/ # Pure Rust game logic — NO Bevy, NO network, NO I/O
|
|
solitaire_sync/ # Shared API types — NO Bevy, serde/uuid/chrono only
|
|
solitaire_data/ # Persistence + SyncProvider trait + server client
|
|
solitaire_engine/ # Bevy ECS systems, components, plugins
|
|
solitaire_server/ # Axum sync server binary
|
|
solitaire_gpgs/ # Google Play Games bridge — STUB ONLY until Android phase
|
|
solitaire_app/ # Thin binary entry point
|
|
assets/ # Loaded at runtime via Bevy AssetServer only
|
|
```
|
|
|
|
---
|
|
|
|
## Build & Test Commands
|
|
|
|
```bash
|
|
# Dev run (fast compile via dynamic linking)
|
|
cargo run -p solitaire_app --features bevy/dynamic_linking
|
|
|
|
# Release build
|
|
cargo build --workspace --release
|
|
|
|
# All tests — MUST pass before any commit
|
|
cargo test --workspace
|
|
|
|
# Lint — MUST pass clean (zero warnings)
|
|
cargo clippy --workspace -- -D warnings
|
|
|
|
# Run sync server locally
|
|
cargo run -p solitaire_server
|
|
|
|
# Check a single crate
|
|
cargo test -p solitaire_core
|
|
cargo clippy -p solitaire_core -- -D warnings
|
|
```
|
|
|
|
---
|
|
|
|
## Hard Rules
|
|
|
|
- `solitaire_core` and `solitaire_sync` must never gain Bevy or network dependencies.
|
|
- No `unwrap()` or `panic!()` in game logic. All state transitions return `Result<_, MoveError>`.
|
|
- No hardcoded bytes in source. All assets go through Bevy's `AssetServer`.
|
|
- Atomic file writes only: write to `filename.json.tmp`, then `rename()`.
|
|
- Passwords and tokens are stored in the OS keychain via the `keyring` crate — never in plaintext files or logs.
|
|
- Sync runs on `AsyncComputeTaskPool` — never block the Bevy main thread.
|
|
- All sync backends implement the `SyncProvider` trait. The `SyncPlugin` is backend-agnostic — never `match` on `SyncBackend` inside a Bevy system.
|
|
- `solitaire_gpgs` is a stub until Android work begins. Do not write JNI bindings yet; keep the compile-time stub so the trait contract is enforced from day one.
|
|
- `cargo clippy --workspace -- -D warnings` must pass clean after every change.
|
|
- `cargo test --workspace` must pass after every change.
|
|
|
|
---
|
|
|
|
## Code Style
|
|
|
|
- Use `thiserror` for error types. Never `Box<dyn Error>` in library crates.
|
|
- Prefer `Into<T>` over concrete types in public API function parameters.
|
|
- All public items must have doc comments (`///`). Private items: comment only when non-obvious.
|
|
- Derive order convention: `#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]`
|
|
- Bevy systems: one responsibility per system. Use `Events` for cross-system communication, never shared mutable state.
|
|
- SQL queries: use `sqlx::query!` macros (compile-time checked), not raw string queries.
|
|
- No `clone()` calls in hot paths (game loop systems). Profile before optimising elsewhere.
|
|
|
|
---
|
|
|
|
## Bevy Conventions
|
|
|
|
- One `Plugin` per major feature: `CardPlugin`, `AudioPlugin`, `AchievementPlugin`, `UIPlugin`, `SyncPlugin`.
|
|
- Resources own shared state. Events communicate between systems. Components own per-entity data.
|
|
- All egui screens live in `solitaire_engine::ui`. Never mix egui and Bevy spawn logic in the same system.
|
|
- Layout is recomputed on `WindowResized` — never assume a fixed window size.
|
|
|
|
---
|
|
|
|
## Git Workflow
|
|
|
|
- Commit after each passing phase, not after every file change.
|
|
- Commit message format: `type(scope): description`
|
|
- `feat(core): add draw-three mode validation`
|
|
- `fix(engine): card z-order during drag`
|
|
- `test(core): undo stack boundary conditions`
|
|
- `chore(server): add sqlx migration 002`
|
|
- Never commit with failing tests or clippy warnings.
|
|
- Never commit secrets, `.env` files, or `*.db` files.
|
|
|
|
---
|
|
|
|
## Ask Before Doing
|
|
|
|
- Adding a new crate dependency (discuss alternatives first).
|
|
- Changing a type in `solitaire_sync` (breaking change on both client and server).
|
|
- Altering the database schema (requires a new sqlx migration).
|
|
- Introducing `unsafe` code anywhere.
|
|
- Changing the merge strategy in `solitaire_sync::merge()`.
|
|
|
|
---
|
|
|
|
## Lessons Learned
|
|
|
|
> Add entries here when Claude makes a mistake so it isn't repeated.
|
|
|
|
- Bevy's `Time` resource uses `f32` seconds; convert to `u64` only when writing to `StatsSnapshot`.
|
|
- `sqlx::migrate!()` macro path is relative to the crate root, not the workspace root.
|
|
- `keyring` on Linux requires a running secret service (e.g. GNOME Keyring or KWallet) — handle `Error::NoStorageAccess` gracefully and fall back to prompting the user.
|
|
- `dirs::data_dir()` returns `None` on some minimal Linux environments — always handle the `None` case explicitly, do not unwrap.
|