orchestrate

package
v0.157.2 Latest Latest
Warning

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

Go to latest
Published: Sep 23, 2026 License: Apache-2.0 Imports: 44 Imported by: 0

Documentation

Overview

Package orchestrate runs typed repository mutations through isolated worktrees, local verification, and optional GitHub publication stages.

Index

Constants

View Source
const (
	CreateRefusalCanonicalClone        = "canonical-clone"
	CreateRefusalDirtyWorktree         = "dirty-worktree"
	CreateRefusalNothingToPush         = "nothing-to-push"
	CreateRefusalUnapprovedPatch       = "unapproved-patch-set"
	CreateRefusalNothingStaged         = "nothing-staged"
	CreateRefusalSecretPath            = "secret-like-path"
	CreateRefusalLeftoverBeforeLanding = "leftover-before-landing"
	CreateRefusalInvalidPath           = "invalid-add-path"
	CreateRefusalBaseMismatch          = "base-mismatch"
	// CreateRefusalIdentityNeedsLand reports the reviewer-identity form of
	// --approved-by given to --auto-merge without --land (round 3, MAJOR
	// fix for #604's `pr create` gap): --auto-merge alone never posts the
	// review comment the identity form requires, so accepting it silently
	// would arm auto-merge on an unrecorded "review".
	CreateRefusalIdentityNeedsLand = "identity-review-needs-land"
)

CreateRefusal codes are the machine-readable half of a refusal.

View Source
const (
	LandRefusalDraft             = "draft-pull-request"
	LandRefusalNotOpen           = "pull-request-not-open"
	LandRefusalLocked            = "pull-request-locked"
	LandRefusalHeadMoved         = "head-moved"
	LandRefusalNotMergeable      = "not-mergeable"
	LandRefusalUnapprovedPatch   = "unapproved-patch-set"
	LandRefusalChecksPending     = "checks-pending"
	LandRefusalChecksFailed      = "checks-failed"
	LandRefusalMergeRejected     = "merge-rejected"
	LandRefusalLandingUnverified = "landing-unverified"
	LandRefusalUnfencedTarget    = "target-has-no-strict-fence"
	LandRefusalCanonicalSync     = "canonical-sync-blocked"
	// LandRefusalLandingLaneHeld reports that a different live WB session
	// already owns the (repository, target) landing lane. See
	// internal/landinglane and LaneGuardRequest.
	LandRefusalLandingLaneHeld = "landing-lane-held"
	// LandRefusalReviewCommentEmpty reports that the identity form of
	// --approved-by was given with no --review-comment/--review-comment-file
	// text, or an empty one (#604).
	LandRefusalReviewCommentEmpty = "review-comment-empty"
)

LandRefusal codes are the machine-readable half of a refusal. A caller branches on these rather than on prose.

View Source
const (
	LandRefusalKeepReasonMissing = "keep-commits-without-reason"
	LandRefusalKeepUnknownCommit = "keep-commit-not-on-branch"
	LandRefusalKeepDoesNotBuild  = "kept-commit-does-not-build"
	LandRefusalKeepNeedsCheckout = "keep-commits-needs-local-checkout"
)

LandRefusalKeepReasonMissing and friends are the refusals specific to a partially kept landing.

View Source
const DefaultCheckPollInterval = 30 * time.Second

DefaultCheckPollInterval deliberately leaves room for other WB operations sharing the authenticated GitHub user budget. A PR receipt still reads every dynamic fact on each observation; only static branch policy is cached within the bounded slice and is fetched again before a pass is returned.

View Source
const FetchMemoMaxAge = 15 * time.Minute

FetchMemoMaxAge bounds how long one memoized `git fetch origin` may stand in for a new one within a run. Discovery over a large fleet plus a full wave of CI waits can hold a campaign open for hours; without a bound, one early fetch of an untouched repository would be trusted for that whole span. With the bound, an external writer's mid-campaign push to an untouched repository is observed within this window at the latest, while nearly all of the per-wave discovery saving is kept (waves separated by less than the bound still skip).

View Source
const LandRefusalReviewCommentCrossRepo = "review-comment-cross-repo"

LandRefusalReviewCommentCrossRepo reports a review comment URL that names a repository or pull/issue number other than the one being landed (round 3, minor 2). Fetching and trusting the wrong PR's comment would bind this landing to a head an entirely different review discusses.

View Source
const LandRefusalReviewHeadMalformed = "review-head-malformed"

LandRefusalReviewHeadMalformed reports a "Reviewed-Head:" line whose value is not a full 40-hex SHA (round 3, minor 1). A short SHA is never treated as a legitimate reviewed head to compare against: it can never again equal a real 40-hex current head, so silently accepting it would produce a permanent, unresolvable "review-stale" rather than a clear parse error the caller can fix by writing the SHA in full.

View Source
const LandRefusalReviewStale = "review-stale"

