homeboy 0.76.0

CLI for multi-component deployment and development workflow automation
Documentation
use crate::code_audit::AuditFinding;
use crate::refactor::auto::preflight;
use crate::refactor::auto::{
    FixPolicy, FixResult, FixSafetyTier, Insertion, NewFile, PolicySummary, PreflightContext,
    PreflightReport, PreflightStatus,
};

pub(crate) fn blocked_reason_from_preflight(report: &PreflightReport) -> Option<String> {
    report
        .checks
        .iter()
        .find(|check| !check.passed)
        .map(|check| format!("Blocked by preflight {}: {}", check.name, check.detail))
}

fn finding_allowed(finding: &AuditFinding, policy: &FixPolicy) -> bool {
    let included = policy
        .only
        .as_ref()
        .is_none_or(|only| only.contains(finding));

    included && !policy.exclude.contains(finding)
}

fn annotate_insertion_for_policy(
    file: &str,
    insertion: &mut Insertion,
    write: bool,
    policy: &FixPolicy,
    context: &PreflightContext<'_>,
) -> bool {
    if !finding_allowed(&insertion.finding, policy) {
        return false;
    }

    insertion.preflight = preflight::run_insertion_preflight(file, insertion, context);

    insertion.auto_apply = if !write {
        true
    } else {
        match insertion.safety_tier {
            FixSafetyTier::SafeAuto => true,
            FixSafetyTier::SafeWithChecks => insertion.preflight.as_ref().is_some_and(|report| {
                matches!(
                    report.status,
                    PreflightStatus::Passed | PreflightStatus::NotApplicable
                )
            }),
            FixSafetyTier::PlanOnly => false,
        }
    };

    insertion.blocked_reason = if insertion.auto_apply {
        None
    } else {
        Some(match insertion.safety_tier {
            FixSafetyTier::SafeAuto => "Blocked by current write policy".to_string(),
            FixSafetyTier::SafeWithChecks => insertion
                .preflight
                .as_ref()
                .and_then(blocked_reason_from_preflight)
                .unwrap_or_else(|| {
                    "Blocked: requires preflight validation before auto-write".to_string()
                }),
            FixSafetyTier::PlanOnly => {
                "Blocked: plan-only fix, not eligible for auto-write".to_string()
            }
        })
    };

    true
}

fn annotate_new_file_for_policy(
    new_file: &mut NewFile,
    write: bool,
    policy: &FixPolicy,
    context: &PreflightContext<'_>,
) -> bool {
    if !finding_allowed(&new_file.finding, policy) {
        return false;
    }

    new_file.preflight = preflight::run_new_file_preflight(new_file, context);

    new_file.auto_apply = if !write {
        true
    } else {
        match new_file.safety_tier {
            FixSafetyTier::SafeAuto => true,
            FixSafetyTier::SafeWithChecks => new_file.preflight.as_ref().is_some_and(|report| {
                matches!(
                    report.status,
                    PreflightStatus::Passed | PreflightStatus::NotApplicable
                )
            }),
            FixSafetyTier::PlanOnly => false,
        }
    };

    new_file.blocked_reason = if new_file.auto_apply {
        None
    } else {
        Some(match new_file.safety_tier {
            FixSafetyTier::SafeAuto => "Blocked by current write policy".to_string(),
            FixSafetyTier::SafeWithChecks => new_file
                .preflight
                .as_ref()
                .and_then(blocked_reason_from_preflight)
                .unwrap_or_else(|| {
                    "Blocked: requires preflight validation before auto-write".to_string()
                }),
            FixSafetyTier::PlanOnly => {
                "Blocked: plan-only fix, not eligible for auto-write".to_string()
            }
        })
    };

    true
}

pub fn apply_fix_policy(
    result: &mut FixResult,
    write: bool,
    policy: &FixPolicy,
    context: &PreflightContext<'_>,
) -> PolicySummary {
    let mut summary = PolicySummary::default();

    result.fixes = result
        .fixes
        .drain(..)
        .filter_map(|mut fix| {
            fix.insertions.retain_mut(|insertion| {
                annotate_insertion_for_policy(&fix.file, insertion, write, policy, context)
            });

            preflight::run_fix_preflight(&mut fix, context, write);

            for insertion in &mut fix.insertions {
                insertion.auto_apply = if !write {
                    true
                } else {
                    match insertion.safety_tier {
                        FixSafetyTier::SafeAuto => true,
                        FixSafetyTier::SafeWithChecks => {
                            insertion.preflight.as_ref().is_some_and(|report| {
                                matches!(
                                    report.status,
                                    PreflightStatus::Passed | PreflightStatus::NotApplicable
                                )
                            })
                        }
                        FixSafetyTier::PlanOnly => false,
                    }
                };

                insertion.blocked_reason = if insertion.auto_apply {
                    None
                } else {
                    Some(match insertion.safety_tier {
                        FixSafetyTier::SafeAuto => "Blocked by current write policy".to_string(),
                        FixSafetyTier::SafeWithChecks => insertion
                            .preflight
                            .as_ref()
                            .and_then(blocked_reason_from_preflight)
                            .unwrap_or_else(|| {
                                "Blocked: requires preflight validation before auto-write"
                                    .to_string()
                            }),
                        FixSafetyTier::PlanOnly => {
                            "Blocked: plan-only fix, not eligible for auto-write".to_string()
                        }
                    })
                };

                summary.visible_insertions += 1;
                if insertion.auto_apply {
                    summary.auto_apply_insertions += 1;
                } else {
                    summary.blocked_insertions += 1;
                    if insertion
                        .preflight
                        .as_ref()
                        .is_some_and(|report| report.status == PreflightStatus::Failed)
                    {
                        summary.preflight_failures += 1;
                    }
                }
            }

            if fix.insertions.is_empty() {
                None
            } else {
                Some(fix)
            }
        })
        .collect();

    result.new_files = result
        .new_files
        .drain(..)
        .filter_map(|mut pending| {
            if !annotate_new_file_for_policy(&mut pending, write, policy, context) {
                return None;
            }

            summary.visible_new_files += 1;
            if pending.auto_apply {
                summary.auto_apply_new_files += 1;
            } else {
                summary.blocked_new_files += 1;
                if pending
                    .preflight
                    .as_ref()
                    .is_some_and(|report| report.status == PreflightStatus::Failed)
                {
                    summary.preflight_failures += 1;
                }
            }

            Some(pending)
        })
        .collect();

    if let Some(ref only) = policy.only {
        if !only.contains(&AuditFinding::GodFile) {
            result.decompose_plans.clear();
        }
    }
    if policy.exclude.contains(&AuditFinding::GodFile) {
        result.decompose_plans.clear();
    }

    result.total_insertions = summary.visible_insertions + summary.visible_new_files;
    summary
}