pkg

package
v0.9.1 Latest Latest
Warning

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

Go to latest
Published: Aug 19, 2026 License: BSD-2-Clause Imports: 24 Imported by: 0

Documentation

Index

Constants

View Source
const (
	PlanOutcomeReady          = "ready"
	PlanOutcomeNoUpdateNeeded = "no_update_needed"
	PlanOutcomeNeedsInput     = "needs_input"
)

Outcome values for PlanOutput.Outcome.

View Source
const (
	VulnActionFix  = "fix"
	VulnActionPark = "park"
)

Action values for PlanVuln.Action.

View Source
const (
	ResultOutcomeOpened         = "opened"
	ResultOutcomeAdopted        = "adopted"
	ResultOutcomeNoUpdateNeeded = "no_update_needed"
	ResultOutcomeFailed         = "failed"
)

Outcome values for ResultOutput.Outcome.

View Source
const AgentLogin = "github-update-go-agent"

AgentLogin is the GitHub-task-system identity of this agent. Per the platform doctrine the CONTROLLER writes previous_assignee — the constant exists for logs and messages only; steps never mutate assignee.

Variables

View Source
var AvailablePRTargets = PRTargets{PRTargetDraft, PRTargetReady}

AvailablePRTargets lists every PRTarget the agent accepts. Validate ranges over this collection — it is the single source of truth.

AvailableUpdateScopes lists every UpdateScope the agent accepts. Validate ranges over this collection — it is the single source of truth.

Functions

func BuildEnv

func BuildEnv(
	ghToken, anthropicBaseURL, anthropicAuthToken, anthropicModel string,
) map[string]string

BuildEnv assembles the env map forwarded into the Claude CLI subprocess. Only set values are forwarded so the subprocess sees a clean env. Shared by both the Kafka entry point (main.go) and the local-CLI entry point (cmd/run-task/main.go).

GH_TOKEN is threaded explicitly because the ClaudeRunner strips the pod env to an allowlist — os.Setenv alone does not reach the Claude subprocess.

func NewClaudeAuthStep

func NewClaudeAuthStep(prober ClaudeProber) agentlib.Step

NewClaudeAuthStep wires the claude-auth preflight with its prober seam.

func NewExecutionStep

func NewExecutionStep(
	runner claudelib.ClaudeRunner,
	ops git.GitOps,
	gh GhCli,
	gate GateRunner,
	bulk BulkUpdater,
	ghToken string,
	prTarget PRTarget,
	autoMergeLabel string,
	defaultScope UpdateScope,
) agentlib.Step

NewExecutionStep wires the execution step with its seams: the Claude runner (repair + CHANGELOG sub-call), the GitOps seam (clone/branch/commit/ push), the gh CLI seam (PR create + adopt), the gate runner (deterministic green-gate re-run), the bulk updater (deterministic `go get -u ./...` + `go mod tidy`, run in Go so the model cannot background it), and the default update scope (UPDATE_SCOPE env; frontmatter `update_scope` overrides per task). prTarget selects draft or ready.

func NewGHTokenCheckStep

func NewGHTokenCheckStep(ghToken string) agentlib.Step

NewGHTokenCheckStep constructs the preflight step. ghToken is the resolved token from startup auth (passed by main → factory). Empty means the step fails fast on the first run.

func NewPlanningRunner added in v0.6.0

func NewPlanningRunner(config claudelib.ClaudeRunnerConfig) claudelib.ClaudeRunner

NewPlanningRunner constructs the tool-result-logging planning runner.

func NewPlanningStep

func NewPlanningStep(
	runner claudelib.ClaudeRunner,
	ops git.GitOps,
	gate GateRunner,
	ghToken string,
	scope InstallationScope,
	defaultScope UpdateScope,
) agentlib.Step

NewPlanningStep wires the planning step with its Claude runner (inspection LLM), the GitOps seam (clone at ref), the GateRunner (repo gate detection + full scanner-output capture), the GitHub token (HTTPS auth URL transformation), the installation-scope allowlist check, and the default update scope (UPDATE_SCOPE env; frontmatter `update_scope` overrides per task).

