# TICKET-PMAT-5005: Git Initialization and Pre-commit Hooks

**Status**: GREEN ✅
**Priority**: P0
**Complexity**: 8
**Estimated Time**: 4 hours
**Dependencies**: TICKET-PMAT-5001, TICKET-PMAT-5004
**Sprint**: Sprint 16 - Scaffolding Foundation

## Objective

Enhance ScaffoldEngine to install pre-commit hooks that enforce quality gates (complexity, coverage, clippy, tests) automatically. This ensures all scaffolded projects start with extreme TDD quality enforcement from day one.

## Success Criteria

- [ ] ScaffoldEngine generates pre-commit hook scripts
- [ ] Hooks install automatically in `.git/hooks/`
- [ ] Hooks execute quality gates: clippy, tests, complexity checks
- [ ] Fast execution (<30s for typical commits)
- [ ] Support both pforge and WASM project quality gates
- [ ] All quality gates pass (complexity <10, coverage >80%, no SATD)

## Test Strategy

### Unit Tests
- [ ] `test_generate_pre_commit_hook_script` - Generate hook script content
- [ ] `test_install_pre_commit_hook` - Install hook to .git/hooks/
- [ ] `test_hook_script_executable` - Verify hook has executable permissions
- [ ] `test_pforge_quality_gates` - pforge-specific quality checks
- [ ] `test_wasm_quality_gates` - WASM-specific quality checks

### Property Tests
- [ ] Property: Generated hooks are always valid bash scripts
- [ ] Property: Hooks execute in <30 seconds for small changes
- [ ] Property: Hooks detect complexity violations
- [ ] Property: Hooks detect test failures

### Integration Tests
- [ ] `integration_hook_prevents_bad_commit` - Hook blocks commit with failing tests
- [ ] `integration_hook_allows_good_commit` - Hook allows clean commits
- [ ] `integration_hook_execution_time` - Verify performance target

## 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
- [ ] Hook execution time <30s

## Implementation Plan

### Phase 1: Hook Script Generation

```rust
// server/src/scaffold/hooks.rs

/// Pre-commit hook configuration
#[derive(Debug, Clone)]
pub struct HookConfig {
    /// Project type (affects which quality gates run)
    pub project_type: TemplateType,
    /// Quality gates to enforce
    pub quality_gates: QualityGateConfig,
}

/// Generate pre-commit hook script for project type
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 3
pub fn generate_pre_commit_hook(config: &HookConfig) -> Result<String> {
    let script = match config.project_type {
        TemplateType::Agent { .. } => generate_pforge_hook(config),
        TemplateType::Wasm { .. } => generate_wasm_hook(config),
        _ => return Err(ScaffoldError::UnsupportedProjectType),
    };

    Ok(script)
}

/// Generate pforge-specific pre-commit hook
///
/// # Quality Gates
/// - cargo clippy -- -D warnings
/// - cargo test
/// - Complexity check (if enabled)
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 1
fn generate_pforge_hook(config: &HookConfig) -> String {
    format!(
        r#"#!/bin/bash
# Pre-commit hook for pforge project
# Generated by PMAT ScaffoldEngine

set -e

echo "Running pre-commit quality gates..."

# Clippy (strict mode)
echo "→ Running clippy..."
cargo clippy --all-targets --all-features -- -D warnings

# Tests
echo "→ Running tests..."
cargo test

# Complexity check (if enabled)
{}

echo "✓ All quality gates passed!"
exit 0
"#,
        if config.quality_gates.check_complexity {
            "echo \"→ Checking complexity...\"\ncargo pmat complexity --threshold 10"
        } else {
            "# Complexity check disabled"
        }
    )
}

/// Generate WASM-specific pre-commit hook
///
/// # Quality Gates
/// - cargo clippy -- -D warnings
/// - cargo test
/// - WASM build verification
/// - Complexity check (if enabled)
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 1
fn generate_wasm_hook(config: &HookConfig) -> String {
    format!(
        r#"#!/bin/bash
# Pre-commit hook for WASM project
# Generated by PMAT ScaffoldEngine

set -e

echo "Running pre-commit quality gates..."

# Clippy (strict mode)
echo "→ Running clippy..."
cargo clippy --all-targets --all-features -- -D warnings

# Tests
echo "→ Running tests..."
cargo test

# WASM build verification
echo "→ Verifying WASM build..."
cargo build --target wasm32-unknown-unknown --release

# Complexity check (if enabled)
{}

echo "✓ All quality gates passed!"
exit 0
"#,
        if config.quality_gates.check_complexity {
            "echo \"→ Checking complexity...\"\ncargo pmat complexity --threshold 10"
        } else {
            "# Complexity check disabled"
        }
    )
}

/// Install pre-commit hook to project directory
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 3
pub fn install_pre_commit_hook(project_dir: &Path, script: &str) -> Result<()> {
    use std::fs;
    use std::os::unix::fs::PermissionsExt;

    let hook_path = project_dir.join(".git/hooks/pre-commit");

    // Write hook script
    fs::write(&hook_path, script)
        .map_err(ScaffoldError::IoError)?;

    // Make executable (chmod +x)
    #[cfg(unix)]
    {
        let mut perms = fs::metadata(&hook_path)
            .map_err(ScaffoldError::IoError)?
            .permissions();
        perms.set_mode(0o755);
        fs::set_permissions(&hook_path, perms)
            .map_err(ScaffoldError::IoError)?;
    }

    Ok(())
}
```

