Repository navigation
feat: api.slothlet.restart() — a clean-slate rebuild that swaps in a fresh instance behind the same api reference #504
Copy link
Copy link
Closed
Labels
area: coreTouches core library / runtime source codeTouches core library / runtime source codepriority: highNeeds attention soon — not blocking, but don't let it sitNeeds attention soon — not blocking, but don't let it sitstatus: implementedBuilt and deployed, but not yet fully tested/verifiedBuilt and deployed, but not yet fully tested/verifiedtype: feature requestA request to add new functionality that does not currently existA request to add new functionality that does not currently exist
Description
Problem
reload() is deliberately not a clean slate. After a full reload, slothlet keeps:
- References —
apiand any held leaf (api.math.add) keep working. - Runtime mutations — every
api.slothlet.api.add()/remove()is replayed from the operation history (docs/RELOAD.md, "Operation History and Replay"). - Runtime assignments —
self.x = …overrides are flaggeduserAssignedand survive verbatim (O15, docs/API-RULES/API-COLLISION-CONDITIONS.md).
That is the right contract for hot-reloading code while an app keeps running, but there is no way to get the opposite: "throw away everything that happened at runtime and come back up exactly as a fresh slothlet({...}) would". Example:
// api/conn.mjs — the module's top level creates the emitter
import { EventEmitter } from "node:events";
const conn = new EventEmitter();
export default conn;api.conn.handlers = { onSend }; // runtime assignment → userAssigned (O15)
await api.slothlet.api.reload(); // conn.mjs re-runs, but api.conn.handlers is still thereAfter a reload, api.conn is a new emitter from the module's top level, yet the runtime-assigned handlers is re-applied on top of it. For a "start over" there is currently nothing short of shutting the instance down and building a new one, which invalidates every reference callers hold.
Proposal
Keep reload() exactly as it is. Add api.slothlet.restart():
- Builds a new internal instance from the instance's original config (same as the
slothlet({...})call that created it). - Swaps it in behind the existing
apiroot proxy, so callers' references keep working, the same way reload preserves them. - Shuts the old instance down through the normal teardown path (
shutdownlifecycle, handler cleanup, permission/version managershutdown()), so nothing from it leaks. - Nothing runtime is carried over: no
userAssignedoverrides, no module-scope state from the old imports (fresh import, cache-busted as reload already does).
Open questions (decide before implementing)
- Runtime
add()/remove()history: replay it (restart = "fresh process with the same mounts") or drop it (restart = "exactly the originalslothlet({...})call")? - Runtime-registered hooks, permission rules and event subscriptions: carried over or dropped? Dropping matches "clean slate", but a caller that registered a hook at startup loses it.
- Held child references: a caller holding
api.conn(not justapi) — should it re-point to the new instance'sconn, as reload does, or is only the root reference guaranteed? - Lifecycle events: a new
restartevent, orshutdown(old) +init(new)?
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area: coreTouches core library / runtime source codeTouches core library / runtime source codepriority: highNeeds attention soon — not blocking, but don't let it sitNeeds attention soon — not blocking, but don't let it sitstatus: implementedBuilt and deployed, but not yet fully tested/verifiedBuilt and deployed, but not yet fully tested/verifiedtype: feature requestA request to add new functionality that does not currently existA request to add new functionality that does not currently exist
Activity
Shinrai commentedon Sep 29, 2026
Design decisions (maintainer, 2026-09-29): a restart is exactly like creating the instance from scratch; reload keeps holding onto things.
add()/remove()history: dropped — restart = the originalslothlet({...})call.const conn = api.conn): re-point to the new instance's value at the same path, as after reload; a path that no longer exists throws a clear error on use.restart(before anything is torn down) →shutdown(old instance, normal teardown) →init(new instance) →restarted(swap complete).feat(reload): add api.slothlet.restart() — a clean-slate rebuild behi…
feat(reload): add api.slothlet.restart() — a clean-slate rebuild behi…
cldmv-bot commentedon Oct 2, 2026
Marked
status: implemented— resolved by #545, merged intonext. This issue closes automatically when that work ships to the default branch.release: v3.22.0 - type the composed api, self, and slothlet() from e…
cldmv-bot commentedon Oct 3, 2026
Closed in v3.22.0 — resolved by #545.