Skip to content

v3.16.3

Choose a tag to compare

@cldmv-bot cldmv-bot released this 17 Sep 13:27
· 9 commits to master since this release
v3.16.3
91c7317

release: v3.16.3 - install routine cascade only at root api., not… (#401)

Slothlet v3.16.3 Changelog

Release Date: September 2026
Release Type: Patch
Branch: release/3.16.3


Overview

Version 3.16.3 corrects two never-intended surfaces in the lifecycle and
routine systems shipped in v3.16.0. The public impl:created /
impl:changed events stop handing subscribers the module's raw,
unwrapped callable and stop announcing leaves that collision resolution
then discards; and a configured routine's root cascade is no longer
mirrored onto slothlet's own api.slothlet.* control namespace. Both
are corrections to behavior that was never meant to exist, so they are
treated as fixes rather than features — but each removes a surface a
consumer could have come to rely on, so the upgrade notes below call out
the one-line migration for each.


🐛 Bug Fixes

impl:created / impl:changed no longer leak the raw callable or

fire for merge-discarded leaves (#398)

The public lifecycle events carried data.impl — the module's raw,
unwrapped implementation
. A subscriber holding it could invoke a leaf
outside slothlet's enforced boundary: no active self extent, no
permission gate, none of the wrapping every real call goes through. The
events also fired per contribution at construction time, before
collision resolution decided placement
, so when two modules
contributed the same leaf path they fired for both — including the one
merge then dropped from the tree. A registry built off the event (the
exact pattern docs/LIFECYCLE.md documents) could therefore record an
enforcement-bypassing raw callable, and map a path to a contributor
whose value never actually lands on the composed api.

The lifecycle emitter now has an internal contribution tier. The
framework's own metadata, routine-manager and ownership systems ride
that internal stream, so they still observe every contribution
(merge-losers included) exactly as before. The public impl:created
/ impl:changed now fire post-placement, once, only for the
contribution that actually owns the path
, and carry the wrapped
leaf on data.wrapper.__impl (a frozen, minimal { __impl } shape)
instead of the raw data.impl. impl:removed is unchanged. See
LIFECYCLE.md for the updated payload
and the Module Registry pattern.

Routine cascade is installed only at the root api.<name>, not

mirrored onto api.slothlet.<name> (#399)

A configured routine's root cascade was installed at two places: the
canonical root api.<name>() and a duplicate api.slothlet.<name>().
api.slothlet.* is slothlet's own control namespace (run, api.add,
versioning, the shutdown / destroy dispose builtins); a consumer
routine's cascade does not belong there, and because both resolved,
consumer code could end up written against the wrong surface
(api.slothlet.initialize() instead of api.initialize()).

The cascade now installs only at the root api.<name> (both the
initial build and the reactive per-path self-heal). The shutdown /
destroy dispose builtins keep their own api.slothlet.shutdown /
api.slothlet.destroy — those are a separate framework surface,
integrated into the existing dispose functions, and are unaffected. See
LIFECYCLE.md.


🔧 CI & tooling

Opt-in, approval-triggered release-merge caller (#397)

Adds an opt-in caller workflow that lets an approval trigger the next → master release merge, without changing the default release flow for
repos that don't enable it.


📚 Documentation


Upgrade notes

  • No change for the vast majority of consumers — normal api calls,
    module authoring, routines, hooks, and permissions are all unaffected.
    The two changes below only matter if you subscribe to lifecycle events
    or invoke a routine cascade through the control namespace.
  • Lifecycle subscribers reading data.impl (#398): switch to
    data.wrapper.__impl. The raw impl field is gone from impl:created
    / impl:changed; data.wrapper.__impl is the leaf's implementation,
    and the event now fires post-placement for the placed owner only (so a
    registry keyed off it no longer records a merge-discarded contribution).
    This removes an enforcement-bypassing callable that was never meant to
    be exposed.
  • Routine cascades invoked as api.slothlet.<name>() (#399): call
    api.<name>() instead (e.g. api.initialize() rather than
    api.slothlet.initialize()). The api.slothlet.<name> mirror no longer
    exists for consumer routines. The shutdown / destroy dispose
    builtins still respond at both api.shutdown() /
    api.slothlet.shutdown() (and destroy) as before — only the mirrored
    routine cascade was removed.

coverage

Metric Coverage
Statements 99.9%
Branches 99.9%
Functions 99.9%
Lines 99.9%

Avg: 99.9% · f1ef7e1 · Node lts/*

👥 Contributors