Skip to content

Latest commit

 

History

History
321 lines (235 loc) · 14.4 KB

File metadata and controls

321 lines (235 loc) · 14.4 KB

AGENTS.md

Guidance for AI/code agents working in the SpeleoDB Compass Sidecar repository.

This file is intentionally opinionated and feature-focused so agents can make correct changes without re-discovering architecture every session.

Project Overview

SpeleoDB Compass Sidecar is a Tauri v2 + Yew desktop application that bridges SpeleoDB (cave survey database) with Compass (desktop cave surveying software). It manages project synchronization, authentication, and launches Compass for editing.

Core Principles

  • Simplicity First: Make every change as simple as possible. Impact minimal code.
  • No Laziness: Find root causes. No temporary fixes. Principal Engineer standards
  • Minimat Impact: Changes should only touch what's necessary. Avoid introducing bugs or changing unrelated parts of the code.
  • Readability & Maintainability: Preserve product behavior while improving maintainability.
  • Performance Conscious: Be aware of the performance impact of your changes and try to minimize the impact on performance.
  • Refactor as necessary: Prefer centralized logic over duplicated code, conditionals or per-call custom checks.
  • Tests are cheap: Every behavior should be tested. Untested code is broken code.

Task Management

  1. Plan First: Write plan to tasks/todos/ with checkable items
  2. Verify Plan: Check in before starting implementation
  3. Track Progress: Mark items complete as you go
  4. Explain Changes: High-level summary at each step
  5. Document Results: Add review section to tasks/todo.md"
  6. Capture Lessons: Update tasks/lessons/ after corrections
  7. Documentation is Key: Document each feature and design inside docs/. What is the feature being implemented, the design space and intents and a rapid summary of the approach taken with key APIs & concepts.

Workflow Orchestration

1. Plan Node Default

  • Enter plan mode for ANY non-trivial task (3+ steps or architectural decisions)
  • If something goes sideways, STOP and re-plan immediately - don't keep pushing
  • Use plan mode for verification steps, not just building
  • Write detailed specs upfront to reduce ambiguity

2. Subagent Strategy

  • Use subagents liberally to keep main context window clean
  • Offload research, exploration, and parallel analysis to subagents
  • For complex problems, throw more compute at it via subagents
  • One tack per subagent for focused execution

3. Self-Improvement Loop

  • After ANY correction from the user: update tasks/lessons/ with the pattern
  • Write rules for yourself that prevent the same mistake
  • Ruthlessly iterate on these lessons until mistake rate drops
  • Review lessons at session start for relevant project

4. Verification Before Done

  • Never mark a task complete without proving it works
  • Diff behavior between main and your changes when relevant
  • Ask yourself: "Would a staff engineer approve this?"
  • Run tests, check logs, demonstrate correctness

5. Demand Elegance (Balanced)

  • For non-trivial changes: pause and ask "is there a more elegant way?"
  • If a fix feels hacky: "Knowing everything I know now, implement the elegant solution"
  • Skip this for simple, obvious fixes - don't over-engineer
  • Challenge your own work before presenting it

6. Autonomous Bug Fizing

  • When given a bug report: just fix it. Don't ask for hand-holding
  • Point at logs, errors, failing tests - then resolve them
  • Zero context switching required from the user
  • Go fix failing CI tests without being told how

Testing Requirements

For map viewer/frontend changes, validate tests:

  • npm run test:js

Backend/API changes should also run relevant pytest targets:

  • pytest

New tests should respects coding existing structures

Linter

Run make lint to validate the codebase. This depends on:

  • make lint-fmtcargo fmt --all -- --check
  • make lint-clippycargo clippy --workspace --all-targets --all-features -- -D warnings

Clippy runs with -D warnings so any clippy lint blocks the lint stage and CI. Fix the lint at its root rather than #[allow(...)]-annotating it unless there is a documented reason.

Documentation Expectations for Agents

When changing feature behavior or architecture, update docs under docs/ for the impacted topic:

  • feature intent
  • engineering scope and ownership boundaries
  • testing and verification strategy
  • performance implications

Do not only document "what changed"; include "why this architecture exists".

Performance and Regression Checklist

Before finishing map viewer work, check:

  1. No duplicated permission matrix logic was added.
  2. Depth mode toggles still avoid per-feature rescans.
  3. Public and private map viewers still initialize shared modules correctly.
  4. Lint and tests pass from root.
  5. Tailwind outputs still generate from root scripts.

Practical Do/Do-Not

Do:

  • Prefer shared utilities/modules over code duplication.
  • Add focused tests when changing anything of significance.
  • Be performance conscious.
  • Systematically document all features & architectural decisions.

Do not:

  • Duplicate code or logic.
  • Introduce "quick patches" that hinder long term maintainability.
  • Add expensive computations.

