#!/usr/bin/env bash
set -euo pipefail
umask 077
PROJECT='/home/cube/projects/richard/traning coach';D=f38d0373a58877806ff64a9cac54ab01e6e6e47f7101e3dcfd5ffbf05f8bb101;E="$PROJECT/.omo/evidence/task26/task26-certification-deployment-$D-st_01a00b39";C="$PROJECT/.omo/evidence/task26/task26-live-dm-checkin-fix-successor-$D";H=/home/cube/projects/richard/hermes-agent;P=/home/cube/.hermes/profiles/dualcoachtest;S="$P/workspace/checkin_cli";PY="$H/.venv/bin/python";TPY=/home/cube/miniconda3/bin/python;W="$C/artifacts/hermes_agent-0.17.0-py3-none-any.whl";FW="$C/../task26-inode-test-fix-successor-6aaef77de87489c3be493978645781b24b55ab07513ff1f880164741d0bd3c73/artifacts/physique_checkin_cli-0.1.0-py3-none-any.whl"
mkdir -p "$E/artifacts" "$E/receipts"
record(){ n=$1;shift;o="$E/artifacts/$n";mkdir -p "$o";printf '%q ' "$@">"$o/command.txt";echo>>"$o/command.txt";date -u +%FT%T.%NZ>"$o/start.txt";set +e;"$@">"$o/stdout.txt" 2>"$o/stderr.txt";rc=$?;set -e;echo $rc>"$o/exit.txt";date -u +%FT%T.%NZ>"$o/end.txt";return $rc;}
inventory(){ root=$1;out=$2;find "$root" -type f -not -path '*/__pycache__/*' -not -name '*.pyc' -printf '%P\0'|sort -z|while IFS= read -r -d '' f;do sha256sum "$root/$f";done >"$out";}
snapshot(){ tag=$1;sha256sum "$P/customers/registry.json" "$P/data/onboarding/telegram-customer-bootstrap-v1/ledger.json" "$P/data/customers/task26_live_2e_r2_20260815_8527916639/nutrition-onboarding/readiness-receipt-v1.json" "$P/data/customers/task26_live_2e_r2_20260815_8527916639/nutrition-plans/feature-epoch.json">"$E/artifacts/authority-$tag.sha256";inventory "$P/data/customers/task26_live_2e_r2_20260815_8527916639" "$E/artifacts/customer-$tag.sha256";inventory "$S" "$E/artifacts/profile-workspace-$tag.sha256";for x in physique-coach quarantine;do if [[ -d "/home/cube/.hermes/profiles/$x" ]];then inventory "/home/cube/.hermes/profiles/$x" "$E/artifacts/unrelated-$x-$tag.sha256";else printf 'ABSENT\n'>"$E/artifacts/unrelated-$x-$tag.sha256";fi;done;sha256sum "$H/.venv/lib/python3.12/site-packages/__editable__.physique_checkin_cli-0.1.0.pth" "$H/.venv/lib/python3.12/site-packages/__editable___physique_checkin_cli_0_1_0_finder.py" "$H/.venv/lib/python3.12/site-packages/physique_checkin_cli-0.1.0.dist-info/direct_url.json">"$E/artifacts/editable-$tag.sha256";"$PY" - "$P" >"$E/artifacts/state-$tag.json" <<'PY'
import json,sys
from pathlib import Path
p=Path(sys.argv[1]);k='task26_live_2e_r2_20260815_8527916639';r=json.loads((p/'customers/registry.json').read_text());c=next(x for x in r['customers'] if x['customer_key']==k);b=json.loads((p/'data/onboarding/telegram-customer-bootstrap-v1/ledger.json').read_text());s=next(x for x in b['sessions'] if x['generation']==6);q=json.loads((p/'data/customers'/k/'nutrition-onboarding/readiness-receipt-v1.json').read_text());f=json.loads((p/'data/customers'/k/'nutrition-plans/feature-epoch.json').read_text());delivery_paths=[p/'data/owner-actions/draft-deliveries.json',p/'data/scheduled-deliveries.jsonl'];print(json.dumps({'customer_enabled':c['enabled'],'bootstrap_generation':s['generation'],'bootstrap_state':s['state'],'adaptive_activation':q['activation_enabled'],'adaptive_delivery':q['delivery_enabled'],'feature_activation':f['activation'],'feature_delivery':f['delivery'],'delivery_files_present':[str(x) for x in delivery_paths if x.exists()],'delivery_count':0},sort_keys=True,separators=(',',':')))
PY
}
case ${1:-} in
 baseline)snapshot before;record candidate-verify "$C/verify_candidate.py" "$C";;
 gate18)record gate18-verify "$C/verify_candidate.py" "$C";record gate18-focused bwrap --die-with-parent --unshare-net --ro-bind / / --bind "$E" "$E" --tmpfs /tmp --dev /dev --proc /proc --setenv HOME /tmp --chdir "$H" "$PY" -B -m pytest -q -p no:cacheprovider -o asyncio_default_fixture_loop_scope=function --junitxml="$E/artifacts/gate18-focused.junit.xml" tests/gateway/test_nutrition_coaching.py::test_customer_dm_fallback_preserves_private_topic_route tests/gateway/test_telegram_polling_receipts.py::test_ptb_error_logs_safe_exception_metadata_without_content tests/gateway/test_nutrition_coaching.py::test_manual_customer_aliases_render_one_reusable_card_with_stable_callback;record gate18-token bwrap --die-with-parent --unshare-net --ro-bind / / --bind "$E" "$E" --tmpfs /tmp --dev /dev --proc /proc --setenv HOME /tmp --chdir "$S" "$PY" -B -m pytest -q -p no:cacheprovider --junitxml="$E/artifacts/gate18-token.junit.xml" tests/test_customer_admin.py -k activation_token_rotation_waiver;;
 gate19)if record gate19-predecessor bwrap --die-with-parent --unshare-net --ro-bind / / --bind "$E" "$E" --ro-bind "$C/snapshot/before/gateway/platforms/telegram.py" "$H/gateway/platforms/telegram.py" --tmpfs /tmp --dev /dev --proc /proc --setenv HOME /tmp --chdir "$H" "$PY" -B -m pytest -q -p no:cacheprovider -o asyncio_default_fixture_loop_scope=function --junitxml="$E/artifacts/gate19-predecessor.junit.xml" tests/gateway/test_nutrition_coaching.py::test_customer_dm_fallback_preserves_private_topic_route tests/gateway/test_telegram_polling_receipts.py::test_ptb_error_logs_safe_exception_metadata_without_content;then exit 1;else rc=$?;[[ $rc == 1 ]];fi;record gate19-successor bwrap --die-with-parent --unshare-net --ro-bind / / --bind "$E" "$E" --tmpfs /tmp --dev /dev --proc /proc --setenv HOME /tmp --chdir "$H" "$PY" -B -m pytest -q -p no:cacheprovider -o asyncio_default_fixture_loop_scope=function --junitxml="$E/artifacts/gate19-successor.junit.xml" tests/gateway/test_nutrition_coaching.py tests/gateway/test_telegram_polling_receipts.py;;
 gate20)T=/tmp/task26-dmfix-profile;rm -rf "$T";mkdir "$T";cp -a "$S"/. "$T"/;chmod -R u+rwX "$T";find "$T" -type d -name __pycache__ -prune -exec rm -rf {} +;record gate20-source-package env -C "$T" HOME=/tmp PYTHONPATH="$T" PYTHONDONTWRITEBYTECODE=1 "$TPY" -B -m pytest -q -p no:cacheprovider --junitxml="$E/artifacts/gate20-source-package.junit.xml" tests;record gate20-full-gateway bwrap --die-with-parent --unshare-net --ro-bind / / --bind "$E" "$E" --tmpfs /tmp --dev /dev --proc /proc --setenv HOME /tmp --setenv PYTHONDONTWRITEBYTECODE 1 --chdir "$H" "$PY" -B -m pytest -q -p no:cacheprovider -o asyncio_default_fixture_loop_scope=function --junitxml="$E/artifacts/gate20-full-gateway.junit.xml" tests/gateway;I="$E/artifacts/installed-hermes";mkdir "$I";record gate20-install "$PY" -m pip install --no-deps --no-compile --target "$I" "$W";record gate20-installed-parity "$PY" - "$I" "$W" <<'PY'
