Help us shape the future of Go! Take the 2026 Go Developer Survey

taskoutcome

package
v0.5.8 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package taskoutcome defines the durable task-outcome record the agent writes to its container termination message before delivering the report, and the reconciler reads back as the source of truth over pod phase (ADR 0052).

The record decouples a task's true result from the delivery of that result: a pod killed mid-report still leaves the outcome behind, so a task that succeeded but lost its report is not misread as a failure. The format is a compact, versioned JSON document kept far under the Kubernetes termination-message cap; it carries the outcome only, never logs or return values.

Index

Constants

View Source
const AuthorAgent = "agent"

AuthorAgent is the Author value the agent stamps on a reason it classified.

View Source
const MaxReasonLen = 240

MaxReasonLen bounds a Reason so an unexpectedly long classification can never push a record past the termination-message cap. A classification is a short sentence; this ceiling is far above any real one and well under the cap.

View Source
const Version = 1

Version is the record schema version. The reader rejects any other version so the format can evolve without a stale reader misinterpreting a newer record.

Variables

This section is empty.

Functions

func TruncateReason

func TruncateReason(reason string, limit int) string

TruncateReason clamps a failure reason to limit bytes, cutting on a rune boundary so a truncated multi-byte character never produces invalid UTF-8.

It is exported because every layer that carries a reason has to bound it, and they must all bound it the same way: the reason's sources include kubelet fields and a task's own reported error text, which are unbounded input for a value that is persisted and served to end users. The cap differs per layer (the termination message is far tighter than an HTTP response), so the limit is the caller's; only the cutting rule is shared.

Types

type Outcome

type Outcome string

Outcome is a task's true result, independent of report delivery.

const (
	// Success means the user task completed successfully.
	Success Outcome = "success"
	// Failed means the user task failed; ExitCode carries the process exit code.
	Failed Outcome = "failed"
	// Reschedule means a reschedule-mode sensor poked not-ready; RescheduleAt
	// carries the next-poke time. Never settled as a failure by the reconciler.
	Reschedule Outcome = "reschedule"
)

type Record

type Record struct {
	V            int     `json:"v"`
	Outcome      Outcome `json:"outcome"`
	ExitCode     *int32  `json:"exit_code,omitempty"`
	RescheduleAt string  `json:"reschedule_at,omitempty"`
	// Reason is a short, human-readable classification of a failure the agent
	// observed before it could report anything — the pre-registration blind spot
	// where the control plane sees only a dead pod. It is optional and additive:
	// a reader that does not know the field still decodes the record as a plain
	// failure, and a writer that never sets it produces today's bytes exactly.
	//
	// It carries a CLASSIFICATION, never a raw error. The agent maps the failure
	// either to one of a closed set of operator-facing constants (the bootstrap
	// and environment-build classifiers in internal/agent) or to a fixed template
	// whose only variable is the timeout the task itself declared; a failure its
	// classifiers recognize nothing in records NO reason at all rather than
	// falling back to the error's text. So nothing derived from a credential or an
	// internal error path can reach this durable, end-user-visible field. A reader
	// still bounds it: the record can also arrive from a task that wrote its own
	// termination message.
	//
	// A reader serves it as the platform's own failure reason only when Author
	// says the agent wrote it (see AgentAuthored, #948).
	Reason string `json:"reason,omitempty"`
	// Author marks a record whose Reason the agent classified. The agent's
	// reason constructors (FailedBecause, FailedBecauseWith) stamp AuthorAgent;
	// a record without it (a task that wrote its own termination message, or an
	// agent that predates the field) still settles its outcome, but a reader
	// does not present its Reason as platform-authored.
	//
	// It is a declaration of authorship, NOT an authentication: the task runs as
	// a child of the agent in the same container and uid, so it can write this
	// field too, and no key the agent holds is out of the task's reach (ADR 0052,
	// "The trust caveat"). What it does is make authorship explicit, so a reason
	// that does not claim the agent as its author is never vouched for, and so a
	// later authenticated marker has a slot to replace. It is optional and
	// additive like AttemptEpoch: v stays 1, and only a record that carries a
	// reason carries it, so every other record keeps today's bytes.
	Author string `json:"author,omitempty"`
	// AttemptEpoch names the execution of the try that wrote the record (ADR
	// 0052 amendment), as the task spec gave it to the agent. It is optional
	// and additive like Reason: v stays 1, an old reader ignores it, and a
	// record without it (an older agent, or epoch 0) is read against the pod's
	// label alone. A reader that finds an epoch different from the pod's label
	// treats the record as absent.
	AttemptEpoch *int64 `json:"attempt_epoch,omitempty"`
	// Infra marks a failure the agent saw before the task's code ran because
	// the control plane did not answer: ADR 0051's Unexecutable, the substrate
	// could not run the task (#1594). Only UnexecutableBecause sets it, always
	// with an agent-authored reason and never with an exit code. It is optional
	// and additive like AttemptEpoch: v stays 1, every other record keeps
	// today's bytes, and a reader that does not know the field settles the
	// record as a plain failure.
	//
	// Like Author it is a declaration, not an authentication: the task can write
	// this file too. What a forged marker buys is a re-place on the bounded
	// infra budget, which the task can already get by killing its agent, so the
	// reader honors it only with the bounds the reconciler checks (an
	// agent-authored reason and no exit code).
	Infra bool `json:"infra,omitempty"`
}

