pmat 3.15.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
// Legacy Debt Tracking for Work Contracts
// Spec: docs/specifications/dbc.md §5
//
// When a project doesn't meet strict Popperian thresholds (95% coverage, etc.),
// this module creates tracked debt tickets and overrides claims.
// This is the "managed migration path" for existing projects.

impl WorkContract {
    /// Acknowledge legacy debt by comparing current metrics against thresholds
    ///
    /// For projects that don't meet strict Popperian thresholds (95% coverage, etc.),
    /// this method creates tracked debt tickets and adds overrides to claims.
    ///
    /// This is the "managed migration path" for existing projects.
    #[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
    pub fn acknowledge_legacy_debt(&mut self, project_path: &Path) -> Result<()> {
        let timestamp = chrono::Utc::now().format("%Y%m%d%H%M%S").to_string();
        let mut debt_items: Vec<DebtItem> = Vec::new();

        // Check coverage against 95% threshold
        if self.baseline_coverage < self.thresholds.min_coverage_pct {
            let ticket_id = format!("DEBT-COV-{}", timestamp);
            debt_items.push(DebtItem {
                ticket_id: ticket_id.clone(),
                category: "coverage".to_string(),
                description: format!(
                    "Coverage {:.1}% is below required {:.1}%",
                    self.baseline_coverage, self.thresholds.min_coverage_pct
                ),
                current_value: self.baseline_coverage,
                required_value: self.thresholds.min_coverage_pct,
            });

            // Add override to AbsoluteCoverage claim
            for claim in &mut self.claims {
                if claim.falsification_method == FalsificationMethod::AbsoluteCoverage {
                    claim.override_info = Some(OverrideInfo {
                        reason: "Legacy Debt: Project predates strict coverage requirements"
                            .to_string(),
                        ticket_id: ticket_id.clone(),
                        timestamp: chrono::Utc::now(),
                    });
                }
            }
        }

        // Check for large files in the manifest
        let large_files: Vec<_> = self
            .baseline_file_manifest
            .files
            .iter()
            .filter(|(_, entry)| entry.lines > self.thresholds.max_file_lines)
            .map(|(path, entry)| (path.clone(), entry.lines))
            .collect();

        if !large_files.is_empty() {
            let ticket_id = format!("DEBT-SIZE-{}", timestamp);
            let details: Vec<String> = large_files
                .iter()
                .take(10)
                .map(|(p, lines)| format!("{}: {} lines", p.display(), lines))
                .collect();

            debt_items.push(DebtItem {
                ticket_id: ticket_id.clone(),
                category: "file_size".to_string(),
                description: format!(
                    "{} file(s) exceed {} line limit: {}",
                    large_files.len(),
                    self.thresholds.max_file_lines,
                    details.join(", ")
                ),
                current_value: large_files.len() as f64,
                required_value: 0.0,
            });

            // Add override to FileSizeRegression claim
            for claim in &mut self.claims {
                if claim.falsification_method == FalsificationMethod::FileSizeRegression {
                    claim.override_info = Some(OverrideInfo {
                        reason: "Legacy Debt: Large files predate strict size requirements"
                            .to_string(),
                        ticket_id: ticket_id.clone(),
                        timestamp: chrono::Utc::now(),
                    });
                }
            }
        }

        // Create debt tickets directory and write YAML files
        if !debt_items.is_empty() {
            write_debt_tickets(project_path, &debt_items)?;
        }

        Ok(())
    }
}

/// Write debt ticket YAML files to .pmat-tickets/
fn write_debt_tickets(project_path: &Path, debt_items: &[DebtItem]) -> Result<()> {
    let tickets_dir = project_path.join(".pmat-tickets");
    std::fs::create_dir_all(&tickets_dir)?;

    for item in debt_items {
        let ticket_path = tickets_dir.join(format!("{}.yaml", item.ticket_id));
        let yaml_content = format!(
            r#"# PMAT Legacy Debt Ticket
# Auto-generated by `pmat comply upgrade`
# DO NOT DELETE - Required for Popperian falsification override

ticket_id: "{}"
category: "{}"
created_at: "{}"
status: "open"

description: |
  {}

metrics:
  current: {:.2}
  required: {:.2}
  gap: {:.2}

resolution:
  # Update this section when addressing the debt
  plan: "TBD"
  target_date: null
  completed_at: null
"#,
            item.ticket_id,
            item.category,
            chrono::Utc::now().to_rfc3339(),
            item.description,
            item.current_value,
            item.required_value,
            item.required_value - item.current_value,
        );

        std::fs::write(&ticket_path, yaml_content)?;
        println!("   Created debt ticket: {}", ticket_path.display());
    }

    Ok(())
}

/// Internal struct for tracking debt items
#[derive(Debug)]
struct DebtItem {
    ticket_id: String,
    category: String,
    description: String,
    current_value: f64,
    required_value: f64,
}