Development chronicle — mb586 to mb591 · Plugins v2
A new wing has opened inside Mediabot’s castle.
Plugins are no longer mysterious boxes that must be opened and read before anyone can understand what they provide. They can now declare their identity, commands, access requirements and public surface through a versioned manifest — then join the live command registry through a controlled, reversible lifecycle.
The same contract now covers trusted in-process Perl plugins and external Perl, Python or Tcl scripts.
No database schema was changed during this arc.
Plugin API v2 begins with a declarative contract:
sub manifest {
return {
api => 2,
name => 'demo',
version => '0.002',
description => 'A small demonstration plugin.',
commands => {
hello => {
help => 'Say hello.',
level => 0,
},
},
events => [ 'public_command_observed' ],
};
}
The manifest is validated before register() can create any side effect.
The validation protects the registry from:
Legacy plugins remain compatible. A plugin without a v2 manifest continues to load through the historical API v1 path.
A command declared by a v2 plugin is mounted automatically into Mediabot’s CommandRegistry.
There is no manual wiring and no second command table to maintain.
The registered wrapper checks the plugin’s current state on every dispatch, then calls:
$plugin->command_<name>($ctx)
The lifecycle is atomic:
This extends the same discipline Mediabot already applies to event listeners: once an object leaves the castle, none of its portraits should keep speaking.
The .plugins partyline command now operates the v2 lifecycle:
.plugins loaded
.plugins load My::Plugin [name]
.plugins reload <name>
.plugins unload <name>
.plugins enable <name>
.plugins disable <name>
Read-only inspection remains available, while mutations are protected by Mediabot’s existing partyline access model.
Reload is real: the module is removed from %INC, loaded again from disk and validated as a new candidate.
If the new code is invalid, the previous instance remains active. A broken replacement cannot leave the bot with half a plugin and half a command registry.
USER_LEVELThe word level once risked representing two incompatible scales.
Mediabot’s database uses named access levels where a lower stored value is more powerful. A plugin manifest now avoids that ambiguity:
commands => {
hello => { help => 'Public command.', level => 0 },
vault => { help => 'Master only.', level => 'Master' },
}
The rules are explicit:
0 means public;USER_LEVEL description;Authorization is checked at every dispatch, not frozen when the plugin loads.
The wrapper verifies:
An Owner can invoke a Master command. An ordinary User cannot. Every refusal is fail-closed, concise to the caller and traceable in the log.
Perl, Python and Tcl scripts can now become plugin v2 participants through a JSON sidecar:
plugins/greet.tcl
plugins/greet.tcl.manifest.json
{
"api": 2,
"name": "greet",
"version": "1.0",
"description": "Greet users from Tcl.",
"commands": {
"greet": {
"help": "Say hello.",
"level": 0
},
"vip": {
"help": "Master only.",
"level": "Master"
}
}
}
They enter through:
.plugins loadscript greet.tcl
The sidecar is validated by the same manifest rules as an in-process plugin, while execution still uses the hardened script bridge:
Reload reads the sidecar again. An invalid replacement leaves the previous script instance active.
The last inspection found several edge cases where the implementation did not yet fully match the guarantees promised by the arc.
They are now closed.
A failure while mounting the replacement command surface restores the previous plugin and its commands instead of merely removing the failed candidate.
Reloading a disabled plugin no longer silently re-enables it.
The 8 KiB sidecar limit is enforced before the whole document is accepted into memory.
The script must exist when it is registered, and the sidecar receives its own containment check. Symbolic-link tricks cannot silently point the manifest outside the approved script directory.
A script command is not announced as successful when ScriptActionRunner reports that its actions could not be applied.
A manifest declaring commands cannot load without a plugin object and a usable CommandRegistry.
The partyline help was also completed so that loadscript is visible to operators.
The complete local test suite passed after the mb591 hardening.
The dedicated arc covers:
USER_LEVEL entries;A plugin author can now describe a command once and let Mediabot handle the rest:
External script authors receive the same operational model without moving their code into the bot process.
This gives Mediabot a coherent extension architecture while preserving the hard-earned compatibility of the legacy plugin system.
Every portrait in the gallery now carries a proper nameplate.
The castle knows which spells it offers, who may cast them, how to replace them safely, and how to remove them without leaving a whisper in the corridor.
Even visitors from other castles — Perl, Python and Tcl scripts — now pass through the same guarded doors.
Plugins v2 is no longer a proposal. The full arc is alive.
You must be logged in to reply.