import hashlib,json,sys,zipfile
from pathlib import Path
i,w=Path(sys.argv[1]),Path(sys.argv[2]);rows=[]
with zipfile.ZipFile(w) as z:
 for n in sorted(x for x in z.namelist() if not x.endswith('/')):
  p=i/n
  if n.endswith('.dist-info/RECORD'):continue
  rows.append(p.is_file() and hashlib.sha256(p.read_bytes()).digest()==hashlib.sha256(z.read(n)).digest())
print(json.dumps({'members':len(rows),'equal':sum(rows),'status':'PASS' if all(rows) else 'FAIL'}));raise SystemExit(not all(rows))
PY
 record gate20-installed-behavior env -C /tmp HOME=/tmp PYTHONPATH="$I:$S" PYTHONDONTWRITEBYTECODE=1 "$PY" -B -m pytest --import-mode=importlib -q -p no:cacheprovider -o asyncio_default_fixture_loop_scope=function --junitxml="$E/artifacts/gate20-installed-behavior.junit.xml" "$H/tests/gateway/test_nutrition_coaching.py::test_customer_dm_fallback_preserves_private_topic_route" "$H/tests/gateway/test_telegram_polling_receipts.py::test_ptb_error_logs_safe_exception_metadata_without_content";PI="$E/artifacts/installed-profile";mkdir "$PI";record gate20-profile-install "$PY" -m pip install --no-deps --no-compile --target "$PI" "$FW";record gate20-profile-installed-tests env -C /tmp HOME=/tmp PYTHONPATH="$PI" PYTHONDONTWRITEBYTECODE=1 "$TPY" -B -m pytest -q -p no:cacheprovider --junitxml="$E/artifacts/gate20-profile-installed.junit.xml" "$T/tests/test_customer_admin.py";;
 gate20-rest)record gate20-full-gateway-corrected env -C "$H" HOME=/home/cube PYTHONPATH="$H:$S" PYTHONDONTWRITEBYTECODE=1 "$PY" -B -m pytest -q -p no:cacheprovider -o asyncio_default_fixture_loop_scope=function --junitxml="$E/artifacts/gate20-full-gateway-corrected.junit.xml" tests/gateway;I="$E/artifacts/installed-hermes";mkdir -p "$I";record gate20-install "$PY" -m pip install --no-deps --no-compile --target "$I" "$W";record gate20-installed-parity "$PY" - "$I" "$W" <<'PY'
import hashlib,json,sys,zipfile
from pathlib import Path
i,w=Path(sys.argv[1]),Path(sys.argv[2]);rows=[]
with zipfile.ZipFile(w) as z:
 for n in sorted(x for x in z.namelist() if not x.endswith('/') and not x.endswith('.dist-info/RECORD')):
  p=i/n;rows.append(p.is_file() and hashlib.sha256(p.read_bytes()).digest()==hashlib.sha256(z.read(n)).digest())
print(json.dumps({'members':len(rows),'equal':sum(rows),'status':'PASS' if all(rows) else 'FAIL'}));raise SystemExit(not all(rows))
PY
 record gate20-installed-behavior env -C /tmp HOME=/tmp PYTHONPATH="$I:$S" PYTHONDONTWRITEBYTECODE=1 "$PY" -B -m pytest --import-mode=importlib -q -p no:cacheprovider -o asyncio_default_fixture_loop_scope=function --junitxml="$E/artifacts/gate20-installed-behavior.junit.xml" "$H/tests/gateway/test_nutrition_coaching.py::test_customer_dm_fallback_preserves_private_topic_route" "$H/tests/gateway/test_telegram_polling_receipts.py::test_ptb_error_logs_safe_exception_metadata_without_content";PI="$E/artifacts/installed-profile";mkdir -p "$PI";record gate20-profile-install "$PY" -m pip install --no-deps --no-compile --target "$PI" "$FW";T=/tmp/task26-dmfix-profile;record gate20-profile-installed-tests env -C /tmp HOME=/tmp PYTHONPATH="$PI" PYTHONDONTWRITEBYTECODE=1 "$TPY" -B -m pytest -q -p no:cacheprovider --junitxml="$E/artifacts/gate20-profile-installed.junit.xml" "$T/tests/test_customer_admin.py";;
 gate20-rest2)I="$E/artifacts/installed-hermes";record gate20-installed-parity-corrected "$PY" - "$I" "$W" <<'PY'
