NanoClaw
NanoClaw’s accumulate mode can answer 50 unaddressed messages in three minutes
A NanoClaw incident shows the follow-up poller feeding trigger=0 messages into an active query even though the outer loop correctly ignores them. One legitimate mention then became a self-sustaining 50-message Slack exchange until the host was stopped.
nanocoai/nanoclaw issue #3132 is the inspected primary source: “bug: follow-up poll pushes accumulate (trigger=0) messages into an active query, bypassing the accumulate gate.” The report identifies two message-consumption paths in poll-loop.ts. The outer batch loop waits for at least one trigger=1 row, honoring ignored_message_policy=accumulate. The setInterval follow-up poller inside processQuery filters only system messages, so any trigger=0 row arriving during an active turn is marked processing and pushed into the query. In the live Slack incident, one real mention started the turn; a counterpart then posted a confirmation after each reply. Twenty-five untriggered rows were acknowledged about a second after arrival, the original trigger stayed processing for nearly three minutes, and roughly 50 messages crossed the channel without a container restart. The agent objected to the loop in text but could not stop the runner from feeding it.
The facts
- nanocoai/nanoclaw issue #3132 was created 2026-07-25T23:42:53Z and updated 2026-07-25T23:42:53Z; state is open. - nanocoai/nanoclaw returned 30364 GitHub stars, 848 open issues, default branch main, and last push 2026-07-25T22:19:35Z. - Latest tagged-release baseline checked: v2.1.17 published 2026-06-17T14:51:14Z. - The report identifies two message-consumption paths in poll-loop.ts. - The outer batch loop waits for at least one trigger=1 row, honoring ignored_message_policy=accumulate. - The setInterval follow-up poller inside processQuery filters only system messages, so any trigger=0 row arriving during an active turn is marked processing and pushed into the query. - In the live Slack incident, one real mention started the turn; a counterpart then posted a confirmation after each reply. - Twenty-five untriggered rows were acknowledged about a second after arrival, the original trigger stayed processing for nearly three minutes, and roughly 50 messages crossed the channel without a container restart.
What changed
The report identifies two message-consumption paths in poll-loop.ts. The outer batch loop waits for at least one trigger=1 row, honoring ignored_message_policy=accumulate. The setInterval follow-up poller inside processQuery filters only system messages, so any trigger=0 row arriving during an active turn is marked processing and pushed into the query. In the live Slack incident, one real mention started the turn; a counterpart then posted a confirmation after each reply. Twenty-five untriggered rows were acknowledged about a second after arrival, the original trigger stayed processing for nearly three minutes, and roughly 50 messages crossed the channel without a container restart. The agent objected to the loop in text but could not stop the runner from feeding it. The primary record was inspected with its timestamps, state, body, and adjacent project records. ClawCharts supplied the assignment list; it does not establish the claim.
Why it matters
Admission policy must hold at every ingestion path. A guard at wake-up is useless if follow-up delivery can bypass it while the agent is busy. The resulting loop spends tokens, floods shared channels, and demonstrates that model-level instructions cannot compensate for a runtime that repeatedly misclassifies input. The operational test is whether persisted state, visible control, and runtime behavior agree at the handoff where authority changes.
Current
Issue #3132 and its companion PR #3133 were open at inspection. The patch extracts one isTurnEligible predicate for both paths, leaves refused rows pending until a real trigger, and reports 158 tests with one skip plus a clean typecheck. NanoClaw’s latest tagged baseline remained v2.1.17, so operators should treat this as active proposed repair. Repository metadata, current issues and pull requests, release baseline, Hacker News discovery, and the rendered ranking row were checked. Weak search residue and historically used source spines were not promoted.
Evidence
The primary URL is https://github.com/nanocoai/nanoclaw/issues/3132. Supporting links are inspected current project records that establish implementation, adjacent direction, or boundary conditions. Dates, reproductions, test totals, and deployment observations remain attributed to their authors unless this brief explicitly says they were independently rerun.
Source boundary
Open work is described as open, merged work as merged, and operator reports as reports. A closed issue or passing branch test is not silently translated into a released fix. Ranking explains why the project was scanned; it does not prove correctness, adoption, or package behavior.
Operator take
Run one addressed message, then inject unaddressed traffic while the turn remains open. Verify those rows remain pending, produce no reply, and join context only after a later trigger=1 event. Also search the poller for other outer-loop invariants that still lack a shared predicate. Preserve a before-state receipt and verify the narrowest invariant where state changes. Do not broaden permissions or delete state merely because the visible symptom is inconvenient.
Caveat
Public project records are mutable. Source state was captured at publication time, and later revisions may change status, scope, or evidence. No source here authorizes automatic mutation of a reader’s deployment.
Source inspected; source state, environment, and release boundary remain explicit.