#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" MODE="development" EXPECTED_ARCH="" APP_BUNDLE="$ROOT_DIR/dist/AgentPetCompanion.app" STATIC_ONLY=0 usage() { cat <<'EOF' usage: validate_app_bundle.sh \ [--development|--github-release] \ [--architecture arm64|x86_64] \ [--static-only] \ [APP_BUNDLE] --development validates a local App with an optional ad-hoc signature. --github-release requires the official ad-hoc signature and release identity. It does not claim Developer ID identity, Apple notarization, or Gatekeeper trust. Architecture is required for every non-development mode. --static-only validates structure, signatures, identities, and resources without executing packaged App, PetCore, or CLI code. EOF } while (($# > 0)); do case "$1" in --development) MODE="development" shift ;; --github-release) MODE="github-release" shift ;; --architecture) (($# >= 2)) || { usage >&2; exit 2; } EXPECTED_ARCH="$2" shift 2 ;; --architecture=*) EXPECTED_ARCH="${1#--architecture=}" shift ;; --static-only) STATIC_ONLY=1 shift ;; -h|--help) usage exit 0 ;; -* ) printf 'unknown argument: %s\n' "$1" >&2 usage >&2 exit 2 ;; *) APP_BUNDLE="$1" shift (($# == 0)) || { usage >&2; exit 2; } ;; esac done case "$APP_BUNDLE" in /*) ;; *) APP_BUNDLE="$PWD/$APP_BUNDLE" ;; esac case "$EXPECTED_ARCH" in "") ;; arm64|aarch64) EXPECTED_ARCH="arm64" ;; x86_64|x64|amd64|intel) EXPECTED_ARCH="x86_64" ;; *) printf 'unsupported expected architecture: %s\n' "$EXPECTED_ARCH" >&2 exit 2 ;; esac if [[ "$MODE" != "development" && -z "$EXPECTED_ARCH" ]]; then echo 'non-development validation requires --architecture arm64 or --architecture x86_64' >&2 exit 2 fi HOST_ARCH="$(uname -m)" case "$HOST_ARCH" in aarch64) HOST_ARCH="arm64" ;; amd64) HOST_ARCH="x86_64" ;; esac RUN_PACKAGED_RUNTIME=1 RUNTIME_VALIDATION_SCOPE="packaged functionality" if [[ "$STATIC_ONLY" == "1" ]]; then RUN_PACKAGED_RUNTIME=0 RUNTIME_VALIDATION_SCOPE="static package validation; packaged code not launched" elif [[ -n "$EXPECTED_ARCH" && "$EXPECTED_ARCH" != "$HOST_ARCH" ]]; then # Launching an Intel App on Apple silicon invokes Rosetta and now presents a # system compatibility warning. Cross-architecture release validation stays # static; the matching native archive exercises the shared runtime behavior. RUN_PACKAGED_RUNTIME=0 RUNTIME_VALIDATION_SCOPE="static package validation; cross-architecture runtime not launched" fi APP_NAME="AgentPetCompanion" APP_CONTENTS="$APP_BUNDLE/Contents" APP_BINARY="$APP_CONTENTS/MacOS/$APP_NAME" APP_RESOURCES="$APP_CONTENTS/Resources" PETCORE="$APP_RESOURCES/bin/petcore" PETCORE_CLI="$APP_RESOURCES/bin/petcore-cli" RUNTIME_MANIFEST="$APP_RESOURCES/runtime-manifest.json" INTERACTION_ATTESTATION="$APP_RESOURCES/interaction-attestation.json" # Validation must exercise the evidence shipped with this exact bundle. A # caller such as test_all may export a source-build attestation for earlier # checks; never let that override redirect packaged PetCore or CLI processes. export APC_INTERACTION_ATTESTATION_PATH="$INTERACTION_ATTESTATION" BUNDLED_SKILL="$APP_RESOURCES/skills/agent-pet-studio/SKILL.md" SOURCE_SKILL="$ROOT_DIR/skills/agent-pet-studio/SKILL.md" BUNDLED_PORTABLE_SKILL="$APP_RESOURCES/skills/agent-pet-maker" SOURCE_PORTABLE_SKILL="$ROOT_DIR/skills/agent-pet-maker" SOURCE_CODEX_PLUGIN_MANIFEST="$ROOT_DIR/plugins/codex/.codex-plugin/plugin.json" LOCALIZATION_BUNDLE="$APP_RESOURCES/AgentPetCompanion_AgentPetCompanion.bundle" BUNDLE_ICON="$APP_RESOURCES/AgentPetCompanion.icns" SOURCE_ICON="$ROOT_DIR/logo/macos/AgentPetCompanionTransparent.icns" BUNDLED_BRAND_MARK="$LOCALIZATION_BUNDLE/AgentPetCompanionMark.png" SOURCE_BRAND_MARK="$ROOT_DIR/logo/transparent/agent-pet-mark-transparent-1024.png" BUNDLED_PETS_DIR="$LOCALIZATION_BUNDLE/BuiltInPets" SOURCE_PETS_DIR="$ROOT_DIR/apps/macos/Sources/AgentPetCompanion/Resources/BuiltInPets" TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/apc-bundle-validation.XXXXXX")" . "$ROOT_DIR/script/validation_helpers.sh" apc_use_isolated_home "$TMP_DIR" PETCORE_PID="" cleanup() { if [[ -n "$PETCORE_PID" ]]; then kill "$PETCORE_PID" >/dev/null 2>&1 || true wait "$PETCORE_PID" >/dev/null 2>&1 || true fi rm -rf "$TMP_DIR" } trap cleanup EXIT [[ -d "$APP_BUNDLE" ]] || { echo "app bundle validation failed: missing bundle $APP_BUNDLE" >&2 exit 1 } [[ -x "$APP_BINARY" ]] || { echo "app bundle validation failed: missing executable $APP_BINARY" >&2 exit 1 } [[ -x "$PETCORE" ]] || { echo "app bundle validation failed: missing bundled petcore $PETCORE" >&2 exit 1 } [[ -x "$PETCORE_CLI" ]] || { echo "app bundle validation failed: missing bundled petcore-cli $PETCORE_CLI" >&2 exit 1 } if [[ -n "$EXPECTED_ARCH" ]]; then "$ROOT_DIR/script/validate_macho_architectures.sh" \ --app "$APP_BUNDLE" \ --architecture "$EXPECTED_ARCH" fi if [[ "$MODE" == "github-release" ]]; then # One SDK 26 build must retain the macOS 14 deployment target and weak-link # every macOS 26 Liquid Glass entry point. This is the static proof that the # same archive renders native glass on macOS 26 and the authored material # fallback on macOS 14/15. "$ROOT_DIR/script/validate_macos_build_contract.py" artifact \ --binary "$APP_BINARY" \ --deployment-target 14.0 \ --minimum-sdk 26.0 fi SIGNATURE_PRESENT=0 if [[ -e "$APP_CONTENTS/_CodeSignature" ]]; then SIGNATURE_PRESENT=1 fi if [[ "$MODE" != "development" && "$SIGNATURE_PRESENT" != "1" ]]; then echo 'packaged app bundle validation failed: the outer App requires a signature' >&2 exit 1 fi if [[ "$SIGNATURE_PRESENT" == "1" ]]; then command -v codesign >/dev/null 2>&1 || { echo 'app bundle validation failed: a signature is present but codesign is unavailable' >&2 exit 1 } if ! codesign --verify --deep --strict --verbose=2 "$APP_BUNDLE"; then echo 'app bundle validation failed: strict signature verification failed' >&2 exit 1 fi fi validate_github_release_signature_before_runtime() { local signed_item local signature_metadata for signed_item in "$APP_BINARY" "$PETCORE" "$PETCORE_CLI" "$APP_BUNDLE"; do if ! signature_metadata="$(codesign -d --verbose=4 "$signed_item" 2>&1)"; then printf 'GitHub Release validation failed: cannot inspect signature: %s\n' \ "$signed_item" >&2 exit 1 fi if ! grep -Fx 'Signature=adhoc' <<<"$signature_metadata" >/dev/null; then printf 'GitHub Release validation failed: expected ad-hoc signature: %s\n' \ "$signed_item" >&2 exit 1 fi done python3 - "$APP_CONTENTS/Info.plist" <<'PY' import plistlib import sys with open(sys.argv[1], "rb") as file: info = plistlib.load(file) if info.get("APCReleaseChannel") != "release": raise SystemExit( "GitHub Release validation failed: APCReleaseChannel must be 'release'" ) PY } if [[ "$MODE" == "github-release" ]]; then # Keep this before every invocation of packaged App, PetCore, or CLI code. validate_github_release_signature_before_runtime fi [[ -f "$RUNTIME_MANIFEST" ]] || { echo "app bundle validation failed: missing runtime manifest $RUNTIME_MANIFEST" >&2 exit 1 } [[ -f "$INTERACTION_ATTESTATION" ]] || { echo "app bundle validation failed: missing interaction attestation $INTERACTION_ATTESTATION" >&2 exit 1 } [[ -f "$BUNDLED_SKILL" ]] || { echo "app bundle validation failed: missing bundled agent-pet-studio skill" >&2 exit 1 } [[ -f "$BUNDLED_PORTABLE_SKILL/SKILL.md" ]] || { echo "app bundle validation failed: missing bundled agent-pet-maker skill" >&2 exit 1 } for table in Common PetLibrary Maker Connections Overlay Settings Diagnostics; do table_root="$LOCALIZATION_BUNDLE/Localization/$table" [[ -f "$table_root/$table.xcstrings" ]] || { echo "app bundle validation failed: missing bundled $table String Catalog" >&2 exit 1 } [[ -f "$table_root/en.lproj/$table.strings" ]] || { echo "app bundle validation failed: missing bundled $table English localization" >&2 exit 1 } [[ -f "$table_root/zh-Hans.lproj/$table.strings" ]] || { echo "app bundle validation failed: missing bundled $table Simplified Chinese localization" >&2 exit 1 } done [[ -f "$BUNDLE_ICON" ]] || { echo "app bundle validation failed: missing app icon" >&2 exit 1 } [[ -f "$BUNDLED_BRAND_MARK" ]] || { echo "app bundle validation failed: missing bundled brand mark" >&2 exit 1 } cmp -s "$SOURCE_SKILL" "$BUNDLED_SKILL" || { echo "app bundle validation failed: bundled agent-pet-studio skill differs from source" >&2 exit 1 } if find "$BUNDLED_PORTABLE_SKILL" \ \( -type d -name '__pycache__' -o -type f \( -name '*.pyc' -o -name '*.pyo' \) \) \ -print -quit | grep -q .; then echo "app bundle validation failed: bundled agent-pet-maker contains Python bytecode" >&2 exit 1 fi diff -qr -x '__pycache__' -x '*.pyc' -x '*.pyo' \ "$SOURCE_PORTABLE_SKILL" "$BUNDLED_PORTABLE_SKILL" >/dev/null || { echo "app bundle validation failed: bundled agent-pet-maker skill differs from source" >&2 exit 1 } cmp -s "$SOURCE_ICON" "$BUNDLE_ICON" || { echo "app bundle validation failed: bundled app icon differs from approved transparent icon" >&2 exit 1 } cmp -s "$SOURCE_BRAND_MARK" "$BUNDLED_BRAND_MARK" || { echo "app bundle validation failed: bundled brand mark differs from approved transparent mark" >&2 exit 1 } for petpack_name in pet_xingwutuanzi.petpack pet_bytebudcodex.petpack pet_pinklace.petpack; do [[ -f "$BUNDLED_PETS_DIR/$petpack_name" && ! -L "$BUNDLED_PETS_DIR/$petpack_name" ]] || { echo "app bundle validation failed: missing bundled pet $petpack_name" >&2 exit 1 } cmp -s "$SOURCE_PETS_DIR/$petpack_name" "$BUNDLED_PETS_DIR/$petpack_name" || { echo "app bundle validation failed: bundled pet differs from audited source: $petpack_name" >&2 exit 1 } if [[ "$RUN_PACKAGED_RUNTIME" == "1" ]]; then "$PETCORE_CLI" petpack validate "$BUNDLED_PETS_DIR/$petpack_name" >/dev/null fi done if [[ "$(find "$BUNDLED_PETS_DIR" -mindepth 1 -maxdepth 1 -print | wc -l | tr -d ' ')" != "3" ]]; then echo "app bundle validation failed: bundled pet inventory must contain exactly the three approved entries" >&2 exit 1 fi python3 - "$BUNDLED_PETS_DIR" <<'PY' import json import pathlib import sys import zipfile root = pathlib.Path(sys.argv[1]) expected = { "pet_xingwutuanzi.petpack": ("pet_xingwutuanzi", "low", (192, 208)), "pet_bytebudcodex.petpack": ("pet_bytebudcodex", "standard", (384, 416)), "pet_pinklace.petpack": ("pet_pinklace", "standard", (384, 416)), } for name, (pet_id, quality, size) in expected.items(): path = root / name with zipfile.ZipFile(path) as archive: manifest = json.loads(archive.read("manifest.json")) actual_size = manifest.get("render_size", {}) if ( manifest.get("schema_version") != "apc.petpack.v3" or manifest.get("id") != pet_id or manifest.get("quality") != quality or (actual_size.get("width"), actual_size.get("height")) != size ): raise SystemExit(f"app bundle validation failed: bundled pet contract mismatch: {name}") PY python3 - "$APP_CONTENTS/Info.plist" <<'PY' import plistlib import sys path = sys.argv[1] with open(path, "rb") as file: info = plistlib.load(file) expected = { "CFBundleExecutable": "AgentPetCompanion", "CFBundleIdentifier": "dev.agentpet.companion", "CFBundleIconFile": "AgentPetCompanion.icns", "CFBundleName": "AgentPetCompanion", "CFBundlePackageType": "APPL", "NSPrincipalClass": "NSApplication", } for key, value in expected.items(): actual = info.get(key) if actual != value: raise SystemExit(f"app bundle validation failed: {key}={actual!r}, expected {value!r}") minimum = info.get("LSMinimumSystemVersion") if minimum != "14.0": raise SystemExit( f"app bundle validation failed: LSMinimumSystemVersion={minimum!r}, expected '14.0'" ) version = info.get("CFBundleShortVersionString") build = info.get("CFBundleVersion") build_id = info.get("APCBuildID") release_channel = info.get("APCReleaseChannel") runtime_manifest_schema = info.get("APCRuntimeManifestSchemaVersion") if not isinstance(version, str) or not version.strip(): raise SystemExit("app bundle validation failed: missing CFBundleShortVersionString") if not isinstance(build, str) or not build.strip(): raise SystemExit("app bundle validation failed: missing CFBundleVersion") if not isinstance(build_id, str) or not build_id.strip(): raise SystemExit("app bundle validation failed: missing APCBuildID") if release_channel not in {"develop", "release"}: raise SystemExit("app bundle validation failed: invalid APCReleaseChannel") if runtime_manifest_schema != "apc.runtime-manifest.v1": raise SystemExit("app bundle validation failed: invalid APCRuntimeManifestSchemaVersion") exported_types = info.get("UTExportedTypeDeclarations") if not isinstance(exported_types, list): raise SystemExit("app bundle validation failed: missing UTExportedTypeDeclarations") petpack = next( ( declaration for declaration in exported_types if declaration.get("UTTypeIdentifier") == "dev.agentpet.petpack" ), None, ) if petpack is None: raise SystemExit("app bundle validation failed: missing dev.agentpet.petpack UTI") if "public.data" not in petpack.get("UTTypeConformsTo", []): raise SystemExit("app bundle validation failed: dev.agentpet.petpack must conform to public.data") extensions = petpack.get("UTTypeTagSpecification", {}).get("public.filename-extension", []) if extensions != ["petpack"]: raise SystemExit( "app bundle validation failed: dev.agentpet.petpack filename extension must be petpack" ) mime = petpack.get("UTTypeTagSpecification", {}).get("public.mime-type") if mime != "application/vnd.agentpet.petpack+zip": raise SystemExit( "app bundle validation failed: dev.agentpet.petpack MIME type is invalid" ) PY python3 - "$APP_CONTENTS/Info.plist" "$RUNTIME_MANIFEST" <<'PY' import json import plistlib import sys with open(sys.argv[1], "rb") as file: info = plistlib.load(file) with open(sys.argv[2], "r", encoding="utf-8") as file: manifest = json.load(file) expected = { "schema_version": "apc.runtime-manifest.v1", "release_channel": info["APCReleaseChannel"], "app_version": info["CFBundleShortVersionString"], "app_build": info["CFBundleVersion"], "build_id": info["APCBuildID"], "petcore_build_id": info["APCBuildID"], "petcore_cli_build_id": info["APCBuildID"], } for key, value in expected.items(): if manifest.get(key) != value: raise SystemExit( f"app bundle validation failed: runtime manifest {key}=" f"{manifest.get(key)!r}, expected {value!r}" ) PY python3 -B - \ "$ROOT_DIR" \ "$APP_CONTENTS/Info.plist" \ "$INTERACTION_ATTESTATION" \ "$ROOT_DIR/script/interaction-contract-files.txt" <<'PY' import hashlib import json import pathlib import plistlib import stat import sys root = pathlib.Path(sys.argv[1]).resolve() with open(sys.argv[2], "rb") as file: info = plistlib.load(file) attestation_path = pathlib.Path(sys.argv[3]) attestation = json.loads(attestation_path.read_text(encoding="utf-8")) expected_keys = { "schema_version", "build_id", "interaction_contract_digest", "passed_suites", "ok", } if set(attestation) != expected_keys: raise SystemExit("app bundle validation failed: interaction attestation fields are not closed") if attestation["schema_version"] != "apc.overlay-interaction-attestation.v1": raise SystemExit("app bundle validation failed: interaction attestation schema mismatch") if attestation["build_id"] != info["APCBuildID"] or attestation["ok"] is not True: raise SystemExit("app bundle validation failed: interaction attestation build identity mismatch") expected_suites = [ "OverlayPlacementAuthorityTests", "AppStoreOverlaySnapshotTests", "OverlayGeometryTests", "OverlayDisplayWidthTests", "OverlayInteractionTelemetryTests", ] if attestation["passed_suites"] != expected_suites: raise SystemExit("app bundle validation failed: interaction attestation suites are incomplete") entries = [ line for line in pathlib.Path(sys.argv[4]).read_text(encoding="utf-8").splitlines() if line ] if not entries or entries != sorted(set(entries)): raise SystemExit("app bundle validation failed: interaction contract file list is invalid") digest = hashlib.sha256() for entry in entries: relative = pathlib.PurePosixPath(entry) if relative.is_absolute() or any(part in {"", ".", ".."} for part in relative.parts): raise SystemExit("app bundle validation failed: unsafe interaction contract path") path = root.joinpath(*relative.parts) metadata = path.lstat() if not stat.S_ISREG(metadata.st_mode) or path.is_symlink(): raise SystemExit("app bundle validation failed: invalid interaction contract file") digest.update(entry.encode("utf-8")) digest.update(b"\0") digest.update(path.read_bytes()) digest.update(b"\0") if attestation["interaction_contract_digest"] != digest.hexdigest(): raise SystemExit("app bundle validation failed: interaction attestation digest is stale") PY grep -q '^name: agent-pet-studio$' "$BUNDLED_SKILL" || { echo 'app bundle validation failed: bundled Studio skill name is invalid' >&2 exit 1 } grep -q 'APC_PETCORE_CLI' "$BUNDLED_SKILL" || { echo 'app bundle validation failed: bundled Studio skill omits the PetCore CLI contract' >&2 exit 1 } grep -q 'security.md' "$BUNDLED_SKILL" || { echo 'app bundle validation failed: bundled Studio skill omits the shared security contract' >&2 exit 1 } grep -q 'Do not read authentication files' \ "$BUNDLED_PORTABLE_SKILL/references/security.md" || { echo 'app bundle validation failed: bundled security contract omits the agent-auth guardrail' >&2 exit 1 } grep -q '^name: agent-pet-maker$' "$BUNDLED_PORTABLE_SKILL/SKILL.md" || { echo 'app bundle validation failed: bundled Maker skill name is invalid' >&2 exit 1 } grep -q 'capability-missing' "$BUNDLED_PORTABLE_SKILL/SKILL.md" || { echo 'app bundle validation failed: bundled Maker skill omits capability preflight' >&2 exit 1 } [[ -x "$BUNDLED_PORTABLE_SKILL/scripts/petpack_workspace.py" ]] || { echo 'app bundle validation failed: bundled Maker workspace helper is not executable' >&2 exit 1 } [[ -x "$BUNDLED_PORTABLE_SKILL/scripts/prepare_transparent_frames.py" ]] || { echo 'app bundle validation failed: bundled Maker transparent-frame helper is not executable' >&2 exit 1 } grep -q 'transparent-frame-production.md' "$BUNDLED_SKILL" || { echo 'app bundle validation failed: bundled Studio skill omits the shared transparency contract' >&2 exit 1 } if [[ "$RUN_PACKAGED_RUNTIME" == "1" ]]; then # Run the exact native packaged Swift/AppKit executable through its # prohibited-activation validation mode. This exercises packaged # optimization, resources, overlay geometry, keyboard accessibility, and # frame-pipeline wiring without opening windows or taking user input. UI_VALIDATION_OUTPUT="$( APC_HOME="$TMP_DIR/ui-validation-home" \ APC_DISABLE_LAUNCH_AGENT=1 \ APC_DISABLE_CODEX_APP_SERVER_AUTO=1 \ "$APP_BINARY" --run-ui-validation )" grep -q '^AgentPetCompanionUIValidation ok: [0-9][0-9]*/[0-9][0-9]* checks passed$' \ <<<"$UI_VALIDATION_OUTPUT" || { echo 'app bundle validation failed: packaged UI validation did not complete' >&2 printf '%s\n' "$UI_VALIDATION_OUTPUT" >&2 exit 1 } printf '%s\n' "$UI_VALIDATION_OUTPUT" APC_HOME="$TMP_DIR/home" "$PETCORE" preflight \ --home "$TMP_DIR/home" \ --manifest "$RUNTIME_MANIFEST" >/dev/null APC_HOME="$TMP_DIR/home" "$PETCORE" init BUNDLE_BUILD_ID="$(python3 - "$APP_CONTENTS/Info.plist" <<'PY' import plistlib import sys with open(sys.argv[1], "rb") as file: print(plistlib.load(file)["APCBuildID"]) PY )" PRODUCTION_INTERACTION="$("$PETCORE_CLI" petpack verify-production-interaction)" JSON="$PRODUCTION_INTERACTION" BUNDLE_BUILD_ID="$BUNDLE_BUILD_ID" python3 - <<'PY' import json import os value = json.loads(os.environ["JSON"]) expected_suites = [ "OverlayPlacementAuthorityTests", "AppStoreOverlaySnapshotTests", "OverlayGeometryTests", "OverlayDisplayWidthTests", "OverlayInteractionTelemetryTests", ] assert value["ok"] is True, value assert value["build_id"] == os.environ["BUNDLE_BUILD_ID"], value assert value["interaction_evidence"] == expected_suites, value assert len(value["interaction_contract_digest"]) == 64, value PY BUDGET="$("$PETCORE_CLI" renderer budget --quality standard --frame-count 40)" JSON="$BUDGET" python3 - <<'PY' import json import os data = json.loads(os.environ["JSON"]) assert data["quality"] == "standard", data assert data["frame_count"] == 40, data assert data["runtime_cache_frame_limit"] == 40, data assert data["uses_ring_cache"] is False, data assert data["decoded_state_mb"] > 0, data PY HIGH_BUDGET="$("$PETCORE_CLI" renderer budget --quality high --frame-count 40)" JSON="$HIGH_BUDGET" python3 - <<'PY' import json import os data = json.loads(os.environ["JSON"]) assert data["quality"] == "high", data assert data["frame_count"] == 40, data assert data["runtime_cache_frame_limit"] == 40, data assert data["uses_ring_cache"] is False, data assert data["decoded_state_mb"] > 0, data PY ( APC_HOME="$TMP_DIR/home" \ APC_AGENT_CONFIG_HOME="$TMP_DIR/agent-home" \ APC_DISABLE_CODEX_APP_SERVER_AUTO=1 \ APC_EXPECTED_BUILD_ID="$BUNDLE_BUILD_ID" \ APC_EXPECTED_RUNTIME_MANIFEST="$RUNTIME_MANIFEST" \ "$PETCORE" serve --ready-file "$TMP_DIR/ready" ) & PETCORE_PID="$!" for _ in {1..100}; do [[ -f "$TMP_DIR/ready" ]] && break sleep 0.05 done [[ -f "$TMP_DIR/ready" ]] HEALTH="$(APC_HOME="$TMP_DIR/home" "$PETCORE_CLI" health)" HEALTH="$HEALTH" BUNDLE_BUILD_ID="$BUNDLE_BUILD_ID" RUNTIME_MANIFEST="$RUNTIME_MANIFEST" python3 - <<'PY' import json import os health = json.loads(os.environ["HEALTH"]) expected = os.environ["BUNDLE_BUILD_ID"] with open(os.environ["RUNTIME_MANIFEST"], "r", encoding="utf-8") as file: manifest = json.load(file) if health.get("build_id") != expected: raise SystemExit( f"app bundle validation failed: PetCore build_id={health.get('build_id')!r}, expected {expected!r}" ) if health.get("runtime_manifest") != manifest: raise SystemExit("app bundle validation failed: PetCore runtime manifest mismatch") PY # Exercise the packaged daemon, CLI and exact packaged resource directory as # one clean-home seed path. Static digest checks above prove provenance; this # closes the release gate by proving the installed summaries, active choice # and database schema that the App's first snapshot consumes. BUNDLED_SEED="$( APC_HOME="$TMP_DIR/home" "$PETCORE_CLI" petpack seed-bundled \ --inventory-root "$BUNDLED_PETS_DIR" )" BUNDLED_PETS="$(APC_HOME="$TMP_DIR/home" "$PETCORE_CLI" pet list)" BUNDLED_SEED="$BUNDLED_SEED" \ BUNDLED_PETS="$BUNDLED_PETS" \ BUNDLED_HOME="$TMP_DIR/home" \ BUNDLED_DB="$TMP_DIR/home/agent-pet.sqlite" \ python3 - <<'PY' import json import os import pathlib import sqlite3 import struct PNG_SIGNATURE = b"\x89PNG\r\n\x1a\n" RUNTIME_ASSET_SCHEMA = "apc.runtime-assets.v3" REQUIRED_STATES = ( "idle", "thinking", "tool", "waiting", "done", "failed", "acknowledge", "drag_left", "drag_right", ) def png_dimensions(path): if path.is_symlink() or not path.is_file(): raise SystemExit( f"app bundle validation failed: render asset is not a regular file: {path}" ) with path.open("rb") as file: header = file.read(24) if ( len(header) != 24 or header[:8] != PNG_SIGNATURE or header[12:16] != b"IHDR" ): raise SystemExit( f"app bundle validation failed: render asset is not a structurally valid PNG: {path}" ) width, height = struct.unpack(">II", header[16:24]) if width <= 0 or height <= 0: raise SystemExit( f"app bundle validation failed: render asset has invalid dimensions: {path}" ) return width, height def require_managed(path, root, label, pet_id): if path.is_symlink(): raise SystemExit( f"app bundle validation failed: {label} must not be a symlink: {pet_id}" ) resolved = path.resolve() if root != resolved and root not in resolved.parents: raise SystemExit( f"app bundle validation failed: {label} is outside the managed pet store: {pet_id}" ) return resolved seed = json.loads(os.environ["BUNDLED_SEED"]) pets = json.loads(os.environ["BUNDLED_PETS"]) expected_ids = ["pet_xingwutuanzi", "pet_bytebudcodex", "pet_pinklace"] outcomes = seed.get("outcomes") if seed.get("inventory") != "apc.bundled-pets.v1": raise SystemExit("app bundle validation failed: bundled seed inventory mismatch") if not isinstance(outcomes, list) or [item.get("pet_id") for item in outcomes] != expected_ids: raise SystemExit("app bundle validation failed: bundled seed outcomes mismatch") if any(item.get("status") != "installed" for item in outcomes): raise SystemExit("app bundle validation failed: clean-home bundled seed did not install all pets") by_id = {pet.get("id"): pet for pet in pets if isinstance(pet, dict)} if len(pets) != 3 or set(by_id) != set(expected_ids): raise SystemExit("app bundle validation failed: clean-home library does not contain exactly all bundled pets") managed_pets_root = pathlib.Path(os.environ["BUNDLED_HOME"]).resolve() / "pets" for pet_id in expected_ids: pet = by_id[pet_id] if pet.get("origin") != "verified_skill_source": raise SystemExit(f"app bundle validation failed: bundled origin mismatch: {pet_id}") if pet.get("generator") != "agent-pet-companion.release-inventory": raise SystemExit(f"app bundle validation failed: bundled generator mismatch: {pet_id}") if pet.get("provenance") != "apc.bundled-pets.v1": raise SystemExit(f"app bundle validation failed: bundled provenance mismatch: {pet_id}") package_path = pathlib.Path(pet.get("petpack_path", "")) if package_path.is_symlink() or not package_path.is_file(): raise SystemExit(f"app bundle validation failed: installed bundled package is unavailable: {pet_id}") package_path = require_managed( package_path, managed_pets_root, "installed bundled package", pet_id ) cover_path = require_managed( pathlib.Path(pet.get("cover_path", "")), managed_pets_root, "bundled cover", pet_id, ) expected_cover_path = package_path.parent / f"{pet_id}-cover.png" if cover_path != expected_cover_path: raise SystemExit( "app bundle validation failed: bundled cover path is not canonical: " f"{pet_id}; got {cover_path}, expected {expected_cover_path}" ) png_dimensions(cover_path) render_size = pet.get("render_size") timings = pet.get("states") if ( not isinstance(render_size, dict) or not isinstance(render_size.get("width"), int) or not isinstance(render_size.get("height"), int) or not isinstance(timings, list) or tuple(sorted(item.get("name") for item in timings if isinstance(item, dict))) != tuple(sorted(REQUIRED_STATES)) ): raise SystemExit( f"app bundle validation failed: bundled render contract is malformed: {pet_id}" ) frames_root = require_managed( package_path.parent / f"{pet_id}-frames", managed_pets_root, "bundled runtime frames", pet_id, ) if frames_root.is_symlink() or not frames_root.is_dir(): raise SystemExit( f"app bundle validation failed: bundled runtime frame directory is unavailable: {pet_id}" ) marker_path = frames_root / ".apc-runtime-assets.json" if marker_path.is_symlink() or not marker_path.is_file(): raise SystemExit( f"app bundle validation failed: bundled runtime completion marker is unavailable: {pet_id}" ) with marker_path.open("r", encoding="utf-8") as file: marker = json.load(file) if ( marker.get("schema_version") != RUNTIME_ASSET_SCHEMA or marker.get("pet_id") != pet_id or marker.get("render_size") != render_size or marker.get("timing") != timings or not isinstance(marker.get("frame_counts"), dict) or tuple(sorted(marker["frame_counts"])) != tuple(sorted(REQUIRED_STATES)) ): raise SystemExit( f"app bundle validation failed: bundled runtime marker mismatch: {pet_id}" ) expected_dimensions = (render_size["width"], render_size["height"]) timing_by_state = {item["name"]: item for item in timings} for state in REQUIRED_STATES: timing = timing_by_state[state] durations = timing.get("frame_durations_ms") if ( not isinstance(durations, list) or not (2 <= len(durations) <= 40) or any(not isinstance(value, int) or not (50 <= value <= 2000) for value in durations) or sum(durations) > 5000 ): raise SystemExit( f"app bundle validation failed: bundled state timing is invalid: {pet_id}/{state}" ) expected_count = len(durations) state_dir = frames_root / state if state_dir.is_symlink() or not state_dir.is_dir(): raise SystemExit( f"app bundle validation failed: bundled runtime state is unavailable: {pet_id}/{state}" ) frames = sorted( path for path in state_dir.iterdir() if path.suffix.lower() == ".png" ) if len(frames) != expected_count or marker["frame_counts"].get(state) != expected_count: raise SystemExit( f"app bundle validation failed: bundled runtime frame count mismatch: {pet_id}/{state}" ) for frame in frames: if png_dimensions(frame) != expected_dimensions: raise SystemExit( f"app bundle validation failed: bundled runtime frame dimensions mismatch: {pet_id}/{state}" ) if by_id["pet_xingwutuanzi"].get("active") is not True: raise SystemExit("app bundle validation failed: first clean-home bundled pet is not active") if by_id["pet_bytebudcodex"].get("active") is not False: raise SystemExit("app bundle validation failed: second clean-home bundled pet unexpectedly became active") with sqlite3.connect(os.environ["BUNDLED_DB"]) as database: schema_version = database.execute("PRAGMA user_version").fetchone()[0] if schema_version != 7: raise SystemExit( f"app bundle validation failed: bundled seed changed database schema to {schema_version}, expected 7" ) PY for source in codex claude_code pi opencode dsh; do APC_HOME="$TMP_DIR/home" "$PETCORE_CLI" connections repair --source "$source" >/dev/null done INSTALLED_CODEX_PLUGIN="$TMP_DIR/agent-home/.agents/plugins/plugins/agent-pet-companion" if ! python3 - \ "$SOURCE_CODEX_PLUGIN_MANIFEST" \ "$INSTALLED_CODEX_PLUGIN/.codex-plugin/plugin.json" <<'PY' import json import pathlib import sys MAX_PLUGIN_MANIFEST_BYTES = 64 * 1024 def reject_duplicate_keys(pairs): value = {} for key, item in pairs: if key in value: raise ValueError("duplicate JSON object key") value[key] = item return value def load_manifest(raw_path): path = pathlib.Path(raw_path) if path.is_symlink() or not path.is_file(): raise ValueError("plugin manifest is not a regular file") size = path.stat().st_size if size <= 0 or size > MAX_PLUGIN_MANIFEST_BYTES: raise ValueError("plugin manifest has an invalid size") return json.loads( path.read_text(encoding="utf-8"), object_pairs_hook=reject_duplicate_keys, ) try: source = load_manifest(sys.argv[1]) installed = load_manifest(sys.argv[2]) except (OSError, UnicodeError, ValueError, json.JSONDecodeError): raise SystemExit(1) raise SystemExit(0 if source == installed else 1) PY then echo 'app bundle validation failed: packaged PetCore emitted a stale Codex plugin manifest' >&2 exit 1 fi cmp -s \ "$BUNDLED_SKILL" \ "$INSTALLED_CODEX_PLUGIN/skills/agent-pet-studio/SKILL.md" || { echo 'app bundle validation failed: packaged PetCore emitted a stale Studio Skill' >&2 exit 1 } while IFS= read -r -d '' source_path; do relative_path="${source_path#"$BUNDLED_PORTABLE_SKILL/"}" cmp -s \ "$source_path" \ "$INSTALLED_CODEX_PLUGIN/skills/agent-pet-maker/$relative_path" || { printf 'app bundle validation failed: packaged PetCore emitted stale Maker Skill file %s\n' \ "$relative_path" >&2 exit 1 } done < <( find "$BUNDLED_PORTABLE_SKILL" \ -type d -name __pycache__ -prune -o \ -type f ! -name '*.pyc' ! -name '*.pyo' -print0 ) grep -qF "$PETCORE_CLI" \ "$INSTALLED_CODEX_PLUGIN/hooks/hooks.json" CLAUDE_HELPER="$TMP_DIR/home/connectors/claude-code/agent-pet-companion-hook.sh" grep -qF "$CLAUDE_HELPER" "$TMP_DIR/agent-home/.claude/settings.json" grep -qF "$PETCORE_CLI" "$CLAUDE_HELPER" grep -qF "$PETCORE_CLI" "$TMP_DIR/agent-home/.pi/agent/extensions/agent-pet-companion.ts" grep -qF "$PETCORE_CLI" "$TMP_DIR/agent-home/.config/opencode/plugins/agent-pet-companion.js" fi if [[ "$MODE" == "github-release" ]]; then printf 'GitHub Release App bundle validation ok (%s, ad-hoc signature, %s)\n' \ "$EXPECTED_ARCH" "$RUNTIME_VALIDATION_SCOPE" elif [[ "$SIGNATURE_PRESENT" == "1" ]]; then if [[ "$STATIC_ONLY" == "1" ]]; then echo 'Development app bundle static validation ok (signature present and strictly valid)' else echo 'Development app bundle validation ok (signature present and strictly valid)' fi else if [[ "$STATIC_ONLY" == "1" ]]; then echo 'Development app bundle static validation ok (no outer signature present)' else echo 'Development app bundle validation ok (no outer signature present)' fi fi