func NewReviewStep

func NewReviewStep(
	ops git.GitOps,
	gh GhCli,
	gate GateRunner,
	ghToken string,
	prTarget PRTarget,
) agentlib.Step

NewReviewStep wires the ai_review verifier with its GitOps seam (fresh clone + tag/rev inspection), gh CLI seam (PR state), gate runner (independent gate re-run), the GitHub token, and the configured PRTarget the step compares observed draft-ness against. On approval the step writes the plain-text ## Your Move operator-action block (PR link + merge action + change summary) above ## Plan for the human operator.

Types

type BulkUpdateResult added in v0.5.0

type BulkUpdateResult struct {
	// Ran is false when the sequence could not complete. FailDetail then
	// explains why, and the model is told to treat the update as NOT done.
	Ran bool
	// FailDetail is empty when Ran is true.
	FailDetail string
	// Output is the tail of the combined command output, for the prompt.
	Output string
}

BulkUpdateResult reports what the deterministic bulk update did.

Like FunnelResult in github-pr-review-agent, this is returned instead of a bare error for the expected non-fatal outcomes, so the caller can surface a fail-closed condition into the prompt rather than aborting the pipeline. The model must be told the update did not run — never left to assume it did.

type BulkUpdater added in v0.5.0

type BulkUpdater interface {
	Run(ctx context.Context, workdir string) (BulkUpdateResult, error)
}

BulkUpdater runs the mechanical dependency-update sequence (`go get -u ./...` + `go mod tidy`) in Go, before the execution model call.

The agent runs this itself rather than instructing the model to. Left to the model, a long-running `go get` invites backgrounding: on 2026-08-16 the execution model put it in a harness background task and then blocked on TaskOutput for 600s, timed out, and re-issued the identical blocking call on the same task_id — burning the Job's full 1800s activeDeadlineSeconds and producing nothing (bborbe/ip, job bc3c6599; also bborbe/run, bborbe/beactive).

Prompt rules could not prevent it: execution.md already forbade backgrounding, but named only the shell forms (`&`, `nohup`, detached jobs), not the harness's own run_in_background/TaskOutput. Running the step in Go removes the model's ability to express it in any form at all — the same reasoning that moved the ast-grep funnel into Go in github-pr-review-agent.

func NewBulkUpdater added in v0.5.0

func NewBulkUpdater() BulkUpdater

NewBulkUpdater constructs the production BulkUpdater.

type ClaudeProber

type ClaudeProber interface {
	// Probe returns nil when claude is authenticated and reachable, or an
	// error describing the failure otherwise.
	Probe(ctx context.Context) error
}

ClaudeProber runs a trivial claude invocation to confirm the CLI is authenticated before any prompt-bearing phase runs.

func NewClaudeProber

func NewClaudeProber(claudeConfigDir claudelib.ClaudeConfigDir) ClaudeProber

NewClaudeProber constructs a ClaudeProber that shells out to `claude --print`. claudeConfigDir, when non-empty, is exported as CLAUDE_CONFIG_DIR so the probe reads the same login the agent will.

type GateRunner

type GateRunner interface {
	// RunTarget runs `make <target>` in workdir. Returns the bounded
	// combined-output tail, the process exit code (0 on success), and a
	// non-nil error when the target failed or could not be started.
	RunTarget(ctx context.Context, workdir, target string) (tail string, exitCode int, err error)

	// RunTargetFull runs `make <target>` in workdir and returns the FULL
	// combined output (no truncation), the process exit code (0 on success),
	// and a non-nil error when the target failed or could not be started.
	// Planning uses this to capture the complete raw scanner output; the
	// bounded RunTarget tail remains for failure messages.
	RunTargetFull(
		ctx context.Context,
		workdir, target string,
	) (output string, exitCode int, err error)
}

GateRunner runs one repo gate target (`make -C <workdir> <target>`) and reports the exit code plus a bounded output tail. The execution step uses it as the deterministic green-gate after the Claude sub-call; the ai_review step re-runs the same targets independently.

