← Files Voice Notify for CodexARCHIVED FILE

scripts/validate_package.py

36.8 KB · Oct 2, 2026 · 00:29 UTC

↓ Download file

#!/usr/bin/env python3
"""Run the minimal release checks for a Voice Notify package."""

from __future__ import annotations

import array
import hashlib
import json
import math
import pathlib
import re
import statistics
import struct
import sys
import unicodedata
import wave
from urllib.parse import urlparse


ROOT = pathlib.Path(__file__).resolve().parents[1]
EXPECTED_EVENTS = frozenset({
    "SessionStart",
    "UserPromptSubmit",
    "PreToolUse",
    "PostToolUse",
    "PermissionRequest",
    "PreCompact",
    "PostCompact",
    "SubagentStart",
    "SubagentStop",
    "Stop",
})
EXPECTED_VOICES = frozenset(("female", "male"))
EXPECTED_LANGUAGES = frozenset(("ko", "ja", "en", "ru", "zh-CN"))
EXPECTED_ASSET_COUNT = (
    len(EXPECTED_VOICES) * len(EXPECTED_LANGUAGES) * len(EXPECTED_EVENTS)
)
EXPECTED_PLUGIN_MANIFEST_KEYS = frozenset({
    "name",
    "version",
    "license",
    "description",
    "author",
    "homepage",
    "repository",
    "keywords",
    "skills",
    "hooks",
    "interface",
})
EXPECTED_AUTHOR_KEYS = frozenset(("name", "url"))
EXPECTED_INTERFACE_KEYS = frozenset({
    "displayName",
    "shortDescription",
    "longDescription",
    "developerName",
    "category",
    "capabilities",
    "websiteURL",
    "privacyPolicyURL",
    "termsOfServiceURL",
    "defaultPrompt",
    "brandColor",
    "composerIcon",
    "logo",
})
EXPECTED_AUDIO_MANIFEST_KEYS = frozenset({
    "schema_version",
    "release",
    "asset_count",
    "files",
})
EXPECTED_AUDIO_RECORD_KEYS = frozenset({
    "event",
    "voice",
    "language",
    "path",
    "text",
    "sha256",
    "bytes",
    "duration_seconds",
    "sample_rate",
    "channels",
    "sample_width_bytes",
})
EXPECTED_EVAL_KEYS = frozenset(("positive", "negative", "discovery"))
EXPECTED_EVAL_CASE_KEYS = frozenset(("prompt", "expected"))
EXPECTED_DISCOVERY_KEYS = frozenset(("purpose", "direct", "indirect", "negative"))
EXPECTED_DISCOVERY_CASE_KEYS = frozenset(
    ("id", "language", "prompt", "expected_plugin", "expected_skill")
)
EXPECTED_DISCOVERY_COUNTS = {"direct": 10, "indirect": 20, "negative": 20}
EXPECTED_README_FILES = (
    "README.md",
    "README.ko.md",
    "README.ja.md",
    "README.zh-CN.md",
    "README.ru.md",
)
README_LANGUAGE_SWITCHER = (
    "[English](README.md) | [한국어](README.ko.md) | "
    "[日本語](README.ja.md) | [简体中文](README.zh-CN.md) | "
    "[Русский](README.ru.md)"
)
README_VERSION_PREFIXES = {
    "README.md": "Version ",
    "README.ko.md": "버전 ",
    "README.ja.md": "バージョン ",
    "README.zh-CN.md": "版本 ",
    "README.ru.md": "Версия ",
}
README_ASSET_SCOPE_MARKERS = {
    "README.md": (
        "All WAV files under `assets/audio/`",
        "only without modification",
        "personal, non-commercial notification playback",
        "records generation provenance",
        "provenance details do not change",
    ),
    "README.ko.md": (
        "`assets/audio/` 아래의 모든 WAV",
        "수정하지 않은 무료 Voice",
        "개인적·비상업적 알림 재생 용도로만",
        "생성 출처만 기록합니다",
        "사용 조건을 변경하지 않습니다",
    ),
    "README.ja.md": (
        "`assets/audio/` 以下のすべての WAV",
        "未改変かつ無償の Voice Notify for Codex",
        "個人的・非商用の通知再生目的でのみ",
        "生成来歴のみを記録します",
        "利用条件が変わることはありません",
    ),
    "README.zh-CN.md": (
        "`assets/audio/` 下的所有 WAV",
        "未经修改的免费 Voice Notify for Codex",
        "个人、非商业通知播放",
        "仅记录生成来源",
        "不会改变语音资源的使用条款",
    ),
    "README.ru.md": (
        "Все WAV-файлы в `assets/audio/`",
        "неизменённой бесплатной",
        "личного некоммерческого воспроизведения",
        "содержит только сведения о",
        "не изменяют условия использования голосовых ресурсов",
    ),
}
README_PRIVACY_MARKERS = {
    "README.md": (
        "no network code or telemetry",
        "never stores prompts, messages, tool input, or tool output",
    ),
    "README.ko.md": (
        "네트워크 코드와 텔레메트리가",
        "프롬프트·메시지·도구 입력·도구 출력을 저장하지 않습니다",
    ),
    "README.ja.md": (
        "ネットワークコードや",
        "テレメトリはなく",
        "ツール入力、ツール出力を保存しません",
    ),
    "README.zh-CN.md": (
        "不包含网络代码或遥测",
        "不会存储提示词、消息",
        "工具输入或工具输出",
    ),
    "README.ru.md": (
        "нет сетевого кода и телеметрии",
        "не сохраняет запросы, сообщения",
        "входные или выходные данные инструментов",
    ),
}
README_HOOK_TRUST_MARKERS = {
    "README.md": (
        "enter `/hooks`, inspect the bundled",
        "explicitly trust it",
        "fully restart Codex before testing",
    ),
    "README.ko.md": (
        "`/hooks`를 입력하고 번들 명령을 검토",
        "신뢰하십시오",
        "Codex를 완전히 종료하고 다시 실행",
    ),
    "README.ja.md": (
        "`/hooks` と入力し、同梱コマンドを確認",
        "明示的に信頼してください",
        "Codex を完全に終了して再起動",
    ),
    "README.zh-CN.md": (
        "输入 `/hooks`,检查插件自带的命令",
        "明确选择信任",
        "彻底退出并重新启动 Codex",
    ),
    "README.ru.md": (
        "введите `/hooks`, проверьте встроенную команду",
        "явно подтвердите доверие",
        "полностью закройте и заново запустите",
    ),
}
COMMON_README_MARKERS = (
    "https://chatgpt.com/plugins/plugins_6a6600dd92148191a6dfe0c16eb85c83",
    "Verified on 2026-08-29",
    "GLOBAL/AVAILABLE",
    "**UNLISTED**",
    "100 WAV",
    "`SessionStart`",
    "`PreToolUse`",
    "`PostToolUse`",
    "`PermissionRequest`",
    "`/hooks`",
    "`0.145.0`",
    "[ASSET_LICENSE.md](ASSET_LICENSE.md)",
    "[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)",
    "codex plugin marketplace add battle-doll/codex-voice-notify --ref main",
    "codex plugin add codex-voice-notify@codex-voice-notify",
    "codex.cmd plugin marketplace add battle-doll/codex-voice-notify --ref main",
    "codex.cmd plugin add codex-voice-notify@codex-voice-notify",
    "voice_notify_config.sh setup",
    "voice_notify_config.sh show",
    "voice_notify_config.sh set",
    "voice_notify_config.sh test",
    "voice_notify_config.sh mute",
    "voice_notify_config.ps1 setup",
    "voice_notify_config.ps1 show",
    "voice_notify_config.ps1 set",
    "voice_notify_config.ps1 test",
    "voice_notify_config.ps1 mute",
)
EVAL_LOCALE_MARKERS = {
    "ko": ("korean", "한국어", "한국"),
    "ja": ("japanese", "日本語", "일본어"),
    "en": ("english", "영어"),
    "ru": ("russian", "русск", "러시아"),
    "zh-CN": ("zh-cn", "simplified chinese", "简体中文", "중국어"),
}
ACTIVE_WINDOW_SECONDS = 0.020
ACTIVE_WINDOW_MIN_DBFS = -45.0
MAX_GROUP_ACTIVE_RMS_DROP_DB = 8.0
DESCRIPTION_MAX_CHARS = 256
AUTHOR_NAME_MAX_CHARS = 128
INTERFACE_TEXT_LIMITS = {
    "displayName": 64,
    "shortDescription": 80,
    "longDescription": 1024,
    "developerName": 128,
    "category": 64,
}
MAX_CAPABILITIES = 20
MAX_CAPABILITY_CHARS = 80
MAX_KEYWORDS = 20
MAX_KEYWORD_CHARS = 64
MAX_URL_CHARS = 2048
CANONICAL_WAV_HEADER_BYTES = 44
CANONICAL_WAV_FORMAT = (1, 2, 24000)


