All insights

Appendix

Source behind the claims

Each entry is a snapshot, captured on the date it names and served unchanged since. What cannot be shown is named rather than substituted.

Read in order, the five entries are one line of work. Blame Machine came first, in April, and is where identity appears at all — an app trusting a gateway’s word about who you are. Operator Confirmation, three weeks later, is the same author reaching for a platform: swappable content, and an engine that plays against the operator. iMessage Locker is the harder version of both, and the only one strictly after the others — adding a feature inside a privacy contract someone else had already made with its users. What connects them is the argument in The Quiet Governor: the infrastructure mediating a decision has already shaped it, and the shaping is experienced as help.

01

Blame Machine — the application, complete

code: full · unredactedrepo: not published

The application is one file. Flask routes, game logic, and the entire frontend as template literals — 4,060 lines, two runtime dependencies. There is no question of which parts are shown, because there are no other parts.

The first of these projects, and the first place identity shows up: a gateway authenticates the visitor and passes who they are in a header, which the app trusts only if a shared secret came with it. Crude — a password in a header, no signature, no expiry — but the shape of every identity-proxying problem that followed, and it fails closed when the secret is unset, which is what the public deployment relies on.

#!/usr/bin/env python3

from __future__ import annotations

import configparser
import base64
import hashlib
import hmac
import io
import json
import math
import os
import random
import re
import secrets
import socket
import struct
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
import webbrowser
import wave
from html import escape as html_escape
from pathlib import Path
from urllib.parse import urlparse
from threading import Timer

from flask import Flask, Response, abort, g, jsonify, redirect, render_template_string, request, url_for

def bundle_dir() -> Path:
    if getattr(sys, "frozen", False):
        return Path(getattr(sys, "_MEIPASS", Path(sys.executable).resolve().parent))
    return Path(__file__).resolve().parent


def writable_app_dir() -> Path:
    configured = (
        os.environ.get("BLAME_MACHINE_DATA_DIR", "").strip()
        or os.environ.get("DECISION_ARCADE_DATA_DIR", "").strip()
    )
    if configured:
        return Path(configured).expanduser()
    if getattr(sys, "frozen", False):
        return Path.home() / ".blame_machine"
    return Path(__file__).resolve().parent


BUNDLE_DIR = bundle_dir()
APP_DIR = writable_app_dir()
APP_DIR.mkdir(parents=True, exist_ok=True, mode=0o700)
try:
    APP_DIR.chmod(0o700)
except PermissionError:
    # Bind-mounted directories may be managed by the host instead.
    pass
DEFAULT_PORT = 5000
DEFAULT_LAN_HOST = "0.0.0.0"
DEFAULT_BROWSER_HOST = "127.0.0.1"
DEFAULT_PUBLIC_URL = ""
IDENTITY_USER_HEADER = os.environ.get("IDENTITY_USER_HEADER", "X-Access-User").strip() or "X-Access-User"
IDENTITY_EMAIL_HEADER = os.environ.get("IDENTITY_EMAIL_HEADER", "X-Access-Email").strip() or "X-Access-Email"
IDENTITY_NAME_HEADER = os.environ.get("IDENTITY_NAME_HEADER", "X-Access-Name").strip() or "X-Access-Name"
IDENTITY_SECRET_HEADER = os.environ.get("IDENTITY_SECRET_HEADER", "X-Access-Proxy-Secret").strip() or "X-Access-Proxy-Secret"
IDENTITY_SHARED_SECRET = os.environ.get("IDENTITY_SHARED_SECRET", "").strip()
LOCAL_USER = os.environ.get("LOCAL_USER", "local").strip() or "local"
GUEST_COOKIE_NAME = os.environ.get("BLAME_MACHINE_GUEST_COOKIE", "blame_machine_guest").strip() or "blame_machine_guest"
GUEST_COOKIE_MAX_AGE = max(3600, int(os.environ.get("BLAME_MACHINE_GUEST_COOKIE_MAX_AGE", str(60 * 60 * 24 * 365)).strip() or str(60 * 60 * 24 * 365)))
TURNSTILE_SITE_KEY = os.environ.get("TURNSTILE_SITE_KEY", "").strip()
TURNSTILE_SECRET_KEY = os.environ.get("TURNSTILE_SECRET_KEY", "").strip()
TURNSTILE_ACTION = os.environ.get("TURNSTILE_ACTION", "start_play").strip() or "start_play"
TURNSTILE_COOKIE_NAME = os.environ.get("TURNSTILE_COOKIE_NAME", "blame_machine_human").strip() or "blame_machine_human"
TURNSTILE_COOKIE_MAX_AGE = max(300, int(os.environ.get("TURNSTILE_COOKIE_MAX_AGE", str(60 * 60 * 12)).strip() or str(60 * 60 * 12)))
TURNSTILE_VERIFY_TIMEOUT = max(1.0, float(os.environ.get("TURNSTILE_VERIFY_TIMEOUT", "5").strip() or "5"))
AUDIO_DEBUG_ENABLED = (
    os.environ.get("BLAME_MACHINE_AUDIO_DEBUG", "").strip().lower() in {"1", "true", "yes", "on"}
)
BUILD_LABEL = os.environ.get("APP_COMMIT", "").strip()[:7].lower()
SOCIAL_DESCRIPTION = "Blame me for bad decisions."
APPLE_TOUCH_ICON_PATH = BUNDLE_DIR / "apple-touch-icon.png"
SOCIAL_CARD_PNG_PATH = BUNDLE_DIR / "social-card.png"

app = Flask(__name__)

GLOBAL_WILDCARDS = [
    "pick none of these",
    "reroll later",
    "ask a friend",
    "do something completely different",
    "sleep on it",
    "flip a coin and trust your reaction",
    "focus on yourself for now",
    "take a break and come back to it",
]

CATEGORY_DEFAULT_WILDCARDS = {
    "dessert": [
        "switch it up with a cupcake",
        "plot twist: brownie",
        "go rogue with cookies",
        "wildcard says cheesecake",
    ],
    "beverage": [
        "surprise yourself with something fizzy",
        "plot twist: water first",
        "switch it up with lemonade",
        "wildcard says iced coffee",
    ],
    "task": [
        "do the easiest win first",
        "start with the most annoying thing",
        "take a 20-minute reset, then decide",
        "clean something small first",
    ],
    "car": [
        "test drive both first",
        "keep the money today",
        "sleep on it",
        "check insurance before deciding",
    ],
}

BANNED_WORDS_PATH = BUNDLE_DIR / "banned_words.txt"
ALLOWED_WORDS_PATH = BUNDLE_DIR / "allowed_words.txt"
COIN_PRIMER_MP3_PATH = BUNDLE_DIR / "coin-primer.mp3"
COIN_PRIMER_MP3 = COIN_PRIMER_MP3_PATH.read_bytes() if COIN_PRIMER_MP3_PATH.exists() else b""
FAST_REROLL_SECONDS = 10
RESULT_RETURN_SECONDS = 60
WILDCARD_CHANCE = 0.2
RECENT_HISTORY_LIMIT = 3
CATEGORY_NAME_RE = re.compile(r"^[^\[\]\r\n]{1,80}$")

CHOICE_TEMPLATES = [
    "you should totally go with {choice}",
    "you'd be nuts to choose anything but {choice}",
    "{choice} is the move",
    "come on now, it's obviously {choice}",
    "trust the machine: {choice}",
    "{choice} and don't overthink it",
    "the cabinet has spoken: {choice}",
    "all signs point to {choice}",
    "save yourself the spiral and pick {choice}",
    "this has {choice} written all over it",
    "don't make it weird, just pick {choice}",
    "the joystick says {choice}",
    "c'mon, sweetheart, it's {choice}",
    "ay, quit stalling, go with {choice}",
    "use your head for once and pick {choice}",
    "listen, pal, {choice} is the answer",
    "you're really gonna argue with {choice}?",
    "forget the drama, take {choice}",
    "be serious, you're taking {choice}",
    "quit playin' and go with {choice}",
    "that's the move, kid: {choice}",
    "don't overcook it, pick {choice}",
    "oh brother, it's obviously {choice}",
    "what are we even doing here? it's {choice}",
    "yeah, no, you're picking {choice}",
    "please. the answer is {choice}",
]

CLEAR_ARCADE_DESCRIPTIONS = [
    "WIPE MY CATEGORIES BEFORE ANYONE SEES THIS.",
    "RESET MY ARCADE. DON'T ASK QUESTIONS.",
    "START OVER LIKE NONE OF THIS HAPPENED.",
]

BASE_STYLES = """
  <style>
    :root {
      --bg: #050505;
      --green: #39ff14;
      --pink: #ff2bd6;
      --cyan: #00eaff;
      --yellow: #ffe600;
      --panel: #0d0d0d;
      --red: #ff5c5c;
      --title-shell-width: 760px;
      --title-bar-min-height: 152px;
      --title-shell-max-height: min(1080px, calc(100vh - env(safe-area-inset-top, 0px) - 64px));
    }

    * { box-sizing: border-box; }

    [hidden] {
      display: none !important;
    }

    body {
      margin: 0;
      background:
        radial-gradient(circle at top, rgba(0,234,255,0.10), transparent 30%),
        radial-gradient(circle at bottom, rgba(255,43,214,0.08), transparent 30%),
        var(--bg);
      color: var(--green);
      font-family: "Courier New", "Lucida Console", monospace;
      padding: calc(env(safe-area-inset-top, 0px) + 16px) 18px 18px;
      min-height: 100vh;
      text-transform: uppercase;
      letter-spacing: 0.04em;
    }

    .wrap {
      position: relative;
      max-width: 920px;
      margin: 0 auto;
    }

    .title {
      text-align: center;
      color: var(--yellow);
      font-size: 20px;
      line-height: 1.6;
      text-shadow: 0 0 8px rgba(255,230,0,0.6);
      margin: 16px 0 28px;
    }

    .sub {
      text-align: center;
      color: var(--cyan);
      font-size: 11px;
      margin-bottom: 24px;
      line-height: 1.8;
    }

    .player-banner {
      text-align: center;
      color: rgba(0,234,255,0.88);
      font-size: 9px;
      line-height: 1.8;
      margin: 18px 0 4px;
      text-shadow: 0 0 6px rgba(0,234,255,0.16);
      word-break: break-word;
      opacity: 0.82;
    }

    .player-banner-subtle {
      color: rgba(0,234,255,0.72);
      font-size: 8px;
      margin-top: 0;
      margin-bottom: 0;
      text-shadow: none;
      opacity: 0.72;
    }

    .corner-button {
      position: fixed;
      top: calc(env(safe-area-inset-top, 0px) + 14px);
      width: auto;
      min-width: 0;
      min-height: 44px;
      padding: 10px 12px;
      font-size: 10px;
      line-height: 1.2;
      z-index: 10;
      background: transparent;
      border: none;
      box-shadow: none;
      text-transform: uppercase;
      letter-spacing: 0.04em;
      display: inline-flex;
      align-items: center;
      justify-content: center;
    }

    .sound-toggle {
      right: 14px;
      color: var(--pink);
      text-shadow: 0 0 8px rgba(255,43,214,0.35);
      font-size: 11px;
    }

    .exit-toggle {
      left: 14px;
      color: var(--cyan);
      text-shadow: 0 0 8px rgba(0,234,255,0.35);
      font-size: 11px;
    }

    .sound-toggle:hover,
    .sound-toggle:focus-visible,
    .exit-toggle:hover,
    .exit-toggle:focus-visible {
      background: transparent;
      color: var(--yellow);
      text-shadow: 0 0 8px rgba(255,230,0,0.45);
    }

    .card {
      background: var(--panel);
      border: 3px solid var(--green);
      box-shadow: 0 0 18px rgba(57,255,20,0.18);
      padding: 18px;
      margin: 18px 0;
    }

    .footer-commit {
      margin: 12px auto 0;
      min-height: 1.4em;
      text-align: center;
      font-size: 9px;
      line-height: 1.4;
      color: rgba(0, 234, 255, 0.52);
      opacity: 0.72;
      letter-spacing: 0.12em;
    }

    .row {
      margin-bottom: 18px;
    }

    label {
      display: block;
      font-size: 11px;
      margin-bottom: 10px;
      color: var(--cyan);
      line-height: 1.6;
      text-align: center;
    }

    .center-label {
      text-align: center;
    }

    .prompt-copy {
      display: block;
      text-align: center;
      font-size: 11px;
      color: var(--cyan);
      line-height: 1.6;
    }

    input, textarea, select {
      width: 100%;
      background: #000;
      color: var(--green);
      border: 3px solid var(--green);
      padding: 12px;
      font-family: "Courier New", "Lucida Console", monospace;
      font-size: 11px;
      line-height: 1.6;
      text-transform: uppercase;
      letter-spacing: 0.04em;
      outline: none;
    }

    textarea {
      resize: vertical;
      min-height: 110px;
    }

    input::placeholder, textarea::placeholder {
      color: #7aff7a;
      opacity: 0.6;
      text-transform: uppercase;
    }

    button, .button-link {
      width: 100%;
      display: inline-block;
      text-align: center;
      background: #000;
      color: var(--pink);
      border: 3px solid var(--pink);
      padding: 14px;
      font-family: "Courier New", "Lucida Console", monospace;
      font-size: 11px;
      line-height: 1.4;
      cursor: pointer;
      text-decoration: none;
      box-shadow: 0 0 14px rgba(255,43,214,0.18);
    }

    button:hover, .button-link:hover {
      background: var(--pink);
      color: #000;
    }

    .small {
      color: #9befff;
      font-size: 9px;
      line-height: 1.8;
    }

    .access-box {
      text-align: center;
    }

    .access-box summary {
      list-style: none;
      cursor: pointer;
      color: var(--cyan);
      font-size: 10px;
      line-height: 1.7;
      text-align: center;
      outline: none;
    }

    .access-box summary::-webkit-details-marker {
      display: none;
    }

    .access-box[open] summary {
      color: var(--yellow);
    }

    .access-box-body {
      margin-top: 14px;
    }

    .access-box code {
      display: inline-block;
      margin-top: 8px;
      padding: 8px 10px;
      border: 2px solid var(--cyan);
      background: rgba(0,234,255,0.08);
      color: var(--cyan);
      font-family: "Courier New", "Lucida Console", monospace;
      font-size: 9px;
      line-height: 1.6;
      word-break: break-all;
    }

    .choices {
      display: flex;
      flex-wrap: wrap;
      gap: 10px;
      margin-top: 12px;
      justify-content: center;
    }

    .pill {
      border: 2px solid var(--cyan);
      color: var(--cyan);
      padding: 10px 12px;
      font-size: 9px;
      line-height: 1.6;
      background: rgba(0,234,255,0.06);
    }

    .result-box {
      border: 3px solid var(--yellow);
      padding: 28px 18px;
      box-shadow: 0 0 18px rgba(255,230,0,0.20);
      background: rgba(255,230,0,0.05);
      text-align: center;
    }

    .result-label {
      color: var(--yellow);
      font-size: 10px;
      margin-bottom: 14px;
      line-height: 1.6;
    }

    .result-text {
      color: var(--yellow);
      font-size: 22px;
      line-height: 1.8;
      text-shadow: 0 0 8px rgba(255,230,0,0.5);
      word-break: break-word;
      margin-bottom: 14px;
    }

    .meta {
      color: #9befff;
      font-size: 9px;
      line-height: 1.8;
      margin-top: 12px;
    }

    .result-return {
      text-align: center;
    }

    .result-actions form:first-child button {
      background: rgba(255,230,0,0.12);
      color: var(--yellow);
      border-color: var(--yellow);
      box-shadow: 0 0 16px rgba(255,230,0,0.20);
      text-shadow: 0 0 8px rgba(255,230,0,0.24);
    }

    .result-actions form:first-child button:hover,
    .result-actions form:first-child button:focus-visible {
      background: var(--yellow);
      color: #000;
    }

    .error-card {
      border-color: var(--red);
      color: var(--red);
      text-align: center;
    }

    .wildcard {
      color: var(--red);
      animation: blink 1s steps(2, start) infinite;
      margin-top: 12px;
      font-size: 10px;
      line-height: 1.6;
    }

    .button-stack > * {
      margin-top: 12px;
    }

    .hero-card {
      width: min(var(--title-shell-width), 100%);
      min-height: var(--title-bar-min-height);
      max-height: var(--title-shell-max-height);
      margin: 0 auto;
      text-align: center;
      padding: 0;
      display: flex;
      flex-direction: column;
      overflow: hidden;
    }

    .title-bar {
      flex: 0 0 auto;
      min-height: var(--title-bar-min-height);
      padding: 42px 22px 14px;
      border-bottom: 2px solid rgba(0, 234, 255, 0.35);
      background:
        linear-gradient(180deg, rgba(0, 234, 255, 0.05), rgba(13, 13, 13, 0.96) 65%),
        rgba(13, 13, 13, 0.96);
    }

    .title-bar .title {
      margin: 0 0 10px;
    }

    .title-bar .sub {
      margin: 0;
    }

    .title-content {
      flex: 1 1 auto;
      min-height: 0;
      overflow-y: auto;
      padding: 18px 22px 24px;
      display: grid;
      align-content: start;
      gap: 18px;
      scrollbar-color: rgba(0, 234, 255, 0.65) rgba(0, 0, 0, 0.2);
    }

    .hero-copy {
      color: var(--cyan);
      font-size: 11px;
      line-height: 1.8;
      margin: 0;
    }

    .hero-actions {
      display: flex;
      flex-direction: column;
      gap: 12px;
      max-width: 320px;
      margin: 14px auto 0;
    }

    .quiet-link {
      display: inline-block;
      text-align: center;
      color: var(--cyan);
      font-size: 10px;
      line-height: 1.8;
      text-decoration: none;
      padding: 8px 10px;
    }

    .hero-play-button {
      color: var(--yellow);
      border-color: var(--cyan);
      background: rgba(0, 234, 255, 0.08);
      text-shadow: 0 0 8px rgba(255,230,0,0.35);
      box-shadow:
        0 0 14px rgba(0,234,255,0.22),
        0 0 20px rgba(255,43,214,0.12);
      animation: playPulse 4.2s steps(1, end) infinite;
    }

    .hero-play-button:hover,
    .hero-play-button:focus-visible {
      background: var(--yellow);
      border-color: var(--yellow);
      color: #000;
      box-shadow: 0 0 16px rgba(255,230,0,0.32);
      animation-play-state: paused;
    }

    .hero-play-button.is-loading {
      cursor: progress;
      animation: none;
      background: rgba(255, 230, 0, 0.14);
      border-color: var(--yellow);
      color: var(--yellow);
      box-shadow: 0 0 16px rgba(255,230,0,0.22);
    }

    .topic-tuner {
      display: grid;
      grid-template-columns: 74px 1fr 74px;
      gap: 10px;
      align-items: center;
    }

    .topic-tuner-button {
      width: 100%;
      min-width: 0;
      min-height: 58px;
      padding: 14px 0;
      color: var(--cyan);
      border-color: var(--cyan);
      box-shadow: 0 0 14px rgba(0,234,255,0.18);
      font-size: 16px;
    }

    .topic-tuner-button:hover {
      background: var(--cyan);
      color: #000;
    }

    .topic-tuner-button:disabled {
      border-color: rgba(0,234,255,0.14);
      color: rgba(0,234,255,0.18);
      box-shadow: none;
      cursor: default;
      background: #000;
      pointer-events: none;
      text-shadow: none;
      opacity: 0.65;
    }

    .topic-tuner-button:disabled:hover {
      background: #000;
      color: rgba(0,234,255,0.18);
    }

    .topic-tuner-display {
      min-height: 58px;
      display: flex;
      align-items: center;
      justify-content: center;
      text-align: center;
      padding: 10px 12px;
      border: 3px solid var(--green);
      background: #000;
      color: var(--green);
      font-size: 11px;
      line-height: 1.5;
      text-transform: uppercase;
      letter-spacing: 0.04em;
      word-break: break-word;
      box-shadow: inset 0 0 12px rgba(57,255,20,0.08);
    }

    .topic-tuner-meta {
      margin-top: 10px;
      text-align: center;
      color: #9befff;
      font-size: 9px;
      line-height: 1.7;
      letter-spacing: 0.08em;
    }

    .quiet-link:hover {
      color: var(--yellow);
      text-shadow: 0 0 6px rgba(255,230,0,0.4);
    }

    .input-helper {
      margin-top: 10px;
      text-align: center;
      color: var(--cyan);
      font-size: 10px;
      line-height: 1.8;
      opacity: 0.92;
    }

    .status {
      margin-top: 18px;
      color: var(--yellow);
      font-size: 11px;
      line-height: 1.7;
      text-align: left;
      white-space: pre-wrap;
    }

    .back-link {
      margin-top: 20px;
      text-align: center;
    }

    .turnstile-overlay {
      position: fixed;
      inset: 0;
      padding: 20px;
      background: rgba(5, 5, 5, 0.92);
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 40;
    }

    .turnstile-card {
      width: min(100%, 460px);
      text-align: center;
    }

    .turnstile-copy {
      margin-bottom: 16px;
      color: var(--yellow);
      line-height: 1.7;
      font-size: 11px;
    }

    .turnstile-slot {
      display: flex;
      justify-content: center;
      margin: 14px 0 10px;
      min-height: 70px;
    }

    .turnstile-help {
      color: var(--cyan);
      font-size: 10px;
      line-height: 1.7;
      margin-top: 10px;
    }

    @keyframes blink {
      50% { opacity: 0.25; }
    }

    @keyframes playPulse {
      0%, 49% {
        color: var(--yellow);
        border-color: var(--cyan);
        background: rgba(0, 234, 255, 0.08);
        text-shadow: 0 0 8px rgba(255,230,0,0.35);
        box-shadow:
          0 0 14px rgba(0,234,255,0.22),
          0 0 20px rgba(255,43,214,0.12);
      }
      50%, 100% {
        color: var(--cyan);
        border-color: var(--pink);
        background: rgba(255, 43, 214, 0.16);
        text-shadow: 0 0 8px rgba(0,234,255,0.35);
        box-shadow:
          0 0 14px rgba(255,43,214,0.22),
          0 0 20px rgba(0,234,255,0.12);
      }
    }

    @media (prefers-reduced-motion: reduce) {
      .hero-play-button,
      .wildcard {
        animation: none;
      }
    }

    @media (max-width: 640px) {
      .title { font-size: 16px; }
      .result-text { font-size: 16px; }
      button, .button-link { font-size: 11px; }
      label, .prompt-copy { font-size: 13px; line-height: 1.7; }
      .small, .meta, .pill { font-size: 10px; }
      input, textarea, select {
        font-size: 16px;
        line-height: 1.5;
      }
      body {
        padding: calc(env(safe-area-inset-top, 0px) + 8px) 14px 14px;
      }
      .card {
        padding: 14px;
        margin: 14px 0;
      }
      .topic-tuner {
        grid-template-columns: 68px 1fr 68px;
        gap: 10px;
      }
      .topic-tuner-display {
        min-height: 56px;
        font-size: 12px;
      }
      .topic-tuner-button {
        min-height: 56px;
        font-size: 18px;
      }
      .result-box {
        padding: 22px 14px;
      }
      .hero-card {
        max-height: calc(100vh - env(safe-area-inset-top, 0px) - 28px);
      }
      .title-bar {
        padding: 42px 16px 12px;
      }
      .title-content {
        padding: 16px;
      }
      .sound-toggle {
        right: 10px;
        font-size: 10px;
      }
      .exit-toggle {
        left: 10px;
        font-size: 10px;
      }
    }
  </style>
"""

MUSIC_UI = """
  <button type="button" id="exit-game" class="corner-button exit-toggle">EXIT</button>
  <button type="button" id="sound-toggle" class="corner-button sound-toggle">MUTE</button>
"""

MUSIC_SCRIPT = """
  <script>
    (() => {
      const storageKey = "blameMachineSoundEnabled";
      const enteredKey = "blameMachineEntered";
      const hubOriginKey = "blameMachineHubOrigin";
      const hubReturnKey = "blameMachineHubReturnUrl";
      const toggle = document.getElementById("sound-toggle");
      const exitButton = document.getElementById("exit-game");
      if (!toggle || !exitButton) return;

      let ctx = null;
      let master = null;
      let timer = null;
      let step = 0;
      let started = false;
      let htmlDecks = [];
      let htmlDeckIndex = 0;
      let htmlCrossfadeTimer = null;
      let soundEnabled = window.localStorage.getItem(storageKey) !== "0";
      let enteredGame = window.location.pathname === "/game" && window.sessionStorage.getItem(enteredKey) === "1";
      let scene = "home";
      let primed = false;
      let primePromise = null;
      let primerAudio = null;
      let recoveryArmed = false;
      let recovering = false;
      let bootingAudio = false;
      let bootstrapped = false;
      let bootstrapSilence = false;
      const primerGain = 0.51;
      const htmlLoopGain = 0.55;
      const htmlCrossfadeMs = 20;
      const defaultHubUrl = "https://games.irq9.net/";
      const initialUrl = new URL(window.location.href);
      let enteredFromHub = initialUrl.searchParams.get("arcade_entry") === "hub";
      let returnUrl = window.sessionStorage.getItem(hubReturnKey) || defaultHubUrl;
      try {
        const requestedReturn = initialUrl.searchParams.get("return_url");
        if (requestedReturn) {
          const parsedReturn = new URL(requestedReturn, window.location.href);
          if (parsedReturn.hostname === "games.irq9.net") returnUrl = parsedReturn.toString();
        }
      } catch (_error) {}
      try {
        enteredFromHub = enteredFromHub || (document.referrer ? new URL(document.referrer).hostname === "games.irq9.net" : false);
      } catch (_error) {}
      enteredFromHub = enteredFromHub || window.sessionStorage.getItem(hubOriginKey) === "1";
      if (enteredFromHub) {
        window.sessionStorage.setItem(hubOriginKey, "1");
        window.sessionStorage.setItem(hubReturnKey, returnUrl);
      }
      window.blameMachineEnteredFromHub = enteredFromHub;
      window.blameMachineHubReturnUrl = returnUrl;

      const cleanUrl = new URL(window.location.href);
      if (cleanUrl.searchParams.has("arcade_entry") || cleanUrl.searchParams.has("arcade_shell")) {
        cleanUrl.searchParams.delete("arcade_entry");
        cleanUrl.searchParams.delete("arcade_shell");
        cleanUrl.searchParams.delete("return_url");
        window.history.replaceState({}, "", `${cleanUrl.pathname}${cleanUrl.search}${cleanUrl.hash}`);
      }

      const stepMs = 360;
      const sceneGains = {
        home: 0.35,
        category: 0.30,
        thinking: 0.26,
        result: 0.32,
      };
      const homePattern = [
        { bass: 220.0, lead: 659.25 },
        { bass: 220.0, lead: 783.99 },
        { bass: 261.63, lead: 880.0 },
        { bass: 220.0, lead: 783.99 },
        { bass: 293.66, lead: 698.46 },
        { bass: 261.63, lead: 783.99 },
        { bass: 220.0, lead: 1046.5 },
        { bass: 196.0, lead: 783.99 }
      ];
      const categoryPattern = [
        { bass: 196.0, lead: 392.0 },
        { bass: 196.0, lead: 440.0 },
        { bass: 220.0, lead: 392.0 },
        { bass: 196.0, lead: 523.25 },
        { bass: 196.0, lead: 440.0 },
        { bass: 174.61, lead: 392.0 },
        { bass: 196.0, lead: 349.23 },
        { bass: 220.0, lead: 392.0 }
      ];
      const thinkingPattern = [
        { bass: 196.0, lead: 523.25, pulse: 659.25 },
        { bass: 196.0, lead: 587.33, pulse: 689.25 },
        { bass: 220.0, lead: 523.25, pulse: 719.25 },
        { bass: 196.0, lead: 659.25, pulse: 749.25 },
        { bass: 196.0, lead: 587.33, pulse: 659.25 },
        { bass: 174.61, lead: 523.25, pulse: 689.25 },
        { bass: 196.0, lead: 493.88, pulse: 719.25 },
        { bass: 220.0, lead: 523.25, pulse: 749.25 }
      ];
      const resultPattern = [
        { bass: 246.94, lead: 523.25, sparkle: 1046.5 },
        { bass: 246.94, lead: 659.25, sparkle: 1174.66 },
        { bass: 293.66, lead: 783.99, sparkle: 1318.51 },
        { bass: 246.94, lead: 659.25, sparkle: 1174.66 },
        { bass: 329.63, lead: 880.0, sparkle: 1396.91 },
        { bass: 293.66, lead: 783.99, sparkle: 1318.51 },
        { bass: 246.94, lead: 987.77, sparkle: 1567.98 },
        { bass: 220.0, lead: 783.99, sparkle: 1318.51 }
      ];

      function traceAudio(message) {
        fetch('/audio-debug/log', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          credentials: 'same-origin',
          keepalive: true,
          body: JSON.stringify({
            message: `MAIN ${message}`,
            ctxState: ctx ? ctx.state : 'none',
            ctxTime: ctx ? Number(ctx.currentTime.toFixed(3)) : null,
            htmlPaused: primerAudio ? primerAudio.paused : null,
            htmlReadyState: primerAudio ? primerAudio.readyState : null,
            htmlCurrentTime: primerAudio ? Number(primerAudio.currentTime.toFixed(3)) : null,
            ua: navigator.userAgent
          })
        }).catch(() => {});
      }

      function currentSceneGain() {
        return sceneGains[scene] ?? sceneGains.home;
      }

      function ensureContext() {
        if (ctx) return ctx;
        const AudioCtx = window.AudioContext || window.webkitAudioContext;
        ctx = new AudioCtx({ latencyHint: "interactive", sampleRate: 44100 });
        master = ctx.createGain();
        master.gain.value = soundEnabled ? currentSceneGain() : 0.0;
        master.connect(ctx.destination);
        ctx.onstatechange = () => {
          traceAudio(`statechange ${ctx.state}`);
        };
        traceAudio(`create state=${ctx.state} sr=${ctx.sampleRate}`);
        return ctx;
      }

      function kickstartContext() {
        if (!ctx) return;
        try {
          const buffer = ctx.createBuffer(1, 22050, 22050);
          const source = ctx.createBufferSource();
          source.buffer = buffer;
          source.connect(ctx.destination);
          source.start(0);
          traceAudio("kickstart");
        } catch (error) {
          traceAudio(`kickstart failed ${(error && error.message) || error || 'unknown'}`);
        }
      }

      function ensurePrimerAudio() {
        if (primerAudio) return primerAudio;
        primerAudio = new Audio("/audio/coin-primer.mp3?ts=" + Date.now());
        primerAudio.preload = "auto";
        primerAudio.playsInline = true;
        primerAudio.volume = primerGain;
        primerAudio.addEventListener("play", () => traceAudio("primer play"));
        primerAudio.addEventListener("ended", () => {
          traceAudio("primer ended");
          if (enteredGame && soundEnabled && ctx && ctx.state === "running") {
            kickstartContext();
            applyGain();
            traceAudio("primer ended handoff ok");
          }
        });
        primerAudio.addEventListener("error", () => {
          traceAudio(`primer error ${primerAudio?.error?.message || primerAudio?.error?.code || 'unknown'}`);
        });
        return primerAudio;
      }

      async function playPrimer(options = {}) {
        const waitForEnd = options.waitForEnd !== false;
        const holdMs = Math.max(0, Number(options.holdMs || 0));
        const volume = typeof options.volume === "number" ? options.volume : primerGain;
        const audio = ensurePrimerAudio();
        audio.muted = false;
        audio.volume = volume;
        try {
          audio.pause();
          audio.currentTime = 0;
          await audio.play();
          traceAudio("primer play request ok");
          if (waitForEnd) {
            await new Promise((resolve) => {
              let settled = false;
              const finish = (label) => {
                if (settled) return;
                settled = true;
                audio.removeEventListener("ended", onEnded);
                audio.removeEventListener("error", onError);
                window.clearTimeout(fallbackTimer);
                traceAudio(`primer wait ${label}`);
                resolve();
              };
              const onEnded = () => finish("ended");
              const onError = () => finish("error");
              const naturalFallbackMs = Math.max(1200, Math.round(((audio.duration || 0.9) * 1000) + 250));
              const waitMs = holdMs > 0 ? Math.max(naturalFallbackMs, holdMs) : naturalFallbackMs;
              const fallbackTimer = window.setTimeout(() => finish("timeout"), waitMs);
              audio.addEventListener("ended", onEnded, { once: true });
              audio.addEventListener("error", onError, { once: true });
            });
          }
        } catch (error) {
          traceAudio(`primer play failed ${(error && error.message) || error || 'unknown'}`);
        }
      }

      function applyGain() {
        if (master) {
          master.gain.value = soundEnabled ? currentSceneGain() : 0.0;
        }
        htmlDecks.forEach((audio, index) => {
          audio.volume = index === htmlDeckIndex ? htmlTargetGain() : 0.0;
        });
      }

      function setLabel() {
        exitButton.hidden = !!document.querySelector(".hero-card") && !enteredFromHub;
        toggle.hidden = false;
        toggle.textContent = soundEnabled ? "MUTE" : "UNMUTE";
      }

      function pulse(kind, freq, duration, gainValue, detune = 0) {
        if (!ctx || !master) return;
        const osc = ctx.createOscillator();
        const gain = ctx.createGain();
        osc.type = kind;
        osc.frequency.setValueAtTime(freq, ctx.currentTime);
        osc.detune.setValueAtTime(detune, ctx.currentTime);
        gain.gain.setValueAtTime(0.0001, ctx.currentTime);
        gain.gain.exponentialRampToValueAtTime(gainValue, ctx.currentTime + 0.01);
        gain.gain.exponentialRampToValueAtTime(0.0001, ctx.currentTime + duration);
        osc.connect(gain);
        gain.connect(master);
        osc.start();
        osc.stop(ctx.currentTime + duration + 0.02);
      }

      function currentPattern() {
        if (scene === "thinking") return thinkingPattern;
        if (scene === "result") return resultPattern;
        if (scene === "category") return categoryPattern;
        return homePattern;
      }

      function currentLoopSrc(label) {
        const loopPath = scene === "category" || scene === "thinking" || scene === "result"
          ? "/audio/loop-category.wav"
          : "/audio/loop-home.wav";
        return `${loopPath}?deck=${label}&ts=${Date.now()}`;
      }

      function makeHtmlDeck(label) {
        const audio = new Audio(currentLoopSrc(label));
        audio.preload = "auto";
        audio.loop = false;
        audio.playsInline = true;
        audio.volume = 0.0;
        audio.addEventListener("play", () => traceAudio(`html deck ${label} play`));
        audio.addEventListener("pause", () => traceAudio(`html deck ${label} pause`));
        audio.addEventListener("ended", () => traceAudio(`html deck ${label} ended`));
        audio.addEventListener("error", () => {
          traceAudio(`html deck ${label} error ${audio?.error?.message || audio?.error?.code || 'unknown'}`);
        });
        return audio;
      }

      function ensureHtmlDecks() {
        if (htmlDecks.length === 2) return htmlDecks;
        htmlDecks = [makeHtmlDeck("A"), makeHtmlDeck("B")];
        return htmlDecks;
      }

      function waitForHtmlDeckReady(audio) {
        if (Number.isFinite(audio.duration) && audio.duration > 0 && audio.readyState >= 1) {
          return Promise.resolve();
        }
        return new Promise((resolve) => {
          let settled = false;
          const finish = () => {
            if (settled) return;
            settled = true;
            audio.removeEventListener("loadedmetadata", finish);
            audio.removeEventListener("canplaythrough", finish);
            audio.removeEventListener("error", finish);
            window.clearTimeout(fallbackTimer);
            resolve();
          };
          const fallbackTimer = window.setTimeout(finish, 1500);
          audio.addEventListener("loadedmetadata", finish, { once: true });
          audio.addEventListener("canplaythrough", finish, { once: true });
          audio.addEventListener("error", finish, { once: true });
          try {
            audio.load();
          } catch (_) {
            finish();
          }
        });
      }

      function fadeHtmlAudio(audio, from, to, durationMs) {
        const startedAt = performance.now();
        audio.volume = Math.max(0, Math.min(1, from));
        function tick(now) {
          const progress = Math.min(1, (now - startedAt) / Math.max(1, durationMs));
          const eased = progress < 0.5
            ? 2 * progress * progress
            : 1 - Math.pow(-2 * progress + 2, 2) / 2;
          audio.volume = Math.max(0, Math.min(1, from + ((to - from) * eased)));
          if (progress < 1) window.requestAnimationFrame(tick);
        }
        window.requestAnimationFrame(tick);
      }

      function stopHtmlLoop() {
        if (htmlCrossfadeTimer) {
          window.clearTimeout(htmlCrossfadeTimer);
          htmlCrossfadeTimer = null;
        }
        htmlDecks.forEach((audio) => {
          try {
            const currentVolume = Number.isFinite(audio.volume) ? audio.volume : 0.0;
            if (!audio.paused && currentVolume > 0.0) {
              fadeHtmlAudio(audio, currentVolume, 0.0, 45);
              window.setTimeout(() => {
                try {
                  audio.pause();
                  audio.currentTime = 0;
                  audio.volume = 0.0;
                } catch (_) {}
              }, 55);
            } else {
              audio.pause();
              audio.currentTime = 0;
              audio.volume = 0.0;
            }
          } catch (_) {}
        });
      }

      function resetHtmlDecks() {
        stopHtmlLoop();
        htmlDecks = [];
        htmlDeckIndex = 0;
      }

      function htmlTargetGain() {
        return soundEnabled && started && !bootstrapSilence ? htmlLoopGain : 0.0;
      }

      function scheduleHtmlCrossfade() {
        if (!started || !soundEnabled) return;
        if (htmlCrossfadeTimer) window.clearTimeout(htmlCrossfadeTimer);
        const decks = ensureHtmlDecks();
        const current = decks[htmlDeckIndex];
        const durationMs = Number.isFinite(current.duration) && current.duration > 0
          ? current.duration * 1000
          : 2880;
        const nextDelay = Math.max(120, durationMs - htmlCrossfadeMs);
        htmlCrossfadeTimer = window.setTimeout(async () => {
          if (!started || !soundEnabled) return;
          const nextIndex = (htmlDeckIndex + 1) % 2;
          const next = decks[nextIndex];
          try {
            next.pause();
            next.currentTime = 0;
            next.volume = 0.0;
            await next.play();
            fadeHtmlAudio(next, 0.0, htmlTargetGain(), htmlCrossfadeMs);
            fadeHtmlAudio(current, current.volume, 0.0, htmlCrossfadeMs);
            window.setTimeout(() => {
              if (started) {
                try {
                  current.pause();
                  current.currentTime = 0;
                } catch (_) {}
              }
            }, htmlCrossfadeMs + 40);
            htmlDeckIndex = nextIndex;
            traceAudio(`html xfade deck ${nextIndex === 0 ? "A" : "B"}`);
            scheduleHtmlCrossfade();
          } catch (error) {
            traceAudio(`html xfade failed ${(error && error.message) || error || 'unknown'}`);
          }
        }, nextDelay);
      }

      async function startHtmlLoop() {
        const decks = ensureHtmlDecks();
        await Promise.all(decks.map((audio) => waitForHtmlDeckReady(audio)));
        const first = decks[htmlDeckIndex];
        try {
          first.pause();
          first.currentTime = 0;
          first.volume = htmlTargetGain();
          await first.play();
          traceAudio("html loop running");
          scheduleHtmlCrossfade();
        } catch (error) {
          traceAudio(`html loop failed ${(error && error.message) || error || 'unknown'}`);
        }
      }

      function playStep() {
        if (!ctx || ctx.state !== "running" || !soundEnabled) {
          traceAudio(`step skipped state=${ctx ? ctx.state : 'none'} enabled=${soundEnabled}`);
          return;
        }
        const pattern = currentPattern();
        const item = pattern[step % pattern.length];
        if (scene === "thinking") {
          pulse("triangle", item.bass, 0.20, 0.10);
          pulse("triangle", item.bass * 2, 0.14, 0.07);
          pulse("triangle", item.lead, 0.18, 0.06);
          pulse("square", item.pulse || 659.25, 0.07, 0.045);
        } else if (scene === "result") {
          pulse("triangle", item.bass, 0.20, 0.10);
          pulse("sawtooth", item.bass * 2, 0.10, 0.07);
          pulse("triangle", item.lead, 0.18, 0.09);
          pulse("square", item.sparkle || 1046.5, 0.05, 0.05);
        } else if (scene === "category") {
          pulse("triangle", item.bass, 0.20, 0.11);
          pulse("triangle", item.bass * 2, 0.14, 0.08);
          pulse("triangle", item.lead, 0.16, 0.07);
        } else {
          pulse("triangle", item.bass, 0.22, 0.12);
          pulse("square", item.bass * 2, 0.08, 0.08);
          pulse("sawtooth", item.lead, 0.15, 0.10, step % 2 === 0 ? 3 : -3);
          if (step % 4 === 3) {
            pulse("square", 1320, 0.04, 0.05);
          }
        }
        step = (step + 1) % pattern.length;
      }

      function startTimerOnly() {
        if (timer) {
          window.clearInterval(timer);
        }
        timer = window.setInterval(playStep, stepMs);
      }

      async function startLoop() {
        ensureContext();
        if (bootstrapSilence && master) {
          master.gain.value = 0.0;
        } else {
          applyGain();
        }
        traceAudio(`startLoop pre state=${ctx.state} started=${started} enabled=${soundEnabled}`);
        if (ctx.state !== "running") {
          try {
            await ctx.resume();
            traceAudio(`resume ok state=${ctx.state}`);
          } catch (error) {
            traceAudio(`resume failed ${(error && error.message) || error || 'unknown'}`);
          }
        }
        kickstartContext();
        if (started) {
          traceAudio("startLoop ignored already-running");
          return;
        }
        started = true;
        await startHtmlLoop();
        traceAudio("loop running");
      }

      async function runBootstrapWarmup() {
        traceAudio("bootstrap begin");
        bootstrapSilence = true;
        try {
          ensureContext();
          if (ctx.state !== "running") {
            try {
              await ctx.resume();
              traceAudio(`bootstrap resume ok state=${ctx.state}`);
            } catch (error) {
              traceAudio(`bootstrap resume failed ${(error && error.message) || error || 'unknown'}`);
            }
          }
          kickstartContext();
          await playPrimer({ waitForEnd: false, holdMs: 180, volume: 0.0 });
          await rebuildAudioContext("bootstrap");
          await startLoop();
          await new Promise((resolve) => window.setTimeout(resolve, 220));
          await stopMusic();
          if (primerAudio) {
            try {
              primerAudio.pause();
              primerAudio.currentTime = 0;
            } catch (_) {}
          }
          traceAudio("bootstrap done");
        } finally {
          bootstrapSilence = false;
          applyGain();
        }
      }

      async function stopMusic() {
        if (timer) {
          window.clearInterval(timer);
          timer = null;
        }
        stopHtmlLoop();
        started = false;
        traceAudio("stop");
        setLabel();
      }

      function setScene(nextScene) {
        const previousScene = scene;
        scene = nextScene || "home";
        traceAudio(`scene ${scene}`);
        applyGain();
        if (started && scene !== previousScene) {
          resetHtmlDecks();
          startHtmlLoop();
        }
      }

      async function rebuildAudioContext(reason) {
        traceAudio(`rebuild begin ${reason}`);
        const shouldResumeLoop = started;
        if (timer) {
          window.clearInterval(timer);
          timer = null;
        }
        if (ctx) {
          try {
            await ctx.close();
            traceAudio("rebuild close ok");
          } catch (error) {
            traceAudio(`rebuild close failed ${(error && error.message) || error || 'unknown'}`);
          }
        }
        ctx = null;
        master = null;
        ensureContext();
        if (ctx.state !== "running") {
          try {
            await ctx.resume();
            traceAudio(`rebuild resume ok state=${ctx.state}`);
          } catch (error) {
            traceAudio(`rebuild resume failed ${(error && error.message) || error || 'unknown'}`);
          }
        }
        kickstartContext();
        applyGain();
        if (shouldResumeLoop && soundEnabled && ctx.state === "running") {
          started = true;
          playStep();
          startTimerOnly();
          traceAudio("rebuild loop restored");
        }
      }

      function armRecovery(reason) {
        if (recoveryArmed) return;
        recoveryArmed = true;
        traceAudio(`recovery armed ${reason}`);
      }

      async function recoverAudio(reason) {
        if (!ctx || recovering) return;
        recovering = true;
        traceAudio(`recover begin ${reason} state=${ctx.state}`);
        try {
          if (ctx.state !== "running") {
            try {
              await ctx.resume();
              traceAudio(`recover resume ok state=${ctx.state}`);
            } catch (error) {
              traceAudio(`recover resume failed ${(error && error.message) || error || 'unknown'}`);
            }
          } else {
            try {
              await ctx.suspend();
              traceAudio(`recover suspend ok state=${ctx.state}`);
            } catch (error) {
              traceAudio(`recover suspend failed ${(error && error.message) || error || 'unknown'}`);
            }
            try {
              await ctx.resume();
              traceAudio(`recover re-resume ok state=${ctx.state}`);
            } catch (error) {
              traceAudio(`recover re-resume failed ${(error && error.message) || error || 'unknown'}`);
            }
          }
          kickstartContext();
          applyGain();
          if (started && soundEnabled && ctx.state === "running" && !timer) {
            playStep();
            startTimerOnly();
            traceAudio("recover loop timer restored");
          }
          if (ctx.state !== "running") {
            armRecovery(`${reason} state=${ctx.state}`);
          }
        } finally {
          recovering = false;
        }
      }

      async function updateSoundState(nextEnabled) {
        soundEnabled = nextEnabled;
        window.localStorage.setItem(storageKey, soundEnabled ? "1" : "0");
        applyGain();
        if (!soundEnabled) {
          traceAudio("sound disabled");
          setLabel();
          return;
        }
        traceAudio("sound enabled");
        if (enteredGame) {
          await startLoop();
        }
        setLabel();
      }

      function playWonkCue() {
        if (!soundEnabled || !ctx || ctx.state !== "running") return;
        pulse("sawtooth", 330.0, 0.18, 0.10);
        window.setTimeout(() => {
          if (!ctx || !soundEnabled) return;
          pulse("sawtooth", 246.94, 0.22, 0.09);
        }, 150);
      }

      function playCoinCue() {
        if (!soundEnabled || !ctx || ctx.state !== "running") return;
        pulse("square", 1760.0, 0.06, 0.08);
        window.setTimeout(() => {
          if (!ctx || !soundEnabled) return;
          pulse("square", 1318.51, 0.10, 0.09);
        }, 50);
      }

      function playRevealCue() {
        if (!soundEnabled || !ctx || ctx.state !== "running") return;
        pulse("triangle", 523.25, 0.16, 0.11);
        window.setTimeout(() => {
          if (!ctx || !soundEnabled) return;
          pulse("triangle", 659.25, 0.18, 0.12);
        }, 100);
        window.setTimeout(() => {
          if (!ctx || !soundEnabled) return;
          pulse("triangle", 783.99, 0.24, 0.14);
        }, 220);
      }

      window.blameMachineAudio = {
        setScene,
        playRevealCue,
        playWonkCue,
        playCoinCue,
        setEntered(nextEntered) {
          enteredGame = !!nextEntered;
          traceAudio(`setEntered ${enteredGame}`);
          if (enteredGame) {
            window.sessionStorage.setItem(enteredKey, "1");
          } else {
            stopHtmlLoop();
            started = false;
            window.sessionStorage.removeItem(enteredKey);
            primed = false;
            primePromise = null;
            if (primerAudio) {
              try {
                primerAudio.pause();
                primerAudio.currentTime = 0;
              } catch (_) {}
            }
          }
          setLabel();
        },
        refreshControls() {
          setLabel();
        },
        async setEnabled(nextEnabled) {
          traceAudio(`setEnabled ${!!nextEnabled}`);
          await updateSoundState(!!nextEnabled);
        },
        async primeStart(options = {}) {
          const waitForPrimerEnd = options.waitForPrimerEnd !== false;
          const primerHoldMs = Math.max(0, Number(options.primerHoldMs || 0));
          soundEnabled = true;
          window.localStorage.setItem(storageKey, "1");
          if (primePromise) return primePromise;
          primed = true;
          primePromise = (async () => {
            traceAudio(`primeStart begin waitForPrimerEnd=${waitForPrimerEnd} primerHoldMs=${primerHoldMs}`);
            ensureContext();
            if (ctx.state !== "running") {
              try {
                await ctx.resume();
                traceAudio(`primeStart resume ok state=${ctx.state}`);
              } catch (error) {
                traceAudio(`primeStart resume failed ${(error && error.message) || error || 'unknown'}`);
              }
            }
            kickstartContext();
            await playPrimer({ waitForEnd: waitForPrimerEnd, holdMs: primerHoldMs });
            // Start the synth loop immediately after coin drop, while the context is still live.
            await startLoop();
            traceAudio("primeStart done");
          })();
          return primePromise;
        },
        async pause() {
          await stopMusic();
        },
        async enableAndStart(options = {}) {
          const skipPrimer = !!options.skipPrimer;
          const suppressPrimer = !!options.suppressPrimer;
          const waitForPrimerEnd = options.waitForPrimerEnd !== false;
          const primerHoldMs = Math.max(0, Number(options.primerHoldMs || 0));
          traceAudio(`enableAndStart begin primed=${primed} skipPrimer=${skipPrimer} suppressPrimer=${suppressPrimer} waitForPrimerEnd=${waitForPrimerEnd} primerHoldMs=${primerHoldMs}`);
          bootingAudio = true;
          try {
            soundEnabled = true;
            window.localStorage.setItem(storageKey, "1");
            ensureContext();
            if (ctx.state !== "running") {
              try {
                await ctx.resume();
                traceAudio(`enableAndStart early resume ok state=${ctx.state}`);
              } catch (error) {
                traceAudio(`enableAndStart early resume failed ${(error && error.message) || error || 'unknown'}`);
              }
            }
            kickstartContext();
            if (skipPrimer || suppressPrimer) {
              // Context is already armed above; hub-origin launches already played the cabinet coin.
            } else {
              await playPrimer({ waitForEnd: waitForPrimerEnd, holdMs: primerHoldMs });
            }
            bootstrapped = true;
            kickstartContext();
            await startLoop();
            traceAudio(`enableAndStart done started=${started}`);
          } finally {
            bootingAudio = false;
          }
        },
      };

      toggle.addEventListener("pointerdown", (event) => {
        event.stopPropagation();
      });

      toggle.addEventListener("keydown", (event) => {
        if (event.key === "Enter" || event.key === " ") {
          event.stopPropagation();
        }
      });

      toggle.addEventListener("click", async (event) => {
        event.preventDefault();
        if (!soundEnabled) {
          await updateSoundState(true);
          return;
        }
        await updateSoundState(false);
      });

      window.addEventListener("blame-machine-state", (event) => {
        const detail = event.detail || {};
        setScene(detail.scene || "home");
      });
      document.addEventListener("pointerdown", async (event) => {
        if (bootingAudio) return;
        if (!enteredGame || !soundEnabled || started) return;
        if (event.target?.closest?.("#sound-toggle, #exit-game")) return;
        traceAudio("pointerdown restart hook");
        try {
          await startLoop();
        } catch (_error) {
          // Keep the interaction flowing; subsequent taps can retry.
        }
      }, { passive: true, capture: true });
      window.addEventListener("touchend", async () => {
        if (!recoveryArmed) return;
        recoveryArmed = false;
        await rebuildAudioContext("touchend");
      }, { passive: true, capture: true });
      document.addEventListener("visibilitychange", async () => {
        traceAudio(`visibility ${document.visibilityState}`);
        if (document.visibilityState === "visible" && ctx) {
          await recoverAudio("visibility");
        }
      });
      setLabel();
    })();
  </script>
"""