func NewOSExecGateRunner

func NewOSExecGateRunner() GateRunner

NewOSExecGateRunner returns a GateRunner shelling out to make. The gate inherits the full pod env (os.Environ()): repo Makefiles run scanners via `go run tool@version`, which needs GOPROXY/GOPATH/HOME plus GH_TOKEN for private module fetches — the design accepts in-pod arbitrary code execution for own repos (design § 7.2).

type GhCli

type GhCli interface {
	// CreatePR opens a pull request from head against base, running gh
	// inside workdir so the repo is inferred from the git remote. target
	// selects draft or ready-for-review at creation time. label, when
	// non-empty, is added to the PR at creation (e.g. "auto-merge" to opt
	// the PR into GitHub-native auto-merge). Returns the PR URL.
	CreatePR(
		ctx context.Context,
		workdir, base, head, title, body string,
		target PRTarget,
		label string,
	) (string, error)

	// FindOpenPRByHead returns the URL of an open PR whose head is the
	// given branch, or "" when none exists. repo is "owner/name".
	FindOpenPRByHead(ctx context.Context, repo, head string) (string, error)

	// ViewPR returns the state (e.g. "OPEN", "MERGED", "CLOSED") and
	// draft flag of the PR identified by URL.
	ViewPR(ctx context.Context, prURL string) (state string, isDraft bool, err error)
}

GhCli is the seam between the steps and the gh binary. Three methods cover the agent's entire PR surface: create a PR at a configurable target (draft or ready-for-review), adopt an already-open PR by head branch (crash-window replay guard), and view PR state for the ai_review checks.

The creation target is chosen per deployment; the agent itself only opens PRs and never changes their draft/ready state after creation. Deliberately absent: no Ready, no Merge — the agent never flips the draft flag of an already-open pull request and never merges; the human does.

func NewOSExecGhCli

func NewOSExecGhCli(ghToken string) GhCli

NewOSExecGhCli returns a GhCli implementation that shells out to the gh binary with a minimal allowlisted env (GH_TOKEN, HOME, PATH).

type GoBump

type GoBump struct {
	From string `json:"from"`
	To   string `json:"to"`
}

GoBump records a go-directive version bump.

type InstallationScope added in v0.3.0

type InstallationScope interface {
	// Allows reports whether repo ("owner/name") is covered by the token's
	// installation. Unknown means the check itself was impossible — never
	// treat that as denial.
	Allows(ctx context.Context, repo string) ScopeVerdict
}

InstallationScope answers whether the GitHub App installation backing the agent's token covers a repo. The App installation's repository selection IS the fleet's per-stage allowlist (design § 7.2): a repo outside it must park in planning, before any clone/update work — not after ten minutes of work at the push step.

func NewGhInstallationScope added in v0.3.0

func NewGhInstallationScope(ghToken string) InstallationScope

NewGhInstallationScope returns an InstallationScope that shells out to `gh api /installation/repositories` with the agent's token. With an App installation token the endpoint lists exactly the accessible repos; with the local GH_TOKEN PAT fallback it 403s, which maps to ScopeUnknown (local runs are operator-supervised — no allowlist to enforce).

type PRTarget added in v0.4.0

type PRTarget string

PRTarget is the closed set of pull-request targets the agent may open a pull request as. It is chosen per deployment at creation time only — the agent never flips an already-open pull request and never merges.

const (
	// PRTargetDraft opens the pull request as a draft (the default).
	PRTargetDraft PRTarget = "draft"
	// PRTargetReady opens the pull request ready for review.
	PRTargetReady PRTarget = "ready"
)

func ParsePRTarget added in v0.4.0

func ParsePRTarget(ctx context.Context, value string) (PRTarget, error)

ParsePRTarget resolves the configured pull-request target. An empty value means "unset" and resolves to PRTargetDraft, preserving the pre-configuration behaviour byte-for-byte. Any other unrecognised value is rejected.

