Git History Tools in DocsFab
The DocsFab git history tools let the planner and the writer read the local git history behind a run’s pinned revision. A step uses them to answer what changed, when it changed, which commit last changed a documented line, which commit added or removed a symbol, and what the old behavior was, for example by reading a file as it was before the change it documents. Every read is bounded, screened, and cited in the run record. For more information, refer to DocsFab Knowledge and Delivery Reference.
Who Gets the Tools
The host registers docsfab_history_blame, docsfab_history_diff,
docsfab_history_log, docsfab_history_search, and docsfab_history_show
only when both of the following conditions hold:
- The step runs the
plan-documentationorwrite-documentationskill. Increate-documentation,update-documentation, andrestructure-documentation, that is the planner and the writer, including the CREATE revision writer. - The run materialized a repository workspace from a local checkout, as
docsfab rundoes. The tools read that checkout’s object store; the model’s own workspace is a tracked-file export with no.git.
The same rule decides the step’s granted tool list and the tools the session
presents, so tools.presented in the step’s attempt record names them exactly
when the step could call them. The five tools are granted together: a sealed
tool ceiling that omits any of the five names, including a ceiling that names
only the four tools that came before search, builds no history reader and
presents none of them. The hosted runtime runs under such a
ceiling, its composition’s declared tools, so it gets no history reader for
its depth-1 private copy. The live agent does not register them.
Tools and Arguments
Each tool takes a JSON object. Every argument other than cursor is optional
on a resumed call, and any argument sent with a cursor must match the page
that issued it. The following table lists each tool, its arguments, and what it
returns.
An argument sent as null, an empty string, or an empty list counts as
omitted, at the top level and inside range. A model that fills every
declared argument can send an unused cursor or range.from as "", and the
tool then reads the call without it rather than refusing an empty cursor or
revision. The run record counts each such argument under
tools.toolArgumentFiller, without its value.
The following table lists each tool, its arguments, and what it returns.
Blame lines count from 1 and the range includes both ends. A file has one
line per line break, plus one for a last line without a break, so an empty
file has none and every range of it is refused. Blame follows a whole-file
rename, as git does by default, but does not look for moved or copied lines
(-M, -C) or ignore whitespace (-w). A repository’s configured
blame.ignoreRevsFile is not applied, so attribution is always the commit
that last changed the line.
Search is for when a symbol, option, or message first appeared or last
vanished. The needle is matched as plain, case-sensitive text: never a
regular expression, glob, option, pathspec, or revision, and never echoed on a
page, in a citation, in a refusal, or in a log line; the host keeps it only in
the attempt’s cursor state. It reaches git as one command-line argument, so
other processes on the same host can see it while git runs. It must not be
empty or hold a NUL or a lone UTF-16 surrogate, and it is measured in UTF-8
bytes against gitHistory.searchNeedleMaxBytes. The following rules
apply to the search:
- Merges never match: git does not diff a merge for the search.
- A match inside a binary file is found and listed by name only.
- In a shallow clone, the commit at the cut is diffed against nothing, so it
is charged with every older addition of the string. Its match carries
atShallowCut: true. - A rename reads as a deletion and an addition, so a renamed file holding the string lists both names.
- A matched file that is credential-bearing, generated, or named with bytes
that are not UTF-8 is never listed, and a commit with no other matched file
is dropped as if it never matched. A match only in such a file leaves no
trace: the page, its labels, and its citation are those of a string found
nowhere, apart from the cursor handle, which is random on every page. Labels
come from a list of the window’s changed files read without the string. One
exception remains: the search reads each matched commit’s subject, so a
string found only in such files of many commits with very long subjects can
make the read pass
hostInvoker.gitOutputMaxBytesand refuse withhistory-output-too-large. - A served file name that holds a configured credential is listed as
null.
The walk is scanned in windows of gitHistory.pickaxeMaxCommitsScanned
commits. A page continues with nextCursor to the next window even when its
window matched nothing, so nothing is skipped.
A revision is head, the run’s pinned revision, or a lowercase hex commit id
of at least gitHistory.revisionMinHexChars (7) characters and at most the
repository’s full id length (40 for SHA-1, 64 for SHA-256). An abbreviation resolves from the object store, never
from a branch or tag name. Only the pinned revision and its ancestors can be
read, so another branch, a stash, or a dangling commit is refused. A path is
repository-relative and literal: globs and pathspec magic are names, not
patterns.
The tools never fetch, never run a hook, filter, textconv driver, external diff, pager, or signature verifier, and never write to the repository. They ignore replace refs and a grafts file, and they pin every output format, so repository configuration cannot change what they return.
Labels
A label reports a limitation on a page that was still served. It is recorded with the page’s citation. The following table lists the labels.
The following table lists each label and its meaning.
Refusal Codes
A refused read returns DocsFab refused this history read: <code>. with a
remediation, records nothing as a citation, and records its failure category
in the step’s tool activity. No new failure category is introduced.
malformed-arguments and recoverable-miss can be recorded as limitations of
a not-ready outcome, and so can denied-path and tool-defect on a
read-only step. On a write step, denied-path and tool-defect stay material,
and a ready outcome refuses every one of these categories. The following
table lists the refusal codes.
The following table lists each code, its category, and when it applies.
A catalogued bound that a call exceeds refuses with that limit’s own refusal
code, records recoverable-miss, and names the observed value and the
remediation. A file that holds one of DocsFab’s configured credentials is
refused whole with the credential-isolation boundary message. In a diff, only
the file whose patch holds one is listed as withheld without its patch, and
in a log, only that commit’s subject is withheld; the rest of the page is
served.
Bounds and Paging
The bounds live in config/limits/gitHistory.yaml. docsfab doctor --limits
shows their effective values. The following table lists the bounds.
The following table lists each limit, its default, and what it bounds.
gitHistory.responseMaxBytes must not exceed the per-call tool output cap
(DOCSFAB_TOOL_OUTPUT_CAP_BYTES, 32,768 bytes by default). When it does, the
step refuses before any provider turn with
git-history-response-max-bytes-bound. Every git call also runs under the
sealed hostInvoker.gitTimeoutMs and hostInvoker.gitOutputMaxBytes.
A file, diff, walk, or blamed range larger than one page is never cut
silently. The page returns nextCursor, an opaque handle that the host keeps
for the rest of the attempt, bound to the tool, the resolved revisions, the
admitted paths, and the offset; for blame, to the revision, the path, the
asked line range, and the next line to serve. The same page asked again
returns the same cursor, and the five tools share one cursor bound. A page
ends at a line break when it holds one. A blame page serves whole lines, so
one line longer than a page is refused with
git-history-response-max-bytes-bound.
A search cursor is bound to the needle, the resolved range ends, the admitted
paths, the window’s offset in the walk, and the next match and file to serve.
A window’s matches page by bytes, and one commit whose files do not fit on a
page continues them on the next page, so a commit can appear on more than one
page. Only one file of one match too long for an empty page is refused with
git-history-response-max-bytes-bound. Each new window walks again past the
commits that earlier windows scanned, so a search limited to paths costs more
the deeper it goes.
Citation Fields
The host records each served page in the attempt’s tool activity as an entry
of tools.historyReads, and copies the attempt entries, in attempt order, to
the step. The model supplies none of these fields. The following table lists the fields.
The following table describes each field.
The number of entries is bounded by the step’s tool-call ceiling,
compositionExecution.maxToolCallsPerStep, because each successful call
serves at most one page. The record readers refuse an entry that is not a full
object id and more entries than a tool’s successful calls, a lines range
from any tool but blame or starting below line 1, and a blame entry with more
than one revision, path, or range. They refuse a search entry without
matchedCommits, with a repeated or abbreviated commit, with more commits than
its window scanned or than gitHistory.pickaxeMaxCommitsScanned allows at its
maximum, or with any range but one window of entries; and matchedCommits on
any other tool. The publication record reader also refuses step entries that
differ from the attempts’ entries. The host and the runtime ship together, so
no reader of an earlier release reads a record that cites blame or search.
Related
For more information, refer to the following topics: