Files
banks2ff/AGENTS.md
Jacob Kiers 5f54124015 chore: Move all command handlers to their own files
This makes the code much easier to follow and shortens main.rs from
>1000 lines to around 150.
2025-11-29 01:22:49 +01:00

9.8 KiB

Banks2FF Development Guide

Project Purpose

Banks2FF is a Rust CLI tool that synchronizes bank transactions from GoCardless Bank Account Data API to Firefly III personal finance manager. It implements a hexagonal architecture for clean separation of concerns and comprehensive testing.

🚨 CRITICAL: Financial Data Security

Financial Data Masking Requirements

FOR LLM/AI INTERACTIONS ONLY: When interacting with coding agents, LLMs, or AI assistants:

  • NEVER expose, log, or display raw financial information including:
    • Transaction amounts
    • Account balances
    • IBANs or account numbers
    • Transaction descriptions
    • Personal identifiers
    • API keys or tokens

FOR DEBUG LOGGING: When using RUST_LOG=debug:

  • STRUCTURED LOGGING shows HTTP requests, responses, and errors
  • NO SENSITIVE DATA is logged (financial amounts, personal info, tokens)
  • REQUEST TRACING includes method, URL, status codes, and error details

Compliance Protocol for AI Agent Debugging

When debugging financial data issues with AI agents:

  1. Create Anonymized Test Scripts: Write small, focused scripts that extract only the necessary data structure information
  2. Use Mock Data: Replace real financial values with placeholder data
  3. Validate Structure, Not Values: Focus on data structure integrity, not actual financial content
  4. Sanitize All Outputs: Ensure any debugging output masks sensitive information

Debug Logging

The application uses structured logging with the tracing crate:

  • Normal operation: Uses INFO level logging for key operations
  • Debug mode: Set RUST_LOG=debug to see detailed HTTP request/response logging
  • No sensitive data: Financial amounts and personal information are never logged
  • Request tracing: HTTP method, URL, status codes, and error responses are logged
// ✅ GOOD: Structure validation with mock data
fn validate_transaction_structure() {
    let mock_tx = BankTransaction {
        amount: Decimal::new(12345, 2), // Mock amount
        currency: "EUR".to_string(),
        // ... other fields with mock data
    };
    // Validate structure only
}

// ❌ BAD: Exposing real financial data
fn debug_real_transactions(transactions: Vec<BankTransaction>) {
    for tx in transactions {
        println!("Real amount: {}", tx.amount); // SECURITY VIOLATION
    }
}

Rust Development Best Practices

Error Handling

  • Use thiserror for domain-specific error types in core modules
  • Use anyhow for application-level error context and propagation
  • Never use panic! in production code - handle errors gracefully
  • Implement From traits for error type conversions
