#!/usr/bin/env bash
set -euo pipefail
umask 077
R=$(cd "$(dirname "$0")"&&pwd);PROJECT='/home/cube/projects/richard/traning coach';H=/home/cube/projects/richard/hermes-agent;PY="$H/.venv/bin/python";P="$PROJECT/.omo/evidence/task26/task26-live-dm-checkin-fix-successor-f38d0373a58877806ff64a9cac54ab01e6e6e47f7101e3dcfd5ffbf05f8bb101";PW="$P/artifacts/hermes_agent-0.17.0-py3-none-any.whl";FW="$PROJECT/.omo/evidence/task26/task26-inode-test-fix-successor-6aaef77de87489c3be493978645781b24b55ab07513ff1f880164741d0bd3c73/artifacts/physique_checkin_cli-0.1.0-py3-none-any.whl";REC="$PROJECT/.omo/evidence/task26/task26-telegram-poll-recovery-st_01a00d2c/FINAL-SEAL.json";OBS="$PROJECT/.omo/evidence/task26/task26-continuous-lifecycle-observer-v6.3-f38d0373/SEAL.json"
mkdir -p "$R"/{artifacts,bindings,snapshot/{before,after}/{gateway/platforms,tests/gateway}}
"$PY" - "$PW" "$R/snapshot/before/gateway/platforms/nutrition_coaching.py" <<'PY'
import sys,zipfile
with zipfile.ZipFile(sys.argv[1]) as z:open(sys.argv[2],'wb').write(z.read('gateway/platforms/nutrition_coaching.py'))
PY
install -m600 "$P/snapshot/after/tests/gateway/test_nutrition_coaching.py" "$R/snapshot/before/tests/gateway/test_nutrition_coaching.py";install -m600 "$H/gateway/platforms/nutrition_coaching.py" "$R/snapshot/after/gateway/platforms/nutrition_coaching.py";install -m600 "$H/tests/gateway/test_nutrition_coaching.py" "$R/snapshot/after/tests/gateway/test_nutrition_coaching.py"
set +e
bwrap --die-with-parent --unshare-net --ro-bind / / --bind "$R" "$R" --ro-bind "$R/snapshot/before/gateway/platforms/nutrition_coaching.py" "$H/gateway/platforms/nutrition_coaching.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="$R/artifacts/red.junit.xml" tests/gateway/test_nutrition_coaching.py::test_coordinator_initializes_customer_service_state_store >"$R/artifacts/red.txt" 2>&1;rc=$?;set -e;[[ $rc == 1 ]];grep -q '1 failed' "$R/artifacts/red.txt";grep -q 'state_path.exists() is True' "$R/artifacts/red.txt"
bwrap --die-with-parent --unshare-net --ro-bind / / --bind "$R" "$R" --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="$R/artifacts/green.junit.xml" tests/gateway/test_nutrition_coaching.py::test_coordinator_initializes_customer_service_state_store >"$R/artifacts/green.txt" 2>&1
# Exact 335-node activation/service regression set.
bwrap --die-with-parent --unshare-net --ro-bind / / --bind "$R" "$R" --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="$R/artifacts/related-335.junit.xml" tests/gateway/test_nutrition_coaching.py tests/gateway/test_adaptive_nutrition.py tests/gateway/test_dualcoach_activation_cutover.py >"$R/artifacts/related-335.txt" 2>&1
"$H/.venv/bin/ruff" check "$H/gateway/platforms/nutrition_coaching.py" "$H/tests/gateway/test_nutrition_coaching.py">"$R/artifacts/ruff.txt"
for i in 1 2;do o=/tmp/task26-service-state-wheel-$i;rm -rf "$o";mkdir "$o";bwrap --die-with-parent --unshare-net --ro-bind / / --bind /home/cube/.cache/uv /home/cube/.cache/uv --bind "$o" "$o" --tmpfs /tmp --dev /dev --proc /proc --setenv HOME /tmp --setenv TMPDIR /tmp --setenv UV_CACHE_DIR /home/cube/.cache/uv --chdir "$H" "$H/scripts/reproducible-wheel-build" "$H" "$o">"$R/artifacts/wheel-build-$i.txt" 2>&1;done
W1=$(find /tmp/task26-service-state-wheel-1 -name '*.whl');W2=$(find /tmp/task26-service-state-wheel-2 -name '*.whl');cmp "$W1" "$W2";install -m400 "$W1" "$R/artifacts/hermes_agent-0.17.0-py3-none-any.whl"
"$PY" - "$PW" "$R/artifacts/hermes_agent-0.17.0-py3-none-any.whl" >"$R/artifacts/wheel-delta.json" <<'PY'
import hashlib,json,sys,zipfile
with zipfile.ZipFile(sys.argv[1]) as a,zipfile.ZipFile(sys.argv[2]) as b:
 A,B=set(a.namelist()),set(b.namelist());c=[]
 for n in sorted(A&B):
  if n.endswith('.dist-info/RECORD'):continue
  x,y=hashlib.sha256(a.read(n)).hexdigest(),hashlib.sha256(b.read(n)).hexdigest()
  if x!=y:c.append({'path':n,'before_sha256':x,'after_sha256':y})
 o={'added':sorted(B-A),'removed':sorted(A-B),'changed':c};o['status']='PASS' if not o['added'] and not o['removed'] and [x['path'] for x in c]==['gateway/platforms/nutrition_coaching.py'] else 'FAIL';print(json.dumps(o,sort_keys=True,separators=(',',':')));raise SystemExit(o['status']!='PASS')
