# TICKET-PMAT-5024: Quality Gate Configuration Management

**Status**: GREEN
**Priority**: P0
**Complexity**: 3
**Estimated Time**: 1 hour
**Dependencies**: TICKET-PMAT-5020 (gate executor), TICKET-PMAT-5023 (CLI commands)
**Sprint**: Sprint 18 - Quality Gate Automation

## Objective

Provide commands to generate, validate, and manage `.pmat-gates.toml` configuration files. This completes the quality gate system by making configuration easy to customize and share across projects.

## Success Criteria

- [ ] `pmat quality-gates init` generates default .pmat-gates.toml
- [ ] `pmat quality-gates validate` validates configuration file
- [ ] `pmat quality-gates show` displays current configuration
- [ ] Generated TOML includes comments explaining each option
- [ ] Validation catches invalid values (negative timeouts, invalid percentages)
- [ ] All quality gates pass (complexity <10, coverage >80%, no SATD)

## Test Strategy

### Unit Tests
- [ ] `test_generate_default_config` - Generate default TOML
- [ ] `test_validate_valid_config` - Valid config passes
- [ ] `test_validate_invalid_coverage` - Catch out-of-range coverage
- [ ] `test_validate_invalid_timeout` - Catch negative timeout
- [ ] `test_show_config` - Display configuration

### Integration Tests
- [ ] `integration_init_config` - Generate config file on disk
- [ ] `integration_validate_file` - Validate file from disk

## Quality Gates

- [ ] Cyclomatic complexity <10 for all functions
- [ ] Cognitive complexity <15 for all functions
- [ ] Line coverage >80%
- [ ] Branch coverage >80%
- [ ] 0 SATD violations
- [ ] 0 clippy warnings
- [ ] All tests pass

## Implementation Plan

### Phase 1: Configuration Template

```rust
// server/src/quality/config.rs

/// Generate default .pmat-gates.toml configuration
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 1
pub fn generate_default_config() -> String {
    r#"# PMAT Quality Gate Configuration
# Generated by: pmat quality-gates init

[gates]
# Run clippy linter
run_clippy = true

# Enforce strict clippy (-D warnings)
clippy_strict = true

# Run test suite
run_tests = true

# Test timeout in seconds
test_timeout = 300

# Check code coverage
check_coverage = true

# Minimum coverage percentage (0-100)
min_coverage = 80.0

# Check cyclomatic complexity
check_complexity = true

# Maximum cyclomatic complexity per function
max_complexity = 10
"#
    .to_string()
}
```

### Phase 2: Validation

```rust
/// Validate gate configuration
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 5
pub fn validate_config(config: &GateConfig) -> Result<(), Vec<String>> {
    let mut errors = Vec::new();

    if config.min_coverage < 0.0 || config.min_coverage > 100.0 {
        errors.push(format!(
            "min_coverage must be 0-100, got {}",
            config.min_coverage
        ));
    }

    if config.test_timeout == 0 {
        errors.push("test_timeout must be > 0".to_string());
    }

    if config.max_complexity == 0 {
        errors.push("max_complexity must be > 0".to_string());
    }

    if errors.is_empty() {
        Ok(())
    } else {
        Err(errors)
    }
}
```

### Phase 3: CLI Subcommands

```rust
// Update server/src/cli/commands.rs

/// Run quality gates on the current project (TICKET-PMAT-5023)
#[command(name = "quality-gates")]
QualityGates {
    #[command(subcommand)]
    command: Option<QualityGatesCommand>,

    /// Path to quality gate configuration file
    #[arg(long, default_value = ".pmat-gates.toml")]
    config: PathBuf,

    /// Generate markdown report (only when no subcommand)
    #[arg(long)]
    report: bool,

    /// Output JSON format (only when no subcommand)
    #[arg(long)]
    json: bool,

    /// Project directory
    #[arg(long, default_value = ".")]
    project_dir: PathBuf,
}

/// Quality gates subcommands
#[derive(Subcommand)]
pub enum QualityGatesCommand {
    /// Initialize .pmat-gates.toml with defaults
    Init {
        /// Force overwrite existing file
        #[arg(long)]
        force: bool,
    },

    /// Validate configuration file
    Validate,

    /// Show current configuration
    Show {
        /// Output format
        #[arg(long, value_enum, default_value = "toml")]
        format: ConfigFormat,
    },
}

#[derive(Clone, Debug, clap::ValueEnum)]
pub enum ConfigFormat {
    Toml,
    Json,
}
```

