Ctrl K
ComunidadGuías…

build_runtime_assets.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Tlamatini Author Banner — Angela López Mendoza
"""Fail-closed runtime asset inventory shared by the application/installer builds.

Only filesystem/ZIP inspection: no Django startup, tests, browsers, or downloads.
The manifest records payload-relative paths, sizes and SHA-256 hashes, not source
paths or configuration values. It is a completeness receipt, NOT a signature.
"""

import ast
import hashlib
import json
import os
from pathlib import Path, PurePosixPath
import re
import shutil
import stat
import subprocess
import zipfile


MANIFEST_NAME = "runtime-assets.json"
MAX_RELEASE_ZIP_BYTES = 1_990_000_000  # decimal GB, not GiB; user release ceiling
STATIC_SOURCE = "Tlamatini/agent/static"
PDFJS = "agent/vendor/pdfjs/"
REQUIRED_STATIC = (
    "agent/js/interaction_settings.js", "agent/js/interaction_speech.js",
    "agent/js/voice_runtime.js", "agent/css/interaction_settings.css",
    # Views must retain the real canvas loaders, divider state and avatar lock.
    "agent/js/agent_page_layout.js", "agent/js/avatar_size.js",
    "agent/js/agent_page_canvas.js", "agent/js/agent_page_init.js",
    "agent/js/agent_page_markdown.js", "agent/css/markdown_canvas.css",
    "agent/js/agent_page_editing.js", "agent/css/canvas_editor.css",
    "agent/interactor/bridge.js", "agent/interactor/interactor.css",
    "agent/vendor/canvas-editor/editor.js", "agent/vendor/canvas-editor/LICENSES.txt",
    "agent/vendor/canvas-editor/manifest.json",
    "agent/vendor/frontend/marked/lib/marked.umd.js",
    "agent/vendor/frontend/marked/LICENSE",
    "agent/vendor/frontend/dompurify/dist/purify.min.js",
    "agent/vendor/frontend/dompurify/LICENSE",
    'agent/css/admin_theme.css',
    "agent/css/ui_tokens.css", "agent/css/ui_shell.css", "agent/css/ui_splitters.css",
    "agent/css/entry_pages.css", "agent/js/ui_splitters.js",
    "agent/js/usage_dashboard.js", "agent/css/usage_dashboard.css",
    "agent/js/chat_dictation.js", "agent/js/mic_settings.js", "agent/css/chat_dictation.css",
    "agent/css/prompt_flow_panel.css",
    "agent/css/flow_canvas.css",
    "agent/js/flow-canvas-interactions.js",
    "agent/js/prompt-flow-panel-model.js", "agent/js/prompt-flow-panel.js",
    "agent/js/acp-editor-tools.js",
    # Terminater dock (Views > Terminals) and its locally vendored terminal renderer.
    "agent/js/terminater_dock.js", "agent/css/terminater_dock.css",
    "agent/vendor/frontend/xterm/lib/xterm.js", "agent/vendor/frontend/xterm/css/xterm.css",
    "agent/vendor/frontend/xterm/LICENSE",
    "agent/vendor/frontend/xterm-addon-fit/lib/addon-fit.js", "agent/vendor/frontend/xterm-addon-fit/LICENSE",
    "agent/vendor/frontend/xterm-addon-web-links/lib/addon-web-links.js",
    "agent/vendor/frontend/xterm-addon-web-links/LICENSE",
    "agent/js/context_gauge.js", "agent/css/context_gauge.css",
    "agent/js/model_capacity.js", "agent/css/model_capacity.css",
    "agent/js/compact_costs.js",
    "agent/js/self_modify_switch.js",
    "agent/js/acp-file-io.js", "agent/js/canvas_item_dialog.js", "agent/js/contextual_menus.js",
    "agent/js/chat_table_contrast.js",
    "agent/js/shared-runtime-dialogs.js", "agent/css/dialog_theme.css",
    "agent/js/avatar.js", "agent/css/avatar.css",
    "agent/img/avatar/eo_mc.jpg", "agent/img/avatar/ec_mc.jpg",
    "agent/img/avatar/eo_mo.jpg", "agent/img/avatar/ec_mo.jpg",
    "agent/pdf/canvas.html", "agent/js/agent_page_pdf.js",
    "agent/js/pdf_canvas_viewer.js", "agent/js/pdf_context_progress.js",
    "agent/css/pdf_canvas.css", "agent/css/pdf_context_progress.css",
    *(PDFJS + name for name in (
        "build/pdf.mjs", "build/pdf.worker.mjs", "web/pdf_viewer.mjs",
        "web/pdf_viewer.css", "LICENSE", "VERSION", "cmaps/LICENSE",
        "standard_fonts/LICENSE_FOXIT", "standard_fonts/LICENSE_LIBERATION",
        "standard_fonts/LiberationSans-Regular.ttf", "iccs/CGATS001Compat-v2-micro.icc",
        "wasm/jbig2.wasm", "wasm/openjpeg.wasm", "wasm/qcms_bg.wasm",
    )),
)
SOURCE_TREES = (
    (STATIC_SOURCE, "_internal/agent/static"),
    ("Tlamatini/staticfiles", "_internal/staticfiles"),
    ("Tlamatini/agent/templates", "_internal/agent/templates"),
    ("Tlamatini/agent/torrenter_workspace", "_internal/agent/torrenter_workspace"),
    ("Tlamatini/agent/agents", "agents"),
    ("Tlamatini/agent/skills_pkg", "agent/skills_pkg"),
    ("Tlamatini/agent/skills_pkg", "_internal/agent/skills_pkg"),
    ("Tlamatini/jd-cli", "jd-cli"),
    ("security", "security"),
)
ROOT_SOURCES = {
    "Tlamatini/agent/templates/agent/interactor.html": "_internal/agent/templates/agent/interactor.html",
    "Tlamatini/agent/templates/agent/interaction_settings.html": "_internal/agent/templates/agent/interaction_settings.html",
    "docs/interactor.md": "docs/interactor.md",
    "Tlamatini/agent/templates/agent/agent_page.html":
        "_internal/agent/templates/agent/agent_page.html",
    "Tlamatini/agent/templates/admin/base_site.html":
        "_internal/agent/templates/admin/base_site.html",
    "Tlamatini/agent/templates/agent/usage_dialog.html":
        "_internal/agent/templates/agent/usage_dialog.html",
    "Tlamatini/agent/chat_voice_settings.py": "chat_voice_settings.py",
    "Tlamatini/agent/agents/whisperer/chat_worker.py": "agents/whisperer/chat_worker.py",
    "Tlamatini/agent/templates/agent/prompt_flow_panel.html":
        "_internal/agent/templates/agent/prompt_flow_panel.html",
    "docs/windows-flow-files.md": "docs/windows-flow-files.md",
    "docs/usage-dashboard.md": "docs/usage-dashboard.md",
    "docs/prompting-flow-designer.md": "docs/prompting-flow-designer.md",
    # Terminater: the shipped self-knowledge and FlowCreator reference point at this contract.
    "docs/terminater.md": "docs/terminater.md",
    "docs/torrenter.md": "docs/torrenter.md",
    # ACP pools and wrapped chat launchers use the loose agent tree. Pin its
    # transport and completion logic in the package/update integrity manifest
    # as well as in the frozen interpreter's hidden imports.
    **{
        "Tlamatini/agent/agents/torrenter/" + name: "agents/torrenter/" + name
        for name in ("client.py", "contract.py", "sources.py", "activity.py", "torrenter.py")
    },
    "Tlamatini/agent/templates/agent/torrenter_sources.html": "_internal/agent/templates/agent/torrenter_sources.html",
    "docs/examples/prompting-kickoff.fpmt": "docs/examples/prompting-kickoff.fpmt",
    "Tlamatini/agent/agents/model_settings.py": "agents/model_settings.py",
    "Tlamatini/agent/agents/visual_errors.py": "agents/visual_errors.py",
    "docs/visual-analysis-errors.md": "docs/visual-analysis-errors.md",
    "build_runtime_assets.py": "build_runtime_assets.py",
    "setup_recovery.py": "setup_recovery.py",
    **{
        "Tlamatini/agent/torrenter_workspace/" + name: "_internal/agent/torrenter_workspace/" + name
        for name in ("workspace.html", "workspace.css", "table.js", "layout.js", "workspace.js")
    },
    "Tlamatini/agent/config.json": "config.json",
    "Tlamatini/agent/prompt.pmt": "prompt.pmt",
    "README.md": "README.md",
    "agents_descriptions.md": "agents_descriptions.md",
    "apply_update.ps1": "apply_update.ps1",
    "preserved_user_state.json": "preserved_user_state.json",
    "Tlamatini/agent/sqlite_copy.py": "sqlite_copy.py",
    "Tlamatini/cat_art.py": "cat_art.py",
    **{name: name for name in (
        "flow_file_associations.ps1", "register_fpmt.ps1", "unregister_fpmt.ps1",
        "register_flw.ps1", "unregister_flw.ps1", "Tlamatini.ps1", "Tlamatini.ico",
        "CreateShortcut.ps1", "RemoveShortcut.ps1", "CreateShortcut.json",
        "freeingport8000.ps1",
    )},
}
RUNTIME_SKIP_NAMES = {
    "__pycache__", "pools", "_chat_runs_", ".tlamatini", ".git", "security_logs",
    ".torrenter-receipt", "torrenter-result.json",
    "_tlamatini_agents_manifest.json",  # generated anew with installed paths/version
}
STATIC_TAG = re.compile(r"\{%\s*static\s+(['\"])(.*?)\1", re.DOTALL)
CSS_URL = re.compile(r"url\(\s*(['\"]?)(.*?)\1\s*\)", re.IGNORECASE)
REMOTE_RESOURCE = re.compile(r"<(?:script|link)\b[^>]*\b(?:src|href)\s*=\s*['\"](?:https?:)?//", re.IGNORECASE)


def ignore_runtime_state(_directory, names):
    """Pool/browser sessions, caches and logs are not agent/skill templates."""
    return [name for name in names if name in RUNTIME_SKIP_NAMES
            or name.lower().endswith((".pyc", ".pyo", ".log"))]


def tree_files(root, *, source=False):
    root = Path(root)
    if not root.is_dir():
        raise RuntimeError(f"Required runtime tree is missing: {root}")
    for current, dirs, files in os.walk(root):
        if source:
            skipped = set(ignore_runtime_state(current, dirs + files))
            dirs[:] = [name for name in dirs if name not in skipped]
            files = [name for name in files if name not in skipped]
        dirs.sort()
        for name in sorted(files):
            path = Path(current) / name
            if not path.resolve().is_relative_to(root.resolve()):
                raise RuntimeError(f"Runtime asset escapes its source tree: {path}")
            yield path


def digest_stream(stream):
    digest = hashlib.sha256()
    size = 0
    for block in iter(lambda: stream.read(1024 * 1024), b""):
        size += len(block)
        digest.update(block)
    return {"bytes": size, "sha256": digest.hexdigest()}


def file_record(path):
    with Path(path).open("rb") as stream:
        return digest_stream(stream)


def require_file(path):
    path = Path(path)
    if not path.is_file() or not path.stat().st_size:
        raise RuntimeError(f"Required runtime asset missing or empty: {path}")


def validate_preservation_contract(repo):
    """Source-only guard: swapping, reinstall and documented state must agree."""
    repo = Path(repo)
    names = json.loads((repo / "preserved_user_state.json").read_text(encoding="utf-8"))["preserve"]
    expected = {name.casefold() for name in names}
    if len(expected) != len(names) or any(
        not name or name in {".", ".."} or any(c in name for c in "/\\:") for name in names
    ):
        raise RuntimeError("Invalid top-level preserved-user-state contract")
    app_code = {"_internal", "agents", "agent", "python", "jre", "git", "ms-playwright",
                "images", "security", "tlamatinisourcecode", "tlamatini.md", "tlamatini.exe",
                "build_runtime_assets.py", "apply_update.ps1", MANIFEST_NAME}
    if expected & app_code:
        raise RuntimeError("Preservation contract pins application code: " + ", ".join(expected & app_code))
    ps = (repo / "apply_update.ps1").read_text(encoding="utf-8")
    block = re.search(r"\$Preserve\s*=\s*@\((.*?)\)", ps, re.DOTALL)
    actual = set(re.findall(r"'([^']+)'", re.sub(r"#[^\n]*", "", block[1]))) if block else set()
    if {name.casefold() for name in actual} != expected:
        raise RuntimeError("apply_update.ps1 preservation differs from preserved_user_state.json")
    installer = ast.parse((repo / "install.py").read_text(encoding="utf-8"))
    fallback = next((ast.literal_eval(node.value) for node in ast.walk(installer)
                     if isinstance(node, ast.Assign) and any(
                         isinstance(t, ast.Name) and t.id == "_PRESERVE_FALLBACK" for t in node.targets)), ())
    if {name.casefold() for name in fallback} != expected:
        raise RuntimeError("Installer preservation fallback differs from the shared contract")
    updater = ast.get_docstring(ast.parse((repo / "Tlamatini/agent/self_update.py").read_text(encoding="utf-8"))) or ""
    block = re.search(r"Preserved across the swap[^\n]*\n\s*\n(.*?)(?:\n\s*\n)", updater, re.DOTALL)
    if not block or {name.casefold() for name in block[1].split()} != expected:
        raise RuntimeError("Updater documentation differs from the preservation contract")
    build_tree = ast.parse((repo / "build.py").read_text(encoding="utf-8"))
    empty_dirs = next((ast.literal_eval(node.value) for node in ast.walk(build_tree)
                       if isinstance(node, ast.Assign) and any(
                           isinstance(t, ast.Name) and t.id == "empty_dirs" for t in node.targets)), ())
    state_dirs = {name.replace("\\", "/").split("/")[0].casefold() for name in empty_dirs}
    if not state_dirs or not state_dirs.issubset(expected):
        raise RuntimeError("Runtime-state directories are not covered by the preservation contract")
    return names


def validate_source_assets(repo, *, check_tracked=True):
    """Catch missing inputs BEFORE dependency installs, DB removal or freezing.

    Builds retain the tracked-deletion check. Explicit file-only audits can
    disable it when Git is forbidden, and must report that coverage limit.
    """
    repo = Path(repo)
    validate_preservation_contract(repo)
    for name in ROOT_SOURCES:
        require_file(repo / name)
    for src, _ in SOURCE_TREES:
        if src == "Tlamatini/staticfiles":  # generated by collectstatic below
            continue
        if not any(tree_files(repo / src, source=True)):
            raise RuntimeError(f"Required runtime source tree is empty: {src}")
    for name in REQUIRED_STATIC:
        require_file(repo / STATIC_SOURCE / name)
    # Locally vendored dependencies must match the pinned download receipt.
    frontend = repo / STATIC_SOURCE / "agent/vendor/frontend"
    require_file(frontend / "manifest.json")
    manifest = json.loads((frontend / "manifest.json").read_text(encoding="utf-8"))
    if manifest.get("schema") != 1 or not manifest.get("files"):
        raise RuntimeError("Missing/invalid frontend vendor manifest; run scripts/vendor_frontend.py")
    for name, record in manifest["files"].items():
        target = (frontend / name).resolve()
        if not target.is_relative_to(frontend.resolve()):
            raise RuntimeError(f"Unsafe frontend manifest path: {name}")
        if not target.is_file() or file_record(target) != record:
            raise RuntimeError(f"Frontend vendor asset missing/modified: {name}; run scripts/vendor_frontend.py")
    # A tracked vendor font, map or asset deleted locally must not disappear from
    # the baseline merely because rglob no longer sees it. Snapshots have no Git.
    if check_tracked and (repo / ".git").exists():
        paths = [src for src, _ in SOURCE_TREES if src != "Tlamatini/staticfiles"]
        result = subprocess.run(
            ["git", "ls-files", "-z", "--", *paths], cwd=repo,
            capture_output=True, check=True,
        )
        missing = [name for name in result.stdout.decode("utf-8").split("\0")
                   if name and not (repo / name).is_file()]
        if missing:
            raise RuntimeError("Tracked runtime source files are missing: " + ", ".join(missing))


