Files
banks2ff/AGENTS.md
Jacob Kiers 3d4ace793d Refine development guidelines for improved code quality
Update the development guide to emphasize best practices including updating specifications during work, mandatory code formatting and linting, README updates for user-visible changes, and cleanup of unused code. This fosters consistent, high-quality contributions that enhance the project's reliability and maintainability.
2025-11-27 21:28:09 +01:00

8.5 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

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
  • ALWAYS format and lint after making a change, and fix 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

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

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