mergeack

package
v0.164.1 Latest Latest
Warning

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

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

Documentation

Overview

Package mergeack owns the on-disk shape, identity hash, and full validation of a worktree-merge absorbed-conflict acknowledgement -- the sidecar `wb worktree merge acknowledge-absorbed-conflict` writes next to a worktree-merge receipt whose every receipted source worktree is gone, once each source's content is proved already reachable from the current remote target.

It is a leaf package: it imports nothing from internal/orchestrate or internal/worktrees, so both can depend on it without cycling. Both callers used to carry their own copy of this logic -- internal/orchestrate wrote and richly validated it, internal/worktrees re-validated a much smaller subset when deciding whether to accept an acknowledgement as cleanup landing proof -- which let a hand-edited sidecar with fabricated or emptied proofs slip past the weaker copy. This package is now the single source of truth for both the format and the validation, so a real sidecar written by internal/orchestrate validates byte-identically here, and a tampered one is rejected the same way regardless of which caller reads it.

Index

Constants

View Source
const FileSuffix = ".absorbed-conflict.ack.json"

FileSuffix names the sidecar file relative to its receipt's own path: `<receipt path><FileSuffix>`.

View Source
const SchemaVersion = 1

SchemaVersion is the current on-disk schema for Acknowledgement. There is exactly one definition of it in the whole module -- here -- so it can never drift between callers.

View Source
const Status = "absorbed_conflict_acknowledged"

Status is the only status value a valid Acknowledgement carries.

Variables

This section is empty.

Functions

func ComputeID

func ComputeID(ack Acknowledgement) string

ComputeID hashes every recorded field of ack (excluding RecordedAt, which carries no evidentiary weight) into its content-addressed ID, including Actor and Reason -- a tamper that only rewrites who acknowledged it, or why, must still invalidate the ID. Same, below, compares evidence without Actor/Reason/ID/RecordedAt instead, so a genuine retry that repeats the same receipt, target, and per-source proof is still recognized as the same acknowledgement even when the operator supplies a different actor or reason on the replay.

func FileSHA256

func FileSHA256(path string) (string, error)

FileSHA256 hashes path's current bytes -- the same plain SHA-256 an Acknowledgement's ReceiptSHA256 binds to its receipt's exact, unchanged bytes.

func IsDerivedPathAllowed

func IsDerivedPathAllowed(path string) bool

IsDerivedPathAllowed narrowly allows the one derived-index shape this recovery may excuse: a generated spec/**/README.md listing index, exactly "README.md" nested anywhere under a repo-root "spec/" directory. Chosen over "any README.md the receipt's sources changed" because that would let an operator excuse an unrelated hand-authored README.md merely for appearing in this receipt; this shape check binds to the generated-index location instead, regardless of receipt contents.

func Path

func Path(receiptPath string) string

Path names the acknowledgement sidecar for a receipt at receiptPath.

func Persist

func Persist(path string, ack Acknowledgement) error

Persist atomically writes ack to path (create-temp, fsync, rename), the same durability shape every other WB receipt/sidecar uses.

func Same

func Same(left, right Acknowledgement) bool

Same compares only the immutable proof evidence two acknowledgements carry, deliberately excluding Actor/Reason/ID/RecordedAt: a retry that repeats the same receipt, target, and per-source proof is idempotent even when the operator supplies a different actor or reason on the replay.

func SameSources

func SameSources(left, right []Source) bool

SameSources reports whether left and right name the same sources, in the same order, by task/worktree/branch/SHA.

Types

type Acknowledgement

type Acknowledgement 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       string        `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"`
	Sources             []Source      `json:"sources"`
	SourceProofs        []SourceProof `json:"source_proofs"`
	ExcusedDerivedPaths []string      `json:"excused_derived_paths,omitempty"`
	Actor               string        `json:"actor"`
	Reason              string        `json:"reason"`
	RecordedAt          time.Time     `json:"recorded_at"`
}

Acknowledgement is a separate, append-only acknowledgement for an unpublished prepare-phase conflict receipt whose every receipted source worktree is already gone from disk, yet whose receipted changes are proved, source by source, to already be reachable from the freshly fetched current remote target either by graph ancestry or a recorded per-path absorption proof. ReceiptStatus is carried as a plain string so this leaf package never needs internal/orchestrate's WorktreeMergeStatus type; callers convert at the boundary.

func Load

func Load(path string, receipt ReceiptIdentity) (Acknowledgement, error)

Load reads and fully validates the acknowledgement sidecar at path against receipt: every immutable identity field, the SHA-256 binding to receipt's exact current bytes, every source proof's identity and method, every excused derived path's shape and use, and the recomputed content hash ID. A sidecar that fails any of these checks -- including one that is merely missing -- returns a non-nil error; the two are distinguished with os.IsNotExist, exactly as os.ReadFile itself would report.

type PathProof

type PathProof struct {
	Path         string `json:"path"`
	Method       string `json:"method"`
	AddedLines   int    `json:"added_lines,omitempty"`
	MatchedLines int    `json:"matched_lines,omitempty"`
}

PathProof records how one path a source changed relative to its merge-base with the current target was proved absorbed: "blob_absorbed" (identical git blob on the target), "lines_absorbed" (every line the source added to a `*.jsonl` append-only ledger, relative to the merge-base, is present verbatim as a line in the target's copy -- AddedLines and MatchedLines record the counts), "go_dependency_upgrade" (a jointly verified, monotonic root go.mod/go.sum dependency upgrade), or "derived_excused" (an operator-audited `--derived-path` exclusion for a known generated-index shape).

type ReceiptIdentity

type ReceiptIdentity struct {
	Path       string
	ID         string
	Status     string
	Lane       string
	Repository string
	Target     string
	TargetSHA  string
	Candidate  Source
	Sources    []Source
}

ReceiptIdentity is the subset of an internal/orchestrate.WorktreeMergeReceipt that Load validates an Acknowledgement against. Path is the receipt's own file path, used both as the immutable ReceiptPath identity and to compute ReceiptSHA256 fresh from the receipt's current bytes.

type Source

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

Source identifies one worktree-merge source or candidate by its immutable task, worktree, branch, and commit SHA. It mirrors the fields internal/orchestrate.WorktreeMergeSource and WorktreeMergeCandidate carry in common, so a slice of either converts to []Source with a plain type conversion.

type SourceProof

type SourceProof struct {
	Task         string      `json:"task"`
	Worktree     string      `json:"worktree"`
	Branch       string      `json:"branch"`
	SHA          string      `json:"sha"`
	Method       string      `json:"method"`
	MergeBaseSHA string      `json:"merge_base_sha,omitempty"`
	PathCount    int         `json:"path_count,omitempty"`
	PathProofs   []PathProof `json:"path_proofs,omitempty"`
}

SourceProof records, for one receipted source, how its content was proved already reachable from the current remote target: either the receipted source SHA is a direct ancestor of the freshly fetched target head ("ancestor"), or every path it changed relative to its merge-base with the target is individually proved absorbed ("content_absorbed"). MergeBaseSHA and PathCount are populated only for the content_absorbed method; PathProofs details how each changed path was individually proved.

Jump to

Keyboard shortcuts

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