runtask

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Oct 2, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package runtask implements the repository task-run lifecycle that ADR-0055 moves from ce-agent-kit into gz-git: run identity, owner, lock, and receipt state live here, and task-manager consumes them through the JSON surface.

The behavioral reference is the parity fixture suite in tests/parity/ — golden files there are machine-captured from the autonomous CE binary (master f927ae5d). When this implementation and the fixtures disagree, the fixtures win; docs/design/RUN_LIFECYCLE_PARITY_CONTRACT.md describes the same surface at the source level.

Index

Constants

View Source
const (
	// TaskRuntimeConfigFile holds this checkout's runtime identity --
	// repository-id, worktree-roots, integration-provider. Those keys name
	// machine-local absolute paths, which is why the repository's .gitignore
	// should exclude it.
	TaskRuntimeConfigFile = ".gz-git-task.yaml"
	// RuntimeStateDir names the directory the run lifecycle keeps its state
	// in, rooted at the git common dir: `<git-common-dir>/gz-git/`. The
	// common dir is shared by a repository and every linked worktree and is
	// never part of any working tree, so state written there outlives a
	// reclaimed worktree without leaving a path that could recreate one.
	RuntimeStateDir = "gz-git"
)
View Source
const (
	TaskExecutionStatusActive  = "ACTIVE"
	TaskExecutionStatusReady   = "READY"
	TaskExecutionStatusAborted = "ABORTED"
	TaskExecutionStatusBlocked = "BLOCKED"
	TaskExecutionStatusDone    = "DONE"
	TaskExecutionStatusUnknown = "UNKNOWN"
)

Task execution status tokens as they appear in response documents and receipt records.

Variables

This section is empty.

Functions

func IsTerminalExecutionStatus

func IsTerminalExecutionStatus(status string) bool

IsTerminalExecutionStatus reports whether a record has reached an end state -- one that no further verb can move. DONE and ABORTED are terminal; ACTIVE is running, and BLOCKED and UNKNOWN are awaiting a decision, which is why both still advertise `run-abort`. READY is not an execution status: it is the list-level answer when the runtime responded and nothing is blocked, so this predicate does not treat it as terminal or live.

func NewEngine

func NewEngine() integrateEngine

NewEngine returns the in-process integration engine for this binary.

func SetRuntimeRevision

func SetRuntimeRevision(v string)

SetRuntimeRevision records the build commit for receipt evidence. An unstamped build says unknown rather than inventing an identifier.

func SetRuntimeVersion

func SetRuntimeVersion(v string)

SetRuntimeVersion records the build version for lifecycle reporting.

Types

type CommandDiagnostics

type CommandDiagnostics struct {
	Command    string   `json:"command,omitempty"`
	Executable string   `json:"executable,omitempty"`
	Args       []string `json:"args,omitempty"`
	Cwd        string   `json:"cwd,omitempty"`
	ExitCode   int      `json:"exitCode"`
	Stdout     string   `json:"stdout,omitempty"`
	Stderr     string   `json:"stderr,omitempty"`
	Error      string   `json:"error,omitempty"`
	NotStarted bool     `json:"notStarted,omitempty"`
}

CommandDiagnostics records one executed or not-started command's evidence for receipt diagnostics.

type CommandRequest

type CommandRequest struct {
	Command string
	Args    []string
	WorkDir string
	Env     map[string]string
}

CommandRequest names one external command: the tool, its fixed argv, the working directory, and extra environment entries.

type CommandResult

type CommandResult struct {
	Stdout     string
	Stderr     string
	ExitCode   int
	Command    string
	Executable string
	Args       []string
	WorkDir    string
	Error      string
	NotStarted bool
}

CommandResult carries output and exit state for one command execution, including the evidence fields receipts record when the command never ran.

type CommandRunner

type CommandRunner interface {
	Run(context.Context, CommandRequest) (CommandResult, error)
}

CommandRunner executes an external command and captures stdout/stderr separately.

type DerivedTaskState

