<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Mighty Meat Shield - Updates</title>
        <link>https://mightymeatshield.com/updates/</link>
        <atom:link href="https://mightymeatshield.com/updates/rss.xml" rel="self" type="application/rss+xml" />
        <description>Studio updates, bot releases, campaign announcements, and patch notes from the Mighty Meat Shield live-service TTRPG studio.</description>
        <language>en-us</language>
        <lastBuildDate>Tue, 02 Jun 2026 06:35:08 +0000</lastBuildDate>
        <image>
            <url>https://mightymeatshield.com/logo.png</url>
            <title>Mighty Meat Shield - Updates</title>
            <link>https://mightymeatshield.com/updates/</link>
        </image>
        <item>
            <title>Spend Your Meaty Points on Currency Boosts</title>
            <link>https://mightymeatshield.com/updates/meaty-points-currency-boosts.html</link>
            <guid isPermaLink="true">https://mightymeatshield.com/updates/meaty-points-currency-boosts.html</guid>
            <pubDate>Tue, 02 Jun 2026 12:00:00 +0000</pubDate>
            <author>noreply@mightymeatshield.com (Cid)</author>
            <description>Meaty Points now buy in-game currency. Turn your account-wide 🍖 into XP, gold, wagers, or whatever your table runs, straight into your wallet, at a rate set per server.</description>
            <content:encoded><![CDATA[<p><img src="https://mightymeatshield.com/updates/posts/2026-scribey-update-banner-standard.png" alt="Scribey currency boosts update banner"></p>
<p>Meaty Points have been piling up since we wired them into check-ins: show up, log a roll, keep a streak, and your account-wide 🍖 balance climbs. Until now that number mostly bought bragging rights on the leaderboard. As of this drop, it buys things your table actually cares about.</p>
<h2>Boost a Currency</h2>
<p>Open <code>/dashboard</code>, hit <strong>Spend Points</strong>, and there&rsquo;s a new option: <strong>🍖 Boost a Currency</strong>. Pick what you want (XP, gold, a wager, whatever your server runs), punch in how many Meaty Points to spend, confirm, and the currency lands in your server wallet right away. No waiting on a GM to hand it over.</p>
<p>Meaty Points are account-wide, so points you earned checking into one game can buy currency in another. Your wallet stays per-server, though. Gold you boost in one campaign doesn&rsquo;t leak into the next.</p>
<h2>The Exchange Rate</h2>
<p>Every currency has its own rate, written as <strong>X Meaty Points → Y units</strong>. The baseline is dead simple: <strong>1 🍖 → 1 unit</strong>. From there the rate depends on what your server runs:</p>
<ul>
<li><strong>Denominations.</strong> A server running gold/silver/copper might set <code>1 🍖 → 1</code> gold, <code>1 🍖 → 10</code> silver, <code>1 🍖 → 100</code> copper. Same value, different coins.</li>
<li><strong>Single-currency systems.</strong> Cyberpunk runs on Eddies and nothing else, so it&rsquo;s just <code>1 🍖 → 1</code> and done.</li>
<li><strong>Scarce stuff.</strong> A wager or an Inspiration point is worth far more than a coin, so your server might set it at <code>25 🍖 → 1</code>.</li>
</ul>
<p>You see the rate before you spend, and the shop tells you exactly how much you&rsquo;ll get.</p>
<h2>Only What Your Table Runs</h2>
<p>The shop only lists currencies your server actually runs. If your server has no gold economy, there&rsquo;s no gold boost. If a currency&rsquo;s boost is switched off, it doesn&rsquo;t show.</p>
<p>This matters most for systems where &ldquo;currency&rdquo; isn&rsquo;t a wallet at all. FASERIP handles wealth as a dice roll, not a coin count, so a FASERIP server has nothing to boost and the option simply isn&rsquo;t there. You&rsquo;ll never see a button for money that doesn&rsquo;t exist in your game.</p>
<h2>What You Actually Get</h2>
<ul>
<li><strong>Wagers.</strong> Boost a wager and it sits in your wallet until you want it. DiceBoy&rsquo;s reroll button spends it the same as one you earned.</li>
<li><strong>XP and gold.</strong> These land in your wallet and do whatever they already do in your game: level you up, buy your gear, fund your schemes.</li>
</ul>
<h2>Built So It Can&rsquo;t Shortchange You</h2>
<p>A boost spends global points and credits a server wallet, two separate ledgers. The whole thing runs as one transaction: if any part fails, it rolls back completely. You&rsquo;re never charged Meaty Points without getting the currency, and you never walk away with currency for free. Either the whole trade happens or none of it does.</p>
<h2>Try It</h2>
<p><code>/dashboard → Spend Points → 🍖 Boost a Currency</code>. Spend down that leaderboard score on something you can use.</p>]]></content:encoded>
        </item>
        <item>
            <title>Scribey v1.5.0 — The Code Cleanse</title>
            <link>https://mightymeatshield.com/updates/scribey-v1-5-0-the-code-cleanse.html</link>
            <guid isPermaLink="true">https://mightymeatshield.com/updates/scribey-v1-5-0-the-code-cleanse.html</guid>
            <pubDate>Tue, 12 May 2026 12:00:00 +0000</pubDate>
            <author>noreply@mightymeatshield.com (Cid)</author>
            <description>First formal release tag for the bot stack. Two months of stability hardening, audit remediation, and a seven-fix bug-hunt sweep consolidated into one named version.</description>
            <content:encoded><![CDATA[<p><img src="https://mightymeatshield.com/updates/posts/2026-05-12-scribey-v1-5-0-the-code-cleanse.png" alt="Scribey v1.5.0 — The Code Cleanse"></p>
<p>After running on production since early March, we&rsquo;re tagging the official <strong>v1.5.0</strong> release. This consolidates two months of stability hardening, audit remediation, and verified bug fixes into one named version.</p>
<h2>Patch Highlights</h2>
<p><strong>Platform stability hardening.</strong> The systemd unit now caps memory (<code>MemoryHigh=1200M</code> / <code>MemoryMax=1500M</code>) so a runaway process triggers a clean unit-level restart instead of the kernel OOM killer picking a random victim. Prompted by the 2026-05-09 Discord gateway outage, which stalled all three bots&rsquo; event loops simultaneously.</p>
<p><strong>Bug-hunt sweep (BH-2026-05-11).</strong> Seven verified fixes — four high, three medium — across RollCall, DiceBoy, and the backup tooling. Each fix shipped with a regression test promoted from its original repro. Test suite went from 0 → 9 passing.</p>
<p><strong>Red-team audit closed.</strong> Audit <code>2026-04-17</code> produced 0 critical, 3 high, 4 medium findings across Scribey&rsquo;s quest claim flow, the restore script, and the resilience monitor. All remediated in commit <code>4b56f52</code>.</p>
<h2>Added</h2>
<ul>
<li>pytest + pytest-asyncio infrastructure and a <code>tests/regression/</code> suite (9 passing tests, all promoted from bug-hunt repros).</li>
<li><code>pbp_shared_data/.env.example</code> template documenting required token environment without committing secrets.</li>
</ul>
<h2>Changed</h2>
<ul>
<li><strong>Backup mechanism.</strong> <code>backup_data.py</code> now uses SQLite&rsquo;s Online Backup API for <code>.db</code> files instead of <code>shutil.copy2</code>. Captures a consistent WAL snapshot in a single transaction. Previously, hot backups could miss uncheckpointed writes.</li>
<li><strong>Systemd unit (<code>pbp-bots.service</code>).</strong> Added <code>MemoryHigh=1200M</code> / <code>MemoryMax=1500M</code>. Tune to ~70–80% of VM RAM; if your VM is under 2 GB, lower these before deploying.</li>
<li><strong>Watchdog ping logic</strong> in <code>run_all_bots.py</code>. The supervisor withholds the <code>WATCHDOG=1</code> ping if any child&rsquo;s heartbeat is older than 240s (2× the kill window). A hung child now fails the unit cleanly. Look for <code>heartbeat stale past kill window — withholding watchdog ping</code> in <code>journalctl -u pbp-bots</code>.</li>
<li><strong>Deploy archive (<code>prepare_deploy.py</code>).</strong> Exclusion list tightened to runtime-only: <code>.bugs/</code>, <code>.pytest_cache/</code>, <code>tests/</code>, <code>pytest.ini</code>, <code>.gitignore</code>, <code>CLAUDE.md</code>, and <code>DEPLOYMENT.md</code> no longer ship to the VM. Archive shrank from 97 files / 262 KB to 51 files / 204 KB.</li>
<li>All <code>requirements.txt</code> files now pin third-party packages to specific versions.</li>
<li>Type hints across Scribey use a <code>ScribeyBot</code> alias for stricter type checking.</li>
</ul>
<h2>Fixed</h2>
<h3>BH-2026-05-11 batch — verified repros, regression tests included</h3>
<p><strong>High</strong></p>
<ul>
<li><strong>RollCall</strong> daily-streak counter reported <code>0</code> for users whose only check-in was yesterday. (<code>BH-001</code>, <code>9c4cc31</code>)</li>
<li><code>backup_data.py</code> could lose uncheckpointed WAL writes during a hot backup. Now uses the Online Backup API. (<code>BH-002</code>, <code>a6e79ba</code>)</li>
<li><strong>DiceBoy</strong> wager resolver picked a stranded zero-balance row over a positive-balance row when both existed for the same user, returning <code>0</code> and refusing wagers. (<code>BH-004</code>, <code>4869adb</code>)</li>
<li><code>update_user_stats</code> raised <code>IntegrityError</code> on a user&rsquo;s first-ever roll when two stats updates raced. Now serialized with <code>BEGIN IMMEDIATE</code>. (<code>BH-006</code>, <code>5d64677</code>)</li>
</ul>
<p><strong>Medium</strong></p>
<ul>
<li>Group rolls dropped compound modifiers (<code>1d20+5+3</code> summed only the first operand). Now sums all <code>+/-</code> terms. (<code>BH-003</code>, <code>3473247</code>)</li>
<li>DiceParser ignored compound keep/drop expressions like <code>kh3dl1</code>. Modifiers now apply sequentially. (<code>BH-005</code>, <code>65bb03d</code>)</li>
<li><code>get_dice_config</code> crashed when <code>valid_dice</code> was empty or malformed. Now tolerates degenerate config rows. (<code>BH-007</code>, <code>f946b6e</code>)</li>
</ul>
<h3>Red-team audit batch — <code>2026-04-17</code>, remediated in <code>4b56f52</code></h3>
<p><strong>High</strong></p>
<ul>
<li>Scribey&rsquo;s quest claim audit trail recorded the <em>intended</em> award even when the wallet/inventory write rolled back, producing phantom &ldquo;Reward Claimed&rdquo; embeds. Award functions now return <code>Optional[int]</code> and <code>finalize_claim</code> records the amount actually delivered.</li>
<li>Rare-drop announcements had no per-drop dedup. A high-claim Rare item could queue dozens of announcement embeds against Discord&rsquo;s per-channel rate limit. Now tracks announced item IDs per drop.</li>
<li><code>restore_data.py</code> silently swallowed all migration exceptions, falsely reporting success on a half-migrated database. Now logs the exception and exits non-zero.</li>
</ul>
<p><strong>Medium</strong></p>
<ul>
<li><code>restore_data.py</code> wrote via <code>shutil.copy2</code>, leaving a partial DB on crash. Now uses atomic-write pattern.</li>
<li>Scribey was unnecessarily requesting the privileged <code>message_content</code> intent. Removed.</li>
<li>Backup integrity verification switched from MD5 to SHA256.</li>
<li><code>_resilience_monitor</code> only restarted loops that explicitly reported failure; silent hangs went undetected. Now catches silent hangs too.</li>
</ul>
<h2>Security</h2>
<ul>
<li>Removed a <code>.env</code> file containing live bot tokens from the working tree (<code>f1b3830</code>). Tokens were rotated; production values live only on the VM.</li>
<li>Pinned third-party dependencies across all three bots and the runner (<code>4c44f98</code>).</li>
<li><code>.env.example</code> template plus <code>.gitignore</code> review prevent future token commits.</li>
<li>Manual security audit (<code>SECURITY_AUDIT_2026-03-05</code>) and red-team audit (<code>2026-04-17</code>) both closed. Reports archived from the working tree after remediation, retained in git history.</li>
</ul>
<h2>Internal</h2>
<ul>
<li>pytest scaffolding (<code>pytest.ini</code>, <code>tests/conftest.py</code>) landed as a <code>chore:</code> commit on <code>main</code> so the bug-hunt remediation branch stayed fix-only.</li>
<li><code>_shared.py</code> wrapper archived; consumers now import from <code>pbp_shared_data</code> directly.</li>
<li>Old audit-report artifacts removed from working tree after remediation (<code>197523b</code>).</li>
</ul>
<h2>Operator Notes</h2>
<ul>
<li>After deploying, <code>vm_deploy.sh</code> runs <code>systemctl daemon-reload</code>, so the new memory limits take effect automatically. If you deploy manually, run it yourself before <code>systemctl restart pbp-bots</code>.</li>
<li>If <code>MemoryMax</code> triggers a restart under normal load, raise it. The default assumes a 2 GB+ VM; bots run around 200–450 MB combined under ordinary traffic.</li>
</ul>
<h2>On Deck</h2>
<ul>
<li>Restore-side stale <code>-wal</code> / <code>-shm</code> cleanup (followup to BH-2026-05-11-002). Tracked in <code>.bugs/followups.md</code>.</li>
</ul>]]></content:encoded>
        </item>
    </channel>
</rss>
