Expand description
What: Internal native cli-git implementation, not yet the production executable. Why: The Rust rewrite preserves Git’s own argument, repository and transaction semantics.
In TS you’d write (pseudocode):
// Internal modules used by the forthcoming native entry point and consumer verification.Modules§
- action
- The decision one invocation ends in: become Git, or print and exit. What: The decision one wrapper invocation ends in. Why: Forwarding replaces the process and cannot run inside a unit test; returning the decision as data lets every path be checked directly.
- auto_
push - Post-commit auto-push of a landed commit: the push argv, the surfaced output and the failure reporting.
What:
Post-commit auto-push of a landed commit: which push runs, what its output shows,
and how a coordination failure is reported without failing the commit.
Why:
Every commit in this repository is backed up (
AGENTS.mdrule APG), and the incumbent’ssrc/auto-push.tsdecides the push argv from the upstream state, prints only GitHubremote:lines on success and the full output on failure, and keeps the commit’s exit status when the push fails. The landed commit is an input, so the integrator can call this after any landing. - auto_
push_ branch_ key - The per-branch push coordination file name: a reversible, flat, case-safe key.
What:
The reversible, flat, case-insensitive-safe file name of one branch’s push coordination files.
Why:
The per-branch push lock and
last-pushedrecord live under<git-common-dir>/cli-git/push/, and the incumbent (src/auto-push-branch-key.ts) and this wrapper must name them identically, because both wrappers contend on the same lock while both are installed. Uppercase letters are escaped too, so refs that differ only in case never share a file on a case-insensitive filesystem. - auto_
push_ record - The per-branch
last-pushedrecord: validation, tolerant reads and atomic publication. What: The per-branchlast-pushedrecord that single-flight auto-push coordinates through. Why: The incumbent (src/auto-push-record.ts) and this wrapper must read each other’s records, because a commit waiting on a push made by the other wrapper decides from the same file. The record is a cache: an absent or unreadable record only costs an extra push, so only a read failure other than “absent” stops the caller. - auto_
push_ single_ flight - Single-flight push coordination per branch: owner lock, record joins and the push itself.
What:
Single-flight auto-push per branch: one owner lock serializes the pushes of a branch,
and the
last-pushedrecord names the most recent attempt. Why: A commit that a recorded successful push already covers finishes without pushing, and a commit that waits behind a live pusher joins the push it waited for, so concurrent landings on one branch cost fewer pushes than commits. The incumbent’ssrc/auto-push-single-flight.tsdecides these joins, and the lock and record paths must match its paths byte for byte. - blob_
batch - Loading many blobs through one
git cat-file --batch. What: Loading many blobs through onegit cat-file --batch, optionally from a private object store, with strict parsing of the batch output. Why: Post-landing completion reads the original and intended bytes of every added path; one batch replaces a Git start per object (src/policy-engine/blob-batch.ts). - bounded_
child - One child program run with a time bound and output caps, through files instead of pipes. What: Run one child program to completion with a time bound and output caps: its input comes from a file, its two outputs go to files, and the parent polls it. Why: The Markdown policy starts the linter once per candidate, and a child that hangs, floods its output or exits early must never hang or exhaust the wrapper. Files instead of pipes need no reader or writer threads: a child that stops reading its input cannot block the parent, a child that writes more than the cap is stopped when the parent sees the file grow past it, and a grandchild that keeps an output open cannot delay the parent once the child itself has exited. Every file is an anonymous temporary file that the operating system removes when it is closed.
- candidate_
batch - Pure framing of one
git cat-file --batchreply. What: Read onegit cat-file --batchreply from a byte stream, with no process involved. Why: Object bytes are untrusted input that may contain anything, including text that looks like a reply header. The reply is therefore framed only by the header’s declared size, never by searching content, and every malformed shape is a typed failure instead of a guess. - candidate_
error - The one failure type of the candidate layer and its closed list of causes. What: The one failure type of the candidate layer, with a closed list of causes. Why: Every candidate failure stops policy evaluation (a file that could not be read was not checked), and callers branch on the cause without parsing message text.
- candidate_
object - Validated Git object names and the file modes a candidate can carry. What: The two Git vocabulary types every candidate carries: an object name and a file mode. Why: Object names are written into the object reader’s request stream, so only text proven to be a complete hexadecimal name may ever become one; modes decide whether a candidate has blob content at all.
- candidate_
prediction - What
git addwould stage, and the worktree files a direct command selects. What: The two candidate sources that need a private index: whatgit addwould stage, and the worktree files a directgit cli-git checkorfixselects. Why: Both are answered before Git touches the real index.git addis replayed on a private copy of the index, and the candidates are the entries the replay changed, as the installed wrapper’sadd-policy-facts.tsandadd-staged-delta.tsdefine them. A direct command stages its scope withgit add --allon a private copy, asdirect-check-facts.tsdoes, and every selected entry is a candidate. Either way the bytes are blobs Git hashed from the worktree at that moment, read through the one object reader, so the number of Git processes does not grow with the number of files. - candidate_
private_ index - A private,
timestamp-preserving copy of the real index,
removed when dropped.
What:
A private copy of the real index,
in its own directory beside it,
removed when
the copy is dropped.
Why:
Predicting what
git addwould stage means running it against an index nobody else reads, so the real index never changes before the policies have answered. The copy keeps the real index’s access and modification times: Git re-reads an entry whose stat data is not older than the index file, and a copy with a fresh time would make it trust stat data from the same second as a later edit of the same size, so the prediction could miss that edit (the installed wrapper’sindex-file-timestamps.ts, issue #544). - candidate_
reader - The long-lived object reader that answers every blob read of an invocation.
What:
One long-lived
git cat-file --batchprocess that answers every object read of an invocation. Why: Starting Git costs far more than reading one object, so candidate bytes are read through a single process whose count does not grow with the number of files. Blob bytes are remembered by object name: a name always denotes the same bytes, so a remembered blob can never become stale. - candidate_
record - Pure parsing of raw NUL-delimited changed-path records.
What:
Parse the NUL-delimited raw records of
git diff-indexandgit diff-tree. Why: One listing process names every changed path with its mode, object and change kind, so no per-path Git process is ever needed. Pathnames are kept as the raw bytes Git printed; they are never decoded. - candidate_
stage - Pure parsing of
git ls-files --stagerecords and the delta between two index states. What: Parse the NUL-delimited stage records ofgit ls-files --stage -z, find the paths one staging operation changed, and turn them into candidate records. Why:git addis checked before Git runs it, by replaying it on a private copy of the index. The paths it would stage are exactly those whose index records differ between the copy before and after the replay. Comparing complete records, conflict stages included, finds a resolved conflict like any other change, and an unrelated conflict elsewhere in the index never stops the comparison. Pathnames stay the raw bytes Git printed. - candidate_
store - Per-invocation listing,
lazy bytes and invalidation of candidate versions.
What:
The per-invocation owner of candidate versions and candidate bytes.
Why:
Policies read committed or staged content,
never whatever is in the live
worktree.
A version costs one listing process,
its bytes come from one
long-lived object reader,
and neither count grows with the number of files.
After a fix or replay changed the index,
invalidateretires every version. - candidate_
tree - The files of a commit’s tree with their modes and objects.
What: Parse the NUL-delimited records of
git ls-tree -r -z --full-tree <commit>: every file of one commit with its mode and object. Why: The dependent-version policy compares the candidate state withHEAD: it reads each workspace manifest’sHEADbytes, and a direct fix may change a tracked file it did not select only whenHEADholds that file as the same ordinary blob. One listing answers both for every path. Pathnames stay the raw bytes Git printed. - candidate_
version - Immutable candidate versions: identity, pathname, mode, change and object per path. What: An immutable candidate version: the paths one Git state changes, each with its identity, mode and object. Why: Every policy of one pass must judge the same files. A version is listed once and then only read, so path metadata is shared instead of re-queried, and a later fix or replay produces a new version instead of altering this one.
- capture_
invocation - The invocation capture:
repository layout, symbolic
HEAD, conclusion kind, ref backend and base. What: The transaction facts recorded before any Git mutation: the repository layout, the symbolicHEADtarget, the conclusion kind, the ref storage backend, and the preparation base. Why: Every later read ofHEADinside a transaction uses the recorded base or the landed commit, never liveHEAD, because another landing may have moved it since the invocation started. The layout query and the base read run as separate steps, so pruning sees the transaction before the base is read (src/policy-engine/commit-transaction-capture.ts,captureInvocationLayoutandcaptureInvocationBase). - capture_
order - The capture under capture order:
the capture lock, the store identity, the sequence and
captured.json. What: The capture under capture order: the per-worktree capture lock, the store identity, the sequence allocation and its durable write, the listing of the paths the capture read from the worktree, andcaptured.json. Why: Every commit transaction captures its content while holding the short per-worktree capture lock and takes the next sequence number under it, so sequence numbers give one total order of the captured disk states of a worktree. The incumbent and this wrapper share the store, so the lock, the identity and the write order must match exactly (src/policy-engine/commit-capture-order-store.ts,commit-capture-order-capture.ts). - capture_
records captured.json, landed-capture records, their recording and pruning. What:captured.json(a transaction’s position in its worktree’s capture order) and the landed-capture records, with recording after a landing and pruning. Why: Replay decides a path both a prepared commit and an earlier-landed commit captured from this worktree by capture order; a landed record is kept while any published transaction may replay over it and pruned after (src/policy-engine/commit-capture-order-journal.ts,commit-capture-order-records.ts,commit-capture-order-prune.ts).- capture_
store - The per-worktree capture store:
location,
sequence and identity.
What:
The per-worktree capture store:
its location,
its sequence file and its identity file,
read as the incumbent reads them.
Why:
Every commit transaction captures under a short per-worktree lock and takes the next
sequence number,
so sequence numbers order the captured disk states of one worktree.
Recovery and pruning read the store;
both wrappers share it
(
src/policy-engine/commit-capture-order-store.ts):<git-dir>/cli-git-captures/{capture.lock/, worktree-id, sequence, landed/<oid>.json}. - child_
environment - Variables added to every real-Git child: lock PID injection and the forward-target marker. What: The environment variables cli-git adds for every real Git it starts. Why: Native Git must write lock-owner PID files, and a wrapper must be able to notice that another wrapper selected it as “real Git”.
- command_
add - The Git 2.56.0 option table of
git addand its bulk-staging facts. What: The completegit addoption table of Git 2.56.0 and the bulk-staging facts read from it. Why: The add-explicit policy rejects staging that names no explicit path. Git accepts-Ainside any cluster (-vA), by abbreviation (--al) and through--no-ignore-removal; a list of exact tokens misses those. - command_
branch_ create - Branch-creation facts of
git branch,git checkoutandgit switch. What: Branch-creation facts ofgit branch,git checkoutandgit switchregions, read as Git 2.56.0 reads them. Why: The branch-worktree policy rejects creating a branch in the current worktree. It needs the explicit forms and the name Git may turn into a new local branch by matching a remote branch, for every spelling Git accepts. Gotcha: The facts read option names and argument counts only. Where Git later refuses for an option value, a combination of options, configuration or repository state, the answer stays “creates”: it errs toward the policy seeing the command. - command_
branch_ table - The complete Git 2.56.0 option table of
git branch. What: The complete option table ofgit branchin Git 2.56.0. Why: Whethergit branch <name>creates a branch depends on which action options were given, and whether a token is a name depends on each option’s arity. Rows followoptions[]incmd_branch(builtin/branch.c:992-1050) in source order. - command_
checkout_ table - The complete Git 2.56.0 option tables of
git checkoutandgit switch. What: The complete option tables ofgit checkoutandgit switchin Git 2.56.0. Why: Both commands create branches explicitly (-b,-c,--orphan,--track) and implicitly (a name that matches one remote branch). The tables are assembled as Git assembles them: the command’s own rows, then the shared groups (builtin/checkout.c:1786-1847, 2118-2129, 2170-2179). - command_
clean - The Git 2.56.0 option table of
git cleanand its deletion facts. What: The completegit cleanoption table of Git 2.56.0 and the deletion facts read from it. Why: The linked-worktree policy guards a clean that can delete files. Git’s last--dry-run/--no-dry-runwins, in clusters (-ndX) and abbreviations (--dry). - command_
commit - Facts about the arguments after
git commit. What: Facts about the arguments aftergit commit, read as Git 2.56.0 reads them. Why: The commit-only transform, the dry-run check and the commit transaction all decide from these facts. Every fact is the final state after Git applied each option in order, so--all --no-allreportsalloff, exactly asgit commitwould act. - command_
commit_ table - The complete Git 2.56.0 option table of
git commit. What: The complete option table ofgit commitin Git 2.56.0. Why: Declaring every option Git accepts removes the guess about whether an undeclared option consumes the next token. Rows followbuiltin_commit_options(builtin/commit.c:1705-1785) in source order, which the completion control compares. - command_
config - The form and file scope of a
git configregion. What: The form and file scope of agit configregion, read as Git 2.56.0 reads it. Why: The require-root policy exemptsgit configwhen it works on the per-user or system file, or lists. Whether--globalis that option or a value being stored depends on the form:git config user.name --globalwrites the text--globalinto the repository’s own configuration. - command_
config_ table - The Git 2.56.0 option groups of
git config. What: The option groups ofgit configin Git 2.56.0, one constant per group. Why: Git builds eachgit configsubcommand’s table from shared groups (CONFIG_LOCATION_OPTIONS,CONFIG_TYPE_OPTIONS,CONFIG_DISPLAY_OPTIONS, builtin/config.c:67-117) plus a few rows of its own. Keeping the same groups keeps one copy of every row. - command_
options - Git 2.56.0
parse-optionstokenization of one command’s argument region. What: Read one Git command’s arguments the way Git 2.56.0parse-options.creads them. Why: A policy that guesses which token is an option, a value or a path can be bypassed by a spelling Git accepts. Every per-command module declares Git’s own option table and this module walks the arguments exactly once with Git’s rules. - command_
options_ query - Final-state and position questions over a tokenized region. What: Small read-only questions over a tokenized Git argument region. Why: Git applies options in order and the last use wins, so command modules ask for final states and positions instead of counting spellings.
- command_
push - The Git 2.56.0 option table of
git pushand its facts. What: The completegit pushoption table of Git 2.56.0 and the facts read from it. Why: The atomic-push transform must know whether the caller already chose atomicity, and the manual-push gate whether the push is a dry run; both in every spelling Git accepts (--at,--no-at,-nf), which a list of exact tokens misses. - command_
reset - The Git 2.56.0 option table of
git resetand its mode. What: The completegit resetoption table of Git 2.56.0 and the mode read from it. Why: The linked-worktree policy guards resets that rewrite worktree files:--hard,--mergeand--keep. The five mode options write one variable, so only the last one counts, in any abbreviation Git accepts. - command_
stash git stashsubcommand dispatch and wrapper-flag positions. What: Whichgit stashsubcommand a region runs in Git 2.56.0, and where wrapper-only flags sit in it. Why: The linked-worktree policy guards stash; its escape hatch must be found in option position of the subcommand that actually reads the arguments, so a message or a path that spells the hatch is forwarded untouched.- command_
stash_ table - The Git 2.56.0 option tables of every
git stashsubcommand. What: The option tables of everygit stashsubcommand in Git 2.56.0. Why:git stashdispatches to subcommands that each parse their own options with their own flags (builtin/stash.c); whether a token is an option value depends on which subcommand reads it. - command_
status - The Git 2.56.0 option table of
git status, its facts and the advice-key reading. What: The completegit statusoption table of Git 2.56.0, the machine-readable fact read from it, and the reading ofadvice.statusHintsset before the subcommand. Why: The status-hints transform must not override an explicit caller choice, and the wrapper adds no note to output a program will parse. - command_
worktree - Whether a
git worktreeinvocation creates or moves a worktree. What: The one fact the wrapper needs aboutgit worktree: whether the invocation creates or moves a worktree. Why: Onlygit worktree addandgit worktree moveare sources of ignored-state synchronization, and the worktree-copy opt-out is written after that second word. - commit_
args - Argument transforms of native preparation: normalisation, global split,
-aremoval and private arguments. What: Argument transforms of native preparation: the normalisedcommitargument region, the split of global options from the commit region, the removal of-a/--all, and the arguments of the realgit committhat run against the complete private index. Why: Nativegit commitmust see the policy-settled index, not worktree bytes again, so selection controls and pathspecs are removed once the private index already holds them (src/policy-engine/commit-preparation-native.ts,commit-transaction-selection.ts,src/parser/commit-normalise.ts). The removals match the incumbent token for token, including its use oflastIndexOf, so a-mvalue equal to a pathspec is removed from the same position. - commit_
command - The
git commitcommand: the transaction, the settled events, the post-commit gate and the backup. What: Thegit commitcommand of the native wrapper: the transaction, the settled events, the post-commit gate and the automatic backup, in the incumbent’s order. Why: The incumbent (src/bin.ts) runs the commit transaction instead of forwarding the commit, prints the pre-forward events only after the transaction returned, then runs the post-commit lifecycle for a landed commit and, when the gate passes, the automatic push. A commit that landed nothing exits with the transaction’s status. - commit_
conclusion - The conclusion of a settled commit transaction: preparation, landing and post-landing completion.
What:
The conclusion of a settled commit transaction:
the normalization-only landing when the corrections removed every change, otherwise the hook dispatcher,
native preparation in the shadow, the prepared record, the landing loop and the post-landing completion.
Why:
The incumbent’s
commit-transaction-conclusion.tsruns these steps in this order: the tree is written from the stable private index, the dispatcher shim is shared by preparation and the post-landingpost-commit, the prepared record is durable before the preparation marker, and the transaction directory is removed only after the worktree copies are installed. - commit_
convergence - Slice 6: the commit path’s bounded policy convergence over the private index.
What:
The commit path’s bounded policy convergence over the private commit index:
each changed pass writes its corrections into the index,
then the whole ordered pass restarts until it proposes nothing.
Why:
The commit path must end with the corrections the policies settled on,
and must stop when they repeat a state or do not settle.
The loop is the shared
converge; this module supplies the commit path’s three operations, as the direct fix does for the worktree. Exact prior states are compared in memory; the incumbent also streams them to snapshot files, which only serve the same comparison. - commit_
facts - Rule facts from the real repository with candidates read from the private index.
What:
The repository facts of a commit transaction:
every rule fact is read from the real repository, and the candidate files are read from the private index
and the shadow object store against the recorded base.
Why:
The incumbent’s rules (
commit-only,commit-index,commit-sequencer) ask real Git about the real index and the real sequencer state, while the content policies read the private commit index that the transaction corrects. Pointing every fact at the private index would answer the rules about a state the user never staged. - commit_
index - The private commit index: copies, selection modes, pathspec files and listings.
What:
The private commit index of one transaction:
its timestamp-preserving copies of the real index,
its initialization for each selection mode,
the intended tree written from it,
its path listings,
the pathspec-file source, and
interactive and patch selection run against it.
Why:
Git’s staging and selection run against a private index and the shadow object store,
so the real index and object store change only at landing.
A copy keeps the real index’s timestamps, because Git re-reads a stat entry
whose time is not older than the index file (
index-file-timestamps.ts, issue #544). - commit_
landing_ lock - The landing lock a landing attempt takes, with the recovery every acquisition runs first.
What:
The per-worktree landing lock a landing attempt takes, and the recovery every acquisition runs first.
Why:
A crashed landing must be resolved before another landing moves the ref or the index, so the lock is acquired
and then dead landings are recovered under it (
src/policy-engine/commit-landing-lock.ts). An invocation nested under the holder, proven by an inherited landing lease, proceeds under its ancestor’s lock. - commit_
landing_ loop - The landing loop with replay, revalidation and the starvation reservation.
What:
The landing loop: land, and after each lost race replay and revalidate outside both locks, until the commit
lands, its replay conflicts, revalidation rejects it, or the target cannot take it.
Why:
Each replay rebuilds the prepared commit from the preparation base onto the target that won, so every replay
starts from the same prepared change. After
landing.reserveAfterLostRaceslost races the transaction takes the landing reservation before it replays, and every landing attempt yields to another transaction’s live reservation; the reservation is released when the loop ends (src/policy-engine/commit-landing-loop.ts). - commit_
lifecycle - Slice 9 skeleton: the order around a landed commit and a guarded Git child; not reachable until landing is merged.
What:
The call order around a guarded Git child:
install the signal guard, run the child, release the locks and the transaction, drop the guard,
then exit with the child’s code.
Why:
The incumbent releases the transaction before the signal guard goes away, so a signal sent during
release is still relayed to the child.
The order after a landed commit (automatic maintenance, then the post-commit hook) is not here:
the incumbent runs maintenance without stopping on failure (
src/policy-engine/commit-transaction-conclusion.ts), and the post-landing lane owns that sequence. Nothing calls this yet: the command path keeps refusinggit commituntil landing is merged. - commit_
no_ change - The normalization-only transaction for a correction that removed every change.
What:
The normalization-only transaction: a correction whose complete intended tree equals the recorded base lands as
a reconciliation of the index and the worktree copies, without an empty commit.
Why:
The incumbent’s
completeNoChangeTransactionlands the normalization through the landing critical section against the recorded base, so a base that moved since invocation fails without touching the index (src/policy-engine/commit-transaction-no-change.ts). - commit_
preparation - Native
git commitin the shadow, the prepared commit read back andprepared.json. What: Nativegit commitin the shadow repository against the private commit index, the prepared commit read back from the shadow, andprepared.json. Why: Git owns the hooks, the editor, the templates, the message cleanup and the signing; this module only points Git at the shadow, the private index and the hooks directory. The hooks directory and the silenced hook events come from the hook dispatcher, which this lane does not build: they arrive throughPreparationHooks. A failing preparation removes the shadow so no shadow outlives the failure (src/policy-engine/commit-preparation-native.ts,src/shadow-repository/shadow-repository.ts). - commit_
recovery - Startup recovery of interrupted commit transactions.
What:
Startup recovery of interrupted commit transactions for the invocation’s worktree.
Why:
Before configuration loads,
and before read-only commands that already asked Git for the
repository location,
every dead owner’s transaction is recovered or fails closed;
live
owners are skipped.
The legacy single-journal directory is not recovered here:
the
command stops and says how to recover it with the previous executable (the owner’s
verdict,
doc/planning/cli-git-rust-open-decisions.md, “Legacy single-journal directory”). An empty registry costs one directory read and no Git process (src/policy-engine/commit-transaction-recovery.ts). - commit_
replay_ options - The replay-relevant commit options.
What:
The commit options a replay repeats outside native
git commit: whether hooks were bypassed with--no-verify, and the key a signed commit is re-signed with. Why: A replay rebuilds the prepared commit on a moved parent, so it must sign the same way and must know whetherpre-commitis allowed to run again (src/policy-engine/commit-replay-options.ts). - commit_
revalidation - Revalidation of a replayed tree outside both landing locks.
What:
Revalidation of a replayed tree, outside both landing locks:
a private index for the replayed tree, the shadow
HEADat the replay parent, the policies converged again over that index, andpre-commitre-run whenever the tree differs from the one it last approved. Why: A replay merges changes the policies and the hook never saw together, so the merged tree must pass the same gates as the prepared one (src/policy-engine/commit-revalidation.ts). - commit_
transaction - The commit transaction in the incumbent’s order, up to the settled policies.
What:
The commit transaction in the incumbent’s order, up to the settled policies:
layout capture, the published workspace, the preparation base,
preparing.json, the shadow repository, the private index under capture order, interactive selection, and the bounded policy convergence. Why: The incumbent (src/policy-engine/commit-transaction.ts) captures before it mutates, publishes the directory before the base is read, and takes the landing lock only inside the landing attempt, never through preparation: holding it here would serialize concurrent commits. The conclusion (commit_conclusion.rs) takes the settled state to a landed commit. - commit_
workspace - The disposable, recoverable workspace of one commit transaction.
What:
The disposable, recoverable workspace of one commit transaction:
a published transaction directory with its owner record, the shadow repository path, and disposal that leaves
everything in place once a landing record handed it to recovery.
Why:
The incumbent’s
createCommitTransactionWorkspace(src/policy-engine/commit-transaction-workspace.ts) publishes the directory before any Git mutation, so recovery can skip a live owner and retire a dead one, and disposal removes the shadow before the directory so no shadow outlives the journal that names it. - commit_
worktree_ records - The worktree completion records of selected corrections and of hook changes.
What:
The worktree completion records a landed commit owes the worktree:
selected final-newline corrections, and the paths a
pre-commithook re-staged. Why: The commit keeps what the private index holds, so the real worktree still holds the bytes the user wrote. After the landing, each recorded file takes the committed bytes only while it still holds its earlier bytes (src/policy-engine/commit-transaction-selected-worktree.tsandcommit-hook-changes.ts). - conclusion_
cleanup - Native conclusion cleanup reproduced in the owning worktree.
What:
Native conclusion cleanup reproduced in the owning worktree’s Git directory after a
merge,
cherry-pick or revert conclusion lands,
and the names of that state.
Why:
Native
git commitremovesMERGE_HEADand its siblings in the shadow; the real worktree must end the same way, but an entry is removed only while it still holds the bytes or value copied at preparation, because a changed entry belongs to a later command (src/shadow-repository/shadow-conclusion-cleanup.ts,-files.ts,-names.ts). - conclusion_
copy - The shadow conclusion copies at preparation.
What:
The merge, cherry-pick and revert conclusion state copied into the shadow at preparation,
and into the transaction’s comparison copies.
Why:
Native
git commitin the shadow reads the conclusion state the real worktree holds at invocation. Landing later compares the real entries with these copies, so an entry changed by another command after invocation is kept (conclusion_cleanup.rsholds the landing side). Pseudorefs of a reftable repository live in the ref store and are copied withupdate-ref(src/shadow-repository/shadow-conclusion-state.ts). - config_
error - The one failure type for rejected configuration content or files.
What:
The one failure type for rejected
cli-git.config.jsonccontent or files. Why: Callers render a single configuration diagnostic without parsing message text. - config_
file - Repository-root discovery and bounded reading of
cli-git.config.jsonc. What: Find and readcli-git.config.jsoncat a repository root. Why: Configuration is one data file at one fixed place; legacy executable configuration is reported for migration and never read, run or silently ignored. - config_
loading - Lazy configuration loading for known inspection versus mutation/ambiguous commands. What: Policy-configuration loading decisions before expensive policy initialization. Why: Known inspection forms keep the fast path; mutations, aliases and ambiguous options require configuration.
- config_
parse - Whole-document JSONC validation into typed settings.
What:
Turn
cli-git.config.jsonctext into validated typed settings. Why: Every unknown key, wrong type or ambiguous duplicate is rejected before any policy or transaction reads a setting. - config_
rules_ file - The
rulesFileoption: a repository-relative name that stays inside the repository. What: TherulesFileoption ofsecurity/forbidden-strings: which file in the repository holds the scanner’s private rules, written relative to the repository’s top level. Why: The user decided on 2026-10-05 that configuration names the rules file first, thenFORBIDDEN_STRINGS_RULES, then the default file. A configured name is checked when the configuration is read: it must stay inside the repository by its words alone, so a configuration cannot point the scanner at a file elsewhere on the machine. A name that is missing on disk is not a configuration error; loading the rules reports it when the policy runs. - config_
schema - Typed settings and their defaults.
What:
The typed settings a validated
cli-git.config.jsoncproduces, with defaults. Why: The policy engine and transactions read plain typed values, never JSON. - dependent_
version_ bundled - Which development dependencies each dependent bundles.
What: Decide which development dependencies each dependent bundles, reading source
only where an edge could carry a raised version.
Why: A development dependency ships inside a dependent only when its non-test source
imports it. Scanning every package’s source on every commit would read the whole
workspace, so, as in the incumbent (
dependent-bump-workflow.ts:176-278), only dependents that could reach a raised package if every development dependency were bundled are scanned, and only for edges that lead toward a raised package. - dependent_
version_ content - The repository content the dependent-version planner reads, and its two failure kinds.
What: The repository content the dependent-version planner reads, behind one small
interface, and the two ways planning can fail to finish.
Why: The planner is pure with respect to the repository: it never starts Git and never
reads the filesystem. The policy’s wiring answers from the wrapper’s candidate
layer, the release path from the worktree, and tests from memory. Failures are
reported by cause: content that could not be read is
content-unavailable; content that was read but that the planner cannot use ispolicy-incomplete. - dependent_
version_ graph - The dependency walk and the plan of dependent bumps.
What: Walk the workspace dependency graph from the packages whose version was raised and
plan a patch bump for every publishable package that reaches one of them.
Why: A dependent’s published contents change when a dependency it ships with changes,
so its version must move too. This is the incumbent’s closure and filter
(
dependent-version-bump.ts:183-350): edges to names outside the workspace and self edges are ignored, the walk passes through unpublished packages and cycles, and only publishable, versioned, not-yet-raised packages are bumped. - dependent_
version_ imports - Whether source text imports a package.
What: Decide whether source text imports a package or one of its subpaths.
Why: A development dependency is bundled, and so carries a bump, only when non-test
source imports it. This is the incumbent’s specifier scan (
source-imports.ts:59-264): a quoted specifier equal to the name, or starting with the name and/, directly afterfrom,import,import(orrequire(. Every character it inspects is ASCII, so scanning UTF-8 bytes decides exactly what scanning UTF-16 units decides. - dependent_
version_ lifecycle - The dependent-version planner’s view of a prepared candidate state.
What: The dependent-version planner’s view of a prepared candidate state: every tracked
file of the private index, its bytes with this invocation’s corrections laid over
them, and the
HEADcommit’s copy of a file. Why: The planner reads workspace manifests, the registry configuration and source files wherever they are, not only the candidates; the installed wrapper’s policy reads them as tracked files of the candidate state (trackedFiles,bytes,headBytes). Everything goes through the store the candidates were prepared in, so a direct fix’s later pass reads what its earlier passes corrected. - dependent_
version_ manifest - Strict JSON reading of a manifest’s name, version and dependency names.
What: Read the facts a dependent bump needs from one
package.json: its name, its version, and the workspace names it depends on. Why: The incumbent parses manifests withJSON.parse(manifest-text.ts:130-161). This module accepts exactly JSON: it parses with the crate’s JSONC parser and first refuses the two things JSONC adds, comments and trailing commas. Keys followJSON.parse: the last duplicate wins. A manifest is decoded as strict UTF-8 after one optional byte-order mark, as the incumbent’s fatalTextDecoderdoes. - dependent_
version_ paths - Which paths the dependent-version planner reads: manifests, configuration, source.
What: Which repository paths the dependent-version planner reads: workspace manifests,
the registry configuration, and a package’s non-test source files.
Why: The planner selects these from the tracked-path list itself, so the provider needs
no pathspec support. The rules are the incumbent’s:
isWorkspaceManifestPath(dependent-version-bump-policy.ts:66-73), the manifest pathspec:(glob)package/*/*/package.json,PNPR_CONFIG_PATH(publishable-names.ts:10) andisNonTestSourcePath(source-imports.ts:38-55). - dependent_
version_ plan - The dependent-version plan with exact manifest bytes before and after.
What: Plan patch bumps for publishable dependents of packages whose version differs from
the base, with the exact manifest bytes before and after each bump.
Why: This is the one planner behind both entry points: the commit-time policy and the
release workflow’s ripple, which will call the native wrapper’s direct fix. It is
the incumbent’s
planWorkspaceBumps(dependent-bump-workflow.ts:280-388) over the content seam: it starts no Git process and reads no file. - dependent_
version_ policy - The decision and findings of
mono/dependent-version-bump. What: The decision ofmono/dependent-version-bump: when to plan, and the findings a plan becomes. Why: The plan is a pre-commit step. Another forwarded command such asgit addcould not apply its patches, so it plans only forgit commitamong forwarded commands, and forgit cli-git checkandgit cli-git fix; and only when a workspace manifest is modified, so ordinary commits read nothing. Each stale dependent is one finding whose patch rewrites a tracked manifest that may not be a candidate, which adds that manifest to the commit (dependent-version-bump-policy.ts:178-235). - dependent_
version_ publishable - Publishable package names from the generated registry configuration.
What: Read the publishable package names from the generated registry configuration
(
package/config/pnpr/config.yaml). Why: Only published packages receive a dependent bump. The generator writes each name as a single-quoted YAML list item on its own line underpackages:, so a line scan reads exactly what it wrote, as the incumbent does (publishable-names.ts:38-81). Lines are trimmed with JavaScript’sString.prototype.trimset of characters. - dependent_
version_ release - Patch bumps of plain release versions and
JSON.stringifyquoting. What: Patch-bump a plainmajor.minor.patchrelease version, and quote text the wayJSON.stringifydoes. Why: A dependent receives a patch bump. Only a plain release can be bumped automatically; anything else (a prerelease, build metadata, a leading zero, a missing component) is reported for a bump by hand, with the incumbent’s message (dependent-version-bump.ts:87-177). The patch component is incremented as a decimal digit string, so it is exact at any size. - dependent_
version_ states - Every workspace manifest at both states, and which versions were raised.
What: Read every workspace manifest at the candidate state and at the base, and decide
which packages’ versions were raised.
Why: The plan starts from these facts. The order of failures follows the incumbent’s:
every manifest is decoded before any is parsed (its reader decodes while listing,
dependent-version-bump-policy.ts:82-92), and each manifest’s base text is parsed before its current text (dependent-bump-workflow.ts:150-174). - dependent_
version_ text - The byte-preserving rewrite of a manifest’s top-level version.
What: Rewrite the top-level
versionvalue in a manifest’s text, leaving every other byte unchanged. Why: The bump is delivered as a full-content patch, so indentation, key order, line endings and the trailing newline must survive exactly. This is the incumbent’s scanner (manifest-text.ts:184-333) over bytes: it tracks{/[depth outside strings, finds the first"version"key at depth one, and requires its raw value to be exactly the JSON quoting of the expected version. - diagnostic_
log - Line-oriented debug and warning diagnostics on standard error.
What:
Line-oriented debug and warning diagnostics on standard error,
switched like the
incumbent’s console sink.
Why:
The incumbent prints debug records only when
MONOCHROMATIC_VERBOSE=trueand warnings unlessMONOCHROMATIC_WARN=false(package/module/logger/src/sink/console.ts), and its spec requires that diagnostics never corrupt a JSONL stream: each diagnostic is one complete line written in one call (SPEC.md, “Debug logging”). - diagnostics
- JSONL event rendering for wrapper-made diagnostics. What: Render wrapper diagnostics as the JSONL events callers already parse. Why: Agents and hooks read cli-git’s event stream line by line; every wrapper-made event must be one compact JSON object per line with stable field order.
- direct_
fix - The direct fix:
converge corrections in memory,
then install them.
What:
git cli-git fix: run policy passes over the selected files, apply the corrections they propose in memory until a pass proposes none, then install the corrected files into the worktree when that last pass is clean. Why: The installed wrapper’sdirect-fix.tsanddirect-fix-convergence.tsdecide the behaviour: only the last pass is reported, a fix summary follows it only when a file changed, nothing reaches the worktree unless the last pass exits 0, and a fix that cycles or keeps changing ends with one engine failure. The bounded loop itself ispolicy_convergence::converge; this module supplies its passes. Corrections replace whole file contents in memory instead of being staged into the private index, so a pass starts no Git process for them. - direct_
fix_ install - Installing a direct fix’s corrections into the worktree,
all or none.
What:
Install the corrections a converged
git cli-git fixsettled on into the worktree, all of them or none, and never touch the index. Why: The installed wrapper’sdirect-fix-install.tsdecides this protocol: every corrected file must still hold the bytes the fix read, each replacement is written beside its file and renamed over it, a copy of the old file is kept until the end, and the real index must hold the same bytes afterwards. Any failure restores every file already replaced. Failure messages name no file, because a pathname can hold the text a scan exists to keep out of output. - effective_
target - Worktree target classification for worktree-enforcing policies. What: Classify the worktree an invocation targets for worktree-enforcing policies. Why: Linked-worktree safeguards discipline a person’s repositories, not the disposable clones a tool keeps in its own cache.
- entry
- The per-invocation decision the thin executable performs.
What:
Decide what one
gitinvocation of the native wrapper does, and perform it. Why: The executable stays thin: it gathers process facts, asks this module for an action, and performs it. The decision itself is testable without replacing the test process. - escape_
hatch - Spellings of the wrapper-only escape hatches shared by several modules.
What:
The spellings of the wrapper-only escape hatches several modules share.
Why:
A command module puts its own hatch first in the flag list it hands the option
tokenizer,
and the control table lists the same spellings;
one constant per
spelling keeps both in step.
Removal is not done here:
wrapper_invocation.rsdeletes wrapper tokens by the positions the tokenizer reports. - event_
path - A pathname as events report it:
text,
plus base64
pathByteswhen it is not UTF-8. What: A repository path as an event reports it: readable text, and the exact bytes as well when those bytes are not UTF-8. Why: Git allows any bytes in a file name, and a JSON string can hold only text. An event keepspathas text with replacement characters, so every existing reader works unchanged, and adds an optionalpathBytesfield, base64 encoded, only for a name that is not UTF-8, so a reader that needs the file can still find it. This is option C ofdoc/planning/cli-git-rust-open-decisions.md, section “Non-UTF-8 paths in events and journals”, which the owner adopted. - filesystem_
identity - The filesystem identity recorded with every real
index.locka transaction creates. What: The filesystem identity recorded with every realindex.locka transaction creates, spelled exactly as the incumbent’s@monochromatic-dev/module-fs-idspells it. Why: Recovery removes a realindex.lockonly when its device, inode and filesystem identity all match the journal; the incumbent compares the recordedfsIdwith the one it resolves now, so both wrappers must resolve the same text for the same filesystem (package/module/fs-id/src/platform-resolvers.ts,parsers.ts). - forwarded_
command - The Git command a forwarded invocation runs,
after ordinary alias expansion.
What:
The Git command a forwarded invocation actually runs,
after ordinary alias expansion.
Why:
Git expands an alias only when no built-in has that name,
reads it from
alias.<name>oralias.<name>.command, splits it withsplit_cmdlinequoting, lets its leading words be global options, and expands again when the result is an alias. Index-writer coordination and the commit rules decide from the expanded command, sogit -c alias.c=commit cis a commit (src/forwarded-command.ts). - forwarding
- Starting real Git with unchanged arguments, streams and exit status. What: Start real Git with the caller’s exact arguments, streams and environment. Why: The wrapper adds policy around Git; it must never change what Git receives or what the caller observes from Git.
- git_
builtins - The names of the commands built into Git 2.56.0.
What:
The names of the commands built into Git 2.56.0,
and the test “is this word one
of them”.
Why:
Git never expands an alias whose name is a built-in command,
so a built-in word
is the command that runs.
Any other word may be an alias for anything,
including
worktree add, and resolving aliases is not ported; the caller needs to know which of the two it is looking at without starting Git. - git_
metadata - Captured read-only real-Git queries over raw bytes. What: Run a read-only real-Git query and capture its output as raw bytes. Why: Repository facts (top level, Git directory) come from Git itself, and the paths it prints need not be UTF-8.
- git_
version - The Git version check run once before work that depends on Git’s behavior.
What:
The real Git version check,
run once per invocation before work that depends on Git’s
behavior:
a commit,
hooks and locks.
Why:
The native tables and protocols follow Git 2.56.0,
and the
owner settled on 2.55.0 as the oldest release that runs the swap,
so 2.55.0 or newer counts as supported and that read-only commands are never checked
(
doc/handover/cli-git-rust-implementation.md, “Adopted without a question”). - global_
arguments - Git 2.56.0 global-argument boundaries over unchanged operating-system strings. What: Locate Git 2.56.0 command boundaries without reconstructing native arguments. Why: Policy stages need the command index, but Git remains authoritative for option validation and -C semantics.
- hook_
dispatch - The hook dispatcher entered by a transaction’s hook entry name, under the hook lock.
What:
The hook dispatcher: a native entry started by a preparation hook name inside a transaction’s
hooksdirectory, which runs the repository’s hooks for that event under the hook lock. Why: Git starts the entry throughcore.hooksPath; the start name selects the event. The incumbent’sdispatch.mjs(src/hook-dispatch/hook-dispatch-program.ts) does the same work, and both take the hook lock at<common>/cli-git/hook.lock, so they serialize on one lock. Any other start name, includingpost-commitor a hook name outside a transaction, behaves asgit. - hook_
dispatch_ entries - The hooks directory of one transaction: its plan and one entry per preparation event.
What:
The hooks directory of one commit transaction:
its
plan.jsonand one entry per preparation event. Why: Git starts a hook throughcore.hooksPathby the entry’s path, so the entry’s name is the event and the executable it starts is the native wrapper. On Windows Git looks for<event>.exe, so entries are hard links or copies with that name. - hook_
dispatch_ plan - The dispatch plan of one commit transaction, its JSON spelling and the config reads it makes.
What:
The dispatch plan of one commit transaction:
which hooks run, how they run, and under which lock.
Why:
Inside native
git committhe hook entries see only the transaction’s overrides, so the plan is computed by the wrapper from the real repository and the caller’s environment. The incumbent (src/hook-dispatch/hook-dispatch-plan.ts) and both dispatchers read the sameplan.json, so its field order and spelling are part of the on-disk format. - index_
lock_ evidence - Evidence about a foreign
index.lockand the verdict it supports. What: Evidence about a foreignindex.lockand the verdict it supports: proven alive, dead, or evidence-free. Why: A live owner is proven by a process holding the lock open (device and inode), or by Git’score.lockfilePidfile naming a running process that started no later than the lock changed. A PID file naming a gone process proves a dead owner. Everything else is evidence-free; an absent open holder never proves abandonment, because nativegit commitkeepsindex.lockwithout a descriptor through its hooks and editor (src/index-lock/index-lock-evidence.ts). - index_
lock_ holder_ parsers - Parsers of
lsof -F pcDirecords and Restart Manager lines, matched by device and inode. What: Parsers of the holder-scan output that is not/proc:lsof -F pcDirecords and the Restart Manager script lines, both matched to a lock by device and inode. Why: The parsers are pure text functions, so their controls run on every host, whichever scan produced the text (src/index-lock/index-lock-holders-darwin.ts,-win32.ts). - index_
lock_ holders - Processes holding a lock file open,
found by device and inode.
What:
Processes holding a lock file open,
found by device and inode:
/proc/<pid>/fdon Linux,lsofon macOS, the Restart Manager on Windows. Why: An open descriptor proves a live owner. Matching by device and inode, never by path text, means bind mounts and renamed paths cannot fake or hide a holder. Processes the scan cannot read are partial evidence (src/index-lock/index-lock-holders-linux.ts,-darwin.ts,-win32.ts). - index_
lock_ wait - Waiting for a foreign index.lock under the classification rules and Git’s backoff schedule.
What:
Waiting for a foreign
index.lockunder the classification rules: a proven-alive owner is waited for without limit, a dead or evidence-free owner is retried on Git’s backoff schedule until the unproven budget runs out. Why: An index writer must not run beside a landing, and a lock nobody can prove alive must not block forever. Nothing here deletes a lock; the caller decides what the failure means (src/index-lock/index-lock-wait.ts). - index_
lock_ wait_ host - The host side effects of the index-lock wait:
/proc reads,
sleeps,
randomness and standard error.
What:
The host side effects of the index-lock wait:
/procand Git-free lock reads, real sleeps, operating-system randomness and standard error. Why:WaitEffectsis the seam the controls replace; this is what the executable runs. - index_
writer_ commands - Index-writer classification and the commands that coordinate with a landing.
What:
Whether a resolved forwarded command writes the real index.
Why:
Index writers take the landing lock so they never interleave with a landing commit.
The set follows
SPEC.md“Index-writer coordination”:add,rm,mv,restore --staged,resetexcept--soft,stash,checkout,switch,merge,rebase,cherry-pick,revert,apply --cachedandapply --index,update-index,read-tree,am,pullandsparse-checkout. Options count only before Git’s--; long options match in full or as the abbreviations Git accepts (src/index-lock/index-writer-commands.ts). - index_
writer_ coordination - Coordination of a forwarded index writer:
the landing lock,
the wait for a foreign lock and the lease.
What:
Coordination of a forwarded index writer with concurrent landings:
the landing lock is taken
after recovery,
a foreign
index.lockis waited for under the classification rules, and the lock is handed to real Git through the landing lease until Git returns. Why: An index writer must not interleave with a landing commit, and a nested wrapper invoked by the coordinated Git (for examplegit rebase --exec 'git add') must not wait on the lock its ancestor holds (src/index-lock/index-writer-coordination.ts). - index_
writer_ forward - Running a coordinated forwarded index writer:
the landing lock held while real Git runs as a child.
What:
Running a forwarded index writer under its landing lock:
coordinate,
run real Git as a child
that inherits the landing lease,
release the lock once Git returns.
Why:
Replacing the process would drop the lock with the process image,
so a coordinated writer
waits for Git instead.
The nested wrappers Git starts see the lease and do not wait on it
(
src/forward-real-git.ts). - invocation_
config - Configuration of the repository one invocation selects.
What:
Load the policy configuration of the worktree one invocation runs in,
and render
the events configuration loading can produce.
Why:
Both wrapped Git commands and
git cli-git check/fixneed the same answer: what does thecli-git.config.jsoncof the worktree Git reported say. - iso_
time - UTC timestamps in the exact form of JavaScript’s
toISOString. What: UTC timestamps in the exact form of JavaScript’sDate.prototype.toISOString:YYYY-MM-DDTHH:MM:SS.mmmZ. Why: Transaction owner records carrycreatedAtand capture recordsinvokedAtin this form; recovery orders dead transactions and the reservation grants turns by comparing these strings, so both wrappers must write the same shape. - js_text
- Text operations with the exact semantics of JavaScript’s built-ins.
What:
Text operations with the exact semantics of the JavaScript built-ins the incumbent
applies to strings it shares with the native wrapper.
Why:
Durable strings such as process birth identities are produced by
.trim()in the incumbent; the native wrapper must produce byte-identical strings, and Rust’sstr::trimuses a different whitespace set (it trims U+0085 and keeps U+FEFF). - json_
record - Compact JSON records read and written with the incumbent’s
JSONsemantics. What: Reading and writing the compact JSON records both wrappers share, with the incumbent’sJSON.parseandJSON.stringifysemantics. Why: Lock, owner, journal and capture records written by either wrapper are read by the other. A record must be read the way the incumbent reads it (numbers are doubles, a whole number in safe-integer range is an integer, the last duplicate key wins) and written with the same field order and escaping. - landed_
disposal - Disposal of a landed transaction: shadow removal, directory removal, then landed-capture pruning.
What:
Disposal of a landed transaction: its shadow repository, then its transaction directory,
then the landed-capture records no published transaction still needs.
Why:
The incumbent’s
commit-transaction-workspace.tsruns these steps in this order. A shadow never outlives the directory that names it, and pruning runs last, so the last transaction to finish sees no other transaction pinning a landed record. A failed removal stops the sequence, so recovery still finds the directory and its records. - landing_
attempt - One landing attempt inside the landing critical section, and its outcome.
What:
One landing attempt inside the landing critical section:
the real
index.lock, the branch-switch and head-moved checks, object migration, the post-index,landing-<n>.json, the compare-and-swap with the nonce reflog message,ref-updated.json, the index install, theindex-installedmarker and conclusion cleanup. Why: A landing is serial under the landing lock and the realindex.lock, and it journals every state so recovery can finish or discard a landing that died anywhere inside (src/policy-engine/commit-landing.ts). The caller takes the landing lock, checks the starvation reservation and replays a moved target; the caller’s post-landing step disposes of the transaction directory. This module never runs hooks, the editor, signing, network operations or the hook lock. - landing_
findings - The
concurrent-commitfinding for a landing that landed nothing. What: Theconcurrent-commitfinding for a landing that landed nothing becauseHEADwas switched or the target moved. Why: Both leave the ref, the real index and the worktree bytes unchanged and end the commit with exit status 1 (src/policy-engine/commit-landing-findings.ts). This module only builds the event and names the exit status; the commit path that renders it is a later slice. - landing_
index - Post-index a landing installs, computed against the real index at landing.
What:
The real index a landing installs, computed against the real index as it is at landing,
never against the invocation-time copy.
Why:
A landing that copied the invocation-time index would erase another process’s staging
and stage a revert of content that already landed. Index mode takes the exact private index only
when nothing was restaged; otherwise it merges the landed entries path by path, and explicit-path
mode resets only the committed paths and the guarded paths still holding their invocation entry
(
src/policy-engine/commit-landing-index.ts). - landing_
location - The landing location a forwarded index writer coordinates with:
the real index and the registry.
What:
Where a forwarded command’s landing lock and real index live:
one
git rev-parsethat reports the git directory, the index Git would use, and the transaction registry. Why: An index writer coordinates with landings through the registry’s landing lock and the real index’s lock. A command outside a repository, or one writing another index, coordinates with nothing (src/index-lock/landing-location.ts). - landing_
objects - Migration of a prepared commit’s shadow objects into a kept pack of the real store.
What:
Migration of a prepared commit’s shadow-only objects into the real store as one pack,
optionally kept by a
.keepfile naming the transaction. Why: The real target may name the commit only after the compare-and-swap, so its objects must be in the real store first, and the keep stops prune or repack from removing them mid-landing (src/policy-engine/commit-landing-objects.ts). - landing_
support - Landing target comparison, nonce reflog message and compare-and-swap.
What:
Target comparison, the nonce-bearing reflog message and the compare-and-swap a landing uses
to advance its target.
Why:
The reflog subject carries the transaction nonce, so recovery can tell whether the target
advanced to this landing even after a crash (
src/policy-engine/commit-landing-support.ts). The swap runs in the owning worktree’s context, which also writes the realHEADreflog. - maintenance_
decision - Automatic maintenance decision: config record parsing and the enable and detach rules.
What:
The decision behind automatic maintenance after a landing:
parsing
git config --null --get-regexprecords, and choosing whethergit maintenance run --autoruns and whether it detaches. Why: Nativegit commitruns this step after updatingHEAD; a landing replaces that commit with plumbing, so the pack count grows by one per commit unless this step runs. The rules mirror Git’sprepare_auto_maintenanceinrun-command.c, including JavaScript number semantics for values the incumbent reads withNumber(...). - maintenance_
hk_ config - Exact removal of retired hk hook registrations from one explicit Git configuration scope.
What:
Exact removal of retired hk hook registrations from one explicit Git configuration scope:
list every scoped key with
git config --list --show-scope, keep the sorted unique keys of that scope that start withhook.hk-, and unset each with--unset-all. Why: The retired hk installer wrote its hooks into Git configuration; this repository’s migration removes only that exact namespace and leaves every other key alone. - maintenance_
run - Running automatic maintenance after a landing: the config read, then
git maintenance run --auto. What: Running automatic maintenance after a landing: onegit configread for the maintenance keys, thengit maintenance run --auto --quiet, detached or not, in the real worktree. Its exit status is returned for the caller to ignore, as native Git does. Why: Every landing migrates its objects into one pack; without this step the pack count grows by one per commit and every later Git process searches every pack. Failures are logged and never fail the landed commit. - management
- The
git cli-gitdecision: help, retired commands, and directcheck/fix. What: Decide what onegit cli-git ...invocation does. Why: Help, refusals and retired commands need no repository;checkandfixvalidate their policy selection and configuration, then run one policy pass over the selected repository and report its events on standard output. - management_
arguments - The
git cli-gitargument grammar. What: The grammar ofgit cli-git <command>, the wrapper’s own management namespace. Why:checkandfixrun policies over an explicit scope;trust,untrustandstatusare retired with executable configuration and only explain that. - manual_
push_ lifecycle - The manual-push candidate scope of one push: the commits and trees the destination lacks.
What:
The manual-push candidate scope of one
git push: the commits and trees the destination lacks. Why: The manual-push policies (final-newline,forbidden-strings) scan the content a push publishes. The incumbent (src/policy-engine/manual-push-lifecycle.ts) discovers the updates, bounds each by what its destination already has, and hands the scan to the policy engine; a failure stops the push with exit2. - manual_
push_ probe - Authoritative manual-push updates discovered by a private dry-run push and confirmed with ls-remote.
What:
The authoritative ref updates a
git pushwould send, discovered by a private dry-run push. Why: The manual-push policies must scan exactly what Git would publish, so the probe asks Git: a privatepre-pushhook in a private directory records the update lines Git negotiated (src/policy-engine/manual-push-hook.ts), andgit ls-remote --refsconfirms each destination value. A remote ref that changed between negotiation and confirmation stops the probe. - manual_
push_ range - Published-set and range computation for manual-push scans: which pushed commits a remote does not have.
What:
The commits a push newly publishes, relative to what each destination remote already has.
Why:
The manual-push policies scan only what becomes public. A pushed commit is already published when a
remote-tracking ref of the destination, or an authoritative prior value of an update to that remote,
reaches it (
src/policy-engine/manual-push-published.ts). The rest of the history is the scan’s range. - markdown_
exclude - The gitignore-syntax
excludepatterns ofmarkdown/autofix, matched as the linter matches them. What: Theexcludeoption ofmarkdown/autofix: gitignore-syntax patterns, relative to the repository’s top level, naming candidates the policy leaves alone. Why: The installed wrapper filtered candidates with theignorepackage before starting the linter (rewrite-candidates.ts,rewriteCandidates), so an excluded file never starts a linter and never fails for one. The linter evaluates its own copy of the option with theignorecrate’s gitignore matcher and a walk over ancestor directories (package/linter/monochromatic-lint/src/markdown_lfs_patterns.rs); this module uses the same matcher and the same walk, so both agree on every path. - markdown_
linter - The executable’s linter for that policy:
monochromatic-linton PATH, bounded, once per candidate. What: The linter the executable givesmarkdown/autofix:monochromatic-lint, found on the invocation’s PATH and started once per candidate asmonochromatic-lint --config=<file> --stdin --stdin-filename=<path> --fixfrom the repository’s top level, with a time bound and output caps. Why: This is the plan’s stream contract (doc/planning/unified-linter.md, section “Distribution and consumers”;doc/planning/cli-git-rust-implementation.md, lines 181 to 189). The program and its one-rule configuration are found and written once per invocation, when the first candidate needs them, so a command with no Markdown candidate never needs the linter. Every option is written as one--name=valueargument, so a pathname that starts with-stays a value. - markdown_
linter_ config - The one-rule linter configuration and the private temporary file it is written to.
What:
The linter configuration the
markdown/autofixpolicy handsmonochromatic-lint: one block that selects the policy’s rules for Markdown and MDX files, written once per invocation to a private temporary file outside the repository. Why: The plan’s contract (doc/planning/unified-linter.md, section “Distribution and consumers”) is a one-rule configuration in a temporary file created with a proper temporary-file call, passed with--config, so a configuration the repository keeps for its own linting never changes what the commit-time policy does. Each rule runs atwarn, so a finding the fixes leave is a record on standard error and exit status 0, and only a run that could not finish exits non-zero. The document is built as a value and printed by the JSONC emitter, so no pattern text can break out of its string. - markdown_
linter_ lookup - Where the
markdown/autofixpolicy findsmonochromatic-lint: absolute PATH entries only. What: Find themonochromatic-lintexecutable themarkdown/autofixpolicy runs, on the PATH of the invocation’s environment. Why: Repository configuration cannot choose the program (doc/planning/cli-git-rust-implementation.md, lines 181 to 189: first-party linter selection is installation wiring). The linter is installed through mise ascargo:monochromatic-lint, and mise exposes installed tools on PATH, throughmise activateor its shims directory; real Git is found on PATH the same way. Only absolute PATH entries are searched: a relative or empty entry names a directory relative to where the command runs, usually inside the very repository whose content the linter checks, and that repository must not be able to supply the program. - markdown_
linter_ output - What one linter run printed:
the fixed source and the findings left,
or why it is unusable.
What:
Read what one
monochromatic-lint --stdin --fixrun produced: the fixed source on standard output, findings it could not fix as JSON Lines on standard error, and its exit status. The result is the fixed bytes and the remaining findings, or the reason the run cannot be trusted. Why: The linter is a child process whose output is untrusted text. Its contract (package/linter/monochromatic-lint/README.md,src/run_stdin.rs,src/run_output.rs) is exit status 0 with the fixed source and warning records, because the policy’s one-rule configuration gives the rule severitywarn; 1 only for error findings or a warning limit, neither of which that configuration can produce; and 2 for a run that could not finish. Anything else, and any record that is not a finding of a selected rule, means the run cannot be trusted, so the policy fails instead of passing an unchecked file. - owner_
lock - Rename-published owner locks with dead-owner retirement and an unbounded wait.
What:
Rename-published owner-lock directories with process-birth ownership:
acquisition
with dead-owner retirement,
an unbounded wait while the owner lives,
and release by
rename before deletion.
Why:
The landing,
reservation,
hook,
capture and push locks all work this way in the
incumbent (
src/owner-lock/owner-lock.ts) and in its hook dispatcher, and both wrappers contend on the same directories while both are installed. A published lock always names its owner because its candidate is complete before the rename. - owner_
lock_ record - The owner record inside every owner-lock directory.
What:
The owner record inside every owner-lock directory (
owner.json, schema version 1): its strict parser, its exact encoding, and the owner-liveness rule. Why: The landing, reservation, hook, capture and push locks are directories holding this record. The incumbent and its hook dispatcher write and read the same record (src/owner-lock/owner-lock-record.ts), so both wrappers contend on one lock and each retires the other’s dead lock. - pack_
keep - The
.keepfiles protecting a landing’s migrated pack. What: The.keepfiles that protect a landing’s migrated pack until the compare-and-swap, named by the messagecli-git <transaction-id>. Why:git index-pack --keep=<message>writes the.keepbefore the pack becomes visible, so no prune or repack drops the objects mid-landing; recovery finds a dead transaction’s.keepby its message even before a landing record names the pack (src/policy-engine/commit-landing-objects.ts). - panic_
notice - The executable’s panic hook: where an internal error happened, never its message. What: The panic hook of the executable: one line naming where an internal error happened, never its message. Why: The linked scanner catches a matcher panic and reports it as a failed scan, but Rust runs the process’s panic hook before the panic can be caught, and the default hook prints the panic’s message, which can hold bytes of the file being scanned. The scanner’s own executable installs a hook that prints nothing; this one keeps the source location, which holds no repository content, so a wrapper defect is still visible.
- policy_
added_ path - Whether a direct fix may change a tracked file it did not select.
What: Whether a direct fix may change a tracked file it did not select, and the
patch-conflictmessage when it may not. Why:SPEC.md(“Added paths”) admits such a file only whenHEAD, the real index, the private index and the worktree all hold the blob the correction was computed against, so the fix discards nobody’s change. The reasons and the remedy are the installed wrapper’s words (commit-transaction-added-paths.ts,assertAddablePathand its direct-fix message), checked in its order. - policy_
checks - The shipped policies as checks over rule cores and repository facts. What: The shipped policies as the engine sees them: one check per policy, built from the pure rule cores and the repository facts they ask for. Why: The engine owns order, severity and stopping; the rule cores own decisions from arguments; this module joins them and fetches a repository fact only when a core asks. The content policies read the lifecycle’s candidates: they report nothing where a lifecycle has none. The Markdown policy checks the candidates it is given with the native Markdown linter.
- policy_
content - What a lifecycle offers content policies,
and its candidates once prepared.
What:
What one lifecycle offers the content policies,
and the candidates it prepared
for them,
read once and shared by every content policy of the invocation.
Why:
A forwarded command other than
git addhas no candidates, so its content policies find nothing.git addand the direct commands have candidates, which are prepared the first time a content policy reads them: an invocation whose content policies are all off or escaped starts no Git process for them. A failure to prepare is remembered, so it is reported once and never retried. - policy_
convergence - The bounded loop that repeats a pass after corrections changed candidate content. What: The bounded loop that repeats a whole policy pass after corrections changed the candidate content, until the content is stable or the loop gives up. Why: A correction by one policy can create a finding for another, so every changed state restarts the complete order. The loop must end: at most eight passes may change the content, and returning to an earlier state is a cycle. This module owns only that control flow; running policies, applying corrections and storing exact states stay behind a small interface, so the loop is testable without Git.
- policy_
dependent_ version - The
mono/dependent-version-bumpcheck over a lifecycle’s candidate state. What: Themono/dependent-version-bumpcheck: the native planner over the lifecycle’s prepared candidate state, its findings, and the corrections a direct fix applies. Why: The installed wrapper’s policy (dependent-version-bump-policy.ts) reports one finding per publishable dependent of a raised manifest, each with a full-content patch, ondirect-checkanddirect-fix; onpre-forwardit plans only for a forwardedcommit. A realgit commitruns through the commit transaction instead of being forwarded, sopre-forwardreaches this check only forgit add, where the policy finds nothing. - policy_
engine - Ordered policy execution for one lifecycle point. What: Run shipped policies in registry order for one lifecycle point and settle the outcome: which events were produced and whether the command may proceed. Why: Every lifecycle shares one execution contract: a policy runs only for the triggers it declares, at the configured severity, unless it is filtered out, escaped or off; the first error finding stops the pass unless the caller asked to keep going; and a policy or lifecycle this executable cannot evaluate is reported as unavailable, never as clean.
- policy_
events - Policy events and their JSON Lines rendering.
What:
The events one policy pass produces,
and their JSON Lines rendering.
Why:
Agents and scripts read these lines from wrapper standard error and from
git cli-git check|fixstandard output. Each event is one compact JSON object with the incumbent’s field names and field order, so existing readers parse native events unchanged. Events are numbered when rendered, so a list of events always prints with consecutive numbers in emission order. - policy_
final_ newline - The built-in final-newline policy:
canonical final LF,
preserved paths,
findings.
What:
The built-in
final-newlinepolicy: a non-empty UTF-8 text file without NUL bytes must end with exactly one LF byte. Why: The rule, its five preserved path families and its finding are the installed wrapper’s (final-newline-policy.ts,final-newline-normalize.ts), so the native wrapper reports the same files with the same words. The normalized bytes are also the full-content correction a fixing lifecycle applies. - policy_
forbidden_ strings - The optional forbidden-strings policy over the linked scanner,
with redacted findings.
What:
The optional
security/forbidden-stringspolicy: scan every candidate’s exact bytes and pathname through the linked scanner and report each match, redacted. Why: The installed wrapper wrote candidate bytes to temporary files, started the scanner executable and parsed its standard error. Here the same scanner is a library: rules are loaded once per invocation, only when a candidate can be scanned, and each finding carries the scanner’s masked display path and an opaque rule name, never the matched text. A scan that could not complete ends the policy with the code of its cause, never as a clean result. - policy_
markdown - The
markdown/autofixpolicy over candidates, behind its linter interface. What: The optionalmarkdown/autofixpolicy: every Markdown candidate the lifecycle offers is passed throughmonochromatic-lint --fixwith the policy’s rules; a changed source is reported, and corrected ongit cli-git fix, and each finding the fixes leave is reported as it is. Why: The installed wrapper’smarkdownLintPolicy(package/git-policy/markdown-lint/src) decides the behaviour: deleted files, symbolic links and submodules are skipped, and so are excluded paths before any linter starts and contents that are not UTF-8; candidates are linted one at a time, in candidate order; the codes and words of both findings are kept. One thing differs by design: the extension test is the linter’s own (.mdand.mdx, exact case), so no file is sent that the linter refuses. The autofix finding keeps the installed wrapper’s words, which name its former linter, so both wrappers report the same file the same way while both are in use. - policy_
pass - One complete pass:
built-in policies,
fixed transforms,
optional policies.
What:
One complete policy pass over the shipped policies:
the built-in policies,
then
the fixed argument transforms of a forwarded command,
then the optional policies.
Why:
Every lifecycle runs the same three stages in the same order,
and each stage can
end the pass.
Keeping the sequence in one place means a wrapped command and a
direct
git cli-git checkcannot drift apart in what they run or when they stop. - policy_
patch - Slice 6: one policy patch applied to the private index with
git apply --cached --3way. What: One policy-proposed patch applied to the private commit index withgit apply --cached --3way. Why: Corrections land in the private index, never the real one. A patch that no longer applies is apatch-conflictfor the transaction, not a silent skip. The incumbent applies the same command inapplyPrivatePatch(src/policy-engine/commit-transaction-git.ts). - policy_
patch_ validate - Slice 6: the exact grammar of one policy-proposed Git patch (
commit-transaction-patch.ts). What: The exact supported grammar of one policy-proposed Git patch. Why: A policy patch is untrusted output, so only one ordinary text path, its declared headers and the candidate base index line may reachgit apply. The rules are the incumbent’svalidatePolicyPatch(src/policy-engine/commit-transaction-patch.ts). - policy_
registry - The fixed registry of shipped policies. What: The fixed registry of policies compiled into the native wrapper. Why: Repository JSONC can only tune shipped policies; it can never name code to run.
- policy_
root_ context - The optional root-context policy:
no top-level
CONTEXT.mdenters the index. What: The optionalmono/forbidden-root-contextpolicy: aCONTEXT.mdfile at the repository’s top level must not enter the index or be checked. Why: This repository keeps no context files; agents read the source on every probe (doc/agent/domain.md). The rule and its finding are the installed wrapper’s (package/git-policy/repository/src/index.ts): only the top-level pathname counts, and removing the file is always allowed. - policy_
transforms - The fixed argument transforms of a forwarded command,
in order.
What:
The fixed argument transforms of a forwarded command,
in their fixed order:
atomic push,
commit-only,
status hints off.
Why:
These are not policies:
they have no severity and cannot be configured.
They add
--atomictogit push,-otogit commitand-c advice.statusHints=falsebeforegit status, and the commit-only transform can reject a commit. The decisions belong to the pure rule cores; this module runs them in order and fetches the two repository facts commit-only may ask for. - policy_
trigger - Lifecycle triggers and the trigger set of every shipped policy. What: The lifecycle points at which policies run, which shipped policy runs at which point, and which points this executable can serve. Why: A policy runs only for the triggers it declares. A trigger whose lifecycle is not ported must be answered as “unavailable”, never as an empty result that would read as “checked and clean”.
- post_
commit - The post-commit gate over a landed commit: its events, its blocking and the backup it allows.
What:
The post-commit policy gate over a landed commit’s own changes, and the backup that follows it.
Why:
The incumbent (
src/policy-engine/post-commit-lifecycle.ts) checks the committed bytes of every path a landed commit changed, and only a gate that passes lets the commit reach auto-push. A blocked gate keeps the commit local and names it in acommit-landedevent; the command exits2. The landed commit is an input, so the integrator can call this after any landing. - post_
commit_ hook - The post-commit run after a landing: author environment, hook lock, and the hook runner with inherited streams.
What:
The single
post-commitrun after a landing: its author environment, the hook lock around it, and the real Git hook runner with inherited standard streams. Why: The incumbent’scommit-landing-post-commit-hook.tsruns it outside both landing locks, under the hook lock unlesshooks.concurrentCommitsistrueor an outer preparation lease is inherited. Its exit status is ignored, as native Git ignores it, and the user sees the hook’s output on the terminal. - post_
landing - Post-landing completion of one landed commit: worktree copies, disposal, maintenance, then
post-commit. What: The post-landing completion of one landed commit, in the incumbent’s order: added-path worktree copies, shadow and transaction removal with capture pruning, automatic maintenance, thenpost-commitunder the hook lock. Why: The incumbent’scommit-transaction-conclusion.tsruns these steps after a landing. Worktree copies come first, so an interruption before cleanup leaves the journal for recovery to finish them. Maintenance bounds the pack count each landing grows, so it runs before the hook. A maintenance failure is logged and never fails the landed commit; a copy, disposal or hook failure fails the command with its message, as the incumbent’s thrown error does. - private_
index_ stage - Slice 6: writes one corrected file into the private commit index as a blob and an index entry.
What:
Write one corrected file into the private commit index: its bytes as a blob,
then the index entry that names that blob.
Why:
Corrections land in the private index, never in the real index or the worktree.
The incumbent patches the private index with
git apply --cached; here the policy already holds the corrected bytes, so the blob and the entry are written directly. - private_
storage - Private,
crash-safe file primitives shared by every durable record.
What:
Private,
crash-safe file primitives shared by every durable record of the wrapper:
exclusive no-follow creation with private modes,
file and directory sync,
and
no-follow reads that refuse links and non-regular files.
Why:
Lock records,
transaction journals and capture records must survive a crash in a
readable state,
must never be written through a planted link,
and must be private to
the account.
The incumbent’s
src/trust/registry-io.tsdefines these semantics; both wrappers write and read the same files. - process_
identity - Process-birth identities in the incumbent’s exact spelling.
What:
Process-birth identities:
a string that names one process for its whole life and
changes when its PID is reused,
in the incumbent’s exact spelling.
Why:
Every lock and transaction records its owner’s PID and birth identity;
an owner is
alive only while its PID names a process with the recorded identity.
Both wrappers
judge each other’s locks,
so the strings must agree byte for byte
(
src/policy-engine/commit-transaction-process-identity.ts; the decision indoc/handover/cli-git-rust-implementation.md, “Adopted without a question”). - process_
start - Process start times for lock owners,
read from
/proc,psor the Windows process table. What: The wall-clock start time of a process, with its resolution, for comparing a Git lock PID file’s owner with the lock’s change time. Why: A PID file names a live owner only when that process started no later than the lock changed; a younger process reused the PID. Linux derives the start from/proc/<pid>/statfield 22,/proc/uptimeand the current time; macOS readsps -o lstart=; Windows readsGet-ProcessStartTime(src/index-lock/process-start-time.ts). - random_
id - Random identifiers in the layout of the incumbent’s
randomUUID. What: Random identifiers in the layout of the incumbent’srandomUUID: lock tokens, transaction IDs and stale-name suffixes. Why: A lock token must be unguessable and unique per acquisition, and a transaction ID names a registry directory that the incumbent accepts only in the canonical layout (isTransactionIdinsrc/policy-engine/commit-transaction-registry.ts). - real_
git - PATH resolution of the real Git executable.
What:
Find the real Git executable on PATH without ever selecting this wrapper.
Why:
The wrapper is installed as
gitahead of real Git; forwarding needs the first usable candidate that is not a cli-git wrapper. - real_
git_ candidate - Wrapper self-exclusion for one PATH candidate.
What:
Decide whether one PATH candidate is real Git,
a cli-git wrapper,
or unusable.
Why:
The wrapper shadows
giton PATH and must never select itself, a copy of itself, or a script that starts the TypeScript wrapper. - real_
index_ lock - The real index.lock a landing creates,
identifies and releases only while it is the same file.
What:
The real
index.locka landing holds, created exclusively, identified by device, inode and filesystem, announced throughindex~pid.lock, and released only while it is still the file that was created. Why: The landing’s index install depends on the exact lock file it created; a replaced lock must never be deleted, and a lock this process did not create must never be released (src/policy-engine/commit-landing-index-lock.ts). - recovery_
completion - Deciding whether a landing landed,
and installing its recorded post-index.
What:
Deciding whether a dead transaction’s landing attempt landed,
and installing or
recognizing its recorded post-index.
Why:
Before
ref-updated.jsonthe only proof of a landing is the nonce entry in the target’s reflog; missing or contradictory evidence fails closed. Artifacts are pinned under stable names by inode before use (src/policy-engine/commit-transaction-recovery-completion.ts). - recovery_
error - The fail-closed failure of reading or recovering durable transaction state.
What:
The fail-closed failure of reading or recovering durable transaction state.
Why:
Malformed,
unsafe or contradictory state is never guessed at or deleted:
the command
stops with exit status 2 and a message naming the path,
and the state stays for
inspection (
CommitTransactionRecoveryErrorin the incumbent; implementation plan, “Commit transactions and recovery”). - recovery_
evidence - Durable evidence a dead transaction left:
attempt records,
owned locks and Git’s PID file.
What:
Durable evidence a dead transaction left:
its numbered attempt records,
the real
index.lockfiles those records prove it created, and Git’s PID file naming it. Why: Recovery releases exactly what the dead owner held and nothing else (src/policy-engine/commit-transaction-recovery-evidence.ts). - recovery_
files - No-follow file operations recovery and landing share.
What:
No-follow file operations recovery and landing share:
existence probes,
exact reads,
byte comparison,
owner-preserving hard links,
proof that a real
index.lockis the exact file a transaction created, and installing an index through such a lock. Why: Recovery removes or installs through a realindex.lockonly when the journal proves the lock is the transaction’s own, by device, inode and filesystem identity, never by path; the incumbent’scommit-transaction-recovery-files.ts,-validation.tsandcommit-transaction-install-link.tsdefine these steps. - recovery_
inspect - Owner inspection of registry entries and the oldest-first order.
What:
Owner inspection of one transaction registry entry,
and the oldest-first recovery order.
Why:
Live owners are skipped,
dead owners recovered,
and a published directory without a valid
owner record fails closed;
recovery runs oldest first because a later transaction was
prepared against the state an earlier one left
(
src/policy-engine/commit-transaction-recovery-inspect.ts,commit-landing-lock.ts). - recovery_
landing - Recovery of one dead-owner transaction.
What:
Recovery of one dead-owner transaction from its schema-version-2 records.
Why:
Without a landing record the real index and refs were never touched:
recovery releases
the
index.lockthe transaction provably created and removes its.keepfiles, shadow and directory. With a landing record (the caller holds the landing lock) it decides fromref-updated.jsonor the reflog nonce whether the commit landed, then discards the attempt or completes it: index, conclusion cleanup, added-path copies. Missing evidence fails closed and keeps the directory (src/policy-engine/commit-transaction-recovery-landing.ts). - recovery_
reflog - The reflog search that proves which commit an interrupted transaction landed.
What:
The reflog search that proves which commit an interrupted transaction landed.
Why:
Before
ref-updated.jsonexists, a landing counts as landed only when the target’s reflog holds the transaction’s nonce entry; the search covers the whole reflog because later landings push the entry down (src/policy-engine/commit-transaction-recovery-reflog.ts). - recovery_
scan - Recovery across every transaction directory of one worktree.
What:
Startup recovery across every per-transaction directory of one worktree.
Why:
Live owners are skipped;
dead owners are recovered oldest first;
dead transactions that
entered the landing critical section are recovered only while holding the landing lock,
so recovery never races a live lander (
src/policy-engine/commit-transaction-recovery-scan.ts,commit-landing-lock.ts). - replay_
capture_ history - Replay: capture-order history and the per-path decisions from the transaction’s and landed records (
replay_capture_history). What: The capture-order inputs of one replay: the first-parent commits landed since the preparation base with their changed paths, and the decisions for the shared paths read fromcaptured.jsonand the landed-capture records. Why: Two Git processes list the history whatever its length; each landed commit’s record says which worktree captured which path and when, which is what the decision compares. - replay_
capture_ order - Replay: capture order over the landed first-parent history and the per-path decision (
replay_capture_order). What: Capture order for one replay: the landed first-parent changes, the per-path decision between the prepared and the landed captures, and the parser of thegit diff-tree --stdinoutput that lists the changes. Why: When the prepared commit and every landed commit that changed a path captured it from the same worktree, the later capture’s bytes land, which is what sequential native commits record from the shared disk. Every other path keeps subsumption and then the three-way merge. - replay_
commit - Replay: one replay of a prepared commit onto a moved target, the integrator’s replay result (
replay_commit). What: One replay of a prepared commit onto a moved target, as the landing loop runs it outside both locks: shared paths, capture order, subsumption, the merge, and either the replayed commit or the conflict. Why: This is the replay result the integrator consumes. Revalidation after a clean merge (policies,pre-commit) and the events are the integrator’s; this function returns the data forcommit-replayed,replay-headers-dropped, andconcurrent-commit/replay-conflict, and the replayed tree the revalidation reads. - replay_
containment - Replay: the one-sided extension of subsumption over zero-context hunks (
replay_containment). What: The context-tolerant half of replay’s subsumption: whether each prepared change is the landed change extended by the prepared commit’s own edit on one side. Why: Strict reverse application rejects a prepared file holding the landed edit plus an own edit next to it, because that own edit sits in the landed hunk’s context. Reading both changes as zero-context hunks in base coordinates accepts exactly the one-sided extensions and still rejects own edits on both sides and modify/delete conflicts. - replay_
merge - Replay: the merge tree, the first conflicting landed commit, and the replayed commit rebuild (
replay_merge). What: The merge and rebuild steps of one replay: the replay merge base, the tree fromgit merge-tree --write-tree, the first landed commit that touched a conflicting path, and the replayed commit written from the prepared one. Why: Replay runs outside both landing locks in the shadow repository. An unsigned prepared commit is rebuilt by rewriting its raw object, which keeps every byte; a signed one is re-signed withgit commit-tree -S, because no Git primitive re-signs a raw object. - replay_
object - Replay: raw commit objects as bytes, the unsigned rewrite and signed-rebuild inputs (
replay_object). What: Raw commit objects as exact bytes: parsing, the unsigned replay rewrite, and the inputs a signed replay passes togit commit-tree. Why: Replay keeps every byte of the prepared commit except itstreeandparentlines, so headers and message stay bytes; a non-UTF-8encodingcommit round-trips unchanged. - replay_
patch - Replay: the unified-diff parser of subsumption (
replay_patch). What: The unified-diff parser that replay’s subsumption reads:git diff-tree -poutput over single-level trees named by decimal index, split into hunks per entry name. Why: Subsumption decides from hunk lines and their markers; the parser keeps every byte exact, so a hunk line round-trips to the blob bytes it came from. - replay_
reservation - Replay: the starvation reservation of the next landing slot (
replay_reservation). What: The starvation reservation of the next landing slot: a transaction that lostlanding.reserveAfterLostRacesraces writesreservation-request, takes the reservation lockreservation.lockin the registry when it is granted, and every other transaction waits before it takes the landing lock. Why: Without it a commit that keeps losing the race can starve behind a stream of landings. Grant order is the oldest live requester, so the transaction that waited longest goes first. - replay_
revalidation - Replay: the replayed index, the shadow HEAD move, and the pre-commit re-run decision of revalidation (
replay_revalidation). What: The parts of revalidation after a replay that need no policy and no hook: the replayed index, the shadowHEADmove the policies see, and the decision whetherpre-commitmust re-run. Why: Policies and thepre-commitre-run belong to the integrator, which owns the policy engine and the hook dispatcher; these three steps are pure Git work the integrator calls before it. - replay_
reverse_ apply - Replay: the exact strict reverse check of subsumption (
replay_reverse_apply). What: The exact emulation ofgit apply --reverse --checkfor one text file: the subsumption test that asks whether a landed change already sits in the prepared bytes. Why: Git’sapply.cmatching (apply_one_fragment,find_pos,match_fragment) with its defaults: no context reduction, no whitespace fixing, no overlapping hunks, and anchoring at the file’s start and end. Replay must give the same answer as Git for the same bytes. - replay_
shadow - Replay: the shadow-repository Git runner that every replay step uses (
replay_shadow). What: Running real Git against the shadow repository for replay: the shadow selection, an optional private index, bytes on standard input, and the caller’s kept global options when a signed rebuild needs them. Why: Every replay step runs ingit --git-dir=<shadow>so its objects stay in the shadow store until the next migration; one runner keeps the selection in one place. - replay_
shared_ paths - Replay: shared paths of the landed history and the prepared commit, with their three entries (
replay_shared_paths). What: The paths both the prepared commit and the landed history changed since the preparation base, with the three tree entries subsumption compares. Why: Subsumption decides per shared path; the listing is twogit diff-treeruns whatever the history length, and paths stay Latin-1 decoded so every byte survives and encodes back exactly. - replay_
subsumption - Replay: subsumption rules and the synthetic merge base (
replay_subsumption). What: Subsumption before replay’s three-way merge: the per-path rules, and the synthetic merge base that makesgit merge-treetake the prepared bytes for every subsumed path and the landed bytes for every path capture order drops. Why: A path whose prepared bytes already contain the landed change keeps the prepared entry, matching what native sequential commits produce in a shared worktree. Setting the synthetic base to the landed content means the merge sees no landed change there. - replay_
subsumption_ text - Replay: the text half of subsumption over index-named blobs and hunks (
replay_subsumption_text). What: The text half of replay’s subsumption: which regular-file paths hold prepared bytes that already contain the landed change. Why: A path is subsumed when the landed change applies in reverse to the prepared bytes with full context, or when the prepared change of every landed region is the landed change extended on one side. Hunks come from index-named single-level trees, so no patch header carries a user path, and binary detection is Git’s content rule (a NUL byte in the first 8000 bytes of any of the three blobs). - repository_
facts - Lazily fetched repository facts behind one interface, with the real-Git provider. What: The repository facts policies and fixed transforms need, behind one small interface, with the provider that asks real Git. Why: The rule cores decide from arguments alone and name the fact they still need. This module fetches those facts lazily and at most once: an invocation whose rules need nothing starts no Git process, and several rules that need the repository’s location share one query. Tests drive the rules with a scripted provider.
- repository_
location - The one query for repository identity,
top level and the path below it.
What:
Ask real Git,
in one query,
which repository an invocation selects and where
inside its worktree the command runs.
Why:
The configuration root,
the require-root policy and the worktree policies all need
answers Git itself computes from
-C,--git-dir,--work-treeand its environment. Onegit rev-parseprints all of them, so an invocation starts one Git process for these facts instead of one per fact, and the wrapper never re-implements Git’s repository discovery or its-Cchaining. - rule_
add_ explicit - Pure decision of the add-explicit policy.
What:
Pure decision of the add-explicit policy:
git addnames the paths it stages. Why:git add .,git add -Aandgit add -usweep up every changed path, including ones the caller never looked at, so one commit stops describing one change. The policy rejects those forms. This file decides from the arguments alone; it reads no file and starts no process. - rule_
argument_ rewrite - The shared result and builder of transforms that add tokens to a command line. What: The shared result and builder of fixed transforms that add tokens to a command line. Why: A transform either leaves the caller’s arguments alone or returns a new owned list; saying which lets the caller keep the original vector without comparing contents.
- rule_
atomic_ push - Pure decision of the atomic-push transform.
What:
The pure decision of the atomic-push transform:
insert
--atomicaftergit pushunless the caller already chose. Why: An atomic push updates every ref on the remote or none, which prevents partially applied pushes. - rule_
branch_ worktree - Pure decision of the branch-worktree-only policy and its one-query remote guess.
What:
Pure decision of the branch-worktree-only policy:
a new branch starts in its own
linked worktree,
not in the current one.
Why:
git branch <name>,git checkout -b,git switch -cand Git’s guess of a remote branch all create a branch in the current worktree. The policy rejects them and points atgit worktree add -b. This file decides from the arguments alone, names the one repository query the guess needs, and reads that query’s answer. It starts no process. - rule_
commit_ index - Pure core of the index-against-
HEADcheck. What: The pure core of the index-against-HEADcheck used by the commit-only transform. Why: The caller runs real Git; this module only says which command to run and what its exit status means, so the decision is testable without a process. - rule_
commit_ only - Pure decision of the commit-only transform.
What:
The pure decision of the commit-only transform:
insert
-oaftergit commit, or say why not. Why: Every commit must name the paths it includes. Two decisions need a repository fact (sequencer state, index againstHEAD); this module asks for the fact by returning aNeeds...variant and the caller answers through aresolve_...function, so no process or file is touched here. - rule_
commit_ only_ message - User-facing diagnostics of the commit-only transform. What: The user-facing diagnostics of the commit-only transform. Why: Each message names the rejected form and every valid way forward; keeping the text apart from the decision keeps both files readable.
- rule_
commit_ sequencer - Pure core of the merge, cherry-pick and revert conclusion check. What: The pure core of the “is a merge, cherry-pick or revert awaiting its commit” check. Why: The caller runs real Git and probes the filesystem; this module says which query to run, reads its output exactly, and decides from three presence facts.
- rule_
linked_ worktree - Pure decision of the linked-worktree-only policy.
What:
Pure decision of the linked-worktree-only policy:
commands that can discard
worktree state run only in a linked worktree.
Why:
git stash, a deletinggit cleanand agit resetthat rewrites files can throw away work in the primary checkout. The policy confines them to linked worktrees. This file decides from the arguments alone whether the command is guarded, and from the worktree kind the caller measured whether to reject it. It starts no process. - rule_
require_ root - Pure decision of the require-root policy. What: Pure decision of the require-root policy: commands run from the repository root. Why: Running Git from a subdirectory makes pathspecs mean something else. The policy rejects that, except for commands that need no repository. This file decides from the argument list alone which commands are exempt, and from two paths the caller measured whether the directory is the root. It reads no file and starts no process.
- rule_
status_ hints - Pure decision of the status-hints transform.
What:
The pure decision of the status-hints transform:
insert
-c advice.statusHints=falsebeforegit statusunless the caller set that key. Why: Git’s stock status hints suggestgit add <file>...andgit commit -a, forms the wrapper’s policies reject; the wrapper prints accurate guidance instead. - scanner_
adapter - The linked forbidden-strings scanner: one load per invocation, typed redacted findings. What: The repository’s forbidden-strings scanner, linked in and loaded once per invocation. Why: The TypeScript wrapper wrote each candidate to a temporary file, started the scanner as a child process and parsed its standard error. Here candidate bytes go straight to the scanner library, and its own typed findings come back. Nothing is re-derived: rule loading, the rule cache, matching, redaction and the fail-closed findings all stay the scanner’s.
- scanner_
failure_ code - Which engine failure code each candidate and scanner failure carries.
What:
Which engine failure code each failure of the candidate and scanner layers carries.
Why:
An engine failure names its cause.
content-unavailablemeans candidate bytes or a repository fact could not be read;policy-incompletemeans the policy’s own machinery failed. Deciding the code here, beside the failures, makes every policy that reads candidates or scans them report the same code for the same cause. - scanner_
run - One scan pass over a candidate version’s exact bytes. What: Scan every eligible candidate of one version through the loaded scanner. Why: This is the whole forbidden-strings check of one policy pass: candidate bytes come from the candidate store (staged or committed, never the worktree), go to the scanner in memory, and the scanner’s own results come back. No temporary file is written and no process is started per file.
- scanner_
selection - Rules-file precedence and which candidates the scanner is given.
What:
Which rules file the scanner loads,
and which candidates it is given.
Why:
The scanner library filters nothing;
the standalone scanner and the TypeScript
policy applied these choices around it.
They are restated here once,
as pure
functions:
the rules-file precedence (
FORBIDDEN_STRINGS_RULES, elseforbidden-strings.local.txtin the repository root) and the paths that hold the rules themselves, which would otherwise match their own content. - shadow_
config - The shadow repository
configfile and the copied repository format. What: The shadow repository’sconfigfile, written by cli-git itself, and the repository format facts it copies from the real common config. Why: Git does not applycore.repositoryformatversionandextensions.*from an included file, so they are copied explicitly.core.hooksPathprecedes the include so a realcore.hooksPathstill overrides it;core.worktree,gc.autoandmaintenance.autofollow it, so the real config cannot redirect the worktree or start maintenance in the shadow (src/shadow-repository/shadow-config.ts). - shadow_
create - Creation of the shadow repository of one transaction.
What:
Creates the shadow repository of one published transaction:
an empty bare repository,
its config and alternates,
the linked shared entries,
the conclusion state,
and the ref snapshot.
Why:
The shadow is a separate repository rather than a registered worktree,
so
git worktree listnever shows it and preparation creates no ref in the real repository. Its object store receives the objects nativegit commitwrites, and its alternates name the real object directory, so a realgit prunecannot delete a pending commit (src/shadow-repository/shadow-repository.ts). - shadow_
git - Git commands run against a transaction’s shadow repository.
What:
Git commands run against a transaction’s shadow repository,
and its location and
removal.
Why:
The shadow lives at
<git-common-dir>/cli-git/shadow/<transaction-id>, derived from the transaction ID so recovery finds it beforeprepared.jsonexists. cli-git’s own shadow commands remove every variable that would redirect Git elsewhere and point hooks at an absent directory, so no hook observes private shadow writes (src/shadow-repository/shadow-refs.ts,runShadowGit). - shadow_
links - The shadow repository links to the real shared entries.
What:
Links every non-private entry of the real common Git directory into a shadow repository,
and copies the per-worktree entries (
config.worktree, a linked worktree’s sparse checkout). Why: Shared entries such ashooks,info,rr-cache,lfsandmodulesmust be the real ones, so hooks and native Git see them; transaction-private entries (refs, index, objects, conclusion state, lock files) stay the shadow’s own (src/shadow-repository/shadow-links.ts). On Windows the incumbent uses junctions and copies; this port uses directory symbolic links and copies, because the standard library has no junction API. - shadow_
refs - The shadow ref store: snapshot, private target, symbolic refs and
HEAD. What: The shadow ref store: a snapshot of every real ref, the private target ref, symbolic refs, and the privateHEAD. Why: Hooks resolve tags, other branches and remote-tracking refs as they stood at invocation. With the files backend the snapshot is onepacked-refsfile and the private target is a loose ref, which takes precedence over a packed entry; with reftable the snapshot is oneupdate-ref --stdintransaction (src/shadow-repository/shadow-refs.ts). - signal_
relay - Catching SIGINT, SIGTERM, SIGHUP and SIGQUIT while a Git child runs, and relaying only process-sent signals.
What:
Catching
SIGINT,SIGTERM,SIGHUPandSIGQUITwhile the wrapper waits for a Git child, relaying a signal to that child only when a process sent it, and reporting how the child ended.SignalGuardowns the handlers for its lifetime and restores the previous dispositions on drop. Why: A wrapper that dies on a signal leaves its commit transaction for recovery and Git running; one that catches the signal can end Git, release its locks and remove the transaction first. A signal from the terminal already reached Git as part of the foreground process group, so relaying it again would deliver it twice. - transaction_
gate - Startup recovery as a step of a wrapped or direct command.
What:
Startup recovery as a step of a wrapped or direct command:
recover,
then report how the
command may go on.
Why:
Every command that reads the repository location recovers dead transactions first.
A
guarded command beside a live transaction is still refused until index writers coordinate
with live transactions;
a read-only command goes on.
A legacy directory or a recovery
failure stops the command with exit 2,
as the incumbent’s
content-unavailableengine failure does. - transaction_
git - Running real Git for transaction,
recovery and landing work.
What:
Running real Git for transaction,
recovery and landing work:
captured output,
an
optional private index and object store,
extra or removed variables,
and bytes on
standard input.
Why:
Every transaction step asks Git a question or writes an object;
one runner keeps every
such start an argument array (never a shell string) carrying the wrapper’s child
environment overlay (lock PID injection),
as the incumbent’s
runTransactionGitdoes. - transaction_
journal - The schema-version-2 journal records of one commit transaction and their filenames.
What:
The schema-version-2 journal records of one commit transaction and their filenames;
encoding and parsing live in
transaction_journal_encode.rsandtransaction_journal_parse.rs. Why: State files are created exclusively and never rewritten, so a crash leaves the newest complete state readable; recovery in either wrapper reads the records the other wrote (src/policy-engine/commit-transaction-journal-states.tsandcommit-transaction-journal-parse.ts). Every missing or mistyped field fails closed with the record and field named. - transaction_
journal_ encode - The exact encoding of every journal record.
What:
The exact encoding of every schema-version-2 journal record:
compact JSON in the
incumbent’s field order,
one record per line.
Why:
writeJournalRecordwritesJSON.stringify(record)and a newline; the native wrapper writes the same bytes for the same values so either wrapper can recover the other’s transactions. - transaction_
journal_ parse - Strict parsers of every journal record.
What:
Strict parsers of every schema-version-2 journal record.
Why:
Recovery reads records either wrapper wrote;
every missing or mistyped field fails
closed with the record and field named,
exactly as the incumbent’s
commit-transaction-journal-parse.tsandcommit-transaction-journal-reader.tsdo. - transaction_
lease - The preparation and landing leases nested wrapper invocations inherit.
What:
The preparation and landing leases:
capabilities a running wrapper passes to the
Git it starts,
so a nested wrapper invocation from a hook or
rebase --execproceeds under its ancestor instead of waiting for it. Why: Without a lease a nested invocation would wait for the hook lock or landing lock its own ancestor holds, forever. Both wrappers format and validate the same leases (src/hook-dispatch/preparation-lease.ts,src/index-lock/landing-lease.ts), because a hook started by one wrapper may run the other. - transaction_
owner - The owner record of one commit-transaction directory and its liveness rule.
What:
The owner record of one commit-transaction directory (
owner.json, schema version 2) and the liveness rule that decides whether recovery may touch the transaction. Why: The owner record is written before the directory is published, so recovery can skip a live owner’s transaction at every phase. Both wrappers write and judge the same record (src/policy-engine/commit-transaction-owner.ts). - transaction_
phase - Test-only transaction phase markers, compiled into the release executable and armed only by an environment variable.
What:
Test-only transaction phase markers for crash and interleaving suites.
An explicit environment variable arms one marker:
CLI_GIT_TEST_ONLY_PHASE_SIGNAL=<phase>:killends the process withSIGKILLat the phase,<phase>:kill:<directory>writes<directory>/<phase>.reachedfirst, and<phase>:pause:<directory>writes that marker and waits until<directory>/<phase>.releaseexists. A repeatable phase numbers its files by occurrence:<phase>-<n>.reached. Why: Crash suites need a deterministic point to kill the wrapper. The marker is compiled into the release executable, but only the explicit variable arms it, and a malformed value fails the invocation at every phase it reaches instead of being ignored (src/policy-engine/commit-transaction-test-phase.ts). - transaction_
registry - The per-worktree registry of commit-transaction directories.
What:
The per-worktree registry of commit-transaction directories,
<git-dir>/cli-git-transactions/: entry names, publication by rename, removal by rename, and the strict listing recovery reads. Why: Each transaction owns<registry>/<uuid>/, published only onceowner.jsonis complete, so every published directory names its owner; removal renames it to a retired name first, so a reader never sees a published directory lose its owner. Both wrappers create and read the same registry (src/policy-engine/commit-transaction-registry.ts). - unported
- The work of the installed cli-git that is not ported, and the refusal notice. What: The work of the installed cli-git that this executable does not do yet, as one typed list, and the notice printed when a command needs any of it. Why: A command that needs unported work must stop with exit status 2 and say what is missing. Forwarding it instead would run the command without the protection the installed wrapper gives it, and nobody would see that it happened.
- worktree_
completion - Bringing added paths’ worktree copies to their landed bytes.
What:
Bringing each added path’s worktree copy to the landed bytes after a commit landed,
with
descriptor-bound checks that never overwrite a concurrent edit.
Why:
An added path’s committed bytes differ from its worktree copy (a policy corrected it);
the
copy is replaced only while it still holds the original bytes,
through a same-directory
temporary file.
A copy holding neither is kept and reported,
since the commit already
landed (
src/policy-engine/commit-transaction-added-paths.ts,-worktree-check.ts,-worktree-replace.ts). - worktree_
copy - The linked-worktree ignored-state copy around one forwarded Git invocation: the integrator’s entry point. What: The linked-worktree ignored-state copy around one forwarded Git invocation: the public entry point the integrator calls, the settlement and summary it writes, and the exit status it returns. Why: A worktree that Git creates (or a bare repository’s) needs its ignored files from the source worktree, copied under one settlement lock per repository and recovered first when an earlier copy was interrupted.
- worktree_
copy_ compare - Exact comparison of manifest entries against the filesystem, and the source stability check after staging. What: Exact comparison of one manifest entry against the filesystem: its kind, its retained mode, its symbolic-link target text or its regular-file bytes. Also the final check that the source still matches the stage after staging. Why: A destination must receive exactly what was staged from the source, and a source that changed during the transfer must block the destination.
- worktree_
copy_ error - The one failure type of the linked-worktree ignored-state copy and its diagnostics. What: The one failure type of the linked-worktree ignored-state copy, with the complete user-facing diagnostic in its message. Why: Every copy step fails with a message that names the path it could not handle and, when there is one, the operating system’s cause. The incumbent renders the cause after the message with a fixed separator, so both wrappers print the same words.
- worktree_
copy_ forward - Which forwarded invocations the copy applies to, and the child run that carries its settlement lease. What: Running a forwarded Git command through the linked-worktree ignored-state copy: which invocations the copy applies to, and the child-process run that carries the settlement lease. Why: A command that creates or moves worktrees must stay under the settlement lock while real Git runs and the ignored state is copied, and a command run from a linked worktree must first recover an interrupted copy; replacing the process with Git would drop both duties, so these invocations run Git as a child. The main checkout is a source only for the configured rules file, so it takes this path only when the command creates a worktree and the configuration names that file.
- worktree_
copy_ install - Installation of a validated stage into a new worktree, with preflight, batches and rollback on failure. What: Installation of one validated stage into a newly registered worktree: preflight of every existing destination entry, batched creation with its durable records, mode application and rollback of this installation’s own paths when it fails. Why: A destination that already holds an identical entry is accepted, and a differing entry is never overwritten, so the copy cannot destroy user state. Recording each batch before and after it runs bounds the work an interrupted run leaves unrecorded.
- worktree_
copy_ install_ entry - The exclusive creation of one destination entry from the stage, and the identity captured after it. What: The exclusive creation of one destination entry from the private stage, the identity captured right after it exists, and the creation of the unselected parent directories an entry needs. Why: A regular file is installed as a hard link to its staged copy, so it appears with its final bytes and mode in one step; the identity captured after creation is the only proof later rollback accepts.
- worktree_
copy_ install_ log - The append-only install log of one stage: per-batch intents and creations, with resumption. What: The append-only install log inside one transaction’s private stage: one synced JSON line per batch of intents and one per batch of proven creations. Why: The journal header changes only with the phase, so the per-batch records live here; bytes written then grow with the entry count instead of with the square of it.
- worktree_
copy_ journal - The durable journal of one copy: exact encoding, strict validation, private storage and removal.
What:
The durable journal of one destination’s linked-worktree copy:
its exact JSON encoding, strict validation of every field recovery will act on,
private storage under
<common>/cli-git-worktree-copy/v1, and removal of a finished transaction’s stage and journal. Why: A crash can happen after any step, so the journal must say which phase the copy reached and which paths it may have touched, and recovery must never act on a journal it cannot prove is its own. - worktree_
copy_ lock - The settlement lock that serializes copy installation and recovery, its owner record, and inherited leases. What: The settlement lock that serializes worktree-copy installation and recovery per common Git directory, its owner record, liveness-based retirement of dead owners, and the inherited-lease check nested Git runs use. Why: Two copies into one repository must not interleave, but a commit in a linked worktree must never wait for them; only an owner proven alive may be waited for, and only an owner proven dead may be replaced.
- worktree_
copy_ manifest - The deterministic parent-first manifest of a copy, and the mode application to a staged copy. What: The deterministic, parent-first list of entries a copy transfers, read without following symbolic links, and the application of the retained permission bits to a staged copy. Why: The stage and the source are compared entry by entry, so both sides must enumerate in the same order and classify the same way. Sockets, FIFOs and device nodes stop the walk before any of them is opened.
- worktree_
copy_ recovery - Recovery of interrupted copies: registration checks, discards with notices, resumption, and the pending check. What: Recovery of interrupted linked-worktree copies: every pending journal is resumed, completed, or discarded with a notice; malformed state fails closed and stays; the pending check that forwarded commands run first. Why: A copy interrupted by a crash must not leave a worktree half-filled forever, but recovery may only act on journals whose destination is still a registration of this repository and whose stage is still private.
- worktree_
copy_ registry - Which linked worktrees a worktree-creating Git command registered, before and after it ran.
What:
The linked worktrees a worktree-creating Git command registered:
the administrative identities before real Git ran,
the identities after it returned,
and the canonical root of each new one.
Why:
A worktree that Git registered before failing still receives the ignored files,
so the comparison runs after Git returns whatever its status was.
The main worktree is observed only when the caller names repository paths to copy from it
(
observe_copy_source), so its other invocations cost no filesystem reads. - worktree_
copy_ rollback - Removal of the paths a failed installation created, only where their identity and content still prove them cli-git’s own. What: Removal of the paths one failed installation created, child first, and only where the filesystem still proves they are cli-git’s own. Why: A failed installation must not leave a partial worktree, but it also must never delete a path the user or another process put there after cli-git created it.
- worktree_
copy_ selection - The ignored source paths a linked-worktree copy selects, and the path validation every selection passes. What: Which source paths the linked-worktree copy selects: the ignored roots Git reports under the standard exclusion stack, the ignored special entries (sockets, FIFOs, devices) Git’s listing omits, and the validation every repository path passes before it touches the filesystem. Why: Git decides what is ignored; the copy only adds the entries Git’s index-oriented listing cannot report, and it never trusts a path that could leave the worktree.
- worktree_
copy_ stage - The private, validated stage of one source’s ignored state beside a destination worktree.
What:
The private, validated copy of one source’s ignored state,
staged in a mode-
0700directory beside a destination worktree. Why: The stage sits on the destination’s filesystem, so a file clone stays local and the install is a rename or a link. The source is copied entry by entry and compared again at the end, so a source that changed during the copy never reaches the destination. - worktree_
copy_ transaction - One journaled installation to an end, resumption from the log, and synchronization of a created worktree. What: One destination’s journaled installation to an end, and the synchronization of every created worktree. The journal records the phase, the install log records each batch’s claims and creations, and a finished or failed installation always removes its journal and stage. Why: A failed installation must not stay pending, because recovery would replay the same failure before every later command; an interrupted one must resume from exactly what it recorded.
- worktree_
identity - Repository and worktree identity as reported by real Git.
What:
Ask real Git which repository and worktree an invocation selects.
Why:
Configuration lives at the worktree top level,
and worktree policies need to
know main versus linked.
Git applies
-C,--git-dir,--work-treeand its environment itself, so the wrapper never re-implements that selection. - wrapped_
command - The lifecycle of one wrapped Git command, from refusals to forwarding. What: Decide what one wrapped Git command does once real Git is known: forward it, possibly rewritten, or stop with policy events or a refusal. Why: This is the whole lifecycle of a forwarded command in one place and in the installed wrapper’s order: unported work is refused, configuration is loaded only for commands that need it, the policy pass runs, and a real push is stopped until its own lifecycle exists. A read-only command never reads configuration and asks Git at most where it runs.
- wrapper_
controls - Wrapper-only control spellings, their meanings, and removal before the subcommand. What: The wrapper-only control tokens, what each one means, and their removal from the arguments that come before the Git subcommand. Why: Git refuses options it does not know, so every control must be gone before Git runs, and every policy decision must read the arguments without them. A control is recognized only where Git itself would look for an option, and it is removed by its position, never by its spelling, so an equal-looking value or path is kept.
- wrapper_
invocation - Removal of every wrapper control from one invocation, by position. What: Remove every wrapper control from one invocation, before and after the subcommand, and say how the command’s own arguments were read. Why: Detecting a control and removing it must be one act. This module is the only place that removes wrapper tokens: positions come from Git’s own option table of the command where one is ported, and are deleted by position, so a control never reaches Git and a message, value or path that spells a control is never removed.