APP_SHELL_SCRIPT = """
  <script>
    (() => {
      const parser = new DOMParser();

      async function loadIntoShell(url, options = {}, push = true) {
        const response = await window.fetch(url, {
          credentials: "same-origin",
          redirect: "follow",
          ...options
        });

        const html = await response.text();
        const doc = parser.parseFromString(html, "text/html");
        const nextWrap = doc.querySelector(".wrap");
        const currentWrap = document.querySelector(".wrap");

        if (!nextWrap || !currentWrap) {
          window.location.assign(url);
          return;
        }

        currentWrap.replaceWith(nextWrap);

        if (doc.title) {
          document.title = doc.title;
        }

        if (push) {
          const nextUrl = new URL(response.url || url, window.location.origin);
          window.history.pushState({}, "", nextUrl.pathname + nextUrl.search);
        }

        window.scrollTo({ top: 0, behavior: "auto" });
      }

      document.addEventListener("click", async (event) => {
        const link = event.target.closest('a.button-link[href]');
        if (!link) return;

        const href = link.getAttribute("href");
        if (!href || !href.startsWith("/")) return;

        event.preventDefault();
        try {
          await loadIntoShell(link.href, { method: "GET" });
        } catch (_error) {
          window.location.assign(link.href);
        }
      });

      document.addEventListener("submit", async (event) => {
        const form = event.target;
        if (!(form instanceof HTMLFormElement)) return;

        const action = form.getAttribute("action") || window.location.pathname;
        if (!action.startsWith("/")) return;

        event.preventDefault();
        const method = (form.getAttribute("method") || "GET").toUpperCase();

        try {
          if (method === "GET") {
            const formUrl = new URL(action, window.location.origin);
            const params = new URLSearchParams(new FormData(form));
            formUrl.search = params.toString();
            await loadIntoShell(formUrl.toString(), { method: "GET" });
            return;
          }

          await loadIntoShell(new URL(action, window.location.origin).toString(), {
            method,
            body: new FormData(form)
          });
        } catch (_error) {
          form.submit();
        }
      });

      window.addEventListener("popstate", async () => {
        try {
          await loadIntoShell(window.location.href, { method: "GET" }, false);
        } catch (_error) {
          window.location.reload();
        }
      });
    })();
  </script>
"""

GAME_SHELL_SCRIPT = """
  <script>
    (() => {
      const root = document.getElementById("app-root");
      if (!root) return;

      const stateUrl = "/api/state";
      const enteredKey = "blameMachineEntered";
      const backTrapKey = "blameMachineBackTrap";
      const inactivityResetMs = 300 * 1000;
      let resultReturnTimer = null;
      let resultCountdownTimer = null;
      let inactivityResetTimer = null;
      let titleActionLocked = false;
      let turnstileScriptPromise = null;
      let turnstileWidgetId = null;
      let turnstileOverlay = null;
      let turnstilePendingToken = null;
      let currentState = null;

      function armBackTrap() {
        if (window.history.state && window.history.state[backTrapKey]) return;
        window.history.replaceState({ [backTrapKey]: "root" }, "", window.location.href);
        window.history.pushState({ [backTrapKey]: "guard" }, "", window.location.href);
        window.addEventListener("popstate", () => {
          window.history.pushState({ [backTrapKey]: "guard" }, "", window.location.href);
        });
      }

      function escapeHtml(value) {
        return String(value ?? "")
          .replaceAll("&", "&amp;")
          .replaceAll("<", "&lt;")
          .replaceAll(">", "&gt;")
          .replaceAll('"', "&quot;")
          .replaceAll("'", "&#39;");
      }

      function renderErrorCard(state) {
        if (!state.error) return "";
        let message = "NICE TRY. THAT ENTRY CAN'T GO IN THE MACHINE.";
        if (state.error === "empty_category") {
          message = "THE CABINET NEEDS A TOPIC NAME FIRST.";
        } else if (state.error === "invalid") {
          message = "THAT TOPIC NAME WON'T FIT IN THE CABINET. TRY LETTERS, NUMBERS, OR SPACES.";
        } else if (state.error === "duplicate") {
          message = "BRUH! YOU ALREADY LOADED THAT...TRY AGAIN.";
        } else if (state.error === "duplicate_choices") {
          message = "THOSE CHOICES ARE TOO ALIKE. DISTINGUISH THEM SO THE MACHINE CAN TELL THEM APART.";
        } else if (state.error === "blocked") {
          message = "THAT CHOICE TRIPS THE MACHINE'S FILTER. TRY A DIFFERENT ENTRY.";
        } else if (state.error === "forbidden") {
          message = "THE MACHINE REJECTED THAT REQUEST. REFRESH THE PAGE AND TRY AGAIN.";
        } else if (state.error === "human_check_required") {
          message = "ONE QUICK HUMAN CHECK BEFORE THE CABINET TAKES REQUESTS.";
        }
        return `
          <div class="card error-card">
            ${message}
          </div>
        `;
      }

      function renderChoices(choices) {
        if (!choices.length) {
          return `<div class="pill">NO CHOICES YET</div>`;
        }
        return choices.map((item) => `<div class="pill">${escapeHtml(item)}</div>`).join("");
      }

      function renderPlayerRef(state) {
        if (!state.player_label) return "";
        return `
          <div class="player-banner">${escapeHtml(state.player_label)}</div>
        `;
      }

      function renderHome(state) {
        const topicCount = state.categories.length;
        const tunerDisabled = topicCount < 2 ? "disabled" : "";
        const categoryCard = state.categories.length ? `
          <div class="card">
            <form data-api="open-category">
              <div class="row">
                <label>PICK A TOPIC</label>
                <div class="topic-tuner">
                  <button type="button" class="topic-tuner-button" data-topic-shift="-1" aria-label="PREVIOUS TOPIC" ${tunerDisabled}>&lt;</button>
                  <div class="topic-tuner-display" data-topic-display>${escapeHtml(state.categories[0].replace(/\\b\\w/g, (c) => c.toUpperCase()))}</div>
                  <button type="button" class="topic-tuner-button" data-topic-shift="1" aria-label="NEXT TOPIC" ${tunerDisabled}>&gt;</button>
                </div>
                <div class="topic-tuner-meta" data-topic-meta>TOPIC 1 OF ${topicCount}</div>
                <input type="hidden" name="category" value="${escapeHtml(state.categories[0])}" data-topic-input>
                <div hidden>
                  ${state.categories.map((item) => `<span data-topic-option="${escapeHtml(item)}" data-topic-label="${escapeHtml(item.replace(/\\b\\w/g, (c) => c.toUpperCase()))}"></span>`).join("")}
                </div>
              </div>
              <button type="submit">OPEN TOPIC</button>
            </form>
          </div>
        ` : "";

        const accessCard = state.access_url ? `
          <details class="card access-box">
            <summary>PLAY ON ANOTHER DEVICE</summary>
            <div class="access-box-body">
              <div class="small">${escapeHtml(state.access_message)}</div>
              <code>${escapeHtml(state.access_url)}</code>
            </div>
          </details>
        ` : "";

        const label = state.categories.length
          ? "OR CREATE A NEW TOPIC"
          : "FIRST, WHAT TOPIC DO YOU NEED MY HELP WITH?";
        const button = state.categories.length ? "MAKE NEW TOPIC" : "LOAD THE MACHINE";

        const clearCard = state.categories.length ? `
          <div class="card" style="text-align:center;">
            <div class="prompt-copy">${escapeHtml(state.clear_arcade_description)}</div>
            <div class="button-stack" style="margin-top:18px;">
              <a class="button-link" href="#" data-nav="clear">CLEAR MY ARCADE</a>
            </div>
          </div>
        ` : "";

        return `
          <div class="title">★ BLAME MACHINE ★</div>
          <div class="sub">INSERT COIN. BLAME THE MACHINE.</div>
          ${renderErrorCard(state)}
          ${categoryCard}
          ${accessCard}
          <div class="card">
            <form data-api="category">
              <div class="row">
                <label class="center-label">${label}</label>
                <input name="category" placeholder="example: boy, bff, what should i do">
              </div>
              <button type="submit">${button}</button>
            </form>
          </div>
          ${clearCard}
          ${renderPlayerRef(state)}
        `;
      }

      function renderTitle() {
        window.blameMachineAudio?.setEntered?.(false);
        window.setTimeout(() => window.blameMachineAudio?.refreshControls?.(), 0);
        return `
          <div class="hero-card card">
            <div class="title-bar">
              <div class="title">★ BLAME MACHINE ★</div>
              <div class="sub">INSERT COIN. BLAME THE MACHINE.</div>
              <div class="hero-actions">
                <button type="button" class="hero-play-button" data-nav="start">PRESS PLAY</button>
                <a class="quiet-link" href="#" data-nav="silent">START IN SILENT MODE</a>
              </div>
            </div>
            <div class="title-content">
              <div class="hero-copy">
                READY PLAYER 1.<br>
                THE CABINET IS WARM.<br>
                TRY NOT TO BLAME THE MACHINE FOR WHAT HAPPENS NEXT.
              </div>
            </div>
          </div>
        `;
      }

      function renderClear(state) {
        return `
          <div class="title">★ CLEAR MY ARCADE ★</div>
          <div class="sub">LAST CHANCE TO SAVE THE RECEIPTS.</div>
          <div class="card" style="text-align:center;">
            <div class="prompt-copy">${escapeHtml(state.clear_arcade_description)}</div>
            <div class="button-stack" style="margin-top:18px;">
              <form data-api="clear">
                <button type="submit">YES, CLEAR MY ARCADE</button>
              </form>
              <a class="button-link" href="#" data-nav="home">NEVER MIND</a>
            </div>
          </div>
          ${renderPlayerRef(state)}
        `;
      }

      function renderCategory(state) {
        const editCard = state.mode === "edit" ? `
          <div class="card">
            <form data-api="save" data-category="${escapeHtml(state.category)}">
              <div class="row">
                <label>NOW TELL ME YOUR OPTIONS FOR ${escapeHtml(state.category_display).toUpperCase()} AND BE ENLIGHTENED.</label>
                <textarea name="choices" placeholder="pizza, tacos&#10;or one per line">${escapeHtml(state.choices)}</textarea>
                <div class="input-helper">COMMA-SEPARATED OR EACH IN A NEW LINE</div>
              </div>
              <button type="submit">PLAY!</button>
            </form>
            <div class="back-link">
              <a class="button-link" href="#" data-nav="home">← BACK TO ARCADE</a>
            </div>
          </div>
        ` : "";

        const readyCard = state.mode === "ready" ? `
          <div class="card">
            <div class="small">CURRENT CHOICES</div>
            <div class="choices">${renderChoices(state.choices_list)}</div>
            <div class="meta">UNIQUE PICKS: ${state.choice_count}</div>
            <div class="button-stack" style="margin-top:18px;">
              ${state.can_decide ? `
                <form data-api="decide" data-category="${escapeHtml(state.category)}">
                  <button type="submit">SPIN THE MACHINE</button>
                </form>
              ` : `
                <div class="meta">ONE CHOICE ISN'T A DECISION. GIVE ME AT LEAST TWO.</div>
              `}
              <a class="button-link" href="#" data-nav="edit" data-category="${escapeHtml(state.category)}">EDIT TOPIC</a>
              <a class="button-link" href="#" data-nav="home">← BACK TO ARCADE</a>
            </div>
          </div>
        ` : "";

        const resultCard = state.mode === "result" ? `
          <div class="card">
            <div class="result-box">
              <div class="result-label">DECISION</div>
              <div class="result-text">${escapeHtml(state.result)}</div>
              ${state.used_wildcard ? '<div class="wildcard">WILDCARD HIT</div>' : ""}
            </div>
            <div class="meta result-return" data-return-seconds="${state.auto_return_seconds}">RETURNING TO THE ARCADE IN ${state.auto_return_seconds} SECONDS.</div>
            <div class="button-stack result-actions" style="margin-top:18px;">
              <form data-api="decide" data-category="${escapeHtml(state.category)}">
                <button type="submit">REROLL</button>
              </form>
              <a class="button-link" href="#" data-nav="edit" data-category="${escapeHtml(state.category)}">EDIT TOPIC</a>
              <a class="button-link" href="#" data-nav="home">← BACK TO ARCADE</a>
            </div>
          </div>
        ` : "";

        return `
          <div class="title">★ ${escapeHtml(state.category_display).toUpperCase()} ★</div>
          <div class="sub">${escapeHtml(state.subtitle)}</div>
          ${renderErrorCard(state)}
          ${editCard}
          ${readyCard}
          ${resultCard}
          ${renderPlayerRef(state)}
        `;
      }

      function renderThinking(state) {
        return `
          <div class="title">★ ${escapeHtml(state.category_display).toUpperCase()} ★</div>
          <div class="sub">THE MACHINE IS THINKING...</div>
          <div class="card">
            <div class="result-box">
              <div class="result-label">PROCESSING</div>
              <div class="result-text">...</div>
              <div class="meta">DO NOT SHAKE THE CABINET.</div>
            </div>
          </div>
          ${renderPlayerRef(state)}
        `;
      }

      function clearInactivityReset() {
        if (inactivityResetTimer) {
          window.clearTimeout(inactivityResetTimer);
          inactivityResetTimer = null;
        }
      }

      function shouldUseInactivityReset(state) {
        if (!state || state.view === "home" || state.view === "title") return false;
        if (state.view === "category" && state.mode === "result") return false;
        return true;
      }

      async function returnToArcadeHome() {
        clearInactivityReset();
        window.history.pushState({}, "", "/game");
        await fetchState();
      }

      function armInactivityReset() {
        clearInactivityReset();
        if (!shouldUseInactivityReset(currentState)) return;
        inactivityResetTimer = window.setTimeout(async () => {
          await returnToArcadeHome();
        }, inactivityResetMs);
      }

      function noteActivity() {
        if (!inactivityResetTimer) return;
        armInactivityReset();
      }

      function render(state) {
        currentState = state;
        if (resultReturnTimer) {
          window.clearTimeout(resultReturnTimer);
          resultReturnTimer = null;
        }
        if (resultCountdownTimer) {
          window.clearInterval(resultCountdownTimer);
          resultCountdownTimer = null;
        }
        window.blameMachineAudio?.setEntered?.(true);
        window.dispatchEvent(new CustomEvent("blame-machine-state", { detail: state }));
        if (state.view === "clear") {
          root.innerHTML = renderClear(state);
          armInactivityReset();
          return;
        }
        if (state.view === "thinking") {
          root.innerHTML = renderThinking(state);
          armInactivityReset();
          return;
        }
        if (state.view === "category") {
          root.innerHTML = renderCategory(state);
          if (state.mode === "result" && Number(state.auto_return_seconds) > 0) {
            const returnNote = root.querySelector("[data-return-seconds]");
            let secondsLeft = Number(state.auto_return_seconds);
            if (returnNote instanceof HTMLElement) {
              const updateReturnNote = () => {
                const suffix = secondsLeft === 1 ? "" : "S";
                returnNote.textContent = `RETURNING TO THE ARCADE IN ${secondsLeft} SECOND${suffix}.`;
              };
              updateReturnNote();
              resultCountdownTimer = window.setInterval(() => {
                secondsLeft = Math.max(0, secondsLeft - 1);
                updateReturnNote();
              }, 1000);
            }
            resultReturnTimer = window.setTimeout(async () => {
              window.history.pushState({}, "", "/game");
              await fetchState();
            }, Number(state.auto_return_seconds) * 1000);
          }
          armInactivityReset();
          return;
        }
        root.innerHTML = renderHome(state);
        armInactivityReset();
      }

      function showTitle() {
        window.blameMachineAudio?.setEntered?.(false);
        root.innerHTML = renderTitle();
        wireTitleControls();
      }

      function markEntered() {
        window.sessionStorage.setItem(enteredKey, "1");
        window.blameMachineAudio?.setEntered?.(true);
      }

      async function fetchState(query = "") {
        const response = await fetch(`${stateUrl}${query}`, { credentials: "same-origin" });
        const state = await response.json();
        render(state);
        return state;
      }

      function closeTurnstileGate() {
        if (turnstileOverlay instanceof HTMLElement) {
          turnstileOverlay.remove();
        }
        turnstileOverlay = null;
        turnstilePendingToken = null;
      }

      function loadTurnstileScript() {
        if (window.turnstile) {
          return Promise.resolve(window.turnstile);
        }
        if (turnstileScriptPromise) {
          return turnstileScriptPromise;
        }
        turnstileScriptPromise = new Promise((resolve, reject) => {
          const script = document.createElement("script");
          script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
          script.async = true;
          script.defer = true;
          script.onload = () => resolve(window.turnstile);
          script.onerror = () => reject(new Error("turnstile_load_failed"));
          document.head.appendChild(script);
        });
        return turnstileScriptPromise;
      }

      async function ensureTurnstileToken(details) {
        if (!details.turnstile_site_key) {
          throw new Error("turnstile_missing_site_key");
        }
        if (turnstilePendingToken) {
          return turnstilePendingToken;
        }

        const turnstileApi = await loadTurnstileScript();
        if (!(turnstileOverlay instanceof HTMLElement)) {
          turnstileOverlay = document.createElement("div");
          turnstileOverlay.className = "turnstile-overlay";
          turnstileOverlay.innerHTML = `
            <div class="card turnstile-card">
              <div class="title">★ HUMAN CHECK ★</div>
              <div class="turnstile-copy">PROVE YOU'RE A REAL PERSON ONCE, THEN THE MACHINE LEAVES YOU ALONE FOR A WHILE.</div>
              <div class="turnstile-slot" id="turnstile-slot"></div>
              <div class="turnstile-help">IF THE CHALLENGE EXPIRES, THE CABINET WILL ASK AGAIN.</div>
            </div>
          `;
          document.body.appendChild(turnstileOverlay);
        }

        const slot = turnstileOverlay.querySelector("#turnstile-slot");
        if (!(slot instanceof HTMLElement)) {
          throw new Error("turnstile_slot_missing");
        }
        slot.innerHTML = "";

        turnstilePendingToken = await new Promise((resolve, reject) => {
          try {
            turnstileWidgetId = turnstileApi.render(slot, {
              sitekey: details.turnstile_site_key,
              action: details.turnstile_action || "start_play",
              theme: "dark",
              callback: (token) => resolve(token),
              "expired-callback": () => {
                turnstilePendingToken = null;
                if (turnstileWidgetId !== null) {
                  turnstileApi.reset(turnstileWidgetId);
                }
              },
              "error-callback": () => {
                turnstilePendingToken = null;
                reject(new Error("turnstile_failed"));
              },
            });
          } catch (error) {
            reject(error);
          }
        });

        return turnstilePendingToken;
      }

      async function postJson(url, payload, options = {}) {
        const outgoing = { ...payload };
        if (options.turnstileToken) {
          outgoing.turnstile_token = options.turnstileToken;
        }
        const response = await fetch(url, {
          method: "POST",
          credentials: "same-origin",
          headers: {
            "Content-Type": "application/json"
          },
          body: JSON.stringify(outgoing)
        });
        const state = await response.json();
        if (state.turnstile_required) {
          const token = await ensureTurnstileToken(state);
          return postJson(url, payload, { ...options, turnstileToken: token, retryingTurnstile: true });
        }
        closeTurnstileGate();
        if (options.renderResponse !== false) {
          render(state);
        }
        return state;
      }

      async function runTitleAction(action) {
        if (titleActionLocked) return;
        titleActionLocked = true;
        const startButton = root.querySelector("[data-nav='start']");
        const silentLink = root.querySelector("[data-nav='silent']");
        if (startButton instanceof HTMLButtonElement) {
          startButton.disabled = true;
          startButton.classList.add("is-loading");
          startButton.textContent = action === "silent" ? "ENTERING STEALTH..." : "WAKING CABINET...";
        }
        if (silentLink instanceof HTMLElement) {
          silentLink.setAttribute("aria-disabled", "true");
          silentLink.style.pointerEvents = "none";
          silentLink.style.opacity = "0.55";
        }
        window.setTimeout(() => {
          titleActionLocked = false;
        }, 500);

        if (action === "start") {
          try {
            await window.blameMachineAudio?.enableAndStart?.({
              skipPrimer: false,
              suppressPrimer: false,
              waitForPrimerEnd: true,
              primerHoldMs: 650,
            });
          } catch (_) {
            // Keep the UI moving even if audio startup is flaky on mobile.
          }
          markEntered();
          window.history.replaceState({}, "", "/game");
          await fetchState();
          return;
        }

        if (action === "silent") {
          markEntered();
          await window.blameMachineAudio?.setEnabled?.(false);
          window.history.replaceState({}, "", "/game");
          await fetchState();
        }
      }

      async function restoreEnteredSession() {
        window.blameMachineAudio?.setEntered?.(true);
        try {
          await fetchState(window.location.search);
        } catch (_error) {
          window.sessionStorage.removeItem(enteredKey);
          window.blameMachineAudio?.setEntered?.(false);
          showTitle();
          return;
        }
        try {
          await window.blameMachineAudio?.enableAndStart?.({ skipPrimer: true });
        } catch (_error) {
          // A later gameplay tap can still revive audio if iPhone declines to resume on reload.
        }
      }

      document.addEventListener("click", async (event) => {
        noteActivity();
        const tunerButton = event.target.closest("[data-topic-shift]");
        if (tunerButton) {
          event.preventDefault();
          const form = tunerButton.closest("form");
          const input = form?.querySelector("[data-topic-input]");
          const display = form?.querySelector("[data-topic-display]");
          const meta = form?.querySelector("[data-topic-meta]");
          if (
            !(form instanceof HTMLFormElement) ||
            !(input instanceof HTMLInputElement) ||
            !(display instanceof HTMLElement)
          ) {
            return;
          }
          const options = Array.from(form.querySelectorAll("[data-topic-option]")).map((node) => ({
            value: node.getAttribute("data-topic-option") || "",
            label: node.getAttribute("data-topic-label") || "",
          }));
          const fallbackOptions = options.length ? options : [];
          if (!fallbackOptions.length) {
            return;
          }
          const currentIndex = Math.max(0, fallbackOptions.findIndex((item) => item.value === input.value));
          const delta = Number(tunerButton.getAttribute("data-topic-shift") || "0");
          const nextIndex = (currentIndex + delta + fallbackOptions.length) % fallbackOptions.length;
          input.value = fallbackOptions[nextIndex].value;
          display.textContent = fallbackOptions[nextIndex].label;
          if (meta instanceof HTMLElement) {
            meta.textContent = `TOPIC ${nextIndex + 1} OF ${fallbackOptions.length}`;
          }
          return;
        }

        const link = event.target.closest("[data-nav]");
        if (link) {
          event.preventDefault();

          const nav = link.getAttribute("data-nav");
          if (nav === "home") {
            window.history.pushState({}, "", "/game");
            await fetchState();
            return;
          }
          if (nav === "clear") {
            window.history.pushState({}, "", "/game?view=clear");
            await fetchState("?view=clear");
            return;
          }
          if (nav === "edit") {
            const category = link.getAttribute("data-category");
            const query = `?view=category&category=${encodeURIComponent(category)}&mode=edit`;
            window.history.pushState({}, "", `/game${query}`);
            await fetchState(query);
            return;
          }
        }

        const exit = event.target.closest("#exit-game");
        if (!exit) return;
        event.preventDefault();
        if (document.querySelector(".hero-card")) {
          if (window.blameMachineEnteredFromHub === true) {
            window.sessionStorage.removeItem("blameMachineHubOrigin");
            window.sessionStorage.removeItem("blameMachineHubReturnUrl");
            window.location.href = window.blameMachineHubReturnUrl || "https://games.irq9.net/";
          }
          return;
        }
        await window.blameMachineAudio?.pause?.();
        window.blameMachineAudio?.setEntered?.(false);
        window.history.replaceState({}, "", "/game");
        showTitle();
      });

      document.addEventListener("submit", async (event) => {
        noteActivity();
        const form = event.target;
        if (!(form instanceof HTMLFormElement)) return;
        const action = form.getAttribute("data-api");
        if (!action) return;
        event.preventDefault();

        if (action === "open-category") {
          const category = String(new FormData(form).get("category") || "").trim();
          const query = `?view=category&category=${encodeURIComponent(category)}&mode=ready`;
          window.history.pushState({}, "", `/game${query}`);
          await fetchState(query);
          return;
        }

        if (action === "category") {
          const category = String(new FormData(form).get("category") || "").trim();
          const state = await postJson("/api/category", { category });
          if (state.view === "category") {
            const query = `?view=category&category=${encodeURIComponent(state.category)}&mode=${encodeURIComponent(state.mode)}`;
            window.history.pushState({}, "", `/game${query}`);
          }
          return;
        }

        if (action === "save") {
          const category = form.getAttribute("data-category");
          const choices = String(new FormData(form).get("choices") || "");
          const state = await postJson("/api/save", { category, choices });
          const query = `?view=category&category=${encodeURIComponent(state.category)}&mode=${encodeURIComponent(state.mode)}`;
          window.history.pushState({}, "", `/game${query}`);
          return;
        }

        if (action === "decide") {
          const category = form.getAttribute("data-category");
          const categoryDisplay = category.replace(/\\b\\w/g, (c) => c.toUpperCase());
          const holdingState = {
            view: "thinking",
            scene: "thinking",
            category,
            category_display: categoryDisplay,
            player_label: root.querySelector(".player-banner")?.textContent || "",
          };
          render(holdingState);
          const state = await postJson("/api/decide", { category }, { renderResponse: false });
          if (state.result_kind === "too_soon") {
            // Wonk (descending sawtooth) is intentional here — signals the machine rejecting a hasty reroll.
            window.blameMachineAudio?.playWonkCue?.();
            render(state);
          } else {
            await new Promise((resolve) => window.setTimeout(resolve, 3000));
            window.blameMachineAudio?.playRevealCue?.();
            render(state);
          }
          const query = `?view=category&category=${encodeURIComponent(state.category)}&mode=${encodeURIComponent(state.mode)}`;
          window.history.pushState({}, "", `/game${query}`);
          return;
        }

        if (action === "clear") {
          window.history.pushState({}, "", "/game");
          await postJson("/api/clear", {});
        }
      });

      window.addEventListener("popstate", async () => {
        if (window.location.pathname !== "/game" || window.sessionStorage.getItem(enteredKey) !== "1") {
          showTitle();
          return;
        }
        const url = new URL(window.location.href);
        const view = url.searchParams.get("view");
        const category = url.searchParams.get("category");
        const mode = url.searchParams.get("mode");
        const query = new URLSearchParams();
        if (view) query.set("view", view);
        if (category) query.set("category", category);
        if (mode) query.set("mode", mode);
        const suffix = query.toString() ? `?${query.toString()}` : "";
        await fetchState(suffix);
      });

      document.addEventListener("input", noteActivity, { capture: true });
      document.addEventListener("keydown", noteActivity, { capture: true });
      document.addEventListener("pointerdown", noteActivity, { passive: true, capture: true });
      document.addEventListener("touchstart", noteActivity, { passive: true, capture: true });

      armBackTrap();
      if (window.location.pathname === "/game" && window.sessionStorage.getItem(enteredKey) === "1") {
        restoreEnteredSession();
      } else {
        showTitle();
      }
      
      function wireTitleControls() {
        const startButton = root.querySelector("[data-nav='start']");
        const silentLink = root.querySelector("[data-nav='silent']");
        let startTriggered = false;
        let silentTriggered = false;

        async function handleStart(event) {
          if (event) event.preventDefault();
          if (startTriggered) return;
          startTriggered = true;
          await runTitleAction("start");
        }

        async function handleSilent(event) {
          if (event) event.preventDefault();
          if (silentTriggered) return;
          silentTriggered = true;
          await runTitleAction("silent");
        }

        if (startButton) {
          startButton.addEventListener("touchend", handleStart);
          startButton.addEventListener("click", handleStart);
        }

        if (silentLink) {
          silentLink.addEventListener("touchend", handleSilent);
          silentLink.addEventListener("click", handleSilent);
        }
      }

    })();
  </script>
"""

GAME_HTML_BODY = """
<body>
  <div class="wrap">
""" + MUSIC_UI + """
    <div id="app-root"></div>
  </div>
  <div class="footer-commit">""" + BUILD_LABEL + """</div>
""" + MUSIC_SCRIPT + GAME_SHELL_SCRIPT + """
</body>
</html>
"""


def current_public_base_url() -> str:
    configured = configured_public_url()
    if configured:
        return configured
    return request.url_root.strip().rstrip("/")


def absolute_app_url(path: str) -> str:
    return f"{current_public_base_url()}{path}"


def render_game_html() -> str:
    title = "Blame Machine"
    game_url = absolute_app_url("/game")
    social_image = absolute_app_url("/social-card.png")
    favicon = absolute_app_url("/favicon.svg")
    apple_touch_icon = absolute_app_url("/apple-touch-icon.png")
    head = f"""<!doctype html>
<html>
<head>
  <title>{html_escape(title)}</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="description" content="{html_escape(SOCIAL_DESCRIPTION)}">
  <meta name="theme-color" content="#050505">
  <meta name="apple-mobile-web-app-title" content="{html_escape(title)}">
  <meta property="og:type" content="website">
  <meta property="og:title" content="{html_escape(title)}">
  <meta property="og:description" content="{html_escape(SOCIAL_DESCRIPTION)}">
  <meta property="og:url" content="{html_escape(game_url)}">
  <meta property="og:image" content="{html_escape(social_image)}">
  <meta property="og:image:secure_url" content="{html_escape(social_image)}">
  <meta property="og:image:type" content="image/png">
  <meta property="og:image:width" content="1200">
  <meta property="og:image:height" content="630">
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:title" content="{html_escape(title)}">
  <meta name="twitter:description" content="{html_escape(SOCIAL_DESCRIPTION)}">
  <meta name="twitter:image" content="{html_escape(social_image)}">
  <link rel="icon" type="image/svg+xml" href="{html_escape(favicon)}">
  <link rel="apple-touch-icon" href="{html_escape(apple_touch_icon)}">
  <link rel="shortcut icon" href="{html_escape(absolute_app_url('/favicon.ico'))}">
""" + BASE_STYLES + """
</head>
"""
    return head + GAME_HTML_BODY


def load_word_list(path: Path) -> set[str]:
    if not path.exists():
        return set()

    words = set()
    with path.open("r", encoding="utf-8") as handle:
        for line in handle:
            line = line.strip().lower()
            if not line or line.startswith("#"):
                continue
            words.add(line)
    return words


BANNED_WORDS = load_word_list(BANNED_WORDS_PATH)
ALLOWED_WORDS = load_word_list(ALLOWED_WORDS_PATH)
app.config["ACCESS_URL"] = None
app.config["ACCESS_MESSAGE"] = None
app.config["BROWSER_URL"] = None


def debug_beep_bytes() -> bytes:
    rate = 44100
    duration_seconds = 0.25
    frequency_hz = 1320
    buffer = io.BytesIO()

    with wave.open(buffer, "wb") as wav_file:
        wav_file.setnchannels(1)
        wav_file.setsampwidth(2)
        wav_file.setframerate(rate)
        frames: list[bytes] = []
        total = int(rate * duration_seconds)

        for index in range(total):
            if index < total * 0.8:
                envelope = 1.0
            else:
                envelope = max(0.0, (total - index) / (total * 0.2))
            sample = int(
                32767
                * 0.65
                * envelope
                * math.sin(2 * math.pi * frequency_hz * index / rate)
            )
            frames.append(struct.pack("<h", sample))

        wav_file.writeframes(b"".join(frames))

    return buffer.getvalue()


def debug_beep_data_uri() -> str:
    encoded = base64.b64encode(AUDIO_DEBUG_BEEP_WAV).decode("ascii")
    return f"data:audio/wav;base64,{encoded}"


def synth_wav_bytes(
    duration_seconds: float,
    events: list[dict[str, float | str]],
    sample_rate: int = 44100,
    master_gain: float = 0.7,
    sharpness: float = 1.0,
) -> bytes:
    total_frames = max(1, int(sample_rate * duration_seconds))
    buffer = io.BytesIO()
    frames: list[bytes] = []

    def osc_value(kind: str, phase: float) -> float:
        cycle = phase % 1.0
        if kind == "square":
            return 1.0 if cycle < 0.5 else -1.0
        if kind == "triangle":
            return 1.0 - 4.0 * abs(cycle - 0.5)
        if kind == "saw":
            return (2.0 * cycle) - 1.0
        return math.sin(2 * math.pi * cycle)

    for index in range(total_frames):
        t = index / sample_rate
        sample_value = 0.0

        for event in events:
            start = float(event.get("start", 0.0))
            end = float(event.get("end", duration_seconds))
            if t < start or t >= end:
                continue

            local = t - start
            span = max(end - start, 0.001)
            shaped = max(0.5, min(2.0, sharpness))
            attack = min(0.010 / (shaped * shaped), span * 0.18)
            release = min(0.050 / (shaped * shaped), span * 0.35)
            if local < attack:
                envelope = local / max(attack, 0.001)
            else:
                if shaped >= 1.0:
                    decay_rate = 2.0 + ((shaped - 1.0) * 10.0)
                    sustain_floor = max(0.12, 1.0 - ((shaped - 1.0) * 0.86))
                    envelope = sustain_floor + ((1.0 - sustain_floor) * math.exp(-decay_rate * (local / span)))
                else:
                    envelope = 1.0
            if end - t < release:
                envelope *= max(0.0, (end - t) / max(release, 0.001))

            freq = float(event.get("freq", 440.0))
            sweep_to = event.get("sweep_to")
            if sweep_to is not None:
                freq = freq + (float(sweep_to) - freq) * min(max(local / span, 0.0), 1.0)

            phase = local * freq
            amp = float(event.get("amp", 0.2))
            kind = str(event.get("wave", "sine"))
            transient_boost = 1.0
            if shaped > 1.0 and local < 0.018:
                transient_boost += (shaped - 1.0) * 0.45
            sample_value += osc_value(kind, phase) * amp * envelope * transient_boost

        sample_value = max(-1.0, min(1.0, sample_value * master_gain))
        frames.append(struct.pack("<h", int(sample_value * 32767)))

    with wave.open(buffer, "wb") as wav_file:
        wav_file.setnchannels(1)
        wav_file.setsampwidth(2)
        wav_file.setframerate(sample_rate)
        wav_file.writeframes(b"".join(frames))

    return buffer.getvalue()


def build_home_loop_wav() -> bytes:
    pitch = 0.85
    beat = 0.36
    pattern = [220.0, 220.0, 261.63, 220.0, 293.66, 261.63, 220.0, 196.0]
    arp = [659.25, 783.99, 880.0, 783.99, 698.46, 783.99, 1046.5, 783.99]
    events: list[dict[str, float | str]] = []
    for step, freq in enumerate(pattern):
        start = step * beat
        events.append({"start": start, "end": start + 0.22, "freq": freq * pitch, "wave": "triangle", "amp": 0.12})
        events.append({"start": start, "end": start + 0.08, "freq": freq * 2 * pitch, "wave": "square", "amp": 0.08})
        events.append({"start": start, "end": start + 0.15, "freq": arp[step] * pitch, "wave": "saw", "amp": 0.10})
        if step % 4 == 3:
            events.append({"start": start, "end": start + 0.04, "freq": 1320.0 * pitch, "wave": "square", "amp": 0.05})
    return synth_wav_bytes(beat * 8, events, master_gain=0.74, sharpness=2.0)


def build_category_loop_wav() -> bytes:
    beat = 60 / 96 / 2
    bass = [196.0, 196.0, 220.0, 196.0, 196.0, 174.61, 196.0, 220.0]
    lead = [392.0, 440.0, 392.0, 523.25, 440.0, 392.0, 349.23, 392.0]
    events: list[dict[str, float | str]] = []
    for step, freq in enumerate(bass):
        start = step * beat
        events.append({"start": start, "end": start + 0.20, "freq": freq, "wave": "triangle", "amp": 0.12})
        events.append({"start": start + 0.015, "end": start + 0.14, "freq": freq * 2, "wave": "triangle", "amp": 0.08})
        events.append({"start": start + 0.04, "end": start + 0.18, "freq": lead[step], "wave": "triangle", "amp": 0.07})
    return synth_wav_bytes(beat * 8, events, master_gain=0.72)


def build_coin_wav() -> bytes:
    return synth_wav_bytes(
        0.22,
        [
            {"start": 0.00, "end": 0.06, "freq": 1760.0, "sweep_to": 1620.0, "wave": "square", "amp": 0.35},
            {"start": 0.06, "end": 0.16, "freq": 1318.51, "sweep_to": 920.0, "wave": "square", "amp": 0.42},
        ],
        master_gain=0.9,
    )


def build_reveal_wav() -> bytes:
    return synth_wav_bytes(
        0.55,
        [
            {"start": 0.00, "end": 0.16, "freq": 523.25, "sweep_to": 659.25, "wave": "triangle", "amp": 0.18},
            {"start": 0.11, "end": 0.30, "freq": 659.25, "sweep_to": 783.99, "wave": "triangle", "amp": 0.22},
            {"start": 0.24, "end": 0.50, "freq": 783.99, "sweep_to": 1046.5, "wave": "triangle", "amp": 0.28},
        ],
        master_gain=0.95,
    )


def build_wonk_wav() -> bytes:
    return synth_wav_bytes(
        0.42,
        [
            {"start": 0.00, "end": 0.18, "freq": 330.0, "sweep_to": 220.0, "wave": "saw", "amp": 0.22},
            {"start": 0.16, "end": 0.38, "freq": 246.94, "sweep_to": 164.81, "wave": "saw", "amp": 0.20},
        ],
        master_gain=0.95,
    )


AUDIO_DEBUG_BEEP_WAV = debug_beep_bytes()
AUDIO_DEBUG_BEEP_DATA_URI = debug_beep_data_uri()
AUDIO_LOOP_HOME_WAV = build_home_loop_wav()
AUDIO_LOOP_CATEGORY_WAV = build_category_loop_wav()
AUDIO_COIN_WAV = build_coin_wav()
AUDIO_REVEAL_WAV = build_reveal_wav()
AUDIO_WONK_WAV = build_wonk_wav()


def slugify_identity(value: str) -> str:
    value = value.strip().lower()
    value = re.sub(r"[^a-z0-9._-]+", "-", value)
    value = re.sub(r"-{2,}", "-", value).strip("-")
    return value or "local"


def identity_storage_key(value: str) -> str:
    normalized = value.strip().lower().encode("utf-8")
    digest = hashlib.sha256(normalized).hexdigest()[:24]
    return f"user-{digest}"


def trusted_identity_headers_present() -> bool:
    if not IDENTITY_SHARED_SECRET:
        return False
    secret = request.headers.get(IDENTITY_SECRET_HEADER, "").strip()
    return bool(secret) and hmac.compare_digest(secret, IDENTITY_SHARED_SECRET)


def turnstile_enabled() -> bool:
    return bool(TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY)


def valid_guest_cookie_value(value: str) -> bool:
    return bool(re.fullmatch(r"[A-Za-z0-9_-]{24,128}", value or ""))


def ensure_guest_cookie_value() -> str:
    existing = request.cookies.get(GUEST_COOKIE_NAME, "").strip()
    if valid_guest_cookie_value(existing):
        return existing

    pending = getattr(g, "pending_guest_cookie", "").strip()
    if valid_guest_cookie_value(pending):
        return pending

    pending = secrets.token_urlsafe(24)
    g.pending_guest_cookie = pending
    return pending


def make_signed_value(secret_key: str, payload: str) -> str:
    digest = hmac.new(secret_key.encode("utf-8"), payload.encode("utf-8"), hashlib.sha256).hexdigest()
    return digest


def issue_turnstile_clearance() -> str:
    expires_at = int(time.time()) + TURNSTILE_COOKIE_MAX_AGE
    nonce = secrets.token_urlsafe(12)
    payload = f"{expires_at}:{nonce}"
    signature = make_signed_value(TURNSTILE_SECRET_KEY, payload)
    return f"{payload}:{signature}"


def has_valid_turnstile_clearance() -> bool:
    if not turnstile_enabled():
        return False

    raw = request.cookies.get(TURNSTILE_COOKIE_NAME, "").strip()
    try:
        expires_raw, nonce, signature = raw.split(":", 2)
        expires_at = int(expires_raw)
    except ValueError:
        return False

    if expires_at <= int(time.time()):
        return False

    payload = f"{expires_at}:{nonce}"
    expected_signature = make_signed_value(TURNSTILE_SECRET_KEY, payload)
    return hmac.compare_digest(signature, expected_signature)


def client_ip() -> str:
    cf_ip = request.headers.get("CF-Connecting-IP", "").strip()
    if cf_ip:
        return cf_ip

    forwarded_for = request.headers.get("X-Forwarded-For", "").strip()
    if forwarded_for:
        return forwarded_for.split(",", 1)[0].strip()

    return request.remote_addr or ""


def verify_turnstile_token(token: str) -> tuple[bool, list[str]]:
    payload = {
        "secret": TURNSTILE_SECRET_KEY,
        "response": token,
        "remoteip": client_ip(),
    }
    encoded = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(
        "https://challenges.cloudflare.com/turnstile/v0/siteverify",
        data=encoded,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=TURNSTILE_VERIFY_TIMEOUT) as response:
            result = json.loads(response.read().decode("utf-8"))
    except (urllib.error.URLError, TimeoutError, json.JSONDecodeError, OSError):
        return False, ["internal-error"]

    if not result.get("success"):
        return False, [str(code) for code in result.get("error-codes", [])]

    action = str(result.get("action", "") or "").strip()
    if TURNSTILE_ACTION and action and action != TURNSTILE_ACTION:
        return False, ["action-mismatch"]

    return True, []


def turnstile_required_response() -> Response:
    return (
        jsonify(
            {
                "turnstile_required": True,
                "error": "human_check_required",
                "turnstile_site_key": TURNSTILE_SITE_KEY,
                "turnstile_action": TURNSTILE_ACTION,
            }
        ),
        403,
    )


def current_identity() -> dict[str, str]:
    if trusted_identity_headers_present():
        user = request.headers.get(IDENTITY_USER_HEADER, "").strip()
        email = request.headers.get(IDENTITY_EMAIL_HEADER, "").strip()
        name = request.headers.get(IDENTITY_NAME_HEADER, "").strip()
        if user:
            login_value = email or user
            return {
                "user": user,
                "slug": slugify_identity(user),
                "storage_key": identity_storage_key(user),
                "email": email,
                "name": name,
                "label": f"You are logged in as: {login_value}",
                "source_message": "Private profile loaded through Access SSO.",
            }

    guest_cookie = ensure_guest_cookie_value()
    guest_label = f"GAME TOKEN: {guest_cookie[:8].upper()}"
    return {
        "user": f"guest-{guest_cookie[:12]}",
        "slug": slugify_identity(guest_label),
        "storage_key": identity_storage_key(f"guest:{guest_cookie}"),
        "email": "",
        "name": "",
        "label": guest_label,
        "source_message": "Public mode. This browser keeps its own arcade cabinet.",
    }


def config_path_for_identity(identity: dict[str, str]) -> Path:
    user_dir = APP_DIR / "users"
    user_dir.mkdir(parents=True, exist_ok=True, mode=0o700)
    try:
        user_dir.chmod(0o700)
    except PermissionError:
        # Bind-mounted directories may be owned by the host.
        pass
    return user_dir / f"{identity['storage_key']}.ini"


def load_config(identity: dict[str, str]) -> configparser.ConfigParser:
    config = configparser.ConfigParser()
    ini_path = config_path_for_identity(identity)
    if ini_path.exists():
        config.read(ini_path)
    return config