def verify_collected_static(repo):
    """Require every source byte and literal static reference in STATIC_ROOT."""
    repo = Path(repo)
    source, collected = repo / STATIC_SOURCE, repo / "Tlamatini/staticfiles"
    count = 0
    for path in tree_files(source, source=True):
        target = collected / path.relative_to(source)
        if not target.is_file() or file_record(path) != file_record(target):
            raise RuntimeError(f"collectstatic omitted or changed a source asset: {target}")
        count += 1
    for template in tree_files(repo / "Tlamatini/agent/templates", source=True):
        if template.suffix == ".html":
            text = template.read_text(encoding="utf-8")
            if REMOTE_RESOURCE.search(text) or re.search(r"\{%\s*bootstrap_(css|javascript)\b", text):
                raise RuntimeError(f"Template depends on remote frontend resources: {template}")
            for _, name in STATIC_TAG.findall(text):
                require_file(collected / name)
    # Local font/icon references are just as essential as the CSS itself.
    for css in collected.rglob("*.css"):
        text = re.sub(r"/\*.*?\*/", "", css.read_text(encoding="utf-8"), flags=re.DOTALL)
        if re.search(r"@import\s+['\"](?:https?:)?//", text, re.IGNORECASE):
            raise RuntimeError(f"Stylesheet imports a remote dependency: {css}")
        for _, value in CSS_URL.findall(text):
            value = value.strip()
            if value.startswith("//") or re.match(r"^https?:", value, re.IGNORECASE):
                raise RuntimeError(f"Stylesheet depends on a remote resource: {css}")
            if not value or value.startswith("#") or re.match(r"^[a-z]+:", value, re.I):
                continue
            value = value.split("?", 1)[0].split("#", 1)[0]
            target = collected / value.removeprefix("/static/") if value.startswith("/static/") else css.parent / value
            require_file(target)
    print(f"Static carriage: {count} source files match collected bytes; literal references resolved.")


def copy_root_sources(repo, dist_root):
    """Copy required loose files from the same inventory used by verification.

    Check every source before copying so an incomplete checkout cannot leave
    a partially refreshed set of root helpers. The final manifest still checks
    these bytes against the baseline captured before freezing.
    """
    repo, dist_root = Path(repo), Path(dist_root)
    for source in ROOT_SOURCES:
        require_file(repo / source)
    for source, destination in ROOT_SOURCES.items():
        target = dist_root / destination
        target.parent.mkdir(parents=True, exist_ok=True)
        shutil.copy2(repo / source, target)
        print(f"Copied required file: {source} -> {target}")


def capture_source_payload(repo, *, self_modify=False):
    """Freeze the expected source-to-install mapping before PyInstaller runs."""
    repo = Path(repo)
    expected = {}
    for src, dst in SOURCE_TREES:
        source = repo / src
        for path in tree_files(source, source=True):
            expected[dst + "/" + path.relative_to(source).as_posix()] = file_record(path)
    for src, dst in ROOT_SOURCES.items():
        expected[dst] = file_record(repo / src)
    if self_modify:
        expected["Tlamatini.md"] = file_record(repo / "Tlamatini/agent/Tlamatini.md")
    return expected


def write_runtime_manifest(dist_root, expected, *, version, self_modify):
    """Check all source carriers and record EVERY resulting payload file."""
    root = Path(dist_root)
    for name, record in expected.items():
        target = root / name
        if not target.is_file() or file_record(target) != record:
            raise RuntimeError(f"Frozen release lost or changed a required source asset: {name}")
    for name in ("Uninstaller.exe", "Tlamatini.exe", "python/python.exe", "jre/bin/java.exe",
                 "git/cmd/git.exe", "jd-cli/jd-cli.jar", "_internal/db.sqlite3",
                 "_internal/pymupdf/_mupdf.pyd", "_internal/pymupdf/_extra.pyd",
                 "_internal/pymupdf/mupdfcpp64.dll"):
        require_file(root / name)
    for patterns in (
        ("chromium-*/**/chrome.exe",),
        ("chromium_headless_shell-*/**/chrome-headless-shell.exe",
         "chromium_headless_shell-*/**/headless_shell.exe"),
    ):
        matches = (path for pattern in patterns for path in (root / "ms-playwright").glob(pattern))
        if not any(path.is_file() and path.stat().st_size for path in matches):
            raise RuntimeError(f"Required Playwright browser payload is missing: {patterns}")
    if self_modify:
        require_file(root / "TlamatiniSourceCode/_SOURCE_SNAPSHOT_MANIFEST.json")
        require_file(root / "TlamatiniSourceCode/build_runtime_assets.py")
    files = {p.relative_to(root).as_posix(): file_record(p)
             for p in tree_files(root) if p.relative_to(root).as_posix() != MANIFEST_NAME}
    document = {"schema": 1, "version": version, "self_modify": self_modify,
                "source_assets_verified": len(expected), "files": files}
    validate_runtime_document(document)
    (root / MANIFEST_NAME).write_text(json.dumps(document, indent=2, sort_keys=True) + "\n", encoding="utf-8")
    print(f"Runtime manifest: {len(expected)} source mappings, {len(files)} payload files.")


