From 3bf06cebd02a897327e39bf72b20e54aebd70f14 Mon Sep 17 00:00:00 2001 From: Arthur Date: Sun, 2 Aug 2026 16:32:37 +0200 Subject: [PATCH] fix(agent): pin nanobot so a restart cannot take the agent off Matrix The agent installed `nanobot-ai` unpinned, and `stack restart` rebuilds the image, so any unrelated restart could pull a new release. When 0.2.x turned `nanobot.channels.matrix` from a module into a package, the runtime shims that patch it stopped attaching. They fail soft on purpose so a broken shim never stops the agent answering, which meant the container still started, still reported healthy, and the family's bot had simply gone quiet. Pinned to the version the shims are written against. Bumping it is now a deliberate change, which is when someone is looking. Also corrects what the shim tests claim to prove: they run against a hand-written stub, so they catch our own mistakes but never upstream moving a symbol. The pin is what guards that, not the unit lane. --- stacklets/agent/Dockerfile | 13 ++++++++++++- stacklets/agent/runtime/sitecustomize.py | 13 ++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/stacklets/agent/Dockerfile b/stacklets/agent/Dockerfile index 6f3c9a66..89fa9c86 100644 --- a/stacklets/agent/Dockerfile +++ b/stacklets/agent/Dockerfile @@ -11,7 +11,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates git libolm-dev tzdata \ && rm -rf /var/lib/apt/lists/* -RUN uv pip install --system --no-cache "nanobot-ai[matrix]" +# Pinned, and it has to stay pinned. `runtime/sitecustomize.py` patches +# nanobot's internals (there is no plugin seam), so a release that moves one +# of those symbols detaches a shim. The shims fail soft on purpose — a broken +# one must never stop the agent answering — so the container still starts, +# still reports healthy, and the Matrix channel is simply gone. Unpinned, that +# arrives on whatever unrelated `stack restart agent` happens to rebuild the +# image, which is how 0.2.x moving `nanobot.channels.matrix` from a module to +# a package took a live instance silently off Matrix. +# +# Bump this deliberately, in its own change, and re-verify every symbol in +# sitecustomize.py's PIN / RECHECK list against the new release. +RUN uv pip install --system --no-cache "nanobot-ai[matrix]==0.2.2" # Non-root, matching nanobot's own image (uid 1000, HOME=/home/nanobot). RUN useradd -m -u 1000 -s /bin/bash nanobot diff --git a/stacklets/agent/runtime/sitecustomize.py b/stacklets/agent/runtime/sitecustomize.py index c7b7199b..3dffd8e0 100644 --- a/stacklets/agent/runtime/sitecustomize.py +++ b/stacklets/agent/runtime/sitecustomize.py @@ -62,9 +62,16 @@ `MatrixChannel._handle_message(sender_id, chat_id, content, metadata, is_dm)` `tests/stacklets/test_agent_runtime_shims.py` asserts every one of these is - attached against a stub nanobot, so this list is executable rather than - aspirational: a moved symbol fails the unit lane instead of silently - reaching production as a logged warning nobody reads. + attached — but against a *stub* nanobot this repo hand-writes, so read what + that does and does not buy. It catches our own mistakes: a shim that stops + attaching, or one whose failure takes another down with it. It cannot catch + upstream moving a symbol, because the stub still has the old one and the + lane stays green while production is broken. The version pin in the + Dockerfile is what actually holds this line; the tests guard the shims, the + pin guards the assumption underneath them. + + So when bumping `nanobot-ai`, re-verify this list against the installed + package, not against a passing unit lane. """ import importlib