refactor(decide): split type definitions into types.go; clarify detecting helper

Address PR review (aa-1):
- Move the decider input/output type definitions (LifecycleDecision,
  ProbeInput, ProcessLiveness, OpenPRInput, DetectingInput) out of the
  execution file into a dedicated types.go, leaving decide.go for behaviour.
- Expand the detecting() helper doc to spell out that it adapts a probe
  verdict into the shared CreateDetectingDecision anti-flap path so each
  probe branch doesn't re-implement the quarantine counter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
harshitsinghbhandari 2026-05-27 00:25:44 +05:30
parent 9920c6daaa
commit 4d8a20676a
2 changed files with 71 additions and 62 deletions

View File

@ -23,65 +23,6 @@ const (
DetectingMaxDuration = 5 * time.Minute
)
// LifecycleDecision is the output of every decider: the derived display status
// plus the canonical sub-state values to persist, the human-readable evidence,
// and the (possibly updated) detecting memory.
type LifecycleDecision struct {
Status domain.SessionStatus
Evidence string
Detecting *domain.DetectingState
SessionState domain.SessionState
SessionReason domain.SessionReason
PRState domain.PRState
PRReason domain.PRReason
}
// ProbeInput reconciles runtime + process liveness. A *failed* probe (timeout
// or error) is distinct from a "dead" verdict and must route to detecting,
// never to a death conclusion. KillRequested short-circuits to terminal.
type ProbeInput struct {
Runtime domain.RuntimeState
RuntimeFailed bool
Process ProcessLiveness
ProcessFailed bool
RecentActivity bool
KillRequested bool
Prior *domain.DetectingState
Now time.Time
}
// ProcessLiveness mirrors isProcessRunning's three-valued answer.
type ProcessLiveness string
const (
ProcessAlive ProcessLiveness = "alive"
ProcessDead ProcessLiveness = "dead"
ProcessIndeterminate ProcessLiveness = "indeterminate"
)
// OpenPRInput drives the PR pipeline ladder for an open PR.
type OpenPRInput struct {
CIFailing bool
ChangesRequested bool
Approved bool
Mergeable bool
ReviewPending bool
IdleBeyond bool // idle past the stuck threshold
Number int
URL string
}
// DetectingInput feeds the quarantine counter. Evidence is hashed with
// timestamps stripped, so "same ambiguous signal" keeps the counter climbing
// while any real change resets it.
type DetectingInput struct {
Evidence string
ProposedState domain.SessionState
ProposedReason domain.SessionReason
Prior *domain.DetectingState
Now time.Time
}
// ResolveProbeDecision reconciles runtime/process liveness into a decision.
//
// The ordering encodes the load-bearing invariants:
@ -285,9 +226,11 @@ var timestampPatterns = []*regexp.Regexp{
regexp.MustCompile(`\b\d{10,13}\b`),
}
// detecting builds a quarantine decision from a probe verdict, threading the
// prior counter through CreateDetectingDecision so probe ambiguity is subject
// to the same anti-flap escalation as any other detecting cause.
// detecting adapts a probe verdict into the shared anti-flap path. It packages
// the proposed reason + evidence (plus the prior counter from the same probe
// input) into a DetectingInput and defers to CreateDetectingDecision, so every
// probe-driven ambiguity is counted and escalated by the identical quarantine
// logic instead of each probe branch re-implementing the counter.
func detecting(in ProbeInput, reason domain.SessionReason, evidence string) LifecycleDecision {
return CreateDetectingDecision(DetectingInput{
Evidence: evidence,

View File

@ -0,0 +1,66 @@
package decide
import (
"time"
"github.com/aoagents/agent-orchestrator/backend/internal/domain"
)
// LifecycleDecision is the output of every decider: the derived display status
// plus the canonical sub-state values to persist, the human-readable evidence,
// and the (possibly updated) detecting memory.
type LifecycleDecision struct {
Status domain.SessionStatus
Evidence string
Detecting *domain.DetectingState
SessionState domain.SessionState
SessionReason domain.SessionReason
PRState domain.PRState
PRReason domain.PRReason
}
// ProbeInput reconciles runtime + process liveness. A *failed* probe (timeout
// or error) is distinct from a "dead" verdict and must route to detecting,
// never to a death conclusion. KillRequested short-circuits to terminal.
type ProbeInput struct {
Runtime domain.RuntimeState
RuntimeFailed bool
Process ProcessLiveness
ProcessFailed bool
RecentActivity bool
KillRequested bool
Prior *domain.DetectingState
Now time.Time
}
// ProcessLiveness mirrors isProcessRunning's three-valued answer.
type ProcessLiveness string
const (
ProcessAlive ProcessLiveness = "alive"
ProcessDead ProcessLiveness = "dead"
ProcessIndeterminate ProcessLiveness = "indeterminate"
)
// OpenPRInput drives the PR pipeline ladder for an open PR.
type OpenPRInput struct {
CIFailing bool
ChangesRequested bool
Approved bool
Mergeable bool
ReviewPending bool
IdleBeyond bool // idle past the stuck threshold
Number int
URL string
}
// DetectingInput feeds the quarantine counter. Evidence is hashed with
// timestamps stripped, so "same ambiguous signal" keeps the counter climbing
// while any real change resets it.
type DetectingInput struct {
Evidence string
ProposedState domain.SessionState
ProposedReason domain.SessionReason
Prior *domain.DetectingState
Now time.Time
}