### Phase 4: Handler Implementation

```rust
// server/src/cli/handlers/quality_gates_handler.rs (extend)

/// Handle quality-gates subcommands
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 4
pub async fn handle_quality_gates_command(
    command: Option<QualityGatesCommand>,
    config_path: PathBuf,
    report: bool,
    json: bool,
    project_dir: PathBuf,
) -> Result<()> {
    match command {
        Some(QualityGatesCommand::Init { force }) => {
            handle_init_config(&config_path, force)?;
        }
        Some(QualityGatesCommand::Validate) => {
            handle_validate_config(&config_path)?;
        }
        Some(QualityGatesCommand::Show { format }) => {
            handle_show_config(&config_path, format)?;
        }
        None => {
            // Original behavior: run gates
            run_quality_gates(config_path, report, json, project_dir).await?;
        }
    }
    Ok(())
}

/// Initialize configuration file
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 3
fn handle_init_config(path: &Path, force: bool) -> Result<()> {
    if path.exists() && !force {
        return Err(anyhow::anyhow!(
            "Configuration file already exists. Use --force to overwrite."
        ));
    }

    let config = generate_default_config();
    std::fs::write(path, config)?;

    println!("✅ Created {}", path.display());
    Ok(())
}

/// Validate configuration file
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 3
fn handle_validate_config(path: &Path) -> Result<()> {
    let config = load_config_from_file(&path.to_path_buf())?;

    match validate_config(&config) {
        Ok(()) => {
            println!("✅ Configuration is valid");
            Ok(())
        }
        Err(errors) => {
            println!("❌ Configuration has {} error(s):", errors.len());
            for error in errors {
                println!("  - {}", error);
            }
            std::process::exit(1);
        }
    }
}

/// Show configuration
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 2
fn handle_show_config(path: &Path, format: ConfigFormat) -> Result<()> {
    let config = load_config_from_file(&path.to_path_buf())?;

    match format {
        ConfigFormat::Toml => {
            let toml = generate_config_toml(&config);
            println!("{}", toml);
        }
        ConfigFormat::Json => {
            let json = serde_json::to_string_pretty(&config)?;
            println!("{}", json);
        }
    }

    Ok(())
}

/// Generate TOML from config struct
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 1
fn generate_config_toml(config: &GateConfig) -> String {
    format!(
        r#"[gates]
run_clippy = {}
clippy_strict = {}
run_tests = {}
test_timeout = {}
check_coverage = {}
min_coverage = {}
check_complexity = {}
max_complexity = {}
"#,
        config.run_clippy,
        config.clippy_strict,
        config.run_tests,
        config.test_timeout,
        config.check_coverage,
        config.min_coverage,
        config.check_complexity,
        config.max_complexity
    )
}
```

### Phase 5: Tests