func (PRTarget) IsDraft added in v0.4.0

func (p PRTarget) IsDraft() bool

IsDraft returns true if the PRTarget is PRTargetDraft.

func (PRTarget) String added in v0.4.0

func (p PRTarget) String() string

String returns the string representation of the PRTarget.

func (PRTarget) Validate added in v0.4.0

func (p PRTarget) Validate(ctx context.Context) error

Validate returns nil when the PRTarget is a member of AvailablePRTargets, otherwise returns an error naming the rejected value and the accepted set.

type PRTargets added in v0.4.0

type PRTargets []PRTarget

PRTargets is a collection of PRTarget values.

func (PRTargets) Contains added in v0.4.0

func (p PRTargets) Contains(target PRTarget) bool

Contains returns true if the collection contains target.

func (PRTargets) Strings added in v0.4.0

func (p PRTargets) Strings() []string

Strings returns a slice of string representations of each PRTarget.

type PlanOutput

type PlanOutput struct {
	Outcome string `json:"outcome"`

	// HasWork is true when the execution phase has anything to do.
	// omitempty is deliberately NOT applied so a `false` decision is
	// always written explicitly.
	HasWork bool `json:"has_work"`

	// GoBump records the go-directive bump (from current directive to the
	// image toolchain per design D5). Nil when the directive is already
	// current.
	GoBump *GoBump `json:"go_bump,omitempty"`

	// DepUpdatesExpected is true when `go list -u -m` style inspection
	// found outdated direct dependencies.
	DepUpdatesExpected bool `json:"dep_updates_expected"`

	// GateTargets are the Makefile targets forming the repo's own gate
	// (e.g. precommit, check, vulncheck). Execution and ai_review re-run
	// exactly these to exit 0 — never a hardcoded scanner.
	GateTargets []string `json:"gate_targets,omitempty"`

	// Vulns holds every scanner finding with its fix-vs-park classification.
	Vulns []PlanVuln `json:"vulns,omitempty"`

	// Reason carries the human-readable explanation on the
	// needs_input / no_update_needed paths.
	Reason string `json:"reason,omitempty"`
}

PlanOutput is the typed contract for the `## Plan` JSON section the planning step writes for every github-update-go task. Round-trips with agentlib.MarshalSectionTyped + agentlib.ExtractSection[PlanOutput].

Shapes (design § 4.3):

  • Outcome="ready" — update work exists; GateTargets populated, HasWork=true
  • Outcome="no_update_needed" — repo already current; HasWork=false
  • Outcome="needs_input" — park path (unfixable finding / precondition); Reason populated

No `Details map[string]any`: concrete fields only. Future fields require a design amendment.

type PlanVuln

type PlanVuln struct {
	// ID is the finding identifier (GO-*, CVE-*, GHSA-*).
	ID string `json:"id"`
	// Package is the vulnerable module path.
	Package string `json:"package,omitempty"`
	// FixedVersion is the patched version the scanner reported; empty when
	// no fix exists (the park case).
	FixedVersion string `json:"fixed_version,omitempty"`
	// Scanner names which gate scanner flagged the finding
	// (govulncheck | osv-scanner | trivy).
	Scanner string `json:"scanner,omitempty"`
	// Action is "fix" (patched version exists — bump it) or "park"
	// (no fix / out-of-scope major — task parks per design D4).
	Action string `json:"action"`
	// Reason is the one-line classification justification.
	Reason string `json:"reason,omitempty"`
}

PlanVuln is one scanner finding with its planned action.

type ResultOutput

type ResultOutput struct {
	Outcome string `json:"outcome"`
	Branch  string `json:"branch,omitempty"`
	PRURL   string `json:"pr_url,omitempty"`

	// GateExit is the exit code of the last gate target run (0 = green).
	// omitempty is deliberately NOT applied — an explicit 0 documents the
	// green gate.
	GateExit int `json:"gate_exit"`

	// DepsUpdated counts the module bumps the update sequence applied.
	DepsUpdated int `json:"deps_updated"`

	// VulnsFixed lists the finding IDs resolved by the update — always a
	// subset of the plan's fix-action vulns.
	VulnsFixed []string `json:"vulns_fixed,omitempty"`

	// FailedTarget names the gate target that stayed red (failure path only).
	FailedTarget string `json:"failed_target,omitempty"`

	ErrorCategory git.ErrorCategory `json:"error_category,omitempty"`
	Error         string            `json:"error,omitempty"`
}