type DerivedTaskState struct {
	Execution      TaskExecution        `json:"execution"`
	Status         string               `json:"status"`
	AllowedActions []string             `json:"allowedActions"`
	Reason         string               `json:"reason"`
	NextAction     string               `json:"nextAction"`
	FinishReady    bool                 `json:"finishReady"`
	Diagnostics    []CommandDiagnostics `json:"diagnostics,omitempty"`
}

DerivedTaskState pairs one recorded execution with the status derived from live repository and receipt evidence.

type Executor

type Executor struct{}

Executor is the production command runner. Only fixed-argv invocations of audited tools (git, wt) go through it; user input never reaches a shell.

func NewExecutor

func NewExecutor() *Executor

NewExecutor returns the production command runner.

func (*Executor) Run

Run executes req with a resolved absolute executable path and separated output capture, mapping cancellation and lookup failures onto the recorded exit codes (125 canceled, 127 not started).

type ImportCEReport

type ImportCEReport struct {
	SchemaVersion int    `json:"schemaVersion"`
	Imported      bool   `json:"imported"`
	DryRun        bool   `json:"dryRun,omitempty"`
	Executions    int    `json:"executions"`
	Receipts      int    `json:"receipts"`
	SourceDir     string `json:"sourceDir"`
	TargetDir     string `json:"targetDir"`
}

ImportCEReport is the answer of the explicit one-shot import.

type IntegrationNetworkPolicy

type IntegrationNetworkPolicy string

IntegrationNetworkPolicy limits network use by the declared integration provider. An omitted value preserves the existing provider-controlled mode.

const (
	IntegrationNetworkPolicyAllow   IntegrationNetworkPolicy = "allow"
	IntegrationNetworkPolicyNoFetch IntegrationNetworkPolicy = "no-fetch"
)

Integration network policy values accepted by the runtime declaration.

type IntegrationProviderDiagnostics

type IntegrationProviderDiagnostics struct {
	Name         string          `json:"name"`
	Present      bool            `json:"present"`
	Version      string          `json:"version,omitempty"`
	Executable   string          `json:"executable,omitempty"`
	Capabilities map[string]bool `json:"capabilities"`
}

IntegrationProviderDiagnostics records the read-only contract probe for the integration engine selected by the repository. When the engine is this binary the probe reports capabilities from in-process knowledge; the executable and version still name this build.

type Service

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

Service carries one repository root and its collaborators for the run lifecycle verbs. It holds no per-run state: all durable state lives in the store under the git common dir.

func NewService

func NewService(root string, runner CommandRunner, engine integrateEngine) *Service

NewService builds a Service for root, using runner for external commands and engine for the in-process integration boundary.

func (*Service) Abort

func (s *Service) Abort(ctx context.Context, task, reason string) (resp TaskResponse, code int)

Abort closes one execution as ABORTED by appending a terminal receipt.

Without it BLOCKED is an absorbing state. A record whose worktree and branch were reclaimed outside gz-git -- by `gz-git integrate`, which is allowed to do exactly that -- derives BLOCKED forever, and listResponse rolls a single blocked record up into a repository-wide failure, so run list and run status exit 1 for as long as it exists.

Evidence is appended, never rewritten. The execution stays in executions.json and receipts.jsonl only grows -- the same shape Finish uses to record a DONE outcome -- so aborting closes the derived state without erasing what happened. Abort touches no worktree, branch, or remote ref: a surviving worktree is reported as a warning for its owner to reclaim deliberately.

func (*Service) Discard

func (s *Service) Discard(ctx context.Context, task, reason, takeOverFrom string) (resp TaskResponse, code int)

Discard removes the exact clean worktree recorded for an unintegrated task. It deliberately uses Worktrunk, and writes an intent receipt before asking Worktrunk to mutate anything, so an interrupted reclaim remains auditable and can be retried from the recorded identity.

func (*Service) Doctor

func (s *Service) Doctor(ctx context.Context) TaskResponse

Doctor answers whether the task runtime dependencies of this checkout are ready, without acquiring the mutation lock: configuration, integration provider presence and capabilities, lock, and receipt readability.

func (*Service) Finish

func (s *Service) Finish(ctx context.Context, task string) (resp TaskResponse, code int)

