commit

package
v0.24.1 Latest Latest
Warning

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

Go to latest
Published: Jul 31, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package commit owns commit mutation, preparation, release, and verification workflows. Analysis models remain in the public repomap package.

Index

Constants

View Source
const (
	PrepStatusReady         = "ready"
	PrepStatusNeedsJudgment = "needs_judgment"
	PrepStatusAbort         = "abort"
)
View Source
const (
	ActionFix     = repomap.ActionFix
	ActionSafe    = repomap.ActionSafe
	ActionReview  = repomap.ActionReview
	VerdictSafe   = repomap.VerdictSafe
	VerdictUnsafe = repomap.VerdictUnsafe
)

Variables

This section is empty.

Functions

func ApplyReviewDecisions

func ApplyReviewDecisions(ctx context.Context, repoRoot string, decisions []ReviewDecision, findings []Finding) error

ApplyReviewDecisions applies LLM-adjudicated verdicts to REVIEW findings. For verdict="unsafe": applies the decision's Replacement at the finding's file+line. For verdict="safe": no-op (finding is cleared without edit). Fail-closed: every edit is verified against current file content BEFORE any file is written — the target line must still contain the finding's snippet (or already equal the replacement, for idempotent retries). A stale finding aborts with zero files mutated. Callers should validate decisions first.

func BuildPrepStateBinding

func BuildPrepStateBinding(ctx context.Context, repoRoot string, groups []CommitGroup) (string, map[string]string, error)

BuildPrepStateBinding captures the git state a plan was computed against: the HEAD SHA plus a sha256 of each planned file's current worktree content. Planned files absent from the worktree (deletions) record the sentinel "absent". commit finish refuses to execute when either no longer matches.

func ContainsLowConf

func ContainsLowConf(lowConf []PrepLowConf, groupID string) bool

ContainsLowConf reports whether lowConf already contains an entry for groupID. Prevents double-adding a group that was already flagged by the confidence pass.

func DeletePrepState

func DeletePrepState(token string) error

DeletePrepState removes the state file for a token. Missing file is not an error — deletion is idempotent.

func DetectJustfileRelease

func DetectJustfileRelease(repoRoot string) bool

DetectJustfileRelease returns true when a Justfile with a `release` recipe exists.

func DetectSessionRepos

func DetectSessionRepos(repoRoot string) []string

DetectSessionRepos returns repos likely touched in this session. Checks known companion repos; always includes repoRoot itself.

func EncodeExecuteResult

func EncodeExecuteResult(r *ExecuteResult, pretty bool) ([]byte, error)

EncodeExecuteResult serializes an ExecuteResult for stdout.

func ExecExitCode

func ExecExitCode(err error) int

ExecExitCode extracts the exit code from an execError, defaulting to 1. Uses errors.As so wrapped execError values (e.g. fmt.Errorf("...: %w", err)) still return the embedded code.

func IsKitchenSink

func IsKitchenSink(g *CommitGroup) bool

IsKitchenSink returns true when a CommitGroup looks like an accidental fusion that should be forced to LLM judgment. Triggers:

  1. Group has more than 10 files.
  2. Group spans more than one distinct top-level plugin segment (e.g. plugins/dc/... and plugins/pi/... in the same group).
  3. Group contains a plugin.json path — signals a new plugin being added.

func LoadDiffSlice

func LoadDiffSlice(diffsPath string, g CommitGroup, maxChars int) string

LoadDiffSlice extracts the diff slice for a group's files, capped at maxChars.

func ModeHint

func ModeHint(p PrepPreflight) string

ModeHint derives FULL/LOCAL from preflight signals.

FULL  = remote present AND gh auth logged in → push + tag
LOCAL = anything else                        → no push, no tag