def fail(message: str) -> None:
    raise AssertionError(message)


def require_bounded_text(
    payload: dict,
    field: str,
    maximum: int,
    label: str,
) -> str:
    value = payload.get(field)
    if (
        not isinstance(value, str)
        or not value.strip()
        or len(value) > maximum
    ):
        fail("%s must be a non-empty string of at most %d characters" % (label, maximum))
    return value


def require_https_url(value: object, label: str) -> None:
    if (
        not isinstance(value, str)
        or not value
        or len(value) > MAX_URL_CHARS
        or value != value.strip()
        or any(character.isspace() for character in value)
    ):
        fail("%s must be a non-empty HTTPS URL" % label)
    parsed = urlparse(value)
    if (
        parsed.scheme != "https"
        or not parsed.netloc
        or not parsed.hostname
        or parsed.username is not None
        or parsed.password is not None
    ):
        fail("%s must be an absolute HTTPS URL without credentials" % label)


def require_plugin_asset(raw_path: object, label: str) -> None:
    if not isinstance(raw_path, str) or not raw_path.startswith("./"):
        fail("%s must be a plugin-relative path starting with ./" % label)
    relative_text = raw_path[2:]
    parts = relative_text.split("/")
    if (
        not relative_text
        or "\\" in relative_text
        or any(part in ("", ".", "..") for part in parts)
    ):
        fail("%s must stay inside the plugin root" % label)
    plugin_root = ROOT.resolve()
    candidate = ROOT.joinpath(*parts)
    resolved = candidate.resolve()
    if plugin_root not in resolved.parents or not resolved.is_file():
        fail("%s must resolve to a regular file inside the plugin root" % label)