import hashlib,json,sys,zipfile,re
from pathlib import Path
i,w=Path(sys.argv[1]),Path(sys.argv[2]);rows=[]
with zipfile.ZipFile(w) as z:
 for n in sorted(x for x in z.namelist() if not x.endswith('/') and not x.endswith('.dist-info/RECORD')):
  rel=re.sub(r'^hermes_agent-0\.17\.0\.data/data/','',n);p=i/rel;rows.append(p.is_file() and hashlib.sha256(p.read_bytes()).digest()==hashlib.sha256(z.read(n)).digest())
print(json.dumps({'members':len(rows),'equal':sum(rows),'status':'PASS' if all(rows) else 'FAIL'}));raise SystemExit(not all(rows))
PY
 record gate20-installed-behavior env -C /tmp HOME=/tmp PYTHONPATH="$I:$S" PYTHONDONTWRITEBYTECODE=1 "$PY" -B -m pytest --import-mode=importlib -q -p no:cacheprovider -o asyncio_default_fixture_loop_scope=function --junitxml="$E/artifacts/gate20-installed-behavior.junit.xml" "$H/tests/gateway/test_nutrition_coaching.py::test_customer_dm_fallback_preserves_private_topic_route" "$H/tests/gateway/test_telegram_polling_receipts.py::test_ptb_error_logs_safe_exception_metadata_without_content";PI="$E/artifacts/installed-profile";mkdir -p "$PI";record gate20-profile-install "$PY" -m pip install --no-deps --no-compile --target "$PI" "$FW";T=/tmp/task26-dmfix-profile;record gate20-profile-installed-tests env -C /tmp HOME=/tmp PYTHONPATH="$PI" PYTHONDONTWRITEBYTECODE=1 "$TPY" -B -m pytest -q -p no:cacheprovider --junitxml="$E/artifacts/gate20-profile-installed.junit.xml" "$T/tests/test_customer_admin.py";;
 after)snapshot after-gates;cmp "$E/artifacts/authority-before.sha256" "$E/artifacts/authority-after-gates.sha256";cmp "$E/artifacts/customer-before.sha256" "$E/artifacts/customer-after-gates.sha256";cmp "$E/artifacts/profile-workspace-before.sha256" "$E/artifacts/profile-workspace-after-gates.sha256";cmp "$E/artifacts/editable-before.sha256" "$E/artifacts/editable-after-gates.sha256";for x in physique-coach quarantine;do cmp "$E/artifacts/unrelated-$x-before.sha256" "$E/artifacts/unrelated-$x-after-gates.sha256";done;cmp "$E/artifacts/state-before.json" "$E/artifacts/state-after-gates.json";;
 *)exit 64;;esac
