On this page
The argv prefixes a preview may really execute, the written standard every entry satisfies -- no user-visible mutation -- and the category each declares.
#rlsbl.observe_allowlist
#rlsbl.observe_allowlist
The observe allowlist and the written standard every entry must satisfy.
An observe is a subprocess that really executes under --dry-run instead of being recorded. strictcli matches an argv against these prefixes element-wise by string equality; a match means the run is performed for real and is legal even inside a read_only command. That makes this list the one place where a preview is allowed to touch the outside world, so every entry needs a reason that survives being read out loud.
The standard: no user-visible mutation --------------------------------------
An allowlisted program may not change anything a user would notice.
Legal under the standard:
- Reads of local state -- the working tree, the object database, refs,
config, the filesystem.
- Reads of a remote over the network -- an HTTP GET against a registry or
the GitHub API changes nothing on the far side.
- Cache writes -- a package manager populating its own download cache is
invisible plumbing: it changes no project state, no output, and no later decision. Deleting the cache costs a re-download and nothing else.
- Scratch writes -- loose objects added to an object database, and
brand-new directories the running process owns. A program qualifies only if it writes NO ref, NO index and NO worktree state in any pre-existing repository: loose objects are unreachable garbage until a ref names them, and a fresh scratch directory is not state anybody had before the preview started. Deleting either costs a recomputation and nothing else, which is the same reasoning the cache clause rests on.
Not legal under the standard:
- Ref updates -- writing any ref, including remote-tracking refs and
FETCH_HEAD. A preview that moves refs has changed the repository. One entry is retained against this ban by an explicit ruling rather than by the ban's own reading: the pinned git fetch origin --quiet, reconciled below.
- Index writes -- anything that takes
index.lock. In a worktree
shared by several sessions the lock is a real hazard, not a formality: a preview must not be able to make a concurrent commit fail.
- Credential emission -- printing a live token to stdout. A preview
must not put a secret on a pipe, into a captured buffer, or into a log, no matter what the caller intends to do with it.
Consequences of the standard, entry by entry --------------------------------------------
git fetchis pinned to the exact argv ``["git", "fetch", "origin",
"--quiet"]rather than the two-token prefix it used to carry. Fetching downloads objects and rewritesFETCH_HEADand the remote-tracking refs, which is a ref update; the narrow prefix keeps the one call site the release flow needs while refusing--pruneand--tags``, which the old prefix silently legalized.
Why it is retained even though the ban names ref updates. This is a ruling, recorded here so nobody has to re-derive it: FETCH_HEAD and origin's remote-tracking refs are git-internal plumbing that no user workflow reads as state. They are cache-like -- deleting them costs a re-fetch and nothing else -- which is the same reasoning the standard already applies to a package manager's download cache. refs/heads and the index are not: those are what a user's work sits on, and writing either from a preview is refused with no exception. The pin is what keeps the ruling narrow: the two-token prefix admitted the short mutating forms (git fetch --prune, git fetch --tags, git fetch --all), and those do reach refs a user reads. Prefix matching cannot refuse a flag APPENDED to the pinned argv (git fetch origin --quiet --prune still matches) -- rlsbl's own call sites are the only producer of these argvs, so what the pin buys is that a future call site cannot reach a mutating fetch by writing a shorter one.
git status/git diff/git diff-indexcarry
--no-optional-locks, both here and at every call site. Without it those commands refresh the index and take index.lock.
npm viewstays: a registry read whose only write is to npm's own cache.go listis split into the two forms rlsbl actually issues -- ``go list
-m ...(the module-proxy notification) andgo list -e -f ...(package enumeration). The bare two-token prefix also admittedgo list -mod=mod ..., which updates go.modandgo.sum`` in place: a manifest write a user would notice, reached through an entry written for a read.
gh auth statusis pinned to ``["gh", "auth", "status", "--hostname",
"github.com"], and its one caller (utils.check_gh_auth) issues exactly that argv. The two-token prefix it used to carry also matched gh auth status --show-tokenand its short form-t``, both of which print the live credential to stdout -- credential emission, admitted by a prefix that was written for the token-free form. github.com is the only host rlsbl talks to, so naming it costs the check nothing.
gh auth tokenis gone. It printed a live credential to stdout, so
it was never observe-safe under any reading of the standard. Its three former callers now let gh resolve and use the credential internally (gh api), so the token never transits an rlsbl pipe.
git subtree split --prefixis pinned with--prefixas its own token,
and the mirror reconciler issues exactly that spelling (["subtree", "split", "--prefix", path], not --prefix=path) so the pin can name it. A branchless split prints a SHA and materializes the synthetic split ancestry as loose objects: no ref, no index, no worktree -- the scratch-write clause. Its residual hazard is the same class as the fetch's: -b <branch> DOES create a ref, and prefix matching cannot refuse a flag APPENDED after the pin. What the pin buys is that -b cannot be reached by writing a SHORTER argv, since a -b in the third position no longer matches.
git clone --quiet --single-branch --branch mainis admitted for its
DESTINATION, not for its flags: the mirror reconciler's inspection clone writes into a directory inside a temp dir the same observation just created (effects.observe_scratch_dirs), so the whole write is scratch the process owns and deletes. Prefix matching cannot see a destination, so the entry is pinned to the exact spelling that one call site issues -- a future call site cloning somewhere durable would satisfy the prefix while breaking the reason, which is why the reason is recorded on the entry rather than assumed.
Purity, which this list also defines ------------------------------------
rlsbl/data/checks.toml declares each check pure or not, and the rule is stated in terms of this list: a pure check starts only programs whose argv matches one of these prefixes. Two consequences worth recording, because they used to be accidents rather than decisions:
config-schemacan reachgo liston its error path, and the retired
local-tag shelled out to git tag --list. Both were declared pure under the older "starts no program" rule and were therefore misdeclared. Under the standard above they are legitimately pure, and config-schema stays declared that way deliberately.
- Nine further checks that spawn only read-only local git flipped from impure
to pure for the same reason.
Adding an entry ---------------
Every entry declares a category from :data:OBSERVE_CATEGORIES and a reason. tests/test_observe_allowlist.py asserts the shape (declared category, non-empty reason, at least two tokens) and runs a corpus of known-mutating argvs past every prefix. A new entry that cannot be justified in one of the declared categories does not belong here.
This list is also what :func:rlsbl.preview_apply.no_writes screens effects.run against: during a reconciler's observation, an argv that matches no prefix here is refused outright. So the list answers one question in one place -- "may this program run while we are only looking?" -- instead of being shadowed by a second, opposite-polarity denylist.
#prefixes
def prefixes()The allowlist in the shape strictcli.App takes it.