# ADR 0176: Tool slots replace and disable by name, and an unknown name refuses

> Source: https://github.com/max-listov/stitchkit/blob/master/docs/decisions/0176-tool-slots-replace-and-disable-by-name.md

---

# ADR 0176 — Tool slots replace and disable by name, and an unknown name refuses

## Context

`mountAgent` composes a `ToolSet` from contract methods and framework-managed
runtime tools. Removing or replacing a built-in was done by filtering the
runtime-tool array by name in application code. That filter rots: a later
framework default is simply not in the list, a typo removes nothing, and neither
mistake is visible — the tool is silently present or silently absent, and the
only record of what the composition intended lives in whoever wrote the filter.

A second list of defaults would be just as bad: it drifts from the array the
mount actually receives, and a reader has to reconcile two sources by eye.

## Decision

`defineToolRegistry({ defaults })` declares the runtime surface once. `disable`
removes a default and is idempotent; `replace` swaps it, and the replacement
must carry the same name and may be given once. `build()` returns exactly the
tools the mount will see plus their names, so introspection and mounting read
one value.

A name that is not in `defaults` is refused at composition time by both verbs.
The refusal is the point: the registry's only job beyond `mountAgent` is to know
whether a name exists to be removed, and answering that with silence would
reproduce the defect it replaces.

`mountAgent` accepts either `runtimeTools` or `registry`, never both; a
registry is passed through unchanged, so lifecycle, hooks and presentation stay
the mount's.

## Alternatives

- **Filter the runtime-tool array in the application.** Rejected: the filter
  cannot distinguish a removed default from a missing one, and it has no
  declared default set to check a name against.
- **A separate manifest of defaults.** Rejected: two declarations of one fact
  drift, and the mount would have to reconcile them.
- **Silently ignore an unknown name.** Rejected: that is the defect, not the fix
  (see *Context*).

## Consequences

A composition states its intent in one place and fails loudly on a typo. The
registry owns no execution: it is a builder over an array, and a future default
set stays an ordinary export. Adding a default does not change the registry, but
it does mean a filter written before it no longer describes the surface — which
is now a compile-time question, not a runtime guess.
