From 4d8a20676ad0a73a7edcc321abc71b265b11a6c3 Mon Sep 17 00:00:00 2001 From: harshitsinghbhandari <24b4506@iitb.ac.in> Date: Wed, 27 May 2026 00:25:44 +0530 Subject: [PATCH] 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) --- backend/internal/domain/decide/decide.go | 67 ++---------------------- backend/internal/domain/decide/types.go | 66 +++++++++++++++++++++++ 2 files changed, 71 insertions(+), 62 deletions(-) create mode 100644 backend/internal/domain/decide/types.go diff --git a/backend/internal/domain/decide/decide.go b/backend/internal/domain/decide/decide.go index e43ad7e40..87b91b189 100644 --- a/backend/internal/domain/decide/decide.go +++ b/backend/internal/domain/decide/decide.go @@ -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, diff --git a/backend/internal/domain/decide/types.go b/backend/internal/domain/decide/types.go new file mode 100644 index 000000000..92d50df77 --- /dev/null +++ b/backend/internal/domain/decide/types.go @@ -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 +}