package operations import ( "fmt" "orchestra/internal/domain" ) // DebtCheck is why a debt item may or may not become work. Reasons are listed // rather than summarised, for the same reason SubmissionCheck lists them: "not // eligible" alone sends an operator reading code. type DebtCheck struct { Eligible bool `json:"eligible"` Reasons []string `json:"reasons,omitempty"` } // CheckDebtEligibility is the whole promotion rule, as one pure function of a // projected item. It decides only whether a maintenance task may be created. // It never creates one, and it never changes what that task must then pass: // classification as debt changes what gets scheduled, never what gets checked. // // The thresholds differ by class on purpose. Correctness and operational debt // have already cost something measurable. Structural debt needs evidence that // it is causing repeated work rather than merely offending taste. Polish never // promotes itself, or the ledger becomes a permanent cleanup generator. func CheckDebtEligibility(item domain.DebtItem) DebtCheck { recurrence, tasks := item.Recurrence(), item.AffectedTasks() blocked, manual := item.BlockedTasks(), item.ManualInterventions() switch item.Class { case domain.DebtCorrectness: if recurrence >= 1 { return DebtCheck{true, []string{fmt.Sprintf("correctness debt is eligible on first confirmed observation, and has %d", recurrence)}} } return DebtCheck{false, []string{"no confirmed observation"}} case domain.DebtOperational: var why []string if manual >= 1 { why = append(why, fmt.Sprintf("%d manual intervention(s) recorded", manual)) } if recurrence >= 3 && tasks >= 2 { why = append(why, fmt.Sprintf("recurred %d times across %d tasks", recurrence, tasks)) } if len(why) > 0 { return DebtCheck{true, why} } return DebtCheck{false, []string{ fmt.Sprintf("needs 3 occurrences across 2 tasks, or 1 manual intervention; has %d across %d tasks with %d interventions", recurrence, tasks, manual), "manual interventions are not recorded by any event type, so that count reads 0 on every current log", }} case domain.DebtStructural: if recurrence >= 3 { return DebtCheck{true, []string{fmt.Sprintf("%d review findings in this component", recurrence)}} } if blocked >= 2 { return DebtCheck{true, []string{fmt.Sprintf("blocked %d distinct tasks in this component", blocked)}} } return DebtCheck{false, []string{ fmt.Sprintf("needs 3 review findings or 2 blocked tasks in one component; has %d findings and %d blocked", recurrence, blocked), }} case domain.DebtPolish: return DebtCheck{false, []string{"polish never promotes itself, an operator promotes it explicitly"}} } return DebtCheck{false, []string{"unknown debt class " + string(item.Class)}} } // EligibleDebt filters a projected ledger to what policy would allow to become // work. It returns the check alongside each item so the reasons stay visible. func EligibleDebt(ledger domain.DebtLedger) []DebtCandidate { out := []DebtCandidate{} for _, item := range ledger.Items { if check := CheckDebtEligibility(item); check.Eligible { out = append(out, DebtCandidate{Item: item, Check: check}) } } return out } type DebtCandidate struct { Item domain.DebtItem `json:"item"` Check DebtCheck `json:"check"` }