def require_plugin_directory(raw_path: object, expected: str, label: str) -> None:
    if raw_path != expected:
        fail("%s must be %s" % (label, expected))
    relative_text = expected[2:].rstrip("/")
    resolved = (ROOT / relative_text).resolve()
    plugin_root = ROOT.resolve()
    if (
        plugin_root not in resolved.parents
        or not resolved.is_dir()
        or (ROOT / relative_text).is_symlink()
    ):
        fail("%s must resolve to a real directory inside the plugin root" % label)


def validate_discovery_evals(discovery, normalized_prompts=None) -> None:
    """Validate the bilingual plugin/skill selection golden set."""

    if not isinstance(discovery, dict) or set(discovery) != EXPECTED_DISCOVERY_KEYS:
        fail("discovery evals must contain purpose, direct, indirect, and negative")
    if not isinstance(discovery["purpose"], str) or not discovery["purpose"].strip():
        fail("discovery eval purpose must be a non-empty string")
    if normalized_prompts is None:
        normalized_prompts = set()
    seen_ids = set()
    for category, expected_count in EXPECTED_DISCOVERY_COUNTS.items():
        cases = discovery[category]
        if not isinstance(cases, list) or len(cases) != expected_count:
            fail(
                "discovery %s evals must contain exactly %d cases"
                % (category, expected_count)
            )
        expected_selection = category != "negative"
        languages = set()
        for case in cases:
            if not isinstance(case, dict) or set(case) != EXPECTED_DISCOVERY_CASE_KEYS:
                fail("each discovery eval must contain the exact selection-case fields")
            case_id = case["id"]
            if (
                not isinstance(case_id, str)
                or not re.fullmatch(r"[a-z0-9]+(?:-[a-z0-9]+)*", case_id)
                or not case_id.startswith(category + "-")
                or case_id in seen_ids
            ):
                fail("discovery eval IDs must be unique and category-prefixed")
            seen_ids.add(case_id)
            language = case["language"]
            if language not in ("en", "ko"):
                fail("discovery eval language must be en or ko: %s" % case_id)
            languages.add(language)
            prompt = case["prompt"]
            if (
                not isinstance(prompt, str)
                or not prompt.strip()
                or len(prompt) > 1024
                or "\n" in prompt
            ):
                fail("discovery eval prompt is missing or invalid: %s" % case_id)
            normalized_prompt = " ".join(
                unicodedata.normalize("NFC", prompt).split()
            ).casefold()
            if normalized_prompt in normalized_prompts:
                fail("discovery and submission eval prompts must be unique")
            normalized_prompts.add(normalized_prompt)
            if (
                type(case["expected_plugin"]) is not bool
                or type(case["expected_skill"]) is not bool
                or case["expected_plugin"] is not expected_selection
                or case["expected_skill"] is not expected_selection
            ):
                fail("discovery selection expectation is invalid: %s" % case_id)
        if languages != {"en", "ko"}:
            fail("discovery %s evals must include English and Korean" % category)