### Phase 2: Integration with ScaffoldEngine

```rust
// server/src/scaffold/mod.rs

impl ScaffoldEngine {
    /// Full scaffolding workflow with hooks (TICKET-PMAT-5005)
    ///
    /// # Complexity
    /// - Time: O(n*m) where n=templates, m=avg template size
    /// - Cyclomatic: 6 (validation, creation, git, structure, files, hooks)
    pub fn scaffold(&self, config: ScaffoldConfig) -> Result<PathBuf> {
        self.validate_config(&config)?;
        let project_dir = self.create_directory(&config.project_name)?;
        self.init_git(&project_dir)?;

        // TICKET-PMAT-5004: Generate project structure and files
        let registry = self.get_template_registry(&config.template_type);
        self.create_project_structure(&project_dir, &config.template_type)?;
        self.generate_files(&project_dir, &registry, &config)?;

        // TICKET-PMAT-5005: Install pre-commit hooks
        self.install_hooks(&project_dir, &config)?;

        Ok(project_dir)
    }

    /// Install pre-commit hooks
    ///
    /// # Complexity
    /// - Time: O(1)
    /// - Cyclomatic: 2
    fn install_hooks(&self, project_dir: &Path, config: &ScaffoldConfig) -> Result<()> {
        use crate::scaffold::hooks::{HookConfig, generate_pre_commit_hook, install_pre_commit_hook};

        let hook_config = HookConfig {
            project_type: config.template_type.clone(),
            quality_gates: config.quality_gates.clone(),
        };

        let script = generate_pre_commit_hook(&hook_config)?;
        install_pre_commit_hook(project_dir, &script)?;

        Ok(())
    }
}
```

### Phase 3: Tests

