Mediabot MB874–MB888 · 21 October 2026 · Fifteen maintenance rounds: clearer module ownership, reusable IRC tools, safer metadata, reliable output queues and a leaner test suite.
The last chapter, True Names, Clear Skies and Faster Clocks, ended at MB873. Since then, a question about weather and Fortnite code living inside YouTube.pm has opened a much larger investigation into the organisation of Mediabot.
Why was a movie catalogue inside Claude.pm? Which parts of IRC formatting could other features reuse? Could the sending code acquire a clear home while keeping its filters, logging and worker interception? And, after adding regression coverage for each discovery, which older tests still earned their place?
Fifteen rounds have addressed those questions through successive, bounded changes. Some correct specific failures. Others move established implementations into modules whose names describe their responsibilities. Several do both, with the behavioural correction kept distinct from the extraction that follows it.
The latest complete development-host run passes 30,155 assertions across 1,156 test files in 176 seconds. All 37 security invariants hold, and startup integrity passes. At the end of the previous article, the suite contained 1,172 files: after substantial new coverage and a deliberate pruning round, there are now sixteen fewer test files.
The work remains on 3.6dev, with no database schema change throughout these fifteen rounds.
The Sorting Hat has had a busy day. There are now clearer places for the spells, and rather better rules for the owls carrying their replies. 🪄
The horoscope parser already checked whether a provider’s declared sign matched the requested one. However, it performed that check only when the declaration was a scalar value. An object, array or JSON boolean could skip the comparison altogether.
The result was a forecast accepted under a sign it had not meaningfully identified. Accepting that response also prevented the existing recovery path from trying a usable mirror.
Every defined declaration now passes through the existing sign normalizer. An unusable container is rejected like an ordinary wrong sign, and diagnostics use a fixed label instead of exposing a Perl reference representation. Supported aliases and response shapes keep working. Missing or null declarations retain their established compatibility; the correction does not invent identity evidence absent from the provider’s response.
The same round removes expensive work from long forecast clipping. Previously, the formatter assembled every Unicode grapheme, removed one from the end, rejoined the remainder and encoded it again until the text fitted. Repeating that process across a long forecast made the work grow quadratically.
The replacement checks whether the whole input fits, then accumulates a prefix of complete graphemes within the existing budget. It reserves the same ellipsis space and applies the established word and punctuation cleanup. Combining marks and joined emoji remain intact.
For the 5,800-character regression input, the old approach presented more than 16 million characters to the encoder while seeking a short display prefix. The new approach uses the initial size check and a bounded prefix scan. That is a deterministic work reduction; the test does not depend on a particular processor completing within an arbitrary number of milliseconds.
The familiar forecast presentation remains: a 380-byte content budget and compact IRC output of at most two 400-byte payloads.
Fortnite responses had guards around their main objects, but several leaves were still trusted when displayed. A valid object root can contain an invalid account name, battle-pass value or statistic.
A declared API status also reached numeric comparison before its type and spelling were checked. A string such as 200oops could be interpreted through its numeric prefix; containers and other malformed values could generate warnings or unsuitable diagnostics.
MB875 requires a supplied status to be a defined scalar containing three ASCII digits before applying the existing status comparison. Integer and string forms of 200 remain successful. A response omitting the status field retains its previous compatibility, and ordinary error statuses keep their existing failure path.
Displayed account and statistic values must likewise be scalars. An unusable account name falls back to the requested nickname. An unusable statistic uses its established missing-value default, while other good fields remain visible. The patch keeps existing rounding and mode selection rather than introducing a new interpretation of every numeric field.
The regressions exercise the real built-in command, its Context and account queries against an isolated SQLite database. Overall, solo, duo, trio and squad selection, access refusal, private routing and complete formatted replies are checked together. The public API is replaced with controlled responses, so malformed inputs can be reproduced without depending on a live service.
The scoreboard should contain the player’s statistics, never HASH(...) or ARRAY(...) masquerading as one.
Those Fortnite changes made a structural problem especially visible: YouTube.pm contained video functionality, weather and game statistics. Their shared address obscured their different responsibilities.
MB876 gives the implementations explicit homes:
| Module | Responsibility after extraction |
|---|---|
Mediabot::External::YouTube |
Video identity, metadata, rendering and search |
Mediabot::External::Weather |
Weather lookup, location handling, caching and recovery |
Mediabot::External::Fortnite |
Account lookup, access checks, API parsing and statistics rendering |
Mediabot::External::URLCodec |
The byte-safe URL encoder shared by compatible callers |
At this extraction point, YouTube’s source drops from 1,727 to 1,311 lines. The moved implementations remain available in their owners, and the existing External facade imports them directly.
Compatibility receives equal attention. Historical imports and fully qualified calls through the YouTube package continue to reach the same functions through aliases. Existing commands retain their names and dispatch. The weather cache remains one shared cache, including when callers enter through different interfaces.
All 25 original function definitions were compared byte for byte with their final owners. This gives the extraction a precise boundary: the corrected Fortnite behaviour from MB875, weather freshness and outage reuse, HTTP settings, access checks, formatting and destinations travel with their implementations.
Cold interpreter loads, different import orders and real command calls test that boundary. A feature acquiring its own module should make dependencies easier to understand without requiring every established caller to migrate at once.
The next review found a familiar gap in TMDB metadata. Root and result-list checks existed, yet optional fields could still contain arrays, objects, booleans or unusable numeric text.
A malformed synopsis now uses the existing missing-synopsis message. An unusable release date no longer hides a usable first-air date. A rating must be a finite numeric scalar before it is formatted; otherwise the reply displays ?. Valid zero, decimals and scientific notation keep their established one-decimal presentation.
The other correction concerns the complete outgoing message. Its limit had been measured in characters, although IRC transport ultimately sends encoded bytes. Accents, CJK text and emoji can occupy several UTF-8 bytes per character. A reply could therefore satisfy the character limit and still exceed the intended payload budget, leaving the downstream splitter to produce extra lines.
TMDB now measures the whole reply in wire bytes, including the title, destination prefix, metadata and synopsis. The three truncation dots are reserved inside that budget. Existing word and punctuation cleanup remains in place, and the prefix helper need only examine a portion capable of fitting.
The configuration range remains 120–900. At the normal 400-byte setting and below, the reply fits one downstream chunk. A configured budget above 400 can still produce multiple transport chunks under the existing sender limit. This change preserves UTF-8 character boundaries; horoscope’s separate grapheme-aware policy remains its own concern.
Tests cover complete replies with long titles and nicknames as well as multilingual synopses. This matters because clipping the synopsis alone cannot establish that the final assembled message fits.
The TMDB correction prompted the next ownership question. Movie and television catalogue search had no reason to live inside the Claude implementation.
Four existing functions move into Mediabot::External::TMDB, carrying query repair, search, metadata fallbacks and rendering. The public External facade imports the catalogue command from its new owner. Historical Claude imports and fully qualified helper calls remain usable through aliases.
At the same time, two byte-budget helpers shared by AI output and TMDB move into Mediabot::IRC::TextBudget. This provides the first small, named home for reusable IRC text accounting.
Claude’s source drops from 2,398 to 2,157 lines at this stage. All 45 original function bodies remain byte-identical in their final owners. TMDB can load without Claude or the AI client and transport. The catalogue feature’s dependencies become visible without pulling provider behaviour into a generic text module.
The security audit follows the move too. Its authenticated HTTP invariant previously inspected Claude, which also contained TMDB. It now requires the expected verified-TLS calls in both owners. Removing a call or an entire module cannot be concealed by the other module’s correct settings. Deliberately damaged source fixtures check that the audit notices these faults.
The audit still has 37 invariants. Its understanding of the implementation has become more accurate.
TextBudget immediately suggested broader reuse. The review started with an essential distinction: several methods mentioning “UTF-8 length” expressed different input contracts.
One caller holds application characters that must be encoded to measure their outgoing size. Another already holds encoded wire bytes. Treating both identically can either undercount text or encode an existing byte buffer for a second time.
The shared module therefore preserves two explicit measurements:
| Measurement | Meaning |
|---|---|
| Wire-byte accounting | Encode flagged character strings; treat unflagged buffers as existing wire bytes |
| Explicit UTF-8 text accounting | Encode application character text, including unflagged Latin-1 characters |
An unflagged scalar containing byte E9 illustrates the difference. As an existing wire buffer it occupies one byte. Interpreted as character U+00E9 and encoded to UTF-8, it occupies two. Choosing the right primitive depends on what the caller actually holds.
MB879 also moves the existing single-line sanitizer, word splitter and legacy truncator into TextBudget. This reverses an awkward dependency: TextBudget had called back into the much larger Helpers module. It can now load independently using only its small Perl dependencies, while Helpers retains callable compatibility wrappers and its existing exports.
Compatible consumers share the accounting primitive: AI/meta output, horoscope, news, plugin replies, RSS, random quotes, UserCommands and VDM among them. Their surrounding policies remain explicit.
Horoscope keeps complete graphemes. News keeps its established codepoint clipping and tiny-budget behaviour. RSS keeps the URL whole and rejects a line if that intact URL cannot fit. Plugin and VDM rejection rules remain. AI retains Markdown handling, balanced styles, suffix reservation and its two-line limit. DTC and Radio keep their specialised fitting methods.
Even the old truncator’s suffix convention is preserved: its maximum bounds the content, with the suffix appended outside it. A caller requiring a total payload limit must continue to reserve suffix space itself. Quietly changing that meaning during extraction would alter established output.
The review also leaves unrelated database limits, HTTP lengths, hashes and prompt budgets in their own domains. They answer different questions from IRC display fitting.
Across 1,226 sampled before/after scenarios, actual implementations agree on results, bytes, UTF-8 flags, warnings and errors. The cases include raw buffers, Latin-1, Unicode, styles, line controls, combining marks and joined emoji.
Madam Malkin would approve of sharing a reliable measuring tape while keeping each robe’s own pattern. 🧵
The next round extends that approach into five modules alongside TextBudget:
Module under Mediabot::IRC |
Shared responsibility |
|---|---|
Target |
Recognise the existing channel-prefix forms |
CaseMapping |
Apply the selected ASCII or IRC case mapping |
Text |
Decode inbound text, normalize line controls and mask service secrets in diagnostics |
Presentation |
Render the existing compact Markdown-style reply |
PacedOutput |
Schedule the established PRIVMSG and help-NOTICE batches |
These boundaries are intentionally specific. Recognising #, &, ! or + as a channel prefix does not establish permissions or fully validate a destination. Folding a target for comparison does not authorise it either. Callers retain their input guards.
Case mapping is shared by compatible conversation, Hailo, radio, authentication and operator paths. Each retains its selected mode. Conversation state still starts conservatively in ASCII mode until network ISUPPORT information selects another supported mapping. Existing queue keys are not silently migrated to a different identity rule.
Presentation receives the existing compact renderer. Its two-line maximum, styles, suffix behaviour and wrap range travel with it. AI::IRCOutput remains the AI-facing compatibility facade, keeping provider-related prompt instructions outside the generic IRC renderer.
PacedOutput receives two established scheduling policies. PRIVMSG batches and help NOTICE batches continue to differ in first-send timing, caps, delays and fallback behaviour. Specialised RSS, Hailo and conversation queues retain their own state machines, including expiry or delivery requirements absent from this general batch scheduler.
The pure utilities load without the bot runtime. Pacing loads its runtime dependencies only when needed. Existing Helpers and Claude entry points keep forwarding into the shared implementation, and actual sends still cross the interception boundary used by asynchronous command workers.
The Room of Requirement now has named doors. A caller can choose a focused IRC tool without wandering through AI or unrelated command code.
Once text, presentation and pacing had clear homes, the actual output boundary followed.
Mediabot::IRC::Output provides privmsg, action and notice to other modules. The existing send implementations move into internal IRC::Sender, and the AntiFlood deferred queue moves into internal IRC::DeferredOutput. PacedOutput adopts the public Output facade.
The forwarding route is significant. Each Output call resolves the corresponding Helpers function at call time. Helpers keeps its historical wrappers, which normally forward to Sender. An asynchronous worker can still replace that boundary to collect output for parent-side replay.
Capturing a real sender’s function reference permanently during import would have bypassed a later worker override. The new facade preserves the dynamic interception point, so a more convenient import does not create an unintended direct route to IRC.
The three sender bodies retain their separate policies and return conventions. PRIVMSG and ACTION keep their existing NoColors, AntiFlood and badword paths. NOTICE keeps its own behaviour. Normalization, byte splitting, CTCP ACTION envelopes, metrics, logs and conversation history retain their established order.
This includes subtleties beyond the wire payload. A rejected channel reply must not claim LIVE output or enter accepted conversation history. Service credentials must reach their intended service while remaining masked in diagnostic copies. Contextual replies using no_defer must keep their immediate-rejection behaviour during AntiFlood.
Deferred output also keeps its existing thirty-item per-target cap and two-second drain. A blocked item returns to the head, and eventual delivery re-enters the ordinary sender so current filters and channel settings are checked again.
The sanitizer audit now follows this complete ownership chain. Actual-sender tests and deliberately broken audit fixtures check the connections, rather than relying on an unrelated newline-cleanup expression elsewhere in Helpers.
Extracting the queues exposed lifecycle failures worth correcting in their own round. An expired AntiFlood countdown could remain registered in its IO::Async loop. A timer-start exception could leave a notifier attached while the queue still believed itself armed or active. A NOTICE send exception could retain its tracked timer slot.
These states matter even when ordinary messages appear to work. A flag claiming that a timer exists can strand pending output, and retained callbacks can keep objects alive beyond their useful lifetime.
Mediabot::IRC::Timer::countdown now owns the lifecycle of a single-use countdown: construction, loop attachment, start, expiry and terminal cleanup. An expired timer is removed before its callback runs. Partial setup failures trigger cleanup, and captured callbacks are released so a retained expiry reference cannot replay completed work.
Original exceptions remain visible. Cleanup failures are suppressed to avoid replacing the original error; a loop that itself refuses removal remains a real limitation. The helper introduces no general scheduler, periodic-timer policy or blanket conversion of independently cancellable timers elsewhere.
Each output queue keeps its own recovery decision:
An attempted send is not automatically replayed: the transport may have transmitted it before throwing. Avoiding an uncertain duplicate is part of the recovery contract.
Deterministic failure fixtures cover each setup stage, stale callbacks, reentrant sends and exception identity. Native IO::Async tests include fifty mixed AntiFlood expiry cycles, checking real notifier cleanup as well as the queue’s own bookkeeping. The Time-Turner has acquired an owner and an exit procedure.
The next defect concerned ordering. The pacing code allowed a new one-chunk reply, or a batch requesting zero delay, to take an immediate path before inspecting existing work for its target.
An older reply could therefore be interrupted:
| Situation | Delivery order |
|---|---|
| Previous behaviour | old1, new, old2, old3 |
| Corrected behaviour | old1, old2, old3, new |
MB883 consults active and pending work first. Later short or zero-delay batches join the existing target queue. They can also resume inactive work left by an earlier scheduling failure, keeping older unsent chunks ahead of new output.
An active queue retains its current timer and loop owner, even if the bot no longer exposes that loop through its lookup. A second request cannot create a competing timer for the same active queue.
Idle targets retain their immediate path for a single reply or zero-delay batch. Different targets remain independent. Each batch keeps its own delay, and the established gap between batches remains. There is no blocking sleep and no new global flood rate.
Both new regression cases fail against the previous implementation and pass against the corrected one. They exercise deterministic scheduling and native delivery, including reentrant enqueues and recovery after repeated setup failure.
The Hogwarts Express can add a short carriage without inserting it through the middle of a train already leaving the platform.
By MB883, the complete suite had reached 30,844 assertions in 1,194 files. The request to control its growth led to a separate review of what the tests actually established.
Thirty-eight files were retired. Their contents included closed-roadmap wording, historical README phrasing, comment markers, source layout, broad function-existence checks, exact implementation spellings and algorithms copied into tests.
A copied formatter can keep passing even after the application’s formatter breaks. Counting prepare and finish strings cannot establish that a statement is cleaned up after a failed query. A test requiring a particular comment can obstruct a valid extraction without detecting a user-visible defect.
The review records each removal and the retained behavioural coverage where it exists. Some constraints, such as exact historical prose and image dimensions, are deliberately no longer enforced. The decision does not claim that every removed assertion was an identical duplicate or that pruning carries no risk.
Where consolidation improved the evidence, it was done before removal. Six log and history sanitation assertions were added to the existing actual-sender case. They exercise production output rather than a copied sanitizer or an ordering inference from source positions.
Authentication and access control, SQL binding and failure cleanup, secrets, wire-byte limits, worker delivery, durable state, guarded updates, process termination and real integration checks remain. The slow ScriptRunner timeout and symlink cases remain too: their cost supports a meaningful property.
The arithmetic is explicit: 744 old assertions removed, six added, a net reduction of 738. The catalogue drops from 1,194 to 1,156 files. Every remaining discovered case still belongs to the ordinary full run. There is no hidden exclusion list, quarantine or cached pass.
MB884 also removes avoidable idle time while retaining the actual protocols under test.
Two network-statistics HTTP fixtures reduce their shutdown polling interval from 500 ms to 10 ms. Requests still cross real local HTTP and SQLite boundaries. Production polling intervals and request deadlines are unchanged.
The radio loopback fixture had accepted a push connection but failed to serve two subsequent queue-observation requests. Those reads waited for the real client’s timeouts. The corrected fixture serves all three real connections, checks both queue readbacks and verifies the observed position, while retaining the metadata, acknowledgement and file-integrity checks. All 166 Python test methods remain.
On the development host, the full-suite result changes from 190 seconds at MB883 to 173 seconds at MB884, around nine percent less observed elapsed time. These are successive qualification runs, with both pruning and fixture changes between them, rather than a repeated benchmark isolating either contribution.
The policy for later rounds follows naturally: extend the owning behaviour test where possible, add distinct boundaries and realistic failure checks, and profile time instead of treating the assertion counter as a trophy cabinet.
With TextBudget shared, improving its work benefits all compatible callers. The splitter still encoded the entire remaining buffer at every chunk and materialized all remaining characters while choosing the next cut. Prefix-only callers invoked the full splitter and discarded everything after its first result.
A private next-chunk helper now examines a window of at most budget plus one characters. Each character occupies at least one wire byte, so that window is sufficient to determine the next cut. Full wrapping consumes successive chunks. A normal-budget prefix request stops after its first decision.
The initial whole-input fit check remains, as do the existing sanitation passes where required. This reduces repeated work; it does not make processing arbitrary-length input constant-time or introduce a new input-size limit.
An isolated local benchmark used the two actual implementations, a 400-byte budget and mixed text containing accents and emoji. Each value below is the median of three executions:
| Input characters | Full wrapping before | Full wrapping after | First prefix before | First prefix after |
|---|---|---|---|---|
| 20,000 | 41.3 ms | 5.4 ms | 40.7 ms | 0.185 ms |
| 40,000 | 149.6 ms | 10.9 ms | 156.3 ms | 0.379 ms |
| 80,000 | 630.6 ms | 26.2 ms | 638.8 ms | 0.597 ms |
These figures concern unusually long strings in this local microbenchmark. They do not describe the latency of every bot command or establish a corresponding full-suite speedup. Already-fitting short messages retain their direct return.
Compatibility includes details invisible in a screenshot: scalar-context returns, whitespace at cuts, normalization order, raw buffers and Perl’s UTF-8 representation flag. A temporary prefix window can lose that flag for an ASCII tail, so returned content is taken from the original working buffer. A mutation removing that preservation is caught by the new regression.
18,900 direct before/after comparisons agree, alongside the 1,226 historical consumer comparisons. The permanent suite gains nine focused assertions in its existing TextBudget case and no new numbered file.
The visible request was small: display [AppleMusic] while keeping the existing colours. The badge retains white text on grey, white-on-black brackets, reset bytes and spacing.
The implementation now has a suitable home too. Seven Apple Music functions move from External::URL to External::AppleMusic, covering duration helpers, URL identity, structured lookup, page details, synchronous fetch and asynchronous handling.
At this extraction point, URL’s source falls from 2,186 to 1,761 lines. Its 28 remaining named function bodies stay unchanged. The External facade imports the handler from its owner, while all seven historical URL symbols remain aliases to the same implementation.
Shared HTML, decoding and browser utilities are resolved dynamically. This preserves existing overrides and avoids an eager circular import. The new feature module can load independently of URL and AI providers.
The badge also reveals an unusually subtle compatibility issue. Its old command-symbol character had caused the composed message to carry Perl’s character representation. Replacing that symbol with an entirely ASCII label could change how legacy byte values were subsequently handled. The new badge explicitly retains the old representation, preserving more than its appearance.
Forty-six sampled before/after scenarios agree after normalizing only the requested label change. Removing the representation-preservation line breaks four of them.
Lookup, HTTP/browser fallback, cache keys, the ten-minute cache lifetime, pending requests, worker options, rich details and legacy truncation retain their policies. Nine assertions extend the existing Apple Music test.
Reviewing the extracted module then exposed actual metadata failures. Controlled JSON passed through the real handler could display HASH(...) as a track name, nested references as artist or album details, and InfhNaNmNaNs as the duration of an enormous millisecond value.
A usable title is now required before a lookup result is accepted. It must be a defined, non-reference scalar containing non-whitespace text. Scalar 0 remains usable. A rejected lookup can reach the existing page fallback instead of stopping recovery with a malformed “success”.
Optional metadata is handled independently. Invalid artist, album or date leaves are omitted while a good title survives. Mixed JSON-LD artist lists retain usable entries in their original order. Duration components must be numeric and finite before formatting; unusable optional durations disappear without arithmetic warnings.
The worker completion boundary validates the data independently and copies the six scalar display leaves without mutating the received result. An unusable title creates neither Apple Music output nor a successful cache entry. Pending state is released so a later attempt can proceed.
This is fault containment at the field and result boundaries. A damaged optional detail should not discard a usable title, while a missing usable title should not become a cached successful reply.
Fourteen assertions extend the existing test, bringing it to 44. Eleven of those 44 fail on the original implementation; all pass after correction. A separate set of 160 retained-behaviour comparisons agrees on valid formatting, bytes, flags, cache and pending state, call traces and numeric duration boundaries.
These are reproducible malformed-response cases, not a claim that a particular Apple service outage was observed.
The latest round returns explicitly to code ownership. UserCommands.pm still combined command dispatch and game state with the HTTP clients and parsers for Wiktionary and Open Trivia DB.
Four source functions move into two modules:
| Owner | Responsibility |
|---|---|
Mediabot::External::Wiktionary |
Deterministic entry selection and synchronous definition lookup |
Mediabot::External::OpenTriviaDB |
Question parsing and synchronous fetching with its existing bounded retry |
Mediabot::UserCommands |
Asynchronous adapters, command validation, callbacks, replies and game state |
UserCommands drops from 8,462 to 7,876 lines. The four moved function bodies and all 85 remaining named function bodies are byte-identical to their pre-extraction versions. Its 101 default exports are retained.
The asynchronous adapters remain in UserCommands and still resolve the historical synchronous symbols at call time. Runtime overrides of those hooks continue to work. The two new owners load independently and keep the existing HTTP factory relationship.
Provider policies remain distinct. Wiktionary retains its 512 KiB response bound, language selection, query escaping and definition cleanup. Open Trivia DB retains its 64 KiB bound, default seven-second hard HTTP deadline, progress protocol and single rate-limit retry. The retry wait stays inside the worker, and outer deadlines and transport remain the adapters’ responsibilities.
Six existing tests follow the new owners. Eight assertions extend two of them to check cold loading, import order, aliases and actual adapter hooks. The targeted lane passes 249 assertions, and 67 sampled before/after scenarios agree on results, encoding flags, request options, errors and retry traces after elapsed-time normalization.
The development-host full suite then passes 30,155 / 30,155. This final extraction adds no numbered test file.
Moving code changes dependencies, symbol resolution and initialization order even when its function bodies remain identical. The qualification work therefore checks several kinds of evidence together.
Source identity establishes the extraction boundary. Byte comparisons verify that moved bodies and unrelated remaining bodies are unchanged where that is the intended contract. Bug-fix rounds separately identify the behaviour they deliberately correct.
Cold loads check the dependency shape. Fresh interpreters load owners alone and in different orders with their legacy facades. This catches eager runtime loading, circular dependencies and aliases pointing at a different implementation.
Real paths check integration. Depending on the feature, fixtures retain actual command dispatch, Context, SQLite queries, local HTTP/TCP, IO::Async notifiers, forked workers, pipe transport and parent replay. Controlled seams replace external services or policy inputs so failure states are reproducible.
Differential checks compare representations as well as text. Visible equality can conceal a changed raw buffer, UTF-8 flag, return value, warning, cache entry, timer trace or diagnostic. Those details matter at boundaries used by several modules.
Faults test the tests. The new ordering cases fail against the old queue. The Apple Music case exposes its original malformed metadata. Audit fixtures deliberately remove ownership links or TLS calls. TextBudget’s representation mutation is detected. These checks demonstrate sensitivity to concrete mistakes.
The limits remain recorded. Sampled equivalence cannot prove every possible Perl input. Local full runs retain an existing MB786 updater-rehearsal failure where it occurs, rather than being described as wholly green. MB888’s local native Wiktionary probes return the same existing fallback before and after; they establish equivalence there, while successful synchronous lookup is qualified separately. They are not presented as successful native delivery evidence.
The reported target-host full suites pass without excluding that updater test or any other remaining case. Source-level security invariants and startup loading checks complement those executions; they do not replace them or claim an exhaustive security proof.
Each row records the complete reported development-host run after its round. Every run also passes the 37 security invariants and startup integrity.
| Round | Main work | Test files | Passed assertions | Elapsed |
|---|---|---|---|---|
| MB874 | Horoscope identity and Unicode fitting | 1,174 | 28,957 | 176 s |
| MB875 | Fortnite status and display contracts | 1,176 | 29,271 | 179 s |
| MB876 | YouTube, weather and Fortnite ownership | 1,178 | 29,418 | 181 s |
| MB877 | TMDB metadata and complete reply byte limit | 1,180 | 29,577 | 183 s |
| MB878 | TMDB extraction and initial TextBudget | 1,182 | 29,733 | 186 s |
| MB879 | Shared compatible text-budget primitives | 1,184 | 29,972 | 186 s |
| MB880 | Reusable IRC utility and pacing modules | 1,186 | 30,182 | 187 s |
| MB881 | Output facade, sender and deferred queue | 1,189 | 30,371 | 188 s |
| MB882 | Owned countdowns and queue recovery | 1,192 | 30,658 | 190 s |
| MB883 | Ordered short and zero-delay replies | 1,194 | 30,844 | 190 s |
| MB884 | Reviewed test pruning and fixture completion | 1,156 | 30,106 | 173 s |
| MB885 | Bounded shared wrapping work | 1,156 | 30,115 | 174 s |
| MB886 | Apple Music module and badge | 1,156 | 30,127 | 175 s |
| MB887 | Apple Music metadata contracts | 1,156 | 30,141 | 176 s |
| MB888 | Wiktionary and Open Trivia DB extraction | 1,156 | 30,155 | 176 s |
Relative to the previous article’s MB873 endpoint, this leaves 1,379 more passing assertions and sixteen fewer files. The later four rounds extend existing cases instead of adding a new numbered file for each patch. Generic module checks also account for some assertion increases when a new owner appears.
The ledger supports a more useful account of progress than an ever-rising total: broader checks where failures matter, retired constraints where their value has passed, and less waiting unrelated to the property being tested.
Weather, Fortnite, TMDB, Apple Music, Wiktionary and Open Trivia DB now have explicit implementation owners. Shared URL encoding has a small home of its own. Under Mediabot::IRC, byte budgets, text handling, target classification, case mapping, presentation, sending, pacing, deferral and countdown ownership have named boundaries.
That structure gives the next change a narrower place to begin. A movie metadata fix can live with the catalogue parser. An output-ordering fix can live with pacing. A wrapping optimisation can be measured once at the common primitive while each consumer keeps its display policy.
Compatibility aliases and dynamic facades keep established callers connected during that work. The database schema remains unchanged, and this chapter records validated development work on 3.6dev without announcing a stable release or production deployment.
There is still plenty of castle left to inspect. Its newly labelled corridors make the next inspection easier to conduct—and the owl post has learned to finish an older letter before delivering a newer one. 🦉
You must be logged in to reply.