Skip to content

A : in moduleID corrupts the ownership index — add() returns the id but leaves()/remove() can't find the mount #303

Description

@Shinrai

Summary

When a moduleID containing a colon is passed to api.slothlet.api.add(), the mount succeeds and the leaf resolves, and add() returns that exact id — but the moduleID→paths ownership index is not keyed under it. As a result api.slothlet.api.leaves(id) returns [] and api.slothlet.api.remove(id) returns false, so the mount cannot be reloaded or removed by the id add() handed back. It is silently unmanageable. Only the colon triggers this; /, ., and - in a moduleID all round-trip correctly.

Reproduce

Verified against @cldmv/slothlet@3.14.0 (repo v3.13.3-9-g0dd03b7), eager mode, empty api dir:

const id = await api.slothlet.api.add("t.a", () => 1, { moduleID: "vine:abc" });
id;                                       // → "vine:abc"   (the exact id back)
await api.slothlet.api.leaves("vine:abc"); // → []          (index doesn't know it)
await api.slothlet.api.leaves(id);         // → []
await api.slothlet.api.remove(id);         // → false       (can't remove) — path still resolves
typeof api.t.a;                            // → "function"  (still mounted)

// controls — all fine:
await api.slothlet.api.add("t.b", () => 2, { moduleID: "a/b" });
await api.slothlet.api.remove("a/b");      // → true
await api.slothlet.api.add("t.c", () => 3, { moduleID: "a.b" });
await api.slothlet.api.remove("a.b");      // → true

Expected

Either add() should reject a moduleID it can't round-trip (a named error), or — better — the id should be stored and matched verbatim so leaves(id)/remove(id)/reload(id) work for any accepted id, colon included. Per RELOAD.md the moduleID is documented only as "a stable identifier … used for targeted reload() and remove()," with no reserved characters, so a caller has no way to know a colon is unsafe.

Impact

A consumer that adopts a namespaced id convention like vine:<nonce> or plugin:<name> gets mounts that leak — they can never be removed or reloaded by id, and leaves(id) reports nothing, so ownership tooling silently under-reports. Found while building @cldmv/slothlet-vine; the workaround was to switch link ids to hyphens and add a post-remove verification + per-path fallback. Likely cause: the colon is parsed as an internal separator somewhere in the ownership-index key.

Severity

Correctness — silent. No corruption, but an accepted id that add() returns yet remove()/leaves() don't honor violates the documented ownership contract.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: coreTouches core library / runtime source codepriority: mediumShould be addressed in the normal course of developmentsemver: patchThis release contains only backwards-compatible bug fixesstatus: confirmedThe issue has been verified and reproduced by a maintainertype: bugSomething is broken or not behaving as expected

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions