Internals
Follow the non-spawning foreground collector, asynchronous Git refresh, cache publication, and ZLE redraw lifecycle.
The main performance rule is simple: foreground prompt rendering never starts Git or Node.
Foreground path
At each prompt, nbsp:
- receives the physical working directory obtained with
getcwd(3)and abbreviates its parent segments; - canonicalizes that path and inspects
.gitmetadata while walking toward the filesystem root, without crossing onto a different device; - reads
HEADdirectly from the resolved Git directory; - loads one bounded, validated cache snapshot for detailed Git counters and
accepts those counters only when its branch matches the current
HEAD; - derives an ASCII version label from
NVM_BINwithout checking a Node process or NVM runtime state; - combines those facts with status, duration, and job count.
The prompt and data commands share this collector. The first renders the
fixed Zsh prompt; the second serializes the facts. The collector can perform
filesystem and cache I/O, but it does not start Git or Node.
Background refresh
Detailed repository state comes from:
git --no-optional-locks -C "$repo_root" \
--git-dir="$git_dir" --work-tree="$repo_root" \
status --porcelain=v2 --branch --show-stash \
--untracked-files=normal --ignore-submodules=dirty --no-renames
The Zsh integration starts nbsp refresh in the background. The worker removes
Git repository-selection variables from its environment, gives Git /dev/null
for standard input and error output, and parses only its bounded standard
output. The refresh:
- discovers the repository;
- obtains a per-repository lock;
- runs Git with a bounded timeout and output cap;
- parses a complete porcelain-v2 snapshot;
- publishes it through an atomic pathname replacement in the cache directory;
- notifies ZLE when the worker completes.
ZLE then reloads prompt data and redraws. A failed or timed-out refresh does not publish a partial snapshot; absent a concurrent cache clear, the last cache pathname remains in place.
The counters describe porcelain records, not an exhaustive file total. One
tracked record can increment both staged and modified counters. With
--untracked-files=normal, an untracked directory can be one record rather than
one record per descendant. Submodule worktree dirt is ignored, while a changed
commit recorded by the superproject can still appear. Rename detection is
disabled, so Git can report delete/add records instead of one rename. These
semantics follow Git’s status options and porcelain-v2 contract.
Branch and counter coherence
The branch can be available before detailed counters because current HEAD is
read separately. A prompt in a repository can therefore contain:
git_present=1
git_valid=0
git_branch=main
git_valid changes to 1 only when the cached branch and the branch read for
that foreground collection are both available and identical. If current branch
metadata cannot be read, a cached branch label may still be shown, but its
counters remain invalid. A refresh also rereads the branch after Git exits and
refuses publication if the branch changed during collection.
This gives one branch-coherent point-in-time snapshot. The worktree or branch can change immediately afterward, so it is not a promise of continuously live state.
The 250 ms debounce
A structurally valid snapshot younger than 250 ms suppresses a duplicate background refresh. The check runs before and after obtaining the repository lock. The foreground collector still refuses its counters if the branch no longer matches.
nbsp refresh --force bypasses only this debounce. Locking, timeout, cache
validation, and pathname-replacement rules remain unchanged. The Zsh
integration uses it when a refresh that started before a foreground command
must be followed by a post-command snapshot.
Cache safety
Cache loading rejects snapshots that are malformed, oversized, incomplete, from another repository, from another format version, or carry a missing, zero, or nondecimal timestamp. Known fields must occur exactly once; syntactically valid unknown fields are ignored for forward compatibility.
Cache location precedence is:
$NBSP_CACHE_DIR;$XDG_CACHE_HOME/nbsp;~/Library/Caches/nbsp.
NBSP_CACHE_DIR must be an absolute path and is fail-closed: an invalid or
unusable override does not fall through to another location. XDG_CACHE_HOME
is used only when absolute. The final cache directories must be owned by the
current user with mode 0700; cache and lock files must be owned by that user,
be regular single-link files, and have mode 0600. Path components are opened
without following symlinks, apart from the exact root-owned /tmp and /var
aliases accepted on macOS.
Writers flush and fsync a same-directory temporary file before renameat(2)
replaces the cache pathname. This prevents readers from accepting a partially
written file, but it is deliberately described as atomic pathname replacement,
not as a durable filesystem transaction: the containing directory is not
fsynced.
nbsp cache clear removes matching cache and temporary snapshot files that
pass the secure-file checks. It preserves persistent per-repository lock files
so a concurrent refresh cannot begin using a different lock inode. A clear can
also remove an active temporary file, in which case that refresh’s publication
fails cleanly rather than recreating data behind the clear.
Redraw and terminal output
The asynchronous worker communicates completion through a dedicated file descriptor. Prompt redraw does not hide or truncate the stdout or stderr of foreground commands.
In built-in mode, nbsp replaces only the prompt body it previously rendered,
preserving user wrappers around it. In detached mode, it never reads or writes
PROMPT or RPROMPT. Custom OSC sequences remain under user ownership.
Deliberate boundaries
There is no persistent daemon, database, prompt-format parser, or native Zsh module. Those approaches can reduce process startup further, but introduce more lifecycle, compatibility, distribution, and crash-isolation cost than this project currently accepts.