Clean loader idempotency state when tool registries are destroyed #67

Closed
opened 2026-08-17 06:54:51 +00:00 by lost-rob0t · 2 comments
lost-rob0t commented 2026-08-17 06:54:51 +00:00 (Migrated from github.com)

Problem

PR #66 introduces deterministic per-registry loader idempotency using private loaded_tool_pack/4 state in rlm_tool_loader. The loader exposes rlm_tool_loader_forget_registry/1, and the new fixture suites call it during cleanup, but ordinary rlm_tool:tool_registry_destroy/1 does not currently notify the loader.

Because registry IDs are monotonic and never reused, this is not a stale-registry correctness or authority bug. It is a process-lifetime retention edge for hosts that repeatedly create, load, and destroy many registries without explicitly calling the loader cleanup predicate.

Severity

Low correctness risk; moderate long-running-host hygiene risk if registry churn is high.

Reproduction / architecture failure

  1. Create a tool registry.
  2. Load one or more external packs/categories through rlm_tool_loader.
  3. Destroy the registry with tool_registry_destroy/1 without calling rlm_tool_loader_forget_registry/1.
  4. The registry's private loader bookkeeping remains in loaded_tool_pack/4 until explicitly forgotten or process exit.

Required invariant

Destroying a tool registry must eventually remove all loader idempotency bookkeeping owned by that registry without exposing loader internals or introducing a circular business-logic dependency.

Acceptance criteria

  • tool_registry_destroy/1 or an established registry cleanup-hook mechanism clears loader state automatically when rlm_tool_loader is loaded.
  • no circular runtime dependency that moves registry ownership into external libraries;
  • destroying a registry that never used the loader remains valid;
  • cleanup is deterministic and idempotent;
  • tests prove repeated registry create/load/destroy cycles do not retain loader state;
  • canonical deterministic and REAL gates stay green.

Scope exclusions

  • no concrete filesystem/git/process/network tool implementations;
  • no changes to capability or authority semantics;
  • no MCP lifecycle redesign;
  • no registry ID reuse.

Discovered while auditing PR #66 / #48.

## Problem PR #66 introduces deterministic per-registry loader idempotency using private `loaded_tool_pack/4` state in `rlm_tool_loader`. The loader exposes `rlm_tool_loader_forget_registry/1`, and the new fixture suites call it during cleanup, but ordinary `rlm_tool:tool_registry_destroy/1` does not currently notify the loader. Because registry IDs are monotonic and never reused, this is not a stale-registry correctness or authority bug. It is a process-lifetime retention edge for hosts that repeatedly create, load, and destroy many registries without explicitly calling the loader cleanup predicate. ## Severity Low correctness risk; moderate long-running-host hygiene risk if registry churn is high. ## Reproduction / architecture failure 1. Create a tool registry. 2. Load one or more external packs/categories through `rlm_tool_loader`. 3. Destroy the registry with `tool_registry_destroy/1` without calling `rlm_tool_loader_forget_registry/1`. 4. The registry's private loader bookkeeping remains in `loaded_tool_pack/4` until explicitly forgotten or process exit. ## Required invariant Destroying a tool registry must eventually remove all loader idempotency bookkeeping owned by that registry without exposing loader internals or introducing a circular business-logic dependency. ## Acceptance criteria - `tool_registry_destroy/1` or an established registry cleanup-hook mechanism clears loader state automatically when `rlm_tool_loader` is loaded. - no circular runtime dependency that moves registry ownership into external libraries; - destroying a registry that never used the loader remains valid; - cleanup is deterministic and idempotent; - tests prove repeated registry create/load/destroy cycles do not retain loader state; - canonical deterministic and REAL gates stay green. ## Scope exclusions - no concrete filesystem/git/process/network tool implementations; - no changes to capability or authority semantics; - no MCP lifecycle redesign; - no registry ID reuse. Discovered while auditing PR #66 / #48.
lost-rob0t commented 2026-08-17 06:58:21 +00:00 (Migrated from github.com)

PR #66 merged as 9ba763d248f6e087b3ff01c651762997f02ee726; #67 remains intentionally open.

The merged loader exposes explicit rlm_tool_loader_forget_registry/1, and deterministic fixtures use it. Canonical registry IDs are monotonic/non-reused, so retained bookkeeping cannot be mistaken for authority/capability state of a future registry. The remaining defect is bounded to process-lifetime hygiene for hosts with repeated registry churn.

Do not fold this into #52 unless the next implementation discovers a clean generic registry-destroy hook that is tiny and dependency-safe.

PR #66 merged as `9ba763d248f6e087b3ff01c651762997f02ee726`; #67 remains intentionally open. The merged loader exposes explicit `rlm_tool_loader_forget_registry/1`, and deterministic fixtures use it. Canonical registry IDs are monotonic/non-reused, so retained bookkeeping cannot be mistaken for authority/capability state of a future registry. The remaining defect is bounded to process-lifetime hygiene for hosts with repeated registry churn. Do not fold this into #52 unless the next implementation discovers a clean generic registry-destroy hook that is tiny and dependency-safe.
lost-rob0t commented 2026-08-17 07:22:13 +00:00 (Migrated from github.com)

Inspected #67 while implementing #52 / PR #72. The current registry destroy path in rlm_tool clears authority plus registry entries/aliveness, while loader idempotency is separate dynamic loaded_tool_pack/4 state with an explicit rlm_tool_loader_forget_registry/1 cleanup call used by loader tests.

There is no existing generic registry-destroy cleanup hook for independent subsystems to contribute teardown. Closing #67 here would therefore require introducing a new cross-module registry lifecycle callback/contract, not merely wiring an already-existing hook, and that is not a natural dependency of the MCP declaration-policy slice.

Keeping #67 open intentionally. Remaining acceptance is still: destroy a tool registry -> loader idempotency state for that registry is reclaimed automatically, without creating a circular rlm_tool/rlm_tool_loader business-logic dependency and with deterministic lifecycle coverage.

Inspected #67 while implementing #52 / PR #72. The current registry destroy path in `rlm_tool` clears authority plus registry entries/aliveness, while loader idempotency is separate dynamic `loaded_tool_pack/4` state with an explicit `rlm_tool_loader_forget_registry/1` cleanup call used by loader tests. There is no existing generic registry-destroy cleanup hook for independent subsystems to contribute teardown. Closing #67 here would therefore require introducing a new cross-module registry lifecycle callback/contract, not merely wiring an already-existing hook, and that is not a natural dependency of the MCP declaration-policy slice. Keeping #67 open intentionally. Remaining acceptance is still: destroy a tool registry -> loader idempotency state for that registry is reclaimed automatically, without creating a circular `rlm_tool`/`rlm_tool_loader` business-logic dependency and with deterministic lifecycle coverage.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nsaspy/prolog-rlm#67
No description provided.