Skip to content

setup_stamp

setup_stamp

Setup-stamp primitive: cheap "did the user run setup?" check for the TUI startup path.

Background

The TUI's start-up budget is ≤2 s. A real sickbay health check takes seconds — too slow for an every-launch probe. Instead we drop a small JSON marker after a successful setup run and compare it against the currently-installed package versions on each launch:

  • match → OK, no surface
  • absent → FIRST_RUN, blocking modal nudges the user to run setup
  • installed > stamped → STALE_AFTER_UPDATE, non-blocking banner
  • installed < stamped → STALE_AFTER_DOWNGRADE, blocking warning (downgrades aren't tested; explicit override required)
  • stamp can't be parsed → STAMP_CORRUPT, treated as FIRST_RUN

Lives in sandbox because it's the lowest layer — every other ecosystem package depends on sandbox, so the primitive is reachable from any frontend without inverting the dep graph.

Resolution detail

Package versions are read via importlib.metadata.version — no subprocess, no parsing, sub-millisecond per package. Total cost of needs_setup on a happy path: one Path.is_file, one JSON decode, four to five importlib.metadata lookups. Well under the 2 s budget.

The set of tracked packages is the v0 ecosystem (terok, terok-executor, terok-sandbox, terok-shield, terok-clearance). A package missing from the local install is ignored on the read side — standalone sandbox installations don't have terok and shouldn't see STALE_AFTER_DOWNGRADE because of it.

__all__ = ['SetupVerdict', 'clear_stamp', 'needs_setup', 'stamp_path', 'write_stamp'] module-attribute

SetupVerdict

Bases: Enum

Result of needs_setup — five possible states a launch can be in.

OK = 'ok' class-attribute instance-attribute

Stamp matches all installed package versions exactly.

FIRST_RUN = 'first_run' class-attribute instance-attribute

No stamp on disk — the user has never run setup (or wiped state).

STALE_AFTER_UPDATE = 'stale_after_update' class-attribute instance-attribute

At least one installed package is newer than the stamped version.

STALE_AFTER_DOWNGRADE = 'stale_after_downgrade' class-attribute instance-attribute

At least one installed package is older than the stamped version.

Downgrades aren't tested and can leave systemd units / state DB in forms the older code can't interpret. Frontends should treat this as a hard stop until the user explicitly overrides.

STAMP_CORRUPT = 'stamp_corrupt' class-attribute instance-attribute

Stamp file exists but can't be parsed. Frontends should treat as FIRST_RUN.

stamp_path()

Return the canonical on-disk location of the setup stamp.

Honours the umbrella paths.root resolver so a user who relocates the state tree (paths.root: /virt/terok in config.yml) sees the stamp move with it — same place every package would look.

Source code in src/terok_sandbox/setup_stamp.py
def stamp_path() -> Path:
    """Return the canonical on-disk location of the setup stamp.

    Honours the umbrella ``paths.root`` resolver so a user who relocates
    the state tree (``paths.root: /virt/terok`` in ``config.yml``) sees
    the stamp move with it — same place every package would look.
    """
    return namespace_state_dir() / _STAMP_FILENAME

needs_setup()

Compare the on-disk stamp against currently-installed package versions.

See SetupVerdict for the five possible outcomes. Designed to be cheap enough to call on every TUI startup.

Source code in src/terok_sandbox/setup_stamp.py
def needs_setup() -> SetupVerdict:
    """Compare the on-disk stamp against currently-installed package versions.

    See [`SetupVerdict`][terok_sandbox.setup_stamp.SetupVerdict] for the five possible outcomes.  Designed
    to be cheap enough to call on every TUI startup.
    """
    path = stamp_path()
    if not path.exists():
        return SetupVerdict.FIRST_RUN
    if not path.is_file():
        # Something at the stamp location, but not a regular file — a
        # directory or device left there by a misbehaving sync tool.
        # That's not "user hasn't run setup" (FIRST_RUN); it's a corrupt
        # state the next ``write_stamp`` would also fail on.
        return SetupVerdict.STAMP_CORRUPT

    try:
        stamped = read_stamp(path)
    except (OSError, json.JSONDecodeError, ValueError):
        return SetupVerdict.STAMP_CORRUPT

    installed = installed_versions()
    return _compare(stamped, installed)

write_stamp()

Capture the currently-installed versions to disk and return the path.

Called by _handle_sandbox_setup after a successful run. An atomic temp-file + rename keeps a partial write from leaving a half-stamp that needs_setup would later flag as STAMP_CORRUPT.

Source code in src/terok_sandbox/setup_stamp.py
def write_stamp() -> Path:
    """Capture the currently-installed versions to disk and return the path.

    Called by ``_handle_sandbox_setup`` after a successful run.  An
    atomic temp-file + rename keeps a partial write from leaving a
    half-stamp that ``needs_setup`` would later flag as ``STAMP_CORRUPT``.
    """
    path = stamp_path()
    path.parent.mkdir(parents=True, exist_ok=True)

    payload = {
        "version": _STAMP_SCHEMA_VERSION,
        "completed_at": datetime.now(UTC).isoformat(timespec="seconds"),
        "packages": installed_versions(),
    }
    tmp = path.with_suffix(path.suffix + ".tmp")
    tmp.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
    tmp.replace(path)
    return path

clear_stamp()

Remove the stamp file if present. Returns True if a file was removed.

EAFP rather than is_file + unlink: keeps the function race-safe under a (rare) concurrent terok uninstall instead of leaking a FileNotFoundError between the existence check and the unlink. Used by terok uninstall and by tests that want to simulate a fresh-install state without nuking the rest of the state tree.

Source code in src/terok_sandbox/setup_stamp.py
def clear_stamp() -> bool:
    """Remove the stamp file if present.  Returns True if a file was removed.

    EAFP rather than ``is_file`` + ``unlink``: keeps the function
    race-safe under a (rare) concurrent ``terok uninstall`` instead of
    leaking a ``FileNotFoundError`` between the existence check and the
    unlink.  Used by ``terok uninstall`` and by tests that want to
    simulate a fresh-install state without nuking the rest of the
    state tree.
    """
    try:
        stamp_path().unlink()
    except FileNotFoundError:
        return False
    return True

installed_versions()

Return {package: version} for every tracked package present in the install.

Missing packages are silently dropped — a standalone terok-sandbox install doesn't have terok available, and that's fine. The invariant we check on the read side is that every package the stamp knows about is also installed (and at the right version).

Source code in src/terok_sandbox/setup_stamp.py
def installed_versions() -> dict[str, str]:
    """Return ``{package: version}`` for every tracked package present in the install.

    Missing packages are silently dropped — a standalone ``terok-sandbox``
    install doesn't have ``terok`` available, and that's fine.  The
    invariant we check on the read side is that every package the *stamp*
    knows about is also installed (and at the right version).
    """
    out: dict[str, str] = {}
    for pkg in _TRACKED_PACKAGES:
        with contextlib.suppress(PackageNotFoundError):
            out[pkg] = _meta_version(pkg)
    return out

read_stamp(path)

Parse the stamp file, returning the packages mapping.

Raises ValueError if the schema version doesn't match — a schema bump should be handled explicitly, not silently coerced.

Source code in src/terok_sandbox/setup_stamp.py
def read_stamp(path: Path) -> dict[str, str]:
    """Parse the stamp file, returning the ``packages`` mapping.

    Raises [`ValueError`][ValueError] if the schema version doesn't match — a
    schema bump should be handled explicitly, not silently coerced.
    """
    raw = json.loads(path.read_text(encoding="utf-8"))
    if not isinstance(raw, dict):
        raise ValueError(f"stamp root is not an object: {type(raw).__name__}")
    if raw.get("version") != _STAMP_SCHEMA_VERSION:
        raise ValueError(f"unsupported stamp schema version: {raw.get('version')!r}")
    pkgs = raw.get("packages")
    if not isinstance(pkgs, dict):
        raise ValueError(f"stamp packages is not an object: {type(pkgs).__name__}")
    # Coerce values to str so a malformed stamp with an int can't sneak past.
    return {str(k): str(v) for k, v in pkgs.items()}