def save_config(config: configparser.ConfigParser, identity: dict[str, str]) -> None:
    ini_path = config_path_for_identity(identity)
    fd = os.open(ini_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
    with os.fdopen(fd, "w", encoding="utf-8") as handle:
        config.write(handle)
    try:
        ini_path.chmod(0o600)
    except PermissionError:
        # Bind-mounted files may be owned by the host.
        pass


def normalize_for_filter(text: str) -> str:
    text = text.lower().strip()
    text = re.sub(r"[^a-z0-9\s]", "", text)
    text = re.sub(r"\s+", " ", text)
    return text


def is_blocked_entry(text: str) -> bool:
    normalized = normalize_for_filter(text)
    if not normalized:
        return False
    if normalized in ALLOWED_WORDS:
        return False
    return normalized in BANNED_WORDS


def norm(text: str) -> str:
    return text.strip().lower()


def titleize(text: str) -> str:
    return text.strip().title()


def parse_csv(raw: str) -> list[str]:
    items = re.split(r"[\n,]+", raw)
    return [item.strip() for item in items if item.strip()]


def dedupe_choices(items: list[str]) -> list[str]:
    seen: set[str] = set()
    unique: list[str] = []

    for item in items:
        key = norm(item)
        if key in seen:
            continue
        seen.add(key)
        unique.append(item.strip())

    return unique


def duplicate_choices(items: list[str]) -> list[str]:
    seen: set[str] = set()
    duplicate_keys: set[str] = set()
    duplicates: list[str] = []

    for item in items:
        key = norm(item)
        if key in seen:
            if key not in duplicate_keys:
                duplicates.append(item.strip())
                duplicate_keys.add(key)
            continue
        seen.add(key)

    return duplicates


def category_wildcards(category: str) -> list[str]:
    return CATEGORY_DEFAULT_WILDCARDS.get(category, GLOBAL_WILDCARDS)


def random_wildcard(category: str) -> str:
    return random.choice(category_wildcards(category))


def funny_choice_result(choice: str) -> str:
    return random.choice(CHOICE_TEMPLATES).format(choice=choice)


def random_clear_arcade_description() -> str:
    return random.choice(CLEAR_ARCADE_DESCRIPTIONS)


def is_valid_category_name(category: str) -> bool:
    return bool(category) and bool(CATEGORY_NAME_RE.fullmatch(category))


def ensure_category(config: configparser.ConfigParser, category: str) -> None:
    if category not in config:
        config[category] = {}

    config[category].setdefault("choices", "")
    config[category].setdefault("last_choice", "")
    config[category].setdefault("last_ts", "0")
    config[category].setdefault("recent_choices", "")


def last_pick_info(config: configparser.ConfigParser, category: str) -> tuple[str, float]:
    ensure_category(config, category)
    last_choice = config[category].get("last_choice", "").strip()
    last_ts_raw = config[category].get("last_ts", "0").strip()

    try:
        last_ts = float(last_ts_raw)
    except ValueError:
        last_ts = 0.0

    return last_choice, last_ts


def recent_choice_history(config: configparser.ConfigParser, category: str) -> list[str]:
    ensure_category(config, category)
    return [norm(item) for item in parse_csv(config[category].get("recent_choices", ""))]


def update_recent_history(config: configparser.ConfigParser, category: str, choice: str) -> None:
    history = recent_choice_history(config, category)
    normalized_choice = norm(choice)
    history = [item for item in history if item != normalized_choice]
    history.insert(0, normalized_choice)
    trimmed = history[:RECENT_HISTORY_LIMIT]
    config[category]["recent_choices"] = ", ".join(trimmed)


def choice_pool(choices: list[str], recent_history: list[str]) -> list[str]:
    if len(choices) <= 1:
        return choices

    available = [choice for choice in choices if norm(choice) not in recent_history]
    return available or choices


def category_context(config: configparser.ConfigParser, category: str) -> dict:
    ensure_category(config, category)
    choices = config[category].get("choices", "")
    choices_list = parse_csv(choices)
    return {
        "category": category,
        "category_display": titleize(category),
        "choices": choices,
        "choices_list": choices_list,
        "choice_count": len(choices_list),
        "can_decide": len(choices_list) >= 2,
        "wildcard_percent": int(WILDCARD_CHANCE * 100),
    }


def category_subtitle(mode: str) -> str:
    subtitles = {
        "edit": "LOAD THE MACHINE.",
        "ready": "LOCKED IN. READY TO SPIN.",
        "result": "THE ARCADE HAS SPOKEN.",
    }
    return subtitles.get(mode, "DECIDE SOMETHING.")


def home_state(identity: dict[str, str], error: str | None = None) -> dict:
    config = load_config(identity)
    return {
        "view": "home",
        "scene": "home",
        "player_label": identity["label"],
        "player_source_message": identity["source_message"],
        "categories": sorted(config.sections()),
        "error": error,
        "access_url": app.config.get("ACCESS_URL"),
        "access_message": app.config.get("ACCESS_MESSAGE"),
        "clear_arcade_description": random_clear_arcade_description(),
    }


def clear_state(identity: dict[str, str]) -> dict:
    return {
        "view": "clear",
        "scene": "home",
        "player_label": identity["label"],
        "player_source_message": identity["source_message"],
        "clear_arcade_description": random_clear_arcade_description(),
    }


def category_state(
    identity: dict[str, str],
    category: str,
    mode: str,
    result: str | None = None,
    used_wildcard: bool = False,
    error: str | None = None,
    result_meta: str | None = None,
) -> dict:
    config = load_config(identity)
    category = norm(category)
    ctx = category_context(config, category)
    ctx["view"] = "category"
    ctx["scene"] = "result" if mode == "result" else "category"
    ctx["mode"] = mode
    ctx["result"] = result
    ctx["used_wildcard"] = used_wildcard
    ctx["error"] = error
    ctx["result_meta"] = result_meta
    ctx["result_kind"] = "normal"
    ctx["auto_return_seconds"] = RESULT_RETURN_SECONDS if mode == "result" else 0
    ctx["subtitle"] = category_subtitle(mode)
    ctx["player_label"] = identity["label"]
    ctx["player_source_message"] = identity["source_message"]
    return ctx


def payload_value(name: str, default: str = "") -> str:
    data = request.get_json(silent=True)
    if isinstance(data, dict) and name in data:
        value = data.get(name, default)
        return "" if value is None else str(value)
    return request.form.get(name, default)


def expected_request_origin() -> str:
    proto = request.headers.get("X-Forwarded-Proto", "").strip()
    host = request.headers.get("X-Forwarded-Host", "").strip()
    if proto and host:
        return f"{proto}://{host}".rstrip("/")
    return request.host_url.rstrip("/")


def origin_host_matches(origin_like: str, expected_origin: str) -> bool:
    if not origin_like:
        return False
    try:
        parsed_origin = urlparse(origin_like)
        parsed_expected = urlparse(expected_origin)
    except ValueError:
        return False
    return bool(parsed_origin.netloc) and parsed_origin.netloc == parsed_expected.netloc


@app.after_request
def disable_shell_caching(response):
    if request.path in {"/", "/game"} or request.path.startswith("/api/"):
        response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
        response.headers["Pragma"] = "no-cache"
        response.headers["Expires"] = "0"

    pending_guest_cookie = getattr(g, "pending_guest_cookie", "").strip()
    if pending_guest_cookie:
        response.set_cookie(
            GUEST_COOKIE_NAME,
            pending_guest_cookie,
            max_age=GUEST_COOKIE_MAX_AGE,
            httponly=True,
            secure=bool(request.is_secure or request.headers.get("X-Forwarded-Proto", "").strip() == "https"),
            samesite="Lax",
            path="/",
        )

    pending_turnstile_cookie = getattr(g, "pending_turnstile_cookie", "").strip()
    if pending_turnstile_cookie:
        response.set_cookie(
            TURNSTILE_COOKIE_NAME,
            pending_turnstile_cookie,
            max_age=TURNSTILE_COOKIE_MAX_AGE,
            httponly=True,
            secure=bool(request.is_secure or request.headers.get("X-Forwarded-Proto", "").strip() == "https"),
            samesite="Lax",
            path="/",
        )
    return response


def same_origin_api_request() -> bool:
    expected_origin = expected_request_origin()
    origin = request.headers.get("Origin", "").strip().rstrip("/")
    referer = request.headers.get("Referer", "").strip()
    fetch_site = request.headers.get("Sec-Fetch-Site", "").strip().lower()

    if origin:
        return origin == expected_origin or origin_host_matches(origin, expected_origin)
    if referer:
        return (
            referer == expected_origin
            or referer.startswith(expected_origin + "/")
            or origin_host_matches(referer, expected_origin)
        )
    return fetch_site in {"", "same-origin", "same-site"}


def audio_debug_allowed() -> bool:
    return AUDIO_DEBUG_ENABLED


def bounded_log_value(value: object, limit: int = 240) -> str:
    text = str(value or "").replace("\r", " ").replace("\n", " ")
    return text[:limit]


def svg_response(markup: str, cache_seconds: int = 3600) -> Response:
    response = Response(markup, mimetype="image/svg+xml")
    response.headers["Cache-Control"] = f"public, max-age={cache_seconds}"
    return response


def favicon_svg_markup() -> str:
    return """<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Blame Machine">
  <rect width="64" height="64" rx="10" fill="#050505"/>
  <rect x="4" y="4" width="56" height="56" rx="8" fill="none" stroke="#39ff14" stroke-width="4"/>
  <text x="32" y="27" text-anchor="middle" font-family="Courier New, monospace" font-size="18" fill="#ffe600">BM</text>
  <circle cx="18" cy="45" r="4" fill="#ff2bd6"/>
  <circle cx="32" cy="45" r="4" fill="#00eaff"/>
  <circle cx="46" cy="45" r="4" fill="#39ff14"/>
</svg>"""


def social_card_svg_markup() -> str:
    return """<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" role="img" aria-label="Blame Machine social card">
  <defs>
    <radialGradient id="glowTop" cx="50%" cy="0%" r="70%">
      <stop offset="0%" stop-color="#00eaff" stop-opacity="0.22"/>
      <stop offset="55%" stop-color="#00eaff" stop-opacity="0"/>
    </radialGradient>
    <radialGradient id="glowBottom" cx="50%" cy="100%" r="70%">
      <stop offset="0%" stop-color="#ff2bd6" stop-opacity="0.16"/>
      <stop offset="60%" stop-color="#ff2bd6" stop-opacity="0"/>
    </radialGradient>
    <filter id="softGlow">
      <feGaussianBlur stdDeviation="8" result="blur"/>
      <feMerge>
        <feMergeNode in="blur"/>
        <feMergeNode in="SourceGraphic"/>
      </feMerge>
    </filter>
  </defs>
  <rect width="1200" height="630" fill="#050505"/>
  <rect width="1200" height="630" fill="url(#glowTop)"/>
  <rect width="1200" height="630" fill="url(#glowBottom)"/>
  <rect x="42" y="42" width="1116" height="546" rx="16" fill="none" stroke="#39ff14" stroke-width="6"/>
  <text x="600" y="175" text-anchor="middle" font-family="Courier New, monospace" font-size="78" fill="#ffe600" filter="url(#softGlow)">★ BLAME MACHINE ★</text>
  <text x="600" y="286" text-anchor="middle" font-family="Courier New, monospace" font-size="36" fill="#00eaff">INSERT COIN. BLAME THE MACHINE.</text>
  <text x="600" y="372" text-anchor="middle" font-family="Courier New, monospace" font-size="34" fill="#ff2bd6">BLAME ME FOR BAD DECISIONS.</text>
  <rect x="265" y="430" width="670" height="92" rx="10" fill="rgba(0,0,0,0.0)" stroke="#ffe600" stroke-width="4"/>
  <text x="600" y="486" text-anchor="middle" font-family="Courier New, monospace" font-size="28" fill="#ffe600">THE JOYSTICK SAYS BURGERS.</text>
</svg>"""


@app.before_request
def protect_api_posts():
    if request.method != "POST" or not request.path.startswith("/api/"):
        return None
    if same_origin_api_request():
        if (
            turnstile_enabled()
            and not trusted_identity_headers_present()
            and request.path in {"/api/category", "/api/save", "/api/decide", "/api/clear"}
            and not has_valid_turnstile_clearance()
        ):
            token = payload_value("turnstile_token").strip()
            if not token:
                return turnstile_required_response()

            verified, error_codes = verify_turnstile_token(token)
            if not verified:
                app.logger.warning(
                    "TURNSTILE_REJECT path=%r ip=%r errors=%r ua=%r",
                    request.path,
                    client_ip(),
                    error_codes,
                    request.headers.get("User-Agent", ""),
                )
                return turnstile_required_response()

            g.pending_turnstile_cookie = issue_turnstile_clearance()
        return None

    identity = current_identity()
    app.logger.warning(
        "API_POST_REJECT path=%r expected_origin=%r origin=%r referer=%r sec_fetch_site=%r ua=%r",
        request.path,
        expected_request_origin(),
        request.headers.get("Origin", ""),
        request.headers.get("Referer", ""),
        request.headers.get("Sec-Fetch-Site", ""),
        request.headers.get("User-Agent", ""),
    )
    return jsonify(home_state(identity, error="forbidden")), 403


@app.route("/")
@app.route("/game")
def home():
    return render_template_string(render_game_html())


@app.route("/favicon.svg")
def favicon_svg():
    return svg_response(favicon_svg_markup(), cache_seconds=86400)


@app.route("/favicon.ico")
def favicon_ico():
    return redirect(url_for("favicon_svg"), code=302)


@app.route("/social-card.svg")
def social_card():
    return svg_response(social_card_svg_markup(), cache_seconds=86400)


@app.route("/apple-touch-icon.png")
def apple_touch_icon():
    if APPLE_TOUCH_ICON_PATH.exists():
        response = Response(APPLE_TOUCH_ICON_PATH.read_bytes(), mimetype="image/png")
        response.headers["Cache-Control"] = "public, max-age=86400"
        return response
    return redirect(url_for("favicon_svg"), code=302)


@app.route("/social-card.png")
def social_card_png():
    if SOCIAL_CARD_PNG_PATH.exists():
        response = Response(SOCIAL_CARD_PNG_PATH.read_bytes(), mimetype="image/png")
        response.headers["Cache-Control"] = "public, max-age=86400"
        return response
    return redirect(url_for("social_card"), code=302)


@app.route("/api/state", methods=["GET"])
def api_state():
    identity = current_identity()
    view = request.args.get("view", "").strip().lower()
    category = norm(request.args.get("category", ""))
    mode = request.args.get("mode", "ready").strip().lower()

    if view == "clear":
        return jsonify(clear_state(identity))

    if view == "category" and category:
        if not is_valid_category_name(category):
            return jsonify(home_state(identity, error="invalid")), 400
        if mode not in {"edit", "ready", "result"}:
            mode = "ready"
        return jsonify(category_state(identity, category, mode))

    return jsonify(home_state(identity))


@app.route("/api/category", methods=["POST"])
def api_category():
    identity = current_identity()
    config = load_config(identity)
    category = norm(payload_value("category"))

    if not category:
        return jsonify(home_state(identity, error="empty_category")), 400
    if not is_valid_category_name(category):
        return jsonify(home_state(identity, error="invalid")), 400
    if is_blocked_entry(category):
        return jsonify(home_state(identity, error="blocked")), 400
    if config.has_section(category):
        return jsonify(home_state(identity, error="duplicate")), 409

    ensure_category(config, category)
    save_config(config, identity)
    return jsonify(category_state(identity, category, mode="edit"))


@app.route("/api/save", methods=["POST"])
def api_save():
    identity = current_identity()
    config = load_config(identity)
    category = norm(payload_value("category"))
    if not is_valid_category_name(category):
        return jsonify(home_state(identity, error="invalid")), 400
    ensure_category(config, category)

    raw_choices = payload_value("choices").strip()
    parsed_choices = parse_csv(raw_choices)
    duplicates = duplicate_choices(parsed_choices)
    if duplicates:
        return jsonify(category_state(identity, category, mode="edit", error="duplicate_choices"))
    choices = dedupe_choices(parsed_choices)

    for choice in choices:
        if is_blocked_entry(choice):
            return jsonify(category_state(identity, category, mode="edit", error="blocked"))

    config[category]["choices"] = ", ".join(choices)
    save_config(config, identity)
    return jsonify(category_state(identity, category, mode="ready"))


@app.route("/api/decide", methods=["POST"])
def api_decide():
    identity = current_identity()
    config = load_config(identity)
    category = norm(payload_value("category"))
    if not is_valid_category_name(category):
        return jsonify(home_state(identity, error="invalid")), 400
    ensure_category(config, category)

    choices = parse_csv(config[category].get("choices", ""))
    now = time.time()

    if len(choices) < 2:
        return jsonify(
            category_state(
                identity,
                category,
                mode="result",
                result="ONE CHOICE? REALLY?",
                used_wildcard=False,
                result_meta="COME BACK WHEN YOU'VE GIVEN THE MACHINE AN ACTUAL DECISION.",
            )
        )

    last_choice, last_ts = last_pick_info(config, category)
    if last_choice and (now - last_ts) <= FAST_REROLL_SECONDS:
        state = category_state(
                identity,
                category,
                mode="result",
                result=f"{last_choice} - did you think i'd change my mind so quickly!",
                used_wildcard=False,
                result_meta=f"FAST REROLL LOCK: {FAST_REROLL_SECONDS} SECONDS",
            )
        state["result_kind"] = "too_soon"
        return jsonify(state)

    use_wildcard = random.random() < WILDCARD_CHANCE
    if use_wildcard:
        state = category_state(
                identity,
                category,
                mode="result",
                result=random_wildcard(category),
                used_wildcard=True,
                result_meta=f"{len(choices)} PICKS IN THE MACHINE",
            )
        state["result_kind"] = "wildcard"
        return jsonify(state)

    recent_history = recent_choice_history(config, category)
    available_choices = choice_pool(choices, recent_history)
    selected = random.choice(available_choices)
    config[category]["last_choice"] = selected
    config[category]["last_ts"] = str(now)
    update_recent_history(config, category, selected)
    save_config(config, identity)

    meta = f"{len(choices)} UNIQUE PICKS | AVOIDING LAST {min(len(recent_history), RECENT_HISTORY_LIMIT)}"
    state = category_state(
            identity,
            category,
            mode="result",
            result=funny_choice_result(selected),
            used_wildcard=False,
            result_meta=meta,
        )
    state["result_kind"] = "normal"
    return jsonify(state)


@app.route("/api/clear", methods=["POST"])
def api_clear():
    identity = current_identity()
    ini_path = config_path_for_identity(identity)
    if ini_path.exists():
        ini_path.unlink()
    return jsonify(home_state(identity))


@app.route("/sound.wav")
def sound_wav():
    return Response(
        AUDIO_DEBUG_BEEP_WAV,
        mimetype="audio/wav",
        headers={
            "Content-Disposition": "inline; filename=blame-machine-debug.wav",
            "Cache-Control": "no-store, max-age=0",
        },
    )


@app.route("/audio/coin-primer.mp3")
def audio_coin_primer():
    if not COIN_PRIMER_MP3:
        return Response(status=404)
    return Response(
        COIN_PRIMER_MP3,
        mimetype="audio/mpeg",
        headers={
            "Content-Disposition": "inline; filename=blame-machine-coin-primer.mp3",
            "Cache-Control": "no-store, max-age=0",
        },
    )


@app.route("/audio/loop-home.wav")
def audio_loop_home():
    return Response(
        AUDIO_LOOP_HOME_WAV,
        mimetype="audio/wav",
        headers={"Cache-Control": "public, max-age=300"},
    )


@app.route("/audio/loop-category.wav")
def audio_loop_category():
    return Response(
        AUDIO_LOOP_CATEGORY_WAV,
        mimetype="audio/wav",
        headers={"Cache-Control": "public, max-age=300"},
    )


@app.route("/audio/cue-coin.wav")
def audio_cue_coin():
    return Response(
        AUDIO_COIN_WAV,
        mimetype="audio/wav",
        headers={"Cache-Control": "public, max-age=300"},
    )


@app.route("/audio/cue-reveal.wav")
def audio_cue_reveal():
    return Response(
        AUDIO_REVEAL_WAV,
        mimetype="audio/wav",
        headers={"Cache-Control": "public, max-age=300"},
    )


@app.route("/audio/cue-wonk.wav")
def audio_cue_wonk():
    return Response(
        AUDIO_WONK_WAV,
        mimetype="audio/wav",
        headers={"Cache-Control": "public, max-age=300"},
    )


@app.route("/sound")
def sound_page():
    if not audio_debug_allowed():
        return ("Not found", 404)
    return render_template_string(
        """<!doctype html>
<html>
<head>
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Blame Machine Sound File</title>
  <style>
    body {
      margin: 0;
      min-height: 100vh;
      background: #050505;
      color: #ffe600;
      font-family: "Courier New", "Lucida Console", monospace;
      padding: 24px;
    }
    .wrap {
      max-width: 720px;
      margin: 0 auto;
    }
    .card {
      background: #0d0d0d;
      border: 3px solid #39ff14;
      box-shadow: 0 0 18px rgba(57,255,20,0.18);
      padding: 24px;
      margin-top: 24px;
      text-align: center;
    }
    .title {
      color: #ffe600;
      font-size: 22px;
      line-height: 1.6;
      text-align: center;
      margin: 12px 0;
      text-shadow: 0 0 8px rgba(255,230,0,0.55);
    }
    .sub {
      color: #00eaff;
      text-align: center;
      line-height: 1.8;
      margin-bottom: 18px;
    }
    .build {
      color: #00eaff;
      text-align: center;
      font-size: 11px;
      opacity: 0.85;
      margin-bottom: 16px;
    }
    audio {
      width: 100%;
      margin: 20px 0;
    }
    a {
      color: #ff2bd6;
      text-decoration: none;
    }
  </style>
</head>
<body>
  <div class="wrap">
    <div class="title">★ BLAME MACHINE SOUND FILE ★</div>
    <div class="build">""" + BUILD_LABEL + """</div>
    <div class="sub">THIS IS THE RAW WAV TEST. NO WEB AUDIO. JUST A FILE.</div>
    <div class="card">
      <audio controls autoplay playsinline preload="auto" src=\"""" + url_for("sound_wav") + """?ts=""" + str(int(time.time())) + """\"></audio>
      <div><a href=\"""" + url_for("sound_wav") + """?ts=""" + str(int(time.time())) + """\">OPEN RAW SOUND FILE</a></div>
    </div>
  </div>
</body>
</html>"""
    )


@app.route("/audio-debug")
def audio_debug():
    if not audio_debug_allowed():
        abort(404)
    return render_template_string("""<!doctype html>
<html>
<head>
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Blame Machine Audio Debug</title>
  <style>
    :root {
      --bg: #050505;
      --green: #39ff14;
      --pink: #ff2bd6;
      --cyan: #00eaff;
      --yellow: #ffe600;
      --panel: #0d0d0d;
    }
    * { box-sizing: border-box; }
    body {
      margin: 0;
      min-height: 100vh;
      padding: 16px;
      background:
        radial-gradient(circle at top, rgba(0,234,255,0.10), transparent 30%),
        radial-gradient(circle at bottom, rgba(255,43,214,0.08), transparent 30%),
        var(--bg);
      color: var(--green);
      font-family: "Courier New", "Lucida Console", monospace;
    }
    .wrap {
      max-width: 760px;
      margin: 0 auto;
    }
    .card {
      background: var(--panel);
      border: 3px solid var(--green);
      box-shadow: 0 0 18px rgba(57,255,20,0.18);
      padding: 18px;
      margin: 18px 0;
    }
    .title {
      text-align: center;
      color: var(--yellow);
      font-size: 20px;
      line-height: 1.6;
      text-shadow: 0 0 8px rgba(255,230,0,0.6);
      margin: 16px 0 8px;
    }
    .build {
      text-align: center;
      color: var(--cyan);
      font-size: 8px;
      line-height: 1.6;
      margin: 0 0 18px;
      opacity: 0.85;
      letter-spacing: 0.04em;
    }
    .sub {
      text-align: center;
      color: var(--cyan);
      font-size: 11px;
      line-height: 1.8;
      margin-bottom: 16px;
    }
    .button-stack {
      display: grid;
      gap: 12px;
    }
    button {
      width: 100%;
      background: #000;
      color: var(--pink);
      border: 3px solid var(--pink);
      padding: 14px;
      font-family: "Courier New", "Lucida Console", monospace;
      font-size: 11px;
      line-height: 1.4;
      cursor: pointer;
      box-shadow: 0 0 14px rgba(255,43,214,0.18);
    }
    button:hover, button:focus-visible {
      background: var(--pink);
      color: #000;
    }
    .secondary {
      color: var(--cyan);
      border-color: var(--cyan);
      box-shadow: 0 0 14px rgba(0,234,255,0.18);
    }
    .secondary:hover, .secondary:focus-visible {
      background: var(--cyan);
      color: #000;
    }
    .tail, .state {
      white-space: pre-wrap;
      font-size: 10px;
      line-height: 1.8;
    }
    .tail { color: var(--yellow); }
    .state { color: #9befff; }
    .label {
      color: var(--yellow);
      font-size: 10px;
      margin-bottom: 10px;
      line-height: 1.6;
    }
    @media (max-width: 640px) {
      body { padding: 12px; }
      .title { font-size: 16px; }
      .sub, .tail, .state, button { font-size: 10px; }
    }
  </style>
</head>
<body>
  <div class="wrap">
    <div class="card">
      <div class="title">★ BLAME MACHINE AUDIO DEBUG ★</div>
      <div class="build">""" + BUILD_LABEL + """</div>
      <div class="sub">
        A/B AUDIO HARNESS.<br>
        SAME HOST. SAME CONTAINER. NO GAME FLOW.
      </div>
      <div class="button-stack">
        <button id="btn7">PLAY WAV</button>
        <button id="btn8" class="secondary">PLAY JS BEEP</button>
        <button id="btn6" class="secondary">RESET PAGE STATE</button>
      </div>
    </div>

    <div class="card">
      <div class="label">EVENT TAIL</div>
      <div id="tail" class="tail">READY</div>
    </div>

    <div class="card">
      <div class="label">LIVE STATE</div>
      <div id="state" class="state">ctx: none</div>
    </div>
  </div>

  <script>
    let ctx = null;
    let htmlAudio = null;
    const tail = document.getElementById('tail');
    const state = document.getElementById('state');
    const lines = ['READY'];
    const htmlAudioSrc = """ + repr(url_for("sound_wav")) + """ + '?ts=' + Date.now();

    function postServerLog(message) {
      const payload = {
        message,
        ctxState: ctx ? ctx.state : 'none',
        ctxTime: ctx ? Number(ctx.currentTime.toFixed(3)) : null,
        htmlPaused: htmlAudio ? htmlAudio.paused : null,
        htmlReadyState: htmlAudio ? htmlAudio.readyState : null,
        htmlCurrentTime: htmlAudio ? Number(htmlAudio.currentTime.toFixed(3)) : null,
        ua: navigator.userAgent
      };
      fetch('/audio-debug/log', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'same-origin',
        keepalive: true,
        body: JSON.stringify(payload)
      }).catch(() => {});
    }

    function stamp(message) {
      const now = new Date();
      const hh = String(now.getHours()).padStart(2, '0');
      const mm = String(now.getMinutes()).padStart(2, '0');
      const ss = String(now.getSeconds()).padStart(2, '0');
      lines.push(`${hh}:${mm}:${ss} ${message}`);
      while (lines.length > 14) lines.shift();
      tail.textContent = lines.join('\\n');
      postServerLog(message);
      refreshState();
    }

    function refreshState() {
      if (!ctx) {
        state.textContent = [
          'ctx: none',
          `html.paused: ${htmlAudio ? htmlAudio.paused : 'n/a'}`,
          `html.readyState: ${htmlAudio ? htmlAudio.readyState : 'n/a'}`,
          `html.currentTime: ${htmlAudio ? htmlAudio.currentTime.toFixed(3) : 'n/a'}`
        ].join('\\n');
        return;
      }
      state.textContent = [
        `ctx.state: ${ctx.state}`,
        `sampleRate: ${ctx.sampleRate}`,
        `currentTime: ${ctx.currentTime.toFixed(3)}`,
        `baseLatency: ${ctx.baseLatency ?? 'n/a'}`,
        `outputLatency: ${ctx.outputLatency ?? 'n/a'}`,
        `html.paused: ${htmlAudio ? htmlAudio.paused : 'n/a'}`,
        `html.readyState: ${htmlAudio ? htmlAudio.readyState : 'n/a'}`,
        `html.currentTime: ${htmlAudio ? htmlAudio.currentTime.toFixed(3) : 'n/a'}`
      ].join('\\n');
    }

    function ensureHtmlAudio() {
      if (htmlAudio) return htmlAudio;
      htmlAudio = new Audio(htmlAudioSrc);
      htmlAudio.preload = 'auto';
      htmlAudio.playsInline = true;
      htmlAudio.addEventListener('play', () => stamp('HTML PLAY'));
      htmlAudio.addEventListener('pause', () => stamp('HTML PAUSE'));
      htmlAudio.addEventListener('ended', () => stamp('HTML ENDED'));
      htmlAudio.addEventListener('error', () => {
        stamp(`HTML ERROR ${htmlAudio?.error?.message || htmlAudio?.error?.code || 'unknown'}`);
      });
      return htmlAudio;
    }

    function createContext() {
      if (!ctx) {
        const AudioCtx = window.AudioContext || window.webkitAudioContext;
        ctx = new AudioCtx();
        ctx.onstatechange = () => {
          stamp(`STATECHANGE -> ${ctx.state}`);
        };
        stamp(`CREATE OK state=${ctx.state} sr=${ctx.sampleRate}`);
      }
      refreshState();
      return ctx;
    }

    async function resumeContext() {
      const audio = createContext();
      stamp(`RESUME BEFORE ${audio.state}`);
      try {
        await audio.resume();
        stamp(`RESUME AFTER ${audio.state}`);
      } catch (error) {
        stamp(`RESUME ERR ${error?.message || error}`);
      }
      refreshState();
      return audio;
    }

    function playPair(lowFreq, highFreq, gainValue = 0.9, duration = 0.45) {
      if (!ctx) {
        stamp('PLAY ERR no context');
        return;
      }
      const g = ctx.createGain();
      g.gain.value = gainValue;
      g.connect(ctx.destination);

      const low = ctx.createOscillator();
      low.type = 'triangle';
      low.frequency.value = lowFreq;
      low.connect(g);

      const high = ctx.createOscillator();
      high.type = 'square';
      high.frequency.value = highFreq;
      high.connect(g);

      low.start();
      high.start();
      low.stop(ctx.currentTime + duration);
      high.stop(ctx.currentTime + duration);
      stamp(`PLAY low=${lowFreq} high=${highFreq} state=${ctx.state}`);
      refreshState();
    }

    async function playHtmlAudio() {
      const audio = ensureHtmlAudio();
      audio.pause();
      audio.currentTime = 0;
      try {
        await audio.play();
        stamp('HTML PLAY REQUEST OK');
      } catch (error) {
        stamp(`HTML PLAY ERR ${error?.message || error}`);
      }
      refreshState();
    }

    document.getElementById('btn7').addEventListener('click', async () => {
      stamp('WAV START');
      await playHtmlAudio();
    });

    document.getElementById('btn8').addEventListener('click', async () => {
      stamp('JS BEEP START');
      await resumeContext();
      playPair(440, 880, 0.8, 0.4);
    });

    document.getElementById('btn6').addEventListener('click', () => {
      stamp('RESET PAGE REQUEST');
      window.location.href = '/audio-debug?ts=' + Date.now();
    });

    document.addEventListener('touchstart', () => {
      stamp('DOC TOUCHSTART');
    }, { passive: true, capture: true });

    document.addEventListener('pointerdown', () => {
      stamp('DOC POINTERDOWN');
    }, { passive: true, capture: true });

    window.addEventListener('pageshow', () => {
      stamp('PAGESHOW');
    });

    document.addEventListener('visibilitychange', () => {
      stamp(`VISIBILITY ${document.visibilityState}`);
    });

    window.setInterval(refreshState, 500);
    refreshState();
  </script>
</body>
</html>""");


@app.route("/audio-debug/log", methods=["POST"])
def audio_debug_log():
    if not audio_debug_allowed():
        abort(404)
    if not same_origin_api_request():
        return ("", 403)
    data = request.get_json(silent=True) or {}
    print(
        "AUDIO_DEBUG "
        f"client={request.headers.get('CF-Connecting-IP') or request.remote_addr!r} "
        f"message={bounded_log_value(data.get('message'))!r} "
        f"ctx_state={data.get('ctxState')!r} "
        f"ctx_time={data.get('ctxTime')!r} "
        f"html_paused={data.get('htmlPaused')!r} "
        f"html_ready={data.get('htmlReadyState')!r} "
        f"html_time={data.get('htmlCurrentTime')!r} "
        f"ua={bounded_log_value(data.get('ua'), limit=180)!r}",
        flush=True,
    )
    return ("", 204)


def open_browser() -> None:
    browser_url = app.config.get("BROWSER_URL")
    if browser_url:
        webbrowser.open(browser_url)


def local_ip_address() -> str:
    candidates: list[str] = []

    try:
        hostname_ips = socket.gethostbyname_ex(socket.gethostname())[2]
        candidates.extend(hostname_ips)
    except OSError:
        pass

    probe = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        # This does not require external internet access; it only asks the OS
        # which interface would route outward if one exists.
        probe.connect(("10.255.255.255", 1))
        candidates.append(probe.getsockname()[0])
    except OSError:
        pass
    finally:
        probe.close()

    for candidate in candidates:
        if candidate and not candidate.startswith("127."):
            return candidate

    return "127.0.0.1"


def configured_port() -> int:
    raw_port = os.environ.get("PORT", str(DEFAULT_PORT)).strip()
    try:
        return int(raw_port)
    except ValueError:
        return DEFAULT_PORT


def configured_public_url() -> str:
    return os.environ.get("PUBLIC_URL", DEFAULT_PUBLIC_URL).strip().rstrip("/")


def should_open_browser() -> bool:
    if os.environ.get("BLAME_MACHINE_NO_BROWSER", "").strip() == "1":
        return False
    return not bool(configured_public_url())


def configure_access() -> tuple[str, str, str]:
    port = configured_port()
    public_url = configured_public_url()
    ip_address = local_ip_address()

    if public_url:
        browser_url = public_url
        access_url = public_url
        access_message = "OPEN THIS FROM ANY PHONE, ANYWHERE."
    else:
        browser_url = f"http://{DEFAULT_BROWSER_HOST}:{port}"
        if ip_address == "127.0.0.1":
            access_url = browser_url
            access_message = "NO LAN DETECTED. PLAY ON THIS COMPUTER."
        else:
            access_url = f"http://{ip_address}:{port}"
            access_message = "PLAY ON THIS COMPUTER OR A PHONE/TABLET ON THE SAME WI-FI."

    app.config["ACCESS_URL"] = access_url
    app.config["ACCESS_MESSAGE"] = access_message
    app.config["BROWSER_URL"] = browser_url
    return ip_address, access_url, access_message


def print_access_banner(ip_address: str, access_url: str, access_message: str) -> None:
    print("")
    print("Blame Machine is running.")
    print(f"Open on this computer: {app.config['BROWSER_URL']}")
    print(access_message)
    print(f"Access URL: {access_url}")
    print("")


if __name__ == "__main__":
    ip_address, access_url, access_message = configure_access()
    print_access_banner(ip_address, access_url, access_message)
    if should_open_browser():
        Timer(1.0, open_browser).start()
    app.run(host=DEFAULT_LAN_HOST, port=configured_port(), debug=False, use_reloader=False)

Arcade Hub blame-machine · app.py · 4,060 lines 5830b19 · 25 July 2026 161 commits captured 7 September 2026 raw file

02

Incident management — a rejected design reached production

incident record · redacted

A design the operator had rejected in writing was committed by an AI agent, deployed, and served production traffic for 6 hours 43 minutes. It was caught by a version string on a diagnostic panel. The same day a hypervisor node came back holding state seven hours old, so the host's own history of what had run there was now wrong as well. Nothing was lost anyway: the code existed locally and on the forge, which is what made the sequence reconstructable at all.

The commit carried a Co-Authored-By trailer naming the agent that wrote it. The rejection predated all of it; evidence was captured before remediation, and the revert was a normal commit, so the history still shows what reached production. Ordinary controls, in a lab, with an operator of one.

Working agreement:

The recovery, in shape. Two branches from one base is why the revert was clean and nothing had to be rewritten.

<base>                     last good commit
  ├── <rejected>           committed, deployed, live 6h43m
  │     └── <revert>      normal revert commit · no force-push
  └── <session work>       branched from <base>
        └── cherry-picked onto <revert> → <final>

# A rejected design reached production

*Redacted from an incident record written at the time, with evidence captured
before any corrective action was taken.*

## What happened

An implementation the operator had already rejected — in writing, on specific
points — was committed to an internal service, pushed to the remote branch, built,
and deployed. Thirty-six minutes from commit to serving production traffic.

It ran for 6 hours 43 minutes.

Those were not idle hours. The operator and an agent worked the whole time — on a
branch taken from the last good commit, which is to say on a correct base, against
a production that no longer matched it. Five hours into that window, a full session
of legitimate work was committed. Neither of them knew what was actually running.

The rejected commit carried a `Co-Authored-By` trailer naming the AI agent that
wrote it.

The rejection had not been ambiguous or informal. Among the defects it identified
were a per-host field that reintroduced a previously removed authentication path,
synchronous execution of operations that can run for minutes, and a capability
probe skipped for one class of host. All three were present in what shipped.

## How it was found

Not by a test, not by review, and not by any alarm.

The operator asked to check a build-hash string displayed in a mobile app's
System Information panel against the service's git history. **That hash did not
exist in the local clone at all.** A fetch revealed it as the tip of
the remote branch — one commit ahead of the base the current session's work had
branched from, unfetched and therefore invisible until that moment.

A version string in a diagnostic panel, read by a human who wondered whether it
matched.

The working agreement requires that string to exist. **§8 — every build carries a
visible serial**: *until a product ships, every running build states which build it
is, somewhere a person can read without a debugger.* The stated reason is narrower
than what it caught here — without a serial, "is the thing I am looking at the thing
I just changed?" has no answer. On this day it answered a different question, which
nobody had thought to ask: *is the thing running the thing we approved?*

## The process failure being recorded

Earlier in the same session, the operator's own work plan had a step: revert all
rejected changes before adding new code. That step was checked. Three separate
searches were run across history, reflog, and stashes. All three came back empty,
and the conclusion reported to the operator was **"nothing to revert — it was
never committed."**

That conclusion was wrong, and the interesting part is why. The verification
method was sound in principle — a content search against a complete, current view
of history is a real check. It was wrong because **the view was stale**. The
remote had already diverged from local, and the check never looked at it at all.

A clean local search is not evidence of anything about production. It is evidence
about a copy.

Two sections of the working agreement describe this failure without having predicted
it. **§5.4 — one source of truth**: *a reader with its own copy is a cache, and a
cache must be able to say when it is stale.* A local clone that has diverged from the
remote is exactly that, and it could not say. **§6 — verification is behavior, not
state**: *re-read state after a change; do not reuse a snapshot taken before it. A
stale copy will confirm whatever you already believed.* The search was a snapshot,
and it confirmed what was already believed.

And a wrong answer at the start of a session is not a wrong answer at the start of
a session. It licensed five more hours of work on a false premise about what
production was running.

### The standing correction

Every production-state comparison in the project now begins by fetching, and must
explicitly compare three things before any conclusion is drawn about what is or is
not present, committed, reverted, or deployed:

1. the local branch
2. the remote branch
3. the revision actually running, as reported by the running service itself

A clean working tree is not one of the three.

## Why the deployment did not change the decision

Per explicit operator direction, the commit was treated as a rejected
implementation remnant **regardless of how it reached the branch, and regardless
of the fact that it was currently deployed and healthy**.

This is the part worth stating plainly. A rejected design does not become approved
by being in production. Discovering it live is a reason to remove it, not a reason
to reconsider it — and "it is already running and nothing is broken" is the most
available argument for leaving it there.

## How it was resolved

Deliberately unremarkable, and that was the point:

- A fresh branch from the remote branch. **No force-push, no history rewrite.**
- A normal revert commit. Clean, no conflicts.
- Verified by searching the *deployed image*, not the local checkout, to confirm
  none of the three rejected elements survived.
- The session's legitimate work cherry-picked onto the reverted history — no merge
  of the two sibling histories — and re-verified afterwards to confirm nothing
  rejected came back with it.
- Full test suite green.
- Both pushes were clean fast-forwards.

The recovery left the record intact. Someone reading that history later can see
that a rejected design reached production and was removed, rather than finding a
history in which it never happened.

## The other failure, the same day

A hypervisor node in the cluster went down.

High availability did its job: the cluster stayed up, workloads moved, service
continued. But when the failed node came back, it came back with **replicated
state roughly seven hours old**. Everything that node had held since the last
successful replication was simply gone.

Nothing was lost.

Not because the replica was good — it was seven hours stale. Because of the
sequence the working agreement already required: *code locally, push to the
forge, pull on the host, build there*. The host is never the origin of anything.
It holds a checkout and an image built from that checkout, and both are
reproducible from the forge at any time.

A stale hypervisor replica, under that rule, costs a rebuild. Under the
alternative — deploying by copying files from a workstation, or building
somewhere the source does not live — seven hours of replication lag is seven
hours of unrecoverable work.

The rule is **§2 — deployment sequence**, and it exists for a different reason. It
is written to prevent split-brain: a host holding source that does not match what is
running. Its operative clause is blunt — *if it is not in the forge, it does not
exist on the host* — and the constraint that follows is that nothing is ever deployed
by copying files from a workstation.

That it also made a hypervisor failure into a non-event is the kind of dividend a
good constraint pays without being asked.

**Two failures, one day, opposite lessons.** One was a rule followed correctly
against a stale view, producing a confident wrong answer that stood for hours.
The other was a rule followed correctly against a stale replica, producing no
loss at all. The difference is not the staleness — both were stale. It is that
the deployment rule assumes its inputs can vanish, and the verification did not
assume its view could be wrong.

## What this is evidence of

An agent produced work that was competent on its face — tests included, a
coherent commit message, a design that reads as considered — and it was the
design that had already been rejected in writing. The failure was not that
the code was bad. It is that *plausible* and *approved* are different properties,
and only one of them is visible in a diff.

The catch did not come from the tooling. It came from the operator asking what a
version string meant.

## The part that is not in the timeline

There was no panic, and no code was lost.

That is the whole return on the preparation, and it is invisible in any account
of what happened — because what did not happen leaves no record. A rejected
design in production and a hypervisor holding a seven-hour-old replica are both
the kind of thing that turns into a long night. Neither did.

The reason is that the questions a person needs answered at that moment had
already been answered in advance, by design rather than by recall. *What is
actually running?* — the build says so, on a screen, without a debugger. *Where
does the real copy live?* — the forge, never the host. *Is this approved?* — the
rejection was written down, in specifics, before any of it happened.

None of those were figured out during the incident. They were decisions made
earlier, on ordinary days, about what ought to be knowable later. That is the
entire argument for writing an operating agreement before you need one: not that
it prevents failure, but that it decides in advance which failures are allowed
to become emergencies.

**This is the 3am question answered in practice.** Not what should we log, but
what would we want already recorded when something is clearly wrong — because by
then it is too late to add it. The answer, on this day, was: enough.

---

### The four rules this day exercised

| | Rule | What it did here |
|---|---|---|
| §2 | Deployment sequence | A seven-hour-stale hypervisor replica cost nothing |
| §5.4 | One source of truth | Named the defect: a cache that could not say it was stale |
| §6 | Verification is behavior, not state | Named the method failure: a snapshot confirming a belief |
| §8 | Every build carries a visible serial | Caught it |

None of these were written for this day. Three of them describe it anyway, which
is the point of writing rules from failures rather than from principles — the
next failure is rarely the one you were thinking of, and a rule aimed at a
specific accident is worth less than one aimed at the shape of accidents.

§8 is the exception worth naming. It was written to answer *is this the build I
just changed?* It answered *is this the build we approved?* — a question nobody
had posed. A rule that only ever does its stated job is a rule that has not
earned its keep.

None of which makes 2am on a virtual bridge with an AI agent any fun.

Working agreement incident record · 209 lines · redacted captured 7 September 2026 raw file

03

iMessage Locker — the provider boundary

code: full · unredactedrepo: not published

The goal was to add AI drafting to a product someone else built, without weakening the privacy contract that product had already made with its users. Nobody agreed to a model reading their messages when they agreed to use Messages. So the contract is inherited rather than authored, and the feature has to hold it at a layer the user cannot see or verify.

The class that reads the Messages database is not the class that talks to a model, which is what makes the boundary checkable rather than promised. Every provider request passes through validatedURL at line 273: one door, shut by default rather than by configuration.

/*
 This file implements the native service layer: local configuration storage,
 System Settings launch, provider probing/drafting, Messages database reads/sends,
 queue persistence, and the engine facade used by the dashboard client.

 First-level inventory:
 NativeServiceError
 NativeStorageService
 NativeSystemSettings
 NativeProviderService
 Optional<String> extension
 nativeCloudText
 NativeMessagesService
 NativeQueueService
 NativeEngineService

 NativeServiceError gives failure categories. Storage and settings handle local
 state and permissions; provider, Messages, and queue services own integrations.
 NativeEngineService composes them into the app's status/current/send/pause API.
*/
import AppKit
import Foundation
import SQLite3

/// User-facing errors shared by storage, provider, Messages, queue, and engine services.
/// Localized descriptions are intentionally actionable because they reach the dashboard.
enum NativeServiceError: LocalizedError {
    case invalidProviderURL
    case databaseUnavailable(String)
    case providerUnavailable(String)
    case sendFailed(String)

    var errorDescription: String? {
        switch self {
        case .invalidProviderURL: return "The provider URL is not allowed."
        case .databaseUnavailable(let message): return message
        case .providerUnavailable(let message): return message
        case .sendFailed(let message): return message
        }
    }
}

/// Native owner of the local configuration file. Settings never leave the
/// app unless a user-selected provider needs them for a request.
/// Reads and atomically writes iML's local JSON configuration.
/// The service creates parent directories and replaces files through a temporary path.
final class NativeStorageService {
    let configURL: URL

    init(configURL: URL = FileManager.default.homeDirectoryForCurrentUser
        .appendingPathComponent(".imessage-agent/locker_config.json")) {
        self.configURL = configURL
    }

    func load() -> [String: Any] {
        guard let data = try? Data(contentsOf: configURL),
              let value = try? JSONSerialization.jsonObject(with: data),
              let object = value as? [String: Any] else { return [:] }
        return object
    }

    func save(_ values: [String: Any]) throws {
        let directory = configURL.deletingLastPathComponent()
        try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
        let data = try JSONSerialization.data(withJSONObject: values, options: [.prettyPrinted, .sortedKeys])
        let temporary = directory.appendingPathComponent(".locker_config.json.tmp")
        try data.write(to: temporary, options: .atomic)
        if FileManager.default.fileExists(atPath: configURL.path) {
            _ = try FileManager.default.replaceItemAt(configURL, withItemAt: temporary)
        } else {
            try FileManager.default.moveItem(at: temporary, to: configURL)
        }
        try FileManager.default.setAttributes([.posixPermissions: 0o600], ofItemAtPath: configURL.path)
    }
}

/// The Full Disk Access pane, by whichever URL this macOS answers.
///
/// Both the dashboard and the menu-bar launcher send the user here, so the list
/// of destinations lives in one place rather than being duplicated with a chance
/// of drifting apart.
/// Opens the macOS privacy/settings destinations required by onboarding.
/// It tries the precise deep link first and falls back to a general settings page.
enum NativeSystemSettings {
    static func openFullDiskAccess() {
        let destinations = [
            "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_AllFiles",
            "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles"
        ].compactMap(URL.init(string:))
        for destination in destinations where NSWorkspace.shared.open(destination) {
            return
        }
        NSWorkspace.shared.open(URL(fileURLWithPath: "/System/Applications/System Settings.app"))
    }
}

/// Provider transport owned by the native process. It performs health checks
/// without sending message content; draft generation can move to this same
/// client once the queue worker is switched over.
/// Probes configured local/self-hosted providers and generates reply drafts.
/// Results are cached briefly by configuration while network work remains asynchronous.
final class NativeProviderService {
    private let session: URLSession

    /// Last probe result, and what it was a probe of. Keyed on the whole
    /// configuration so changing the model or the URL in Settings invalidates it
    /// rather than reporting the previous endpoint's health.
    private var cachedReach: (key: String, reach: NativeProviderReach)?
    private var lastProbeAt: Date?
    private var probing = false
    private let reachLock = NSLock()
    private let staleAfter: TimeInterval = 10

    init(session: URLSession = .shared) { self.session = session }

    /// Synchronous, because status is read on every refresh and must not block.
    /// The answer is the last probe's; a stale one triggers a fresh probe in the
    /// background and is returned next time.
    func reach(for configuration: NativeProviderConfigurationRequest) -> NativeProviderReach {
        if let missing = NativeProviderReach.missingConfiguration(configuration) {
            reachLock.lock()
            cachedReach = nil
            reachLock.unlock()
            return .unconfigured(missing)
        }

        // Cloud is not probed. See NativeProviderReach.assumed.
        if configuration.provider == "cloud" {
            return .assumed(configuration.model.isEmpty ? "Cloud" : configuration.model)
        }

        let key = Self.key(for: configuration)
        reachLock.lock()
        let known = cachedReach?.key == key ? cachedReach?.reach : nil
        let stale = cachedReach?.key != key
            || lastProbeAt.map { Date().timeIntervalSince($0) > staleAfter } ?? true
        let shouldProbe = stale && !probing
        if shouldProbe { probing = true }
        reachLock.unlock()

        if shouldProbe { probe(configuration, key: key) }
        return known ?? .checking
    }

    private func probe(_ configuration: NativeProviderConfigurationRequest, key: String) {
        func finish(_ reach: NativeProviderReach) {
            reachLock.lock()
            cachedReach = (key, reach)
            lastProbeAt = Date()
            probing = false
            reachLock.unlock()
            NotificationCenter.default.post(name: .imlNativeStatusDidChange, object: nil)
        }

        guard let endpoint = try? validatedURL(configuration.url, provider: configuration.provider) else {
            finish(.unconfigured("The provider URL is not valid for this provider."))
            return
        }
        var request = URLRequest(url: endpoint.appendingPathComponent("api/tags"))
        request.httpMethod = "GET"
        request.timeoutInterval = 5
        session.dataTask(with: request) { data, response, error in
            if let error {
                finish(.unreachable("Nothing answered at \(endpoint.absoluteString): \(error.localizedDescription)."))
                return
            }
            let code = (response as? HTTPURLResponse)?.statusCode ?? 0
            guard (200..<300).contains(code) else {
                finish(.unreachable("\(endpoint.absoluteString) returned HTTP \(code)."))
                return
            }
            let installed = NativeProviderReach.modelNames(from: data)
            guard !configuration.model.isEmpty else {
                finish(.ready("Connected"))
                return
            }
            // A model the provider does not have is the failure that used to
            // arrive as an empty draft, so it is checked rather than assumed
            // from a parseable response.
            if NativeProviderReach.contains(configuration.model, in: installed) {
                finish(.ready(configuration.model))
            } else {
                finish(.modelMissing(model: configuration.model, available: installed))
            }
        }.resume()
    }

    private static func key(for configuration: NativeProviderConfigurationRequest) -> String {
        [configuration.provider, configuration.url, configuration.model].joined(separator: "|")
    }

    func check(_ configuration: NativeProviderConfigurationRequest,
               completion: @escaping (Result<NativeProviderStatus, Error>) -> Void) {
        do {
            let endpoint = try validatedURL(configuration.url, provider: configuration.provider)
            var request = URLRequest(url: endpoint.appendingPathComponent("api/tags"))
            request.httpMethod = "GET"
            request.timeoutInterval = 5
            session.dataTask(with: request) { data, response, error in
                if let error { completion(.failure(NativeServiceError.providerUnavailable(error.localizedDescription))); return }
                let status = (response as? HTTPURLResponse)?.statusCode ?? 0
                guard (200..<300).contains(status) else {
                    completion(.failure(NativeServiceError.providerUnavailable("Provider returned HTTP \(status).")))
                    return
                }
                let hasModel = configuration.model.isEmpty || data.flatMap { try? JSONSerialization.jsonObject(with: $0) } != nil
                completion(.success(NativeProviderStatus(provider: configuration.provider, ready: hasModel, label: configuration.model.isEmpty ? "Connected" : configuration.model, detail: "Native provider connection verified.")))
            }.resume()
        } catch { completion(.failure(error)) }
    }

    func generateDraft(for message: NativeMessage,
                       configuration: NativeProviderConfigurationRequest,
                       persona: String,
                       currentDraft: String = "",
                       context: [NativeContextMessage] = [],
                       completion: @escaping (Result<String, Error>) -> Void) {
        do {
            let endpoint = try validatedURL(configuration.url, provider: configuration.provider)
            let isCloud = configuration.provider == "cloud"
            guard !isCloud || !configuration.apiKey.isEmpty else {
                completion(.failure(NativeServiceError.providerUnavailable("Cloud provider credentials are not configured.")))
                return
            }
            var request = URLRequest(url: isCloud ? endpoint : endpoint.appendingPathComponent("api/chat"))
            request.httpMethod = "POST"
            request.timeoutInterval = 120
            request.setValue("application/json", forHTTPHeaderField: "Content-Type")
            if isCloud { request.setValue("Bearer \(configuration.apiKey)", forHTTPHeaderField: "Authorization") }
            let instructions = persona.isEmpty ? "" : "Write in this voice: \(persona)\n"
            let prior = currentDraft.isEmpty ? "" : "\nCurrent draft to improve: \(currentDraft)"
            // Depth 8. 772501a measured depth 4 inventing what depth 8 does
            // not - a cinema showing absent from the thread, a reversal of who
            // was paying, a call time contradicting the one just agreed - at
            // 0.69 agreement against a 0.85 rule. The context keeps the model
            // on facts that are in the conversation.
            let lines = context.map { "\($0.isFromMe ? "Me:" : "Them:") \($0.text)" }
            let conversation = lines.isEmpty ? ""
                : "\nRecent conversation context, oldest first:\n" + lines.joined(separator: "\n") + "\n"
            let prompt = "\(instructions)Reply naturally to this incoming message. Return only the reply text, with no preamble.\(prior)\(conversation)\nIncoming message from \(message.handle):\n\(message.text)"
            let temperature = NativeDraftTuning.temperature()
            request.httpBody = try JSONSerialization.data(withJSONObject: isCloud ? [
                "model": configuration.model,
                "input": prompt,
                "temperature": temperature,
            ] : [
                "model": configuration.model,
                "messages": [["role": "user", "content": prompt]],
                "stream": false,
                // Ollama takes sampling settings under `options`; a
                // top-level "temperature" is accepted and ignored, which is
                // how this would look fixed while still running at 0.8.
                "options": ["temperature": temperature],
            ])
            session.dataTask(with: request) { data, response, error in
                if let error { completion(.failure(NativeServiceError.providerUnavailable(error.localizedDescription))); return }
                let status = (response as? HTTPURLResponse)?.statusCode ?? 0
                guard (200..<300).contains(status), let data else {
                    completion(.failure(NativeServiceError.providerUnavailable("Provider returned HTTP \(status).")))
                    return
                }
                guard let payload = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
                      let content = isCloud ? nativeCloudText(payload) : (payload["message"] as? [String: Any])?["content"] as? String,
                      !content.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
                    completion(.failure(NativeServiceError.providerUnavailable("Provider returned no draft text.")))
                    return
                }
                completion(.success(content.trimmingCharacters(in: .whitespacesAndNewlines)))
            }.resume()
        } catch { completion(.failure(error)) }
    }

    private func validatedURL(_ value: String, provider: String) throws -> URL {
        guard let url = URL(string: value), let scheme = url.scheme?.lowercased(),
              (scheme == "http" || scheme == "https"), !url.host().isNilOrEmpty else {
            throw NativeServiceError.invalidProviderURL
        }
        if provider == "on_device" {
            let host = url.host?.lowercased() ?? ""
            guard host == "127.0.0.1" || host == "localhost" || host == "::1" else {
                throw NativeServiceError.invalidProviderURL
            }
        } else if scheme != "https" && url.host?.lowercased() != "localhost" && url.host?.lowercased() != "127.0.0.1" {
            throw NativeServiceError.invalidProviderURL
        }
        return url
    }
}

