fix

package
v0.2.0-rc.2 Latest Latest
Warning

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

Go to latest
Published: Nov 12, 2026 License: MIT Imports: 27 Imported by: 0

Documentation

Overview

Package fix runs a fix a person asked for on one of their pull requests: an agent resolves its merge conflicts, fixes its failing checks or addresses its unresolved review comments in a temporary checkout of its branch, and baum commits the result and pushes it. After the push, baum replies on each review thread the agent addressed, with text it composes itself, and resolves the thread (see threads.go).

The agent gets write access to that checkout only, never git credentials or the network (see agent.Fixer). baum itself fetches the branch, starts the merge of the base branch for a conflict, commits what the agent left (with no hook running), refuses a result that does not descend from the head the fix was confirmed for, and pushes with a plain, never forced, git push using the user's own git credentials for the clone's remote. The checkout is a linked worktree of the registered clone under baum's cache directory, removed when the fix ends however it ends.

Index

Constants

View Source
const (
	MaxLogChecks = 4
	MaxLogLines  = 200
)

Bounds on what a check contributes to a prompt.

View Source
const (
	ReasonFork               = "fork"
	ReasonNoPushAccess       = "no_push_access"
	ReasonNothingToFix       = "nothing_to_fix"
	ReasonNoChat             = "chat_unavailable"
	ReasonChatOutdated       = "chat_outdated"
	ReasonUnsupportedBackend = "unsupported_backend"
)

Why a fix cannot run, found before it is queued.

View Source
const (
	ReasonHeadChanged      = "head_changed"
	ReasonThreadsChanged   = "threads_changed"
	ReasonChatChanged      = "chat_changed"
	ReasonNoRemote         = "no_remote"
	ReasonGit              = "git"
	ReasonSigning          = "signing"
	ReasonBackend          = "backend"
	ReasonSignedOut        = "signed_out"
	ReasonBudget           = "budget"
	ReasonNoChanges        = "no_changes"
	ReasonConflictsRemain  = "conflicts_remain"
	ReasonHistoryRewritten = "history_rewritten"
	ReasonWorkflowChanged  = "workflow_changed"
	ReasonPushRejected     = "push_rejected"
	ReasonPushFailed       = "push_failed"
)

Why a fix that ran failed.

View Source
const (
	MaxThreads = 20

	// MaxReplySummary bounds, in characters, the agent's summary in a reply.
	MaxReplySummary = 200
)

Bounds on what review threads contribute to a prompt.

View Source
const (
	// ThreadResolved: baum replied and resolved the thread.
	ThreadResolved = "resolved"
	// ThreadReplied: baum replied, but could not resolve the thread.
	ThreadReplied = "replied"
	// ThreadSkipped: the agent did not address the thread, or said nothing
	// of it; baum left it untouched.
	ThreadSkipped = "skipped"
	// ThreadAlreadyResolved: someone resolved the thread during the fix.
	ThreadAlreadyResolved = "already_resolved"
	// ThreadFailed: the reply could not be posted.
	ThreadFailed = "failed"
)

What became of a thread after a fix.

View Source
const MaxAllChecks = 100

MaxAllChecks bounds how many checks the app lists; AllChecks reads more, up to maxCheckPages and maxStatusPages, so that sorting can choose which stay.

View Source
const MaxChatBytes = 32 << 10

MaxChatBytes caps the conversation a fix is given: the turns' questions, answers, citations and suggestions together.

Variables

This section is empty.

Functions

func CanPush

func CanPush(ctx context.Context, r Reader, repository github.Repository) (bool, error)

CanPush reports whether the signed-in account may push to the repository: GitHub's permissions.push, which write, maintain and admin access give. Who wrote the pull request does not matter; whoever confirms the fix pushes.

func ChatDigest

func ChatDigest(c ChatContext) string

ChatDigest identifies the conversation a fix was confirmed for, with its turns: a different conversation, turn or text changes it. It is empty without turns.

func DefaultDirectory

func DefaultDirectory() (string, error)

DefaultDirectory is where fixes check out: fixes under the user's cache directory, outside every clone and outside the system temporary directory, which a Codex fix may not write to.

func Digest

func Digest(threads []Thread) string

Digest identifies the threads a fix was started for, with their comments: a reply or an edit after the start changes it, and the fix must be started again.

func Excerpt

func Excerpt(t Thread) string

Excerpt is the headline of a thread's first comment, at most 120 characters, for the job to show.

func Headline

func Headline(body string) string

Headline is a review comment's point in one line of plain text: the first line written wholly in bold among its first few, where review bots put their title, or else its first line of text. HTML comments, tags, images (such as priority badges), code blocks and Markdown markup are left out, and a link keeps its text.

func OpenForFetch

func OpenForFetch(ctx context.Context, clone string, repository github.Repository) (*gitlocal.Repo, error)

OpenForFetch opens the clone with the remote whose fetch URL, as configured or as git rewrites it, names repository, origin first, as the registry files a clone, for a read that only fetches: where it pushes does not matter. Its error is an *Error; the reason is ReasonNoRemote when no remote names the repository.