PY
"$PY" - "$R" "$P" "$PW" "$FW" "$REC" "$OBS" <<'PY'
import hashlib,json,sys
from pathlib import Path
r,p,pw,fw,rec,obs=map(Path,sys.argv[1:]);h=lambda x:hashlib.sha256(x.read_bytes()).hexdigest();canon=lambda x:json.dumps(x,sort_keys=True,separators=(',',':')).encode();paths=['gateway/platforms/nutrition_coaching.py','tests/gateway/test_nutrition_coaching.py'];d=[{'path':x,'change':'modified','before_sha256':h(r/'snapshot/before'/x),'after_sha256':h(r/'snapshot/after'/x)} for x in paths];q={'red':{'failed':1,'junit_sha256':h(r/'artifacts/red.junit.xml')},'green':{'passed':1,'junit_sha256':h(r/'artifacts/green.junit.xml')},'related':{'passed':335,'junit_sha256':h(r/'artifacts/related-335.junit.xml')},'ruff':'PASS','wheel_reproducible':True};(r/'artifacts/quality-summary.json').write_text(json.dumps(q,sort_keys=True,separators=(',',':'))+'\n');bindings={'predecessor_full_digest':'f38d0373a58877806ff64a9cac54ab01e6e6e47f7101e3dcfd5ffbf05f8bb101','predecessor_manifest_sha256':h(p/'candidate-manifest.json'),'predecessor_inventory_sha256':h(p/'hash-inventory.json'),'predecessor_wheel_sha256':h(pw),'polling_recovery_seal_sha256':h(rec),'observer_v63_seal_sha256':h(obs),'profile_wheel_sha256':h(fw),'source_delta':d};(r/'bindings/predecessor-and-evidence.json').write_text(json.dumps(bindings,sort_keys=True,separators=(',',':'))+'\n');core={'schema':'task26-customer-service-state-bootstrap-core-v1',**bindings,'hermes_wheel_sha256':h(r/'artifacts/hermes_agent-0.17.0-py3-none-any.whl'),'quality':q};cd=hashlib.sha256(canon(core)).hexdigest();core['core_digest']=cd;(r/'bindings/core.json').write_text(json.dumps(core,sort_keys=True,separators=(',',':'))+'\n');exclude={'candidate-manifest.json','candidate-seal.json','hash-inventory.json','verifier-input.json','independent-verification.json','verify_candidate.py','seal-successor.sh'};rows=[]
for x in sorted(r.rglob('*')):
 rel=x.relative_to(r).as_posix()
 if x.is_file() and rel not in exclude:rows.append({'path':rel,'bytes':x.stat().st_size,'sha256':h(x)})
id=hashlib.sha256(canon(rows)).hexdigest();(r/'hash-inventory.json').write_text(json.dumps({'schema':'task26-customer-service-state-bootstrap-inventory-v1','entries':rows,'inventory_digest':id},sort_keys=True,separators=(',',':'))+'\n');full=hashlib.sha256(canon({'core_digest':cd,'inventory_digest':id})).hexdigest();m={'schema':'task26-customer-service-state-bootstrap-successor-v1','state':'PASS_SEALED_UNDEPLOYED','full_candidate_digest':full,'core_candidate_digest':cd,'inventory_digest':id,'inventory_count':len(rows),'predecessor_full_digest':bindings['predecessor_full_digest'],'hermes_wheel_sha256':core['hermes_wheel_sha256'],'profile_wheel_sha256':bindings['profile_wheel_sha256'],'source_delta':d};(r/'candidate-manifest.json').write_text(json.dumps(m,sort_keys=True,separators=(',',':'))+'\n');(r/'candidate-seal.json').write_text(json.dumps({'schema':'task26-customer-service-state-bootstrap-seal-v1','full_candidate_digest':full,'core_candidate_digest':cd,'inventory_digest':id,'verdict':'PASS'},sort_keys=True,separators=(',',':'))+'\n');(r/'verifier-input.json').write_text(json.dumps({'expected_full_candidate_digest':full,'expected_core_candidate_digest':cd,'predecessor_root':str(p),'predecessor_wheel':str(pw),'profile_wheel':str(fw),'polling_recovery_seal':str(rec),'observer_v63_seal':str(obs)},sort_keys=True,separators=(',',':'))+'\n');print(json.dumps({'full':full,'core':cd,'inventory':id,'wheel':core['hermes_wheel_sha256']}))
PY