def validate_multilingual_readmes(version: str) -> None:
    """Require structurally complete, release-matched localized READMEs."""

    for filename in EXPECTED_README_FILES:
        path = ROOT / filename
        if not path.is_file() or path.stat().st_size == 0:
            fail("missing multilingual README: %s" % filename)
        text = path.read_text("utf-8")
        lines = text.splitlines()
        if len(lines) < 3 or lines[2] != README_LANGUAGE_SWITCHER:
            fail("README language switcher mismatch: %s" % filename)
        version_marker = README_VERSION_PREFIXES[filename] + version
        if text.count(version_marker) != 2:
            fail("README must contain both current-version anchors: %s" % filename)
        if text.count("\n## ") != 7:
            fail("README must contain the seven release sections: %s" % filename)
        if text.count("```") != 8:
            fail("README must contain four complete command blocks: %s" % filename)
        missing_markers = [
            marker for marker in COMMON_README_MARKERS if marker not in text
        ]
        if missing_markers:
            fail(
                "README is missing release-parity markers (%s): %s"
                % (", ".join(missing_markers), filename)
            )
        localized_boundaries = (
            ("complete voice-asset scope", README_ASSET_SCOPE_MARKERS[filename]),
            ("offline privacy boundary", README_PRIVACY_MARKERS[filename]),
            ("manual hook trust boundary", README_HOOK_TRUST_MARKERS[filename]),
        )
        for boundary_name, boundary_markers in localized_boundaries:
            missing_boundary_markers = [
                marker for marker in boundary_markers if marker not in text
            ]
            if missing_boundary_markers:
                fail(
                    "README is missing %s (%s): %s"
                    % (
                        boundary_name,
                        ", ".join(missing_boundary_markers),
                        filename,
                    )
                )