Finish integrates a clean, fully pushed task branch into its configured source branch and reclaims the worktree, local branch, and remote branch in the same step, answering DONE only from receipt evidence that proves all three.

func (*Service) ImportCE

func (s *Service) ImportCE(ctx context.Context, dryRun bool) (ImportCEReport, error)

ImportCE copies in-flight CE task-runtime records into this runtime's state directory, byte for byte. Executions and receipts are evidence: they carry CE's tool stamps and CE's words, and rewriting them would falsify what happened; deriveState reconciles their worktrees and branches against the live repository the same way it reconciles any reclaimed run.

It refuses to merge: gz-git state that already exists is an operator decision, not an import. It refuses to invent: a source with neither executions nor receipts has nothing to carry over. Records are parsed before they are copied, so a corrupt CE file stops here instead of surfacing later as an unreadable state.

func (*Service) List

func (s *Service) List(ctx context.Context) TaskResponse

List answers the repository-shared execution inventory without acquiring the mutation lock: every live execution keeps its own derived state, and terminal ones fold into the aggregate counts.

func (*Service) Recover

func (s *Service) Recover(ctx context.Context, task string) (resp TaskResponse, code int)

Recover closes the narrow exit-3 outcome from run finish. It never invokes the integration engine and never removes a worktree or a ref: it only verifies that the recorded task commit is in the pushed source branch, then appends evidence naming whatever engine cleanup left behind.

func (*Service) Start

func (s *Service) Start(ctx context.Context, task string, kind TaskExecutionType) TaskResponse

Start creates the owner-bound task worktree through Worktrunk and records the ACTIVE execution, returning an existing live execution instead of starting twice and replacing a closed record in place.

func (*Service) Status

func (s *Service) Status(ctx context.Context, task string) TaskResponse

Status derives one task's runtime state without acquiring the mutation lock; the empty task name asks for the repository-wide aggregate.

type StringList

type StringList []string

StringList accepts either a scalar or a sequence.

The documented shape is a sequence, but a hand-written `integrationBranch: master` parses as a scalar and would otherwise fail the whole file — turning a repository that has declared its target into one reported as having declared nothing, which is the opposite of the truth.

func (StringList) First

func (l StringList) First() string

First returns the first non-empty entry, or "" when the field declares nothing.

Several entries take the first rather than being refused, because that is the answer the worktree audit has always given for this field and a second answer here would put the disagreement back. An empty string is a declaration of nothing, not a declaration of "": a branch with no name resolves to no ref, and failing later with an unresolvable name hides which of the two happened.

func (*StringList) UnmarshalYAML

func (l *StringList) UnmarshalYAML(value *yaml.Node) error

UnmarshalYAML implements the scalar-or-sequence tolerance. A mapping is not a shape this field has; it reads as no declaration rather than failing the whole file, because the other keys in it are still true.

type TaskExecution

type TaskExecution struct {
	Task      string            `json:"task"`
	Type      TaskExecutionType `json:"type"`
	Source    string            `json:"source"`
	Owner     TaskOwner         `json:"owner"`
	Branch    string            `json:"branch"`
	Worktree  string            `json:"worktree"`
	StartedAt time.Time         `json:"startedAt"`
	UpdatedAt time.Time         `json:"updatedAt"`
}

TaskExecution is the current execution identity from metadata.

type TaskExecutionType

type TaskExecutionType string

TaskExecutionType classifies a task run session.

const (
	TaskExecutionTypeFeat     TaskExecutionType = "feat"
	TaskExecutionTypeFix      TaskExecutionType = "fix"
	TaskExecutionTypeRefactor TaskExecutionType = "refactor"
	TaskExecutionTypeDocs     TaskExecutionType = "docs"
	TaskExecutionTypeTest     TaskExecutionType = "test"
	TaskExecutionTypeChore    TaskExecutionType = "chore"
	TaskExecutionTypePerf     TaskExecutionType = "perf"
)

Task execution types, matching the commit type the task branch name and its worktree directory were created with.

func (TaskExecutionType) Valid

func (t TaskExecutionType) Valid() bool

Valid reports whether t is one of the declared execution types.

type TaskLockInfo