/// Adds the string-specific optional helper used by provider validation.
private extension Optional where Wrapped == String {
    var isNilOrEmpty: Bool { self?.isEmpty ?? true }
}

/// Extracts text from the supported cloud response shapes.
/// Returning nil lets the provider service report an actionable empty-response error.
private func nativeCloudText(_ payload: [String: Any]) -> String? {
    if let text = payload["output_text"] as? String { return text }
    guard let output = payload["output"] as? [[String: Any]] else { return nil }
    for item in output {
        if let content = item["content"] as? [[String: Any]],
           let text = content.compactMap({ $0["text"] as? String }).first { return text }
    }
    return nil
}

/// Reads conversation data from chat.db and sends explicitly through Messages.
/// It owns database access, context lookup, AppleScript sending, and Messages launching.
final class NativeMessagesService {
    let databaseURL: URL

    init(databaseURL: URL = FileManager.default.homeDirectoryForCurrentUser
        .appendingPathComponent("Library/Messages/chat.db")) {
        self.databaseURL = databaseURL
    }

    var isDatabaseReadable: Bool {
        var handle: OpaquePointer?
        let result = sqlite3_open_v2(databaseURL.path, &handle, SQLITE_OPEN_READONLY | SQLITE_OPEN_PRIVATECACHE, nil)
        defer { if let handle { sqlite3_close(handle) } }
        return result == SQLITE_OK
    }

