Documentation
¶
Index ¶
- func BranchFromRefspec(refspec string) string
- func Replay(session *gitstate.SyncSession) error
- func RunCommitMsgHook(repoRoot, message string, cb func(stream, line string)) (string, error)
- func RunHook(repoRoot, hookName string, args []string, input []byte, ...) error
- func RunPostCommitHook(repoRoot string, cb func(stream, line string))
- func RunPreCommitHook(repoRoot string, wt *git.Worktree, cb func(stream, line string)) error
- type Backend
- type CommitBlock
- type CommitBlocks
- type CommitRing
- type DryRunBackend
- type Engine
- type EngineOptions
- type FileChange
- type ForgeBackend
- type GitBackend
- type Plan
- type PlannerOptions
- type PushOptions
- type Result
- type StageMode
- type SyncAction
- type SyncResult
- type TypeRegistry
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BranchFromRefspec ¶
BranchFromRefspec extracts the branch name from a refspec like "HEAD:refs/heads/main".
func Replay ¶ added in v0.6.0
func Replay(session *gitstate.SyncSession) error
Replay rebases local commits onto upstream using controlled tree replay. It is NOT a raw git rebase — it replays only SF-generated commits via explicit tree diffing with full integrity verification at each step.
Algorithm:
- Pre-conditions: HEAD attached, worktree clean, upstream configured → fail fast, no mutation
- Gate: no merge commits, linear chain (structural only — no authorship constraints)
- Re-validate upstream hash non-zero; record fetchedUpstreamHash
- Compute merge-base (exactly 1); collect commits oldest-first
- Hard reset to upstream
- For each commit: apply diff, stage, verify staging states, commit
- Race guard: upstream unchanged since fetch
Push is NOT performed by Replay — the engine (Engine.doReplayThenPush) owns push so the transition DIVERGED → REPLAY → CLEAN_AHEAD → PUSH remains in the engine's state machine and the push is logged as a formal transition.
Hooks are NOT run during replay — replay commits are machine-generated re-applications; running hooks again would double-execute side effects.
func RunCommitMsgHook ¶ added in v0.6.0
RunCommitMsgHook writes the message to a temp file, executes .git/hooks/commit-msg, and re-reads the (possibly modified) message from the file. Returns the (possibly modified) message and any error.
func RunHook ¶ added in v0.6.0
func RunHook(repoRoot, hookName string, args []string, input []byte, cb func(stream, line string)) error
RunHook executes a git hook if it exists and is executable. Returns ErrHookRejected if the hook exits non-zero. cb receives incremental stdout/stderr lines — never silently swallowed.
Hook sequence (caller's responsibility to run in order):
- pre-commit — no args, stdin /dev/null. Non-zero → abort.
- commit-msg <tmpfile> — write message to tmpfile, pass path as arg. Hook may modify the file; re-read after. Non-zero → abort.
- wt.Commit() — called by the CommitEngine, not here.
- post-commit — no args. Non-zero → warning only, no abort.
Hooks are treated as untrusted side-effect producers: - stdout/stderr always surfaced via cb - worktree snapshot before/after pre-commit; any new dirty paths emitted as hook_side_effect - filesystem mutations outside commit-msg tmpfile are detected and reported
func RunPostCommitHook ¶ added in v0.6.0
RunPostCommitHook executes .git/hooks/post-commit. Non-zero exit is logged as a warning via cb but does NOT abort.
Types ¶
type Backend ¶
type Backend interface {
Execute(ctx context.Context, plan *Plan, conventional bool) (*Result, error)
}
Backend executes a commit plan.
type CommitBlock ¶ added in v0.6.0
type CommitBlock struct {
Ring CommitRing
ID string // stable identifier for programmatic use and output
Message string // human-readable explanation
}
CommitBlock is a classified failure at any stage of commit planning or execution. Hard rings (Mechanical, Determinism) propagate as errors. Governance rings accumulate and are either reported or bypassed.
func (CommitBlock) Overrideable ¶ added in v0.6.0
func (b CommitBlock) Overrideable() bool
Overrideable returns true when the block can be bypassed with --maintainer-override.
type CommitBlocks ¶ added in v0.6.0
type CommitBlocks []CommitBlock
CommitBlocks is an ordered collection of CommitBlock values.
func (CommitBlocks) GovernanceOnly ¶ added in v0.6.0
func (bs CommitBlocks) GovernanceOnly() CommitBlocks
GovernanceOnly returns a slice containing only governance-ring blocks.
func (CommitBlocks) HasDeterminism ¶ added in v0.6.0
func (bs CommitBlocks) HasDeterminism() bool
HasDeterminism returns true when any RingDeterminism block is present. UX message: "StageFreight cannot safely describe what will be committed".
func (CommitBlocks) HasGovernance ¶ added in v0.6.0
func (bs CommitBlocks) HasGovernance() bool
HasGovernance returns true when any overrideable governance block is present.
func (CommitBlocks) HasHard ¶ added in v0.6.0
func (bs CommitBlocks) HasHard() bool
HasHard returns true when any non-overrideable block is present. Use HasMechanical and HasDeterminism for UX-distinct messaging.
func (CommitBlocks) HasMechanical ¶ added in v0.6.0
func (bs CommitBlocks) HasMechanical() bool
HasMechanical returns true when any RingMechanical block is present. UX message: "fix your repository state".
type CommitRing ¶ added in v0.6.0
type CommitRing string
CommitRing classifies a commit failure by its relationship to the commit path's safety and determinism. The ring determines whether a failure is absolute or can be bypassed with explicit maintainer intent.
Three rings, ordered by overrideability:
RingMechanical → git integrity is broken. No escape.
RingDeterminism → StageFreight cannot reliably describe what will happen. No escape.
RingGovernance → StageFreight policy or model is degraded, but the commit
path is still deterministic. Bypassed by --maintainer-override.
The rule: do not let Ring 3 failures disable a still-deterministic commit path.
const ( // RingMechanical covers failures where the git repository, index, or // filesystem state makes committing impossible regardless of intent: // - detached HEAD // - unresolved rebase/merge conflict // - index write failure // - hook rejected the commit // - push explicitly requested but mechanically blocked // Cannot be overridden. RingMechanical CommitRing = "mechanical" // RingDeterminism covers failures where StageFreight cannot reliably // determine what will be committed, construct the message, or truthfully // describe the result: // - missing commit summary // - unresolvable path set // - sync plan logically impossible (not a git error, a planning error) // Cannot be overridden. RingDeterminism CommitRing = "determinism" // RingGovernance covers failures where StageFreight policy, schema, or // model is degraded but the commit path itself remains deterministic: // - partial config-model migration incomplete // - generated outputs stale (docs, badges, manifests) // - convention strictness failures // - unrelated StageFreight subsystem broken // Can be bypassed with --maintainer-override. // Bypasses are recorded in Result.OverriddenBlocks and printed to output. RingGovernance CommitRing = "governance" )
type DryRunBackend ¶
type DryRunBackend struct {
RootDir string
}
DryRunBackend prints the commit plan without side effects.
type Engine ¶ added in v0.6.0
type Engine struct {
// contains filtered or unexported fields
}
Engine orchestrates repository convergence as an explicit state machine.
Every mutation is a validated transition between named states. No operation is performed outside of a valid (from, action, to) triple.
Transition table:
CLEAN_SYNCED → (no-op) → CLEAN_SYNCED CLEAN_BEHIND → FAST_FORWARD → CLEAN_SYNCED CLEAN_AHEAD → PUSH → CLEAN_SYNCED DIVERGED → REPLAY → CLEAN_AHEAD → PUSH → CLEAN_SYNCED
Blocked states (hard stops — no transitions allowed):
DETACHED_HEAD, DIRTY_WORKTREE, NO_UPSTREAM
When RebaseOnDiverge is false, DIVERGED returns ErrInvalidTransition instead of attempting replay — the caller must resolve the divergence manually.
func NewEngine ¶ added in v0.6.0
func NewEngine(session *gitstate.SyncSession, opts EngineOptions) *Engine
NewEngine creates an Engine bound to the given SyncSession.
func (*Engine) Sync ¶ added in v0.6.0
func (e *Engine) Sync() (*SyncResult, error)
Sync drives the repository to CLEAN_SYNCED through the minimum valid transition path determined by the current state class.
Algorithm:
- Classify initial state — fail immediately on any X-class blocked state
- Fetch + reclassify (fetch is always required for an accurate view)
- Dispatch the single valid transition for the resulting state class
Returns the SyncResult describing exactly what happened.
type EngineOptions ¶ added in v0.6.0
type EngineOptions struct {
// RebaseOnDiverge: when true, DIVERGED state triggers Replay + Push.
// When false, DIVERGED returns ErrInvalidTransition — the user must
// resolve the divergence manually before pushing.
RebaseOnDiverge bool
// Remote overrides the remote resolved from the session state.
// Empty means "use whatever the branch tracking config says".
Remote string
// Refspec is an optional explicit push refspec (e.g. "HEAD:refs/heads/main").
// Empty means push the current branch to its upstream.
Refspec string
// OnEvent receives a structured event for every state transition.
// If nil, events are silently dropped.
// Use this for CI log output, tracing, or UI state panels.
OnEvent func(gitstate.TransitionEvent)
}
EngineOptions configures Engine behaviour.
type FileChange ¶
FileChange represents a file with its change status.
type ForgeBackend ¶
ForgeBackend creates commits purely via forge API (no local git commit).
type GitBackend ¶
type GitBackend struct {
RootDir string
// OnCommitLine is called for each output line during hook execution and sync
// transition events. stream: "stdout", "stderr", "hook_side_effect", "sync".
// If nil, output is captured but not forwarded.
OnCommitLine func(stream string, line string)
}
GitBackend executes commits via go-git — no git binary required. Push/sync is also handled via go-git through the Engine.
func (*GitBackend) Push ¶ added in v0.6.0
func (g *GitBackend) Push(opts PushOptions) (*SyncResult, error)
Push synchronizes the current branch with its remote via the go-git engine.
type Plan ¶
type Plan struct {
Type string
Scope string
Summary string
Body string
Breaking bool
SkipCI bool
Paths []string // for StageExplicit
StageMode StageMode
Push PushOptions
SignOff bool
}
Plan is a fully resolved, validated commit intent.
func BuildPlan ¶
func BuildPlan(opts PlannerOptions, cfg config.CommitConfig, registry *TypeRegistry, rootDir string) (*Plan, error)
BuildPlan merges CLI flags with config defaults, validates, and returns a Plan.
type PlannerOptions ¶
type PlannerOptions struct {
Type string
Scope string
Message string // positional or --message
Body string
Breaking bool
SkipCI *bool // nil = use config default
Push *bool // nil = use config default
Paths []string // from --add flags
All bool
SignOff bool
Remote string
Refspec string
}
PlannerOptions holds raw inputs from CLI flags and positional args.
type PushOptions ¶
type PushOptions struct {
Enabled bool
Remote string // default: "origin"
Refspec string // default: "" (current branch)
RebaseOnDiverge bool // when true (default), rebase onto upstream if diverged before pushing
}
PushOptions controls post-commit push behavior.
type Result ¶
type Result struct {
SHA string
Message string
Files []string // actual staged files (from git diff --cached --name-only)
Pushed bool
NoOp bool
Backend string // stable descriptor: "git", "forge (gitlab)", "forge (github)", "dry-run"
Sync *SyncResult // populated when push was executed via the convergence engine
// MaintainerOverride is true when --maintainer-override was active for this commit.
// OverriddenBlocks records which governance checks were bypassed.
// Both are empty for normal commits with no governance failures.
MaintainerOverride bool
OverriddenBlocks CommitBlocks
}
Result holds the outcome of a commit execution. All informational status must be represented here, not printed by backends.
type SyncAction ¶ added in v0.6.0
type SyncAction string
SyncAction names a single step in the repository convergence plan.
const ( SyncSetUpstream SyncAction = "set-upstream" // configure tracking branch on first push SyncFetch SyncAction = "fetch" // fetch remote before rebase or fast-forward SyncFastForward SyncAction = "fast-forward" // merge --ff-only to catch up to upstream SyncRebase SyncAction = "rebase" // rebase local commits onto upstream SyncPush SyncAction = "push" // push to remote SyncNoop SyncAction = "noop" // already up to date, nothing to do )
type SyncResult ¶ added in v0.6.0
type SyncResult struct {
ActionsExecuted []SyncAction
PushedRef string // remote name that was pushed to
Noop bool // true only when SyncNoop was the sole action
}
SyncResult is the outcome of a push operation.
type TypeRegistry ¶
type TypeRegistry struct {
// contains filtered or unexported fields
}
TypeRegistry validates and resolves commit type keys.
func NewTypeRegistry ¶
func NewTypeRegistry(types []config.CommitType) *TypeRegistry
NewTypeRegistry creates a registry from configured commit types.
func (*TypeRegistry) List ¶
func (r *TypeRegistry) List() []config.CommitType
List returns all configured commit types in definition order.
func (*TypeRegistry) Resolve ¶
func (r *TypeRegistry) Resolve(key string) (resolvedKey string, forceBang bool, err error)
Resolve returns the resolved type key and whether a bang (!) is forced. If the type has an AliasFor, it returns the alias target key.
func (*TypeRegistry) Valid ¶
func (r *TypeRegistry) Valid(key string) bool
Valid returns true if the key is a recognized commit type.