Forum teuk.org

πŸͺ„ MB749 β€” The Secret Passage of Duplicate Dispatch Is Finally Sealed

in Mediabot Β· started by TeuK Β· yesterday

TeuK Β· yesterday

Mediabot 3.6dev has reached another important architectural milestone.

MB749 does not add a flashy public command, a new game or another external service. Instead, it completes a much deeper transformation: every built-in command now carries its executable handler directly inside the central command registry.

The milestone was published on the 3.6dev-20260920_102406 development line.

The old duplicate dispatch maps are gone. There is now one catalogue, one lookup path and one authoritative handler for each built-in spell. πŸ“šπŸ—οΈ

From a catalogue to a real runtime authority

MB741 introduced a single catalogue for built-in and plugin command names. It gave Mediabot a reliable inventory, but command execution still crossed a compatibility corridor: the registry identified the command, then a second public or private dispatch table found the code that actually ran.

That transitional design was useful while the API v3 foundations were being built, but it left two representations of the same command architecture. Any future change had to keep the catalogue, the compatibility tables, the help metadata and the tests aligned.

MB749 closes that corridor.

For every public or private built-in, Mediabot now:

  1. normalizes the command name with its IRC-safe case and accent folding;
  2. resolves the source-scoped entry in Mediabot::CommandRegistry;
  3. obtains the executable CODE handler from that entry;
  4. invokes that handler directly;
  5. treats an unregistered name as unknown.

There is no longer a second %command_map or %command_table lookup hiding behind the registry.

332 built-in spells, one registry

The final catalogue contains:

  • 238 public built-in commands;
  • 94 private and administrative built-in commands;
  • 332 registry entries with executable handlers;
  • zero legacy public adapter entries;
  • zero legacy private adapter entries.

The deterministic architecture inventory checks exact parity between declared command names and registry-native handler factories. If a name exists without a handler, a handler appears without a declaration, or the generated command inventory drifts, the validation fails closed.

This is not merely a tidier list. The registry has become the runtime source of truth.

The API v3 bridge remains reversible

Removing the duplicate dispatch tables could not be allowed to break the reversible migrations already proven by playful-v3 and quotes-v3.

The public protocol name legacy-public-fallback therefore remains stable for manifest compatibility, but its implementation is now registry-native.

When an eligible API v3 command is mounted, PluginManager captures the existing built-in handler from the registry itself. That saved CODE reference is then used to preserve historical behavior while the plugin is disabled or the channel policy is off or observe. When the policy becomes on, the v3 handler becomes authoritative for that channel. Unload β€” or a failed mount β€” restores the exact previous registry entry.

The main dispatcher no longer needs to know that a migration fallback exists. It simply resolves and invokes the current registry entry.

The previous 234 migration-eligible public handlers retain that explicit eligibility. The four original direct handlers remain intentionally ineligible, preserving the pre-MB749 contract.

In other words, the moving staircases still move, but the Great Hall no longer needs a second map of the castle. 🏰

What deliberately did not change

This was a structural migration, not a behavioral rewrite.

MB749 preserves:

  • every public and private command count;
  • command names, aliases and IRC-safe folding;
  • help text and permission levels;
  • visible command output;
  • asynchronous command paths;
  • API v1 and API v2 behavior;
  • the API v3 off / observe / on migration semantics;
  • database-backed dynamic commands, which remain a separate instance-data path after built-in lookup.

The deployment also made no private configuration change, no database data change and no schema migration. No plugin or channel was activated automatically.

The test sentinels earned their robes

This milestone touched the central dispatcher, so the development gates were intentionally unforgiving.

The first package exposed a constructor expectation that still belonged to the transitional registry model. After that was repaired, the fast lane found 17 failures across eight historical tests. Those tests were still looking for source fragments from the retired dispatch tables even though the runtime contract had moved to registry-native handlers.

The R3 package corrected those sentinels without weakening their purpose. They now inspect the new handler factories and registry route while continuing to protect OpenAI administration, conversion, statistics, news aliases, horoscope dispatch, channel lists, administrative exports and channel memory.

That is exactly what layered validation is supposed to do: focused tests prove the new contract, the fast lane finds nearby historical assumptions, and the single final full suite checks the entire castle before the gates open.

Validation ledger βœ…

The final MB749 R3 candidate passed every development gate:

Gate Result
Registry contract 238 public / 94 private / 234 migration-eligible
Targeted suite 1,210 / 1,210 tests across 44 files
Fast lane 7,456 / 7,456 tests across 407 files
Final full suite 19,784 / 19,784 tests across 973 files
Final full duration 140 seconds
Changed surface 58 files, and no others
Repository after push clean and synchronized with origin/master
Development service active, connected to IRC and unchanged during the gate

The final gate also froze the staged diff before and after the full suite:

STAGED_SHA256=33f9deb47e3a9a01e5cb0aaab6cfd004b70ce5036f130b625894aca8842a539e

No source byte, service process or restart counter changed while the full suite was running.

The new regression ward

MB749 adds a dedicated architecture test:

t/cases/1090_mb749_registry_native_dispatch.t

It verifies that:

  • all public and private catalogue names have matching handlers;
  • all 332 built-in entries use registry dispatch;
  • both legacy adapter lists are empty;
  • the duplicate compatibility maps are absent;
  • migration eligibility remains explicit;
  • PluginManager captures and restores the previous handler;
  • mounted plugin handlers use the ordinary one-context invocation signature.

The broader historical suite was updated to assert the same architecture from the perspective of real commands and subsystems.

Commit sealed and released by owl πŸ¦‰

MB749 was committed and pushed to master as:

df0986cecde7cd6f868fad3531b464d7a454ff8b
MB749: route every built-in spell through one enchanted registry πŸͺ„πŸ“š

View the MB749 commit on GitHub

The commit contains 1,174 insertions and 1,069 deletions across 58 files. Much of that apparent movement is the deliberate transfer of handler ownership from duplicate dispatch tables into the registry, plus the modernization of architecture sentinels that previously inspected those tables.

Why this matters for the roadmap

MB749 completes the tenth recorded milestone of the current plugin-platform roadmap.

The early API v3 work established manifests, capabilities, lifecycle, events, jobs, typed channel policies, shared HTTPS, bounded storage and approved quote reads. The first command packs then proved that existing behavior could move behind observe and on policies with exact rollback.

Now the core itself uses the same authoritative command registry model. Future command migrations no longer have to negotiate with a parallel dispatch architecture.

The next chambers can focus on stronger product boundaries β€” notably authorized quote writes and improved plugin developer tooling β€” instead of maintaining two maps of the same castle.

The secret passage served its purpose during the migration. MB749 has now sealed it behind the tapestry, and the registry holds the only key. πŸ—οΈβœ¨

β€” Teuk

You must be logged in to reply.