Skip to main content

Crate git_policy_cli

Crate git_policy_cli 

Source
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.md rule APG), and the incumbent’s src/auto-push.ts decides the push argv from the upstream state, prints only GitHub remote: 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-pushed record 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-pushed record: validation, tolerant reads and atomic publication. What: The per-branch last-pushed record 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-pushed record 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’s src/auto-push-single-flight.ts decides 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 one git 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 --batch reply. What: Read one git cat-file --batch reply 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 add would stage, and the worktree files a direct command selects. What: The two candidate sources that need a private index: what git add would stage, and the worktree files a direct git cli-git check or fix selects. Why: Both are answered before Git touches the real index. git add is replayed on a private copy of the index, and the candidates are the entries the replay changed, as the installed wrapper’s add-policy-facts.ts and add-staged-delta.ts define them. A direct command stages its scope with git add --all on a private copy, as direct-check-facts.ts does, 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 add would 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’s index-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 --batch process 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-index and git 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 --stage records and the delta between two index states. What: Parse the NUL-delimited stage records of git ls-files --stage -z, find the paths one staging operation changed, and turn them into candidate records. Why: git add is 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, invalidate retires 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 with HEAD: it reads each workspace manifest’s HEAD bytes, and a direct fix may change a tracked file it did not select only when HEAD holds 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 symbolic HEAD target, the conclusion kind, the ref storage backend, and the preparation base. Why: Every later read of HEAD inside a transaction uses the recorded base or the landed commit, never live HEAD, 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, captureInvocationLayout and captureInvocationBase).
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, and captured.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 add and its bulk-staging facts. What: The complete git add option 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 -A inside 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 checkout and git switch. What: Branch-creation facts of git branch, git checkout and git switch regions, 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 of git branch in Git 2.56.0. Why: Whether git 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 follow options[] in cmd_branch (builtin/branch.c:992-1050) in source order.
command_checkout_table
The complete Git 2.56.0 option tables of git checkout and git switch. What: The complete option tables of git checkout and git switch in 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 clean and its deletion facts. What: The complete git clean option 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-run wins, in clusters (-ndX) and abbreviations (--dry).
command_commit
Facts about the arguments after git commit. What: Facts about the arguments after git 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-all reports all off, exactly as git commit would act.
command_commit_table
The complete Git 2.56.0 option table of git commit. What: The complete option table of git commit in Git 2.56.0. Why: Declaring every option Git accepts removes the guess about whether an undeclared option consumes the next token. Rows follow builtin_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 config region. What: The form and file scope of a git config region, read as Git 2.56.0 reads it. Why: The require-root policy exempts git config when it works on the per-user or system file, or lists. Whether --global is that option or a value being stored depends on the form: git config user.name --global writes the text --global into the repository’s own configuration.
command_config_table
The Git 2.56.0 option groups of git config. What: The option groups of git config in Git 2.56.0, one constant per group. Why: Git builds each git config subcommand’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-options tokenization of one command’s argument region. What: Read one Git command’s arguments the way Git 2.56.0 parse-options.c reads 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 push and its facts. What: The complete git push option 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 reset and its mode. What: The complete git reset option table of Git 2.56.0 and the mode read from it. Why: The linked-worktree policy guards resets that rewrite worktree files: --hard, --merge and --keep. The five mode options write one variable, so only the last one counts, in any abbreviation Git accepts.
command_stash
git stash subcommand dispatch and wrapper-flag positions. What: Which git stash subcommand 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 stash subcommand. What: The option tables of every git stash subcommand in Git 2.56.0. Why: git stash dispatches 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 complete git status option table of Git 2.56.0, the machine-readable fact read from it, and the reading of advice.statusHints set 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 worktree invocation creates or moves a worktree. What: The one fact the wrapper needs about git worktree: whether the invocation creates or moves a worktree. Why: Only git worktree add and git worktree move are 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, -a removal and private arguments. What: Argument transforms of native preparation: the normalised commit argument region, the split of global options from the commit region, the removal of -a/--all, and the arguments of the real git commit that run against the complete private index. Why: Native git commit must 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 of lastIndexOf, so a -m value equal to a pathspec is removed from the same position.
commit_command
The git commit command: the transaction, the settled events, the post-commit gate and the backup. What: The git commit command 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.ts runs these steps in this order: the tree is written from the stable private index, the dispatcher shim is shared by preparation and the post-landing post-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.reserveAfterLostRaces lost 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 refusing git commit until 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 completeNoChangeTransaction lands 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 commit in the shadow, the prepared commit read back and prepared.json. What: Native git commit in the shadow repository against the private commit index, the prepared commit read back from the shadow, and prepared.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 through PreparationHooks. 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 whether pre-commit is 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 HEAD at the replay parent, the policies converged again over that index, and pre-commit re-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-commit hook 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.ts and commit-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 commit removes MERGE_HEAD and 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 commit in 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.rs holds the landing side). Pseudorefs of a reftable repository live in the ref store and are copied with update-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.jsonc content 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 read cli-git.config.jsonc at 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.jsonc text 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 rulesFile option: a repository-relative name that stays inside the repository. What: The rulesFile option of security/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, then FORBIDDEN_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.jsonc produces, 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 is policy-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 after from, import, import( or require(. 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 HEAD commit’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 with JSON.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 follow JSON.parse: the last duplicate wins. A manifest is decoded as strict UTF-8 after one optional byte-order mark, as the incumbent’s fatal TextDecoder does.
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) and isNonTestSourcePath (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 of mono/dependent-version-bump: when to plan, and the findings a plan becomes. Why: The plan is a pre-commit step. Another forwarded command such as git add could not apply its patches, so it plans only for git commit among forwarded commands, and for git cli-git check and git 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 under packages:, so a line scan reads exactly what it wrote, as the incumbent does (publishable-names.ts:38-81). Lines are trimmed with JavaScript’s String.prototype.trim set of characters.
dependent_version_release
Patch bumps of plain release versions and JSON.stringify quoting. What: Patch-bump a plain major.minor.patch release version, and quote text the way JSON.stringify does. 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 version value 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=true and warnings unless MONOCHROMATIC_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’s direct-fix.ts and direct-fix-convergence.ts decide 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 is policy_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 fix settled on into the worktree, all of them or none, and never touch the index. Why: The installed wrapper’s direct-fix-install.ts decides 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 git invocation 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.rs deletes wrapper tokens by the positions the tokenizer reports.
event_path
A pathname as events report it: text, plus base64 pathBytes when 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 keeps path as text with replacement characters, so every existing reader works unchanged, and adds an optional pathBytes field, 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 of doc/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.lock a transaction creates. What: The filesystem identity recorded with every real index.lock a transaction creates, spelled exactly as the incumbent’s @monochromatic-dev/module-fs-id spells it. Why: Recovery removes a real index.lock only when its device, inode and filesystem identity all match the journal; the incumbent compares the recorded fsId with 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> or alias.<name>.command, splits it with split_cmdline quoting, 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, so git -c alias.c=commit c is 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 hooks directory, which runs the repository’s hooks for that event under the hook lock. Why: Git starts the entry through core.hooksPath; the start name selects the event. The incumbent’s dispatch.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, including post-commit or a hook name outside a transaction, behaves as git.
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.json and one entry per preparation event. Why: Git starts a hook through core.hooksPath by 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 commit the 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 same plan.json, so its field order and spelling are part of the on-disk format.
index_lock_evidence
Evidence about a foreign index.lock and the verdict it supports. What: Evidence about a foreign index.lock and 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’s core.lockfilePid file 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 native git commit keeps index.lock without a descriptor through its hooks and editor (src/index-lock/index-lock-evidence.ts).
index_lock_holder_parsers
Parsers of lsof -F pcDi records and Restart Manager lines, matched by device and inode. What: Parsers of the holder-scan output that is not /proc: lsof -F pcDi records 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>/fd on Linux, lsof on 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.lock under 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: /proc and Git-free lock reads, real sleeps, operating-system randomness and standard error. Why: WaitEffects is 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, reset except --soft, stash, checkout, switch, merge, rebase, cherry-pick, revert, apply --cached and apply --index, update-index, read-tree, am, pull and sparse-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.lock is 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 example git 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/fix need the same answer: what does the cli-git.config.jsonc of 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’s Date.prototype.toISOString: YYYY-MM-DDTHH:MM:SS.mmmZ. Why: Transaction owner records carry createdAt and capture records invokedAt in 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’s str::trim uses a different whitespace set (it trims U+0085 and keeps U+FEFF).
json_record
Compact JSON records read and written with the incumbent’s JSON semantics. What: Reading and writing the compact JSON records both wrappers share, with the incumbent’s JSON.parse and JSON.stringify semantics. 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.ts runs 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, the index-installed marker and conclusion cleanup. Why: A landing is serial under the landing lock and the real index.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-commit finding for a landing that landed nothing. What: The concurrent-commit finding for a landing that landed nothing because HEAD was 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-parse that 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 .keep file 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 real HEAD reflog.
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-regexp records, and choosing whether git maintenance run --auto runs and whether it detaches. Why: Native git commit runs this step after updating HEAD; a landing replaces that commit with plumbing, so the pack count grows by one per commit unless this step runs. The rules mirror Git’s prepare_auto_maintenance in run-command.c, including JavaScript number semantics for values the incumbent reads with Number(...).
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 with hook.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: one git config read for the maintenance keys, then git 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-git decision: help, retired commands, and direct check/fix. What: Decide what one git cli-git ... invocation does. Why: Help, refusals and retired commands need no repository; check and fix validate 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-git argument grammar. What: The grammar of git cli-git <command>, the wrapper’s own management namespace. Why: check and fix run policies over an explicit scope; trust, untrust and status are 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 exit 2.
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 push would 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 private pre-push hook in a private directory records the update lines Git negotiated (src/policy-engine/manual-push-hook.ts), and git ls-remote --refs confirms 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 exclude patterns of markdown/autofix, matched as the linter matches them. What: The exclude option of markdown/autofix: gitignore-syntax patterns, relative to the repository’s top level, naming candidates the policy leaves alone. Why: The installed wrapper filtered candidates with the ignore package 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 the ignore crate’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-lint on PATH, bounded, once per candidate. What: The linter the executable gives markdown/autofix: monochromatic-lint, found on the invocation’s PATH and started once per candidate as monochromatic-lint --config=<file> --stdin --stdin-filename=<path> --fix from 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=value argument, 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/autofix policy hands monochromatic-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 at warn, 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/autofix policy finds monochromatic-lint: absolute PATH entries only. What: Find the monochromatic-lint executable the markdown/autofix policy 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 as cargo:monochromatic-lint, and mise exposes installed tools on PATH, through mise activate or 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 --fix run 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 severity warn; 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 .keep files protecting a landing’s migrated pack. What: The .keep files that protect a landing’s migrated pack until the compare-and-swap, named by the message cli-git <transaction-id>. Why: git index-pack --keep=<message> writes the .keep before the pack becomes visible, so no prune or repack drops the objects mid-landing; recovery finds a dead transaction’s .keep by 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-conflict message when it may not. Why: SPEC.md (“Added paths”) admits such a file only when HEAD, 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, assertAddablePath and 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 add has no candidates, so its content policies find nothing. git add and 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-bump check over a lifecycle’s candidate state. What: The mono/dependent-version-bump check: 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, on direct-check and direct-fix; on pre-forward it plans only for a forwarded commit. A real git commit runs through the commit transaction instead of being forwarded, so pre-forward reaches this check only for git 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|fix standard 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-newline policy: 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-strings policy: 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/autofix policy over candidates, behind its linter interface. What: The optional markdown/autofix policy: every Markdown candidate the lifecycle offers is passed through monochromatic-lint --fix with the policy’s rules; a changed source is reported, and corrected on git cli-git fix, and each finding the fixes leave is reported as it is. Why: The installed wrapper’s markdownLintPolicy (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 (.md and .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 check cannot 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 with git apply --cached --3way. Why: Corrections land in the private index, never the real one. A patch that no longer applies is a patch-conflict for the transaction, not a silent skip. The incumbent applies the same command in applyPrivatePatch (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 reach git apply. The rules are the incumbent’s validatePolicyPatch (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.md enters the index. What: The optional mono/forbidden-root-context policy: a CONTEXT.md file 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 --atomic to git push, -o to git commit and -c advice.statusHints=false before git 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 a commit-landed event; the command exits 2. 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-commit run after a landing: its author environment, the hook lock around it, and the real Git hook runner with inherited standard streams. Why: The incumbent’s commit-landing-post-commit-hook.ts runs it outside both landing locks, under the hook lock unless hooks.concurrentCommits is true or 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, then post-commit under the hook lock. Why: The incumbent’s commit-transaction-conclusion.ts runs 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.ts defines 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 in doc/handover/cli-git-rust-implementation.md, “Adopted without a question”).
process_start
Process start times for lock owners, read from /proc, ps or 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>/stat field 22, /proc/uptime and the current time; macOS reads ps -o lstart=; Windows reads Get-Process StartTime (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’s randomUUID: 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 (isTransactionId in src/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 git ahead 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 git on 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.lock a landing holds, created exclusively, identified by device, inode and filesystem, announced through index~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.json the 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 (CommitTransactionRecoveryError in 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.lock files 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.lock is the exact file a transaction created, and installing an index through such a lock. Why: Recovery removes or installs through a real index.lock only when the journal proves the lock is the transaction’s own, by device, inode and filesystem identity, never by path; the incumbent’s commit-transaction-recovery-files.ts, -validation.ts and commit-transaction-install-link.ts define 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.lock the transaction provably created and removes its .keep files, shadow and directory. With a landing record (the caller holds the landing lock) it decides from ref-updated.json or 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.json exists, 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 from captured.json and 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 the git diff-tree --stdin output 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 for commit-replayed, replay-headers-dropped, and concurrent-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 from git 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 with git 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 to git commit-tree. Why: Replay keeps every byte of the prepared commit except its tree and parent lines, so headers and message stay bytes; a non-UTF-8 encoding commit 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 -p output 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 lost landing.reserveAfterLostRaces races writes reservation-request, takes the reservation lock reservation.lock in 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 shadow HEAD move the policies see, and the decision whether pre-commit must re-run. Why: Policies and the pre-commit re-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 of git apply --reverse --check for one text file: the subsumption test that asks whether a landed change already sits in the prepared bytes. Why: Git’s apply.c matching (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 in git --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 two git diff-tree runs 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 makes git merge-tree take 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-tree and its environment. One git rev-parse prints 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 -C chaining.
rule_add_explicit
Pure decision of the add-explicit policy. What: Pure decision of the add-explicit policy: git add names the paths it stages. Why: git add ., git add -A and git add -u sweep 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 --atomic after git push unless 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 -c and Git’s guess of a remote branch all create a branch in the current worktree. The policy rejects them and points at git 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-HEAD check. What: The pure core of the index-against-HEAD check 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 -o after git commit, or say why not. Why: Every commit must name the paths it includes. Two decisions need a repository fact (sequencer state, index against HEAD); this module asks for the fact by returning a Needs... variant and the caller answers through a resolve_... 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 deleting git clean and a git reset that 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=false before git status unless the caller set that key. Why: Git’s stock status hints suggest git add <file>... and git 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-unavailable means candidate bytes or a repository fact could not be read; policy-incomplete means 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, else forbidden-strings.local.txt in the repository root) and the paths that hold the rules themselves, which would otherwise match their own content.
shadow_config
The shadow repository config file and the copied repository format. What: The shadow repository’s config file, written by cli-git itself, and the repository format facts it copies from the real common config. Why: Git does not apply core.repositoryformatversion and extensions.* from an included file, so they are copied explicitly. core.hooksPath precedes the include so a real core.hooksPath still overrides it; core.worktree, gc.auto and maintenance.auto follow 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 list never shows it and preparation creates no ref in the real repository. Its object store receives the objects native git commit writes, and its alternates name the real object directory, so a real git prune cannot 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 before prepared.json exists. 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 as hooks, info, rr-cache, lfs and modules must 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 private HEAD. Why: Hooks resolve tags, other branches and remote-tracking refs as they stood at invocation. With the files backend the snapshot is one packed-refs file and the private target is a loose ref, which takes precedence over a packed entry; with reftable the snapshot is one update-ref --stdin transaction (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, SIGHUP and SIGQUIT while 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. SignalGuard owns 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-unavailable engine 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 runTransactionGit does.
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.rs and transaction_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.ts and commit-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: writeJournalRecord writes JSON.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.ts and commit-transaction-journal-reader.ts do.
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 --exec proceeds 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>:kill ends the process with SIGKILL at the phase, <phase>:kill:<directory> writes <directory>/<phase>.reached first, and <phase>:pause:<directory> writes that marker and waits until <directory>/<phase>.release exists. 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 once owner.json is 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-0700 directory 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-tree and 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.