Skip to content
rlsbl.observe_allowlist
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 fetch is 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-index carry

--no-optional-locks, both here and at every call site. Without it those commands refresh the index and take index.lock.

  • npm view stays: a registry read whose only write is to npm's own cache.
  • go list is 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 status is 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 token is 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 --prefix is pinned with --prefix as 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 main is 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-schema can reach go list on 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

python
def prefixes()

The allowlist in the shape strictcli.App takes it.

More tools from this site

  • claudestream Drive Claude Code from Python: run it as a subprocess and read its output as typed events, with async and sync sessions, sandbox policies, and tools you define in Python
  • claudewheel A TUI Claude Code Launcher that lets you have more than one profile, manage sessions lifecycle, pick the exact CC version, model to use (even older unlisted ones), pick which GitHub account to use, etc.
  • dirstat Fast, single-binary directory statistics CLI: every file under a tree grouped by format, with counts, sizes, and lines of code, as a colored terminal table or as JSON
  • fastware A batteries-included ASGI framework: msgspec JSON, a managed Granian server, dependency injection, SSE, WebSockets, auth, and a test client
  • go-toml-edit Zero-dep TOML editing library for Go with comment preservation
  • howmuchleft The fastest Claude Code statusline: context window, 5-hour, and weekly limit usage as three customizable gradient bars, rendering in about 6 ms
  • orxtra
  • pgdesign
  • predraw Declarative rendering pipeline: describe a scene in JSON and get SVG, PNG and WebP out, with light and dark style tokens, reusable components and text converted to path outlines
  • reposummary Turn a git repository's history into a Markdown journal: pick a time window or revision range and get a readable digest of what changed, optionally narrated by an LLM
  • safegit git wrapper CLI that gives each commit its own temporary index and retries ref updates on conflict, so concurrent agents share one repository
  • saferm Command-line replacement for rm that archives every deletion with a mandatory reason and the context it ran in, so deleted files can be listed, inspected and restored
  • selfdoc Static Site Generator that builds a project's documentation site directly from its source code, so the docs can never drift from the code they describe, with SEO/AEO, first-class blog, search, and cross-project linking built in
  • strictcli
  • stricttest An always-on test-isolation floor: a pytest plugin and a Go env-hygiene module that make a test suite structurally unable to reach real credentials, the real HOME, the network, or the development repository.
  • wesktop A Python framework that turns an ASGI web app into a desktop application, serving it from a local Granian server and displaying it in a native OS window via pywebview
Search