Skip to content
rlsbl.overlay_state
On this page

Dev-overlay sentinel loading and venv inspection, shared between the ``rlsbl dev sync``/``dev status`` commands and the ``dev-overlay-drift`` check.

#rlsbl.overlay_state

#rlsbl.overlay_state

Dev-overlay sentinel loading and venv inspection, shared between the rlsbl dev sync/dev status commands and the dev-overlay-drift check.

Extracted from commands/dev_sync.py to break the checks/ -> commands/ layering violation: checks/project.py's dev-overlay-drift check needs to read the overlay sentinel and inspect the venv, but the checks layer must not import the commands layer. This module hosts the read-side logic (sentinel loading, venv dist-info inspection, overlay classification) so both layers can consume it without a cross-layer import. The write-side (_write_sentinel) and the sync orchestration stay in commands/dev_sync.py, which is the only writer.

Pure relocation -- no behavior change from the original dev_sync definitions.

#MalformedSentinelError

Raised when the overlay sentinel exists but cannot be parsed or read.

A present-but-corrupt sentinel must never read as "no overlays declared" (which would silently SKIP the drift check and exit dev status 0). The sentinel is regenerable local state, so the remedy is to delete it and re-run rlsbl dev sync. Callers surface this loudly rather than degrading.

#_normalize

python
def _normalize(name)

PEP 503 distribution-name normalization.

#load_sentinel

python
def load_sentinel(project_root)

Read SENTINEL_FILENAME. Returns a list of {"package", "path", "version"} dicts, or None when the sentinel does not exist.

A missing sentinel means no overlays were ever declared -- e.g. a fresh CI checkout, where the gitignored sentinel never existed. That is the honest not-applicable state (skip), never a failure.

A present-but-unparseable sentinel (invalid TOML) or a present-but- unreadable one (OSError) is a hard error (:class:MalformedSentinelError), NEVER a silent empty list: reading corruption as "no overlays" would make the drift check SKIP and dev status exit 0 while overlays may in fact be wiped. Mirrors _load_overlays, which also hard-errors on invalid TOML.

#OverlayModeConflictError

Raised when the overlay declaration and the sentinel disagree.

The two local-only files decide, together, which mode a project's environment is in: neither present is registry mode, both present and agreeing is overlay mode. Any disagreement means neither answer is true, and a command that had to pick one would either wipe the overlays or test against a checkout nobody declared. It is a hard error instead.

#_load_overlay_pairs

python
def _load_overlay_pairs(project_root, filename)

Read package -> absolute checkout path from one of the two overlay files, or None when the file does not exist.

Deliberately lighter than dev_sync._load_overlays: this is the read side, which only needs to know which packages are overlaid and from where. dev_sync keeps the full write-side validation (pyproject name match and the rest), since it is the only writer.

#active_overlays

python
def active_overlays(project_root)

Return the overlays this project's environment is currently running on.

Returns a list of {"package", "path"} dicts in overlay mode, or None in registry mode (neither local-only file present -- CI, and every machine with no overlays). Raises :class:OverlayModeConflictError when the declaration and the sentinel disagree, or when a declared checkout is gone.

The mode is decided by BOTH files, exactly as the sandboxed test runner decides it, so every entry point reads one answer. A command that syncs an environment must consult this before running a bare uv sync: an exact sync reinstalls the locked registry wheel over an editable overlay without a word, and the project would then be tested against released code while its own drift check reports the wipe.

#project_environment

python
def project_environment(project_root)

Return the directory of the environment uv manages for project_root.

uv gives a project exactly ONE environment, and it is not always a .venv beside the project's own pyproject.toml:

  • A uv workspace member shares the environment at the WORKSPACE ROOT

(uv sync and uv pip install from inside the member both target it), and has no .venv of its own. Looking only under the member directory reported a perfectly healthy overlay as missing.

  • UV_PROJECT_ENVIRONMENT relocates it entirely; a relative value is

resolved against the workspace root. Read here for the same reason: it is uv's own selection of the directory this function has to describe, not rlsbl configuration.

#_site_packages_dirs

python
def _site_packages_dirs(env_dir)

Return the existing site-packages directories inside an environment (one per Python minor version present).

#_read_direct_url

python
def _read_direct_url(dist)

Return (editable, path) from an installed distribution's direct_url.json (PEP 610).

A registry wheel has no direct_url.json -> (False, None). An editable install writes dir_info.editable = true and a file:// url pointing at the checkout -> (True, "/abs/checkout"). A non-editable local install -> (False, "/abs/path").

This record is the only honest evidence of editability. An editable install does NOT put a package directory in site-packages -- it writes a .pth import hook whose name and content vary by build backend -- so nothing in the file layout may be used to decide this.

#inspect_installed

python
def inspect_installed(project_root, package)

Inspect the project's environment for how package is installed.

Returns {"found", "editable", "path", "version", "environment"}:

  • found=False: the environment holds no distribution named package.
  • editable=True with path: an editable install; path is the

file:// checkout its direct_url.json points at.

  • editable=False: a registry wheel or non-editable install -- i.e. the

overlay was wiped.

  • environment: the environment directory that was inspected, so a

not-found answer can say where it looked.

Distributions are read through importlib.metadata pointed at the environment's site-packages, so dist-info naming, metadata parsing and the PEP 610 record all follow the packaging standard rather than a hand-rolled guess at the on-disk layout.

#classify_overlay

python
def classify_overlay(entry, installed)

Compare a sentinel entry against the installed venv state.

Returns (state, detail) where state is OVERLAY_HEALTHY / OVERLAY_WIPED / OVERLAY_MISSING and detail is a human-readable line naming the package and the exact rlsbl dev sync remediation.

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