## Development claim / 开发声明
- Domain: `agent-connections`
- Claim: `dsh-connector`
- Base commit: `f31e9580b55564e4bd2566d6383e50ecc58e721c`
- Release preparation: `no`
- Focused validation:
- `cargo test -p petcore --test integration_d --locked`
- `./script/validate_connectors_runtime.sh`
<!-- apc-engineering-coordination-v1
{"claim_overlap_rejections":0,"merge_tree_conflicts":0,"schema_version":"apc.engineering-coordination.v1","task_created_at":"2026-08-18T16:45:48Z","train_conflict_resolutions":0}
-->
Co-authored-by: gd-ops <gd-ops@xmfunny.com>
944 lines
34 KiB
Bash
Executable File
944 lines
34 KiB
Bash
Executable File
#!/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
|