Files
orchestra/internal/operations/debt_test.go
T
kami 0aefe021b0 Explain a debt refusal with facts about the item
Reasons are the auditable explanation of a pure decision, so one that
describes the architecture rots the moment the architecture moves. The
operational refusal asserted that manual interventions were recorded by no
event type: true when written, false the day OperatorInterventionRecorded
landed, and still printed under every refusal after that.

It now reports the counts and the thresholds they missed. Why a count is
zero is not this function's business, since no intervention happening, none
being recorded, and none being migrated all read the same from here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVbaKucEYBjMqVeUgJUsc1
2026-08-30 14:57:25 +04:00

79 lines
3.0 KiB
Go

package operations
import (
"strings"
"testing"
"orchestra/internal/domain"
)
func debtItem(class domain.DebtClass, kinds ...struct {
kind domain.ObservationKind
task string
}) domain.DebtItem {
item := domain.DebtItem{Class: class}
for _, k := range kinds {
item.Observations = append(item.Observations, domain.DebtObservation{Kind: k.kind, TaskID: k.task})
}
return item
}
type obs = struct {
kind domain.ObservationKind
task string
}
// The thresholds are the whole policy, so each class gets its own case. Polish
// is the one that must never pass, or the ledger becomes a cleanup generator.
func TestDebtEligibilityPerClass(t *testing.T) {
fail := obs{domain.ObservationFailureClass, "t1"}
if !CheckDebtEligibility(debtItem(domain.DebtCorrectness, fail)).Eligible {
t.Fatal("correctness debt is eligible on first observation")
}
twice := debtItem(domain.DebtOperational, fail, obs{domain.ObservationFailureClass, "t1"})
if CheckDebtEligibility(twice).Eligible {
t.Fatal("two occurrences on one task must not qualify as operational debt")
}
spread := debtItem(domain.DebtOperational, fail,
obs{domain.ObservationFailureClass, "t2"}, obs{domain.ObservationFailureClass, "t3"})
if !CheckDebtEligibility(spread).Eligible {
t.Fatal("three occurrences across three tasks must qualify")
}
manual := debtItem(domain.DebtOperational, obs{domain.ObservationManualIntervention, "t1"})
if !CheckDebtEligibility(manual).Eligible {
t.Fatal("one manual intervention must qualify on its own")
}
blocked := debtItem(domain.DebtStructural,
obs{domain.ObservationBlockReason, "t1"}, obs{domain.ObservationBlockReason, "t2"})
if !CheckDebtEligibility(blocked).Eligible {
t.Fatal("two blocked tasks in one component must qualify as structural")
}
polish := debtItem(domain.DebtPolish, fail, fail, fail, fail, fail)
if CheckDebtEligibility(polish).Eligible {
t.Fatal("polish must never promote itself, at any recurrence")
}
}
// A refusal has to say what is missing. "Not eligible" alone sends an operator
// reading code, which is the mistake SubmissionCheck already documents.
func TestDebtRefusalNamesTheMissingEvidence(t *testing.T) {
check := CheckDebtEligibility(debtItem(domain.DebtOperational, obs{domain.ObservationFailureClass, "t1"}))
if check.Eligible || len(check.Reasons) == 0 {
t.Fatalf("want a refusal with reasons, got %+v", check)
}
joined := strings.Join(check.Reasons, " ")
// Every reason is a fact about this item. The old text asserted that no
// event type recorded manual interventions, which stopped being true the
// day one did, leaving a correct decision explained by a lie.
for _, want := range []string{"1 occurrences across 1 tasks", "0 recorded, needs 1"} {
if !strings.Contains(joined, want) {
t.Fatalf("the refusal does not state %q: %v", want, check.Reasons)
}
}
for _, forbidden := range []string{"event type", "every current log"} {
if strings.Contains(joined, forbidden) {
t.Fatalf("a reason describes the architecture instead of the item: %v", check.Reasons)
}
}
}