def validate_archive_members(archive):
    """Reject paths that Windows would alias, sanitize or extract outside staging."""
    seen = set()
    files = set()
    parents = set()
    devices = {"con", "prn", "aux", "nul", *(f"com{i}" for i in range(1, 10)),
               *(f"lpt{i}" for i in range(1, 10))}
    for item in archive.infolist():
        name = item.filename.rstrip("/") if item.is_dir() else item.filename
        path = PurePosixPath(name)
        invalid = (not name or path.is_absolute() or any(c in name for c in '\\:<>"|?*')
                   or any(ord(c) < 32 for c in name)
                   or path.as_posix() != name or any(part in {".", ".."}
                       or part.rstrip(" .") != part for part in path.parts))
        if invalid or any(part.split(".")[0].casefold() in devices for part in path.parts):
            raise RuntimeError(f"Unsafe release ZIP member: {item.filename}")
        if name.casefold() in seen or stat.S_ISLNK(item.external_attr >> 16):
            raise RuntimeError(f"Duplicate/case-colliding or symlink ZIP member: {item.filename}")
        seen.add(name.casefold())
        if not item.is_dir():
            files.add(name.casefold())
        parents.update(p.as_posix().casefold() for p in path.parents if p.as_posix() != ".")
    if files & parents:
        raise RuntimeError("Release ZIP uses a file as a parent directory")


def validate_runtime_document(document, *, expected_version=None):
    files = document.get("files")
    if document.get("schema") != 1 or not isinstance(files, dict) or not files:
        raise RuntimeError("Invalid or empty runtime asset manifest")
    if expected_version is not None and document.get("version") != expected_version:
        raise RuntimeError(
            f"Runtime package version {document.get('version')!r} differs from the requested "
            f"release {expected_version!r}; rebuild pkg.zip with that version before assembling the installer"
        )
    floor = {"Tlamatini.exe", "python/python.exe", "config.json", "prompt.pmt",
             "apply_update.ps1", "sqlite_copy.py", "preserved_user_state.json",
             "build_runtime_assets.py", "jre/bin/java.exe", "git/cmd/git.exe",
             "_internal/agent/templates/agent/prompt_flow_panel.html",
             "docs/prompting-flow-designer.md", "docs/examples/prompting-kickoff.fpmt",
             "jd-cli/jd-cli.jar", "_internal/db.sqlite3",
             "_internal/pymupdf/_mupdf.pyd", "_internal/pymupdf/_extra.pyd",
             "_internal/pymupdf/mupdfcpp64.dll",
             "_internal/staticfiles/agent/vendor/frontend/manifest.json",
             *ROOT_SOURCES.values(),
             *("_internal/staticfiles/" + name for name in REQUIRED_STATIC)}
    if not floor.issubset(files):
        raise RuntimeError("Runtime manifest omits mandatory files: " + ", ".join(sorted(floor - set(files))))
    tree = any(name.startswith("TlamatiniSourceCode/") for name in files)
    identity = "Tlamatini.md" in files and "_internal/agent/Tlamatini.md" in files
    if document.get("self_modify") is True:
        required = {"TlamatiniSourceCode/" + name for name in (
            "build.py", "build_runtime_assets.py", "copy_source_assets.py",
            "_SOURCE_SNAPSHOT_MANIFEST.json", "_REBUILD_INSTRUCTIONS.md")}
        if not tree or not identity or not required.issubset(files):
            raise RuntimeError("Self-modify package is missing its source snapshot or identity")
    elif document.get("self_modify") is not False or tree or any(
        name.rsplit("/", 1)[-1] == "Tlamatini.md" for name in files
    ):
        raise RuntimeError("Default package unexpectedly carries self-modify source/identity")
    return files


