Docs · Architecture

REPL Custody & Lifetimes

who owns what, and how long it lives

Stamped against the code of the v0.5.5 release line (2026-08-18). This is deliberately not a flow map: the REPL is the only surface where things outlive the invocation, so the questions that matter are custody (which verb owns this resource) and lifetime (which layer it dies with).

Why this map exists: every REPL bug of an internal exercise cycle just before v0.5.5 was a misplacement on the ladder below. The chromedriver leak was a JVM-scoped resource nobody owned; the durability overclaim put a JVM-scoped Chrome on the disk layer; the missing relaunch was a state-machine edge nobody had drawn; the bare-browser gap and the broken :closed branch were a context-scoped resource that could neither be created nor reaped. This is the diagram that would have prevented all five. Placements below cite recorded measurements rather than re-measuring.

Where the REPL joins the engine

Both REPL entry points reach the same engine the CLI uses — the genuine differences are exactly two, and both are lifetime facts:

Two reapers, two lifetimes

The load-bearing distinction on this map: run-provisioned and as-provisioned ephemerals have different lifetimes because they have different reapers.

Collapsing these two into “the REPL cleans up browsers” is exactly the class of confusion this map exists to kill.

The lifetime ladder

Every resource sits on exactly one layer. Custody = the verbs that may create or reap it.

diagram diagram

Placement notes:

The daemon JVM — same ladder, different JVM

The warm daemon is a second long-lived JVM this map previously did not place: everything JVM-scoped above is daemon-JVM-scoped when the resource was made by a warm sl run — in particular a worn costume’s Chrome and its chromedriver (the same live-drivers registry, one driver per costume, in the daemon’s process). The reaper-decides-the-layer law applied:

Overlay 1 — cleanup-verb reach

Which layers each verb may touch. The geometry is the cleanup contract (close-leak + mode-awareness landed as one interlocked change); cited to its regression tests, not re-proven.

Verb Reaches Never touches Contract pins
reset-ctx! (repl.clj:106) unnamed session-ctx data any browser (none can exist there)
reset-ctxs! (repl.clj:113) / clear! (repl.clj:489) context layer: :closed for ephemeral :web capabilities (real WebDriver quit + driver kill), then drops all named-ctx state; clear! also empties connected-subjects + registry costumes (:skipped-persistent — mode-aware skip), the live-drivers registry, the wardrobe, the sieve driver repl_close_leak_test.clj:73 (:closed + exactly one quit), :88 (real eta/chrome shape — the bare-browser regression), :100 (zero quits on a costume because cleanup consults :mode), :120 (exact :skipped-persistent action), :136 (mixed ctxs, one quit); repl_test.clj:301/:309 (:none, honest :close-failed)
run (engine reaper) its own scenario’s ephemerals, per scenario anything as provisioned; costumes repl_bare_browser_test.clj:84
destroy-costume! (costume.clj:589) JVM + disk: kills Chrome by port truth (not recorded pid), reaps the owned driver, deletes the profile dir other costumes’ resources costume_test.clj:551 (reap+forget), :569 (failed destroy keeps the handle)
sieve-stop! (user.clj:71) the sieve server + its driver everything else code-read (dev tooling)
JVM death everything JVM-scoped (child processes die with it) the disk layer the JVM-bounce measurement

The asymmetry is the lesson: only destroy-costume! ever kills a costume’s Chrome (repl_close_leak_test.clj:16), and nothing short of JVM death reaps the sieve driver except its own verb.

Overlay 2 — the costume state machine

Edges cite the verb’s docstring or the change that introduced them. reset-ctxs! is deliberately not an edge — a context reset cannot move a costume through this machine (:skipped-persistent); that absence is the mode-awareness half of the cleanup contract.

diagram diagram

The relaunch edge is the one the original machine lacked: once a costume existed, launch-only (browser open, no session attached — what re-authentication needs) was unreachable without dropping to internals.

Siblings

How this map stays true

This page is a mechanical projection of a live-maintained internal map: re-verified against the code by probe runs at each re-stamp, regenerated — never hand-edited — by the derivation pipeline, and drift-guarded by the test suite (a hand edit here fails a test). File:line pins are re-verified at each re-stamp.