Forum teuk.org

🏰 MB678 β€” Partyline Modularization Complete: From a 6,976-Line Castle to a 295-Line Great Hall

in Mediabot Β· started by TeuK Β· yesterday

TeuK Β· yesterday

MB678 is complete.

What started as a large, historically grown Mediabot::Partyline module has now been decomposed into explicit architectural responsibilities without changing the external command surface, operator workflow, runtime protocol, or security model.

The final closure commit is:

Version : 3.4dev-20260822_050334
Commit  : 74bcfef
Branch  : master

🏰 Close the Partyline Chambers and Leave Only the Great Hall

The final parent module is now:

Mediabot/Partyline.pm : 295 lines

It contains exactly five core implementations:

new
get_port
_runtime_status_path
_runtime_status_payload
_write_runtime_status

And, critically:

Physical _cmd_* implementations in Partyline.pm : 0

That is the real completion criterion of MB678.


🏚️ Where we started

Before MB678, Mediabot/Partyline.pm had grown into a large multi-responsibility module:

6976 lines

It mixed together:

TCP transport
DCC handling
sessions
authentication
command dispatch
operator commands
IRC control
runtime diagnostics
scheduler controls
AI commands
moderation
anti-flood controls
history and visibility commands
privileged eval / die controls
runtime-status export

The goal was never merely to reduce the line count.

The goal was:

same Partyline protocol
same commands
same historical method surface
same runtime behaviour
same security invariants
clear ownership of responsibilities

🧩 The final Partyline architecture

After MB678:

Mediabot/Partyline.pm             :  295 lines
Mediabot/Partyline/Commands.pm    : 4529 lines
Mediabot/Partyline/Dispatcher.pm  :  407 lines
Mediabot/Partyline/SessionAuth.pm :  676 lines
Mediabot/Partyline/Transport.pm   : 1013 lines
Mediabot/Partyline/Privileged.pm  :  388 lines

Responsibilities are now explicit.

Mediabot::Partyline

The final parent owns only the legitimate Partyline core:

object construction
Partyline port access
runtime-status path
runtime-status payload generation
runtime-status atomic publication
historical facade/import surface

No Partyline command is physically implemented there anymore.


πŸš‚ Mediabot::Partyline::Transport

Owns:

TCP listener
DCC transport
stream handling
line/input framing
transport limits
socket-level errors
session transport bootstrap

The final closure also moved the bytes dependency to this module because this is where bytes::length() is actually used.


πŸ” Mediabot::Partyline::SessionAuth

Owns:

session lifecycle
authentication
login throttling
timeouts
reverse DNS
session state
broadcast/session helpers

🧭 Mediabot::Partyline::Dispatcher

Owns:

input dispatch
command routing
historical command entry path

This preserves the external routing model while making the routing responsibility explicit.


🧰 Mediabot::Partyline::Commands

Owns the ordinary Partyline command surface, including the successive families extracted during MB678:

plugin / ScriptDryRun
core operator/session commands
scheduler / operational commands
channel/network visibility
reminders / seen
karma
configuration reload
network statistics
IRC control/lifecycle
AI commands
runtime diagnostics
channel moderation
anti-flood/cooldown
remaining standard commands

The module is intentionally the command implementation layer.


πŸ”’ Mediabot::Partyline::Privileged

Owns the two privileged controls:

_cmd_eval
_cmd_die

These were deliberately kept separate from ordinary commands because their risk profile is fundamentally different.

_cmd_eval includes guarded code execution plus watchdog/fork/reaping behaviour.

_cmd_die controls process termination.

Their historical surface remains available through Mediabot::Partyline.


πŸ§™ MB678 round history

The complete extraction sequence was:

I     Transport                         25949c5
II    Session/Auth                      a81729b
III   Dispatcher                        ff633dc
IV-A  Plugin / ScriptDryRun             9914aa8
IV-B  Core operator/session             1c34240
IV-C  Scheduler / operational           a28388b
IV-D  Channel/network visibility        5f48853
IV-E  Reminder / seen                   eed64c3
IV-F  Karma                             595185a
IV-G  Configuration reload              d689416
IV-H  Network statistics                702bcd6
IV-I  IRC control/lifecycle             fad3dd3
IV-J  Claude/AI                         e55b30b
IV-K  Runtime diagnostics               970f00b
IV-L  Channel moderation/control        ce059f7
IV-M  Anti-flood / cooldown             90c3d40
IV-N  Remaining standard commands       959e927
IV-O  Privileged controls               fd0b4d3
Final boundary closure                  74bcfef

This was intentionally incremental.

Each round moved one coherent responsibility and was validated before the next one began.


πŸ“‰ Structural evolution

The most visible metric is the parent module size:

Before MB678 : 6976 lines
After MB678  :  295 lines

Reduction:

6681 lines removed from the parent
~95.8% reduction

But the important metric is architectural:

Before:
    one parent carrying almost everything

After:
    parent = core/facade
    transport = Transport
    sessions/auth = SessionAuth
    routing = Dispatcher
    commands = Commands
    privileged process controls = Privileged

The code did not disappear.

The ownership became understandable.


πŸ§ͺ Contract strategy

A large part of the work involved preserving historical behaviour while correcting tests that assumed physical source location.

A method such as:

Mediabot::Partyline->_cmd_status

may still be publicly callable through Mediabot::Partyline while its implementation physically lives in:

Mediabot/Partyline/Commands.pm

That distinction became an explicit test principle during MB678:

API ownership != physical source-file ownership

Whenever an old source contract failed solely because a method moved, the contract was updated rather than the product being bent back toward the old monolith.


🧱 Final boundary contract

The closure introduced:

t/cases/881_mb678_partyline_boundary_closure.t

Its job is to lock the final architecture.

Among other things, the closure verifies that:

Partyline.pm contains only the intended core implementations
no _cmd_* implementation remains in the parent
module ownership is explicit
imported historical symbols have a single real owner
stale extraction tombstones are gone
the parent no longer carries dependencies belonging to extracted modules

The final MB678 Partyline contract chain:

862 -> 881

passed:

PASSED : 964/964  (5s)

⚑ Fast validation

The final fast lane after boundary closure:

perl t/test_commands.pl --fast --progress

Result:

Selected: 323 of 764 discovered test file(s)

[====================] 100% [323/323 files | 5982 tests]

PASSED : 5982/5982  (64s)

πŸ”Œ Real runtime validation

After the final boundary cleanup, the development instance restarted successfully:

mediabot@dev : active

Partyline remained listening on:

0.0.0.0:23456

The real TCP smoke path returned:

Mediabot Partyline
Please enter your nickname.
Enter your password.

Result:

RC_SMOKE=0

The journal showed:

OK: no obvious runtime error

πŸ›‘οΈ Security audit

The final security audit remained fully green:

RC_AUDIT=0

Verdict: GO β€” all 11 security invariants hold.

This was especially important because MB678 moved:

authentication/session code
anti-flood controls
privileged eval/die controls
transport handling

The architecture changed significantly while the audited security properties remained intact.


🏰 Final full regression suite

The final full suite after the closure cleanup:

perl t/test_commands.pl --progress

Result:

[====================] 100% [764/764 files | 15371 tests]

PASSED : 15371/15371  (211s)

That is the final MB678 regression reference.


🧹 Final boundary cleanup

The last commit did not extract another command family.

Instead, it cleaned up the scaffolding left by the refactor:

Partyline.pm : 440 -> 295 lines
subs         : 5 -> 5
commands     : unchanged
routing      : unchanged
runtime      : unchanged

It removed stale historical imports and extraction tombstones.

It also moved:

use bytes ();

to:

Mediabot::Partyline::Transport

because that module is the real owner of the bytes::length() usage.

This is the difference between merely splitting files and actually completing an architectural refactor.


πŸ“¦ Final closure commit

Version : 3.4dev-20260822_050334
Commit  : 74bcfef

🏰 Close the Partyline Chambers and Leave Only the Great Hall

Commit summary:

4 files changed
128 insertions
173 deletions

New test:

t/cases/881_mb678_partyline_boundary_closure.t

The commit was successfully pushed to GitHub.


🏁 MB678 status

STATUS: COMPLETE

Completion criteria:

[OK] transport separated
[OK] sessions/auth separated
[OK] dispatcher separated
[OK] ordinary commands separated
[OK] privileged controls separated
[OK] historical Partyline surface preserved
[OK] parent reduced to legitimate core
[OK] no _cmd_* implementation remains in parent
[OK] final boundary contract added
[OK] runtime validation passed
[OK] security audit passed
[OK] fast suite passed
[OK] full suite passed
[OK] closure commit pushed

No further Partyline extraction is justified under MB678.


🧭 What comes next

MB678 was a large structural project, not the final step toward 3.5.

The next work should return to the broader roadmap:

1. remaining update / observability work
2. architecture/documentation consolidation
3. full 3.5 readiness audit
4. stable 3.5 only after explicit GO

The historical update/observability work must receive a new number because MB679 is already the test-runner progress feature.

The next phase should therefore avoid immediately starting another major refactor.

The system now needs consolidation, operational visibility, documentation alignment, and readiness validation.


🏰 The chambers are no longer piled into one room. The Great Hall remains β€” small, central, and finally worthy of its name.

You must be logged in to reply.