LandRefusalReviewStale is distinct from LandRefusalHeadMoved (#586): head-moved protects the gap between one invocation's own observation and its own merge write; review-stale protects the much longer gap between when a review was recorded and whenever a landing later executes against it. A rebase, a "fix lint" commit from another agent, or a force-push between review and land produces exactly this: a merge of an unreviewed head carrying a stale "Review:" line.

View Source
const (
	// LandRefusalUpdateConflict is a candidate whose update against the target
	// conflicts. Resolving someone else's conflict is a judgement WB does not
	// make on their behalf.
	LandRefusalUpdateConflict = "update-branch-conflict"
)

Landing refusals specific to bringing a candidate up to date.

View Source
const MaxForegroundCheckWaitSlice = 9 * time.Minute

MaxForegroundCheckWaitSlice keeps a single agent-tool call under the common ten-minute harness ceiling. Longer CI is observed by explicit re-invocation, never a detached worker or a hidden thirty-minute loop.

View Source
const WorktreeMergeFindingDeferredValidationCheckSkipped = "deferred-validation-check-skipped"

WorktreeMergeFindingDeferredValidationCheckSkipped is the finding code recordDeferredValidationCheckSkippedFinding records: a required check on the landed PR-route head concluded "skipped" or "neutral" instead of actually running, on a candidate whose own local validation was deferred to CI (sneat-dev/wb#591 round 3 red-team follow-up). It is informational only — GitHub branch protection's own evaluation is what decided the candidate was landable, and this finding never refuses a landing or lengthens a wait.

View Source
const WorktreeMergeSchemaVersion = 1

Variables

View Source
var DefaultStableRereadDelay = 15 * time.Second

DefaultStableRereadDelay bounds the wait before the confirming reread of a checks-bearing terminal observation. The stability fingerprint exists to catch a check set that is still registering, not to space out load: once every observed and required check is terminal, only this one confirming observation (plus the fresh authority and identity receipts) stands between the campaign and its receipt, so waiting a full quota-aware poll interval there adds DefaultCheckPollInterval of pure latency to every passing PR merge, PR validation, and direct-target receipt. Fifteen seconds sits outside GitHub's usual push-to-registration envelope for lazily created non-required checks (matrix expansion, workflow_run chains) while still cutting half the default cadence. It is a variable, and PullRequestWaitOptions.StableRereadDelay overrides it per wait, so tests and unusual deployments can tune it without recompiling callers.

Two terminal receipts never shorten this wait: the no-applicable-checks receipt (an empty observed set with an enumerated empty policy), whose only time-based guard against a repository whose CI simply has not registered yet IS this gap, and any reread after the previous observation was already terminal — a churning terminal fingerprint (for example a moving target head) falls back to the full poll cadence instead of re-observing on the short delay without bound.

Functions

func AcknowledgeRetiredPrepareCandidate added in v0.154.0

AcknowledgeRetiredPrepareCandidate authorizes only retirement of the receipted candidate. In particular it neither proves nor records that a source landed, and it deliberately leaves the old merge lane untouched.

func CanonicalClonePath added in v0.138.0

func CanonicalClonePath(githubDir string, repository Repository) (string, error)

CanonicalClonePath resolves one repository's canonical clone below githubDir.

An existing clone is used where it is — the literal host level first, then the legacy two-level placement — so a caller never creates a second copy beside a clone the machine already has. When no clone exists the destination is the literal host level the clone URL names, which is where `wb sync` and orchestrate place a new one. It is a thin typed wrapper over the one resolution every package shares.

func IsTransientGitHubFailure added in v0.150.2

func IsTransientGitHubFailure(err error) bool

IsTransientGitHubFailure includes both exhausted read retries and a write whose provider/transport response was lost. Both are resumable; the latter is deliberately not retried until authoritative state has been re-read.

func IsTransientReadFailure added in v0.140.0

func IsTransientReadFailure(err error) bool

IsTransientReadFailure is the exported form of isTransientReadReason for callers outside this package that hold an error rather than a flattened reason string. A verb that reports rather than merges uses it to keep a target pending across a provider blip instead of ending a wait with a verdict WB never observed.

func MatchesHold added in v0.82.0

func MatchesHold(slug string, patterns []string) bool

MatchesHold reports whether an "owner/name" repository slug matches any hold pattern. Patterns use path.Match semantics, where "*" never crosses a "/", so "sneat-co/*" holds every repository in one owner and "sneat-co/sneat-go" holds exactly one. An exact string equal to the slug always matches, so a caller never has to think about glob metacharacters in a literal repository name.

func PeekWorktreeMergeValidationDeferral added in v0.146.0

func PeekWorktreeMergeValidationDeferral(ctx context.Context, projectsRoot string, sources []string, target string, requestedRoute WorktreeMergeRoute, validateLocally, allowUnfenced bool) (bool, error)

PeekWorktreeMergeValidationDeferral cheaply resolves the merge route for not-yet-prepared source worktrees and reports whether this call's local candidate validation will be deferred to the pull-request route's authoritative CI, reusing the exact same resolveWorktreeMergeValidationPlan logic RunWorktreeMerge itself applies once a receipt exists. It performs only source inspection and remote route/required-check-policy reads -- never a CPU-heavy local validation run -- so a caller deciding whether to gate a not-yet-started merge on host load can learn the answer without paying for the validation it is trying to avoid gating on (Minor 10, sneat-dev/wb#591 round 3 red-team follow-up: the combined `wb worktree land`/`wb land` used to check host load before it could know validation would be deferred).

func PullRequestNumber added in v0.89.0

func PullRequestNumber(selector string) (string, error)

PullRequestNumber accepts every spelling a caller already has in hand — a bare number, "#12", "owner/repo#12", or the pull request's own URL — and returns the number the API is addressed by. Callers hold whichever form their own source gave them, and making each one normalize it separately is how a URL reaches an endpoint path and produces a 404 that reads like a missing pull request.

func RepositoryFromPullRequestURL added in v0.89.0

func RepositoryFromPullRequestURL(pullRequestURL string) (string, error)

RepositoryFromPullRequestURL extracts owner/repository from a pull request URL, so a caller holding only the URL can still address the API.

func ResolvePullRequestCreateWorktree added in v0.146.0

func ResolvePullRequestCreateWorktree(ctx context.Context, projectsRoot, argument string) (string, error)

resolvePullRequestCreateWorktree resolves the CLI's `<worktree|task>` argument. An existing directory is used as-is; anything else is looked up as a task name against the fleet's worktree inventory. Resolution never runs a network fetch: the guard and the base-branch fetch that follow are where a caller pays that cost, once, for the worktree it actually meant. ResolvePullRequestCreateWorktree exports resolvePullRequestCreateWorktree for cmd/wb's best-effort #615 prompt-suggestion lookup, which needs the same worktree-path-or-task-name resolution `wb pr create` itself uses but runs before CreatePullRequest is called.

func ReviewDigest added in v0.146.0

func ReviewDigest(comment string) string

ReviewDigest hashes a review comment's exact text, so the receipt records which review authorized a landing without needing to keep the whole text.

func SuggestClosesFromPrompt added in v0.146.0

func SuggestClosesFromPrompt(prompt string) []int

SuggestClosesFromPrompt finds issue numbers named in a task's original prompt (Work Log), in first-seen order with duplicates removed. It never adds them itself (#615): the caller prints them as a suggestion, and only --closes adds them to the pull request body.

Three shapes that contain "#<digits>" are never this repository's own issue number, and must not be suggested as one:

  • "owner/repo#123" names an issue in a DIFFERENT repository — detected by the immediately-preceding token (back to the previous whitespace) containing a "/", the one character an issue number's own prefix never has;
  • a hex colour literal spelled entirely in decimal digits, such as "#123456" — GitHub, and every other "#NNN" convention, only ever writes a colour as exactly 3, 4, 6, or 8 hex digits, so a decimal run of exactly one of those lengths is treated as a colour, not an issue. (A colour that mixes digits and hex letters, like "#3b82f6" or "#00ff00", never reaches this check at all: issueReferencePattern's own `\b` already excludes it — round 4, minor 3.)
  • "#0" — GitHub issue numbering starts at 1, so 0 is never a real issue number regardless of what wrote it (round 4, minor 3).

func UnsatisfiedRequiredChecks added in v0.140.0

func UnsatisfiedRequiredChecks(ctx context.Context, repository, selector string) ([]string, error)

UnsatisfiedRequiredChecks names the checks a pull request's target branch requires that have no passing observation on its exact head.

It exists for the renamed-workflow trap. When branch protection requires "build" and the workflow that produced it is renamed to "build-and-test", nothing ever reports "build": the required check is absent from the observed set rather than pending in it. A caller that only counts pending checks sees none, concludes the head is settled, and reports a green pull request that can never merge. Naming the gap is the difference between "ready" and "permanently blocked, and here is the check nobody is producing".

A name is reported when no observation matches it, and also when a ruleset pins it to one GitHub App and the matching producer did not report it.

func WorktreeMergeLaneReleasable added in v0.120.0

func WorktreeMergeLaneReleasable(status WorktreeMergeStatus) bool

WorktreeMergeLaneReleasable reports whether a worktree-merge receipt in this status should free its landing lane immediately. A resumable, still-live receipt (preparing, prepared, published and awaiting merge, or awaiting checks) keeps the lane so a different session cannot start landing onto the same target underneath a batch this session is still driving; every other status — landed, failed, or complete — releases it.

Types

type AbsorbedConflictAcknowledgementLookup added in v0.120.0

type AbsorbedConflictAcknowledgementLookup struct {
	ReceiptPath     string
	Receipt         WorktreeMergeReceipt
	Acknowledged    bool
	Acknowledgement WorktreeMergeAbsorbedConflictAcknowledgement
}

AbsorbedConflictAcknowledgementLookup names one worktree-merge receipt found under a projects root's reports directory whose candidate matches a task and worktree, together with its absorbed-conflict acknowledgement (see AcknowledgeAbsorbedConflict) when one currently validates against that exact, unchanged receipt.

func FindAbsorbedConflictAcknowledgement added in v0.120.0

func FindAbsorbedConflictAcknowledgement(projectsRoot, candidateTask, candidateWorktree string) (lookup AbsorbedConflictAcknowledgementLookup, ok bool, err error)

FindAbsorbedConflictAcknowledgement is a read-only lookup for other callers -- a status display, a diagnostic, a landing-proof consumer outside this package -- that need to know whether a candidate worktree already has a validated absorbed-conflict acknowledgement, without duplicating the receipt-matching and sidecar-validation logic AcknowledgeAbsorbedConflict and readAbsorbedConflictAcknowledgement already own. It never writes the receipt, the acknowledgement, or any Work Log, and it never deletes anything.

ok is false only when no receipt under projectsRoot's worktree-merge reports names candidateTask/candidateWorktree as its candidate. When a matching receipt is found, lookup.Acknowledged reports whether its absorbed-conflict acknowledgement sidecar currently validates; a missing sidecar is reported as Acknowledged=false with a nil error, while a present-but-invalid (tampered, stale, or identity-mismatched) sidecar is reported as an error, never silently treated as absent -- a caller that wants "no usable acknowledgement" for either case should treat any error here the same way it treats Acknowledged=false.

type AppliedFileReporter added in v0.136.4

type AppliedFileReporter[T any] interface {
	AppliedFiles(T) []string
}

AppliedFileReporter identifies files the handler changed. The engine unions these with the Git-status delta for an in-place request, so pre-existing dirty or untracked implementation files do not become operation report evidence merely because they were already present in the checkout.

type Assessment

type Assessment[T any] struct {
	Metadata    T
	Applicable  bool
	NeedsChange bool
	Reason      string
}

Assessment is adapter-owned planning metadata plus an execution decision.

type CIFailureAnnotation added in v0.108.0

type CIFailureAnnotation struct {
	Path      string `json:"path" yaml:"path"`
	StartLine int    `json:"start_line" yaml:"start_line"`
	EndLine   int    `json:"end_line,omitempty" yaml:"end_line,omitempty"`
	Message   string `json:"message" yaml:"message"`
}

CIFailureAnnotation is a compact, deduplicated GitHub check-run finding. It is deliberately narrower than GitHub's annotation payload so CI receipts remain useful to machines without becoming a copy of the Actions log.

type CIFailureDetail added in v0.98.0

type CIFailureDetail struct {
	Check       string                `json:"check" yaml:"check"`
	RunURL      string                `json:"run_url,omitempty" yaml:"run_url,omitempty"`
	JobURL      string                `json:"job_url,omitempty" yaml:"job_url,omitempty"`
	Annotations []CIFailureAnnotation `json:"annotations,omitempty" yaml:"annotations,omitempty"`
	Excerpt     string                `json:"excerpt,omitempty" yaml:"excerpt,omitempty"`
	Reason      string                `json:"reason,omitempty" yaml:"reason,omitempty"`
}

CIFailureDetail is a bounded diagnostic for one failed GitHub Actions job. It deliberately carries an excerpt rather than the raw job log so a machine receipt remains compact and does not become an accidental log archive.

func PullRequestFailureDetails added in v0.140.0

func PullRequestFailureDetails(ctx context.Context, repository, selector string) ([]CIFailureDetail, error)

PullRequestFailureDetails reports why a pull request's head is red, in the form a machine can act on: the failing check, its job URL, GitHub's own deduplicated annotations (path, line, message), and a bounded log excerpt when no annotation exists.

It exists so a caller that has just observed a red head does not have to download the Actions log and grep it. WB already extracts this for landing receipts; the only thing missing was a way to ask for it without asking to merge. The result is deliberately bounded — it is the failure, not a copy of the run's output.

type ChangedFile added in v0.89.0

type ChangedFile struct {
	Filename string `json:"filename"`
	Status   string `json:"status"`
	Patch    string `json:"patch"`
}

ChangedFile is one file of a pull request's diff, with the patch GitHub returns for it.

type CheckoutUpdate added in v0.127.0

type CheckoutUpdate struct {
	Checkout string
	OldSHA   string
	NewSHA   string
	Cause    string
}

type CreateOutcome added in v0.144.0

type CreateOutcome string

CreateOutcome is the envelope outcome, mapped onto WB's exit-code contract by ExitCode: success is 0, findings is 1, refused is 2.

const (
	CreateSuccess  CreateOutcome = "success"
	CreateFindings CreateOutcome = "findings"
	CreateRefused  CreateOutcome = "refused"
)

type FetchMemo added in v0.74.0

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

FetchMemo memoizes `git fetch origin` per canonical clone within one process-local run, so a campaign loop that alternates fleet-wide discovery and per-wave mutation (wb deps bump) does not pay one fetch per repository per wave for repositories the run itself never wrote to.

Only read-only graph discovery may consume the memo (see Options.FetchMemoDiscovery): the mutation engine always re-fetches before cutting a branch, so a wave's base ref is never a snapshot up to one discovery pass old. The engine still records its fetches here and drives the touch-invalidation below.

The invalidation rule is deliberately "ever touched", not "wrote since the last fetch": once this run has pushed a branch to, opened a pull request for, or merged into a repository, that repository is permanently un-memoizable for the rest of the run. WB merges server-side with `gh pr merge`, so the resulting default-branch commit appears on origin with no local push at all — an own-writes rule keyed on observed local pushes would provably miss it, and a stale origin/<ref> read then either never observes the landed manifest (burning waves until --max-waves fails the campaign) or cuts a duplicate PR from the stale base. Re-fetching a touched repository on every subsequent EnsureCanonical costs one redundant fetch per wave for the handful of repositories a wave actually changed; it can never serve a stale read.

Honest staleness window: this memo widens exposure to EXTERNAL writers. A teammate's merge, a sibling campaign, or a bot landing on an untouched repository's default branch mid-run is observed within one wave WITHOUT the memo, but only within FetchMemoMaxAge WITH it. That is the flag's real trade — which is why it is opt-in, why the age bound exists, and why the skill docs say not to use --fetch-cache when anything other than this run may land on main mid-campaign.

State is process-local only: a memo lives exactly as long as the run that created it, is never persisted, and a fresh invocation always fetches. Keys are canonical clone directories (not owner/repo slugs), so two directories that happen to claim one slug can never satisfy each other's fetches. A nil *FetchMemo is valid and disables memoization entirely (every method is a no-op / zero), which keeps every caller that does not thread a memo — deps set, worktree operations, all non-campaign engines — byte-identical to the pre-memo behavior.

func NewFetchMemo added in v0.74.0

func NewFetchMemo() *FetchMemo

NewFetchMemo returns an empty per-run fetch memo.

func (*FetchMemo) MarkFetched added in v0.74.0

func (memo *FetchMemo) MarkFetched(canonical string)

MarkFetched records that this run completed `git fetch origin` for the canonical clone, refreshing its age. A clone already marked touched is deliberately not re-memoized: touched repositories stay un-memoizable for the rest of the run regardless of how many times they are fetched again.

func (*FetchMemo) MarkTouched added in v0.74.0

func (memo *FetchMemo) MarkTouched(canonical string)

MarkTouched permanently disqualifies the canonical clone from fetch memoization for the rest of this run. It is called before every stage that publishes state for the repository — branch push, pull-request creation, and the server-side merge — so a failed or ambiguous publication also invalidates (the only cost of over-invalidating is one extra fetch).

func (*FetchMemo) SkipFetch added in v0.74.0

func (memo *FetchMemo) SkipFetch(canonical string) bool

SkipFetch reports whether a discovery pass may reuse this run's previous fetch of the canonical clone: it was fetched no longer than FetchMemoMaxAge ago and has never been touched. A nil memo never skips. Each true result is counted for the campaign report (see Skips).

func (*FetchMemo) Skips added in v0.74.1

func (memo *FetchMemo) Skips() int

Skips returns how many fetches this memo has skipped so far. Campaign reports use deltas of this counter to attribute per-wave savings.

type Handler

type Handler[T any] interface {
	Inspect(context.Context, string, string, Repository) (Assessment[T], error)
	Apply(context.Context, string, Repository) (T, error)
	ValidatePublishable(context.Context, string, Repository) error
	CommitMessage(Repository) string
	PullRequest(Repository) (title, body string)
}

Handler supplies mutation policy while Engine owns repository lifecycle.

type HeadCheck added in v0.89.0

type HeadCheck struct {
	Name   string
	Bucket string
}

HeadCheck is one observed check on a commit, in the shape a caller outside this package needs: a name and a normalized bucket.

func PullRequestHeadChecks added in v0.89.0

func PullRequestHeadChecks(ctx context.Context, repository, selector string) ([]HeadCheck, bool, error)

PullRequestHeadChecks reads every check GitHub has for a pull request's current head, and reports whether they have all passed.

It exists so there is exactly one implementation of "are this pull request's checks green?" in WB, reachable from outside this package. The alternative — `gh pr checks --json` — is unavailable on the installed client, and even where it works it is a second dialect for a fact the API already answers.

type InPlaceInspector added in v0.136.4

type InPlaceInspector[T any] interface {
	InspectWorkingTree(context.Context, string, Repository) (Assessment[T], error)
}

InPlaceInspector is implemented by handlers whose plans can read a supplied managed worktree directly. The normal Handler contract intentionally plans from a fetched canonical base; this narrower opt-in keeps that behavior for every other caller while allowing dependency updates to respect staged and unstaged manifest state in an explicit in-place request.

type LandOutcome added in v0.89.0

type LandOutcome string

LandOutcome is the envelope outcome. It maps onto the exit-code contract: success is 0, findings is 1, refused is 2.

const (
	LandSuccess  LandOutcome = "success"
	LandFindings LandOutcome = "findings"
	LandRefused  LandOutcome = "refused"
)

type LandedCommit added in v0.89.0

type LandedCommit struct {
	SourceSHA string `json:"source_sha"`
	LandedSHA string `json:"landed_sha,omitempty"`
	Subject   string `json:"subject"`
	Kept      bool   `json:"kept"`
}

LandedCommit pairs a source commit with the commit that carried it onto the base. GitHub's rebase merge always rewrites the commits, so after landing these pairs are the only way back to the originals.

func MapLandedCommits added in v0.89.0

func MapLandedCommits(ctx context.Context, canonical, base, mergeBase string, landed []LandedCommit) ([]LandedCommit, error)

MapLandedCommits pairs each source commit with the commit that carried it onto the base, by patch identity.

GitHub's rebase merge replays every commit with new committer metadata and new SHAs, so the only durable link back to a source commit is the content it carried. `git patch-id --stable` is that link: it hashes the diff, ignoring whitespace-insensitive noise and every piece of metadata a replay rewrites.

The aggregated sources all map to the one commit that absorbed them, which is found by elimination: it is the landed commit no kept source claims.

type LaneGuardRequest added in v0.120.0

type LaneGuardRequest struct {
	Owner          landinglane.Owner
	TakeOver       bool
	TakeoverReason string
	// StaleAfter overrides how long a prior owner's heartbeat may go
	// unrefreshed before it is taken over automatically. Zero uses
	// landinglane.DefaultStaleAfter.
	StaleAfter time.Duration
}

LaneGuardRequest optionally names the acquiring WB session for the landing-lane ownership guard (see internal/landinglane). A zero value (empty Owner.WBSessionID) skips the guard entirely: every existing direct caller of PrepareWorktreeMerge, LandWorktreeMerge, ResumeWorktreeMerge, or LandPullRequest that never populates this field — including every test in this package — keeps working exactly as before. Only `cmd/wb`, which knows the calling session's identity, populates it.

On 2026-09-07 two live WB sessions landed on sneat-dev/wb main concurrently: main advanced under a published candidate four times in one session, each time costing a re-prepare or stranding a receipt. The working agreement is one landing owner per (repository, target branch); this guard is the mechanical enforcement of it.

type MechanicalVerdict added in v0.89.0

type MechanicalVerdict struct {
	Mechanical bool
	// Reasons name each file that made the change non-mechanical, and why.
	Reasons []string
	// NonManifest is the subset of Reasons that is just "this is code".
	NonManifest []string
}

MechanicalVerdict explains a classification well enough to argue with.

func ClassifyMechanical added in v0.89.0

func ClassifyMechanical(files []ChangedFile) MechanicalVerdict

ClassifyMechanical decides from the diff whether a change is a mechanical dependency bump.

func (MechanicalVerdict) Summary added in v0.89.0

func (verdict MechanicalVerdict) Summary() string

Summary renders the verdict for a refusal.

type MergeLaneClaim added in v0.73.0

type MergeLaneClaim struct {
	// Lane identifies the exclusive (repository, target) merger lane that
	// claimed the branch.
	Lane string `json:"lane"`
	// Target is the branch the lane is draining toward.
	Target string `json:"target"`
	// Status is the receipt's current lifecycle status (see
	// WorktreeMergeStatus), e.g. "prepared" or "checks_pending".
	Status string `json:"status"`
	// ReceiptPath is the exact local receipt backing the claim, so a caller
	// can inspect the full batch (wb worktree merge land <receipt>).
	ReceiptPath string `json:"receipt_path"`
}

MergeLaneClaim reports that a branch is currently a source of a non-terminal merger-lane receipt: some merger lane has already selected it as an input to a batch it may land at any time. See the lesson merger-lane-branch-race (spec/lessons/merger-lane-branch-race in sneat-co/backstage): a branch with an open PR is not a private workspace once a lane owns it, and neither PR state, reviews, nor CI surface that fact to a main agent deciding whether to push.

func ActiveMergeLaneClaim added in v0.73.0

func ActiveMergeLaneClaim(projectsRoot, repository, branch string) (*MergeLaneClaim, error)

ActiveMergeLaneClaim reports whether branch in repository is a source of an active (non-terminal, non-superseded) merger-lane receipt, regardless of which target that lane is draining toward — a main agent about to push does not necessarily know the target a lane already chose. It returns a nil claim, not an error, when no active receipt claims the branch or when no merger has ever run for this WB home (a fresh reports directory).

type OperationLock

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

OperationLock prevents two processes from mutating the same operation worktrees. Higher-level planners may hold a campaign lock while individual lifecycle runs also protect their wave directories.

func AcquireOperationLock

func AcquireOperationLock(githubDir, operation string, resume bool) (OperationLock, error)

AcquireOperationLock creates an exclusive lock below the operation root. An unheld remnant is reclaimable only by an explicit resume and only when its descriptor proves exact ownership of this operation.

func (OperationLock) Release

func (lock OperationLock) Release() error

Release retires the exact held lock inode. It is safe to call from defer and cannot unlink a successor lock installed after this operation acquired one.

type Options

type Options struct {
	GitHubDir string
	Operation string
	Branch    string
	Ref       string
	Parallel  int
	DryRun    bool
	Resume    bool
	Verify    bool
	Checks    []quality.Check
	Timeout   time.Duration
	Retry     int
	// CheckPollInterval overrides the GitHub-check polling delay. A zero value
	// uses the production default. It is primarily useful for deterministic
	// lifecycle tests.
	CheckPollInterval time.Duration
	Commit            bool
	Push              bool
	PR                bool
	Merge             bool
	// WaitForPRChecks observes exact PR-head checks after opening a pull
	// request, but deliberately does not merge it. It is only valid with PR
	// publication and is intended for validation-only campaigns.
	WaitForPRChecks bool
	// Hold is a set of "owner/name" glob patterns naming repositories whose
	// merge is a human decision. A held repository is changed, verified,
	// pushed, has its pull request opened, and has its exact PR-head checks
	// waited on exactly like any other — and is then left OPEN, even under
	// Merge. It is not a way to skip a repository (that is the caller's own
	// exclusion, applied before this engine sees the repository); it is a way
	// to do all the mechanical work and stop at the one step that needs an
	// owner's judgement, such as a deploy repository the founder gates.
	Hold     []string
	Progress progress.Reporter

	// Prompt is recorded as the originating instruction in the WB manifest
	// journal of every worktree this operation creates, satisfying wb's own
	// commit-admission hook (internal/worktrees.CheckAdmission) — without it,
	// a worktree this engine creates and then commits into is rejected by
	// wb's own pre-commit hook as carrying no record of what it is or who
	// asked for it. Normalize fills in an operation-derived default when
	// empty, so every caller gets a truthful record even if it has nothing
	// more specific to say.
	Prompt string
	// Model, AgentRuntime, Initiator, CLI, and Provider identify who or what
	// asked for this operation, recorded in the same manifest for
	// provenance. Normalize defaults Model to "unknown" when empty, matching
	// the same explicit-over-guessed convention used everywhere else a
	// child model identity is recorded (see internal/worktrees.WorkLogOptions).
	Model        string
	AgentRuntime string
	Initiator    string
	CLI          string
	Provider     string
	// DependencyCampaign marks worktrees created by dependency set/bump
	// campaigns. Their supersession receipts require exact dependency proof.
	DependencyCampaign bool
	// FetchMemo, when non-nil, memoizes this run's completed origin fetches
	// and receives touch-invalidation from the push, PR-open, and merge
	// stages (see FetchMemo). Only a campaign loop that alternates fleet-wide
	// discovery and mutation over the same repositories within one process —
	// wb deps bump with --fetch-cache — threads one memo through every
	// discovery and wave lifecycle it runs. Every other caller leaves it nil
	// and keeps the unconditional engine fetch: for deps set --fleet there is
	// no prior discovery, so that fetch is the operation's only origin read
	// and must never be skipped.
	FetchMemo *FetchMemo
	// FetchMemoDiscovery marks this lifecycle as read-only graph discovery,
	// which is the ONLY context allowed to consume the memo: EnsureCanonical
	// may then skip a fresh, untouched memoized fetch. The mutation engine
	// leaves it false even when FetchMemo is threaded, so a wave's branch
	// base is always cut from a fetch completed moments before — never from a
	// snapshot up to one full discovery pass old, which would interact badly
	// with strict up-to-date branch protection and exact-head merges. The
	// engine's own fetches still refresh the memo, and its publication
	// stages still invalidate through it.
	FetchMemoDiscovery bool
}

Options controls a repository operation independently of mutation policy.

func Normalize

func Normalize(options Options) (Options, error)

Normalize validates lifecycle settings and applies cumulative publication implications shared by every orchestrated command.

type PullRequestCreateOptions added in v0.144.0

type PullRequestCreateOptions struct {
	// Worktree is a linked worktree path or a task name. Empty resolves to
	// the current directory.
	Worktree     string
	ProjectsRoot string
	Title        string
	// Body is literal pull-request body text, as `gh pr create --body` takes
	// it. Mutually exclusive with BodyFile; the caller validates that.
	Body     string
	BodyFile string
	Draft    bool
	// Base overrides the pull request's target branch. Empty uses the
	// worktree's own recorded base.
	Base string
	// Closes lists issue numbers this pull request closes; one "Closes #N"
	// line per issue is written at the top of the body (#615). Never
	// populated from the task's prompt automatically — see
	// SuggestClosesFromPrompt, which the caller decides whether to act on.
	Closes []int

	// AutoMerge arms GitHub auto-merge immediately after the pull request is
	// created or adopted, under the same authority `wb pr land` requires to
	// arm it: a mechanical diff, or a non-mechanical one with ApprovedBy, and
	// a target whose policy AllowUnfenced does not have to widen.
	AutoMerge     bool
	ApprovedBy    string
	AllowUnfenced bool
	MergeMethod   string
	// Lane optionally names the acquiring WB session for the same
	// landing-lane guard `wb pr land` acquires around arming; see
	// LaneGuardRequest. A zero value skips the guard, exactly as it does for
	// LandPullRequest. Only meaningful with AutoMerge; --land threads its own
	// Lane through LandOptions instead.
	Lane LaneGuardRequest

	// CommitStaged commits exactly the worktree's index. It refuses when
	// nothing is staged. CommitAll runs `git add -A` first (respecting
	// .gitignore, and refusing any path that looks like a secret), then
	// commits everything. They are mutually exclusive; the caller validates
	// that. Message is required with either one and is used verbatim.
	// Add commits exactly the named paths: `git add -- <paths>`, then
	// `git commit -m Message -- <paths>`, so anything else already staged is
	// not included. It is a third commit mode, mutually exclusive with
	// CommitStaged and CommitAll; the caller validates that.
	Add          []string
	CommitStaged bool
	CommitAll    bool
	Message      string

	// Land lands the pull request in-process through the existing
	// `LandPullRequest`, using LandOptions as a template: Repository and
	// PullRequest are filled in here once both are known. `--land` implies
	// arming, the way `wb pr land` itself does, so AutoMerge is not armed
	// separately when Land is set.
	Land        bool
	LandOptions *PullRequestLandOptions

	// LinkPreflight runs immediately before arming auto-merge or landing,
	// exactly as `wb pr land` runs `refuseLinkedRepositoryWorktrees` before
	// its own GitHub calls. It lives in cmd/wb, not here, so it is injected
	// rather than imported: internal/orchestrate must not depend on cmd/wb.
	LinkPreflight func(repository string) error

	Timeout time.Duration
	Retry   int

	// Events receives one structured record per invocation, whatever the
	// outcome. A nil appender discards. EventsForRepository, when set,
	// overrides it once the repository is resolved: this verb's own
	// repository is not known until the worktree's manifest is read, unlike
	// `wb pr land`, which receives it as its own CLI argument up front, so
	// cmd/wb cannot resolve the repository-scoped stream ahead of the call
	// the way it does for `wb pr land`. --land also uses it to give the
	// nested `LandPullRequest` call the same repository-scoped stream
	// `wb pr land` itself would have used.
	Events              streams.EventAppender
	Stream              string
	EventsForRepository func(repository string) (streams.EventAppender, string)
}

PullRequestCreateOptions identifies the worktree to open a pull request for.

type PullRequestCreateResult added in v0.144.0

type PullRequestCreateResult struct {
	SchemaVersion     int           `json:"v"`
	Verb              string        `json:"verb"`
	Outcome           CreateOutcome `json:"outcome"`
	RefusalCode       string        `json:"refusal_code,omitempty"`
	SanctionedCommand string        `json:"sanctioned_command,omitempty"`
	Reason            string        `json:"reason,omitempty"`

	Repository  string `json:"repository,omitempty"`
	PullRequest int    `json:"pull_request,omitempty"`
	URL         string `json:"url,omitempty"`
	Title       string `json:"title,omitempty"`
	HeadRef     string `json:"head_ref,omitempty"`
	HeadSHA     string `json:"head_sha,omitempty"`
	BaseRef     string `json:"base_ref,omitempty"`
	// Adopted records that an already-open pull request for this branch was
	// reused rather than a new one created.
	Adopted bool `json:"adopted,omitempty"`

	Mechanical bool   `json:"mechanical"`
	ApprovedBy string `json:"approved_by,omitempty"`

	AutoMergeArmed  bool   `json:"auto_merge_armed,omitempty"`
	AutoMergeReason string `json:"auto_merge_not_armed_reason,omitempty"`

	// NextCommand is the exact follow-up invocation: `wb pr land` unless
	// AutoMerge was requested, in which case it is `wb wait pr`.
	NextCommand string `json:"next_command,omitempty"`

	// Task and ClaimID identify the durable Work Log claim the pull request
	// was bound to, when binding succeeded.
	Task    string `json:"task,omitempty"`
	ClaimID string `json:"claim_id,omitempty"`

	// CommittedPaths lists every path a requested --commit-staged/--commit-all
	// committed, in the order `git diff-tree` reports them.
	CommittedPaths []string `json:"committed_paths,omitempty"`

	// LandResult is populated when Land was requested: the full receipt of the
	// in-process `LandPullRequest` call this invocation's own Outcome mirrors.
	LandResult *PullRequestLandResult `json:"land_result,omitempty"`

	Evidence map[string]string `json:"evidence,omitempty"`
}

PullRequestCreateResult is the receipt, and the JSON envelope.

func CreatePullRequest added in v0.144.0

func CreatePullRequest(ctx context.Context, options PullRequestCreateOptions) (result PullRequestCreateResult, err error)

CreatePullRequest resolves one worktree, pushes its branch through the normal push path, and opens or adopts its pull request. It performs no local build, test, or lint: CI is the gate, and the caller that wants a verified merge runs `wb pr land` on the number this prints.

func (PullRequestCreateResult) ExitCode added in v0.144.0

func (result PullRequestCreateResult) ExitCode() int

ExitCode maps the outcome onto WB's exit contract.

type PullRequestLandOptions added in v0.89.0

type PullRequestLandOptions struct {
	Repository   string
	PullRequest  string
	ProjectsRoot string
	// Keep retains the task's worktrees and claims. Cleanup is the default
	// precisely because the opt-in form was never passed.
	Keep bool
	// ApprovedBy records the review that authorized a non-mechanical change.
	// It is one of: an existing file path or a pull-request comment URL
	// (unchanged, back-compatible); the literal "ci"; or a reviewer identity
	// `{model}[@{harness}[@{session}]]`, which requires ReviewComment or
	// ReviewCommentFile and causes WB to post the review as a PR comment and
	// bind it to the exact head it reviewed (#604, #586).
	ApprovedBy string
	// ReviewComment is the review text for the identity form of ApprovedBy.
	// Mutually exclusive with ReviewCommentFile; the caller validates that.
	ReviewComment string
	// ReviewCommentFile is a path to the review text for the identity form
	// of ApprovedBy.
	ReviewCommentFile string
	// MergeMethod is merge by default: preserve commits and the reviewed PR boundary.
	MergeMethod string
	// MergeMethodExplicit distinguishes an operator-selected method from the merge
	// default. KeepCommits requires an explicitly selected squash hybrid.
	MergeMethodExplicit bool
	// Subject overrides the explicit squash commit subject. The default is the pull
	// request's own title, which is the thing GitHub will otherwise replace
	// with the branch's first commit subject.
	Subject string
	// KeepCommits names source commits that must land as their own commits
	// instead of being folded into the aggregate. Reason is mandatory with it:
	// the exception has to be justified in the history it creates.
	KeepCommits []string
	Reason      string
	// BuildCommand overrides the per-kept-commit build guard. Empty uses the
	// repository's own target, which is `go build ./...` for a Go module.
	BuildCommand []string
	// AllowUnfenced lands on observed checks alone, where the target branch has
	// no server-enforced strict up-to-date policy. Without such a fence, green
	// checks prove the head was green, not that it is still green against the
	// target the merge will use, so this is an explicit widening rather than a
	// default — and the receipt records that it was used.
	AllowUnfenced bool
	// Slice is the total foreground wait budget retained under its historical
	// name for API compatibility. A landing may outlive the bounded CI waiter:
	// WB divides this budget into exact-identity observation slices instead of
	// rejecting an otherwise valid long-running landing.
	Slice             time.Duration
	CheckPollInterval time.Duration
	Progress          func(PullRequestWaitProgress)
	OperationProgress progress.Reporter
	// Events receives one structured record per invocation, whatever the
	// outcome. A refusal is the most useful event of all — it is the one that
	// says a verb was reached and declined — so `--keep` and every refusal
	// write one too. A nil appender discards.
	Events streams.EventAppender
	// Stream names the stream this landing belongs to, when it belongs to one.
	Stream string
	Now    func() time.Time
	// NoUpdateBranch keeps the historical behaviour of refusing a candidate
	// that is behind the target instead of bringing it up to date. The default
	// updates, because a target with a strict up-to-date policy puts every
	// candidate behind whenever anything else lands, and refusing then costs a
	// manual merge plus a full fresh CI cycle.
	NoUpdateBranch bool
	// NoAutoMerge keeps the historical behaviour of returning checks-pending
	// when the wait budget runs out. The default arms GitHub auto-merge
	// instead, so a complete change is not left stranded on whoever remembers
	// it next.
	//
	// The arming is deliberately not withdrawn when checks fail. CI is the
	// gate: whoever pushes a fix is responsible for it, the required checks
	// re-run against exactly what they pushed, and the merge happens only if
	// those pass. A review that must not be skippable belongs in the workflow,
	// where it runs on every push and nobody can decline to re-request it —
	// not in an approval recorded once against a head that no longer exists.
	NoAutoMerge bool
	// Lane optionally names the acquiring session for the landing-lane
	// ownership guard (see LaneGuardRequest in internal/orchestrate). Left
	// zero, no guard runs — existing direct callers are unaffected.
	Lane            LaneGuardRequest
	CheckoutUpdated func(context.Context, CheckoutUpdate)
	// contains filtered or unexported fields
}

PullRequestLandOptions identifies one pull request to land.

type PullRequestLandResult added in v0.89.0

type PullRequestLandResult struct {
	SchemaVersion int         `json:"v"`
	Verb          string      `json:"verb"`
	Outcome       LandOutcome `json:"outcome"`
	RefusalCode   string      `json:"refusal_code,omitempty"`
	// SanctionedCommand is the exact command that satisfies the guard that
	// fired. A refusal an agent cannot resolve becomes a hand-written
	// workaround, which is how the cleanup path was bypassed in the first place.
	SanctionedCommand string `json:"sanctioned_command,omitempty"`
	// AutoMergeArmed records that this invocation armed GitHub auto-merge. It
	// says nothing about who performed the merge: evidence "merged_by" is set
	// when GitHub did. When the invocation ends before the merge, GitHub lands
	// the change without WB, and the worktree is still the caller's to retire.
	AutoMergeArmed bool   `json:"auto_merge_armed,omitempty"`
	Reason         string `json:"reason,omitempty"`

	Repository  string `json:"repository"`
	PullRequest int    `json:"pull_request"`
	URL         string `json:"url,omitempty"`
	Title       string `json:"title,omitempty"`
	HeadRef     string `json:"head_ref,omitempty"`
	HeadSHA     string `json:"head_sha,omitempty"`
	BaseRef     string `json:"base_ref,omitempty"`
	MergeSHA    string `json:"merge_sha,omitempty"`
	Subject     string `json:"subject,omitempty"`

	// Mechanical records the diff-derived classification and the files it was
	// derived from, so a reader can check the judgement rather than trust it.
	Mechanical   bool     `json:"mechanical"`
	ChangedFiles []string `json:"changed_files,omitempty"`
	NonManifest  []string `json:"non_manifest_files,omitempty"`
	ApprovedBy   string   `json:"approved_by,omitempty"`
	// Reviewer, ReviewedHeadSHA, ReviewDigest, ReviewCommentURL, and
	// SelfReview are the #604/#586 receipt fields: the full reviewer
	// identity triple, the exact head it reviewed, a digest of the review
	// text, the posted comment's URL (identity form only), and whether the
	// reviewer identity fully matches this session's own (see
	// currentSessionIdentity).
	Reviewer         string `json:"reviewer,omitempty"`
	ReviewedHeadSHA  string `json:"reviewed_head,omitempty"`
	ReviewDigest     string `json:"review_digest,omitempty"`
	ReviewCommentURL string `json:"review_comment_url,omitempty"`
	SelfReview       bool   `json:"self_review,omitempty"`
	// ReviewBound is nil (omitted from JSON) when no review applied at all
	// (a mechanical landing, round 3 minor 7). Once a review did apply, it
	// is a pointer to false when the recorded review named no commit to
	// bind to, or when the binding could not be verified (#586's
	// warn-still-land design, founder-decided 2026-09-18) — the landing
	// proceeds either way, this is never a refusal — but Evidence["review"]
	// carries the informational finding so the gap is visible rather than
	// silent. It is a pointer to true only once the binding is positively
	// confirmed.
	ReviewBound *bool `json:"review_bound,omitempty"`
	// Closes lists the issues GitHub's own closingIssuesReferences reports
	// this landing closes (#615). Empty is reported as the informational
	// "no linked issue" finding in Evidence["closes"], never a refusal.
	Closes []int `json:"closes,omitempty"`

	Checks *PullRequestWaitResult `json:"checks,omitempty"`

	BranchDeleted bool   `json:"branch_deleted"`
	LandingOnBase bool   `json:"landing_on_base"`
	CanonicalSync string `json:"canonical_sync,omitempty"`
	// LocalSync records the outcome of fast-forwarding the local WB worktree
	// (if any) after a server-side update-branch, or the reason it was left
	// alone (#611). Empty when no worktree holds the branch.
	LocalSync string `json:"local_sync,omitempty"`
	// Commits pairs every source commit with the commit that landed it, and
	// marks the ones kept separate. GitHub's rebase merge rewrites the SHAs, so
	// after landing this pairing is the only way back to the originals.
	Commits        []LandedCommit `json:"commits,omitempty"`
	KeptCommits    []string       `json:"kept_commits,omitempty"`
	KeepReason     string         `json:"keep_reason,omitempty"`
	CleanedTasks   []string       `json:"cleaned_tasks,omitempty"`
	CleanupReports []string       `json:"cleanup_reports,omitempty"`
	Kept           bool           `json:"kept"`

	// ManualEquivalent is the ordered list of calls a caller would otherwise
	// have made. SavedToolCalls is that count minus one — the one call they
	// made instead.
	ManualEquivalent    []string `json:"manual_equivalent"`
	SavedToolCalls      int      `json:"saved_tool_calls"`
	SavedTokensEstimate int      `json:"saved_tokens_est"`
	// AbsorbedPolls counts the check observations the verb waited through.
	// Absorbing a poll loop is the largest single saving it makes.
	AbsorbedPolls int `json:"absorbed_polls"`

	Evidence map[string]string `json:"evidence,omitempty"`

	// LaneOwner is the landing-lane record this landing acquired, when the
	// caller populated Lane. It is nil when no guard ran.
	LaneOwner *landinglane.Record `json:"lane_owner,omitempty"`
}

PullRequestLandResult is the receipt, and the JSON envelope.

func LandPullRequest added in v0.89.0

func LandPullRequest(ctx context.Context, options PullRequestLandOptions) (result PullRequestLandResult, err error)

LandPullRequest verifies, merges, and tidies up after one pull request.

func (PullRequestLandResult) ExitCode added in v0.89.0

func (result PullRequestLandResult) ExitCode() int

ExitCode maps the outcome onto WB's exit contract.

func (PullRequestLandResult) FooterLine added in v0.89.0

func (result PullRequestLandResult) FooterLine() string

FooterLine is the interactive-mode summary. It is suppressed under --non-interactive, where the JSON envelope carries the same figures.

type PullRequestView added in v0.89.0

type PullRequestView struct {
	Number         int        `json:"number"`
	State          string     `json:"state"`
	Draft          bool       `json:"draft"`
	Locked         bool       `json:"locked"`
	Title          string     `json:"title"`
	Body           string     `json:"body"`
	HTMLURL        string     `json:"html_url"`
	Merged         bool       `json:"merged"`
	MergedAt       *time.Time `json:"merged_at"`
	MergeCommitSHA string     `json:"merge_commit_sha"`
	// Mergeable is nil while GitHub is still computing the merge state. A nil
	// is not a "no": it is "ask again", and a verb must not read it as either
	// mergeable or conflicted.
	Mergeable      *bool  `json:"mergeable"`
	MergeableState string `json:"mergeable_state"`
	Head           struct {
		Ref  string `json:"ref"`
		SHA  string `json:"sha"`
		Repo *struct {
			FullName string `json:"full_name"`
		} `json:"repo"`
	} `json:"head"`
	Base struct {
		Ref  string `json:"ref"`
		SHA  string `json:"sha"`
		Repo *struct {
			FullName string `json:"full_name"`
		} `json:"repo"`
	} `json:"base"`
}

PullRequestView is the pull-request state the land and merge verbs branch on. It is deliberately small: every field here is one a verb actually reads.

func ReadPullRequest added in v0.89.0

func ReadPullRequest(ctx context.Context, repository, selector string) (PullRequestView, error)

ReadPullRequest reads one pull request. It replaces `gh pr view --json`, which the installed client supports but which would still be a second dialect for the same fact.

type PullRequestWaitOptions added in v0.28.0

type PullRequestWaitOptions struct {
	Repository  string
	PullRequest string
	Target      string
	Head        string
	// AllowTargetDescendant is only for post-landing target CI: the exact
	// landed Head must remain an ancestor of the observed target. Pre-landing
	// candidate and pull-request waits retain exact target-head freshness.
	AllowTargetDescendant bool
	// AllowUnfenced permits a validation-only PR check receipt when the target
	// branch has no server-enforced strict freshness fence. Merge callers leave
	// this false; it is an explicit opt-in for wait-only validation.
	AllowUnfenced     bool
	Slice             time.Duration
	CheckPollInterval time.Duration
	// StableRereadDelay overrides the shortened wait before the confirming
	// reread of a checks-bearing terminal observation. A zero value uses
	// DefaultStableRereadDelay, and the delay never exceeds
	// CheckPollInterval. The no-applicable-checks receipt and any reread
	// after fingerprint churn always wait the full CheckPollInterval.
	StableRereadDelay time.Duration
	// Progress receives completed GitHub observations. It is diagnostic only;
	// callers must use the returned result as the authoritative receipt.
	Progress          func(PullRequestWaitProgress)
	OperationProgress progress.Reporter
}

PullRequestWaitOptions identifies exactly one direct-push or pull-request head whose observed checks are read by a bounded foreground invocation. A caller resumes a pending result with the same repository, target, PR (when supplied), and head; any later head is a distinct integration candidate.

type PullRequestWaitProgress added in v0.50.0

type PullRequestWaitProgress struct {
	Observation int
	Result      PullRequestWaitResult
	NextPoll    time.Duration
}

PullRequestWaitProgress is one completed observation inside a bounded wait.

type PullRequestWaitResult added in v0.28.0

type PullRequestWaitResult struct {
	Status                     PullRequestWaitStatus `json:"status" yaml:"status"`
	Repository                 string                `json:"repository" yaml:"repository"`
	PullRequest                string                `json:"pull_request,omitempty" yaml:"pull_request,omitempty"`
	Target                     string                `json:"target" yaml:"target"`
	Head                       string                `json:"head" yaml:"head"`
	ObservedHead               string                `json:"observed_head,omitempty" yaml:"observed_head,omitempty"`
	ObservedTargetHead         string                `json:"observed_target_head,omitempty" yaml:"observed_target_head,omitempty"`
	CandidateContainsTarget    bool                  `json:"candidate_contains_target,omitempty" yaml:"candidate_contains_target,omitempty"`
	TargetContainsHead         bool                  `json:"target_contains_head,omitempty" yaml:"target_contains_head,omitempty"`
	TargetFreshnessAuthority   string                `json:"target_freshness_authority,omitempty" yaml:"target_freshness_authority,omitempty"`
	Checks                     []RemoteCheck         `json:"checks,omitempty" yaml:"checks,omitempty"`
	FailureDetails             []CIFailureDetail     `json:"failure_details,omitempty" yaml:"failure_details,omitempty"`
	RequiredChecks             []RequiredRemoteCheck `json:"required_checks,omitempty" yaml:"required_checks,omitempty"`
	RequiredChecksAuthority    string                `json:"required_checks_authority,omitempty" yaml:"required_checks_authority,omitempty"`
	PolicyAuthorityUnavailable string                `json:"policy_authority_unavailable,omitempty" yaml:"policy_authority_unavailable,omitempty"`
	UnfencedValidation         bool                  `json:"unfenced_validation,omitempty" yaml:"unfenced_validation,omitempty"`
	StableObservations         int                   `json:"stable_observations" yaml:"stable_observations"`
	Reason                     string                `json:"reason,omitempty" yaml:"reason,omitempty"`
	// Evidence carries auxiliary receipt facts that are not part of the wait
	// outcome itself. "github_read_retries" mirrors the same key on
	// PullRequestLandResult: the count and last cause of in-process transient
	// GitHub read recoveries absorbed while producing this result.
	Evidence map[string]string `json:"evidence,omitempty" yaml:"evidence,omitempty"`
}

PullRequestWaitResult is one terminating foreground observation slice. Pending means resume is required, not that the merger is finished.

func WaitForCommitChecks added in v0.28.0

func WaitForCommitChecks(ctx context.Context, options PullRequestWaitOptions) (PullRequestWaitResult, error)

WaitForCommitChecks observes checks for one exact target commit. PullRequest is optional: when present it corroborates that exact PR head and target and augments the exact-head check-run/status receipt with GitHub's PR view. Every mode observes the exact commit through producer-aware APIs. Pending is an intermediate terminal result that callers resume with the same identity, not successful completion.

func WaitForPullRequestChecks added in v0.28.0

func WaitForPullRequestChecks(ctx context.Context, options PullRequestWaitOptions) (PullRequestWaitResult, error)

WaitForPullRequestChecks retains the original internal seam for existing orchestrated PR flows while using the exact-commit waiter above.

type PullRequestWaitStatus added in v0.28.0

type PullRequestWaitStatus string

PullRequestWaitStatus is intentionally small so callers can branch on a machine result instead of parsing human GitHub CLI output.

const (
	PullRequestWaitPassed  PullRequestWaitStatus = "passed"
	PullRequestWaitPending PullRequestWaitStatus = "pending"
	PullRequestWaitFailed  PullRequestWaitStatus = "failed"
)

type RemoteCheck

type RemoteCheck struct {
	Name   string `json:"name" yaml:"name"`
	Bucket string `json:"bucket" yaml:"bucket"`
	// Conclusion is the raw GitHub check-run/workflow-run conclusion (e.g.
	// "success", "skipped", "neutral", "failure"), kept alongside Bucket so a
	// strict deferral-satisfaction check (sneat-dev/wb#591 red-team finding
	// X2) can tell an actually-executed pass ("success") apart from a check
	// that never ran ("skipped" or "neutral") even though checkRunBucket
	// buckets both "success" and "neutral" the same, as an ordinary "pass"
	// (only "skipped" gets its own "skipping" bucket) for the overall
	// pass/fail loop. Empty for a commit-status-derived check, which has no
	// conclusion.
	Conclusion string `json:"conclusion,omitempty" yaml:"conclusion,omitempty"`
	Link       string `json:"link,omitempty" yaml:"link,omitempty"`
	AppID      int64  `json:"app_id,omitempty" yaml:"app_id,omitempty"`
	CheckRunID int64  `json:"check_run_id,omitempty" yaml:"check_run_id,omitempty"`
}

RemoteCheck is the normalized GitHub check state observed before merge.

type Repository

type Repository struct {
	Slug     string
	Path     string
	CloneURL string
	Archived bool
}

Repository identifies a canonical clone selected by command-level discovery.

type RequiredRemoteCheck added in v0.28.0

type RequiredRemoteCheck struct {
	Name          string `json:"name" yaml:"name"`
	IntegrationID int64  `json:"integration_id,omitempty" yaml:"integration_id,omitempty"`
}

RequiredRemoteCheck is GitHub's target-policy expectation. IntegrationID is non-zero when a ruleset pins the context to one GitHub App; every receipt must then observe the matching exact-head check-run producer, not merely a same-named PR summary or legacy status from another actor.

type ResolvedBase added in v0.59.2

type ResolvedBase struct {
	// Ref is the short branch name (no "origin/" prefix).
	Ref string
	// Fallback is true when the operation's configured ref did not exist for
	// this repository and Ref instead names its actual origin/HEAD default
	// branch. Nothing about this is silent: every caller that receives a
	// Fallback result is expected to surface it in its own report.
	Fallback bool
}

ResolvedBase is the git ref EnsureCanonical verified exists in a repository's canonical clone. Every remaining lifecycle stage for this repository — graph inspection, worktree creation, and any eventual pull request base — must use Ref rather than the operation's configured options.Ref, since the two differ exactly when Fallback is true.

func EnsureCanonical

func EnsureCanonical(ctx context.Context, repository Repository, canonical string, options Options) (ResolvedBase, error)

EnsureCanonical clones a missing repository, fetches origin, and verifies a usable base ref without checking out or modifying the canonical tree. It first tries the operation's configured options.Ref (default "main"); a fleet inevitably contains repositories whose default branch is something else (commonly "master", or a branch renamed after the local clone was made), so a repository that lacks options.Ref falls back to its actual origin/HEAD default branch instead of failing outright. A repository for which neither ref resolves still fails loudly — this is a fallback to a known-good alternative, never a silent skip.

type Result

type Result[T any] struct {
	Repository    string
	CanonicalDir  string
	WorktreeDir   string
	Branch        string
	Ref           string
	Status        string
	Reason        string
	Metadata      T
	ChangedFiles  []string
	Verifications []quality.VerificationEntry
	Commit        string
	Pushed        bool
	PR            string
	Checks        []RemoteCheck
	Merged        bool
	// Held records that Options.Hold matched this repository, so its pull
	// request was deliberately left open for a human decision rather than
	// merged. It is never inferred from a failure.
	Held bool
}

Result records lifecycle state and typed adapter metadata for one repository.

func Run

func Run[T any](ctx context.Context, repositories []Repository, handler Handler[T], options Options) ([]Result[T], error)

Run executes a typed mutation over independent repositories. It completes every safe local/PR stage before entering the CI wait-and-merge phase.

type ReviewerIdentity added in v0.146.0

type ReviewerIdentity struct {
	Model   string `json:"model"`
	Harness string `json:"harness,omitempty"`
	Session string `json:"session,omitempty"`
}

ReviewerIdentity is the `{model}[@{harness}[@{session}]]` triple #604 records for a declared review. Any part the caller did not supply, and that the environment cannot fill, is "unknown" — never guessed.

func FillReviewerIdentityFromEnvironment added in v0.146.0

func FillReviewerIdentityFromEnvironment(identity ReviewerIdentity) ReviewerIdentity

FillReviewerIdentityFromEnvironment fills whatever part of identity the caller omitted from the invoking process's environment. It never overwrites a part the caller supplied, and it never guesses: a part nothing determines is left for FinalizeReviewerIdentity to record as "unknown".

func FinalizeReviewerIdentity added in v0.146.0

func FinalizeReviewerIdentity(identity ReviewerIdentity) ReviewerIdentity

FinalizeReviewerIdentity records whatever part is still empty after FillReviewerIdentityFromEnvironment as "unknown" — the receipt always stores the full triple, per #604.

func ParseReviewerIdentity added in v0.146.0

func ParseReviewerIdentity(raw string) ReviewerIdentity

ParseReviewerIdentity splits a raw `--approved-by` identity value on "@": model[@harness[@session]].

func (ReviewerIdentity) SelfReview added in v0.146.0

func (identity ReviewerIdentity) SelfReview(other ReviewerIdentity) bool

SelfReview is true only when every one of the three parts matches the other identity — model-only matching is wrong for the normal flow (Sonnet implements, Opus subagent reviews, in the same session), per #604.

func (ReviewerIdentity) String added in v0.146.0

func (identity ReviewerIdentity) String() string

String renders the canonical `model@harness@session` form recorded on the receipt and printed in the posted review comment's header.

type SourceCommit added in v0.89.0

type SourceCommit struct {
	SHA     string `json:"sha"`
	Subject string `json:"subject"`
	Body    string `json:"body,omitempty"`
}

SourceCommit is one commit of the pull request's branch.

type WorktreeMergeAbsorbedConflictAcknowledgement added in v0.117.0

type WorktreeMergeAbsorbedConflictAcknowledgement = mergeack.Acknowledgement

WorktreeMergeAbsorbedConflictSourceProof, WorktreeMergeAbsorbedConflictPathProof, and WorktreeMergeAbsorbedConflictAcknowledgement alias internal/mergeack's types directly (rather than duplicating them) so this package's on-disk format, identity hash, and validation are always exactly the ones internal/mergeack defines -- the single source of truth internal/worktrees also reads. See internal/mergeack's package doc for the full rationale.

func AcknowledgeAbsorbedConflict added in v0.117.0

AcknowledgeAbsorbedConflict proves, for every receipted source of a prepare-phase conflict receipt whose source worktrees are all gone, that the source's changes are safely represented on the freshly fetched current remote target -- either because the source SHA is a graph ancestor of that target, or because every path it changed relative to its merge-base with the target is independently proved absorbed (including a narrowly checked root Go dependency upgrade) -- then records a separate audited acknowledgement so a fresh candidate can own the merger lane. It never reads or requires a receipted source worktree (that infrastructure being gone is exactly the failure this recovers from), never rewrites the historical receipt or any Work Log, and never deletes a preserved, unpublished candidate worktree. This is a dry-run by default; --apply requires --actor and --reason and writes only the new acknowledgement artifact.

type WorktreeMergeAbsorbedConflictAcknowledgementOptions added in v0.117.0

type WorktreeMergeAbsorbedConflictAcknowledgementOptions struct {
	ProjectsRoot string
	Receipt      string
	Apply        bool
	Actor        string
	Reason       string
	// DerivedPaths audits an operator exclusion for derived/generated files
	// (repo-relative, repeatable) -- each must exist on the freshly fetched
	// target and match the built-in derived-index allowlist
	// (isAbsorbedConflictDerivedPathAllowed), or the whole call refuses.
	DerivedPaths []string
}

WorktreeMergeAbsorbedConflictAcknowledgementOptions configures AcknowledgeAbsorbedConflict.

type WorktreeMergeAbsorbedConflictPathProof added in v0.117.0

type WorktreeMergeAbsorbedConflictPathProof = mergeack.PathProof

WorktreeMergeAbsorbedConflictSourceProof, WorktreeMergeAbsorbedConflictPathProof, and WorktreeMergeAbsorbedConflictAcknowledgement alias internal/mergeack's types directly (rather than duplicating them) so this package's on-disk format, identity hash, and validation are always exactly the ones internal/mergeack defines -- the single source of truth internal/worktrees also reads. See internal/mergeack's package doc for the full rationale.

type WorktreeMergeAbsorbedConflictSourceProof added in v0.117.0

type WorktreeMergeAbsorbedConflictSourceProof = mergeack.SourceProof

WorktreeMergeAbsorbedConflictSourceProof, WorktreeMergeAbsorbedConflictPathProof, and WorktreeMergeAbsorbedConflictAcknowledgement alias internal/mergeack's types directly (rather than duplicating them) so this package's on-disk format, identity hash, and validation are always exactly the ones internal/mergeack defines -- the single source of truth internal/worktrees also reads. See internal/mergeack's package doc for the full rationale.

type WorktreeMergeCandidate added in v0.66.0

type WorktreeMergeCandidate struct {
	Task     string `json:"task"`
	Worktree string `json:"worktree"`
	Branch   string `json:"branch"`
	SHA      string `json:"sha"`
}

type WorktreeMergeConflictCandidateAdvance added in v0.97.9

type WorktreeMergeConflictCandidateAdvance struct {
	SchemaVersion        int                    `json:"schema_version"`
	ID                   string                 `json:"id"`
	Status               string                 `json:"status"`
	ReceiptPath          string                 `json:"receipt_path"`
	AcknowledgementPath  string                 `json:"acknowledgement_path"`
	ReceiptSHA256        string                 `json:"receipt_sha256"`
	ReceiptID            string                 `json:"receipt_id"`
	Lane                 string                 `json:"lane"`
	Repository           string                 `json:"repository"`
	Target               string                 `json:"target"`
	ReceiptTargetSHA     string                 `json:"receipt_target_sha"`
	CurrentTargetSHA     string                 `json:"current_target_sha"`
	OriginalCandidate    WorktreeMergeCandidate `json:"original_candidate"`
	AdvancedCandidateSHA string                 `json:"advanced_candidate_sha"`
	ClaimBaseSHA         string                 `json:"claim_base_sha"`
	Sources              []WorktreeMergeSource  `json:"sources"`
	RecordedAt           time.Time              `json:"recorded_at"`
}

WorktreeMergeConflictCandidateAdvance is the append-only bridge between a conflict receipt's original candidate and the clean, manually resolved descendant. It is written before the mutable receipt is advanced to that descendant, so an interrupted resume cannot publish an unaudited head.

type WorktreeMergeConflictCandidateRefresh added in v0.97.7

type WorktreeMergeConflictCandidateRefresh struct {
	Status                      string                                   `json:"status"`
	ReceiptPath                 string                                   `json:"receipt_path"`
	ReceiptSHA256               string                                   `json:"receipt_sha256"`
	ImmutableClaimSHA256        string                                   `json:"immutable_claim_sha256"`
	CurrentTargetSHA            string                                   `json:"current_target_sha"`
	ObservedCandidateDescendant string                                   `json:"observed_candidate_descendant_sha,omitempty"`
	Sources                     []WorktreeMergeSource                    `json:"sources"`
	RequiredRoots               []WorktreeMergeValidationFailureSealRoot `json:"required_roots"`
	Candidate                   WorktreeMergeCandidate                   `json:"candidate"`
	Actor                       string                                   `json:"actor"`
	Reason                      string                                   `json:"reason"`
}

WorktreeMergeConflictCandidateRefresh is the unconsumed candidate created while a failed conflict receipt still owns its merger lane. The existing supersession acknowledgement remains the only transition that frees it.

func PrepareConflictWorktreeMergeReplacement added in v0.97.7

func PrepareConflictWorktreeMergeReplacement(ctx context.Context, options WorktreeMergeConflictCandidateRefreshOptions) (result WorktreeMergeConflictCandidateRefresh, retErr error)

PrepareConflictWorktreeMergeReplacement breaks only the cycle where a non-terminal, unpublished prepare conflict owns the lane that ordinary prepare would need to create the replacement. It writes no receipt or acknowledgement; callers pass the returned candidate to supersede-validation-failed.

type WorktreeMergeConflictCandidateRefreshOptions added in v0.97.7

type WorktreeMergeConflictCandidateRefreshOptions struct {
	ProjectsRoot, Receipt, ExpectedReceiptSHA256, ExpectedImmutableClaimSHA256, ExpectedCurrentTargetSHA string
	Sources, ExpectedSourceSHAs                                                                          []string
	Apply                                                                                                bool
	Actor, Reason                                                                                        string
	Model, AgentRuntime, AgentID, Initiator, CLI, Provider                                               string
	SessionRequired                                                                                      bool
	Timeout                                                                                              time.Duration
	Retry                                                                                                int
	Progress                                                                                             progress.Reporter
}

func (WorktreeMergeConflictCandidateRefreshOptions) RefreshTask added in v0.97.7

type WorktreeMergeFinding added in v0.146.0

type WorktreeMergeFinding struct {
	Code    string   `json:"code"`
	Message string   `json:"message"`
	Checks  []string `json:"checks,omitempty"`
}

WorktreeMergeFinding is one non-blocking observation recorded on a receipt. See WorktreeMergeReceipt.Findings.

type WorktreeMergeForwardRepairReceipt added in v0.66.0

type WorktreeMergeForwardRepairReceipt struct {
	Status       WorktreeMergeStatus   `json:"status"`
	TargetSHA    string                `json:"target_sha"`
	CandidateSHA string                `json:"candidate_sha"`
	LandingSHA   string                `json:"landing_sha"`
	PullRequest  string                `json:"pull_request,omitempty"`
	Checks       PullRequestWaitResult `json:"checks"`
	Failure      string                `json:"failure"`
}

WorktreeMergeForwardRepairReceipt preserves the exact landed attempt whose failed target CI required a new forward repair. The active lane reuses its candidate and receipt instead of abandoning either or pretending the prior remote landing never happened.

type WorktreeMergeHostLoadAdmission added in v0.118.0

type WorktreeMergeHostLoadAdmission struct {
	Load       float64 `json:"load"`
	Floor      float64 `json:"floor"`
	Overridden bool    `json:"overridden"`
	// SkippedReason names why admission was disabled for this check (never
	// evaluated against Load): "env" (WB_ADMISSION_LOAD_FLOOR<=0), "ci"
	// (CI/GITHUB_ACTIONS declared), or "config" (wb.yaml admission.load_floor:
	// 0). Empty means admission was active — see internal/hostload.Resolve.
	SkippedReason string    `json:"skipped_reason,omitempty"`
	CheckedAt     time.Time `json:"checked_at"`
}

WorktreeMergeHostLoadAdmission records one host-load admission check that actually ran against a merge verb (prepare/merge/land/resume/revert). A receipt with no such check recorded either predates this field or the check was skipped because the step it gates never re-runs local CPU-heavy validation (see cmd/wb's hostLoadCheckSkippable).

type WorktreeMergeLandOptions added in v0.66.0

type WorktreeMergeLandOptions struct {
	ProjectsRoot  string
	Receipt       string
	Route         WorktreeMergeRoute
	Cleanup       bool
	AllowUnfenced bool
	OnFailure     string
	Timeout       time.Duration
	Retry         int
	// ValidateLocally forces the old unconditional behavior: local candidate
	// validation always runs, even when this call resolves the pull-request
	// route and its target's authoritative required-check policy would
	// otherwise be eligible for a validation deferral. See
	// worktreeMergeValidationDeferralEligible and sneat-dev/wb#591.
	ValidateLocally bool
	// PrepareTimeout bounds recovery of an interrupted preparing receipt.
	// It does not apply after the candidate has reached prepared state.
	PrepareTimeout time.Duration
	// CheckTimeout and ShardAttemptTimeout override the validation limits stored
	// by an interrupted preparing receipt. The effective limits are persisted
	// before validation so a later retry observes the same policy.
	CheckTimeout        time.Duration
	ShardAttemptTimeout time.Duration
	CheckPollInterval   time.Duration
	Progress            progress.Reporter
	ProgressRequested   bool
	// StopBeforeMerge is the explicit PR-only handoff mode. It validates the
	// preserved candidate and proves the remote open PR identity, then returns
	// before CI observation or any merge operation. It is deliberately not
	// persisted as future landing intent: a bare resume is how the merger takes
	// over from the published handoff.
	StopBeforeMerge bool
	// HostLoadAdmission is the host-load admission check cmd/wb ran, if any,
	// before calling Land/ResumeWorktreeMerge. Nil means the caller decided no
	// check was needed for this step (e.g. it neither validates nor pushes).
	// When set it is copied onto the receipt so the override is provable from
	// the receipt itself, not only from the caller's own log.
	HostLoadAdmission *WorktreeMergeHostLoadAdmission
	// Lane optionally names the acquiring session for the landing-lane
	// ownership guard (see LaneGuardRequest). Left zero, no guard runs.
	Lane LaneGuardRequest
	// CheckoutUpdated is called only after the checked-out canonical target
	// has moved and the exact landed commit is proven reachable. Nil discards.
	CheckoutUpdated func(context.Context, CheckoutUpdate)
}

type WorktreeMergeLandedFailureAcknowledgement added in v0.69.2

type WorktreeMergeLandedFailureAcknowledgement struct {
	SchemaVersion       int                   `json:"schema_version"`
	ID                  string                `json:"id"`
	Status              string                `json:"status"`
	ReceiptPath         string                `json:"receipt_path"`
	AcknowledgementPath string                `json:"acknowledgement_path"`
	ReceiptID           string                `json:"receipt_id"`
	ReceiptStatus       WorktreeMergeStatus   `json:"receipt_status"`
	Lane                string                `json:"lane"`
	Repository          string                `json:"repository"`
	Target              string                `json:"target"`
	ReceiptTargetSHA    string                `json:"receipt_target_sha"`
	ReceiptLandingSHA   string                `json:"receipt_landing_sha,omitempty"`
	CurrentTargetSHA    string                `json:"current_target_sha"`
	CandidateSHA        string                `json:"candidate_sha"`
	ClaimBaseSHA        string                `json:"claim_base_sha"`
	CandidateWorktree   string                `json:"candidate_worktree"`
	CandidateBranch     string                `json:"candidate_branch"`
	Sources             []WorktreeMergeSource `json:"sources"`
	Actor               string                `json:"actor"`
	Reason              string                `json:"reason"`
	RecordedAt          time.Time             `json:"recorded_at"`
}

WorktreeMergeLandedFailureAcknowledgement is a separate, append-only acknowledgement for a historical merge receipt whose candidate is proved to be present in the current target but whose validation boundary never became terminal. The original merge receipt and Work Log remain untouched.

func AcknowledgeLandedMergeFailure added in v0.69.2

AcknowledgeLandedMergeFailure proves that a non-terminal failed receipt is already represented by the current remote target, then writes a distinct audited acknowledgement. It never rewrites the historical receipt or any Work Log record. A failed proof is always a refusal.

type WorktreeMergeLandedFailureAcknowledgementOptions added in v0.69.2

type WorktreeMergeLandedFailureAcknowledgementOptions struct {
	ProjectsRoot string
	Receipt      string
	Apply        bool
	Actor        string
	Reason       string
}

type WorktreeMergeLegacyConflictIdentity added in v0.121.2

type WorktreeMergeLegacyConflictIdentity struct {
	SchemaVersion       int                    `json:"schema_version"`
	ID                  string                 `json:"id"`
	Status              string                 `json:"status"`
	ReceiptPath         string                 `json:"receipt_path"`
	AcknowledgementPath string                 `json:"acknowledgement_path"`
	ReceiptSHA256       string                 `json:"receipt_sha256"`
	ReceiptID           string                 `json:"receipt_id"`
	Lane                string                 `json:"lane"`
	Repository          string                 `json:"repository"`
	Target              string                 `json:"target"`
	ReceiptTargetSHA    string                 `json:"receipt_target_sha"`
	CurrentTargetSHA    string                 `json:"current_target_sha"`
	Candidate           WorktreeMergeCandidate `json:"candidate"`
	ClaimBaseSHA        string                 `json:"claim_base_sha"`
	Sources             []WorktreeMergeSource  `json:"sources"`
	Actor               string                 `json:"actor"`
	Reason              string                 `json:"reason"`
	RecordedAt          time.Time              `json:"recorded_at"`
}

WorktreeMergeLegacyConflictIdentity records the only candidate SHA WB may derive for an unpublished prepare conflict whose writer omitted it. The candidate must still be clean, locally claimed, unpublished, unlanded, and contain the receipt target plus every exact receipted source.

type WorktreeMergeLegacyValidationFailureIdentity added in v0.97.1

type WorktreeMergeLegacyValidationFailureIdentity struct {
	SchemaVersion       int                    `json:"schema_version"`
	ID                  string                 `json:"id"`
	Status              string                 `json:"status"`
	ReceiptPath         string                 `json:"receipt_path"`
	AcknowledgementPath string                 `json:"acknowledgement_path"`
	ReceiptSHA256       string                 `json:"receipt_sha256"`
	ReceiptID           string                 `json:"receipt_id"`
	Lane                string                 `json:"lane"`
	Repository          string                 `json:"repository"`
	Target              string                 `json:"target"`
	ReceiptTargetSHA    string                 `json:"receipt_target_sha"`
	CurrentTargetSHA    string                 `json:"current_target_sha"`
	Candidate           WorktreeMergeCandidate `json:"candidate"`
	ClaimBaseSHA        string                 `json:"claim_base_sha"`
	Sources             []WorktreeMergeSource  `json:"sources"`
	Actor               string                 `json:"actor"`
	Reason              string                 `json:"reason"`
	RecordedAt          time.Time              `json:"recorded_at"`
}

WorktreeMergeLegacyValidationFailureIdentity records the only identity WB may derive for a legacy validation_failed receipt whose writer omitted the candidate SHA. The historical receipt remains immutable; every field here is corroborated from its registered candidate worktree, active claim, exact sources, and current remote-target observation.

type WorktreeMergeMissingCleanupAcknowledgement added in v0.97.4

type WorktreeMergeMissingCleanupAcknowledgement struct {
	SchemaVersion       int                                    `json:"schema_version"`
	ID                  string                                 `json:"id"`
	Status              string                                 `json:"status"`
	ReceiptPath         string                                 `json:"receipt_path"`
	AcknowledgementPath string                                 `json:"acknowledgement_path"`
	ReceiptSHA256       string                                 `json:"receipt_sha256"`
	ReceiptID           string                                 `json:"receipt_id"`
	Lane                string                                 `json:"lane"`
	Repository          string                                 `json:"repository"`
	Target              string                                 `json:"target"`
	LandingSHA          string                                 `json:"landing_sha"`
	CurrentTargetSHA    string                                 `json:"current_target_sha"`
	Assets              []worktrees.TerminalWorkLogExpectation `json:"absent_assets"`
	Actor               string                                 `json:"actor"`
	Reason              string                                 `json:"reason"`
	RecordedAt          time.Time                              `json:"recorded_at"`
}

WorktreeMergeMissingCleanupAcknowledgement records the narrow legacy case where a landed receipt's exact worktrees and branches were already removed, but the historical cleanup did not retain terminal Work Log evidence.

func AcknowledgeMissingWorktreeMergeCleanup added in v0.97.4

AcknowledgeMissingWorktreeMergeCleanup records independently reproducible evidence for legacy cleanup which removed every exact asset but lost one or more terminal Work Logs. It never creates replacement Work Log evidence.

type WorktreeMergeMissingCleanupAcknowledgementOptions added in v0.97.4

type WorktreeMergeMissingCleanupAcknowledgementOptions struct {
	ProjectsRoot string
	Receipt      string
	Apply        bool
	Actor        string
	Reason       string
}

type WorktreeMergePhase added in v0.66.0

type WorktreeMergePhase string
const (
	WorktreeMergePhasePrepare WorktreeMergePhase = "prepare"
	WorktreeMergePhaseLand    WorktreeMergePhase = "land"
	WorktreeMergePhaseRevert  WorktreeMergePhase = "revert"
)

type WorktreeMergePrepareOptions added in v0.66.0

type WorktreeMergePrepareOptions struct {
	ProjectsRoot string
	Sources      []string
	Target       string
	Model        string
	AgentRuntime string
	AgentID      string
	Initiator    string
	CLI          string
	Provider     string
	Timeout      time.Duration
	Retry        int
	// Route lets a caller decide the merge route before preparing (and
	// therefore before deciding whether local validation may be deferred to
	// the pull-request route's authoritative CI). Empty resolves as auto, the
	// same default LandWorktreeMerge uses.
	Route WorktreeMergeRoute
	// ValidateLocally forces local candidate validation during prepare even
	// when the pull-request route would otherwise be eligible to defer it.
	ValidateLocally bool
	// PrepareTimeout bounds one prepare invocation. Zero leaves preparation
	// unbounded apart from the existing command timeout.
	PrepareTimeout time.Duration
	// CheckTimeout bounds one logical candidate or baseline validation check.
	// Zero retains the existing per-command behavior.
	CheckTimeout time.Duration
	// ShardAttemptTimeout bounds one process-isolated Go test shard attempt.
	// Zero retains the existing --timeout behavior for shard attempts.
	ShardAttemptTimeout time.Duration
	Progress            progress.Reporter
	ProgressRequested   bool
	// RebatchReceipt is an immutable, still-unlanded prepared or exact published
	// pending/failed-check receipt whose sources are replaced additively.
	RebatchReceipt string
	// HostLoadAdmission is the host-load admission check cmd/wb ran before
	// calling PrepareWorktreeMerge, if any. It is copied onto the new receipt
	// so the override is provable from the receipt itself.
	HostLoadAdmission *WorktreeMergeHostLoadAdmission
	// RequireHostLoadAdmission is cmd/wb's lazy fallback for the gap round
	// 4's minor 2 closes: its combined `wb worktree land`/`wb land` command
	// skips the up-front host-load admission check whenever a cheap
	// PeekWorktreeMergeValidationDeferral call predicts the resolved
	// validation plan will defer to CI (so no local CPU work is coming,
	// and the check would gate nothing). If a transient GitHub read makes
	// the ACTUAL resolve here disagree with that prediction and fall back
	// to local validation after all, HostLoadAdmission is still nil — the
	// up-front check never ran — and running CPU-heavy validation ungated
	// is exactly the saturated-host incident admission exists to prevent.
	// When set, this is invoked exactly once, only when the resolved plan
	// does not defer and HostLoadAdmission is still nil; a non-nil error
	// stops the prepare before validation ever runs, exactly as the
	// up-front check would have. Nil (every other caller) changes nothing.
	RequireHostLoadAdmission func() (*WorktreeMergeHostLoadAdmission, error)
	// Lane optionally names the acquiring session for the landing-lane
	// ownership guard (see LaneGuardRequest). Left zero, no guard runs.
	Lane LaneGuardRequest
}

type WorktreeMergePreparedRebatch added in v0.71.0

type WorktreeMergePreparedRebatch struct {
	SchemaVersion          int                    `json:"schema_version"`
	ID                     string                 `json:"id"`
	Status                 string                 `json:"status"`
	ReceiptPath            string                 `json:"receipt_path"`
	AcknowledgementPath    string                 `json:"acknowledgement_path"`
	ReceiptID              string                 `json:"receipt_id"`
	ReceiptSHA256          string                 `json:"receipt_sha256"`
	ReceiptStatus          WorktreeMergeStatus    `json:"receipt_status"`
	Lane                   string                 `json:"lane"`
	Repository             string                 `json:"repository"`
	Target                 string                 `json:"target"`
	ReceiptTargetSHA       string                 `json:"receipt_target_sha"`
	CurrentTargetSHA       string                 `json:"current_target_sha"`
	OriginalCandidate      WorktreeMergeCandidate `json:"original_candidate"`
	OriginalSources        []WorktreeMergeSource  `json:"original_sources"`
	ReplacementReceiptPath string                 `json:"replacement_receipt_path"`
	Replacement            WorktreeMergeCandidate `json:"replacement"`
	Sources                []WorktreeMergeSource  `json:"sources"`
	RecordedAt             time.Time              `json:"recorded_at"`
	// ClosedPullRequest names the superseded original candidate's own pull
	// request when this rebatch closed it (red-team finding M6): a
	// published-unlanded original has very likely already armed GitHub
	// auto-merge (the PR-land engine arms it before any check wait), and
	// leaving it open would let GitHub land the superseded content
	// alongside this replacement the moment its checks — or a later
	// update-branch — go green. Empty when the original never published a
	// pull request, so there was nothing to close.
	ClosedPullRequest string `json:"closed_pull_request,omitempty"`
}

WorktreeMergePreparedRebatch is an append-only link from one unlanded prepared receipt to a newly prepared candidate with an additive source set. It deliberately retains the original candidate and its exact receipt digest; neither historical record is changed to make room for a later source.

type WorktreeMergePublishedCandidateAdoption added in v0.95.2

type WorktreeMergePublishedCandidateAdoption struct {
	SchemaVersion       int                    `json:"schema_version"`
	ID                  string                 `json:"id"`
	Status              string                 `json:"status"`
	ReceiptPath         string                 `json:"receipt_path"`
	AcknowledgementPath string                 `json:"acknowledgement_path"`
	ReceiptSHA256       string                 `json:"receipt_sha256"`
	ReceiptID           string                 `json:"receipt_id"`
	Lane                string                 `json:"lane"`
	Repository          string                 `json:"repository"`
	Target              string                 `json:"target"`
	Candidate           WorktreeMergeCandidate `json:"candidate"`
	PullRequest         string                 `json:"pull_request"`
	Actor               string                 `json:"actor"`
	Reason              string                 `json:"reason"`
	RecordedAt          time.Time              `json:"recorded_at"`
}

WorktreeMergePublishedCandidateAdoption records remote publication that completed outside WB after prepare persisted but before it could record the PR. It is deliberately separate from the immutable merge receipt.

type WorktreeMergePublishedCandidateAdoptionOptions added in v0.95.2

type WorktreeMergePublishedCandidateAdoptionOptions struct {
	ProjectsRoot, Receipt, PullRequest string
	Apply                              bool
	Actor, Reason                      string
}

type WorktreeMergePublishedForwardRepair added in v0.80.2

type WorktreeMergePublishedForwardRepair struct {
	Status               string                                   `json:"status"`
	ReceiptPath          string                                   `json:"receipt_path"`
	ReceiptSHA256        string                                   `json:"receipt_sha256"`
	ImmutableClaimSHA256 string                                   `json:"immutable_claim_sha256"`
	SupersessionPath     string                                   `json:"supersession_path"`
	SupersessionSHA256   string                                   `json:"supersession_sha256"`
	CurrentTargetSHA     string                                   `json:"current_target_sha"`
	Sources              []WorktreeMergeSource                    `json:"sources"`
	RequiredRoots        []WorktreeMergeValidationFailureSealRoot `json:"required_roots"`
	Candidate            WorktreeMergeCandidate                   `json:"candidate"`
	Actor                string                                   `json:"actor"`
	Reason               string                                   `json:"reason"`
}

WorktreeMergePublishedForwardRepair is a deliberately unprepared candidate for correcting one historical self-supersession. It has no merge receipt: the later correction is the only append-only transition that can consume it.

func PreparePublishedValidationFailureForwardRepair added in v0.80.2

func PreparePublishedValidationFailureForwardRepair(ctx context.Context, options WorktreeMergePublishedForwardRepairOptions) (result WorktreeMergePublishedForwardRepair, retErr error)

PreparePublishedValidationFailureForwardRepair is the explicit cycle breaker for an already-published historical self-supersession. Ordinary prepare stays fail-closed. This path writes no historical artifact and creates no merge receipt; on apply it creates only one new WB-managed clean candidate whose DAG contains every immutable historical root and every pinned current source.

type WorktreeMergePublishedForwardRepairOptions added in v0.80.2

type WorktreeMergePublishedForwardRepairOptions struct {
	ProjectsRoot, Receipt, ExpectedReceiptSHA256, ExpectedImmutableClaimSHA256 string
	ExpectedSupersessionSHA256, ExpectedCurrentTargetSHA                       string
	Sources, ExpectedSourceSHAs                                                []string
	Apply                                                                      bool
	Actor, Reason                                                              string
	Model, AgentRuntime, AgentID, Initiator, CLI, Provider                     string
	SessionRequired                                                            bool
	Timeout                                                                    time.Duration
	Retry                                                                      int
}

WorktreeMergePublishedForwardRepairOptions pins every mutable historical input needed to create a distinct candidate for one known self-supersession. ExpectedSourceSHAs are positional with Sources, so a caller cannot silently replace a requested current repair source with another clean worktree.

func (WorktreeMergePublishedForwardRepairOptions) RepairTask added in v0.80.2

RepairTask is deterministic solely from caller-pinned immutable evidence, allowing apply retries to resume the same WB-managed candidate without guessing a branch from mutable filesystem state.

type WorktreeMergePushGateReceipt added in v0.66.0

type WorktreeMergePushGateReceipt struct {
	Remote            string    `json:"remote"`
	RemoteRef         string    `json:"remote_ref"`
	PreviousRemoteSHA string    `json:"previous_remote_sha"`
	LocalSHA          string    `json:"local_sha"`
	Status            string    `json:"status"`
	ObservedAt        time.Time `json:"observed_at"`
}

type WorktreeMergeRebaseReceipt added in v0.66.0

type WorktreeMergeRebaseReceipt struct {
	CandidateBefore string `json:"candidate_before"`
	TargetBefore    string `json:"target_before"`
	TargetAfter     string `json:"target_after"`
	CandidateAfter  string `json:"candidate_after"`
}

type WorktreeMergeReceipt added in v0.66.0

type WorktreeMergeReceipt struct {
	SchemaVersion         int                         `json:"schema_version"`
	ID                    string                      `json:"id"`
	Lane                  string                      `json:"lane"`
	Phase                 WorktreeMergePhase          `json:"phase"`
	Status                WorktreeMergeStatus         `json:"status"`
	Repository            string                      `json:"repository"`
	Target                string                      `json:"target"`
	TargetSHA             string                      `json:"target_sha"`
	Sources               []WorktreeMergeSource       `json:"sources"`
	Candidate             WorktreeMergeCandidate      `json:"candidate"`
	Rebase                *WorktreeMergeRebaseReceipt `json:"rebase,omitempty"`
	RevertOf              *WorktreeMergeRevertReceipt `json:"revert_of,omitempty"`
	Route                 WorktreeMergeRouteDecision  `json:"route,omitempty"`
	PullRequest           string                      `json:"pull_request,omitempty"`
	PublishedCandidateSHA string                      `json:"published_candidate_sha,omitempty"`
	PreviousTargetSHA     string                      `json:"previous_target_sha,omitempty"`
	LandingSHA            string                      `json:"landing_sha,omitempty"`
	CanonicalSync         string                      `json:"canonical_sync,omitempty"`
	LocalSync             string                      `json:"local_sync,omitempty"`
	Validation            quality.VerificationReport  `json:"validation,omitempty"`
	BaselineValidation    quality.VerificationReport  `json:"baseline_validation,omitempty"`
	// ValidationDeferral is set exactly when local validation was skipped for
	// the pull-request route rather than run. See
	// WorktreeMergeValidationDeferral.
	ValidationDeferral *WorktreeMergeValidationDeferral `json:"validation_deferral,omitempty"`
	ValidationIdentity *WorktreeMergeValidationIdentity `json:"validation_identity,omitempty"`
	ValidationTimeouts *WorktreeMergeValidationTimeouts `json:"validation_timeouts,omitempty"`
	// Findings are non-blocking observations recorded alongside an otherwise
	// successful outcome (sneat-dev/wb#591 round 3 red-team follow-up): they
	// never cause a refusal and never lengthen a wait. See
	// WorktreeMergeFinding and recordDeferredValidationCheckSkippedFinding.
	Findings       []WorktreeMergeFinding              `json:"findings,omitempty"`
	Checks         PullRequestWaitResult               `json:"checks,omitempty"`
	PushGate       *WorktreeMergePushGateReceipt       `json:"push_gate,omitempty"`
	ForwardRepairs []WorktreeMergeForwardRepairReceipt `json:"forward_repairs,omitempty"`
	Cleanup        bool                                `json:"cleanup_requested"`
	// AllowUnfenced is monotonic landing intent: an interrupted resume keeps
	// the explicit approval to rely on observed exact-head checks when the
	// target has no server-enforced strict up-to-date fence.
	AllowUnfenced      bool                                           `json:"allow_unfenced,omitempty"`
	OnFailure          string                                         `json:"on_failure,omitempty"`
	CleanupReports     []string                                       `json:"cleanup_reports,omitempty"`
	CleanedTasks       []string                                       `json:"cleaned_tasks,omitempty"`
	SourceRefreshes    []WorktreeMergeSourceRefresh                   `json:"source_refreshes,omitempty"`
	TargetRefreshes    []WorktreeMergeTargetRefresh                   `json:"target_refreshes,omitempty"`
	SourcePullRequests []WorktreeMergeSourcePullRequestReconciliation `json:"source_pull_requests,omitempty"`
	// AutoMergeArmed records that the PR route armed GitHub auto-merge for
	// this receipt's pull request. It says nothing about who performed the
	// merge: MergedBy records that when GitHub did.
	AutoMergeArmed bool `json:"auto_merge_armed,omitempty"`
	// MergedBy names who performed the merge when it was not this WB
	// invocation's own merge write — "github auto-merge" when armed
	// auto-merge landed it while WB was waiting, resuming, or absent.
	MergedBy string `json:"merged_by,omitempty"`
	// SupersededPullRequest names a prior candidate's own pull request that
	// a rebatch replacing it closed (red-team finding M6), so its armed
	// auto-merge could not land it alongside this replacement. Empty when
	// this candidate is not a rebatch replacement of a published-unlanded
	// original.
	SupersededPullRequest string `json:"superseded_pull_request,omitempty"`
	// RebatchOf binds this candidate to an immutable prepared receipt whose
	// source set was safely expanded. The old receipt is never rewritten.
	RebatchOf           string                   `json:"rebatch_of,omitempty"`
	RebatchedCandidates []WorktreeMergeCandidate `json:"rebatched_candidates,omitempty"`
	Failure             string                   `json:"failure,omitempty"`
	ResumeArgs          []string                 `json:"resume_args,omitempty"`
	// HostLoadAdmission records the most recent host-load admission check
	// that actually ran for this receipt's lane, so the override is provable
	// from the receipt rather than only from the caller's own log. Set by
	// cmd/wb before the orchestrate call whose validation the check gates.
	HostLoadAdmission *WorktreeMergeHostLoadAdmission `json:"host_load_admission,omitempty"`
	// LaneOwner is the landing-lane record this receipt's session acquired,
	// when the caller populated Lane (see LaneGuardRequest); nil when no
	// guard ran. Recording it on the receipt is what lets `--format json`
	// show a takeover's reason and prior owner, matching what `cmd/wb`'s help
	// and ai/skills/wb-merge/SKILL.md promise.
	LaneOwner   *landinglane.Record `json:"lane_owner,omitempty"`
	ReceiptPath string              `json:"receipt_path"`
	CreatedAt   time.Time           `json:"created_at"`
	UpdatedAt   time.Time           `json:"updated_at"`
}

func LandWorktreeMerge added in v0.66.0

func LandWorktreeMerge(ctx context.Context, options WorktreeMergeLandOptions) (WorktreeMergeReceipt, error)

LandWorktreeMerge resumes a prepared receipt from its first incomplete boundary. It never reconstructs identity from a branch name and never force-pushes either the candidate or target branch.

func PeekWorktreeMergeReceipt added in v0.118.0

func PeekWorktreeMergeReceipt(projectsRoot, input string) (WorktreeMergeReceipt, error)

PeekWorktreeMergeReceipt resolves input (a candidate worktree or a receipt path/task, per resolveWorktreeMergeReceiptPath) and reads the receipt it names, read-only. It exists so a landing guard — such as cmd/wb's host-load admission check — can inspect a receipt's exact state before deciding whether the step it gates will re-run local CPU-heavy validation, without duplicating receipt-resolution logic outside this package.

func PrepareWorktreeMerge added in v0.66.0

func PrepareWorktreeMerge(ctx context.Context, options WorktreeMergePrepareOptions) (preparedReceipt WorktreeMergeReceipt, prepareErr error)

func PrepareWorktreeMergeRevert added in v0.66.0

func PrepareWorktreeMergeRevert(ctx context.Context, projectsRoot, input string, timeout time.Duration, retry int) (WorktreeMergeReceipt, error)

PrepareWorktreeMergeRevert creates a fresh forward candidate which applies the inverse landing tree delta onto today's remote target. It never resets or force-pushes shared history.

func ResumeWorktreeMerge added in v0.66.0

func ResumeWorktreeMerge(ctx context.Context, options WorktreeMergeLandOptions) (WorktreeMergeReceipt, error)

func RunWorktreeMerge added in v0.66.0

type WorktreeMergeReceiptCollisionAcknowledgement added in v0.80.2

type WorktreeMergeReceiptCollisionAcknowledgement struct {
	SchemaVersion                               int                    `json:"schema_version"`
	ID                                          string                 `json:"id"`
	Status                                      string                 `json:"status"`
	ReceiptPath                                 string                 `json:"receipt_path"`
	AcknowledgementPath                         string                 `json:"acknowledgement_path"`
	ReceiptSHA256                               string                 `json:"receipt_sha256"`
	ImmutableClaimSHA256                        string                 `json:"immutable_claim_sha256"`
	ReceiptID                                   string                 `json:"receipt_id"`
	Lane                                        string                 `json:"lane"`
	Repository                                  string                 `json:"repository"`
	Target                                      string                 `json:"target"`
	ExpectedTargetSHA                           string                 `json:"expected_target_sha"`
	ExpectedCandidateSHA                        string                 `json:"expected_candidate_sha"`
	ExpectedCurrentSourceSHA                    string                 `json:"expected_current_source_sha"`
	ExpectedHistoricalRefreshSourceSHA          string                 `json:"expected_historical_refresh_source_sha"`
	ClaimBaseSHA                                string                 `json:"claim_base_sha"`
	Candidate                                   WorktreeMergeCandidate `json:"candidate"`
	CurrentSources                              []WorktreeMergeSource  `json:"current_sources"`
	HistoricalRefreshSources                    []WorktreeMergeSource  `json:"historical_refresh_sources"`
	HistoricalValidationFailedOperatorAssertion bool                   `json:"historical_validation_failed_operator_assertion"`
	Actor                                       string                 `json:"actor"`
	Reason                                      string                 `json:"reason"`
	RecordedAt                                  time.Time              `json:"recorded_at"`
}

WorktreeMergeReceiptCollisionAcknowledgement is the narrowly scoped, append-only recovery record for a receipt that was historically rewritten by the pre-guard prepare collision. Historical validation_failed is an operator assertion here: no byte digest of the pre-mutation receipt exists.

func AcknowledgeWorktreeMergeReceiptCollision added in v0.80.2

AcknowledgeWorktreeMergeReceiptCollision records the one audited recovery path for a known historical receipt collision. It never infers the incident: the caller must pin every observed digest and revision before --apply can write the separate acknowledgement.

type WorktreeMergeReceiptCollisionAcknowledgementOptions added in v0.80.2

type WorktreeMergeReceiptCollisionAcknowledgementOptions struct {
	ProjectsRoot, Receipt, ExpectedReceiptSHA256, ExpectedImmutableClaimSHA256                            string
	ExpectedTargetSHA, ExpectedCandidateSHA, ExpectedCurrentSourceSHA, ExpectedHistoricalRefreshSourceSHA string
	Apply                                                                                                 bool
	Actor, Reason                                                                                         string
}

type WorktreeMergeRetiredPrepareCandidateAcknowledgementOptions added in v0.154.0

type WorktreeMergeRetiredPrepareCandidateAcknowledgementOptions struct {
	ProjectsRoot, Receipt, Actor, Reason string
	Apply                                bool
}

type WorktreeMergeRetiredPublicationAcknowledgement added in v0.119.0

type WorktreeMergeRetiredPublicationAcknowledgement struct {
	SchemaVersion         int                   `json:"schema_version"`
	ID                    string                `json:"id"`
	Status                string                `json:"status"`
	ReceiptPath           string                `json:"receipt_path"`
	AcknowledgementPath   string                `json:"acknowledgement_path"`
	ReceiptID             string                `json:"receipt_id"`
	ReceiptSHA256         string                `json:"receipt_sha256"`
	ReceiptPhase          WorktreeMergePhase    `json:"receipt_phase"`
	ReceiptStatus         WorktreeMergeStatus   `json:"receipt_status"`
	Lane                  string                `json:"lane"`
	Repository            string                `json:"repository"`
	Target                string                `json:"target"`
	ReceiptTargetSHA      string                `json:"receipt_target_sha"`
	CurrentTargetSHA      string                `json:"current_target_sha"`
	CandidateTask         string                `json:"candidate_task"`
	CandidateWorktree     string                `json:"candidate_worktree"`
	CandidateBranch       string                `json:"candidate_branch"`
	CandidateSHA          string                `json:"candidate_sha,omitempty"`
	PublishedCandidateSHA string                `json:"published_candidate_sha,omitempty"`
	PullRequest           string                `json:"pull_request"`
	PullRequestState      string                `json:"pull_request_state"`
	PullRequestClosedAt   string                `json:"pull_request_closed_at,omitempty"`
	PullRequestHeadSHA    string                `json:"pull_request_head_sha"`
	Sources               []WorktreeMergeSource `json:"sources"`
	Actor                 string                `json:"actor"`
	Reason                string                `json:"reason"`
	RecordedAt            time.Time             `json:"recorded_at"`
}

WorktreeMergeRetiredPublicationAcknowledgement is a separate, append-only acknowledgement for a prepare- or land-phase conflict/validation_failed/ checks_failed receipt whose exact published pull request was later retired -- closed without ever merging, and its remote candidate branch deleted -- after the receipted target advanced past the candidate, leaving WB's own "refusing to rewrite the published branch without force-push" refusal (or an equivalent validation/checks failure) as a dead end: resume repeats the same refusal forever, and every other conflict-recovery verb requires an unpublished receipt. It proves, using a fresh read of GitHub's own PR state and a freshly fetched current remote target, that the publication is genuinely gone and nothing from it landed, then records a separate audited acknowledgement so a fresh candidate can own the lane. It never rewrites the historical receipt or any Work Log, and never deletes the preserved candidate worktree.

func AcknowledgeRetiredPublication added in v0.119.0

AcknowledgeRetiredPublication proves, from a fresh read of GitHub's own pull-request state and a freshly fetched current remote target, that a conflict/validation_failed/checks_failed receipt's exact published pull request was closed without ever merging, that its candidate branch carries no remote ref any more, and that neither its published candidate nor its preserved candidate landed on the target -- then records a separate audited acknowledgement so a fresh candidate can own the merger lane. It never rewrites the historical receipt or any Work Log, and never deletes the preserved candidate worktree (which this proof requires, for read-only git object resolution against the candidate's own remote). This is a dry-run by default; --apply requires --actor and --reason and writes only the new acknowledgement artifact.

type WorktreeMergeRetiredPublicationAcknowledgementOptions added in v0.119.0

type WorktreeMergeRetiredPublicationAcknowledgementOptions struct {
	ProjectsRoot string
	Receipt      string
	Apply        bool
	Actor        string
	Reason       string
}

WorktreeMergeRetiredPublicationAcknowledgementOptions configures AcknowledgeRetiredPublication.

type WorktreeMergeRevertReceipt added in v0.66.0

type WorktreeMergeRevertReceipt struct {
	PreviousTargetSHA string `json:"previous_target_sha"`
	LandingSHA        string `json:"landing_sha"`
	CandidateSHA      string `json:"candidate_sha"`
}

type WorktreeMergeRoute added in v0.66.0

type WorktreeMergeRoute string
const (
	WorktreeMergeRouteAuto        WorktreeMergeRoute = "auto"
	WorktreeMergeRouteDirect      WorktreeMergeRoute = "direct"
	WorktreeMergeRoutePullRequest WorktreeMergeRoute = "pr"
	WorktreeMergeRouteUnsupported WorktreeMergeRoute = "unsupported"
)

type WorktreeMergeRouteDecision added in v0.66.0

type WorktreeMergeRouteDecision struct {
	Requested WorktreeMergeRoute `json:"requested"`
	Route     WorktreeMergeRoute `json:"route"`
	Reason    string             `json:"reason"`
}

func ResolveWorktreeMergeRoute added in v0.66.0

func ResolveWorktreeMergeRoute(ctx context.Context, repository, target string, requested WorktreeMergeRoute) (WorktreeMergeRouteDecision, error)

type WorktreeMergeSelfSupersessionCorrection added in v0.80.2

type WorktreeMergeSelfSupersessionCorrection struct {
	SchemaVersion           int                    `json:"schema_version"`
	ID                      string                 `json:"id"`
	Status                  string                 `json:"status"`
	CorrectionPath          string                 `json:"correction_path"`
	ReceiptPath             string                 `json:"receipt_path"`
	ReceiptSHA256           string                 `json:"receipt_sha256"`
	ImmutableClaimSHA256    string                 `json:"immutable_claim_sha256"`
	SupersessionPath        string                 `json:"supersession_path"`
	SupersessionSHA256      string                 `json:"supersession_sha256"`
	SupersessionID          string                 `json:"supersession_id"`
	OriginalCandidate       WorktreeMergeCandidate `json:"original_candidate"`
	OriginalClaimBaseSHA    string                 `json:"original_claim_base_sha"`
	CorrectedReplacement    WorktreeMergeCandidate `json:"corrected_replacement"`
	ReplacementClaimBaseSHA string                 `json:"replacement_claim_base_sha"`
	CurrentTargetSHA        string                 `json:"current_target_sha"`
	Sources                 []WorktreeMergeSource  `json:"sources"`
	Actor                   string                 `json:"actor"`
	Reason                  string                 `json:"reason"`
	RecordedAt              time.Time              `json:"recorded_at"`
}

WorktreeMergeSelfSupersessionCorrection is the only repair for a historical supersession acknowledgement that incorrectly named the failed candidate as its own replacement. It is append-only and binds both the exact corrupt ack bytes and one distinct, fully revalidated replacement candidate.

func CorrectValidationFailedSelfSupersession added in v0.80.2

CorrectValidationFailedSelfSupersession repairs only a pre-guard self-supersession. It never replaces that acknowledgement: it records one separate correction whose identity pins the exact existing acknowledgement, receipt, immutable claim, and distinct replacement evidence.

type WorktreeMergeSelfSupersessionCorrectionOptions added in v0.80.2

type WorktreeMergeSelfSupersessionCorrectionOptions struct {
	ProjectsRoot, Receipt, ReplacementWorktree, ExpectedSupersessionSHA256, ExpectedImmutableClaimSHA256 string
	Apply                                                                                                bool
	Actor, Reason                                                                                        string
}

type WorktreeMergeSource added in v0.66.0

type WorktreeMergeSource struct {
	Task     string `json:"task"`
	Worktree string `json:"worktree"`
	Branch   string `json:"branch"`
	SHA      string `json:"sha"`
	Merged   bool   `json:"merged"`
}

type WorktreeMergeSourcePullRequestReconciliation added in v0.111.0

type WorktreeMergeSourcePullRequestReconciliation struct {
	Number       int       `json:"number"`
	URL          string    `json:"url"`
	SourceSHA    string    `json:"source_sha"`
	ObservedSHA  string    `json:"observed_sha,omitempty"`
	ObservedBase string    `json:"observed_base,omitempty"`
	Outcome      string    `json:"outcome"`
	Commented    bool      `json:"commented,omitempty"`
	Closed       bool      `json:"closed,omitempty"`
	Reason       string    `json:"reason,omitempty"`
	UpdatedAt    time.Time `json:"updated_at"`
}

type WorktreeMergeSourceRefresh added in v0.66.0

type WorktreeMergeSourceRefresh struct {
	RecordedAt time.Time             `json:"recorded_at"`
	Sources    []WorktreeMergeSource `json:"sources"`
}

type WorktreeMergeStatus added in v0.66.0

type WorktreeMergeStatus string
const (
	WorktreeMergePreparing        WorktreeMergeStatus = "preparing"
	WorktreeMergePrepared         WorktreeMergeStatus = "prepared"
	WorktreeMergeConflict         WorktreeMergeStatus = "conflict"
	WorktreeMergeValidationFailed WorktreeMergeStatus = "validation_failed"
	// WorktreeMergePublished is an intentional non-terminal handoff point: the
	// exact validated candidate is remotely published in an open pull request,
	// but WB has not observed checks or attempted a merge. A later ordinary
	// resume continues the landing journey from this exact receipt.
	WorktreeMergePublished            WorktreeMergeStatus = "published_merge_pending"
	WorktreeMergeChecksPending        WorktreeMergeStatus = "checks_pending"
	WorktreeMergeChecksFailed         WorktreeMergeStatus = "checks_failed"
	WorktreeMergeLanded               WorktreeMergeStatus = "landed_cleanup_pending"
	WorktreeMergeCanonicalSyncBlocked WorktreeMergeStatus = "landed_canonical_sync_blocked"
	WorktreeMergePostTargetCIFailed   WorktreeMergeStatus = "landed_post_target_ci_failed"
	WorktreeMergeComplete             WorktreeMergeStatus = "complete"
)

type WorktreeMergeStrandedLandingAcknowledgement added in v0.79.0

type WorktreeMergeStrandedLandingAcknowledgement struct {
	SchemaVersion       int                 `json:"schema_version"`
	ID                  string              `json:"id"`
	Status              string              `json:"status"`
	ReceiptPath         string              `json:"receipt_path"`
	AcknowledgementPath string              `json:"acknowledgement_path"`
	ReceiptID           string              `json:"receipt_id"`
	ReceiptSHA256       string              `json:"receipt_sha256"`
	ReceiptStatus       WorktreeMergeStatus `json:"receipt_status"`
	Lane                string              `json:"lane"`
	Repository          string              `json:"repository"`
	Target              string              `json:"target"`
	ReceiptTargetSHA    string              `json:"receipt_target_sha"`
	CandidateSHA        string              `json:"candidate_sha"`
	PullRequest         string              `json:"pull_request"`
	// PullRequestHeadSHA records a strict descendant of CandidateSHA that
	// GitHub reports as the merged pull request head. It is empty when the PR
	// head still exactly matches CandidateSHA. A descendant is accepted only
	// when GitHub proves both its ancestry and its containment in the current
	// remote target.
	PullRequestHeadSHA string `json:"pull_request_head_sha,omitempty"`
	// ProvedLandingSHA is GitHub's own server merge-result commit for
	// PullRequest, discovered live by this acknowledgement. The historical
	// receipt's own LandingSHA is deliberately left empty by this recovery
	// path: the tool itself never observed the landing at the time, and this
	// field records the later, separately audited proof instead.
	ProvedLandingSHA string `json:"proved_landing_sha"`
	CurrentTargetSHA string `json:"current_target_sha"`
	// CandidateLanding names which proof established that the receipted
	// candidate is contained in CurrentTargetSHA: "ancestor" for a plain
	// merge (direct git ancestry) or "tree-identical" for a squash merge (or
	// a single-commit rebase merge), where the candidate can never be an
	// ancestor because the merge rewrites it but the server merge commit's
	// tree matches the candidate's own tree exactly.
	CandidateLanding string `json:"candidate_landing"`
	// CandidateLandingTreeSHA is the tree SHA the two commits were proved to
	// share, set only when CandidateLanding is "tree-identical".
	CandidateLandingTreeSHA string                `json:"candidate_landing_tree_sha,omitempty"`
	Sources                 []WorktreeMergeSource `json:"sources"`
	Actor                   string                `json:"actor"`
	Reason                  string                `json:"reason"`
	RecordedAt              time.Time             `json:"recorded_at"`
}

WorktreeMergeStrandedLandingAcknowledgement is a separate, append-only acknowledgement for a land/conflict receipt whose published pull request is proved MERGED at the receipted candidate or a strict descendant, and whose merge commit, observed head, and preserved candidate are all proved contained in the freshly fetched current remote target, using only GitHub's remote state. This receipt shape exists precisely because the candidate (and every receipted source) worktree is already gone -- typically because a resume's landing-result read failed on pure I/O after infrastructure cleanup raced ahead of it -- so no local git ancestry check against that worktree is possible. The historical merge receipt and every Work Log remain untouched; this acknowledgement only frees the merger lane for the next candidate.

func AcknowledgeStrandedPullRequestLanding added in v0.79.0

AcknowledgeStrandedPullRequestLanding proves, using only GitHub's remote state, that a land/conflict receipt's published candidate, or a strict descendant retaining it, merged and remains reachable from the current remote target, then records a separate audited acknowledgement so a fresh forward candidate can own the lane. It never reads or requires the candidate or any receipted source worktree: that infrastructure being gone is exactly the failure this recovers from. It never rewrites the historical receipt or any Work Log. This is a dry-run by default; --apply requires --actor and --reason.

type WorktreeMergeStrandedLandingAcknowledgementOptions added in v0.79.0

type WorktreeMergeStrandedLandingAcknowledgementOptions struct {
	ProjectsRoot string
	Receipt      string
	Apply        bool
	Actor        string
	Reason       string
}

WorktreeMergeStrandedLandingAcknowledgementOptions configures AcknowledgeStrandedPullRequestLanding.

type WorktreeMergeTargetRefresh added in v0.120.0

type WorktreeMergeTargetRefresh struct {
	RecordedAt           time.Time `json:"recorded_at"`
	PreviousTargetSHA    string    `json:"previous_target_sha"`
	NewTargetSHA         string    `json:"new_target_sha"`
	PreviousCandidateSHA string    `json:"previous_candidate_sha"`
	NewCandidateSHA      string    `json:"new_candidate_sha"`
}

WorktreeMergeTargetRefresh records one occasion where the target branch advanced past a receipt's recorded target SHA while its candidate was already published (an open pull request), and WB refreshed the candidate in place by merging the new target into it rather than refusing to rewrite the published branch. See refreshPublishedWorktreeMergeCandidateTarget.

type WorktreeMergeUnpublishedValidationFailureAcknowledgement added in v0.120.3

type WorktreeMergeUnpublishedValidationFailureAcknowledgement struct {
	SchemaVersion           int                    `json:"schema_version"`
	ID                      string                 `json:"id"`
	Status                  string                 `json:"status"`
	ReceiptPath             string                 `json:"receipt_path"`
	AcknowledgementPath     string                 `json:"acknowledgement_path"`
	ReceiptID               string                 `json:"receipt_id"`
	ReceiptSHA256           string                 `json:"receipt_sha256"`
	Lane                    string                 `json:"lane"`
	Repository              string                 `json:"repository"`
	Target                  string                 `json:"target"`
	ReceiptTargetSHA        string                 `json:"receipt_target_sha"`
	CurrentTargetSHA        string                 `json:"current_target_sha"`
	Candidate               WorktreeMergeCandidate `json:"candidate"`
	CandidateCleanupBacklog string                 `json:"candidate_cleanup_backlog,omitempty"`
	Sources                 []WorktreeMergeSource  `json:"sources"`
	PreservedSources        []WorktreeMergeSource  `json:"preserved_sources,omitempty"`
	Actor                   string                 `json:"actor"`
	Reason                  string                 `json:"reason"`
	RecordedAt              time.Time              `json:"recorded_at"`
}

WorktreeMergeUnpublishedValidationFailureAcknowledgement retires one unpublished prepare attempt without discarding its source work. The historical receipt and Work Logs remain immutable; the sidecar records fresh proof that the candidate never reached the remote target and that every exact receipted source remains clean and actively claimed. An interrupted preparing attempt is accepted only when WB's private cleanup backlog proves its exact candidate was deliberately discarded.

func AcknowledgeUnpublishedValidationFailure added in v0.120.3

AcknowledgeUnpublishedValidationFailure proves that a prepare-time validation failure was never published or landed and that all receipted source work is still preserved. It is a dry-run unless Apply is true.

type WorktreeMergeUnpublishedValidationFailureAcknowledgementOptions added in v0.120.3

type WorktreeMergeUnpublishedValidationFailureAcknowledgementOptions struct {
	ProjectsRoot string
	Receipt      string
	Apply        bool
	Actor        string
	Reason       string
}

type WorktreeMergeValidationDeferral added in v0.146.0

type WorktreeMergeValidationDeferral struct {
	Route        WorktreeMergeRoute `json:"route"`
	CandidateSHA string             `json:"candidate_sha"`
	Reason       string             `json:"reason"`
	RecordedAt   time.Time          `json:"recorded_at"`
}

WorktreeMergeValidationDeferral records that local candidate validation was deferred rather than run, because this call resolved the pull-request route and the target's required-check policy was read authoritatively, is non-empty, and is fenced by a server-enforced strict up-to-date policy (see worktreeMergeValidationDeferralEligible). CandidateSHA pins the deferral to the exact candidate it was recorded for: an advance past this SHA (a further commit, a rebase, a target refresh) makes the deferral stale, and every guard that consults it (requireWorktreeMergePublishedValidation, preparedValidationStillValid, hostLoadCheckSkippable) requires an exact match. See sneat-dev/wb#591.

type WorktreeMergeValidationFailureSeal added in v0.70.0

type WorktreeMergeValidationFailureSeal struct {
	Status           string                                   `json:"status"`
	ReceiptPath      string                                   `json:"receipt_path"`
	ReceiptID        string                                   `json:"receipt_id"`
	ReceiptSHA256    string                                   `json:"receipt_sha256"`
	Repository       string                                   `json:"repository"`
	Target           string                                   `json:"target"`
	CurrentTargetSHA string                                   `json:"current_target_sha"`
	TargetTreeSHA    string                                   `json:"target_tree_sha"`
	RequiredRoots    []WorktreeMergeValidationFailureSealRoot `json:"required_roots"`
	Candidate        WorktreeMergeCandidate                   `json:"candidate"`
	Actor            string                                   `json:"actor"`
	Reason           string                                   `json:"reason"`
}

WorktreeMergeValidationFailureSeal is the result of preparing a clean, target-tree-identical replacement candidate. It is not a supersession acknowledgement; the historical receipt remains active until the caller separately records one with supersede-validation-failed.

func PrepareValidationFailedWorktreeMergeSeal added in v0.70.0

func PrepareValidationFailedWorktreeMergeSeal(ctx context.Context, options WorktreeMergeValidationFailureSealOptions) (WorktreeMergeValidationFailureSeal, error)

PrepareValidationFailedWorktreeMergeSeal creates a WB-managed candidate at the freshly fetched target and, when necessary, adds only merge ancestry via Git's ours strategy. It refuses unless the candidate's final tree is exactly the fetched target tree and every approved immutable root is an ancestor. It never edits the historical merge receipt or any existing Work Log.

type WorktreeMergeValidationFailureSealOptions added in v0.70.0

type WorktreeMergeValidationFailureSealOptions struct {
	ProjectsRoot    string
	Receipt         string
	Apply           bool
	Actor           string
	Reason          string
	Model           string
	AgentRuntime    string
	AgentID         string
	Initiator       string
	CLI             string
	Provider        string
	SessionRequired bool
	Timeout         time.Duration
	Retry           int
}

type WorktreeMergeValidationFailureSealRoot added in v0.70.0

type WorktreeMergeValidationFailureSealRoot struct {
	Kind string `json:"kind"`
	SHA  string `json:"sha"`
}

WorktreeMergeValidationFailureSealRoot names one immutable history root that a no-content recovery candidate must contain.

type WorktreeMergeValidationFailureSupersession added in v0.69.3

type WorktreeMergeValidationFailureSupersession struct {
	SchemaVersion                  int                    `json:"schema_version"`
	ID                             string                 `json:"id"`
	Status                         string                 `json:"status"`
	ReceiptPath                    string                 `json:"receipt_path"`
	AcknowledgementPath            string                 `json:"acknowledgement_path"`
	ReceiptID                      string                 `json:"receipt_id"`
	ReceiptSHA256                  string                 `json:"receipt_sha256"`
	ReceiptStatus                  WorktreeMergeStatus    `json:"receipt_status"`
	Lane                           string                 `json:"lane"`
	Repository                     string                 `json:"repository"`
	Target                         string                 `json:"target"`
	ReceiptTargetSHA               string                 `json:"receipt_target_sha"`
	CurrentTargetSHA               string                 `json:"current_target_sha"`
	OriginalCandidate              WorktreeMergeCandidate `json:"original_candidate"`
	ObservedCandidateDescendantSHA string                 `json:"observed_candidate_descendant_sha,omitempty"`
	OriginalClaimBaseSHA           string                 `json:"original_claim_base_sha"`
	Replacement                    WorktreeMergeCandidate `json:"replacement"`
	ReplacementClaimBaseSHA        string                 `json:"replacement_claim_base_sha"`
	Sources                        []WorktreeMergeSource  `json:"sources"`
	Actor                          string                 `json:"actor"`
	Reason                         string                 `json:"reason"`
	RecordedAt                     time.Time              `json:"recorded_at"`
}

WorktreeMergeValidationFailureSupersession is a separate, append-only transition for a failed prepare candidate that never landed. It binds the immutable failed receipt to one clean replacement candidate without changing either candidate's Work Log or the historical receipt.

func SupersedeValidationFailedWorktreeMerge added in v0.69.3

SupersedeValidationFailedWorktreeMerge proves that a clean replacement candidate contains every immutable root of an unlanded prepare failure. The failed candidate itself need not be an ancestor: it may have diverged after validation failed. This transition is deliberately narrower than the landed acknowledgement because it never asserts that the failed candidate landed.

type WorktreeMergeValidationFailureSupersessionOptions added in v0.69.3

type WorktreeMergeValidationFailureSupersessionOptions struct {
	ProjectsRoot        string
	Receipt             string
	ReplacementWorktree string
	Apply               bool
	Actor               string
	Reason              string
}

type WorktreeMergeValidationIdentity added in v0.98.3

type WorktreeMergeValidationIdentity struct {
	CandidateSHA     string            `json:"candidate_sha"`
	TargetSHA        string            `json:"target_sha"`
	SourceSHAs       []string          `json:"source_shas"`
	QualityPolicySHA string            `json:"quality_policy_sha"`
	WBBuild          string            `json:"wb_build"`
	WBExecutableSHA  string            `json:"wb_executable_sha"`
	Validators       map[string]string `json:"validators,omitempty"`
}

WorktreeMergeValidationIdentity binds a successful prepare validation to every cheap input that can make rerunning it produce a different result. Missing identity is deliberately treated as a cache miss for old receipts.

type WorktreeMergeValidationTimeouts added in v0.99.0

type WorktreeMergeValidationTimeouts struct {
	Check        time.Duration `json:"check_timeout,omitempty"`
	ShardAttempt time.Duration `json:"shard_attempt_timeout,omitempty"`
}

WorktreeMergeValidationTimeouts retains explicit validation limits on a prepared candidate so a later land/resume repeats the same validation policy. The overall prepare deadline is intentionally not retained: it bounds one caller's operation rather than the candidate's validation contract.

Jump to

Keyboard shortcuts

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