runtime

package
v0.11.0-beta.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2026 License: MIT Imports: 54 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrAdmissionNotReady    = errors.New("daemon admission is not ready")
	ErrAdmissionStopping    = errors.New("daemon admission is stopping")
	ErrAdmissionDegraded    = errors.New("daemon admission is degraded")
	ErrAdmissionIllegalMove = errors.New("illegal admission state transition")
)

ErrAdmissionNotReady is returned when a mutation or queue claim is refused because admission is not ready.

View Source
var ErrAgentLiveHandleMissing = errors.New("agent live containment handle is missing")

ErrAgentLiveHandleMissing is returned by stop paths when an in-scope agent execution has no live Supervisor handle. After #576 full agent coverage, live stop/kill must not reconstruct ownership from SQLite PID.

View Source
var ErrOperationAdmissionClosed = errors.New("queue operation admission is closed")

ErrOperationAdmissionClosed is returned when an operation lease is refused because Supervisor admission is closed (daemon shutdown / degraded).

View Source
var ErrOperationFinalizeFailed = errors.New("queue operation durable finalize failed")

ErrOperationFinalizeFailed is returned when durable complete/cancel/requeue of a claimed queue item fails. Ownership must be retained and admission degraded rather than treating release as success (ADR-0015 R6 / #579).

View Source
var ErrOperationLeaseCancelled = errors.New("queue operation lease cancelled before bind")

ErrOperationLeaseCancelled is returned by BindClaim when stop/shutdown (or another Supervisor cancel) closed the lease before the durable claim can be owned. Context cancel alone is insufficient — callers must treat this explicit error as "do not start the queue processor".

Functions

func LockLoopRequeue added in v0.11.0

func LockLoopRequeue(loopID string) func()

LockLoopRequeue acquires the process-wide per-loop requeue mutex shared by API discard+retry and runtime requeue paths. See loops.LockLoopRequeue. Call order with LockLoopTarget: take the per-loop lock first, then the target lock.

func LockLoopTarget added in v0.11.0

func LockLoopTarget(key string) func()

LockLoopTarget acquires the process-wide same-target mutex. See loops.LockLoopTarget.

func LoopTargetGuardKey added in v0.11.0

func LoopTargetGuardKey(projectID, loopType, targetType, targetKey string) string

LoopTargetGuardKey builds the process-wide target mutex key. See loops.LoopTargetGuardKey.

func LoopTargetGuardKeyFromRecord added in v0.11.0

func LoopTargetGuardKeyFromRecord(loop storage.LoopRecord) string

LoopTargetGuardKeyFromRecord derives LoopTargetGuardKey from a stored loop.

Types

type ActiveExecutionRegistry

type ActiveExecutionRegistry struct {
	// contains filtered or unexported fields
}

ActiveExecutionRegistry is the in-process Supervisor registry for live agent executions and queue operation leases (ADR-0015 R3 / R6 / #576 / #579). It owns:

  • spawn admission leases before cmd.Start
  • containment handle binding after spawn
  • stop/shutdown race linearization (kill+confirmed drain before Start success)
  • Kill via bound handle for looper stop / haltLoop
  • non-agent handle tracking for shutdown drain / retain-storage (#577)
  • operation leases that span durable queue claims until durable finalize (#579)

Non-agent tracking is not a second agent lease registry: short shell/proxy jobs only register the live containment handle so BeginShutdown can wait or force-drain and surface failures for retain-storage.

func NewActiveExecutionRegistry

func NewActiveExecutionRegistry() *ActiveExecutionRegistry

func (*ActiveExecutionRegistry) AdmitOperation added in v0.11.0

func (r *ActiveExecutionRegistry) AdmitOperation(ctx context.Context, meta OperationMeta) (OperationLease, error)

AdmitOperation acquires a Supervisor operation lease before durable ClaimNext* (ADR-0015 R6 / #579). Successful claim must BindClaim; miss/error releases immediately. Bound claims release only after durable finalize.

func (*ActiveExecutionRegistry) AdmitSpawn added in v0.11.0

AdmitSpawn acquires a Supervisor spawn lease before cmd.Start (ADR-0015 / #576).

func (*ActiveExecutionRegistry) BeginLoopStop added in v0.11.0

func (r *ActiveExecutionRegistry) BeginLoopStop(loopID, reason string) (release func(), err error)

BeginLoopStop closes spawn admission for one loop, cancels both pending and bound (active) leases for that loop, and confirmed-drains every bound containment handle for the loop. Bound-lease cancel is required so native-resume fallback cannot re-spawn after the old handle is drained. Handle drain here covers the BindHandle→persistStatus window where the registry owns a live process but haltLoop may not yet see a durable AgentExecutionRecord to Kill by ID.

Successfully drained keys are recorded in stopDrained so a subsequent haltLoop Kill-by-id still returns killed=true when releaseLease removes the registry entry while handle.Kill is waiting (process exit → run finish).

Drain failures from processcontainment.Handle.Kill and pending spawn/rebind wait timeouts are returned so stop/close cannot report success when a just-started agent is only unconfirmed or still live. The release func is still returned on drain failure: the gate was opened and callers manage sticky vs temporary windows as before.

After a durable stop (pause/terminate), callers must keep the gate closed: do not invoke the returned release. In-flight runners that claimed work before stop may still reach AgentExecutor.Start after halt returns; reopening would let AdmitSpawn succeed and start a process after looper stop. Clear the gate only via ClearLoopStop when the loop is intentionally re-activated (API unpause/retry/handback). Do not clear from scheduler claim dispatch: a pre-stop claim can race past parked checks and would reopen admission.

For terminal close abort paths (before durable terminate), callers should invoke the returned release so a still-running loop can AdmitSpawn again.

Pending spawn windows (AdmitSpawn through BindHandle/Release) and native rebind windows are waited with the same handshake before stop returns, so a just-started process cannot outlive the stop response without confirmed drain.

The returned release is also used in tests and temporary windows.

func (*ActiveExecutionRegistry) BeginShutdown added in v0.11.0

func (r *ActiveExecutionRegistry) BeginShutdown(reason string) error

BeginShutdown closes spawn admission, cancels pending and bound (active) leases, and confirmed-drains every bound containment handle.

Also waits for Supervisor-owned non-agent handles (shell/trusted-review) to release after cancel, force-kills stragglers, and joins ReportDrainFailure results so retain-storage covers non-agent containment failures (#577).

Cancels pending queue operation leases so BindClaim cannot start processors after shutdown, and waits for bound operation leases to Release after durable finalize (ADR-0015 R6 / #579). Does not force-release operation leases on timeout — that would create unowned durable running claims.

Returns a non-nil error when any handle Kill fails or a spawn/rebind wait times out. ADR-0015 / #577: drain failure must not be reported as graceful success; Runtime.Stop retains SQLite when this returns an error.

func (*ActiveExecutionRegistry) BoundOperationCount added in v0.11.0

func (r *ActiveExecutionRegistry) BoundOperationCount() int

BoundOperationCount returns the number of bound (post-claim) operation leases.

func (*ActiveExecutionRegistry) ClearLoopStop added in v0.11.0

func (r *ActiveExecutionRegistry) ClearLoopStop(loopID string) (wasActive bool)

ClearLoopStop reopens spawn admission for a loop after intentional re-activation (API unpause, retry, or handback). Not for scheduler claim dispatch.

Returns whether a stop gate was active under the same lock that clears it. Callers that restore on abort must use this return value instead of a separate LoopStopActive check: a concurrent BeginLoopStop between those two calls would leave gateWasActive=false while this delete still removes the new gate, and a failed start/retry/reuse TX would skip RestoreLoopStop.

Outstanding BeginLoopStop release closures captured before this clear are invalidated via stopEpoch: deleting the refcount alone is not enough, because a temporary release (stopCandidateExecution) can still run after a failed reactivation's RestoreLoopStop and would otherwise drop the restored sticky gate when it sees count <= 1.

func (*ActiveExecutionRegistry) HasLiveHandle added in v0.11.0

func (r *ActiveExecutionRegistry) HasLiveHandle(loopID, runID, executionID string) bool

HasLiveHandle reports whether the registry holds a live entry for the key. Used by stop paths and contract tests.

func (*ActiveExecutionRegistry) Kill

func (r *ActiveExecutionRegistry) Kill(loopID, runID, executionID, reason string) (bool, error)

Kill stops a live owned agent by containment handle (confirmed drain) when bound, otherwise via softKill. Returns (false, nil) when no live ownership entry exists — callers must not fall back to SQLite PID after #576.

When BeginLoopStop already confirmed-drained this key and releaseLease removed the entry during handle.Kill, returns (true, nil) so haltLoop does not treat the missing entry as ErrAgentLiveHandleMissing for a process stop just killed.

func (*ActiveExecutionRegistry) LiveCount added in v0.11.0

func (r *ActiveExecutionRegistry) LiveCount() int

LiveCount returns the number of bound/registered live agent executions.

func (*ActiveExecutionRegistry) LoopStopActive added in v0.11.0

func (r *ActiveExecutionRegistry) LoopStopActive(loopID string) bool

LoopStopActive reports whether spawn admission is closed for loopID.

func (*ActiveExecutionRegistry) NonAgentDrainErr added in v0.11.0

func (r *ActiveExecutionRegistry) NonAgentDrainErr() error

NonAgentDrainErr returns accumulated non-agent containment drain failures. Runtime.Stop re-reads this after producer waits so late shell/proxy reports still retain storage.

func (*ActiveExecutionRegistry) OwnsQueueClaim added in v0.11.0

func (r *ActiveExecutionRegistry) OwnsQueueClaim(queueItemID string) bool

OwnsQueueClaim reports whether a durable queue item id is currently owned by a live operation lease (bound, not released).

func (*ActiveExecutionRegistry) PendingCount added in v0.11.0

func (r *ActiveExecutionRegistry) PendingCount() int

PendingCount returns the number of pre-Start spawn leases.

func (*ActiveExecutionRegistry) PendingOperationCount added in v0.11.0

func (r *ActiveExecutionRegistry) PendingOperationCount() int

PendingOperationCount returns the number of pre-claim/pre-bind operation leases.

func (*ActiveExecutionRegistry) Register

func (r *ActiveExecutionRegistry) Register(loopID, runID, executionID string, execution activeExecution) func()

Register is retained for tests and transitional paths that hold an activeExecution without a containment handle. Production agent spawns must use AdmitSpawn + BindHandle at the common executor boundary (#576). A contract test fails if only the worker role registers post-spawn.

func (*ActiveExecutionRegistry) ReportDrainFailure added in v0.11.0

func (r *ActiveExecutionRegistry) ReportDrainFailure(err error)

ReportDrainFailure implements processcontainment.LiveTracker. Callers report Kill/Drain failures from their ownership path so late cancel drains still feed retain-storage even when BeginShutdown already returned.

func (*ActiveExecutionRegistry) ReportHardPersistFailure added in v0.11.0

func (r *ActiveExecutionRegistry) ReportHardPersistFailure(err error)

ReportHardPersistFailure surfaces a hard agent_executions write failure into daemon admission. Safe to call from agent executor mid-life paths.

func (*ActiveExecutionRegistry) RestoreLoopStop added in v0.11.0

func (r *ActiveExecutionRegistry) RestoreLoopStop(loopID string) error

RestoreLoopStop re-closes spawn admission after a failed intentional reactivation that already called ClearLoopStop. Cancels pending/active leases and confirmed-drains bound handles admitted during the clear window so a failed retry/start/worker-reuse cannot leave a live agent for a loop that was never reactivated.

Always increments the stop-gate refcount (sticky restore reference) even when a temporary BeginLoopStop is already active for the same loop. Leaving the count unchanged would let that temporary release reopen AdmitSpawn after the failed reactivation; restore must outlive unrelated releases.

Returns cancelAndDrainLoop's error when kill or confirmed-drain fails so callers can join it with the original validation/TX failure; the gate is still closed even when drain fails.

func (*ActiveExecutionRegistry) SetAllowSpawn added in v0.11.0

func (r *ActiveExecutionRegistry) SetAllowSpawn(fn func() error)

SetAllowSpawn wires the daemon Admission projection for spawn decisions.

func (*ActiveExecutionRegistry) SetOnHardPersistFailure added in v0.11.0

func (r *ActiveExecutionRegistry) SetOnHardPersistFailure(fn func(error))

SetOnHardPersistFailure wires sticky admission degrade for hard execution observation write failures (initial/heartbeat/output/terminal). Soft cancel and conflict after terminal won must not invoke this callback.

func (*ActiveExecutionRegistry) Track added in v0.11.0

func (r *ActiveExecutionRegistry) Track(handle *processcontainment.Handle) (release func())

Track implements processcontainment.LiveTracker for Supervisor-owned non-agent handles (validation/shell, trusted review). release is idempotent.

type Admission added in v0.11.0

type Admission struct {
	// contains filtered or unexported fields
}

Admission is the single Authority for live daemon admission. All gates must call AllowMutations / AllowClaim under the same mutex as state reads so there is no check-then-act dual flag that can disagree. Deletion attempt: remove separate ownershipAcquired readiness and trust only agent/process signals — insufficient for multi-PR rollout because recovery and ingress need a process-lifetime closed gate before Supervisor ownership.

func NewAdmission added in v0.11.0

func NewAdmission() *Admission

NewAdmission starts in starting; recovery/CompleteStartup must move it to ready.

func (*Admission) AllowClaim added in v0.11.0

func (a *Admission) AllowClaim() error

AllowClaim is the atomic gate for work-producing scheduler activity (full tick and each durable ClaimNext*). Same Authority as AllowMutations — a projection, not a second decision.

func (*Admission) AllowMutations added in v0.11.0

func (a *Admission) AllowMutations() error

AllowMutations is the atomic gate for HTTP mutating ingress. Callers must treat a nil error as admission to mutate; there is no separate ready flag.

func (*Admission) AllowsReads added in v0.11.0

func (a *Admission) AllowsReads() bool

AllowsReads reports whether read-only HTTP may proceed. Reads remain available in starting, ready, stopping, and degraded.

func (*Admission) BeginShutdown added in v0.11.0

func (a *Admission) BeginShutdown(reason string) error

BeginShutdown is ready|starting|degraded → stopping. Idempotent when already stopping.

func (*Admission) BeginShutdownThen added in v0.11.0

func (a *Admission) BeginShutdownThen(reason string, then func()) error

BeginShutdownThen is BeginShutdown plus a then callback still holding a.mu after the stopping transition (or when already stopping).

func (*Admission) IsReady added in v0.11.0

func (a *Admission) IsReady() bool

IsReady is a projection helper for status surfaces. Prefer AllowMutations / AllowClaim for gates so state and decision cannot diverge.

func (*Admission) MarkDegraded added in v0.11.0

func (a *Admission) MarkDegraded(reason string) error

MarkDegraded is sticky until process restart (degraded → ready is illegal). Recovery is restart looperd; Runtime.MarkDegraded also cancels work producers.

func (*Admission) MarkReady added in v0.11.0

func (a *Admission) MarkReady(reason string) error

MarkReady is starting → ready after CompleteStartup recovery finishes.

func (*Admission) Reason added in v0.11.0

func (a *Admission) Reason() string

Reason returns the last transition reason (empty when unset).

func (*Admission) State added in v0.11.0

func (a *Admission) State() AdmissionState

State returns the current admission state.

func (*Admission) Transition added in v0.11.0

func (a *Admission) Transition(to AdmissionState, reason string) error

Transition applies a legal state change. Illegal moves return ErrAdmissionIllegalMove without changing state.

func (*Admission) TransitionThen added in v0.11.0

func (a *Admission) TransitionThen(to AdmissionState, reason string, then func()) error

TransitionThen applies a legal state change and runs then while still holding a.mu. Runtime.MarkDegraded / BeginShutdown use this so cancelWorkProducers runs in the same critical section as the closed transition — there is no window where admission is already closed but producer cancel has not run yet (worktree cleanup could otherwise start git worktree remove after close). then must not call back into Admission methods that take a.mu (would deadlock).

func (*Admission) WithAllowWork added in v0.11.0

func (a *Admission) WithAllowWork(fn func()) error

WithAllowWork runs fn only when admission currently allows work, holding the same mutex as AllowMutations/AllowClaim for the full duration of fn. Use this for check-then-act sections (e.g. webhook accept + enqueue) so MarkDegraded and BeginShutdown cannot interleave between the gate and the mutation. fn must not call back into Admission methods that take a.mu (would deadlock).

type AdmissionState added in v0.11.0

type AdmissionState string

AdmissionState is the single authoritative live-daemon admission state (ADR-0015 R1 / issue #575). HTTP mutation readiness and scheduler work (full tick: discovery/HITL/claims/stale-reconcile) are projections of this state — not a second ready flag.

Trade-off (AGENTS.md new-concept gate):

Failure prevented: mid-rollout dual ready flags (ownershipAcquired vs HTTP/ scheduler gates) that disagree, admitting mutations or enqueueing work while recovery is incomplete or shutdown has begun; recovery inventing cleanliness from reusable PIDs without a single closed admission Authority.

Costs / new edge cases: sticky degraded until process restart; startup window where reads work but all mutations and work-producing ticks no-op; shutdown must BeginShutdown before storage close; every new work-producing path must call AllowMutations/AllowClaim (audited under #580); more manual_intervention quarantine instead of aggressive auto-clean.

Why simpler alternatives are insufficient: a boolean ready flag next to ownershipAcquired re-creates dual Authority; gating only ClaimNext* leaves discovery/HITL/reconcile free to mutate queue storage while admission is closed; trusting SQLite or PID probes as live Authority lags and is not atomic with admission decisions.

Legal transitions (monotonic / legal only):

starting  → ready | stopping | degraded
ready     → stopping | degraded
degraded  → stopping          (sticky until process restart; no ready)
stopping  → (none)            (terminal for this process lifetime)

any → degraded is sticky until process restart. There is no ClearDegraded: Runtime.MarkDegraded cancels producer contexts (scheduler, cleanup, webhook execute), so reopening admission without restart would leave a ready-looking daemon with permanently dead work producers.

const (
	AdmissionStarting AdmissionState = "starting"
	AdmissionReady    AdmissionState = "ready"
	AdmissionStopping AdmissionState = "stopping"
	AdmissionDegraded AdmissionState = "degraded"
)

type ConfigPatch added in v0.11.0

type ConfigPatch struct {
	Revision string
	Set      map[string]json.RawMessage
	Unset    []string
}

type ConfigPatchError added in v0.11.0

type ConfigPatchError struct {
	Kind    string
	Message string
	Paths   []string
	Err     error
}

func (*ConfigPatchError) Error added in v0.11.0

func (e *ConfigPatchError) Error() string

func (*ConfigPatchError) Unwrap added in v0.11.0

func (e *ConfigPatchError) Unwrap() error

type ConfigReloadError added in v0.11.0

type ConfigReloadError struct {
	Kind  string
	Paths []string
	Err   error
}

ConfigReloadError reports a rejected candidate without replacing the last-known-good runtime snapshot.

func (*ConfigReloadError) Error added in v0.11.0

func (e *ConfigReloadError) Error() string

func (*ConfigReloadError) Unwrap added in v0.11.0

func (e *ConfigReloadError) Unwrap() error

type ConfigReloadStatus added in v0.11.0

type ConfigReloadStatus struct {
	ConfigPath    string
	Format        string
	FilePresent   bool
	Revision      string
	LastAttemptAt *time.Time
	LastAppliedAt *time.Time
	LastError     string
	RejectedPaths []string
	FieldSources  map[string]config.ValueSource
}

ConfigReloadStatus is transient diagnostic state. The config file overlaid by the daemon's startup environment and CLI flags remains the authority for global policy; this status only explains whether that authority was applied.

type ContainmentClass added in v0.11.0

type ContainmentClass string

ContainmentClass is the startup-recovery classification of durable execution evidence after a daemon restart (ADR-0015 R8 / #581).

PID/PGID inspection is drift evidence only. It never authorizes live stop, terminal marking, requeue, or overlapping work, and never alone establishes confirmed-dead after restart.

const (
	// ContainmentConfirmedDead means Authority exists to treat the execution as
	// non-runnable for recovery purposes.
	//
	// After restart, only:
	//   - durable terminal finalization already committed before crash, or
	//   - a current-daemon owned processcontainment.Handle that has completed
	//     confirmed drain
	// may authorize this class.
	//
	// Must not authorize confirmed-dead: PID/PGID missing or not running,
	// probe-then-signal on raw PID/PGID, or leader exit alone without
	// descendant/containment proof.
	ContainmentConfirmedDead ContainmentClass = "confirmed_dead"

	// ContainmentObservedLive means a process probe matched the durable row.
	// This is evidence only — not adopted live ownership. Recovery must not
	// signal, terminalize, requeue, or start overlapping work from this class.
	ContainmentObservedLive ContainmentClass = "observed_live"

	// ContainmentUncertain covers every other observation (PID absent, command
	// mismatch, probe error, leader-exit-only without containment proof, etc.).
	// Uncertain work stays quarantined without raw PID/PGID action.
	ContainmentUncertain ContainmentClass = "uncertain"
)

type ContainmentClassification added in v0.11.0

type ContainmentClassification struct {
	Class ContainmentClass
	// Reason is a stable machine-oriented explanation (event payloads / tests).
	Reason string
	// PID is the durable PID when present (evidence only).
	PID int
}

ContainmentClassification is one classified durable observation.

type OperationLease added in v0.11.0

type OperationLease interface {
	// Context is cancelled when stop/shutdown races the in-flight claim or run.
	// Cancellation alone must not be treated as a bind permit failure — use
	// BindClaim's explicit error.
	Context() context.Context
	// BindClaim binds a successful durable claim to this lease. Returns an
	// explicit OperationPermit only when the lease is still owned and live.
	// On ErrOperationLeaseCancelled the processor must never start; the claim
	// remains owned until durable finalize then Release.
	BindClaim(item storage.QueueItemRecord) (OperationPermit, error)
	// Release drops the lease. Call immediately on claim miss/error. After a
	// successful claim, call only once complete/cancel/requeue is durably
	// committed. Never call after a finalize persistence failure.
	Release()
	// Owns reports whether this lease currently owns queueItemID (bound, not released).
	Owns(queueItemID string) bool
}

OperationLease is the Supervisor ownership token for one durable queue claim from before ClaimNext* until durable complete / cancel / requeue (ADR-0015 R6).

type OperationMeta added in v0.11.0

type OperationMeta struct {
	// ClaimedBy is the durable claimed_by value (e.g. "scheduler").
	ClaimedBy string
}

OperationMeta identifies one queue claim operation admitted before durable ClaimNext*. Loop/item identity is filled at BindClaim after a successful claim.

type OperationPermit added in v0.11.0

type OperationPermit struct {
	// contains filtered or unexported fields
}

OperationPermit is the explicit token proving BindClaim succeeded. The queue processor / agent spawn path must not start without a non-zero permit.

func (OperationPermit) QueueItemID added in v0.11.0

func (p OperationPermit) QueueItemID() string

QueueItemID returns the durable queue item bound to this permit.

func (OperationPermit) Valid added in v0.11.0

func (p OperationPermit) Valid() bool

Valid reports whether this permit authorizes starting the queue processor.

type Options

type Options struct {
	Config config.Config
	// InitialConfig and ReloadConfig are supplied by bootstrap so hot reloads
	// replay the daemon's exact startup precedence (file, environment, and CLI).
	// Tests and embedders that omit ReloadConfig simply run without a watcher.
	InitialConfig        config.LoadedFileConfig
	ReloadConfig         func() (config.LoadedFileConfig, error)
	LoadConfigAt         func(string) (config.LoadedFileConfig, error)
	ConfigReloadInterval time.Duration
	// ConfigPath is the daemon-loaded config file path (from --config /
	// LOOPER_CONFIG resolution). Runtime config management patches this source;
	// trusted review-submit children receive a separate sanitized run snapshot.
	ConfigPath                  string
	Logger                      bootstrap.Logger
	Now                         func() time.Time
	ShutdownTimeout             time.Duration
	WorktreeCleanupInitialDelay time.Duration
	OpenSQLiteCoordinator       OpenSQLiteCoordinatorFunc
	SyncConfiguredProjects      SyncConfiguredProjectsFunc
	RunSchedulerTick            RunSchedulerTickFunc
	ReadProcessCommand          ReadProcessCommandFunc
	SignalProcess               SignalProcessFunc
	DeferRecovery               bool
}

type ReadProcessCommandFunc

type ReadProcessCommandFunc func(context.Context, int) (string, error)

type RecoveryOrphanAgentCleanup

type RecoveryOrphanAgentCleanup struct {
	Attempted        bool  `json:"attempted"`
	CleanedCount     int64 `json:"cleanedCount"`
	QuarantinedCount int64 `json:"quarantinedCount"`
	// Classification counts for active execution evidence (ADR-0015 R8 / #581).
	// ConfirmedDead requires durable terminal finalization or current-daemon drain;
	// PID absence never increments ConfirmedDead.
	ConfirmedDeadCount int64  `json:"confirmedDeadCount"`
	ObservedLiveCount  int64  `json:"observedLiveCount"`
	UncertainCount     int64  `json:"uncertainCount"`
	Warning            string `json:"warning,omitempty"`
}

type RecoverySummary

type RecoverySummary struct {
	StartedAt             string                     `json:"startedAt,omitempty"`
	CompletedAt           string                     `json:"completedAt,omitempty"`
	OrphanAgentCleanup    RecoveryOrphanAgentCleanup `json:"orphanAgentCleanup"`
	ExpiredLocksReleased  int64                      `json:"expiredLocksReleased"`
	InterruptedRunsMarked int64                      `json:"interruptedRunsMarked"`
	LoopsRequeued         int64                      `json:"loopsRequeued"`
	EventsWritten         int64                      `json:"eventsWritten"`
}

type RunSchedulerTickFunc

type RunSchedulerTickFunc func(context.Context, Services) error

type Runtime

type Runtime struct {
	// contains filtered or unexported fields
}

func New

func New(options Options) *Runtime

func (*Runtime) AdmissionState added in v0.11.0

func (r *Runtime) AdmissionState() AdmissionState

AdmissionState returns the authoritative live admission state.

func (*Runtime) AllowClaim added in v0.11.0

func (r *Runtime) AllowClaim() error

AllowClaim is the scheduler work-producing projection of admission (full tick + durable claims).

func (*Runtime) AllowMutations added in v0.11.0

func (r *Runtime) AllowMutations() error

AllowMutations is the HTTP mutation readiness projection of admission.

func (*Runtime) BeginShutdown added in v0.11.0

func (r *Runtime) BeginShutdown(reason string)

BeginShutdown transitions admission to stopping without closing storage. Daemon stop drains HTTP ingress after this so mutations/claims stop first. Also cancels the scheduler context so an in-flight full tick observes cancellation during the HTTP drain window before Runtime.Stop closes the loop; work-producing lanes still recheck AllowClaim as the Authority. Cancels deferred reviewer recovery so a post-ready recovery goroutine cannot still requeue loops/queue items after admission is already stopping; the wait for recovery exit remains in Runtime.Stop via stopDeferredReviewerRecovery. Cancels webhook-forward discovery so process exit can abort in-flight CreateOrGetActiveByDedupe promptly (sticky MarkDegraded does not cancel webhook execute — accepted/202 deliveries must still complete).

Shutdown order (ADR-0015 / #577): close admission → cancel producers → confirmed-drain handles (agents + tracked non-agent shell/trusted-review). Producer cancel must run before ActiveExecutionRegistry.BeginShutdown waits on tracked non-agent handles: validation shell.Run only enters Kill after its owner ctx is canceled. Waiting first would burn the full kill budget then force-kill instead of cancel/drain promptly. SQLite close happens only in Stop after drain succeeds; drain failure is recorded for retain-storage. Non-agent Kill/Drain failures that finish after this returns are re-collected in Stop via NonAgentDrainErr.

func (*Runtime) CompleteStartup added in v0.7.1

func (r *Runtime) CompleteStartup(ctx context.Context) error

func (*Runtime) Config

func (r *Runtime) Config() config.Config

Config returns the current runtime configuration with Projects materialized from the authoritative Project Catalog.

func (*Runtime) ConfigReloadStatus added in v0.11.0

func (r *Runtime) ConfigReloadStatus() ConfigReloadStatus

func (*Runtime) ConfigSnapshot added in v0.11.0

func (r *Runtime) ConfigSnapshot() (config.Config, ConfigReloadStatus)

ConfigSnapshot returns effective values and reload metadata from one configReloadMu generation for API responses.

func (*Runtime) ExecutionMatchesProcess

func (r *Runtime) ExecutionMatchesProcess(ctx context.Context, execution storage.AgentExecutionRecord, pid int) (matches bool, running bool, err error)

func (*Runtime) MarkDegraded added in v0.11.0

func (r *Runtime) MarkDegraded(reason string) error

MarkDegraded sticks admission until process restart and cancels work-producing contexts (scheduler, recovery, cleanup) so new discovery/claims/cleanup that already passed AllowClaim cannot complete after the transition. Unlike BeginShutdown, this does not drain active agent handles and does not CancelExecute webhook workers: Forward may already have returned accepted/202 for in-memory queue entries, and sticky degrade leaves the daemon up with no GitHub retry. New webhook accepts are still refused via AllowExecute / AllowExecuteWhile. There is no clear-and-resume path: canceled producer contexts are not recreated; operators must restart looperd.

func (*Runtime) NetworkStatus added in v0.9.0

func (r *Runtime) NetworkStatus() networkclient.Status

func (*Runtime) PatchConfig added in v0.11.0

func (r *Runtime) PatchConfig(ctx context.Context, patch ConfigPatch) error

PatchConfig applies a targeted mutation to the file layer. It rereads the source under the mutation lock, validates a same-directory temporary file through the exact startup loader, performs a final identity/mode/byte check, then atomically renames it into place. See the final-check comment below for the narrow portable-filesystem race that remains.

func (*Runtime) ReconcileStaleRunningRuns added in v0.9.1

func (r *Runtime) ReconcileStaleRunningRuns(ctx context.Context) (StaleRunReconcileSummary, error)

func (*Runtime) ReconcileWebhookForwarders added in v0.8.0

func (r *Runtime) ReconcileWebhookForwarders()

func (*Runtime) RecordWebhookDelivery added in v0.8.0

func (r *Runtime) RecordWebhookDelivery(eventType, deliveryID string)

func (*Runtime) RecoverySummary

func (r *Runtime) RecoverySummary() RecoverySummary

func (*Runtime) RefreshWebhookForwarders added in v0.8.0

func (r *Runtime) RefreshWebhookForwarders() error

func (*Runtime) ReloadConfig added in v0.11.0

func (r *Runtime) ReloadConfig(ctx context.Context) error

ReloadConfig reparses the configured source using the exact loader captured at bootstrap. A candidate is published atomically only when every changed field is hot-safe; invalid and restart-bound candidates leave running work and the last-known-good snapshot untouched.

func (*Runtime) Services

func (r *Runtime) Services() Services

func (*Runtime) ShutdownDrainError added in v0.11.0

func (r *Runtime) ShutdownDrainError() error

ShutdownDrainError returns the drain failure recorded during BeginShutdown, if any.

func (*Runtime) Start

func (r *Runtime) Start(ctx context.Context) error

func (*Runtime) StartedAt

func (r *Runtime) StartedAt() (time.Time, bool)

func (*Runtime) Stop

func (r *Runtime) Stop(reason string)

func (*Runtime) StorageRetained added in v0.11.0

func (r *Runtime) StorageRetained() bool

StorageRetained reports whether Stop skipped SQLite close after a drain failure (ADR-0015 / #577). Operators must not treat stop as graceful success.

func (*Runtime) TriggerSchedulerClaim added in v0.8.0

func (r *Runtime) TriggerSchedulerClaim()

func (*Runtime) TriggerSchedulerTick

func (r *Runtime) TriggerSchedulerTick()

func (*Runtime) WaitForDeferredReviewerRecovery added in v0.11.0

func (r *Runtime) WaitForDeferredReviewerRecovery(ctx context.Context) error

WaitForDeferredReviewerRecovery blocks until the post-ready deferred reviewer recovery goroutine exits, or until ctx is canceled. It returns immediately when deferred recovery was never started (for example when no GitHub gateway is configured). Test fixtures call this after CompleteStartup so later inserts of terminal reviewer metadata cannot race normalizeTerminalReviewerLoopForRecovery.

func (*Runtime) WaitForShutdown

func (r *Runtime) WaitForShutdown()

func (*Runtime) WebhookForwarder added in v0.8.0

func (r *Runtime) WebhookForwarder() WebhookForwarder

func (*Runtime) WebhookStatus added in v0.8.0

func (r *Runtime) WebhookStatus() WebhookStatus

func (*Runtime) WithAllowClaim added in v0.11.0

func (r *Runtime) WithAllowClaim(fn func()) error

WithAllowClaim runs fn only while claim admission is open, holding the admission mutex for the full duration of fn so MarkDegraded/BeginShutdown cannot interleave with the critical section (webhook accept + enqueue).

func (*Runtime) WorktreeCleanupStatus added in v0.9.0

func (r *Runtime) WorktreeCleanupStatus() WorktreeCleanupStatus

type Services

type Services struct {
	Coordinator      *storage.SQLiteCoordinator
	Repositories     *storage.Repositories
	Projects         *projects.Service
	Loops            *loops.Service
	Runs             *runs.Service
	ActiveExecutions *ActiveExecutionRegistry
}

type SignalProcessFunc

type SignalProcessFunc func(int, syscall.Signal) error

type StaleRunReconcileSummary added in v0.9.1

type StaleRunReconcileSummary struct {
	Mode                string `json:"mode"`
	StartedAt           string `json:"startedAt,omitempty"`
	CompletedAt         string `json:"completedAt,omitempty"`
	CandidateRuns       int64  `json:"candidateRuns"`
	InterruptedRuns     int64  `json:"interruptedRuns"`
	LoopsRequeued       int64  `json:"loopsRequeued"`
	QueueItemsRequeued  int64  `json:"queueItemsRequeued"`
	QueueItemsCancelled int64  `json:"queueItemsCancelled"`
	CleanedExecutions   int64  `json:"cleanedExecutions"`
	// QuarantinedExecutions counts executions parked via quarantineRecoveryEvidence
	// (still-running evidence + manual_intervention). Never report these as cleaned.
	QuarantinedExecutions int64    `json:"quarantinedExecutions"`
	SkippedUncertainRuns  int64    `json:"skippedUncertainRuns"`
	EventsWritten         int64    `json:"eventsWritten"`
	RunIDs                []string `json:"runIds,omitempty"`
	LoopIDs               []string `json:"loopIds,omitempty"`
	ExecutionIDs          []string `json:"executionIds,omitempty"`
}

type SyncConfiguredProjectsFunc

type SyncConfiguredProjectsFunc func(context.Context, *projects.Service, config.Config, time.Time) error

type WebhookCounters added in v0.8.0

type WebhookCounters struct {
	DeliveriesReceived int `json:"deliveriesReceived"`
	Coalesced          int `json:"coalesced"`
	Dropped            int `json:"dropped"`
	Queued             int `json:"queued"`
	Processed          int `json:"processed"`
	Failed             int `json:"failed"`
}

type WebhookForwarder added in v0.8.0

type WebhookForwarder interface {
	Forward(context.Context, webhookforward.DeliveryRequest) (webhookforward.ForwardResult, error)
	Stats() webhookforward.Stats
	// CancelExecute aborts in-flight webhook discovery without waiting for drain.
	// BeginShutdown and MarkDegraded call this so admission-closed races cannot
	// still enqueue after a one-time AllowExecute pass.
	CancelExecute()
	Close()
}

type WebhookForwarderState added in v0.8.0

type WebhookForwarderState struct {
	Repo          string   `json:"repo"`
	Running       bool     `json:"running"`
	PID           *int     `json:"pid,omitempty"`
	Adopted       bool     `json:"adopted"`
	Latched       bool     `json:"latched"`
	LatchReason   *string  `json:"latchReason,omitempty"`
	Fingerprint   string   `json:"fingerprint,omitempty"`
	SpawnedAt     *string  `json:"spawnedAt,omitempty"`
	Command       []string `json:"command"`
	RestartCount  int      `json:"restartCount"`
	LastStartedAt *string  `json:"lastStartedAt,omitempty"`
	LastExitAt    *string  `json:"lastExitAt,omitempty"`
	LastError     string   `json:"lastError,omitempty"`
	StdoutTail    []string `json:"stdoutTail,omitempty"`
	StderrTail    []string `json:"stderrTail,omitempty"`
}

type WebhookForwarderStatus added in v0.8.0

type WebhookForwarderStatus struct {
	Repo         string   `json:"repo"`
	Events       []string `json:"events"`
	Command      []string `json:"command"`
	Running      bool     `json:"running"`
	RespawnCount int      `json:"respawnCount"`
	LastStartAt  *string  `json:"lastStartAt,omitempty"`
	LastExitAt   *string  `json:"lastExitAt,omitempty"`
	LastError    *string  `json:"lastError,omitempty"`
	Tail         []string `json:"tail"`
}

type WebhookQueueStatus added in v0.8.0

type WebhookQueueStatus struct {
	Pending       int `json:"pending"`
	Capacity      int `json:"capacity"`
	ActiveWorkers int `json:"activeWorkers"`
}

type WebhookRecentOutcome added in v0.8.0

type WebhookRecentOutcome struct {
	At      string `json:"at"`
	Outcome string `json:"outcome"`
	Message string `json:"message"`
}

type WebhookRuntimeStatus added in v0.8.0

type WebhookRuntimeStatus struct {
	Enabled         bool                     `json:"enabled"`
	Healthy         bool                     `json:"healthy"`
	Degraded        bool                     `json:"degraded"`
	Endpoint        *string                  `json:"endpoint,omitempty"`
	DegradedReasons []string                 `json:"degradedReasons,omitempty"`
	Forwarders      []WebhookForwarderStatus `json:"forwarders"`
}

type WebhookStatus added in v0.8.0

type WebhookStatus struct {
	Enabled                     bool                    `json:"enabled"`
	Mode                        config.WebhookMode      `json:"mode"`
	ConfiguredTunnelProjectIDs  []string                `json:"configuredTunnelProjectIds,omitempty"`
	FallbackPollIntervalSeconds int                     `json:"fallbackPollIntervalSeconds"`
	ListenerPath                string                  `json:"listenerPath"`
	EndpointURL                 string                  `json:"endpointUrl"`
	TunnelListenerURL           string                  `json:"tunnelListenerUrl,omitempty"`
	TunnelPublicBaseURL         string                  `json:"tunnelPublicBaseUrl,omitempty"`
	Degraded                    bool                    `json:"degraded"`
	DegradedReasons             []string                `json:"degradedReasons"`
	Queue                       WebhookQueueStatus      `json:"queue"`
	Counters                    WebhookCounters         `json:"counters"`
	RecentOutcomes              []WebhookRecentOutcome  `json:"recentOutcomes"`
	Forwarders                  []WebhookForwarderState `json:"forwarders"`
	TunnelHooks                 []WebhookTunnelState    `json:"tunnelHooks"`
}

type WebhookTunnelState added in v0.8.1

type WebhookTunnelState struct {
	Repo                string  `json:"repo"`
	HookID              *int64  `json:"hookId,omitempty"`
	ManagedURL          string  `json:"managedUrl,omitempty"`
	LastPingAt          *string `json:"lastPingAt,omitempty"`
	ConsecutiveDisables int64   `json:"consecutiveDisables"`
	Latched             bool    `json:"latched"`
	Orphaned            bool    `json:"orphaned"`
	LastError           string  `json:"lastError,omitempty"`
}

type WorktreeCleanupStatus added in v0.9.0

type WorktreeCleanupStatus struct {
	Enabled         bool    `json:"enabled"`
	DryRun          bool    `json:"dryRun"`
	LastStartedAt   *string `json:"lastStartedAt,omitempty"`
	LastCompletedAt *string `json:"lastCompletedAt,omitempty"`
	LastStatus      string  `json:"lastStatus"`
	Scanned         int     `json:"scanned"`
	Candidates      int     `json:"candidates"`
	Cleaned         int     `json:"cleaned"`
	Skipped         int     `json:"skipped"`
	Failed          int     `json:"failed"`
	LastError       string  `json:"lastError,omitempty"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL