pub struct GitRepo { /* private fields */ }Expand description
Handle to a local git repository rooted at a working-tree directory.
Implementations§
Source§impl GitRepo
impl GitRepo
Sourcepub fn open(root: impl Into<PathBuf>) -> Result<Self>
pub fn open(root: impl Into<PathBuf>) -> Result<Self>
Open root as a git repository, HARDENED.
Verifies git rev-parse --git-dir succeeds inside root; returns
EngineError::Git when root is not a repository (or git itself
cannot be invoked).
Every invocation from the returned handle runs with executable git
configuration disabled — see Self::build_exec_disable_flags for
the flag set. This is the DEFAULT because engine-side git runs inside
the tree the worker controls (audit 2026-09-01 H3): the worker’s
session cwd is the active tree, .git is inside its write allowlist,
and the engine’s next checkpoint git status / git add /
git commit would otherwise execute a planted pre-commit hook,
core.fsmonitor, filter driver or gpg.program OUTSIDE every sandbox
with the engine’s full ambient environment. Hardening was previously
opt-in and applied at five sites; the sixteen that did not opt in
(integration-worktree handle, checkpoint commits, checkout, tag,
push_mission_branch) were the hole.
Self::open_unhardened is the explicit escape hatch for a caller
that genuinely needs the repository’s own executable config.
Sourcepub fn open_unhardened(root: impl Into<PathBuf>) -> Result<Self>
pub fn open_unhardened(root: impl Into<PathBuf>) -> Result<Self>
Open root as a git repository WITHOUT the executable-config
neutralization Self::open applies.
There is no engine caller: it exists so a future one that genuinely wants the repository’s hooks (a deliberate “run the project’s own pre-commit” feature, say) has to say so at the open site rather than getting it by forgetting to opt in. Do not use it on a tree an agent can write.
Sourcepub fn with_hooks_disabled(&self) -> Result<GitRepo>
pub fn with_hooks_disabled(&self) -> Result<GitRepo>
A handle to the same repository whose every git invocation runs with
executable configuration disabled (see Self::build_exec_disable_flags
for the exact flag set and the surfaces each entry neutralizes, 13th-pass
review P1 — the set previously stopped at core.hooksPath= +
core.fsmonitor= while this doc claimed “every executable surface”,
leaving planted filter drivers and gpg.program executable).
Self::open now returns a hardened handle already, so on an
ordinary handle this keeps the initial driver boundary (including
across clones). Each local invocation checks that boundary again;
re-wrapping must not authorize a driver introduced by a worker.
The gated merge path uses this: its scratch worktree’s gitdir points
into the primary .git, so mission-authored gate/test code can plant
executable config — which the merge’s own checkout / merge / worktree
commands would then execute with the server’s full inherited
environment, exactly the tokens the sanitized gate executor withholds.
The validator-integrity fingerprint runs on a verification handle for
the same reason: a validator that poisons core.fsmonitor must not
get its payload executed by the detection itself (4th-pass review —
detection previously ran git status BEFORE comparing config, so the
payload ran first). Opt-in per handle: worker-side git behavior is
deliberately unchanged.
Building the handle enumerates the repo’s configured filter and merge drivers; an enumeration failure fails CLOSED (no handle) — a verification handle that cannot name its armed drivers cannot promise the surface is disabled.
Sourcepub fn git_common_dir(&self) -> Result<PathBuf>
pub fn git_common_dir(&self) -> Result<PathBuf>
The shared git directory (.git in a plain checkout, the MAIN repo’s
git dir for a linked worktree) — where config, hooks, and refs live.
Relative --git-common-dir output resolves against the repo root.
Sourcepub fn for_each_ref(&self) -> Result<String>
pub fn for_each_ref(&self) -> Result<String>
Mission-significant refs for the tamper fingerprint: the CONTENT of
refs/heads/kranz/* (mission branches — a validator force-moving one
retargets the deliverable), refs/tags/*, AND refs/replace/* (a
replace ref changes how EVERY later git command resolves an object —
git show <base> renders a fake without HEAD, status, heads, or tags
moving), plus the COUNT of all refs/heads/* (a validator-created
sneaky branch shows as count+1).
refs/remotes/* is excluded (ambient mirror state: any operator/CI
fetch), and other local heads’ CONTENT is excluded too — the operator
committing to main mid-round is ambient work, not tamper (mission
m-83d1ed’s second tripwire fire was exactly that: the instrumented
refs field catching the operator’s own push to main).
Sourcepub fn current_branch(&self) -> Result<String>
pub fn current_branch(&self) -> Result<String>
Name of the currently checked-out branch ("HEAD" when detached).
Sourcepub fn rev_parse(&self, refname: &str) -> Result<String>
pub fn rev_parse(&self, refname: &str) -> Result<String>
Sha of an arbitrary ref (git rev-parse <refname>).
Rejects a flag-shaped refname (leading -) with an
EngineError::Git before invoking git, mirroring the guard on
GitRepo::add_worktree/GitRepo::merge_no_ff/
GitRepo::push_mission_branch.
Sourcepub fn is_ancestor(&self, ancestor: &str, descendant: &str) -> Result<bool>
pub fn is_ancestor(&self, ancestor: &str, descendant: &str) -> Result<bool>
Whether ancestor is an ancestor of (or equal to) descendant
(git merge-base --is-ancestor <ancestor> <descendant>).
git’s contract: exit 0 => Ok(true); exit 1 => Ok(false); any other
exit code is a real git failure, surfaced as EngineError::Git.
Rejects a flag-shaped ancestor/descendant (leading -) before
invoking git, mirroring GitRepo::rev_parse/GitRepo::merge_no_ff.
Sourcepub fn branch_exists(&self, name: &str) -> Result<bool>
pub fn branch_exists(&self, name: &str) -> Result<bool>
Whether a local branch of this name exists.
Sourcepub fn create_branch(&self, name: &str, from: Option<&str>) -> Result<()>
pub fn create_branch(&self, name: &str, from: Option<&str>) -> Result<()>
Create branch name at from (a sha or ref), or at HEAD when
from is None. Does not check the branch out.
Sourcepub fn checkout(&self, name: &str) -> Result<()>
pub fn checkout(&self, name: &str) -> Result<()>
Check out an existing branch (or any committish).
Sourcepub fn is_clean(&self) -> Result<bool>
pub fn is_clean(&self) -> Result<bool>
True when the working tree has no changes at all. --porcelain
output includes untracked files, so those count as dirty too.
Sourcepub fn porcelain_status(&self) -> Result<String>
pub fn porcelain_status(&self) -> Result<String>
Full git status --porcelain (v1) output: index + worktree status of
tracked files plus untracked non-ignored paths, respecting .gitignore
(so build-artifact churn like target/ and the gitignored .kranz
runtime never appears). The validator immutability fingerprint
(crate::validator_integrity) compares this verbatim across a
session; v1’s C-quoting keeps even exotic paths to one line per entry.
Sourcepub fn ls_files_v(&self) -> Result<String>
pub fn ls_files_v(&self) -> Result<String>
git ls-files -v: every index entry with its flag column (S =
skip-worktree, lowercase = assume-unchanged). A skip-worktree flag
hides worktree modifications from git status entirely (4th-pass
review: set the flag, overwrite the file, HEAD and porcelain both
unchanged), so the immutability fingerprint covers the flags too.
Sourcepub fn is_clean_tracked(&self) -> Result<bool>
pub fn is_clean_tracked(&self) -> Result<bool>
Like Self::is_clean but ignoring untracked files: true when no
TRACKED file is modified, staged, or deleted. Untracked files never
block a branch switch (git carries them across), so restore-checkout
paths use this rather than full cleanliness.
Sourcepub fn is_clean_tracked_strict(&self) -> Result<bool>
pub fn is_clean_tracked_strict(&self) -> Result<bool>
Like Self::is_clean_tracked, but also rejects index flags that can
hide working-tree changes (assume-unchanged, skip-worktree, or
fsmonitor-valid).
Scratch merge worktrees are never sparse and never need either flag,
so every tracked entry must have git’s normal H tag.
Sourcepub fn has_normal_index_entry(&self, path: &str) -> Result<bool>
pub fn has_normal_index_entry(&self, path: &str) -> Result<bool>
Whether one tracked path has Git’s normal index tag. Lowercase tags
(assume-unchanged or fsmonitor-valid) and S (skip-worktree) can
hide worktree bytes from ordinary diff/status commands and must not
guard a trust decision.
Sourcepub fn add_all_and_commit(&self, message: &str) -> Result<String>
pub fn add_all_and_commit(&self, message: &str) -> Result<String>
git add -A then git commit -m <message>; returns the new head sha.
A no-change commit attempt exits non-zero, so it surfaces as an
EngineError::Git carrying git’s own “nothing to commit” output.
Sourcepub fn dirty_paths(&self) -> Result<Vec<PathBuf>>
pub fn dirty_paths(&self) -> Result<Vec<PathBuf>>
Paths currently dirty in the working tree (git status --porcelain),
relative to the repo root. Empty when clean.
Sourcepub fn commit_dirty_paths(&self, message: &str) -> Result<CheckpointOutcome>
pub fn commit_dirty_paths(&self, message: &str) -> Result<CheckpointOutcome>
Stage and commit only currently-dirty paths (scoped checkpoint).
Prefer this over Self::add_all_and_commit for engine checkpoints so
a concurrent operator edit outside the worker’s tree is not scooped in
via git add -A. No-op (returns current HEAD) when the tree is clean.
A secret-scan refusal is reported as
CheckpointOutcome::RefusedBySecretScan, never as an Err —
checkpoint callers sit on the mission loop and must record the refusal
instead of erroring the run (see CheckpointOutcome). Real git
failures still propagate.
Sourcepub fn commit_paths(&self, paths: &[&Path], message: &str) -> Result<String>
pub fn commit_paths(&self, paths: &[&Path], message: &str) -> Result<String>
Stage and commit only the given paths; returns the new head sha.
Paths may be absolute or relative to the repo root. Content staged
for other paths is left staged and untouched (git commit -- <paths>
commits just the named pathspecs).
Idempotent: if staging the named paths yields no change (e.g. a
crash-replayed re-commit of byte-identical files), this is a no-op that
returns the current head rather than an empty-commit error. An empty
paths slice is still rejected up front.
Sourcepub fn commits_between(&self, from: &str, to: &str) -> Result<Vec<CommitInfo>>
pub fn commits_between(&self, from: &str, to: &str) -> Result<Vec<CommitInfo>>
Commits reachable from to but not from (from..to), oldest first.
Sourcepub fn merge_commit_count(&self, from: &str, to: &str) -> Result<usize>
pub fn merge_commit_count(&self, from: &str, to: &str) -> Result<usize>
Count merge commits reachable from to but not from.
Sourcepub fn count_first_parent_commits(
&self,
branch: &str,
since: &DateTime<Utc>,
until: &DateTime<Utc>,
) -> Result<u64>
pub fn count_first_parent_commits( &self, branch: &str, since: &DateTime<Utc>, until: &DateTime<Utc>, ) -> Result<u64>
Count first-parent commits on branch whose committer date falls in
(since, until] (git rev-list --first-parent --count --since --until) — the landed-changes denominator of the industry-comparison
fold (ticket outcomes-comparison-metrics, KRZ-333). First-parent
counts one entry per change that landed on the branch’s own line of
history — a direct commit or a --no-ff merge — never the commits a
merge brought with it, so a landed mission merge and a hand-written
commit each count once. git’s --since is exclusive and --until
inclusive; the timestamps go to git verbatim as RFC 3339.
Sourcepub fn diff_stat(&self, from: &str, to: &str) -> Result<String>
pub fn diff_stat(&self, from: &str, to: &str) -> Result<String>
git diff --stat <from>..<to> output, verbatim.
Sourcepub fn diff_full(&self, from: &str, to: &str) -> Result<String>
pub fn diff_full(&self, from: &str, to: &str) -> Result<String>
Full git diff <from>..<to> output, verbatim.
Sourcepub fn diff_range(&self, range: &str) -> Result<String>
pub fn diff_range(&self, range: &str) -> Result<String>
Full git diff <range> output for a caller-supplied range.
Sourcepub fn diff_staged(&self) -> Result<String>
pub fn diff_staged(&self) -> Result<String>
Full staged diff (git diff --cached) output.
Sourcepub fn diff_head(&self) -> Result<String>
pub fn diff_head(&self) -> Result<String>
Full git diff --binary HEAD output (index + working tree vs HEAD),
verbatim — everything a worker left uncommitted on TRACKED files,
binary-safe so it replays byte-for-byte through git apply
(GitRepo::apply_patch). The validator snapshot
(crate::validator_snapshot) captures this in the real checkout and
applies it in the throwaway copy so validators judge exactly the tree
the worker left.
Sourcepub fn apply_patch(&self, patch_file: &Path) -> Result<()>
pub fn apply_patch(&self, patch_file: &Path) -> Result<()>
git apply <patch_file> against the worktree (index untouched). The
validator snapshot replays the real checkout’s GitRepo::diff_head
this way; the patch comes from a file path so no stdin plumbing is
needed.
Sourcepub fn untracked_files(&self) -> Result<Vec<OsString>>
pub fn untracked_files(&self) -> Result<Vec<OsString>>
Untracked, non-ignored files (git ls-files --others --exclude-standard -z), repo-relative. -z gives unquoted raw paths
(NUL is the only byte git never allows in one), so even
newline-bearing names survive the split. Ignored paths (target/,
the .kranz runtime) never appear — mirroring
GitRepo::porcelain_status.
Untracked non-ignored files, NUL-separated raw bytes preserved:
ls-files -z output is byte-oriented, and a name that is not valid
UTF-8 must NOT be lossy-mangled — the replacement character turns
into a path that then fails to copy and (pre-fix) was silently
swallowed as NotFound (5th-pass review). On unix the raw bytes are
used verbatim; on Windows (where git emits WTF-8) the lossy form is
the pragmatic fallback, documented.
Sourcepub fn diff_head_paths(&self, paths: &[&Path]) -> Result<String>
pub fn diff_head_paths(&self, paths: &[&Path]) -> Result<String>
Full git diff HEAD -- <paths> output (index + working tree vs HEAD),
verbatim — the checkpoint scan’s “what this mission actually changed”,
never the pre-existing base content of files it merely touches.
Sourcepub fn diff_range_paths(
&self,
from: &str,
to: &str,
paths: &[String],
) -> Result<String>
pub fn diff_range_paths( &self, from: &str, to: &str, paths: &[String], ) -> Result<String>
Full git diff <from>..<to> -- <paths> output, verbatim — the
affected-path diff a Flight Rules waiver’s digest binds (KRZ-344
D-I): only changes under the named paths alter the bytes, so an
unrelated-path change can never invalidate (or be covered by) the
waiver. Refuses flag-shaped refs (the GitRepo::changed_paths
guard) and an EMPTY path set — git diff <range> -- with no
pathspec silently means the WHOLE diff, which would bind authority
the caller never scoped.
Sourcepub fn changed_paths(&self, from: &str, to: &str) -> Result<Vec<String>>
pub fn changed_paths(&self, from: &str, to: &str) -> Result<Vec<String>>
Paths changed in from..to (git diff --name-only <from>..<to>),
one per line as git reports them.
Rejects a flag-shaped from/to (leading -) before invoking git,
mirroring the guard on GitRepo::is_ancestor/GitRepo::rev_parse.
Sourcepub fn dashboard_touched(&self, from: &str, to: &str) -> Result<bool>
pub fn dashboard_touched(&self, from: &str, to: &str) -> Result<bool>
Whether from..to touches anything under apps/dashboard/ — the
signal the gate suite uses to decide whether to run the dashboard
gates (roadmap M6 gated merge).
Sourcepub fn commit_that_added(&self, rel_path: &str) -> Result<Option<AddedCommit>>
pub fn commit_that_added(&self, rel_path: &str) -> Result<Option<AddedCommit>>
The most recent commit that ADDED rel_path (repo-relative,
forward-slash), with its subject and full message body — or None if
the path is untracked / was never added under version control.
Used to check lesson-file provenance: a lesson only reaches a planning
prompt if a [kranz] mission report commit carrying a matching
Kranz-Mission trailer introduced it, so an untracked drop or a
worker feature-commit fails the check (see the lesson-manifest render).
Sourcepub fn path_changed_since(&self, path: &str, since_ymd: &str) -> Result<bool>
pub fn path_changed_since(&self, path: &str, since_ymd: &str) -> Result<bool>
Whether path has a commit after the UTC since_ymd calendar day.
Used by knowledge-refresh drift checks: a note whose verified_against
path has history after last_verified is check-needed. Empty history
(unknown path, or no commits in the window) is false, not an error.
Flag-shaped/non-repository paths and invalid dates are refused before
git runs. A non-zero git log is an error, never “unchanged”.
Sourcepub fn tag(&self, name: &str, message: &str) -> Result<()>
pub fn tag(&self, name: &str, message: &str) -> Result<()>
Create an annotated tag at HEAD (git tag -a <name> -m <message>).
Sourcepub fn add_worktree(
&self,
path: &Path,
branch: &str,
from_sha: &str,
) -> Result<()>
pub fn add_worktree( &self, path: &Path, branch: &str, from_sha: &str, ) -> Result<()>
Create a new worktree at path, checked out to a NEW branch branch
created at from_sha (git worktree add -b <branch> <path> <from_sha>).
path may be absolute or relative to the repo root; git records the
absolute path either way. The branch must not already exist (git’s -b
fails otherwise) — callers use a fresh per-feature branch name.
Sourcepub fn add_worktree_checkout(&self, path: &Path, branch: &str) -> Result<()>
pub fn add_worktree_checkout(&self, path: &Path, branch: &str) -> Result<()>
Create a new worktree at path, checked out to the EXISTING branch
branch (git worktree add <path> <branch>, no -b).
path may be absolute or relative to the repo root; git records the
absolute path either way. branch must already exist and must NOT
already be checked out in another worktree — git refuses to check the
same branch out twice and that failure surfaces as EngineError::Git.
Sourcepub fn add_detached_worktree(&self, path: &Path, commit: &str) -> Result<()>
pub fn add_detached_worktree(&self, path: &Path, commit: &str) -> Result<()>
Create a detached worktree at path pinned to commit.
Gated merge uses this to build and validate an integration commit without checking out either moving branch in the primary tree.
Sourcepub fn remove_worktree(&self, path: &Path) -> Result<()>
pub fn remove_worktree(&self, path: &Path) -> Result<()>
Remove a worktree at path (git worktree remove --force <path>),
tolerating a worktree that is already gone.
--force is used so a worktree with a dirty tree (a worker that left
uncommitted changes, or a merge that has already consumed its commits)
is still removed — leaked worktrees are the failure mode this guards
against. When git reports the worktree is not registered / does not
exist, that is treated as success (idempotent cleanup). Any OTHER git
failure surfaces as EngineError::Git.
Sourcepub fn merge_no_ff(&self, branch: &str) -> Result<MergeOutcome>
pub fn merge_no_ff(&self, branch: &str) -> Result<MergeOutcome>
Merge branch into the current branch with an explicit merge commit
(git merge --no-ff --no-edit <branch>), reporting clean vs conflict.
A clean merge returns MergeOutcome::Clean with the merge commit on
the current branch. On conflict the merge is rolled back with
git merge --abort (so the working tree is left CLEAN — the porcelain
status is empty afterwards) and MergeOutcome::Conflict is returned,
carrying the conflicting paths git named. When git refuses the merge
before it ever starts (no MERGE_HEAD, e.g. an untracked file in the
way) MergeOutcome::RefusedPreMerge is returned instead, carrying
git’s verbatim refusal — no abort is attempted, since there is nothing
to abort. Only a genuine git failure (git could not be spawned, or the
abort itself failed on a real conflict) is an Err.
Sourcepub fn merge_no_ff_with_message(
&self,
branch: &str,
message: Option<&str>,
) -> Result<MergeOutcome>
pub fn merge_no_ff_with_message( &self, branch: &str, message: Option<&str>, ) -> Result<MergeOutcome>
Like Self::merge_no_ff but supplies an explicit merge commit
message, used for kranz-authored trailer metadata.
Sourcepub fn fast_forward_to(&self, commit: &str) -> Result<MergeOutcome>
pub fn fast_forward_to(&self, commit: &str) -> Result<MergeOutcome>
Move the current branch to an already-created descendant commit with
git merge --ff-only. Gated merge uses this after validating the exact
integration commit in a scratch worktree.
Sourcepub fn show_file(&self, branch: &str, path: &str) -> Result<Option<Vec<u8>>>
pub fn show_file(&self, branch: &str, path: &str) -> Result<Option<Vec<u8>>>
Bytes of path as it exists on branch (git show <branch>:<path>),
or None when the path does not exist on that branch. Used to compare
an untracked working-tree file byte-for-byte against the version a
merge would bring in, so it can be safely removed when identical.
Sourcepub fn is_tracked(&self, path: &str) -> Result<bool>
pub fn is_tracked(&self, path: &str) -> Result<bool>
Whether path is tracked in the index (git ls-files --error-unmatch -- <path>): exit 0 ⇒ tracked; exit 1 ⇒ untracked/absent (NOT an
error); any other status is a real git failure. The Flight Rules
trust boundary (KRZ-341, D-A/D-J) uses this to decide whether a pack
may activate ENFORCED rules: only tracked, repo-relative pack bytes
have provable base history.
Sourcepub fn ls_tree_recursive(
&self,
refname: &str,
prefix: &str,
) -> Result<Vec<TreeEntry>>
pub fn ls_tree_recursive( &self, refname: &str, prefix: &str, ) -> Result<Vec<TreeEntry>>
Recursive git ls-tree -r -l <refname> -- <prefix>: every entry under
prefix at refname with its git mode, object kind, and blob size.
The Flight Rules loader (KRZ-341) reads a standards corpus from a
PINNED base tree through this — never from the worktree — so a mission
branch edit cannot reshape the policy judging it. A flag-shaped ref
or prefix is refused before invoking git (mirroring Self::show_file).
Sourcepub fn is_untracked(&self, path: &str) -> Result<bool>
pub fn is_untracked(&self, path: &str) -> Result<bool>
Whether path is currently untracked in the working tree
(git status --porcelain -- <path> reports a ?? entry). false
when the path is tracked, ignored-and-absent, or simply not present.
Sourcepub fn list_worktrees(&self) -> Result<Vec<String>>
pub fn list_worktrees(&self) -> Result<Vec<String>>
Absolute paths of every registered worktree (git worktree list),
including the primary working tree. Used by cleanup to detect leaks.
Sourcepub fn prune_worktrees(&self) -> Result<()>
pub fn prune_worktrees(&self) -> Result<()>
Prune administrative records of worktrees whose directories are gone
(git worktree prune). Safe to call unconditionally after cleanup.
Sourcepub fn delete_branch_force(&self, name: &str) -> Result<()>
pub fn delete_branch_force(&self, name: &str) -> Result<()>
Delete a local branch, force (git branch -D <name>), tolerating a
branch that is already gone. Used to tidy per-feature worktree branches
after their worktrees are removed (roadmap M3 cleanup).
Sourcepub fn remote_url(&self, name: &str) -> Result<Option<String>>
pub fn remote_url(&self, name: &str) -> Result<Option<String>>
URL of remote name (git remote get-url), or Ok(None) when absent.
Sourcepub fn remote_has_branch(&self, remote: &str, branch: &str) -> Result<bool>
pub fn remote_has_branch(&self, remote: &str, branch: &str) -> Result<bool>
Whether remote advertises branch branch (git ls-remote --heads).
Read-only network probe — never updates local refs.
Sourcepub fn has_remote(&self, name: &str) -> Result<bool>
pub fn has_remote(&self, name: &str) -> Result<bool>
Whether a remote named name is configured (git remote get-url).
A probe, not an assertion: returns Ok(false) when the remote is
absent and only errors when git itself cannot be spawned. Callers use
this to decide whether a cloud mission has anywhere to push to before
calling GitRepo::push_mission_branch.
Sourcepub fn push_mission_branch(&self, remote: &str, branch: &str) -> Result<()>
pub fn push_mission_branch(&self, remote: &str, branch: &str) -> Result<()>
Push a single kranz/* mission ref to remote — the one and only
push path in Kranz, and it is cloud-opt-in.
§Local default: Kranz never pushes (plan §4.4)
Git is the source of truth, but on a local host Kranz writes only to the
working tree and local refs — it never contacts a remote. No mission
loop or server route calls this method. The sole caller is the explicit
kranz exec --push <REMOTE> M6 cloud handoff; nothing about the local
default changes unless a human or cloud job supplies that flag.
§Guard rails (why this is safe to expose)
- The branch must begin with
kranz/— mission branches arekranz/mission-<id>and mission tags live underkranz/<id>/…. Anything else (main,master,HEAD, a bare sha,--force, or a refspec smuggling a second ref) is rejected withEngineError::Gitbefore any git process runs — no network. remotemust be an already-configured, non-flag-shaped remote name. The push is a plaingit push <remote> <branch>: never--force,--mirror, a custom receive-pack, asrc:dstrefspec,main, or a merge. The human still reviews thekranz/*branch and opens the PR.- On failure git’s stderr is surfaced verbatim via
EngineError::Git, so a bad deploy key or a rejected non-fast-forward shows up in the mission log with git’s own words.
The deploy key / GitHub App backing remote should itself be scoped to
kranz/* refs (see docs/deploy.md); this guard is defence in depth, not
the only line of defence.
Sourcepub fn ensure_identity(&self) -> Result<()>
pub fn ensure_identity(&self) -> Result<()>
Guarantee commits can be made: PIN user.name / user.email into the
repo’s LOCAL config when they are not already set there — to whatever
the operator’s config resolves them to, falling back to
kranz <kranz@localhost> when nothing resolves at all. A local
identity is never overwritten, and missions never fail on hosts
without a global git identity.
Pinning into local scope (rather than only writing the fallback pair
when nothing resolved) is what keeps commit authorship unchanged now
that hardened invocations no longer read the operator’s ~/.gitconfig
(audit H3 hardening, [UserConfig::Ignored]): without it, every
engine commit on a host whose identity lives only in the global file
would silently be restamped kranz <kranz@localhost>.
Sourcepub fn resolved_identity(&self) -> Result<(String, String)>
pub fn resolved_identity(&self) -> Result<(String, String)>
The git identity this repo resolves to right now: (user.name, user.email) from any config scope (local/global/system) visible to
the calling process’s environment, falling back to the same
kranz/kranz@localhost pair Self::ensure_identity would write when
neither key resolves.
Used to carry the engine’s resolved identity into a worker session
whose relocated HOME can no longer see the operator’s global
~/.gitconfig (see GIT_AUTHOR_NAME etc. injection in
runner::seed_worker_env).