```rust
#[cfg(test)]
mod config_tests {
    use super::*;

    #[test]
    fn test_generate_default_config() {
        let config = generate_default_config();

        assert!(config.contains("[gates]"));
        assert!(config.contains("run_clippy = true"));
        assert!(config.contains("min_coverage = 80.0"));
        assert!(config.contains("max_complexity = 10"));
    }

    #[test]
    fn test_validate_valid_config() {
        let config = GateConfig::default();
        assert!(validate_config(&config).is_ok());
    }

    #[test]
    fn test_validate_invalid_coverage_too_high() {
        let mut config = GateConfig::default();
        config.min_coverage = 150.0;

        let result = validate_config(&config);
        assert!(result.is_err());
        let errors = result.unwrap_err();
        assert_eq!(errors.len(), 1);
        assert!(errors[0].contains("min_coverage"));
    }

    #[test]
    fn test_validate_invalid_coverage_negative() {
        let mut config = GateConfig::default();
        config.min_coverage = -10.0;

        let result = validate_config(&config);
        assert!(result.is_err());
    }

    #[test]
    fn test_validate_zero_timeout() {
        let mut config = GateConfig::default();
        config.test_timeout = 0;

        let result = validate_config(&config);
        assert!(result.is_err());
        let errors = result.unwrap_err();
        assert!(errors.iter().any(|e| e.contains("test_timeout")));
    }

    #[test]
    fn test_validate_zero_complexity() {
        let mut config = GateConfig::default();
        config.max_complexity = 0;

        let result = validate_config(&config);
        assert!(result.is_err());
    }

    #[test]
    fn test_generate_config_toml() {
        let config = GateConfig {
            run_clippy: true,
            clippy_strict: false,
            run_tests: true,
            test_timeout: 120,
            check_coverage: true,
            min_coverage: 85.0,
            check_complexity: true,
            max_complexity: 8,
        };

        let toml = generate_config_toml(&config);

        assert!(toml.contains("run_clippy = true"));
        assert!(toml.contains("clippy_strict = false"));
        assert!(toml.contains("test_timeout = 120"));
        assert!(toml.contains("min_coverage = 85"));
        assert!(toml.contains("max_complexity = 8"));
    }

    #[test]
    #[ignore] // Integration test
    fn integration_init_config() {
        use tempfile::TempDir;

        let temp_dir = TempDir::new().unwrap();
        let config_path = temp_dir.path().join(".pmat-gates.toml");

        handle_init_config(&config_path, false).unwrap();

        assert!(config_path.exists());
        let content = std::fs::read_to_string(&config_path).unwrap();
        assert!(content.contains("[gates]"));
    }

    #[test]
    #[ignore] // Integration test
    fn integration_validate_file() {
        use tempfile::NamedTempFile;
        use std::io::Write;

        let mut temp_file = NamedTempFile::new().unwrap();
        writeln!(
            temp_file,
            r#"
[gates]
run_clippy = true
clippy_strict = true
run_tests = true
test_timeout = 300
check_coverage = true
min_coverage = 80.0
check_complexity = true
max_complexity = 10
"#
        )
        .unwrap();

        let result = handle_validate_config(temp_file.path());
        assert!(result.is_ok());
    }
}
```

## Complexity Analysis

Functions with complexity:
- `generate_default_config`: CC=1
- `validate_config`: CC=5 (4 validation branches + return)
- `handle_quality_gates_command`: CC=4 (3 subcommands + none)
- `handle_init_config`: CC=3 (exists check, force check)
- `handle_validate_config`: CC=3 (load, validate match)
- `handle_show_config`: CC=2 (format match)
- `generate_config_toml`: CC=1

All functions under CC=10 threshold ✓

## Verification Commands

```bash
# Initialize configuration
pmat quality-gates init

# Validate configuration
pmat quality-gates validate

# Show configuration
pmat quality-gates show
pmat quality-gates show --format json

# Run with custom config
pmat quality-gates --config custom-gates.toml

# Test suite
cargo test --lib quality::config
cargo test --lib cli::handlers::quality_gates_handler
```

## Files to Create/Modify

### New Files
- `server/src/quality/config.rs` - Configuration generation and validation

### Modified Files
- `server/src/quality/mod.rs` - Add config module
- `server/src/cli/commands.rs` - Add QualityGatesCommand enum
- `server/src/cli/handlers/quality_gates_handler.rs` - Add subcommand handlers

## Risk Assessment

**Low Risk:**
- Simple TOML generation
- Straightforward validation rules
- Clear error messages

**Mitigation:**
- Comprehensive validation tests
- Clear documentation in generated TOML
- Integration tests with real files

## Notes

This ticket completes the quality gate system:

**Configuration Workflow:**
1. **Init**: `pmat quality-gates init` → Creates `.pmat-gates.toml`
2. **Customize**: Edit TOML file with project-specific values
3. **Validate**: `pmat quality-gates validate` → Check for errors
4. **Use**: `pmat quality-gates` → Run with configuration

**Integration:**
- Pre-commit hooks use `.pmat-gates.toml` (PMAT-5021)
- GitHub Actions reads same config (PMAT-5022)
- CLI commands use config (PMAT-5023)
- Consistent behavior across local/CI environments

**Benefits:**
- Single source of truth for quality standards
- Easy to version control and share
- Flexible per-project customization
- Clear validation feedback

**TDD Cycle Duration**: Estimated 1 hour for RED → GREEN → REFACTOR
