version: 1
rules:
  - id: pywatt-module-creation
    # Rule to guide the creation of PyWatt modules using the Rust SDK.
    goal: Create a PyWatt Rust module.
    keywords:
      - pywatt
      - module
      - sdk
      - rust
      - axum
      - serve_module
      - AppState
      - #[pywatt::module]
      - secret
      - announce
    matches:
      language: rust
      text_query: "(create|build|develop|make) .*pywatt module"
    # Key information about creating PyWatt modules
    information: |
      **Goal:** Develop a standalone Rust service (module) managed by the PyWatt orchestrator, typically exposing an Axum-based HTTP API.

      **Core Components:**
      * `pywatt_sdk`: The Rust SDK providing necessary functions and types.
      * `axum`: Web framework for building the module's API.
      * `tokio`: Async runtime.
      * `serde`: For JSON serialization/deserialization (IPC, API).
      * `tracing`: For logging.
      * `secrecy`: For handling sensitive data (`SecretString`, `Secret<T>`).

      **Development Approaches:**
      1. **`#[pywatt::module]` Macro (Recommended):**
         * Requires `proc_macros` feature.
         * Annotate an `async fn(AppState<()>) -> Router`.
         * Macro generates `main` function, handles bootstrap, IPC, serving.
         * Simplifies boilerplate significantly.
         * Manages state via `AppState<()>` (SDK state only) + separate custom state (e.g., `Arc<Mutex<T>>`, Axum extensions).
         * Attributes: `secrets = [...]`, `health = "..."`, `metrics = bool`, `version = "vX"`.
         * Requires `build.rs` with `pywatt_sdk::build::emit_build_info()` for default health endpoint.
      2. **`serve_module` Function (Manual):**
         * Provides full control over initialization.
         * Write `#[tokio::main] async fn main() -> Result<()>`.
         * Define initial `secret_keys: Vec<String>`.
         * Define `announced_endpoints: Vec<AnnouncedEndpoint>` OR use `discover_endpoints` feature (pass `vec![]`).
         * Provide `state_builder: Fn(&OrchestratorInit, Vec<SecretString>) -> T` closure.
         * Provide `router_builder: Fn(AppState<T>) -> Router` closure.
         * Call `serve_module(secret_keys, announced_endpoints, state_builder, router_builder).await?`.

      **Key Types & Structs:**
      * `AppState<T>`: Shared state (module_id, orchestrator_api, secret_client, user_state: T). Passed via `Extension` layer.
      * `SecretClient`: Client for secret operations (`Arc<SecretClient>`).
      * `OrchestratorInit`: Initial data from orchestrator (via `read_init()`).
      * `ModuleAnnounce`: Message sent to orchestrator (via `send_announce()`).
      * `AnnouncedEndpoint`: Describes a single API endpoint for announcement.
      * `SecretString`, `Secret<T>`: Wrapper types for sensitive data.
      * `Error`: SDK error enum.
      * `Result<T>`: Alias for `Result<T, pywatt_sdk::Error>`.

      **Key Functions & Macros:**
      * `#[pywatt::module]`: Proc macro for simplified setup.
      * `serve_module(...)`: Core function for manual setup.
      * `pywatt_sdk::prelude::*`: Imports common items.
      * `init_module()`: Initializes logging (called by `serve_module`/macro).
      * `read_init()`: Reads handshake info from stdin.
      * `get_module_secret_client()`: Creates `SecretClient`.
      * `get_secret()`, `get_secrets()`, `get_typed_secret()`: Fetch secrets.
      * `subscribe_secret_rotations()`: Register callback for secret updates.
      * `send_announce()`: Send endpoint info to orchestrator.
      * `process_ipc_messages()`: Background task listening on stdin (rotations, shutdown).
      * `build::emit_build_info()`: Used in `build.rs` for build metadata.

      **Important SDK Features (Cargo.toml):**
      * `proc_macros`: Enables `#[pywatt::module]`.
      * `router_ext`: Enables `RouterExt` trait (`.with_default_health()`, `.with_prometheus_metrics()`).
      * `metrics`: Enables metrics endpoint functionality.
      * `jwt_auth`: Provides JWT authentication tools.
      * `discover_endpoints`: Enables automatic endpoint announcement from Router.
      * `builder`: Enables `ModuleBuilder`, `AppStateBuilder`.

      **Module Lifecycle:**
      Start -> Logging Init -> Handshake (`read_init`) -> Secret Client Init -> Initial Secret Fetch -> State Build -> Router Build -> Announce (`send_announce`) -> IPC Loop (`process_ipc_messages`) -> Serve -> Shutdown (via IPC).

      **Setup:**
      * `Cargo.toml`: Add `pywatt_sdk` with desired features, `tokio`, `axum`, `serde`, `tracing`, `secrecy`.
      * `build.rs`: Add if using `#[pywatt::module]` or `RouterExt::with_default_health()`.
      * `src/main.rs`: Entry point for module logic.

      **State Management (Axum):**
      * Use `Extension<AppState<T>>` layer/extractor to access shared state in handlers.
      * For macro (`AppState<()>`): Manage custom mutable state using `Arc<Mutex<...>>` within an `Extension` or other shared state patterns.
      * For manual (`AppState<T>`): Custom state `T` is part of `AppState` (`.user_state`).

      **Reference:** See `MODULE_CREATION_GUIDE.md` for full details and examples.

  - id: pywatt-database-protocol
    # Rule to guide the usage of the database protocol within the PyWatt SDK
    goal: Use the standardized database protocol within PyWatt modules.
    keywords:
      - database
      - postgres
      - mysql
      - sqlite
      - sqlx
      - connection
      - query
      - transaction
      - DatabaseConnection
      - DatabaseConfig
    matches:
      language: rust
      text_query: "(database|DB|sql) .*(connection|query|transaction)"
    # Key information about using the database protocol
    information: |
      **Goal:** Standardized database access for PyWatt modules using a database-agnostic API.

      **Core Components:**
      * `database` feature: Enable the database protocol in your module.
      * `postgres`, `mysql`, `sqlite` features: Enable specific database backend support.
      * `sqlx`: Underlying library used for database interactions.

      **Setup in Cargo.toml:**
      ```toml
      [dependencies]
      pywatt_sdk = { version = "0.2.5", features = ["database", "postgres"] }
      ```

      **Key Types & Traits:**
      * `DatabaseConfig`: Configuration structure for database connections.
      * `DatabaseConnection`: Core trait for database operations.
      * `DatabaseTransaction`: Trait for transaction operations.
      * `DatabaseRow`: Trait for accessing data from query results.
      * `DatabaseValue`: Enum representing parameter values for queries.
      * `DatabaseError`: Error type for database operations.
      * `DatabaseResult<T>`: Alias for `Result<T, DatabaseError>`.
      * `DatabaseType`: Enum for supported database types (Postgres, MySQL, SQLite).

      **Usage Pattern:**
      1. **Create Configuration:**
         ```rust
         let config = DatabaseConfig {
             db_type: DatabaseType::Postgres,
             host: Some("localhost".to_string()),
             port: Some(5432),
             database: "my_database".to_string(),
             username: Some("user".to_string()),
             password: Some("password".to_string()),
             ssl_mode: None,
             pool: PoolConfig::default(),
             extra_params: HashMap::new(),
         };
         ```

      2. **Establish Connection:**
         ```rust
         let connection = create_database_connection(&config).await?;
         ```

      3. **Execute Queries:**
         ```rust
         // Simple execution (no return)
         let affected_rows = connection.execute("INSERT INTO users (name) VALUES ($1)", 
                                               &[DatabaseValue::Text("John".to_string())]).await?;
         
         // Query returning rows
         let rows = connection.query("SELECT id, name FROM users", &[]).await?;
         for row in rows {
             let id: i64 = row.get("id")?;
             let name: String = row.get("name")?;
             println!("User: {} (ID: {})", name, id);
         }
         
         // Query returning a single row
         if let Some(row) = connection.query_one("SELECT * FROM users WHERE id = $1", 
                                                &[DatabaseValue::Integer(1)]).await? {
             let name: String = row.get("name")?;
             println!("Found user: {}", name);
         }
         ```

      4. **Transactions:**
         ```rust
         let transaction = connection.begin_transaction().await?;
         
         transaction.execute("INSERT INTO users (name) VALUES ($1)", 
                           &[DatabaseValue::Text("Alice".to_string())]).await?;
                           
         transaction.execute("UPDATE user_count SET count = count + 1", &[]).await?;
         
         transaction.commit().await?; // Or transaction.rollback().await? to abort
         ```

      **Integration with AppState:**
      ```rust
      #[pywatt::module(secrets = ["DB_PASSWORD"])]
      async fn module(state: AppState<()>) -> Router {
          // Get database password from secrets
          let db_password = get_secret(&state, "DB_PASSWORD").await.unwrap();
          
          // Create database configuration
          let config = DatabaseConfig {
              db_type: DatabaseType::Postgres,
              host: Some("db.example.com".to_string()),
              port: Some(5432),
              database: "module_db".to_string(),
              username: Some("app_user".to_string()),
              password: Some(db_password.expose_secret().to_string()),
              ssl_mode: Some("prefer".to_string()),
              pool: PoolConfig::default(),
              extra_params: HashMap::new(),
          };
          
          // Create connection and store in application state
          let db = create_database_connection(&config).await.unwrap();
          let db_state = Arc::new(db);
          
          // Create router with database access
          Router::new()
              .route("/data", get(get_data))
              .route("/data", post(create_data))
              .layer(Extension(state))
              .layer(Extension(db_state))
      }
      
      async fn get_data(
          Extension(db): Extension<Arc<Box<dyn DatabaseConnection>>>,
      ) -> impl IntoResponse {
          let rows = db.query("SELECT * FROM data", &[]).await.unwrap();
          // Process rows and return response...
      }
      ```

      **Database-Specific Features:**
      * **PostgreSQL:** Enables the most advanced features (arrays, JSON, etc.)
      * **MySQL:** Provides compatibility with MySQL and MariaDB
      * **SQLite:** Lightweight embedded database, good for development/testing

      **Handling Migrations:**
      * For schema migrations, consider using `sqlx` migration tools directly, or libraries like `refinery`.
      * You can execute migration SQL through the `DatabaseConnection` interface or use `sqlx` CLI tools.

      **Error Handling:**
      ```rust
      match result {
          Ok(data) => /* Handle success */,
          Err(DatabaseError::Connection(msg)) => /* Handle connection error */,
          Err(DatabaseError::Query(msg)) => /* Handle query error */,
          Err(e) => /* Handle other errors */,
      }
      ```

      **Monitoring & Performance:**
      * Connection pooling is handled automatically with configurable settings
      * Use the `ping()` method for health checks
      * Consider timing query execution for performance monitoring 

  - id: pywatt-cache-protocol
    # Rule to guide the usage of the cache protocol within the PyWatt SDK
    goal: Use the standardized cache protocol within PyWatt modules.
    keywords:
      - cache
      - redis
      - memcached
      - in-memory
      - file
      - CacheService
      - CacheConfig
      - cache_aside
      - write_through
      - distributed lock
    matches:
      language: rust
      text_query: "(cache|caching) .*(service|connection|store|redis|memcached)"
    # Key information about using the cache protocol
    information: |
      **Goal:** Standardized cache access for PyWatt modules using a cache-agnostic API.

      **Core Components:**
      * `cache` feature: Enable the cache protocol in your module.
      * `redis_cache`, `memcached`, `file_cache` features: Enable specific backend support.
      * In-memory cache is always available with the `cache` feature.

      **Setup in Cargo.toml:**
      ```toml
      [dependencies]
      pywatt_sdk = { version = "0.2.5", features = ["cache", "redis_cache"] }
      ```

      **Key Types & Traits:**
      * `CacheConfig`: Configuration structure for cache connections.
      * `CacheService`: Core trait for cache operations.
      * `CacheError`: Error type for cache operations.
      * `CacheResult<T>`: Alias for `Result<T, CacheError>`.
      * `CacheType`: Enum for supported cache types (Redis, Memcached, InMemory, File).
      * `CacheStats`: Structure for cache statistics.

      **Usage Pattern:**
      1. **Create Configuration:**
         ```rust
         use std::collections::HashMap;
         let config = CacheConfig {
             cache_type: CacheType::Redis,
             hosts: vec!["localhost".to_string()],
             port: Some(6379),
             connection_timeout_seconds: 5,
             operation_timeout_seconds: 2,
             default_ttl_seconds: 3600, // 1 hour
             username: None,
             password: Some("password".to_string()),
             pool: PoolConfig::default(),
             tls_enabled: false,
             file_path: None,  // Only for file-based cache
             max_size_bytes: None,
             namespace: Some("my-app".to_string()),
             extra_params: HashMap::new(),
         };
         ```

      2. **Establish Connection:**
         ```rust
         let cache = create_cache_service(&config).await?;
         ```

      3. **Basic Operations:**
         ```rust
         // Set a value with a specific TTL
         cache.set("user:123", user_data.as_bytes(), Some(Duration::from_secs(3600))).await?;
         
         // Get a value
         if let Some(data) = cache.get("user:123").await? {
             // Use data...
         }
         
         // Delete a value
         let deleted = cache.delete("user:123").await?;
         
         // Check if a key exists
         let exists = cache.exists("user:123").await?;
         
         // Set only if key doesn't exist
         let set = cache.set_nx("counter", b"1", None).await?;
         
         // Get and set in a single operation
         let old_value = cache.get_set("user:123", new_user_data.as_bytes()).await?;
         
         // Increment a counter
         let new_count = cache.increment("visitor_count", 1).await?;
         
         // Decrement a counter
         let new_stock = cache.decrement("product:123:stock", 1).await?;
         ```

      4. **Bulk Operations:**
         ```rust
         // Set multiple values
         let mut items = HashMap::new();
         items.insert("key1".to_string(), b"value1".to_vec());
         items.insert("key2".to_string(), b"value2".to_vec());
         cache.set_many(&items, Some(Duration::from_secs(3600))).await?;
         
         // Get multiple values
         let keys = vec!["key1".to_string(), "key2".to_string()];
         let values = cache.get_many(&keys).await?;
         
         // Delete multiple keys
         let deleted_count = cache.delete_many(&keys).await?;
         
         // Clear cache (with optional namespace)
         cache.clear(Some("user:")).await?; // Clear keys in the "user:" namespace
         ```

      5. **Distributed Locking:**
         ```rust
         // Acquire a lock
         if let Some(token) = cache.lock("critical-section", Duration::from_secs(30)).await? {
             // Do critical operation...
             
             // Release the lock
             cache.unlock("critical-section", &token).await?;
         }
         ```

      **Integration with AppState:**
      ```rust
      #[pywatt::module(secrets = ["REDIS_PASSWORD"])]
      async fn module(state: AppState<()>) -> Router {
          // Get cache password from secrets
          let redis_password = get_secret(&state, "REDIS_PASSWORD").await.unwrap();
          
          // Create cache configuration
          let config = CacheConfig {
              cache_type: CacheType::Redis,
              hosts: vec!["redis.example.com".to_string()],
              port: Some(6379),
              connection_timeout_seconds: 5,
              operation_timeout_seconds: 2,
              default_ttl_seconds: 3600,
              username: None,
              password: Some(redis_password.expose_secret().to_string()),
              pool: PoolConfig::default(),
              tls_enabled: true,
              namespace: Some("my-module".to_string()),
              file_path: None,
              max_size_bytes: None,
              extra_params: HashMap::new(),
          };
          
          // Create cache service and store in application state
          let cache = create_cache_service(&config).await.unwrap();
          let cache_service = Arc::new(cache);
          
          // Create router with cache access
          Router::new()
              .route("/data", get(get_data))
              .route("/data", post(create_data))
              .layer(Extension(state))
              .layer(Extension(cache_service))
      }
      
      async fn get_data(
          Extension(cache): Extension<Arc<Box<dyn CacheService>>>,
          Query(params): Query<HashMap<String, String>>,
      ) -> impl IntoResponse {
          let key = format!("data:{}", params.get("id").unwrap_or(&"default".to_string()));
          
          if let Some(cached_data) = cache.get(&key).await.unwrap() {
              // Return cached data
              return Json(serde_json::from_slice(&cached_data).unwrap());
          }
          
          // Fetch from source, cache, and return
          // ...
      }
      ```

      **Advanced Caching Patterns:**
      1. **Cache-Aside Pattern:**
         ```rust
         let user: User = cache_aside(
             &cache,
             &format!("user:{}", user_id),
             || async { 
                 // This will only be called if item is not in cache
                 fetch_user_from_database(user_id).await 
             },
             Some(Duration::from_secs(3600))
         ).await?;
         ```

      2. **Write-Through Pattern:**
         ```rust
         write_through(
             &cache,
             &format!("user:{}", user.id),
             &user,
             || async {
                 // Update source (database)
                 save_user_to_database(&user).await
             },
             Some(Duration::from_secs(3600))
         ).await?;
         ```

      3. **Distributed Locking (with DistributedLock):**
         ```rust
         let mut lock = DistributedLock::new(
             Arc::clone(&cache),
             "inventory:lock",
             Duration::from_secs(30),
             None  // Default retry config
         );
         
         let result = lock.with_lock(|| async {
             // Critical section code here...
             Ok(result)
         }).await?;
         ```

      4. **Cache Invalidation Strategies:**
         ```rust
         // TTL-based invalidation
         let ttl_strategy = TtlInvalidationStrategy::new(
             "product", 
             Duration::from_secs(3600)
         );
         
         // Event-based invalidation with versioning
         let event_strategy = EventInvalidationStrategy::new(
             "product", 
             Duration::from_secs(3600),
             true  // Use versioning
         );
         
         // Invalidate a specific entity
         event_strategy.invalidate(&cache, "product", "123").await?;
         
         // Invalidate all entities of a type
         event_strategy.invalidate_type(&cache, "product").await?;
         ```

      **Cache Backend Features:**
      * **Redis:** Full-featured distributed cache with pub/sub, atomic operations, etc.
      * **Memcached:** Simple, high-performance distributed cache.
      * **InMemory:** Local cache, great for development and testing.
      * **File:** Local persistent cache, suitable for development.

      **Error Handling:**
      ```rust
      match result {
          Ok(data) => /* Handle success */,
          Err(CacheError::Connection(msg)) => /* Handle connection error */,
          Err(CacheError::Operation(msg)) => /* Handle operation error */,
          Err(CacheError::Timeout(msg)) => /* Handle timeout */,
          Err(e) => /* Handle other errors */,
      }
      ```

      **Monitoring & Performance:**
      * Use `cache.stats().await?` to get cache statistics (hits, misses, memory usage)
      * Use `cache.ping().await?` for health checks
      * Consider namespacing and expiration policies for effective cache management 

  - id: model-manager
    display_name: "Model Manager"
    description: |-
      Database schema modeling toolkit with adapter pattern.
      * SQLite, MySQL and PostgreSQL adapters are fully implemented.
      * Define models once, generate SQL for any supported database.
      * Integrates with PyWatt DatabaseConnection API.
    keywords:
      - model_manager
      - ModelManager
      - ModelDescriptor
      - DataType
      - DatabaseAdapter
      - apply_model
      - sync_schema
    matches:
      language: rust
      include_pattern: "src/model_manager/**"
    information: |-
      The Model Manager provides database-agnostic schema modeling:
      
      ```rust
      // Define your model
      let model = ModelDescriptor {
          name: "users".to_string(),
          columns: vec![
              ColumnDescriptor {
                  name: "id".to_string(),
                  data_type: DataType::Integer(IntegerSize::I32),
                  is_primary_key: true,
                  auto_increment: true,
                  ..Default::default()
              },
              // More columns...
          ],
          ..Default::default()
      };
      
      // Apply it to your database
      database.apply_model(&model).await?;
      ```
      
      Documentation: docs/model_manager.md

  - id: rust-sdk-cli-database-tool
    goal: Provide CLI for database and model management
    keywords:
      - database-tool
      - Schema
      - Model
      - Validate
      - Generate
      - Apply
      - Drop
    matches:
      language: rust
      include_pattern: "src/bin/database_tool.rs"
    information: |
      **Goal:** CLI for generating and applying database schemas and models.
      * Schema commands:
        - `database-tool schema generate --model-file FILE --database-type TYPE --output OUTPUT`
        - `database-tool schema apply --model-file FILE --database-config CONFIG`
      * Model commands:
        - `database-tool model validate --model-file FILE`
        - `database-tool model generate --model-file FILE --output OUTPUT`
        - `database-tool model apply --model-file FILE --database-config CONFIG`
        - `database-tool model drop --table TABLE --schema SCHEMA --database-config CONFIG`