func PlainText

func PlainText(body string) string

PlainText is a review comment's body as plain text to show whole: HTML comments, tags and images are left out, emphasis and code marks dropped, a link keeps its text, a list item keeps a bullet or its number, and paragraphs stay apart by one blank line. A code block keeps its lines as written, without its fences.

func Supported

func Supported(backend string) bool

Supported reports whether a backend can fix: only claude and codex confine an agent with write access (see agent.Fixer).

Types

type Answer

type Answer struct {
	Addressed bool
	// Summary is one plain line: see cleanSummary.
	Summary string
}

Answer is what the agent reported for one thread.

type ChatContext

type ChatContext struct {
	Conversation string     `json:"conversation"`
	Omitted      int        `json:"omitted,omitempty"`
	Turns        []ChatTurn `json:"turns"`
}

ChatContext is the conversation a fix is made from: the turns up to the one the fix was confirmed for, oldest first, within MaxChatBytes. Omitted counts the earlier turns CapChat left out.

func CapChat

func CapChat(conversation string, turns []ChatTurn) ChatContext

CapChat keeps the newest turns of turns (oldest first) that fit in MaxChatBytes, dropping the oldest first and counting them in Omitted. A turn is never cut, except the newest when it alone exceeds the cap: it is then truncated, with a marker, to what fits.

type ChatTurn

type ChatTurn struct {
	Seq        int      `json:"seq"`
	Question   string   `json:"question"`
	Answer     string   `json:"answer"`
	Citations  []string `json:"citations,omitempty"`
	Suggestion string   `json:"suggestion,omitempty"`
}

ChatTurn is one answered turn of a reviewer's conversation with the agent that wrote the review. Question is stored as the reviewer typed it; Answer is the stored, already masked answer markdown. Both are masked again when the prompt is built.

type Check

type Check struct {
	Name       string `json:"name"`
	Conclusion string `json:"conclusion"`
	// Status is queued, in_progress or completed; AllChecks sets it, and
	// Conclusion is then empty until the check completes.
	Status string `json:"status,omitempty"`
	// Title, Summary and Text are a check run's output; a commit status has
	// its description in Summary.
	Title   string `json:"title,omitempty"`
	Summary string `json:"summary,omitempty"`
	Text    string `json:"text,omitempty"`
	// Log is the part of a GitHub Actions job's log that shows the failure,
	// read by Logs.
	Log string `json:"log,omitempty"`
	// JobID is the GitHub Actions job behind a check run, 0 for another app.
	JobID int64 `json:"-"`
	// URL is where the check's details are: the app's details page, else
	// the check run on GitHub, or a commit status's target. The app links it;
	// the prompt leaves it out.
	URL string `json:"-"`
}

Check is a failing check of a pull request's head commit. Everything but Name and Conclusion comes from the check's own output and is untrusted.

func AllChecks

func AllChecks(ctx context.Context, r Reader, repository github.Repository, sha string) (checks []Check, total int, err error)

AllChecks lists every check run (their latest attempt) and commit status of the head commit, passing or not, with their output bounded, in no particular order. It reads at most maxCheckPages pages of check runs and maxStatusPages of statuses, so a head with more has more checks than are returned; total is how many GitHub reports (never fewer than returned). The caller sorts and caps the list. A commit status that is pending is in_progress, and any other is completed; JobID is set only for a failing GitHub Actions run, as in FailingChecks.

func FailingChecks

func FailingChecks(ctx context.Context, r Reader, repository github.Repository, sha string) ([]Check, error)

FailingChecks lists the head commit's failing check runs (their latest attempt) and commit statuses, at most 20, with their output bounded.

func Logs

func Logs(ctx context.Context, r Reader, repository github.Repository, checks []Check) []Check

Logs reads the job log of the first four failing GitHub Actions checks and keeps, of each, the last 200 lines up to the end of the step that failed (jobLog), at most 32 KiB. A log that cannot be read is left out.

type Error

type Error struct {
	Reason string
	Err    error
}

Error is a fix that failed, with the machine-readable Reason. Its text is baum's own or git's sanitised stderr, never a provider's error, so a job may keep it.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Unwrap

func (e *Error) Unwrap() error

type Options

