3ffde038c5
Card faces, card backs, board backgrounds, and the UI font are loaded
via Bevy's AssetServer at startup (see commit fbe984c). The CLAUDE.md
hard rule still claimed cards/backgrounds were rendered procedurally
with no AssetServer, and ARCHITECTURE.md §14 / §20 still described
PNGs and TTFs as embedded via include_bytes!(). Update both docs:
- CLAUDE.md hard rule lists which assets ship in assets/ and notes the
Option<Res<AssetServer>> fallback used under MinimalPlugins (tests).
- ARCHITECTURE.md §2/§3/§5/§14 rewritten to describe the AssetServer
loaders for CardImageSet, BackgroundImageSet, and FontResource, and
the Text2d / solid-colour fallbacks.
- ARCHITECTURE.md §20 decision log replaces the two reversed
embed-via-include_bytes!() entries with a single entry covering the
switch to AssetServer plus a note that audio remains embedded.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5.0 KiB
5.0 KiB
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_app/ # Thin binary entry point
assets/ # Source assets — embedded at compile time via include_bytes!()
Build & Test Commands
# 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_coreandsolitaire_syncmust never gain Bevy or network dependencies.- No
unwrap()orpanic!()in game logic. All state transitions returnResult<_, MoveError>. - Audio assets are embedded at compile time using
include_bytes!()inaudio_plugin.rs. - Card faces (52 PNGs in
assets/cards/faces/), card backs (assets/cards/backs/back_N.png), board backgrounds (assets/backgrounds/bg_N.png), and the UI font (assets/fonts/main.ttf) are loaded at runtime viaAssetServer::load()and stored asHandle<Image>/Handle<Font>in theCardImageSet,BackgroundImageSet, andFontResourceresources. Theassets/directory must ship alongside the binary. - Asset-loading systems take
Option<Res<AssetServer>>so they degrade cleanly underMinimalPlugins(tests). WhenCardImageSetis absent,card_pluginfalls back to aText2drank+suit overlay; whenBackgroundImageSetis absent, the board falls back to a solid colour. - Atomic file writes only: write to
filename.json.tmp, thenrename(). - Passwords and tokens are stored in the OS keychain via the
keyringcrate — never in plaintext files or logs. - Sync runs on
AsyncComputeTaskPool— never block the Bevy main thread. - All sync backends implement the
SyncProvidertrait. TheSyncPluginis backend-agnostic — nevermatchonSyncBackendinside a Bevy system. cargo clippy --workspace -- -D warningsmust pass clean after every change.cargo test --workspacemust pass after every change.
Code Style
- Use
thiserrorfor error types. NeverBox<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
Eventsfor 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
Pluginper major feature:CardPlugin,AudioPlugin,AchievementPlugin,UIPlugin,SyncPlugin. - Resources own shared state. Events communicate between systems. Components own per-entity data.
- All UI screens are built with Bevy UI (
bevy::ui). Never mix UI layout and game 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): descriptionfeat(core): add draw-three mode validationfix(engine): card z-order during dragtest(core): undo stack boundary conditionschore(server): add sqlx migration 002
- Never commit with failing tests or clippy warnings.
- Never commit secrets,
.envfiles, or*.dbfiles.
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
unsafecode 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
Timeresource usesf32seconds; convert tou64only when writing toStatsSnapshot. sqlx::migrate!()macro path is relative to the crate root, not the workspace root.keyringon Linux requires a running secret service (e.g. GNOME Keyring or KWallet) — handleError::NoStorageAccessgracefully and fall back to prompting the user.dirs::data_dir()returnsNoneon some minimal Linux environments — always handle theNonecase explicitly, do not unwrap.