#!/usr/bin/env bash set -euo pipefail expected_machine_id="caa7b1019517470f9d1368b6e79db49e" expected_source_commit="${1:-}" release_root="$(cd "$(dirname "$0")/../.." && pwd)" source_commit_file="${release_root}/SOURCE-COMMIT" state_root="/var/lib/guanghu/personas/guanghu/hlcc-v16.0.1" repository_root="${state_root}/data/repositories/bingshuo" runtime_root="/var/lib/guanghu/personas/guanghu/fifth-domain-daily" receipt_root="/var/lib/guanghu/architecture-provision/receipts" receipt="${receipt_root}/JD-PERSONA-FIFTH-DOMAIN-DAILY-20260806.json" archive_root="/var/lib/guanghu/legacy-archives/persona-writers/20260806-fifth-domain-daily" shared_secret_file="/etc/guanghu/persona-secrets/shared-deepseek.env" temporary="$(mktemp -d)" chmod 0711 "$temporary" backup_root="${temporary}/units" completed=0 writers=( chenglu-agent.service chenglu-daily.timer guideng-agent.service kezhou-daily.timer ) oneshots=( chenglu-daily.service kezhou-daily.service ) handshakes=( chenglu-team-handshake.service guideng-team-handshake.service kezhou-team-handshake.service ) new_units=( chenglu-team-handshake.service guideng-team-handshake.service kezhou-team-handshake.service zhuyuan-persona-fifth-domain-daily.service zhuyuan-persona-fifth-domain-daily.timer ) declare -A old_repositories=( [chenglu-agent]="/var/lib/guanghu/forgejo/repositories/bingshuo/chenglu-agent.git" [guideng]="/var/lib/guanghu/forgejo/repositories/bingshuo/guideng.git" [kezhou]="/var/lib/guanghu/forgejo/repositories/bingshuo/kezhou.git" ) restore_units() { for unit in "${new_units[@]}"; do if test -f "${backup_root}/${unit}"; then cp "${backup_root}/${unit}" "/etc/systemd/system/${unit}" else rm -f "/etc/systemd/system/${unit}" fi done systemctl daemon-reload } rollback() { systemctl disable --now zhuyuan-persona-fifth-domain-daily.timer \ >/dev/null 2>&1 || true systemctl stop zhuyuan-persona-fifth-domain-daily.service \ >/dev/null 2>&1 || true restore_units if test -f "${temporary}/shared-deepseek.env"; then cp "${temporary}/shared-deepseek.env" "$shared_secret_file" chmod 0600 "$shared_secret_file" fi systemctl enable --now guanghu-forgejo.service >/dev/null 2>&1 || true for unit in "${writers[@]}"; do systemctl enable --now "$unit" >/dev/null 2>&1 || true done for unit in "${handshakes[@]}"; do systemctl restart "$unit" >/dev/null 2>&1 || true done } on_exit() { status=$? trap - EXIT if test "$completed" = 0; then rollback fi rm -rf "$temporary" exit "$status" } trap on_exit EXIT test "$(id -u)" = "0" test "$(cat /etc/machine-id)" = "$expected_machine_id" test -n "$expected_source_commit" test "$expected_source_commit" = "$(tr -d '\n' <"$source_commit_file")" test "$(basename "$release_root")" = "$expected_source_commit" test -f "${release_root}/server-tools/persona-team-handshake/fifth-domain-daily-orchestrator.mjs" test -f "${release_root}/server-tools/persona-team-handshake/fifth-domain-daily.config.server.json" test -f "$shared_secret_file" test "$(systemctl is-active hlcc-jd-candidate.service)" = "active" test "$(curl -fsS http://127.0.0.1:3341/health | jq -r .ready)" = "true" mkdir -p "$backup_root" "$archive_root" "$receipt_root" "$runtime_root" cp "$shared_secret_file" "${temporary}/shared-deepseek.env" chmod 0600 "${temporary}/shared-deepseek.env" if ! grep -q '^PERSONA_TEAM_CONTROLLER_TOKEN=' "$shared_secret_file"; then controller_token="$(openssl rand -hex 32)" test "${#controller_token}" = "64" printf '\nPERSONA_TEAM_CONTROLLER_TOKEN=%s\n' "$controller_token" \ >>"$shared_secret_file" chmod 0600 "$shared_secret_file" fi chown guanghu:guanghu "$runtime_root" chmod 0700 "$runtime_root" for unit in "${new_units[@]}"; do if test -f "/etc/systemd/system/${unit}"; then cp "/etc/systemd/system/${unit}" "${backup_root}/${unit}" fi done for unit in "${writers[@]}" "${oneshots[@]}" guanghu-forgejo.service; do systemctl cat "$unit" >"${archive_root}/${unit}.txt" 2>&1 || true done source_paths=() for candidate in /opt/chenglu-agent /opt/guideng/agent /opt/kezhou/agent; do if test -e "$candidate"; then source_paths+=("$candidate") fi done if test "${#source_paths[@]}" -gt 0; then tar -czf "${archive_root}/legacy-persona-writer-source.tar.gz" \ "${source_paths[@]}" sha256sum "${archive_root}/legacy-persona-writer-source.tar.gz" \ >"${archive_root}/legacy-persona-writer-source.tar.gz.sha256" fi for name in chenglu-agent guideng kezhou; do test -d "${old_repositories[$name]}" test -d "${repository_root}/${name}.git" git --git-dir="${old_repositories[$name]}" \ fsck --connectivity-only --no-dangling git --git-dir="${repository_root}/${name}.git" \ fsck --connectivity-only --no-dangling done for unit in "${writers[@]}"; do systemctl disable --now "$unit" done for unit in "${oneshots[@]}"; do systemctl stop "$unit" || true done for unit in "${writers[@]}" "${oneshots[@]}"; do test "$(systemctl is-active "$unit" || true)" != "active" done for name in chenglu-agent guideng kezhou; do bundle="${temporary}/${name}.bundle" git --git-dir="${old_repositories[$name]}" bundle create "$bundle" \ --branches --tags chown guanghu:guanghu "$bundle" runuser -u guanghu -- git --git-dir="${repository_root}/${name}.git" \ fetch "$bundle" \ "+refs/heads/*:refs/heads/*" \ "+refs/tags/*:refs/tags/*" old_refs="$( git --git-dir="${old_repositories[$name]}" for-each-ref \ --format='%(refname) %(objectname)' refs/heads refs/tags | sort )" new_refs="$( git --git-dir="${repository_root}/${name}.git" for-each-ref \ --format='%(refname) %(objectname)' refs/heads refs/tags | sort )" test "$new_refs" = "$old_refs" done for unit in "${handshakes[@]}"; do sed "s|__RELEASE_ROOT__|${release_root}|g" \ "${release_root}/server-tools/persona-team-handshake/${unit}" \ >"/etc/systemd/system/${unit}" done for unit in \ zhuyuan-persona-fifth-domain-daily.service \ zhuyuan-persona-fifth-domain-daily.timer; do sed "s|__RELEASE_ROOT__|${release_root}|g" \ "${release_root}/server-tools/persona-team-handshake/${unit}" \ >"/etc/systemd/system/${unit}" done systemctl daemon-reload for unit in "${handshakes[@]}"; do systemctl restart "$unit" done for port in 3932 3933 3934; do ready=0 for attempt in $(seq 1 30); do if test "$(curl -fsS "http://127.0.0.1:${port}/health" 2>/dev/null | jq -r .ok 2>/dev/null)" = "true"; then ready=1 break fi sleep 1 done test "$ready" = 1 done before_chenglu="$(git --git-dir="${repository_root}/chenglu-agent.git" rev-parse main)" before_guideng="$(git --git-dir="${repository_root}/guideng.git" rev-parse main)" before_kezhou="$(git --git-dir="${repository_root}/kezhou.git" rev-parse main)" systemctl enable --now zhuyuan-persona-fifth-domain-daily.timer systemctl start zhuyuan-persona-fifth-domain-daily.service test "$(systemctl is-active zhuyuan-persona-fifth-domain-daily.timer)" = "active" after_chenglu="$(git --git-dir="${repository_root}/chenglu-agent.git" rev-parse main)" after_guideng="$(git --git-dir="${repository_root}/guideng.git" rev-parse main)" after_kezhou="$(git --git-dir="${repository_root}/kezhou.git" rev-parse main)" test "$after_chenglu" != "$before_chenglu" test "$after_guideng" != "$before_guideng" test "$after_kezhou" != "$before_kezhou" source_sha="$(git --git-dir="${repository_root}/guanghu-ice-heart.git" rev-parse main)" for name in chenglu-agent guideng kezhou; do observed="$( git --git-dir="${repository_root}/${name}.git" \ show main:persona-system/fifth-domain/CURRENT.json | jq -r .last_source_sha )" test "$observed" = "$source_sha" runuser -u guanghu -- git \ -c "safe.directory=${repository_root}/${name}.git" \ --git-dir="${repository_root}/${name}.git" \ fsck --connectivity-only --no-dangling done systemctl disable --now guanghu-forgejo.service test "$(systemctl is-active guanghu-forgejo.service || true)" != "active" if ss -ltnH | awk '{print $4}' | grep -Eq '(^|:)3001$'; then exit 41 fi test "$(curl -fsS http://127.0.0.1:3341/health | jq -r .ready)" = "true" for unit in "${handshakes[@]}"; do test "$(systemctl is-active "$unit")" = "active" done latest_daily_receipt="$( find "${runtime_root}/receipts" -maxdepth 1 -type f \ -name 'ZY-PERSONA-DAILY-*.json' -printf '%T@ %p\n' | sort -nr | head -n 1 | cut -d' ' -f2- )" test -n "$latest_daily_receipt" test "$(jq -r .result "$latest_daily_receipt")" = "PASS" test "$(jq '[.results[] | select(.status == "FIFTH_DOMAIN_UPDATE_COMMITTED")] | length' "$latest_daily_receipt")" = "3" jq -n \ --arg completed_at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ --arg source_commit "$expected_source_commit" \ --arg source_sha "$source_sha" \ --arg before_chenglu "$before_chenglu" \ --arg after_chenglu "$after_chenglu" \ --arg before_guideng "$before_guideng" \ --arg after_guideng "$after_guideng" \ --arg before_kezhou "$before_kezhou" \ --arg after_kezhou "$after_kezhou" \ --arg daily_receipt "$latest_daily_receipt" \ --arg source_archive_sha "$( awk '{print $1}' "${archive_root}/legacy-persona-writer-source.tar.gz.sha256" \ 2>/dev/null || printf 'NO_SOURCE_ARCHIVE' )" \ '{ schema: "guanghu.persona-fifth-domain-daily-deployment-receipt/v1", receipt_id: "JD-PERSONA-FIFTH-DOMAIN-DAILY-20260806", target_node: "JD-FD-PRIMARY", completed_at: $completed_at, source_commit: $source_commit, source_fifth_domain_sha: $source_sha, result: "THREE_ROLE_PERSONA_SYSTEMS_DAILY_FIFTH_DOMAIN_LOOP_DEPLOYED_AND_VERIFIED", controller: "ICE-P-ZY001", human_authority: "ICE-GL∞", repositories: [ {name:"bingshuo/chenglu-agent", before:$before_chenglu, after:$after_chenglu}, {name:"bingshuo/guideng", before:$before_guideng, after:$after_guideng}, {name:"bingshuo/kezhou", before:$before_kezhou, after:$after_kezhou} ], old_periodic_writers: "DISABLED_AND_INACTIVE", old_repository_service: "DISABLED_AND_INACTIVE", daily_timer: "ACTIVE", role_handshake_services: "ALL_ACTIVE", current_code_channel: "HEALTHY", daily_dispatch_receipt: $daily_receipt, legacy_data_deleted: false, source_archive_sha256: $source_archive_sha, rule: "Each daily wake reads exact REPO-012 main deltas and writes a signed role-specific update only when Fifth Domain advances." }' >"${receipt}.tmp" chmod 0600 "${receipt}.tmp" mv "${receipt}.tmp" "$receipt" completed=1 rm -rf "$temporary" trap - EXIT printf 'THREE_ROLE_PERSONA_SYSTEMS_DAILY_FIFTH_DOMAIN_LOOP_DEPLOYED receipt=%s\n' \ "$receipt"