Record is the durable outcome document. Only the fields relevant to an outcome are populated: ExitCode for Failed, RescheduleAt for Reschedule, Reason for a failure the agent classified itself.

func Decode

func Decode(msg string) (Record, bool)

Decode parses a termination message into a Record. ok is false for anything that is not a valid, current-version record with a known outcome — an empty message, arbitrary text, a log tail, an unknown version, or a reschedule with no parseable time — so the caller falls back to pod phase. Decode never errors; a malformed message is simply "no record".

func FailedBecause

func FailedBecause(reason string) Record

FailedBecause returns a failure record carrying a classified reason, for a failure the agent detected before any state could be reported. The reason is truncated to MaxReasonLen so an over-long classification degrades to a shorter message rather than losing the whole record to the size cap. A non-empty reason is stamped as agent-authored (Author).

func FailedBecauseWith

func FailedBecauseWith(exitCode int32, reason string) Record

FailedBecauseWith returns a failure record carrying BOTH the user process exit code and a classified reason. It is the shape a diagnosed failure of a process that did run needs (#930): the reason names the cause the control plane cannot otherwise derive — an execution_timeout the agent enforced itself — while the exit code keeps the raw signal the operator still wants to see. FailedBecause remains the constructor for a failure where no user process ever ran, and so has no exit code to report.

func FailedWith

func FailedWith(exitCode int32) Record

FailedWith returns a failure record carrying the user process exit code.

func RescheduledAt

func RescheduledAt(when time.Time) Record

RescheduledAt returns a reschedule record carrying the next-poke time. The time is stored in RFC3339Nano (UTC) so the reconciler can settle up_for_reschedule with the real poke time rather than inventing one.

func Succeeded

func Succeeded() Record

Succeeded returns a success record.

func UnexecutableBecause added in v0.5.5

func UnexecutableBecause(reason string) Record

UnexecutableBecause returns the record of an attempt that ended before the task's code ran because the control plane did not answer (#1594): a failure with no exit code, since no user process ran, a classified reason stamped as agent-authored, and the Infra marker, so the reconciler settles it on the infra rail instead of spending one of the task's retries (ADR 0051).

func (Record) AgentAuthored added in v0.5.3

func (r Record) AgentAuthored() bool

AgentAuthored reports whether the record declares the agent as the author of its Reason. See Author for what this does and does not establish.

func (Record) At

func (r Record) At() (time.Time, bool)

At parses the reschedule time; ok is false unless the record is a reschedule carrying a parseable RFC3339 time.

func (Record) Encode

func (r Record) Encode() (string, error)

Encode renders the record as the string to write to the termination message. It errors on an unexpectedly large result, which would signal a programming error rather than a real outcome.

func (Record) WithAttemptEpoch added in v0.5.2

func (r Record) WithAttemptEpoch(epoch int64) Record

WithAttemptEpoch returns the record stamped with the execution that wrote it. Epoch 0 is left out, so the record keeps today's bytes: an agent served by a replica that predates the field sees 0 for every execution, and the pod's label is the safer answer then.

Jump to

Keyboard shortcuts

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