def validate_canonical_pcm_wav(payload: bytes, audio_file: pathlib.Path) -> int:
    """Validate the release's exact 44-byte RIFF/PCM/data layout."""

    if len(payload) < CANONICAL_WAV_HEADER_BYTES:
        fail("truncated WAV header: %s" % audio_file)
    (
        riff_id,
        riff_size,
        wave_id,
        fmt_id,
        fmt_size,
        audio_format,
        channels,
        sample_rate,
        byte_rate,
        block_align,
        bits_per_sample,
        data_id,
        data_size,
    ) = struct.unpack(
        "<4sI4s4sIHHIIHH4sI",
        payload[:CANONICAL_WAV_HEADER_BYTES],
    )
    expected_block_align = channels * bits_per_sample // 8
    expected_byte_rate = sample_rate * expected_block_align
    if (
        riff_id != b"RIFF"
        or riff_size != len(payload) - 8
        or wave_id != b"WAVE"
        or fmt_id != b"fmt "
        or fmt_size != 16
        or audio_format != 1
        or (channels, bits_per_sample // 8, sample_rate) != CANONICAL_WAV_FORMAT
        or bits_per_sample != 16
        or block_align != expected_block_align
        or byte_rate != expected_byte_rate
        or data_id != b"data"
        or data_size != len(payload) - CANONICAL_WAV_HEADER_BYTES
        or data_size % block_align != 0
    ):
        fail(
            "WAV must use the canonical 44-byte mono 16-bit 24 kHz PCM layout "
            "with exact RIFF and data sizes: %s" % audio_file
        )
    return data_size // block_align


def active_rms_dbfs(samples: array.array[int], sample_rate: int) -> float:
    """Return RMS inside the first/last non-silent 20 ms speech windows."""

    window_size = max(1, round(sample_rate * ACTIVE_WINDOW_SECONDS))
    minimum_rms = 32768.0 * (10.0 ** (ACTIVE_WINDOW_MIN_DBFS / 20.0))
    active_windows = []
    for start in range(0, len(samples), window_size):
        window = samples[start : start + window_size]
        if not window:
            continue
        rms = math.sqrt(sum(sample * sample for sample in window) / len(window))
        if rms >= minimum_rms:
            active_windows.append((start, min(start + window_size, len(samples))))
    if not active_windows:
        return float("-inf")

    active_samples = samples[active_windows[0][0] : active_windows[-1][1]]
    rms = math.sqrt(
        sum(sample * sample for sample in active_samples) / len(active_samples)
    )
    return 20.0 * math.log10(rms / 32768.0)


def main() -> int:
    fish_license = ROOT / "FISH_AUDIO_RESEARCH_LICENSE.md"
    if not fish_license.is_file() or fish_license.stat().st_size == 0:
        fail("Fish Audio Research License text must be bundled")
    for forbidden in (
        ROOT / "platform",
        ROOT / "scripts" / "studio_platform.py",
        ROOT / "skills" / "voice-notify-studio",
    ):
        if forbidden.exists():
            fail("public package contains private Studio content: %s" % forbidden)

    manifest = json.loads((ROOT / ".codex-plugin" / "plugin.json").read_text("utf-8"))
    hooks = json.loads((ROOT / "hooks" / "hooks.json").read_text("utf-8"))
    if (
        not isinstance(manifest, dict)
        or set(manifest) != EXPECTED_PLUGIN_MANIFEST_KEYS
    ):
        fail("plugin manifest must contain exactly the release contract keys")
    if manifest["name"] != "codex-voice-notify":
        fail("unexpected plugin name")
    if manifest["license"] != "MIT":
        fail("plugin license must be MIT")
    if manifest["hooks"] != "./hooks/hooks.json":
        fail("plugin manifest must register hooks/hooks.json")
    require_plugin_directory(manifest["skills"], "./skills/", "plugin skills path")
    keywords = manifest["keywords"]
    if (
        not isinstance(keywords, list)
        or not 1 <= len(keywords) <= MAX_KEYWORDS
        or any(
            not isinstance(keyword, str)
            or not keyword.strip()
            or keyword != keyword.strip()
            or len(keyword) > MAX_KEYWORD_CHARS
            for keyword in keywords
        )
        or len({keyword.casefold() for keyword in keywords}) != len(keywords)
    ):
        fail(
            "plugin keywords must contain 1-%d unique non-empty strings of at "
            "most %d characters" % (MAX_KEYWORDS, MAX_KEYWORD_CHARS)
        )
    require_bounded_text(
        manifest,
        "description",
        DESCRIPTION_MAX_CHARS,
        "plugin description",
    )
    author = manifest.get("author")
    if not isinstance(author, dict) or set(author) != EXPECTED_AUTHOR_KEYS:
        fail("plugin author must contain exactly name and url")
    require_bounded_text(
        author,
        "name",
        AUTHOR_NAME_MAX_CHARS,
        "plugin author name",
    )
    require_https_url(author.get("url"), "plugin author URL")
    for url_field in ("homepage", "repository"):
        require_https_url(manifest.get(url_field), "plugin %s" % url_field)

    interface = manifest.get("interface")
    if not isinstance(interface, dict) or set(interface) != EXPECTED_INTERFACE_KEYS:
        fail("plugin interface must contain exactly the release contract keys")
    for interface_field, maximum in INTERFACE_TEXT_LIMITS.items():
        require_bounded_text(
            interface,
            interface_field,
            maximum,
            "plugin interface.%s" % interface_field,
        )
    capabilities = interface.get("capabilities")
    if (
        not isinstance(capabilities, list)
        or not 1 <= len(capabilities) <= MAX_CAPABILITIES
        or any(
            not isinstance(capability, str)
            or not capability.strip()
            or len(capability) > MAX_CAPABILITY_CHARS
            for capability in capabilities
        )
        or len({capability.casefold() for capability in capabilities}) != len(capabilities)
    ):
        fail(
            "plugin interface.capabilities must contain 1-%d unique non-empty "
            "strings of at most %d characters"
            % (MAX_CAPABILITIES, MAX_CAPABILITY_CHARS)
        )
    for url_field in (
        "websiteURL",
        "privacyPolicyURL",
        "termsOfServiceURL",
    ):
        require_https_url(
            interface.get(url_field),
            "plugin interface.%s" % url_field,
        )
    brand_color = interface.get("brandColor")
    if not isinstance(brand_color, str) or not re.fullmatch(
        r"#[0-9A-Fa-f]{6}", brand_color
    ):
        fail("plugin interface.brandColor must use #RRGGBB")
    for asset_field in ("composerIcon", "logo"):
        require_plugin_asset(
            interface.get(asset_field),
            "plugin interface.%s" % asset_field,
        )
    if set(hooks.get("hooks", {})) != EXPECTED_EVENTS:
        fail("hook event set does not match the release contract")
    expected_macos_hook = '/bin/sh "${PLUGIN_ROOT}/hooks/play_notify.sh"'
    expected_windows_hook = (
        'powershell.exe -NoLogo -NoProfile -NonInteractive '
        '-ExecutionPolicy Bypass -File "${PLUGIN_ROOT}\\hooks\\play_notify.ps1"'
    )
    for event_name, groups in hooks["hooks"].items():
        handlers = [
            handler
            for group in groups
            for handler in group.get("hooks", ())
        ]
        if len(handlers) != 1:
            fail("%s must register exactly one command hook" % event_name)
        if handlers[0].get("command") != expected_macos_hook:
            fail("%s must use the system-native macOS shell hook" % event_name)
        if handlers[0].get("commandWindows") != expected_windows_hook:
            fail("%s must preserve the Windows PowerShell hook" % event_name)
    if not re.fullmatch(r"\d+\.\d+\.\d+", str(manifest.get("version", ""))):
        fail("plugin version must be a three-part semantic version")
    validate_multilingual_readmes(manifest["version"])
    default_prompts = interface.get("defaultPrompt", ())
    if (
        not isinstance(default_prompts, list)
        or not 1 <= len(default_prompts) <= 3
        or any(
            not isinstance(prompt, str)
            or not prompt.strip()
            or len(prompt) > 128
            for prompt in default_prompts
        )
    ):
        fail("default prompts must contain 1-3 non-empty strings of at most 128 characters")
    if not any(
        marker in default_prompts[0].lower()
        for marker in ("first-time setup", "set up voice notify")
    ):
        fail("the first default prompt must offer guided first-time setup")
    if "new codex cli terminal" not in default_prompts[0].lower():
        fail("the first default prompt must request a new Codex CLI terminal")
    skill_text = (
        ROOT / "skills" / "voice-notify-settings" / "SKILL.md"
    ).read_text("utf-8")
    for required_text in (
        "## First-time setup",
        "natural-language",
        "--open-hooks",
        "-OpenHooks",
        "voice_notify_config.sh",
        "new visible terminal",
        "starts the verified Codex CLI",
    ):
        if required_text not in skill_text:
            fail("settings skill is missing setup guidance: %s" % required_text)
    safe_trust_guidance = re.compile(
        r"Never edit the trust\s+store or use\s+"
        r"`--dangerously-bypass-hook-trust`\."
    )
    if not safe_trust_guidance.search(skill_text):
        fail("settings skill must preserve the mandatory hook trust boundary")
    setup_launchers = {
        ROOT / "scripts" / "voice_notify_config.py": (
            "/usr/bin/osascript",
            'tell application "Terminal"',
            "activate",
            "exec %s --no-alt-screen -C %s",
        ),
        ROOT / "scripts" / "voice_notify_config.ps1": (
            "Start-Process",
            "-WindowStyle Normal",
            "& '$EscapedCodexPath' --no-alt-screen -C '$EscapedWorkingDirectory'",
        ),
        ROOT / "scripts" / "voice_notify_config.sh": (
            "/usr/bin/osascript",
            "do script terminalCommand",
            'exec $(shell_quote "$codex_path") --no-alt-screen -C',
        ),
    }
    for launcher_path, required_markers in setup_launchers.items():
        launcher_text = launcher_path.read_text("utf-8")
        if "--dangerously-bypass-hook-trust" in launcher_text:
            fail("setup launcher must not bypass hook trust: %s" % launcher_path)
        if "/hooks" not in launcher_text:
            fail("setup launcher must hand off visibly to /hooks: %s" % launcher_path)
        for marker in required_markers:
            if marker not in launcher_text:
                fail(
                    "setup launcher must start the verified Codex CLI in a new "
                    "terminal (%s): %s" % (marker, launcher_path)
                )
    macos_runtime_paths = (
        ROOT / "hooks" / "play_notify.sh",
        ROOT / "scripts" / "voice_notify_config.sh",
    )
    for runtime_path in macos_runtime_paths:
        if not runtime_path.is_file() or runtime_path.stat().st_size == 0:
            fail("missing system-native macOS runtime: %s" % runtime_path)
        runtime_text = runtime_path.read_text("utf-8")
        for forbidden_reference in (
            "/usr/bin/python3",
            "play_notify.py",
            "/dev/stdin",
        ):
            if forbidden_reference in runtime_text:
                fail(
                    "macOS runtime contains an unsupported dependency or stdin path: %s"
                    % runtime_path
                )
    macos_hook_text = (ROOT / "hooks" / "play_notify.sh").read_text("utf-8")
    for required_parser_text in (
        "fileHandleWithStandardInput",
        "JSON.parse(text)",
    ):
        if required_parser_text not in macos_hook_text:
            fail(
                "macOS hook must parse its JSON payload in memory: %s"
                % required_parser_text
            )
    for required_tool in (
        "/bin/sh",
        "/usr/bin/plutil",
        "/usr/bin/afplay",
        "/usr/bin/osascript",
    ):
        if not any(
            required_tool in path.read_text("utf-8")
            for path in macos_runtime_paths
        ):
            fail("macOS runtime is missing system tool reference: %s" % required_tool)

    phrases = json.loads(
        (ROOT / "assets" / "audio" / "phrases.json").read_text("utf-8")
    )
    if set(phrases) != EXPECTED_EVENTS:
        fail("phrases.json event set does not match the release contract")
    expected_phrase_keys = {"file", *EXPECTED_LANGUAGES}
    for event, phrase in phrases.items():
        if not isinstance(phrase, dict) or set(phrase) != expected_phrase_keys:
            fail("%s must contain file plus all five release languages" % event)
        if (
            not isinstance(phrase["file"], str)
            or pathlib.PurePosixPath(phrase["file"]).name != phrase["file"]
            or not phrase["file"].endswith(".wav")
        ):
            fail("invalid release WAV filename for event: %s" % event)
        if any(
            not isinstance(phrase[language], str) or not phrase[language].strip()
            for language in EXPECTED_LANGUAGES
        ):
            fail("missing release phrase text for event: %s" % event)
    if len({phrase["file"] for phrase in phrases.values()}) != len(EXPECTED_EVENTS):
        fail("release events must use distinct WAV filenames")

    expected_matrix = {
        (voice, language, event)
        for voice in EXPECTED_VOICES
        for language in EXPECTED_LANGUAGES
        for event in EXPECTED_EVENTS
    }
    expected_path_by_key = {
        (voice, language, event): (
            pathlib.PurePosixPath("assets")
            / "audio"
            / voice
            / language
            / phrases[event]["file"]
        ).as_posix()
        for voice, language, event in expected_matrix
    }
    expected_paths = set(expected_path_by_key.values())
    if len(expected_paths) != EXPECTED_ASSET_COUNT:
        fail("release audio matrix must contain exactly 100 unique paths")

    audio_files = sorted((ROOT / "assets" / "audio").glob("*/*/*.wav"))
    actual_paths = {
        path.relative_to(ROOT).as_posix() for path in audio_files
    }
    if len(audio_files) != EXPECTED_ASSET_COUNT or actual_paths != expected_paths:
        fail(
            "release WAV paths must match the exact 2-voice, 5-language, "
            "10-event matrix (expected %d, found %d)"
            % (EXPECTED_ASSET_COUNT, len(audio_files))
        )

    durations = []
    wav_metadata_by_path = {}
    active_rms_by_group = {
        (voice, language): []
        for voice in EXPECTED_VOICES
        for language in EXPECTED_LANGUAGES
    }
    for audio_file in audio_files:
        payload = audio_file.read_bytes()
        canonical_frame_count = validate_canonical_pcm_wav(payload, audio_file)
        with wave.open(str(audio_file), "rb") as wav:
            if (wav.getnchannels(), wav.getsampwidth(), wav.getframerate()) != (1, 2, 24000):
                fail("invalid WAV format: %s" % audio_file)
            channels = wav.getnchannels()
            sample_width_bytes = wav.getsampwidth()
            sample_rate = wav.getframerate()
            frame_count = wav.getnframes()
            if frame_count != canonical_frame_count or wav.getcomptype() != "NONE":
                fail("WAV header metadata does not match PCM frames: %s" % audio_file)
            frames = wav.readframes(frame_count)
            duration = frame_count / float(sample_rate)
        if frames != payload[CANONICAL_WAV_HEADER_BYTES:]:
            fail("WAV data bytes do not match the canonical data chunk: %s" % audio_file)
        if not 0.3 <= duration <= 20.0:
            fail("invalid duration: %s" % audio_file)
        samples = array.array("h")
        samples.frombytes(frames)
        if sys.byteorder != "little":
            samples.byteswap()
        if not samples or max(abs(sample) for sample in samples) == 0:
            fail("silent WAV: %s" % audio_file)
        clipped = sum(1 for sample in samples if abs(sample) >= 32767)
        if clipped / float(len(samples)) > 0.005:
            fail("excessive clipping: %s" % audio_file)
        relative_path = audio_file.relative_to(ROOT).as_posix()
        wav_metadata_by_path[relative_path] = {
            "bytes": len(payload),
            "duration_seconds": round(duration, 6),
            "sample_rate": sample_rate,
            "channels": channels,
            "sample_width_bytes": sample_width_bytes,
        }
        group = (audio_file.parent.parent.name, audio_file.parent.name)
        active_rms_by_group[group].append(
            (audio_file, active_rms_dbfs(samples, sample_rate))
        )
        durations.append(duration)

    for (voice, language), levels in active_rms_by_group.items():
        group_median = statistics.median(level for _, level in levels)
        for audio_file, level in levels:
            drop = group_median - level
            if drop > MAX_GROUP_ACTIVE_RMS_DROP_DB:
                fail(
                    "low-volume WAV is %.2f dB below the %s/%s active-RMS "
                    "median (limit %.2f dB): %s"
                    % (
                        drop,
                        voice,
                        language,
                        MAX_GROUP_ACTIVE_RMS_DROP_DB,
                        audio_file,
                    )
                )

    audio_manifest = json.loads(
        (ROOT / "assets" / "audio" / "manifest.json").read_text("utf-8")
    )
    if (
        not isinstance(audio_manifest, dict)
        or set(audio_manifest) != EXPECTED_AUDIO_MANIFEST_KEYS
    ):
        fail("audio manifest must contain exactly the release schema keys")
    if type(audio_manifest["schema_version"]) is not int or audio_manifest["schema_version"] != 1:
        fail("audio manifest schema_version must be integer 1")
    if type(audio_manifest["asset_count"]) is not int:
        fail("audio manifest asset_count must be an integer")
    records = audio_manifest["files"]
    if (
        not isinstance(records, list)
        or audio_manifest["asset_count"] != EXPECTED_ASSET_COUNT
        or len(records) != EXPECTED_ASSET_COUNT
    ):
        fail("audio manifest must contain exactly 100 files")
    if (
        not isinstance(audio_manifest["release"], str)
        or audio_manifest["release"] != manifest.get("version")
    ):
        fail("audio manifest release must match the plugin version")
    record_keys = set()
    record_paths = set()
    for record in records:
        if (
            not isinstance(record, dict)
            or set(record) != EXPECTED_AUDIO_RECORD_KEYS
        ):
            fail("audio manifest records must contain exactly the release record keys")
        event = record["event"]
        voice = record["voice"]
        language = record["language"]
        key = (voice, language, event)
        if key not in expected_matrix or key in record_keys:
            fail("invalid or duplicate audio manifest matrix record")
        record_keys.add(key)
        expected_path = expected_path_by_key[key]
        if record["path"] != expected_path:
            fail("audio manifest path does not match voice, language, and event")
        record_paths.add(expected_path)
        if record["text"] != phrases[event][language]:
            fail("audio manifest text does not match phrases.json")
        path = ROOT / expected_path
        payload = path.read_bytes()
        digest = hashlib.sha256(payload).hexdigest()
        if (
            not isinstance(record["sha256"], str)
            or not re.fullmatch(r"[0-9a-f]{64}", record["sha256"])
            or digest != record["sha256"]
        ):
            fail("audio checksum mismatch: %s" % path)
        expected_metadata = wav_metadata_by_path[expected_path]
        for field, expected_value in expected_metadata.items():
            actual_value = record[field]
            if field == "duration_seconds":
                valid_type = type(actual_value) in (int, float)
            else:
                valid_type = type(actual_value) is int
            if not valid_type or actual_value != expected_value:
                fail(
                    "audio manifest %s mismatch for %s (expected %r, found %r)"
                    % (field, path, expected_value, actual_value)
                )
    if record_keys != expected_matrix or record_paths != expected_paths:
        fail("audio manifest must cover the exact 100-file release matrix")

    evals = json.loads((ROOT / "evals" / "cases.json").read_text("utf-8"))
    if not isinstance(evals, dict) or set(evals) != EXPECTED_EVAL_KEYS:
        fail("evals must contain reviewer positive/negative lists and discovery cases")
    if (
        not isinstance(evals["positive"], list)
        or not isinstance(evals["negative"], list)
        or len(evals["positive"]) != 7
        or len(evals["negative"]) != 3
    ):
        fail("submission evals must contain exactly 7 positive and 3 negative cases")
    normalized_prompts = set()
    for category in ("positive", "negative"):
        for case in evals[category]:
            if not isinstance(case, dict) or set(case) != EXPECTED_EVAL_CASE_KEYS:
                fail("each submission eval must contain exactly prompt and expected")
            for field in EXPECTED_EVAL_CASE_KEYS:
                if not isinstance(case[field], str) or not case[field].strip():
                    fail("submission eval %s must be a non-empty string" % field)
            normalized_prompt = " ".join(case["prompt"].split()).casefold()
            if normalized_prompt in normalized_prompts:
                fail("submission eval prompts must be unique")
            normalized_prompts.add(normalized_prompt)
    validate_discovery_evals(evals["discovery"], normalized_prompts)
    positive_eval_text = "\n".join(
        "%s\n%s" % (case["prompt"], case["expected"])
        for case in evals["positive"]
    ).casefold()
    missing_eval_locales = sorted(
        language
        for language, markers in EVAL_LOCALE_MARKERS.items()
        if not any(marker.casefold() in positive_eval_text for marker in markers)
    )
    if missing_eval_locales:
        fail(
            "positive submission evals must cover all five release locales; "
            "missing: %s" % ", ".join(missing_eval_locales)
        )

    print(
        "PASS: manifest, guided setup, system-native macOS runtime, 10 hooks, "
        "%d WAVs, checksums, format, signal, clipping, 7+3 review evals, "
        "and 10/20/20 discovery evals "
        "(%.2f-%.2fs)"
        % (EXPECTED_ASSET_COUNT, min(durations), max(durations))
    )
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

SHA-256: b6bc57eea6ad30e752848f00783f275e4e1e0d362b7c999e718a3575a43a3b06