ResultOutput is the typed contract for the `## Result` JSON section the execution step writes. Round-trips with agentlib.MarshalSectionTyped + agentlib.ExtractSection[ResultOutput].

Shapes (design § 4.3):

  • Outcome="opened" — draft PR created; Branch/PRURL populated, GateExit=0
  • Outcome="adopted" — crash-window replay found the PR already open; adopted as-is
  • Outcome="no_update_needed" — the update sequence produced no effective change (workdir diff empty or CHANGELOG.md-only) once MVS settled; no commit/push/PR, workdir discarded (go-skeleton PR #51 guard)
  • Outcome="failed" — any failure; ErrorCategory + Error populated

Invariant (design § 4.4): Branch == "fix/update-go-" + ref[:7]; VulnsFixed ⊆ {v.ID | v ∈ Plan.Vulns, Action=fix}.

type ReviewChecks

type ReviewChecks struct {
	// PROpen — `gh pr view` reports state OPEN.
	PROpen bool `json:"pr_open"`
	// PRDraft — the raw isDraft value reported by `gh pr view`.  The review
	// compares this against the configured PR_TARGET (draft default, ready
	// opt-in); a mismatch is surfaced in the notes and causes approval to
	// fail.  This field always reports what was observed — the match verdict
	// lives in the approval flag and the notes, not here.
	PRDraft bool `json:"pr_draft"`
	// GateGreen — every planned gate target re-ran to exit 0 on a fresh
	// worktree at the branch.
	GateGreen bool `json:"gate_green"`
	// VulnsClear — the scanner-bearing gate targets came back green on the
	// re-run (the gate IS the vuln verdict, per design § 7.3 the repo's
	// gate wraps govulncheck/osv-scanner/trivy).
	VulnsClear bool `json:"vulns_clear"`
	// ChangelogUnreleased — CHANGELOG.md carries a bullet under
	// ## Unreleased and introduces no new version header vs master.
	ChangelogUnreleased bool `json:"changelog_unreleased"`
	// NoNewTag — `git ls-remote --tags` shows no tag pointing at any
	// branch commit.
	NoNewTag bool `json:"no_new_tag"`
}

ReviewChecks holds the boolean verification results of the pure-Go ai_review step. Every field is derived from independent re-execution — never copied from ## Result (design § 4.4).

type ReviewOutput

type ReviewOutput struct {
	Approved bool         `json:"approved"`
	Checks   ReviewChecks `json:"checks"`
	Notes    string       `json:"notes"`
}

ReviewOutput is the typed contract for the `## Review` JSON section the ai_review step writes. Round-trips with agentlib.MarshalSectionTyped + agentlib.ExtractSection[ReviewOutput].

type ScannerFinding added in v0.6.0

type ScannerFinding struct {
	ID           string
	Package      string
	FixedVersion string // empty when the scanner reports no fix
	Scanner      string // govulncheck | osv-scanner | trivy | <target> fallback
}

ScannerFinding is one parsed finding row from a gate target's raw output.

type ScannerTable added in v0.6.0

type ScannerTable []ScannerFinding

ScannerTable is the parsed ground-truth findings table for a planning run, accumulated across every detected gate target.

func (ScannerTable) Contains added in v0.6.0

func (t ScannerTable) Contains(id string) bool

Contains reports whether id appears verbatim in the table (exact match).

func (ScannerTable) Row added in v0.6.0

func (t ScannerTable) Row(id string) (ScannerFinding, bool)

Row returns the first row whose ID equals id exactly.

type ScopeVerdict added in v0.3.0

type ScopeVerdict string

ScopeVerdict is the outcome of an installation-scope check.

const (
	// ScopeAllowed — the repo is in the App installation's repository list.
	ScopeAllowed ScopeVerdict = "allowed"
	// ScopeDenied — the installation token definitively cannot access the repo.
	ScopeDenied ScopeVerdict = "denied"
	// ScopeUnknown — the check could not be performed (PAT fallback, API
	// error). Callers proceed; a real permission gap still fails at push.
	ScopeUnknown ScopeVerdict = "unknown"
)

type UpdateScope added in v0.7.0

type UpdateScope string

UpdateScope is the closed set of update-work scopes the agent may apply to a repo. It selects what the update sequence touches: the Go toolchain directive, the module dependency graph, or both. Chosen per task from frontmatter `update_scope` (falling back to the UPDATE_SCOPE env default).

const (
	// UpdateScopeBoth updates the Go toolchain directive AND the dependency
	// graph (the default — matches the pre-knob behaviour byte-for-byte).
	UpdateScopeBoth UpdateScope = "both"
	// UpdateScopeGolang updates only the Go toolchain directive (go.mod
	// `go`/`toolchain` + Dockerfile golang pins); deps are left untouched.
	UpdateScopeGolang UpdateScope = "golang"
	// UpdateScopeDeps updates only the module dependency graph; the Go
	// toolchain directive is left untouched.
	UpdateScopeDeps UpdateScope = "deps"
)

func ParseUpdateScope added in v0.7.0

func ParseUpdateScope(ctx context.Context, value string) (UpdateScope, error)

ParseUpdateScope resolves the configured update scope. An empty value means "unset" and resolves to UpdateScopeBoth, preserving the pre-knob behaviour byte-for-byte. Any other unrecognised value is rejected.

func (UpdateScope) IsBoth added in v0.7.0

func (u UpdateScope) IsBoth() bool

IsBoth returns true if the UpdateScope is UpdateScopeBoth.

func (UpdateScope) IsDepsOnly added in v0.7.0

func (u UpdateScope) IsDepsOnly() bool

IsDepsOnly returns true if the UpdateScope is UpdateScopeDeps.

func (UpdateScope) IsGolangOnly added in v0.7.0

func (u UpdateScope) IsGolangOnly() bool

IsGolangOnly returns true if the UpdateScope is UpdateScopeGolang.

func (UpdateScope) String added in v0.7.0

func (u UpdateScope) String() string

String returns the string representation of the UpdateScope.

func (UpdateScope) Validate added in v0.7.0

func (u UpdateScope) Validate(ctx context.Context) error

Validate returns nil when the UpdateScope is a member of AvailableUpdateScopes, otherwise returns an error naming the rejected value and the accepted set.

type UpdateScopes added in v0.7.0

type UpdateScopes []UpdateScope

UpdateScopes is a collection of UpdateScope values.

func (UpdateScopes) Contains added in v0.7.0

func (u UpdateScopes) Contains(scope UpdateScope) bool

Contains returns true if the collection contains scope.

func (UpdateScopes) Strings added in v0.7.0

func (u UpdateScopes) Strings() []string

Strings returns a slice of string representations of each UpdateScope.

Directories

Path Synopsis
Package factory wires concrete dependencies for the github-update-go-agent binary.
Package factory wires concrete dependencies for the github-update-go-agent binary.
Package git wraps git shell-outs behind the GitOps interface so the execution and ai_review steps can be tested with a counterfeiter mock.
Package git wraps git shell-outs behind the GitOps interface so the execution and ai_review steps can be tested with a counterfeiter mock.
Package githubauth resolves the github-update-go agent's effective GitHub credential at startup.
Package githubauth resolves the github-update-go agent's effective GitHub credential at startup.
Package prompts provides embedded prompt modules for the Claude-backed phases: planning (inspect + classify) and execution (update + repair).
Package prompts provides embedded prompt modules for the Claude-backed phases: planning (inspect + classify) and execution (update + repair).

Jump to

Keyboard shortcuts

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