This one is a cleanup commit, but an important one.
It fixes a real Claude integration issue, removes a dangerous duplicate module trap, and makes DCC CHAT passive usable with modern IRC clients.
No database schema change.
No migration.
No table touched.
Just code, tests, and repository hygiene.
During the Claude integration audit, we found two separate problems that looked unrelated but both had the same root smell:
state that looked correct from the outside, but was not actually clean inside
One problem was in the Claude session state.
The other was in the repository layout itself.
There was also a DCC CHAT parsing issue that made passive DCC fail for modern clients using non-numeric tokens.
This pass cleans all of that before moving forward.
The repository had two files declaring the same package:
Mediabot/External/Claude.pm
Mediabot/Claude.pm
Both declared:
package Mediabot::External::Claude;
But Perl resolves:
require Mediabot::External::Claude;
to the canonical path:
Mediabot/External/Claude.pm
not:
Mediabot/Claude.pm
That made Mediabot/Claude.pm an orphan duplicate.
This is the worst kind of file in a codebase: it looks real, can be edited, can receive fixes, and yet production never loads it.
So the cleanup is simple and correct:
Mediabot/Claude.pm is removed from the repository
Mediabot/External/Claude.pm remains the only canonical Claude module
A new regression test now prevents that duplicate from coming back.
!ai forget now really forgetsThe !ai forget command promised to clear Claude history and persona.
But the Claude session state had grown over time.
The command only cleared:
_claude_history
_claude_persona
It did not clear:
_claude_pinned
_ai_last_active
That meant a user could run:
!ai forget
and still keep a pinned context active.
That is misleading.
The command now clears all relevant per-user/per-channel Claude state:
_claude_history
_claude_persona
_claude_pinned
_ai_last_active
It also keeps the existing key conventions straight:
history -> raw nick
persona -> lower-case nick
pinned -> lower-case nick
last_active -> lower-case nick
The user-facing message now reflects the truth:
Your Claude history, persona and pinned context have been cleared.
That is better than pretending only history and persona exist.
The second Claude bug was nastier.
When claudeAI() sends a prompt, it pushes a user message into the in-memory history before calling the API.
If the API call succeeds, an assistant response is appended and the history remains valid:
user
assistant
user
assistant
But if the API fails before an assistant response is appended, the last user message remains orphaned:
user
assistant
user
The next prompt adds another user message:
user
assistant
user
user
That violates the expected user/assistant alternation and can cause the API to reject the conversation until the state is cleared.
The fix is precise:
if the API result is undef
and the last history role is still user
then pop that orphan user message
Cache hits are preserved because they append an assistant answer internally before returning.
So the code distinguishes:
API failure -> last role is user -> rollback
cache hit success -> last role is assistant -> keep
That makes transient API failures recoverable instead of poisoning the session.
The DCC parser expected passive DCC tokens to be numeric:
(\d+)
and _handle_dcc_chat_request also required:
$token =~ /^\d+$/
That is too strict for modern IRC clients.
Passive DCC tokens are opaque identifiers. Clients may send values like:
1234567890abcdef
f7e3a4b9-c8d2-e1f0
abc.def_123-XYZ
The token is not evaluated.
It is not passed to a shell.
It is just compared and logged as an opaque string.
So the accepted safe format is now:
[A-Za-z0-9._-]+
This preserves numeric compatibility while supporting hex, UUID-like, and mixed safe tokens.
Dangerous characters remain rejected:
;
space
|
$
<
So this is broader without becoming sloppy.
This pass keeps the recent habit of making tests directly executable.
The Claude test validates:
!ai forget clears all four Claude session caches
other users' state remains untouched
private context uses the private key correctly
API failure rolls back orphan user messages
cache hit does not trigger rollback
history remains alternating after failure recovery
The DCC test validates:
numeric passive token still works
hex token works
UUID-like token works
mixed dot/underscore/dash token works
unsafe token characters are rejected
active DCC CHAT remains unchanged
Mediabot.pm no longer requires numeric-only passive tokens
The new duplicate-module test validates:
only one file declares package Mediabot::External::Claude
that file is Mediabot/External/Claude.pm
Mediabot/Claude.pm is absent
External.pm requires the canonical module
This prevents the exact mistake from returning later.
Recommended checks:
cd /home/mediabot/mediabot_v3 || exit 1
perl -I. -c Mediabot/External/Claude.pm
perl -I. -c Mediabot/External.pm
perl -I. -c Mediabot/DCC.pm
perl -I. -c Mediabot/Mediabot.pm
perl -c t/cases/391_mb141_claude_forget_and_history_rollback.t
perl -c t/cases/392_mb142_dcc_token_alphanumeric.t
perl -c t/cases/395_mb144_no_orphan_claude_duplicate.t
perl t/cases/391_mb141_claude_forget_and_history_rollback.t
perl t/cases/392_mb142_dcc_token_alphanumeric.t
perl t/cases/395_mb144_no_orphan_claude_duplicate.t
git diff --check
Expected direct test coverage:
Claude forget/history rollback 26 assertions
DCC passive token handling 26 assertions
Claude duplicate module guard 5 assertions
The Claude fix prevents confusing sticky state after !ai forget.
The history rollback fix prevents an API failure from poisoning the conversation until manual cleanup.
The DCC fix makes passive DCC CHAT work with modern client tokens, especially useful behind NAT/firewall situations.
The module cleanup removes a repository trap that could silently swallow future fixes.
This is the kind of cleanup that saves time later.
The best bugfixes are not always the loudest ones.
Sometimes the important work is removing ghosts.
This pass removes a phantom Claude module, makes !ai forget honest, keeps Claude history balanced after failures, and revives passive DCC CHAT for modern clients.
A cleaner castle.
Fewer ghosts.
Better spells.
π§ββοΈπ§Ήπ
You must be logged in to reply.