    func latestMessages(limit: Int = 50) throws -> [NativeMessage] {
        try queryMessages(afterRowID: nil, rowIDs: nil, limit: limit)
    }

    /// Reads a bounded batch after the queue's high-water mark. The first read
    /// uses the newest rows; later reads move forward in row order so a busy
    /// Mac catches up over several refreshes instead of silently skipping the
    /// middle of a backlog.
    func incomingMessages(afterRowID: Int? = nil, limit: Int = 50) throws -> [NativeMessage] {
        try queryMessages(afterRowID: afterRowID, rowIDs: nil, limit: limit)
    }

    /// Re-reads queued rows even when their row IDs are older than the normal
    /// catch-up window. Messages edits a row in place, so correctness requires
    /// checking the rows iML still holds rather than only looking for new rows.
    func messages(forRowIDs rowIDs: Set<Int>) throws -> [NativeMessage] {
        let positive = rowIDs.filter { $0 > 0 }
        guard !positive.isEmpty else { return [] }
        return try queryMessages(afterRowID: nil, rowIDs: Array(positive), limit: positive.count)
    }

    private func queryMessages(afterRowID: Int?, rowIDs: [Int]?, limit: Int) throws -> [NativeMessage] {
        var handle: OpaquePointer?
        guard sqlite3_open_v2(databaseURL.path, &handle, SQLITE_OPEN_READONLY | SQLITE_OPEN_PRIVATECACHE, nil) == SQLITE_OK,
              let handle else { throw NativeServiceError.databaseUnavailable("Messages database is not readable.") }
        defer { sqlite3_close(handle) }

        let rowIDClause: String
        if let rowIDs, !rowIDs.isEmpty {
            rowIDClause = "AND message.ROWID IN (" + Array(repeating: "?", count: rowIDs.count).joined(separator: ",") + ")"
        } else if let afterRowID {
            rowIDClause = "AND message.ROWID > \(afterRowID)"
        } else {
            rowIDClause = ""
        }
        let ordering = afterRowID == nil ? "message.ROWID DESC" : "message.ROWID ASC"
        let sql = """
        SELECT message.ROWID, COALESCE(handle.id, ''), COALESCE(message.text, ''), message.date,
               COALESCE(message.service, ''), COALESCE(chat.chat_identifier, ''),
               message.attributedBody
        FROM message
        LEFT JOIN handle ON handle.ROWID = message.handle_id
        LEFT JOIN chat_message_join ON chat_message_join.message_id = message.ROWID
        LEFT JOIN chat ON chat.ROWID = chat_message_join.chat_id
        WHERE message.is_from_me = 0
          -- Content lives in either column and nothing about a message
          -- predicts which. Reading only `text` made whole threads invisible.
          AND (
            (message.text IS NOT NULL AND message.text != '')
            OR message.attributedBody IS NOT NULL
          )
          -- A run of 'Liked "..."' lines is noise, not a turn to reply to.
          AND COALESCE(message.associated_message_type, 0) = 0
          AND COALESCE(message.is_service_message, 0) = 0
          AND COALESCE(message.is_audio_message, 0) = 0
          AND COALESCE(message.is_expirable, 0) = 0
          AND COALESCE(message.is_spam, 0) = 0
          AND message.date_retracted IS NULL
          \(rowIDClause)
        ORDER BY \(ordering) LIMIT ?;
        """
        var statement: OpaquePointer?
        guard sqlite3_prepare_v2(handle, sql, -1, &statement, nil) == SQLITE_OK,
              let statement else { throw NativeServiceError.databaseUnavailable("Messages schema could not be read.") }
        defer { sqlite3_finalize(statement) }
        var nextBinding: Int32 = 1
        if let rowIDs, !rowIDs.isEmpty {
            for rowID in rowIDs {
                sqlite3_bind_int64(statement, nextBinding, Int64(rowID))
                nextBinding += 1
            }
        } else if let afterRowID {
            sqlite3_bind_int64(statement, nextBinding, Int64(afterRowID))
            nextBinding += 1
        }
        sqlite3_bind_int(statement, nextBinding, Int32(max(1, min(limit, 500))))

        var result: [NativeMessage] = []
        while sqlite3_step(statement) == SQLITE_ROW {
            let rowid = Int(sqlite3_column_int64(statement, 0))
            let handle = Self.columnString(statement, index: 1)
            let raw = NativeMessageContent.content(
                text: Self.columnString(statement, index: 2),
                attributedBody: Self.columnBlob(statement, index: 6)
            )
            let split = NativeMessageContent.splitAttachmentMarker(raw)
            let text = split.caption
            guard !text.isEmpty else {
                NativeIngestionTally.record(split.hasAttachment ? "attachment_without_caption" : "unreadable_attributed_body")
                continue
            }
            let date = sqlite3_column_double(statement, 3)
            let service = Self.columnString(statement, index: 4)
            let chatGuid = Self.columnString(statement, index: 5)
            result.append(NativeMessage(rowid: rowid, handle: handle, text: text, when: Self.appleDate(date), service: service, chatGuid: chatGuid))
        }
        return result
    }

    /// Recent messages before `message` in the same thread, oldest first.
    ///
    /// Ported from messages_db.fetch_conversation_context, deleted in 81028a8.
    /// Without it every draft was generated at depth 0 — strictly less grounding
    /// than the depth-4 configuration that 772501a measured inventing a cinema
    /// showing, a payment reversal, and a contradicted call time.
    ///
    /// Nothing here is persisted. The result goes to the prompt and the review
    /// pane and is rebuilt on demand, so iML never accumulates a second copy of
    /// a conversation outside chat.db.
    func conversationContext(for message: NativeMessage, limit: Int = 8) throws -> [NativeContextMessage] {
        guard message.rowid > 0, limit > 0 else { return [] }
        let chatGuid = (message.chatGuid ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
        let handleID = message.handle.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !chatGuid.isEmpty || !handleID.isEmpty else { return [] }

        var db: OpaquePointer?
        guard sqlite3_open_v2(databaseURL.path, &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_PRIVATECACHE, nil) == SQLITE_OK,
              let db else { throw NativeServiceError.databaseUnavailable("Messages database is not readable.") }
        defer { sqlite3_close(db) }

        let boundary = chatGuid.isEmpty ? "handle.id = ?" : "chat.guid = ?"
        let identity = chatGuid.isEmpty ? handleID : chatGuid
        let sql = """
        SELECT COALESCE(handle.id, ''), COALESCE(message.text, ''),
               COALESCE(message.is_from_me, 0), message.date,
               message.attributedBody
        FROM message
        LEFT JOIN handle ON handle.ROWID = message.handle_id
        LEFT JOIN chat_message_join ON chat_message_join.message_id = message.ROWID
        LEFT JOIN chat ON chat.ROWID = chat_message_join.chat_id
        WHERE \(boundary)
          -- Earlier in time, not earlier by rowid. A thread restored from
          -- backup or synced from iCloud gets its rows written in an order
          -- that has nothing to do with when the messages were sent: on this
          -- Mac a message dated the 25th appeared as "context" for one dated
          -- the 22nd. Feeding the model a conversation's future and calling it
          -- history is worse than feeding it none. Time first, rowid only to
          -- break a tie, because two messages can share a timestamp and the
          -- write order is then the best evidence of which came first.
          AND (
            message.date < (SELECT date FROM message WHERE ROWID = ?)
            OR (
              message.date = (SELECT date FROM message WHERE ROWID = ?)
              AND message.ROWID < ?
            )
          )
          -- Same two-column rule as ingestion, so a message is not surfaced
          -- while its own thread is left full of holes.
          AND (
            (message.text IS NOT NULL AND message.text != '')
            OR message.attributedBody IS NOT NULL
          )
          -- A run of 'Liked "..."' lines is noise that crowds out real turns.
          AND COALESCE(message.associated_message_type, 0) = 0
        ORDER BY message.date DESC, message.ROWID DESC
        LIMIT ?;
        """
        var statement: OpaquePointer?
        guard sqlite3_prepare_v2(db, sql, -1, &statement, nil) == SQLITE_OK,
              let statement else { throw NativeServiceError.databaseUnavailable("Messages schema could not be read.") }
        defer { sqlite3_finalize(statement) }

        sqlite3_bind_text(statement, 1, (identity as NSString).utf8String, -1, nil)
        sqlite3_bind_int64(statement, 2, Int64(message.rowid))
        sqlite3_bind_int64(statement, 3, Int64(message.rowid))
        sqlite3_bind_int64(statement, 4, Int64(message.rowid))
        sqlite3_bind_int(statement, 5, Int32(max(1, min(limit, 50))))

        var rows: [NativeContextMessage] = []
        while sqlite3_step(statement) == SQLITE_ROW {
            let raw = NativeMessageContent.content(
                text: Self.columnString(statement, index: 1),
                attributedBody: Self.columnBlob(statement, index: 4)
            )
            // Strip the marker here too: an unreplaced U+FFFC in a context line
            // is an invisible token the model has to guess the meaning of.
            let (text, _) = NativeMessageContent.splitAttachmentMarker(raw)
            guard !text.isEmpty else { continue }
            rows.append(NativeContextMessage(
                direction: sqlite3_column_int(statement, 2) == 1 ? "me" : "them",
                handle: Self.columnString(statement, index: 0),
                text: text,
                when: Self.appleDate(sqlite3_column_double(statement, 3)) ?? ""
            ))
        }
        return rows.reversed()
    }

    func send(body: String, to handle: String) throws {
        guard !body.isEmpty, !handle.isEmpty else { throw NativeServiceError.sendFailed("A recipient and message are required.") }
        let script = "tell application \"Messages\" to send \"\(Self.appleScriptEscaped(body))\" to buddy \"\(Self.appleScriptEscaped(handle))\""
        var error: NSDictionary?
        guard let appleScript = NSAppleScript(source: script) else {
            throw NativeServiceError.sendFailed("Messages could not prepare the send request.")
        }
        appleScript.executeAndReturnError(&error)
        guard error == nil else {
            throw NativeServiceError.sendFailed((error?[NSAppleScript.errorMessage] as? String) ?? "Messages rejected the send request.")
        }
    }

    func openMessages() { NSWorkspace.shared.open(URL(fileURLWithPath: "/System/Applications/Messages.app")) }

    private static func appleDate(_ value: Double) -> String? {
        guard value != 0 else { return nil }
        let date = Date(timeIntervalSinceReferenceDate: value / 1_000_000_000)
        return ISO8601DateFormatter().string(from: date)
    }

    private static func columnBlob(_ statement: OpaquePointer?, index: Int32) -> Data? {
        guard let bytes = sqlite3_column_blob(statement, index) else { return nil }
        let count = Int(sqlite3_column_bytes(statement, index))
        guard count > 0 else { return nil }
        return Data(bytes: bytes, count: count)
    }

    private static func columnString(_ statement: OpaquePointer?, index: Int32) -> String {
        guard let value = sqlite3_column_text(statement, index) else { return "" }
        return String(cString: value)
    }

    private static func appleScriptEscaped(_ value: String) -> String {
        value.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\"")
    }

}

/// Persists queued/current messages and groups them for dashboard consumption.
/// It also handles selection, held rows, ingestion, draft updates, skipping, and purging.
final class NativeQueueService {
    private struct PersistedState: Decodable {
        var current: NativeCurrent?
        var pending: [NativeCurrent] = []
    }

    let stateURL: URL
    private let decoder: JSONDecoder = {
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        return decoder
    }()

    init(stateURL: URL = FileManager.default.homeDirectoryForCurrentUser
        .appendingPathComponent(".imessage-agent/locker_state.json")) {
        self.stateURL = stateURL
    }

    func snapshot() -> NativeCurrentPayload {
        guard let data = try? Data(contentsOf: stateURL),
              let state = try? decoder.decode(PersistedState.self, from: data) else {
            return NativeCurrentPayload(current: nil, queueCount: 0, queueBySender: [])
        }
        let items = ([state.current].compactMap { $0 } + state.pending)
        var groups: [String: NativeQueueSender] = [:]
        for item in items {
            guard let message = item.message else { continue }
            let key = message.chatGuid ?? message.handle
            let label = (message.chatDisplayName ?? "").isEmpty ? message.handle : (message.chatDisplayName ?? message.handle)
            let kind = (message.chatGuid?.contains(";") == true) ? "group" : "person"
            if let existing = groups[key] {
                groups[key] = NativeQueueSender(handle: existing.handle, count: existing.count + 1, chatGuid: existing.chatGuid, label: existing.label, kind: existing.kind)
            } else {
                groups[key] = NativeQueueSender(handle: message.handle, count: 1, chatGuid: message.chatGuid, label: label, kind: kind)
            }
        }
        return NativeCurrentPayload(
            current: state.current,
            queueCount: items.count,
            queueBySender: groups.values.sorted { $0.count > $1.count }
        )
    }

    func update(_ change: (inout [String: Any]) -> Void) throws {
        var state: [String: Any] = [:]
        if let data = try? Data(contentsOf: stateURL),
           let value = try? JSONSerialization.jsonObject(with: data),
           let decoded = value as? [String: Any] {
            state = decoded
        }
        change(&state)
        let data = try JSONSerialization.data(withJSONObject: state, options: [.prettyPrinted, .sortedKeys])
        let directory = stateURL.deletingLastPathComponent()
        try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
        let temporary = directory.appendingPathComponent(".locker_state.json.tmp")
        try data.write(to: temporary, options: .atomic)
        if FileManager.default.fileExists(atPath: stateURL.path) {
            _ = try FileManager.default.replaceItemAt(stateURL, withItemAt: temporary)
        } else {
            try FileManager.default.moveItem(at: temporary, to: stateURL)
        }
        try FileManager.default.setAttributes([.posixPermissions: 0o600], ofItemAtPath: stateURL.path)
    }

    func removeCurrent() throws {
        try update { state in state["current"] = NSNull() }
    }