Build Commands

# Development with hot-reload
make dev
# or: cd app && cargo tauri dev

# Build release
make build-tauri
# or: cd app && cargo tauri build

# Build UI only (WASM)
make build-ui
# or: cd app && trunk build --release

Testing

All tests make real HTTP requests - no mocks. Requires .env file with valid credentials.

# Setup: copy .env.dist to .env and add credentials
cp .env.dist .env
# Edit .env with TEST_SPELEODB_INSTANCE and TEST_SPELEODB_OAUTH

# Test everything (Rust + WASM)
make test

# Test only Rust (excludes WASM UI)
make test-rust

# Test specific crates
make test-tauri    # Tauri backend only
make test-common   # Common crate only
make test-ui       # WASM UI tests (requires wasm-pack)

# Verbose output
make test-rust-verbose

# Run specific test
cargo test native_auth_request

# Run tests serially (prevents race conditions)
cargo test -- --test-threads=1

Workspace Structure

Five Cargo workspace members (resolver v3, Rust edition 2024):

  • api/ - SpeleoDB REST API client (auth, project CRUD, mutex acquire/release)
  • app/ - Yew WASM frontend (speleodb-compass-sidecar-ui, components + IPC to backend)
  • app/src-tauri/ - Tauri backend (speleodb-compass-sidecar, commands, state management, Compass integration)
  • common/ - Shared types (ApiInfo, OauthToken, ProjectInfo, UiState, LoadingState, LocalProjectStatus)
  • errors/ - Single Error enum with ~30 variants, serializable for frontend

Workspace dependencies defined in root Cargo.toml: bytes, log, serde, serde_json, thiserror, tokio, toml, url, uuid.

Architecture

Data Flow

  1. Authentication: Frontend calls auth_request command → backend calls api crate → credentials stored in ~/.compass/user_prefs.json (TOML format, 0o600 permissions on Unix)

  2. Project Sync: Backend fetches from SpeleoDB API → compares with local .revision.txt files → emits LocalProjectStatus (RemoteOnly, EmptyLocal, UpToDate, OutOfDate, Dirty, DirtyAndOutOfDate)

  3. Compass Launch: Backend acquires project mutex via API → launches wcomp32.exe (Windows) → monitors process via sysinfo crate → releases mutex when Compass closes. On macOS/Linux, opens the project folder in the system file explorer instead.

  4. Background Tasks: AppState runs a background async task polling every 120s for remote project updates and every 1s for local status changes (including Compass process monitoring on Windows).

Key Files

Backend (app/src-tauri/src/)

  • lib.rs - Tauri app setup: plugins (updater, dialog), command registration, window close prevention if Compass is open, menu with sign-out, Sentry init
  • commands.rs - Tauri commands: about_info, auth_request, clear_active_project, create_project, discard_changes, ensure_initialized, import_compass_project, open_project, pick_compass_project_file, reimport_compass_project, release_project_mutex, save_project, set_active_project, sign_out
  • state.rs - AppState with Mutex-protected fields (api_info, project_info HashMap, active_project, compass_pid, loading_state), background task, emit_app_state_change() to push UiState to frontend via UI_STATE_EVENT
  • paths.rs - Path constants and helpers: ~/.compass/ home dir, ~/.compass/projects/{uuid}/index and working_copy layout, file logger setup
  • user_prefs.rs - UserPrefs persistence: load/save TOML credentials, env var fallback for tests (TEST_SPELEODB_INSTANCE, TEST_SPELEODB_OAUTH)
  • project_management/mod.rs - ProjectManager: local status detection, project download/upload, mutex management
  • project_management/local_project.rs - LocalProject: Compass file handling (.MAK, .DAT, .PLT), dirty detection (index vs working_copy), ZIP packing, project import via compass_data crate
  • project_management/revision.rs - .revision.txt read/write for tracking synced commit hash

Frontend (app/src/)

  • main.rs - WASM entry: panic hook, wasm_logger, renders App
  • app.rs - Root component: subscribes to UI_STATE_EVENT, calls ensure_initialized(), routes to AuthScreen / MainLayout / LoadingScreen based on LoadingState
  • speleo_db_controller.rs - SpeleoDBController singleton wrapping Tauri invoke() calls with input validation (OAuth = 40 hex chars)
  • error.rs - Frontend Error enum (Command, Serde variants)
  • ui_constants.rs - Color palette constants (warn, alarm, good, blue, grey)
  • components/mod.rs - Module declarations for all components
  • components/auth_screen.rs - Login UI with OAuth token and email/password tabs, instance URL dropdown (stage/production)
  • components/main_layout.rs - Two-pane authenticated layout: project listing + project details, header with user email and sign-out
  • components/project_listing.rs - Scrollable project list from UiState.project_status
  • components/project_listing_item.rs - Individual project row with status indicator
  • components/project_details.rs - Project detail view: open in Compass, download, commit form, read-only indicator, mutex status
  • components/create_project_modal.rs - New project form (name, description, country, coordinates)
  • components/loading_screen.rs - Loading state display with status text
  • components/modal.rs - Generic modal component (Success, Error, Info, Warning, Confirmation types)

