Expand description
Candidate content: what a commit or push would add, read from the index or objects, and the scanner run over it. What: Candidate content: what a commit or push would add, read from the index or objects, and the scanner run over it. Why: Policies judge candidates, so the readers and the scanner adapter share a folder.
In TS you’d write (pseudocode):
// candidate/index.ts: one `export * from './file'` per module below.Modules§
- blob_
batch - Loading many blobs through one
git cat-file --batch. What: Loading many blobs through onegit cat-file --batch, optionally from a private object store, with strict parsing of the batch output. Why: Post-landing completion reads the original and intended bytes of every added path; one batch replaces a Git start per object (src/policy-engine/blob-batch.ts). - candidate_
attribute - Which candidate paths have their
textGit attribute unset, read from the state the candidates were listed from. What: Which candidate paths have theirtextGit attribute unset (-text, which thebinarymacro includes), resolved bygit check-attragainst the same state the candidates were listed from. Why: A path whosetextattribute is unset declares that its exact bytes matter, as a fuzz seed’s trailing bytes do. The attribute lives in the package’s own.gitattributes, so it moves with the package, unlike a path list kept elsewhere. Staged and projected candidates are answered from the private index, so a.gitattributesstaged by the same command applies to it and an unstaged worktree edit does not; landed and pushed candidates are answered from the tree of the commit they came from. - candidate_
batch - Pure framing of one
git cat-file --batchreply. What: Read onegit cat-file --batchreply from a byte stream, with no process involved. Why: Object bytes are untrusted input that may contain anything, including text that looks like a reply header. The reply is therefore framed only by the header’s declared size, never by searching content, and every malformed shape is a typed failure instead of a guess. - candidate_
error - The one failure type of the candidate layer and its closed list of causes. What: The one failure type of the candidate layer, with a closed list of causes. Why: Every candidate failure stops policy evaluation (a file that could not be read was not checked), and callers branch on the cause without parsing message text.
- candidate_
known_ content - Which candidates hold content that a parent commit’s tree already holds, found by exact object name. What: Which candidates hold content that a parent commit’s tree already holds, found by exact blob object name. Why: A pure rename or a copy re-lists a file as an addition, and a merge lists every file one parent lacks as an addition, although the bytes are already in history. Scanning them again refuses moves of files that were accepted when they entered history, while an object name is the hash of the bytes, so a candidate whose object a parent holds carries no byte that is new. The comparison is by object name only: never by pathname, never by similarity, so one changed byte makes the content new.
- candidate_
object - Validated Git object names and the file modes a candidate can carry. What: The two Git vocabulary types every candidate carries: an object name and a file mode. Why: Object names are written into the object reader’s request stream, so only text proven to be a complete hexadecimal name may ever become one; modes decide whether a candidate has blob content at all.
- candidate_
prediction - What
git addwould stage, and the worktree files a direct command selects. What: The two candidate sources that need a private index: whatgit addwould stage, and the worktree files a directgit cli-git checkorfixselects. Why: Both are answered before Git touches the real index.git addis replayed on a private copy of the index, and the candidates are the entries the replay changed, as the installed wrapper’sadd-policy-facts.tsandadd-staged-delta.tsdefine them. A direct command stages its scope withgit add --allon a private copy, asdirect-check-facts.tsdoes, and every selected entry is a candidate. Either way the bytes are blobs Git hashed from the worktree at that moment, read through the one object reader, so the number of Git processes does not grow with the number of files. - candidate_
private_ index - A private,
timestamp-preserving copy of the real index,
removed when dropped.
What:
A private copy of the real index,
in its own directory beside it,
removed when
the copy is dropped.
Why:
Predicting what
git addwould stage means running it against an index nobody else reads, so the real index never changes before the policies have answered. The copy keeps the real index’s access and modification times: Git re-reads an entry whose stat data is not older than the index file, and a copy with a fresh time would make it trust stat data from the same second as a later edit of the same size, so the prediction could miss that edit (the installed wrapper’sindex-file-timestamps.ts, issue #544). - candidate_
reader - The long-lived object reader that answers every blob read of an invocation.
What:
One long-lived
git cat-file --batchprocess that answers every object read of an invocation. Why: Starting Git costs far more than reading one object, so candidate bytes are read through a single process whose count does not grow with the number of files. Blob bytes are remembered by object name: a name always denotes the same bytes, so a remembered blob can never become stale. - candidate_
record - Pure parsing of raw NUL-delimited changed-path records.
What:
Parse the NUL-delimited raw records of
git diff-indexandgit diff-tree. Why: One listing process names every changed path with its mode, object and change kind, so no per-path Git process is ever needed. Pathnames are kept as the raw bytes Git printed; they are never decoded. - candidate_
stage - Pure parsing of
git ls-files --stagerecords and the delta between two index states. What: Parse the NUL-delimited stage records ofgit ls-files --stage -z, find the paths one staging operation changed, and turn them into candidate records. Why:git addis checked before Git runs it, by replaying it on a private copy of the index. The paths it would stage are exactly those whose index records differ between the copy before and after the replay. Comparing complete records, conflict stages included, finds a resolved conflict like any other change, and an unrelated conflict elsewhere in the index never stops the comparison. Pathnames stay the raw bytes Git printed. - candidate_
store - Per-invocation listing,
lazy bytes and invalidation of candidate versions.
What:
The per-invocation owner of candidate versions and candidate bytes.
Why:
Policies read committed or staged content,
never whatever is in the live
worktree.
A version costs one listing process,
its bytes come from one
long-lived object reader,
and neither count grows with the number of files.
After a fix or replay changed the index,
invalidateretires every version. - candidate_
tree - The files of a commit’s tree with their modes and objects.
What: Parse the NUL-delimited records of
git ls-tree -r -z --full-tree <commit>: every file of one commit with its mode and object. Why: The dependent-version policy compares the candidate state withHEAD: it reads each workspace manifest’sHEADbytes, and a direct fix may change a tracked file it did not select only whenHEADholds that file as the same ordinary blob. One listing answers both for every path. Pathnames stay the raw bytes Git printed. - candidate_
version - Immutable candidate versions: identity, pathname, mode, change and object per path. What: An immutable candidate version: the paths one Git state changes, each with its identity, mode and object. Why: Every policy of one pass must judge the same files. A version is listed once and then only read, so path metadata is shared instead of re-queried, and a later fix or replay produces a new version instead of altering this one.
- scanner_
adapter - The linked forbidden-strings scanner: one load per invocation, typed redacted findings. What: The repository’s forbidden-strings scanner, linked in and loaded once per invocation. Why: The TypeScript wrapper wrote each candidate to a temporary file, started the scanner as a child process and parsed its standard error. Here candidate bytes go straight to the scanner library, and its own typed findings come back. Nothing is re-derived: rule loading, the rule cache, matching, redaction and the fail-closed findings all stay the scanner’s.
- scanner_
failure_ code - Which engine failure code each candidate and scanner failure carries.
What:
Which engine failure code each failure of the candidate and scanner layers carries.
Why:
An engine failure names its cause.
content-unavailablemeans candidate bytes or a repository fact could not be read;policy-incompletemeans the policy’s own machinery failed. Deciding the code here, beside the failures, makes every policy that reads candidates or scans them report the same code for the same cause. - scanner_
run - One scan pass over a candidate version’s exact bytes. What: Scan every eligible candidate of one version through the loaded scanner. Why: This is the whole forbidden-strings check of one policy pass: candidate bytes come from the candidate store (staged or committed, never the worktree), go to the scanner in memory, and the scanner’s own results come back. No temporary file is written and no process is started per file.
- scanner_
selection - Rules-file precedence and which candidates the scanner is given.
What:
Which rules files 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 (the configured
rulesFileentries, then theFORBIDDEN_STRINGS_RULESlist, elseforbidden-strings.local.txtin the repository root) and the paths that hold the rules themselves, which would otherwise match their own content.