// Core domain errors
#[derive(Error, Debug)]
pub enum SyncError {
    #[error("Failed to fetch transactions from source: {0}")]
    SourceError(#[from] anyhow::Error),
    #[error("Failed to store transaction: {0}")]
    DestinationError(#[from] anyhow::Error),
}

Async Programming

  • Use tokio as the async runtime (workspace dependency)
  • Prefer async-trait for trait methods that need to be async
  • Handle cancellation properly with select! or tokio::time::timeout
  • Use ? operator for error propagation in async functions

Testing Strategy

  • Unit Tests: Test pure functions and business logic in isolation
  • Integration Tests: Test adapter implementations with wiremock
  • Mock External Dependencies: Use mockall for trait-based testing
  • Test Fixtures: Store sample JSON responses in tests/fixtures/
#[cfg(test)]
mod tests {
    use super::*;
    use mockall::predicate::*;
    
    #[tokio::test]
    async fn test_sync_with_mock_source() {
        let mut mock_source = MockTransactionSource::new();
        // Setup mock expectations
        // Test core logic
    }
}

Code Organization

  • Workspace Dependencies: Define common dependencies in root Cargo.toml
  • Feature Flags: Use features for optional functionality
  • Module Structure: Keep modules focused and single-responsibility
  • Public API: Minimize public surface area; prefer internal modules

Dependencies and Patterns

Key Workspace Dependencies:

  • tokio: Async runtime with full features
  • reqwest: HTTP client with JSON support
  • serde/serde_json: Serialization/deserialization
  • chrono: Date/time handling with serde support
  • rust_decimal: Precise decimal arithmetic for financial data
  • tracing/tracing-subscriber: Structured logging
  • clap: CLI argument parsing with derive macros
  • anyhow/thiserror: Error handling
  • async-trait: Async trait support
  • wiremock: HTTP mocking for tests
  • mockall: Runtime mocking for tests

Development Workflow

1. Code Development

  • Write code in appropriate modules following the hexagonal architecture
  • Keep core business logic separate from external integrations
  • Use workspace dependencies consistently
  • When working from a spec, update the spec with the current status as soon as you finish something
  • MANDATORY: After making ANY code change, complete the Post-Change Verification Checklist (see Code Quality section below)

2. Testing

  • Write tests alongside code in #[cfg(test)] modules
  • Test both happy path and error conditions
  • Use mock objects for external dependencies
  • Ensure all tests pass: cargo test --workspace

3. Code Quality

  • Follow Rust idioms and conventions
  • Use cargo fmt for formatting
  • Use cargo clippy for linting
  • Ensure documentation for public APIs
  • MANDATORY: ALWAYS format and lint after making ANY change, and fix ALL linting errors and warnings
  • When a change is end-user visible, update the README.md. Use the README.md documentation guidelines
  • Always clean up unused code. No todo's or unused code is allowed after a change. Remove unused variables, functions, imports, etc. Do NOT hide unused code with underscores - delete it!

Post-Change Verification Checklist (MANDATORY)

After making ANY code change, you MUST run these commands and fix any issues:

  1. Format code: cargo fmt --all
  2. Run linter: cargo clippy --all-targets --all-features -- -D warnings
  3. Run tests: cargo test --workspace
  4. Build project: cargo build --workspace
  5. Clean up unused code: Remove any unused variables, functions, imports, etc.

FAILURE TO COMPLETE THIS CHECKLIST WILL RESULT IN CODE REJECTION

4. Commit Standards

  • Always ensure the workspace compiles: cargo build --workspace
  • Commit both code and tests together
  • Write clear, descriptive commit messages, focusing on user benefits over technical details. Use prose over bullet points

Version Control

  • Use JJ (Jujutsu) as the primary tool for all source control operations due to its concurrency and conflict-free design. Use a specialized agent if available
  • Git fallback: Only for complex operations unsupported by JJ (e.g., interactive rebasing)

Project Structure Guidelines

Core Module (banks2ff/src/core/)

  • models.rs: Domain entities (BankTransaction, Account)
  • ports.rs: Trait definitions (TransactionSource, TransactionDestination)
  • sync.rs: Business logic orchestration

Adapters Module (banks2ff/src/adapters/)

  • gocardless/: GoCardless API integration
  • firefly/: Firefly III API integration
  • Each adapter implements the appropriate port trait

Commands Module (banks2ff/src/commands/)

  • sync.rs: Sync command handler
  • accounts/: Account management commands
    • mod.rs: Account command dispatch
    • link.rs: Account linking logic and LinkCommands dispatch
    • list.rs: Account listing handler
    • status.rs: Account status handler
  • transactions/: Transaction management commands
    • mod.rs: Transaction command dispatch
    • list.rs: Transaction listing handler
    • cache.rs: Cache status handler
    • clear.rs: Cache clearing handler
  • list.rs: Source/destination listing handler

Client Libraries

  • gocardless-client/: Standalone GoCardless API wrapper
  • firefly-client/: Standalone Firefly III API wrapper
  • Both use reqwest for HTTP communication

Security Considerations

  • Never log sensitive data: Use tracing filters to exclude financial information
  • Environment variables: Store credentials in .env file (never in code)
  • Input validation: Validate all external data before processing
  • Error messages: Don't expose sensitive information in error messages

Performance Considerations

  • Caching: Use caching to reduce API calls (see GoCardlessAdapter)
  • Rate Limiting: Handle 429 responses gracefully
  • Batch Processing: Process transactions in reasonable batches
  • Async Concurrency: Use tokio for concurrent operations where appropriate

Observability

  • Structured Logging: Use tracing with spans for operations
  • Error Context: Provide context in error messages for debugging
  • Metrics: Consider adding metrics for sync operations
  • Log Levels: Use appropriate log levels (debug, info, warn, error)

Documentation Guidelines

README.md

  • Keep High-Level: Focus on user benefits and key features, not technical implementation details
  • User-Centric: Describe what the tool does and why users would want it
  • Skip Implementation Details: Avoid technical jargon, architecture specifics, or internal implementation that users don't need to know
  • Feature Descriptions: Use concise, benefit-focused language (e.g., "Robust Error Handling" rather than "Implements EUA expiry detection with multiple requisition fallback")

Technical Documentation

  • docs/architecture.md: Detailed technical specifications, implementation details, and developer-focused content
  • specs/: Implementation planning, API specifications, and historical context
  • Code Comments: Use sparingly for implementation details. Do explain complex logic