```rust
// server/src/scaffold/hooks.rs tests

#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::TempDir;
    use std::fs;

    #[test]
    fn test_generate_pforge_hook() {
        let config = HookConfig {
            project_type: TemplateType::Agent { based_on: AgentFramework::Pforge },
            quality_gates: QualityGateConfig::default(),
        };

        let script = generate_pre_commit_hook(&config).unwrap();

        assert!(script.contains("#!/bin/bash"));
        assert!(script.contains("cargo clippy"));
        assert!(script.contains("cargo test"));
    }

    #[test]
    fn test_generate_wasm_hook() {
        let config = HookConfig {
            project_type: TemplateType::Wasm { based_on: WasmFramework::WasmLabs },
            quality_gates: QualityGateConfig::default(),
        };

        let script = generate_pre_commit_hook(&config).unwrap();

        assert!(script.contains("#!/bin/bash"));
        assert!(script.contains("cargo clippy"));
        assert!(script.contains("wasm32-unknown-unknown"));
    }

    #[test]
    fn test_install_pre_commit_hook() {
        let temp_dir = TempDir::new().unwrap();
        let git_dir = temp_dir.path().join(".git/hooks");
        fs::create_dir_all(&git_dir).unwrap();

        let script = "#!/bin/bash\necho test";
        install_pre_commit_hook(temp_dir.path(), script).unwrap();

        let hook_path = git_dir.join("pre-commit");
        assert!(hook_path.exists());

        let content = fs::read_to_string(&hook_path).unwrap();
        assert_eq!(content, script);

        // Check executable permission on Unix
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt;
            let perms = fs::metadata(&hook_path).unwrap().permissions();
            assert!(perms.mode() & 0o111 != 0, "Hook should be executable");
        }
    }

    #[test]
    fn test_hook_with_complexity_check() {
        let config = HookConfig {
            project_type: TemplateType::Agent { based_on: AgentFramework::Pforge },
            quality_gates: QualityGateConfig::extreme_tdd(),
        };

        let script = generate_pre_commit_hook(&config).unwrap();
        assert!(script.contains("complexity"));
    }

    #[test]
    fn test_hook_without_complexity_check() {
        let config = HookConfig {
            project_type: TemplateType::Agent { based_on: AgentFramework::Pforge },
            quality_gates: QualityGateConfig::default(),
        };

        let script = generate_pre_commit_hook(&config).unwrap();
        assert!(script.contains("Complexity check disabled"));
    }
}

#[cfg(test)]
mod property_tests {
    use super::*;
    use proptest::prelude::*;

    proptest! {
        #[test]
        fn prop_hooks_are_valid_bash(
            project_type in prop_oneof![
                Just(TemplateType::Agent { based_on: AgentFramework::Pforge }),
                Just(TemplateType::Wasm { based_on: WasmFramework::WasmLabs })
            ]
        ) {
            let config = HookConfig {
                project_type,
                quality_gates: QualityGateConfig::default(),
            };

            let script = generate_pre_commit_hook(&config).unwrap();

            // Property: All hooks are valid bash scripts
            prop_assert!(script.starts_with("#!/bin/bash"));
            prop_assert!(script.contains("set -e"));
            prop_assert!(script.contains("exit 0"));
        }

        #[test]
        fn prop_hooks_check_clippy_and_tests(
            project_type in prop_oneof![
                Just(TemplateType::Agent { based_on: AgentFramework::Pforge }),
                Just(TemplateType::Wasm { based_on: WasmFramework::WasmLabs })
            ]
        ) {
            let config = HookConfig {
                project_type,
                quality_gates: QualityGateConfig::default(),
            };

            let script = generate_pre_commit_hook(&config).unwrap();

            // Property: All hooks enforce clippy and tests
            prop_assert!(script.contains("cargo clippy"));
            prop_assert!(script.contains("cargo test"));
        }

        #[test]
        fn prop_wasm_hooks_verify_wasm_build(
            _complexity in 1..10u8
        ) {
            let config = HookConfig {
                project_type: TemplateType::Wasm { based_on: WasmFramework::WasmLabs },
                quality_gates: QualityGateConfig::default(),
            };

            let script = generate_pre_commit_hook(&config).unwrap();

            // Property: WASM hooks verify WASM builds
            prop_assert!(script.contains("wasm32-unknown-unknown"));
        }
    }
}
```

## Complexity Analysis

New functions with complexity:
- `generate_pre_commit_hook`: CC=3 (match with 3 arms)
- `generate_pforge_hook`: CC=1
- `generate_wasm_hook`: CC=1
- `install_pre_commit_hook`: CC=3 (write, chmod on unix, error handling)
- `ScaffoldEngine::install_hooks`: CC=2
- `ScaffoldEngine::scaffold`: CC=6 (one additional step) - within threshold

All functions under CC=10 threshold ✓

## Verification Commands

```bash
# Run hook tests
cargo test --lib scaffold::hooks

# Manual test: Generate project and verify hooks
cd /tmp
cargo run --bin pmat -- scaffold agent --name hook-test
cd hook-test

# Verify hook exists and is executable
ls -la .git/hooks/pre-commit
cat .git/hooks/pre-commit

# Test hook execution (should pass)
echo "test" > README.md
git add README.md
git commit -m "test"

# Test hook catches failures (should fail)
# Add code with complexity violation and commit
```

## Files to Create/Modify

### New Files
- `server/src/scaffold/hooks.rs` - Hook generation and installation

### Modified Files
- `server/src/scaffold/mod.rs` - Add `install_hooks()` method
- `server/src/scaffold/config.rs` - Extend `QualityGateConfig` if needed
- `server/src/scaffold/tests.rs` - Add integration test for hook installation

## Risk Assessment

**Low Risk:**
- Hook generation is simple string formatting
- Installation is straightforward file write + chmod

**Mitigation:**
- Comprehensive tests for hook script validity
- Property tests ensure hooks always valid bash
- Integration tests verify hooks actually run

## Notes

This ticket completes Sprint 16 by adding the final quality enforcement layer. Now scaffolded projects have:
1. Complete directory structure (TICKET-PMAT-5004)
2. All template files (TICKET-PMAT-5002, 5003)
3. Git initialization (TICKET-PMAT-5001)
4. Pre-commit hooks enforcing quality gates (TICKET-PMAT-5005)

Every new project starts with extreme TDD quality enforcement from commit #1!

**TDD Cycle Duration**: Estimated 2-3 hours for RED → GREEN → REFACTOR

## Performance Target

- Hook execution: <30 seconds for typical commit (1-5 files)
- Hook generation: <1ms
- Hook installation: <10ms
