Files
orchestra/internal/operations/debt.go
T
kami a757cffc78 Project a debt ledger from canonical history, read-only
Slice one of DEBT-DESIGN.md, with the four amendments applied. It writes
nothing: no new event types, no scheduling, no clustering, no
maintenance tasks. The point is to find out whether the model can
represent debt this project already knows about, before committing to a
durable schema.

The projected type lives in domain and the fold lives in store, so the
first implementation does not bake a read model into the command layer.
operations owns the one action that exists, CheckDebtEligibility, which
is a pure function returning explicit reasons like CheckSubmission.

Signatures carry their version in the string. Normalization rules will
change, and without a version that silently regroups history and moves
the recurrence counts eligibility was already decided on.

Observations require exactly one of event_id and legacy_ref. Imported
Fxx history predates the events that would justify it, and a fabricated
event id would break the provenance rule the ledger exists to enforce.

Incompleteness is reported, not hidden. Manual interventions and worker
observations are carried by no event type, so the ledger names both as
non-durable gaps rather than reading as "no operational cost". The
operational refusal reason says the intervention count is structurally
zero on every current log.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVbaKucEYBjMqVeUgJUsc1
2026-08-29 02:00:58 +04:00

82 lines
3.3 KiB
Go

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"`
}