API (api/src/)

  • lib.rs - Module declarations, global HTTP client (reqwest) with 10s timeout
  • http.rs - Centralized v2 plumbing: v2_url(), authenticated(), send_json(), send_raw(), map_status_to_error(). Single chokepoint for status-code → typed Error mapping (401/403→Unauthorized, 404→NotFound, 422→Unprocessable, 409/423→Conflict, otherwise Api{status,message})
  • auth.rs - authorize_with_token() and authorize_with_email() against api/v2/user/auth-token/
  • project.rs - create_project(), fetch_project_info(), fetch_projects(), acquire_project_mutex() (Conflict→ProjectMutexLocked), release_project_mutex(), download_project_zip() (Unprocessable→NoProjectData), upload_project_zip() — all under api/v2/projects/
  • test_support.rs (test-only) - .env autoloader, test_api_info(), unauthorized_api_info(), fixture_project_id() (lazy shared OnceCell), build_minimal_compass_zip(). See docs/api-v2.md for the testing strategy.

Common (common/src/)

  • lib.rs - Re-exports, conditional API_BASE_URL (stage in debug, production in release)
  • api_info.rs - ApiInfo (instance URL, email, oauth_token), OauthToken newtype
  • api_types.rs - ProjectInfo, CommitInfo, ProjectType (ARIANE, COMPASS), ProjectSaveResult
  • ui_state.rs - UiState, LoadingState, LocalProjectStatus, ProjectStatus, Platform
  • error.rs - Single Error enum covering auth, file I/O, project state, network, OS/Compass, serialization. HTTP-derived variants: Unauthorized(String), NotFound(String), Unprocessable(String), Conflict(String), generic Api{status,message} — every variant carries the server-provided message.

IPC Communication

  • Frontend → Backend: invoke() calls via tauri-sys (JSON serialized with serde-wasm-bindgen)
  • Backend → Frontend: emit() events via UI_STATE_EVENT ("ui-state-update")
  • Frontend listens with Yew stream subscription in App component

Local Project Layout

~/.compass/
├── user_prefs.json          # Credentials (TOML format despite .json extension)
├── speleodb_compass*.log    # Application logs (flexi_logger)
└── projects/
    └── {project-uuid}/
        ├── index/           # Last synced remote copy
        │   ├── .compass/    # compass.toml with SpeleoDb metadata
        │   └── ...          # Compass project files
        ├── working_copy/    # User's editable copy
        │   └── ...          # Compass project files (.MAK, .DAT, .PLT)
        └── .revision.txt    # Commit hash of last sync

Logging

Logs written to: ~/.compass/speleodb_compass*.log

# Real-time logs
tail -f ~/.compass/speleodb_compass*.log

# Search logs
grep "pattern" ~/.compass/speleodb_compass*.log

Key Dependencies

  • Tauri 2 with plugins: tauri-plugin-dialog, tauri-plugin-updater
  • Yew 0.22 (CSR mode) with yew_icons (FontAwesome)
  • compass_data 0.0.7 - Parses Compass survey file formats
  • sentry 0.46 - Error tracking
  • reqwest 0.12 (rustls-tls) - HTTP client for SpeleoDB API
  • sysinfo 0.33 (Windows only) - Compass process monitoring
  • zip 7 - Project packaging for upload/download

Windows Development Setup

Requires Windows toolchain with MinGW:

rustup toolchain install stable-x86_64-pc-windows-gnu
rustup default stable-x86_64-pc-windows-gnu
rustup target add wasm32-unknown-unknown
cargo install tauri-cli --version "^2.0.0" --locked
cargo install trunk --locked
cargo install wasm-pack

Also requires MSYS2 with base-devel and mingw-w64-ucrt-x86_64-toolchain packages.

CI/CD

  • ci.yml - Runs on push/PR to main, executes make test on Windows. Uses cargo-binstall for trunk/wasm-pack.
  • publish.yml - Triggered by git tags, depends on CI passing. Builds for macOS (aarch64) and Windows via tauri-action. Creates signed GitHub release with updater artifacts.
  • dependabot.yml - Automated dependency updates

Version Info

  • Cargo.toml and tauri.conf.json: 0.1.0
  • SPELEODB_COMPASS_VERSION constant in lib.rs: 1.0.0