Preserve leases needing recovery
This commit is contained in:
@@ -36,6 +36,11 @@ const (
|
||||
StateCompleted TaskState = "completed"
|
||||
StateFailed TaskState = "failed"
|
||||
StateBlocked TaskState = "blocked"
|
||||
// StateNeedsAttention records a recoverable fault without abandoning the
|
||||
// current fenced lease. The owning worker may still reconcile a late
|
||||
// completion, explicitly release it, or renew it while an operator
|
||||
// investigates; expiry remains the only automatic reclaim.
|
||||
StateNeedsAttention TaskState = "needs_attention"
|
||||
)
|
||||
|
||||
// BlockReason is the machine-readable diagnosis for a TaskBlocked event.
|
||||
@@ -185,7 +190,7 @@ func ValidateEvent(e Event) error {
|
||||
if e.SchemaVersion >= 2 && strings.TrimSpace(e.Surface) == "" {
|
||||
return fmt.Errorf("%w: surface required", ErrInvalid)
|
||||
}
|
||||
allowed := map[string]bool{"TaskCreated": true, "TaskLeased": true, "TaskLeaseRenewed": true, "TaskReleased": true, "TaskPickupValidated": true, "TaskCompleted": true, "TaskFailed": true, "TaskBlocked": true, "ApprovalRequested": true, "ApprovalGranted": true, "ApprovalDenied": true, "TaskAmended": true, "TaskCorrected": true, "QuotaReported": true, "StandupAdvisory": true}
|
||||
allowed := map[string]bool{"TaskCreated": true, "TaskLeased": true, "TaskLeaseRenewed": true, "TaskReleased": true, "TaskPickupValidated": true, "TaskCompleted": true, "TaskFailed": true, "TaskBlocked": true, "TaskNeedsAttention": true, "ApprovalRequested": true, "ApprovalGranted": true, "ApprovalDenied": true, "TaskAmended": true, "TaskCorrected": true, "QuotaReported": true, "StandupAdvisory": true}
|
||||
if !allowed[e.Type] {
|
||||
return fmt.Errorf("%w: unknown type %q", ErrInvalid, e.Type)
|
||||
}
|
||||
@@ -308,7 +313,7 @@ func ValidatePayload(typ string, p map[string]any) error {
|
||||
if err := requiredString("reason"); err != nil {
|
||||
return err
|
||||
}
|
||||
case "TaskBlocked":
|
||||
case "TaskBlocked", "TaskNeedsAttention":
|
||||
if err := requiredString("blocker"); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -345,7 +350,7 @@ func ValidatePayload(typ string, p map[string]any) error {
|
||||
return fmt.Errorf("%w: state must be a string", ErrInvalid)
|
||||
}
|
||||
switch TaskState(s) {
|
||||
case StateQueued, StateLeased, StateCompleted, StateFailed, StateBlocked:
|
||||
case StateQueued, StateLeased, StateCompleted, StateFailed, StateBlocked, StateNeedsAttention:
|
||||
default:
|
||||
return fmt.Errorf("%w: state invalid", ErrInvalid)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
// producers happen to send well-formed payloads.
|
||||
var eventTypesUnderTest = []string{
|
||||
"TaskCreated", "TaskLeased", "TaskReleased", "TaskCompleted", "TaskFailed",
|
||||
"TaskBlocked", "ApprovalRequested", "ApprovalGranted", "ApprovalDenied",
|
||||
"TaskBlocked", "TaskNeedsAttention", "ApprovalRequested", "ApprovalGranted", "ApprovalDenied",
|
||||
"TaskAmended", "QuotaReported", "StandupAdvisory",
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user