type TaskLockInfo struct {
	Path       string    `json:"path"`
	PID        int       `json:"pid,omitempty"`
	CreatedAt  time.Time `json:"createdAt,omitempty"`
	AgeSeconds int64     `json:"ageSeconds,omitempty"`
	PIDRunning *bool     `json:"pidRunning,omitempty"`
}

TaskLockInfo describes the mutation lock file for diagnostics: where it lives, who holds it, and whether that holder is still running.

type TaskOwner

type TaskOwner struct {
	Actor string `json:"actor"`
	Host  string `json:"host"`
	Kind  string `json:"kind"`
}

TaskOwner expresses who controls one execution.

func (TaskOwner) String

func (o TaskOwner) String() string

String renders the owner as "actor/host", the form reasons quote.

func (TaskOwner) Valid

func (o TaskOwner) Valid() bool

Valid reports whether the owner names both an actor and a host.

type TaskReceipt

type TaskReceipt struct {
	Task                string               `json:"task"`
	Operation           string               `json:"operation"`
	Status              string               `json:"status"`
	Owner               TaskOwner            `json:"owner"`
	PerformedBy         *TaskOwner           `json:"performedBy,omitempty"`
	Branch              string               `json:"branch"`
	Worktree            string               `json:"worktree"`
	Reason              string               `json:"reason,omitempty"`
	CreatedAt           time.Time            `json:"createdAt"`
	SourceHead          string               `json:"sourceHead,omitempty"`
	SourceHeadBefore    string               `json:"sourceHeadBefore,omitempty"`
	BaseHead            string               `json:"baseHead,omitempty"`
	TaskHead            string               `json:"taskHead,omitempty"`
	SourcePushed        bool                 `json:"sourcePushed"`
	WorktreeRemoved     bool                 `json:"worktreeRemoved"`
	LocalBranchRemoved  bool                 `json:"localBranchRemoved"`
	RemoteBranchRemoved bool                 `json:"remoteBranchRemoved"`
	ToolVersion         string               `json:"tool_version,omitempty"`
	ToolRevision        string               `json:"tool_revision,omitempty"`
	Diagnostics         []CommandDiagnostics `json:"diagnostics,omitempty"`
}

TaskReceipt is the durable evidence one lifecycle operation wrote: the decision it made, the Git heads it observed, and the recovery state it proved before answering DONE.

type TaskResponse

type TaskResponse struct {
	SchemaVersion  int                             `json:"schemaVersion"`
	Status         string                          `json:"status"`
	ActiveCount    int                             `json:"activeCount"`
	Task           string                          `json:"task"`
	AllowedActions []string                        `json:"allowedActions"`
	Reason         string                          `json:"reason"`
	NextAction     string                          `json:"nextAction"`
	FinishReady    bool                            `json:"finishReady"`
	Warnings       []string                        `json:"warnings,omitempty"`
	Lock           *TaskLockInfo                   `json:"lock,omitempty"`
	Execution      *TaskExecution                  `json:"execution,omitempty"`
	Executions     []TaskExecution                 `json:"executions,omitempty"`
	States         []DerivedTaskState              `json:"states,omitempty"`
	Receipt        *TaskReceipt                    `json:"receipt,omitempty"`
	Diagnostics    []CommandDiagnostics            `json:"diagnostics,omitempty"`
	Provider       *IntegrationProviderDiagnostics `json:"provider,omitempty"`
}

TaskResponse is the single response document every verb prints with --json; task-manager parses only this shape.

type TaskRuntimeConfig

type TaskRuntimeConfig struct {
	SchemaVersion            int                      `yaml:"schema-version,omitempty" json:"schemaVersion"`
	RepositoryID             string                   `yaml:"repository-id" json:"repositoryId"`
	WorktreeRoots            map[string]string        `yaml:"worktree-roots" json:"worktreeRoots"`
	IntegrationProvider      string                   `yaml:"integration-provider" json:"integrationProvider"`
	IntegrationNetworkPolicy IntegrationNetworkPolicy `yaml:"integration-network-policy,omitempty" json:"integrationNetworkPolicy"`
}

TaskRuntimeConfig declares repository-owned runtime configuration.

Jump to

Keyboard shortcuts

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