Permissive on auth string format: any "logged in" substring is truthy unless the line also says "not logged in" (gh's own negative phrasing).

func PersistPrepState

func PersistPrepState(state *PrepState) (string, error)

PersistPrepState writes state to tmpdir and returns the prep_token.

func PersistPrepStateAt

func PersistPrepStateAt(token string, state *PrepState) error

PersistPrepStateAt rewrites the state file for an existing token in place. Used by commit finish after review decisions mutate planned files, so a retry of the same token still passes freshness verification.

func Polish

func Polish(g CommitGroup) (subject string, confidence float64)

Polish generates a heuristic commit subject for a CommitGroup. Returns (subject, confidence) where confidence >= 0.6 is safe to use without LLM review. Below 0.6, callers should mark the group for LLM polish.

Algorithm: classify files by extension family and directory prefix, then combine with a diff-stat action verb to produce a templated subject. Specificity rules:

  • test files → type=test, confidence 0.7
  • single file, known family → confidence depends on scope clarity
  • mixed / unknown → chore fallback at 0.3

func PolishGroup

func PolishGroup(g *CommitGroup, threshold float64) bool

PolishGroup is a convenience wrapper that applies Polish and updates g.SuggestedMsg when the result meets the confidence threshold. Returns whether the message was updated.

func ReviewFindingCount

func ReviewFindingCount(findings []Finding) int

ReviewFindingCount returns the number of findings requiring judgment.

func StashArtifacts

func StashArtifacts(repoRoot string, artifacts []string)

StashArtifacts adds artifact paths to .gitignore and unstages them. Best-effort: I/O failures are swallowed (artifacts are advisory).

func StashArtifactsContext

func StashArtifactsContext(ctx context.Context, repoRoot string, artifacts []string)

StashArtifactsContext is StashArtifacts with caller cancellation.

func ValidateConventionalMsg

func ValidateConventionalMsg(msg string) error

ValidateConventionalMsg returns an error if the first line of msg does not match conventionalSubjectRe. Only the first line is validated; a multi-line body is allowed.

func ValidateReviewDecisions

func ValidateReviewDecisions(findings []Finding, decisions []ReviewDecision) error

ValidateReviewDecisions verifies that all REVIEW findings have one explicit verdict before commit finish executes the prepared plan.

func ValidateTag

func ValidateTag(tag string) error

ValidateTag returns an error if tag does not match the semver format.

func VerifyPrepStateFresh

func VerifyPrepStateFresh(ctx context.Context, state *PrepState) error

VerifyPrepStateFresh checks that the repo still matches the state captured at prep time. Fail-closed: legacy states without a binding are rejected.

Types

type Candidate

type Candidate struct {
	File        string `json:"file"`
	Line        int    `json:"line"`
	Kind        string `json:"kind"`                  // go_lint | go_vet | go_complexity | ts_lint | py_lint
	Hint        string `json:"hint"`                  // human-readable finding
	Replacement string `json:"replacement,omitempty"` // "" means no auto-fix available
}

Candidate is one code-quality finding from the simplify detector. Replacement is empty when the detector does not provide an auto-fix (the current shell script never does — it only reports).

func ApplyCandidates

func ApplyCandidates(ctx context.Context, repoRoot string, candidates []Candidate) (applied, skipped []Candidate, err error)

ApplyCandidates attempts to apply each candidate's Replacement at file:line. Candidates with an empty Replacement are always marked skipped (the current simplify-detect.sh never provides one — findings are informational only).

For candidates that do carry a Replacement, the function reads the file, verifies the line still matches the expected content, and rewrites it atomically via temp+rename. Mismatches are skipped (not errors) to be idempotent across re-runs.

func RunSimplifyDetect

func RunSimplifyDetect(ctx context.Context, repoRoot string) ([]Candidate, error)

RunSimplifyDetect execs simplify-detect.sh and parses its section output into a []Candidate. Returns nil candidates (not an error) when the script is absent, exits non-zero, or finds nothing.

type CommitAnalysis

type CommitAnalysis = repomap.CommitAnalysis

type CommitGroup

type CommitGroup = repomap.CommitGroup

func ConsolidateGroups

func ConsolidateGroups(groups []CommitGroup) []CommitGroup

ConsolidateGroups enforces the cap-3/fold-riders/merge-smallest rules from commit-agent.md §93-106. It is a pure function: input groups are not mutated.

Algorithm (deterministic — sort before every decision):

  1. Sort groups by file count desc, then ID alpha (stability).
  2. If ≤3 groups, return as-is.
  3. "Rider" = group with 1-2 files. Fold each rider into the largest group that shares its top-level directory. Riders with no match are left alone.
  4. If still >3 groups, merge the two smallest (by file count, then ID) into the smaller one's entry, keeping the larger group's SuggestedMsg.
  5. Repeat step 4 until ≤3.

type CommitRecord

type CommitRecord struct {
	SHA     string `json:"sha"`
	Message string `json:"message"`
}

CommitRecord is one landed commit.

type CommitRefs

type CommitRefs = repomap.CommitRefs

type ExecuteOptions

type ExecuteOptions struct {
	Root             string    // repo root (default ".")
	PlanFile         string    // path to CommitAnalysis JSON (required)
	Push             bool      // git push origin <branch> --follow-tags
	Tag              string    // annotated tag to create at HEAD (semver)
	NoRelease        bool      // skip gh release create
	ReleaseNotesFrom string    // --notes-start-tag for gh release create
	DryRun           bool      // print actions, mutate nothing
	JSON             bool      // machine-readable result on stdout
	SkipFix          bool      // bypass consolidation pass
	Output           io.Writer // human dry-run output (default os.Stdout)
}

ExecuteOptions configures a commit-execute run.

type ExecuteResult

type ExecuteResult struct {
	Branch     string          `json:"branch"`
	Commits    []CommitRecord  `json:"commits"`
	Tag        *string         `json:"tag"`
	Pushed     bool            `json:"pushed"`
	ReleaseURL *string         `json:"release_url"`
	Postflight PostflightCheck `json:"postflight"`
}

ExecuteResult is the JSON-serializable result of a successful execute run.

func ExecuteCommit

func ExecuteCommit(ctx context.Context, opts ExecuteOptions) (*ExecuteResult, error)

ExecuteCommit loads the plan, validates it, consolidates groups, then executes git add/commit per group, then push/tag/release.

func ExecuteFromGroups

func ExecuteFromGroups(ctx context.Context, repoRoot string, groups []CommitGroup, opts ExecuteOptions) (*ExecuteResult, error)

ExecuteFromGroups runs the commit pipeline directly from a validated slice of CommitGroups, bypassing the plan-file load path. Intended for commit finish, which already has groups in memory. opts.PlanFile and opts.SkipFix are ignored.

type Finding

type Finding = repomap.Finding

func ApplyFixFindings

func ApplyFixFindings(ctx context.Context, repoRoot string, findings []Finding) (applied, skipped []Finding, err error)

ApplyFixFindings applies the substitution table to all findings whose DefaultAction is "fix". Each line is rewritten in place using an atomic temp+rename write. Idempotent: if the placeholder is already present at that line, the finding is marked skipped.

Returns the applied and skipped findings, or an error on I/O failure.

func LoadFindings

func LoadFindings(path string) ([]Finding, error)

LoadFindings reads a findings JSON file. Returns nil, nil when absent.

type PostflightCheck

type PostflightCheck struct {
	Clean     bool `json:"clean"`
	Convent   bool `json:"conventional"`
	TagLocal  bool `json:"tag_local"`
	TagRemote bool `json:"tag_remote"`
	Release   bool `json:"release"`
}

PostflightCheck records the result of each postflight verification. Checks that are not applicable (e.g. TagRemote when --push was not set) are set to true so callers can use postflightOK() without special-casing.

type PrepLowConf

type PrepLowConf struct {
	GroupID   string   `json:"group_id"`
	Files     []string `json:"files"`
	DiffSlice string   `json:"diff_slice"`
}

PrepLowConf is one group requiring LLM subject polish (capped at 3; diff_slice ≤500 chars).

type PrepPayload

type PrepPayload struct {
	Preflight       PrepPreflight    `json:"preflight"`
	ModeHint        string           `json:"mode_hint"` // "FULL" | "LOCAL"
	PrepToken       string           `json:"prep_token"`
	Status          string           `json:"status"` // "ready" | "needs_judgment" | "abort"
	AbortReason     string           `json:"abort_reason,omitempty"`
	Plan            []PrepPlanGroup  `json:"plan"`
	Review          []PrepReviewItem `json:"review"`
	LowConfSubjects []PrepLowConf    `json:"low_conf_subjects"`
	ReleaseRecipe   bool             `json:"release_recipe"`
	SessionRepos    []string         `json:"session_repos"`
	ReleaseGate     *PrepReleaseGate `json:"release_gate,omitempty"`
}

PrepPayload is the JSON document emitted by `repomap commit prep --json`.

type PrepPlanGroup

type PrepPlanGroup struct {
	Type       string   `json:"type"`
	Scope      string   `json:"scope"`
	Subject    string   `json:"subject"`
	Files      []string `json:"files"`
	Confidence float64  `json:"confidence"`
}

PrepPlanGroup is one consolidated commit in the plan.

func GroupsToPlan

func GroupsToPlan(groups []CommitGroup) []PrepPlanGroup

GroupsToPlan converts CommitGroups to PrepPlanGroups for the payload.

type PrepPreflight

type PrepPreflight struct {
	Branch    string `json:"branch"`
	Working   string `json:"working"`
	Remote    string `json:"remote"`
	Unpushed  string `json:"unpushed"`
	LatestTag string `json:"latest_tag"`
	GHAuth    string `json:"gh_auth"`
}

PrepPreflight mirrors the cpt.md context block fields.

type PrepReleaseGate

type PrepReleaseGate struct {
	Applied []any `json:"applied"`
	BuildOK bool  `json:"build_ok"`
}

PrepReleaseGate holds the result of running the release gate.

func RunReleaseGate

func RunReleaseGate(repoRoot string) *PrepReleaseGate

RunReleaseGate shells out to release-gate.sh and returns a summary. build_ok=true when the script exits 0 or is absent.

func RunReleaseGateContext

func RunReleaseGateContext(ctx context.Context, repoRoot string) *PrepReleaseGate

RunReleaseGateContext is RunReleaseGate with caller cancellation.

type PrepReviewItem

type PrepReviewItem struct {
	ID            string `json:"id"`
	File          string `json:"file"`
	Line          int    `json:"line"`
	Snippet       string `json:"snippet"`
	Detail        string `json:"detail"`
	DefaultAction string `json:"default_action"`
}

PrepReviewItem is one finding that needs LLM judgment (capped at 5; snippet ≤200 chars).

func BuildReviewItems

func BuildReviewItems(findings []Finding, maxItems int) []PrepReviewItem

BuildReviewItems extracts REVIEW findings into PrepReviewItems, capped at maxItems.

type PrepState

type PrepState struct {
	Analysis      *CommitAnalysis   `json:"analysis"`
	Plan          []CommitGroup     `json:"plan"`
	SessionRepos  []string          `json:"session_repos"`
	ReleaseRecipe bool              `json:"release_recipe"`
	ReleaseGate   *PrepReleaseGate  `json:"release_gate,omitempty"`
	RepoRoot      string            `json:"repo_root"`
	HeadSHA       string            `json:"head_sha,omitempty"`    // HEAD at prep time; finish refuses on mismatch
	FileHashes    map[string]string `json:"file_hashes,omitempty"` // planned rel path → sha256 hex ("absent" for planned deletions)
}

PrepState is persisted to tmpdir and loaded by `commit finish`.

func LoadPrepState

func LoadPrepState(token string) (*PrepState, error)

LoadPrepState reads a persisted PrepState from tmpdir by token.

type RepoStatus

type RepoStatus struct {
	Repo  string   `json:"repo"`
	Dirty []string `json:"dirty"` // lines from git status --porcelain; empty = clean
}

RepoStatus records the porcelain status of a single repo.

func CrossRepoVerify

func CrossRepoVerify(ctx context.Context, sessionRepos []string) (results []RepoStatus, allClean bool)

CrossRepoVerify checks git porcelain status across multiple repos.

type ReviewDecision

type ReviewDecision struct {
	ID          string `json:"id"`          // matches Finding identity (file+line)
	Verdict     string `json:"verdict"`     // "safe" | "unsafe"
	Replacement string `json:"replacement"` // applied when verdict="unsafe"
}

ReviewDecision is one LLM verdict for a REVIEW finding.

type SecretsSummary

type SecretsSummary = repomap.SecretsSummary

type VerifyResult

type VerifyResult struct {
	Mode              string `json:"mode"` // "local" | "full"
	OK                bool   `json:"ok"`
	LastCommitSubject string `json:"last_commit_subject,omitempty"` // local mode
	Tag               string `json:"tag,omitempty"`                 // full mode
	ReleaseURL        string `json:"release_url,omitempty"`         // full mode
	FailureDetail     string `json:"failure_detail,omitempty"`
}

VerifyResult records the outcome of a self-verify run.

func SelfVerify

func SelfVerify(ctx context.Context, repoRoot, mode string) (VerifyResult, error)

SelfVerify performs mode-aware verification of the current repo.

Jump to

Keyboard shortcuts

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