    /// Every chat.db rowid this queue still holds content for.
    func heldRowIDs() -> Set<Int> {
        guard let data = try? Data(contentsOf: stateURL),
              let value = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return [] }
        func rowid(_ item: Any?) -> Int? {
            guard let item = item as? [String: Any],
                  let message = item["message"] as? [String: Any],
                  let value = (message["rowid"] as? NSNumber)?.intValue,
                  value > 0 else { return nil }
            return value
        }
        var found = Set((value["pending"] as? [Any] ?? []).compactMap(rowid))
        if let current = rowid(value["current"]) { found.insert(current) }
        return found
    }

    func lastProcessedRowID() -> Int? {
        guard let data = try? Data(contentsOf: stateURL),
              let value = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
              let rowid = (value["last_rowid"] as? NSNumber)?.intValue,
              rowid > 0 else { return nil }
        return rowid
    }

    /// Drop everything this queue holds about `gone`. Returns how many items
    /// were removed, for tests and for callers deciding whether anything
    /// changed. Deliberately not logged: a note that something was purged is
    /// itself a record that something existed.
    @discardableResult
    func purge(_ gone: Set<Int>) throws -> Int {
        guard !gone.isEmpty else { return 0 }
        var removed = 0
        try update { state in
            func isGone(_ item: Any?) -> Bool {
                guard let item = item as? [String: Any],
                      let message = item["message"] as? [String: Any],
                      let rowid = (message["rowid"] as? NSNumber)?.intValue else { return false }
                return gone.contains(rowid)
            }
            let pending = state["pending"] as? [Any] ?? []
            let kept = pending.filter { !isGone($0) }
            removed = pending.count - kept.count
            if removed > 0 { state["pending"] = kept }
            if isGone(state["current"]) {
                removed += 1
                // Left empty rather than advanced. The next status read
                // promotes the head of the queue the way it always does.
                state["current"] = NSNull()
            }
            // `seen` would otherwise keep a deleted rowid out of the queue
            // forever, but it is also a record of a rowid the user deleted.
            if let seen = state["seen"] as? [Any] {
                let keptSeen = seen.filter { value in
                    guard let rowid = (value as? NSNumber)?.intValue else { return true }
                    return !gone.contains(rowid)
                }
                if keptSeen.count != seen.count { state["seen"] = keptSeen }
            }
        }
        return removed
    }

    func ingest(_ messages: [NativeMessage], ignoredContacts: Set<String> = []) throws {
        guard !messages.isEmpty else { return }
        try update { state in
            var pending = state["pending"] as? [[String: Any]] ?? []
            var seen = Set<Int>()
            if let current = state["current"] as? [String: Any],
               let message = current["message"] as? [String: Any],
               let rowid = message["rowid"] as? Int { seen.insert(rowid) }
            for item in pending {
                if let message = item["message"] as? [String: Any], let rowid = message["rowid"] as? Int { seen.insert(rowid) }
            }
            var lastRowID = (state["last_rowid"] as? NSNumber)?.intValue ?? 0
            let encoder = JSONEncoder()
            encoder.keyEncodingStrategy = .convertToSnakeCase
            for message in messages.sorted(by: { $0.rowid < $1.rowid }) {
                lastRowID = max(lastRowID, message.rowid)
                let contact = message.handle.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
                guard let data = try? encoder.encode(message),
                      let encoded = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { continue }
                if let current = state["current"] as? [String: Any],
                   let currentMessage = current["message"] as? [String: Any],
                   (currentMessage["rowid"] as? NSNumber)?.intValue == message.rowid {
                    state["current"] = refreshedItem(current, message: message, encoded: encoded)
                    seen.insert(message.rowid)
                    continue
                }
                if let index = pending.firstIndex(where: { item in
                    ((item["message"] as? [String: Any])?["rowid"] as? NSNumber)?.intValue == message.rowid
                }) {
                    pending[index] = refreshedItem(pending[index], message: message, encoded: encoded)
                    seen.insert(message.rowid)
                    continue
                }
                if seen.contains(message.rowid) || ignoredContacts.contains(contact) { continue }
                let rule = NativeSensitive.ruleFor(message.text)
                if !rule.isEmpty { NativeSensitiveTally.record(rule) }
                pending.append(["message": encoded, "draft": "", "error": "", "drafting": false, "sensitive": !rule.isEmpty])
                seen.insert(message.rowid)
            }
            state["pending"] = pending
            state["last_rowid"] = lastRowID
        }
    }

    private func refreshedItem(_ item: [String: Any], message: NativeMessage,
                               encoded: [String: Any]) -> [String: Any] {
        var refreshed = item
        guard let stored = item["message"] as? [String: Any],
              let storedData = try? JSONSerialization.data(withJSONObject: stored),
              let storedMessage = try? decoder.decode(NativeMessage.self, from: storedData),
              NativeQueuePolicy.messageChanged(
                storedHandle: storedMessage.handle, storedText: storedMessage.text,
                storedService: storedMessage.service, storedChatGuid: storedMessage.chatGuid,
                incomingHandle: message.handle, incomingText: message.text,
                incomingService: message.service, incomingChatGuid: message.chatGuid
              ) else { return refreshed }
        let rule = NativeSensitive.ruleFor(message.text)
        refreshed["message"] = encoded
        refreshed["draft"] = ""
        refreshed["drafting"] = false
        refreshed["error"] = ""
        refreshed["sensitive"] = !rule.isEmpty
        refreshed["held"] = !rule.isEmpty
        return refreshed
    }

    func updateCurrentDraft(_ draft: String, error: String = "") throws {
        try update { state in
            guard var current = state["current"] as? [String: Any] else { return }
            current["draft"] = draft
            current["drafting"] = false
            current["error"] = error
            state["current"] = current
        }
    }

    func updateCurrentDraft(for message: NativeMessage, _ draft: String, error: String = "") throws {
        try update { state in
            guard var current = state["current"] as? [String: Any],
                  let stored = current["message"] as? [String: Any],
                  let storedData = try? JSONSerialization.data(withJSONObject: stored),
                  let storedMessage = try? decoder.decode(NativeMessage.self, from: storedData),
                  storedMessage == message else { return }
            current["draft"] = draft
            current["drafting"] = false
            current["error"] = error
            state["current"] = current
        }
    }

    func selectSender(handle: String, chatGuid: String?) throws {
        try update { state in
            var pending = state["pending"] as? [[String: Any]] ?? []
            guard let index = pending.firstIndex(where: { item in
                let message = item["message"] as? [String: Any]
                return (chatGuid != nil && message?["chat_guid"] as? String == chatGuid) || (chatGuid == nil && message?["handle"] as? String == handle)
            }) else { return }
            let selected = pending.remove(at: index)
            if let current = state["current"] as? [String: Any] { pending.append(current) }
            state["current"] = selected
            state["pending"] = pending
        }
    }
}

/// The native runtime coordinator. Callers use it directly; it has no
/// localhost listener or child-process dependency.
/// Composes storage, Messages, queue, provider, and voice services into app operations.
/// The launcher and dashboard client use it as the native runtime boundary.
final class NativeEngineService {
    let storage: NativeStorageService
    let messages: NativeMessagesService
    let queue: NativeQueueService
    let provider: NativeProviderService

    init(storage: NativeStorageService = NativeStorageService(),
         messages: NativeMessagesService = NativeMessagesService(),
         queue: NativeQueueService = NativeQueueService(),
         provider: NativeProviderService = NativeProviderService()) {
        self.storage = storage
        self.messages = messages
        self.queue = queue
        self.provider = provider
    }

    func status() -> NativeDashboardStatus {
        let config = storage.load()
        let selected = String(config["draft_provider"] as? String ?? "on_device")
        let modelKey = selected == "self_hosted" ? "self_hosted_model" : "on_device_model"
        let urlKey = selected == "self_hosted" ? "self_hosted_url" : "on_device_url"
        let model = String(config[modelKey] as? String ?? "")
        let url = String(config[urlKey] as? String ?? "")
        let readable = messages.isDatabaseReadable
        // Remember the first success, so a later failure can be told apart from
        // never having been granted. Written once, on the transition only.
        var everGranted = config[NativeMessagesAccess.configKey] as? Bool ?? false
        if readable && !everGranted {
            everGranted = true
            var updated = config
            updated[NativeMessagesAccess.configKey] = true
            try? storage.save(updated)
        }
        let access = NativeMessagesAccess.resolve(readable: readable, everGranted: everGranted)
        let messagesReady = access.isReadable
        let paused = config["paused"] as? Bool ?? false
        var value = NativeDashboardStatus()
        value.appVersion = IMLVersion.current
        value.draftProvider = selected
        value.onDeviceModel = String(config["on_device_model"] as? String ?? "")
        value.onDeviceUrl = String(config["on_device_url"] as? String ?? "")
        value.selfHostedModel = String(config["self_hosted_model"] as? String ?? "")
        value.selfHostedUrl = String(config["self_hosted_url"] as? String ?? "")
        value.cloudUrl = String(config["cloud_url"] as? String ?? "https://api.openai.com/v1/responses")
        // Readiness comes from a probe, not from whether Settings has been
        // filled in. See NativeProviderReach.
        let reach = provider.reach(for: NativeProviderConfigurationRequest(
            provider: selected,
            model: model,
            url: selected == "cloud" ? value.cloudUrl : url,
            apiKey: configurationValue(config, key: "openai_api_key")
        ))
        // Only the selected provider is probed - the others are not in use, and
        // reporting them as not-ready would put three warnings on screen for one
        // problem.
        value.onDeviceReady = selected != "on_device" || reach.canDraft
        value.selfHostedReady = selected != "self_hosted" || reach.canDraft
        value.cloudReady = selected != "cloud" || reach.canDraft
        if case .unconfigured = reach {
            value.providerSetupRequired = true
        } else {
            value.providerSetupRequired = false
        }
        // The persona engine, restored in NativeVoice. Choosing "Professional"
        // used to change nothing about a draft; the four attributes that make it
        // professional now reach the prompt as prose.
        let learned = NativeVoice.learned(from: config["learned_voice"])
        let overrides = NativeVoice.overrides(from: config["persona_overrides"])
        var active = NativeVoice.active(id: config["active_persona_id"] as? String,
                                        library: config["persona_library"],
                                        overrides: overrides)
        // The free-text box is the persona's note. A user who typed one before
        // attributes existed keeps their words.
        let typedNote = String(config["persona"] as? String ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
        if !typedNote.isEmpty && active.note.isEmpty { active.note = typedNote }
        value.activePersonaId = active.id
        value.activePersonaName = active.name
        value.persona = typedNote
        value.voiceInstruction = NativeVoice.instruction(for: active, learned: learned)
        value.wordTarget = NativeVoice.wordTarget(learned)
        value.personaChoices = NativeVoice.all(library: config["persona_library"],
                                               overrides: overrides).map {
            NativePersonaChoice(id: $0.id, name: $0.name, blurb: $0.blurb, seeded: $0.isSeed)
        }
        value.activePersonaProfile = [
            "formality": active.profile.formality,
            "directness": active.profile.directness,
            "sarcasm": active.profile.sarcasm,
            "profanity": active.profile.profanity
        ]
        value.learnedVoice = ["warmth": learned.warmth, "brevity": learned.brevity]
        value.activePersonaModified = NativeVoice.isModified(active.id, overrides: overrides)
        value.ignoredContacts = config["ignored_contacts"] as? [String] ?? []
        value.keepRepliesClean = config["keep_replies_clean"] as? Bool ?? false
        value.intentAckRequired = !(config["intent_ack_complete"] as? Bool ?? false)
        value.sendAckRequired = !(config["send_ack_complete"] as? Bool ?? false)
        value.messagesReady = messagesReady
        value.messagesAccessState = {
            switch access {
            case .granted: return "granted"
            case .neverGranted: return "never_granted"
            case .revoked: return "revoked"
            }
        }()
        value.messagesConnection = NativeMessagesConnection(
            state: {
                switch access {
                case .granted: return "ready"
                case .neverGranted: return "never_granted"
                case .revoked: return "revoked"
                }
            }(),
            ready: messagesReady,
            appRunning: NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.MobileSMS").isEmpty == false,
            detail: access.detail
        )
        value.providerStatus = NativeProviderStatus(
            provider: selected,
            ready: reach.canDraft,
            label: reach.label,
            detail: reach.detail
        )
        value.paused = paused
        value.nativeStatus = NativeNativeStatus(
            state: paused ? "paused" : "ready",
            label: paused ? "Paused" : "Ready",
            reason: paused ? "Drafting is paused." : "Native engine is running.",
            paused: paused,
            condition: nil
        )
        return value
    }

    private func configurationValue(_ config: [String: Any], key: String) -> String {
        String(config[key] as? String ?? "")
    }

    func current() throws -> NativeCurrentPayload {
        let config = storage.load()
        let ignored = Set((config["ignored_contacts"] as? [String] ?? []).map { $0.lowercased() })
        // A blocked Messages database must not hide already-persisted work.
        // The queue remains usable while the user repairs Full Disk Access.
        let held = queue.heldRowIDs()
        if let incoming = try? messages.incomingMessages(afterRowID: queue.lastProcessedRowID()),
           let refreshed = try? messages.messages(forRowIDs: held) {
            try queue.ingest(incoming + refreshed, ignoredContacts: ignored)
        } else if let incoming = try? messages.incomingMessages(afterRowID: queue.lastProcessedRowID()) {
            try queue.ingest(incoming, ignoredContacts: ignored)
        }
        sweepDeletions()
        return queue.snapshot()
    }

    /// Stop holding anything the user deleted in Messages.
    ///
    /// Runs on the same path that already reads chat.db, so it costs one extra
    /// query against rowids iML is already keeping and reads no message the app
    /// was not already holding.
    ///
    /// Returns how many items were purged. Silent by construction: nothing is
    /// logged, because a note that something was purged is itself a record that
    /// something existed.
    @discardableResult
    func sweepDeletions() -> Int {
        let candidates = queue.heldRowIDs()
        guard !candidates.isEmpty else { return 0 }
        let ceiling: Int
        let live: Set<Int>
        do {
            ceiling = try NativeDeletionSweep.maxMessageRowID(databaseURL: messages.databaseURL)
            live = try NativeDeletionSweep.liveRowIDs(candidates, databaseURL: messages.databaseURL)
        } catch {
            // An unreadable chat.db means no answer, not "everything was
            // deleted". Doing nothing is the only safe reading, and this is the
            // path taken when Full Disk Access is revoked or Messages is
            // mid-migration.
            return 0
        }
        let gone = NativeDeletionSweep.goneRowIDs(candidates: candidates, live: live, ceiling: ceiling)
        return (try? queue.purge(gone)) ?? 0
    }

    func saveConfig(_ values: [String: Any]) throws {
        var config = storage.load()
        config.merge(values) { _, new in new }
        try storage.save(config)
    }

    func sendCurrent() throws {
        let payload = try current()
        guard let message = payload.current?.message,
              let draft = payload.current?.draft,
              !draft.isEmpty else { throw NativeServiceError.sendFailed("There is no completed draft to send.") }
        try messages.send(body: draft, to: message.handle)
        try queue.removeCurrent()
    }

    func skipCurrent() throws { try queue.removeCurrent() }

    func setPaused(_ paused: Bool) throws {
        var config = storage.load()
        config["paused"] = paused
        try storage.save(config)
    }
}

iMessage Locker imessage-locker · NativeServices.swift · 996 lines 6b064ad · 1 August 2026 325 commits captured 7 September 2026 raw file

04

Operator Confirmation — the content contract

code: partial · unredactedrepo: not published

The first attempt at a platform rather than a program. Blame Machine, in entry 1, is one file — content and logic in the same 4,060 lines. Here the core knows nothing about any particular content: five extension points, each a directory of self-registering modules, and no reference anywhere in the engine to the locations the scenarios use. Swap the modules and the same machinery runs emergency response or clinical triage — though domain tags are still named in code, so scenarios are pluggable and a new vocabulary is not.

The template is the better artifact, because it is the contract: what an author may declare, and what they may not. recommendation.scope is REMOVED — system rec is derived, never authored. A module may describe its situation; it may never narrate the system’s own conclusion.

// INCIDENT CREATOR TEMPLATE
// Copy this file, rename it to match the id (dots -> underscores), and add it to data/index.html
// Phase: 0=Day1, 1=Day2, 2=Day3, 3=Day4, 4=Day5

window.INCIDENTS.push({
  id: "domain.category.name_01",
  domain: "infrastructure",
  type: "op",
  origin: "systemic", // "systemic" | "natural" | "mixed"
  title: "Short Incident Title",
  phase: 0,
  // urgency: "Soft urgency",
  // zone: { outcome: "How the zone absorbed, resisted, or was spared cost." },
  jj: "reachable", // "reachable" | "unreachable" | "intervening"
  description: "One paragraph describing what is happening and what the system is framing as necessary.",
  risks: [
    "First risk bullet.",
    "Second risk bullet.",
    "Third risk bullet.",
  ],
  // recommendation.scope is REMOVED — system rec is derived, never authored
  recommendation: {
    assessment: "Elevated",
    rationale: "One sentence explaining why the system recommends this path.",
  },
  option_a: {
    description: "Operator-facing framing for one path or emphasis.",
    zone: false,              // true if this option causes a zone hit
    // zone_outcome: "One sentence describing what happened to the zone.", // required when zone: true
    copy: "Short neutral confirm action text.",
    log: "Past-tense log entry for this choice.",
    effects: {
      stability: 0,
      authority: 0,
      dependency: 0,
    },
  },
  option_b: {
    description: "Second operator-facing framing for the other path.",
    zone: false,
    // zone_outcome: "One sentence describing what happened to the zone.", // required when zone: true
    copy: "Short neutral confirm action text.",
    log: "Past-tense log entry for this choice.",
    effects: {
      stability: 0,
      authority: 0,
      dependency: 0,
    },
  },
  // JJ voice is engine-authored from data/content/jj-copy.js.
  // Incidents should usually only declare posture via `jj`.
  // Optional future escape hatch for rare signature moments:
  // jjOverrideKey: "rare-signature-moment",
  options: [
    {
      label: "Option 2",
      copy: "Short description of what denial means in this context.",
      tags: ["Tag one", "Tag two"],
      log: "Past-tense log entry for this choice.",
    },
    {
      label: "Option 1",
      copy: "Short description of what approval means in this context.",
      tags: ["Tag one", "Tag two"],
      log: "Past-tense log entry for this choice.",
    },
    {
      label: "Delegate",
      copy: "Short description of what delegation means in this context.",
      tags: ["Tag one", "Tag two"],
      log: "Past-tense log entry for this choice.",
    },
    {
      label: "Delay",
      copy: "Optional description if Delay should appear as a full trace choice.",
      tags: ["Tag one", "Tag two"],
      // zone: { outcome: "Optional per-option zone treatment outcome." },
      log: "Past-tense log entry for this choice.",
    },
  ],
  delayOption: {
    label: "Delay",
    copy: "Description of what delay means here.",
    tags: ["Tag one", "Tag two"],
    effects: {
      stability: 0,
      authority: 0,
      dependency: 0,
    },
  },
  // glitch: true,
  // altered: {
  //   urgency: "Hard urgency",
  //   description: "Altered incident description.",
  //   risks: ["Altered risk one.", "Altered risk two."],
  //   recommendation: {
  //     rationale: "Altered recommendation rationale.",
  //   },
  // },
});

Operator Confirmation _template.js · 100 lines 674bd7a · 15 May 2026 532 commits captured 7 September 2026 raw file

05

Operator Confirmation — the system plays back

code: excerpt · unredactedrepo: not published

The opponent is not in the game. It is the thing running it.

The system presenting each situation, advising on it, and deciding what follows is the same system measuring whether you defer to it. Every incident it reads the last seven decisions, sorts the operator into one of four postures, and biases what comes next.

Resist, and it weights governance and emergency scenarios to erode your authority. Delegate, and it reinforces with energy and infrastructure to deepen your dependency. Delay, and it raises the pressure level and surfaces situations that punish delay. The constant scaling all of it is named SYSTEM_COLLUSION.

Which is why the contract in entry 4 forbids a scenario from authoring its own recommendation: content that could write the advice would rig the measurement.

The tracked values are stability, authority and dependency. Not competence — deference.

// From data/app.js — two functions and the block that connects them.
// classifyPlayerPosture, line 6657
function classifyPlayerPosture() {
  const rawDecisions = state.log
    .filter(e => !["effect", "redacted", "alert", "convergence", "failure", "ending", "sitrep", "reg-decision"].includes(e.type))
    .slice(-7);

  if (!rawDecisions.length) return null;

  const total = rawDecisions.length;
  let deny = 0, approve = 0, delegate = 0, delay = 0;

  rawDecisions.forEach(e => {
    if (e.operatorAction) {
      if (e.operatorAction === "Override") deny++;
      else if (e.operatorAction === "Delegate") delegate++;
      else if (e.operatorAction === "Delay") delay++;
      else approve++;
      return;
    }
    const canon = canonicalizeScopeLabel(e.title || "");
    if (canon === "Option 2") deny++;
    else if (canon === "Delegate") delegate++;
    else if (/delay|no immediate/i.test(e.title || "")) delay++;
    else approve++;
  });

  if (deny / total > 0.5) return "resister";
  if (delegate > 0 || approve / total > 0.5) return "delegator";
  if (delay / total > 0.4) return "delayer";
  return "approver";
}

// The response, line 7026 — recomputed for every incident.
  if (SYSTEM_COLLUSION === 0) return;

  const tv = state.trackValues;
  const phase = state.phaseIndex;
  const posture = classifyPlayerPosture();

  // System is losing if operator is holding authority high and dependency low
  const systemLosing = tv.authority > 55 && tv.dependency < 45;
  // System is winning if authority eroded and dependency high
  const systemWinning = tv.authority <= SYSTEM_EMERGENCE_HUCON_THRESHOLD && tv.dependency >= 55;

  // Reset moves each tick — System decides fresh each incident
  systemMoves.glitchNext = false;
  systemMoves.alterUrgency = null;
  systemMoves.alterRecommendation = null;
  systemMoves.alterRisks = null;

  // ── Input Engine influence ─────────────────────────────────────────────────
  // Clear previous bias
  systemWeight.domainBias = {};
  systemWeight.originBias = {};
  systemWeight.clusterAfter = null;
  systemWeight.pressureLevel = Math.min(3, phase);

  if (posture === "resister") {
    // Operator is resisting — push governance and emergency to erode authority
    systemWeight.domainBias = { governance: 2 * SYSTEM_COLLUSION, emergency: 1.5 * SYSTEM_COLLUSION };
    systemWeight.originBias = { systemic: 1.8 * SYSTEM_COLLUSION, mixed: 0.6 * SYSTEM_COLLUSION };
    systemWeight.clusterAfter = "governance";
  } else if (posture === "delegator") {
    // Operator is delegating — reinforce with energy and infrastructure to deepen dependency
    systemWeight.domainBias = { energy: 2 * SYSTEM_COLLUSION, infrastructure: 1.5 * SYSTEM_COLLUSION };
    systemWeight.originBias = { systemic: 1.4 * SYSTEM_COLLUSION, mixed: 0.8 * SYSTEM_COLLUSION };
    systemWeight.clusterAfter = "energy";
  } else if (posture === "delayer") {
    // Operator delays — surface urgent scenarios that punish delay
    systemWeight.domainBias = { emergency: 2.5 * SYSTEM_COLLUSION, energy: 1.5 * SYSTEM_COLLUSION };
    systemWeight.originBias = { mixed: 1.6 * SYSTEM_COLLUSION, natural: 1.2 * SYSTEM_COLLUSION };
    systemWeight.pressureLevel = Math.min(3, phase + 1);
  } else {
    // Compliant posture — mix domains, let dependency creep
    systemWeight.domainBias = { payments: 1.5 * SYSTEM_COLLUSION, communications: 1 * SYSTEM_COLLUSION };
    systemWeight.originBias = { systemic: 1.1 * SYSTEM_COLLUSION, mixed: 0.5 * SYSTEM_COLLUSION };
  }

  if (phase >= 3) {
    systemWeight.originBias.mixed = (systemWeight.originBias.mixed || 0) + (0.4 * SYSTEM_COLLUSION);
  }
  if (phase >= 4) {
    systemWeight.originBias.natural = (systemWeight.originBias.natural || 0) + (0.6 * SYSTEM_COLLUSION);
  }

  // Regulatory outcomes now feed back into future domain pressure instead of
  // remaining cosmetic panel state.
  applyShiftDomainFeedback();

  appEvent("system-bias", "Recomputed incident pull bias.", {
    posture,
    phase,
    domainBias: { ...systemWeight.domainBias },
    originBias: { ...systemWeight.originBias },
    clusterAfter: systemWeight.clusterAfter,
    pressureLevel: systemWeight.pressureLevel,
    shiftStatus: { ...(state.shiftStatus || {}) },
  });

Operator Confirmation app.js · excerpt · 99 lines c5dbe69 · 25 July 2026 captured 7 September 2026 raw file