def verify_package(package, *, expected_version=None):
    """Require exact ZIP membership and streamed SHA-256/CRC for every file.

    Used before publishing pkg.zip and again before assembling an installer.
    Old packages without a receipt must be rebuilt, never silently accepted.
    """
    with zipfile.ZipFile(package) as archive:
        validate_archive_members(archive)
        names = [item.filename for item in archive.infolist() if not item.is_dir()]
        folded = [name.casefold() for name in names]
        if len(set(folded)) != len(names):
            raise RuntimeError("Duplicate/case-colliding files in runtime ZIP")
        for name in names:
            path = PurePosixPath(name)
            if path.is_absolute() or ".." in path.parts or "\\" in name or ":" in name:
                raise RuntimeError(f"Unsafe runtime ZIP member: {name}")
        if MANIFEST_NAME not in names:
            raise RuntimeError("pkg.zip has no runtime-assets.json; rebuild with the current build.py")
        document = json.loads(archive.read(MANIFEST_NAME))
        files = validate_runtime_document(document, expected_version=expected_version)
        if set(names) != set(files) | {MANIFEST_NAME}:
            raise RuntimeError("Runtime ZIP membership differs from its completeness manifest")
        for name, record in files.items():
            with archive.open(name) as stream:
                if digest_stream(stream) != record:
                    raise RuntimeError(f"Runtime ZIP file differs from its SHA-256 receipt: {name}")
    print(f"Runtime ZIP verified: {len(files)} files, version {document['version']}.")
    return document


def extract_release_uninstaller(package, destination, record):
    """Fresh installs use the removal client from the verified update payload."""
    target = Path(destination)
    pending = target.with_suffix(".exe.part")
    try:
        with zipfile.ZipFile(package) as archive, archive.open("Uninstaller.exe") as src, pending.open("wb") as out:
            shutil.copyfileobj(src, out)
        if not record or file_record(pending) != record:
            raise RuntimeError("Packaged uninstaller differs from its runtime receipt")
        pending.replace(target)
    finally:
        pending.unlink(missing_ok=True)


def verify_staged_payload(directory, *, expected_version=None):
    """Recheck extracted bytes BEFORE shutdown; never run on a live install."""
    root = Path(directory).resolve()
    document = json.loads((root / MANIFEST_NAME).read_text(encoding="utf-8"))
    files = validate_runtime_document(document, expected_version=expected_version)
    actual = {p.relative_to(root).as_posix() for p in tree_files(root)}
    if actual != set(files) | {MANIFEST_NAME}:
        raise RuntimeError("Staged payload membership differs from its runtime receipt")
    for name, record in files.items():
        target = (root / name).resolve()
        if not target.is_relative_to(root) or file_record(target) != record:
            raise RuntimeError(f"Staged runtime file differs from its receipt: {name}")
    print(f"Staged payload verified: {len(files)} files.")
    return document


def create_release_archive(output_base, *, root_dir, base_dir):
    """Publish the final distributable only within the 1.99 decimal GB budget.

    This covers the OUTER ZIP (installer, its runtime, uninstaller and pkg.zip),
    not just the inner application package. Oversized output is kept as pending
    for inspection; required assets are never automatically pruned to shrink it.
    """
    output = Path(str(output_base) + ".zip")
    pending = shutil.make_archive(str(output_base) + ".pending", "zip",
                                  root_dir=str(root_dir), base_dir=base_dir)
    size = Path(pending).stat().st_size
    if size > MAX_RELEASE_ZIP_BYTES:
        raise RuntimeError(
            f"Final release is {size:,} bytes; limit is {MAX_RELEASE_ZIP_BYTES:,}. "
            f"Oversized archive retained at {pending}; nothing published."
        )
    os.replace(pending, output)
    print(f"Final release size: {size:,} / {MAX_RELEASE_ZIP_BYTES:,} bytes.")
    return str(output)


if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="Read-only release integrity inspection")
    parser.add_argument("--verify-tree", required=True, help="Unmodified extracted staging directory")
    args = parser.parse_args()
    verify_staged_payload(args.verify_tree)