type Options struct {
	// ID names the fix's directory and temporary refs: the job's ID.
	ID string
	// Clone is the registered clone's directory.
	Clone      string
	Repository github.Repository
	Number     int
	Title      string
	// HeadRef and HeadSHA are the branch to push to and the commit the fix
	// was confirmed for; BaseRef the base branch.
	HeadRef, HeadSHA, BaseRef string
	// Conflicts merges the base branch first; Checks fixes the failing
	// Checks.
	Conflicts, Checks bool
	Evidence          []Check
	// Threads are the review threads to address; Replies posts baum's
	// replies on those the agent addressed, after the push.
	Threads []Thread
	// Chat, when set, is a reviewer's conversation with the agent that wrote
	// the review, describing a change to make: untrusted data in the prompt.
	Chat    *ChatContext
	Replies ThreadWriter
	Agent   agent.Fixer
	// Backend and Model name the agent in the commit message.
	Backend, Model string
	// MaxCalls and MaxTokens bound the agent when set; zero is no limit.
	// WallClock is its time limit, defaultWallClock when unset.
	MaxCalls, MaxTokens int
	WallClock           time.Duration
	// Logger receives how the agent ended, its error included, which the
	// job keeps no text of; nil logs nothing.
	Logger *slog.Logger
	// Directory holds the fixes' temporary directories; empty is
	// DefaultDirectory.
	Directory string
	// Progress receives the phase the fix is in.
	Progress func(phase string)
	// Env are the user's agent variables for the git commands of the fix
	// (gitlocal.FixOptions.Env).
	Env []string
	// Prompts replace the built-in fix prompts (prompt.FixWith); nil keeps
	// them. The texts must already be validated.
	Prompts prompt.Overrides
	// Transcript, when set, receives the agent's steps (agent.Request.Transcript).
	Transcript func(agent.TranscriptEntry)
}

Options are one fix.

type Outcome

type Outcome struct {
	// Commit is the commit pushed to the pull request's branch.
	Commit string
	// Summary is the agent's final message, without its answer block about
	// review threads.
	Summary string
	// Agent is the agent's run, nil when none was needed (a base branch
	// that merged cleanly).
	Agent *agent.Result
	// Threads is what became of each review thread, in order, once the
	// push succeeded.
	Threads []ThreadOutcome
}

Outcome is what a fix did.

func Run

func Run(ctx context.Context, o Options) (out Outcome, err error)

Run makes the fix: see the package documentation. A failure is an *Error, except a cancellation, which is the context's error. The checkout is removed before Run returns.

type PullRequest

type PullRequest struct {
	// Title is the pull request's, untrusted.
	Title   string
	State   string
	Draft   bool
	HeadSHA string
	// HeadRef is the head branch; HeadRepository and BaseRepository are
	// "owner/name", and differ for a fork.
	HeadRef, HeadRepository string
	BaseRef, BaseRepository string
	// Mergeable is nil while GitHub has not computed it.
	Mergeable      *bool
	MergeableState string
	URL            string
}

PullRequest is what GitHub reports of a pull request a fix is for.

func ReadPullRequest

func ReadPullRequest(ctx context.Context, r Reader, repository github.Repository, number int) (PullRequest, error)

ReadPullRequest reads an open pull request.

func (PullRequest) Conflicts

func (p PullRequest) Conflicts() bool

Conflicts reports whether GitHub says the pull request cannot be merged cleanly into its base.

func (PullRequest) Fork

func (p PullRequest) Fork() bool

Fork reports a head branch in another repository than the base, which a fix never pushes to.

type Reader

type Reader interface {
	Read(ctx context.Context, path, etag string) (int, http.Header, []byte, error)
	Download(ctx context.Context, path string, w io.Writer) (int, error)
}

Reader is the GitHub REST access a fix needs; *github.RESTReader is one.

type Selection

type Selection struct {
	Threads []Thread
	// Capped: more unresolved threads than MaxThreads, or than baum read.
	Capped bool
	// Untrusted counts the unresolved threads left out because an account
	// without access to the repository started them.
	Untrusted int
}

Selection is the threads a fix addresses, and what it left out.

func SelectThreads

func SelectThreads(list github.ReviewThreads) Selection

SelectThreads picks the threads a fix addresses from what GitHub returned: those the signed-in account can reply to and resolve, whose first comment comes from a trusted author, the first MaxThreads in GitHub's order. Within a thread, comments of untrusted authors are left out.

type Thread

type Thread struct {
	Ref, ID, Path string
	// Line is 0 for a comment on the whole file.
	Line     int
	Outdated bool
	// Comments are the comments of trusted authors, their bodies bounded.
	Comments []github.ThreadComment
	// LeftOut counts the thread's comments not in Comments: beyond the
	// first ones baum read, or from accounts it does not trust.
	LeftOut int
}

Thread is an unresolved review thread a fix addresses. Ref is its id in the prompt and in the agent's answer; ID, its GitHub node id, never reaches the agent.

type ThreadOutcome

type ThreadOutcome struct {
	Ref     string `json:"ref"`
	Outcome string `json:"outcome"`
	Summary string `json:"summary,omitempty"`
}

ThreadOutcome is what became of one thread, with the agent's cleaned summary of it.

type ThreadWriter

type ThreadWriter interface {
	ThreadState(ctx context.Context, repository github.Repository, id string) (github.ThreadState, error)
	ReplyToThread(ctx context.Context, repository github.Repository, id, body string) error
	ResolveThread(ctx context.Context, repository github.Repository, id string) error
}

ThreadWriter is the GitHub access the replies need; *github.Client is one.

Jump to

Keyboard shortcuts

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