#!/usr/bin/env bash
set -euo pipefail
umask 077
ROOT=$(cd "$(dirname "$0")"&&pwd); PROJECT='/home/cube/projects/richard/traning coach'; REPO='/home/cube/projects/richard/hermes-agent'; PY="$REPO/.venv/bin/python"
PRED="$PROJECT/.omo/evidence/task26/task26-inode-test-fix-successor-6aaef77de87489c3be493978645781b24b55ab07513ff1f880164741d0bd3c73"
PW="$PRED/historical/predecessor-candidate/historical/predecessor-candidate/artifacts/hermes_agent-0.17.0-py3-none-any.whl"; FW="$PRED/artifacts/physique_checkin_cli-0.1.0-py3-none-any.whl"
mkdir -p "$ROOT"/{artifacts,bindings,snapshot/{before,after}/{gateway/platforms,tests/gateway}}
"$PY" - "$PW" "$ROOT/snapshot/before/gateway/platforms/telegram.py" <<'PY'
import sys,zipfile
with zipfile.ZipFile(sys.argv[1]) as z:open(sys.argv[2],'wb').write(z.read('gateway/platforms/telegram.py'))
PY
install -m600 "$REPO/gateway/platforms/telegram.py" "$ROOT/snapshot/after/gateway/platforms/telegram.py"
for f in test_nutrition_coaching.py test_telegram_polling_receipts.py;do install -m600 "$REPO/tests/gateway/$f" "$ROOT/snapshot/after/tests/gateway/$f";done
"$PY" - "$REPO" "$ROOT" <<'PY'
from pathlib import Path
import sys
repo,root=map(Path,sys.argv[1:]); specs=[('tests/gateway/test_nutrition_coaching.py','def test_customer_dm_fallback_preserves_private_topic_route'),('tests/gateway/test_telegram_polling_receipts.py','async def test_ptb_error_logs_safe_exception_metadata_without_content')]
for rel,mark in specs:
 s=(repo/rel).read_text();a=s.index('\n'+mark);ends=[x for x in (s.find('\ndef ',a+2),s.find('\nasync def ',a+2)) if x>=0];b=min(ends) if ends else len(s);p=root/'snapshot/before'/rel;p.write_text(s[:a]+s[b:])
PY
set +e
bwrap --die-with-parent --unshare-net --ro-bind / / --bind "$ROOT" "$ROOT" --ro-bind "$ROOT/snapshot/before/gateway/platforms/telegram.py" "$REPO/gateway/platforms/telegram.py" --tmpfs /tmp --dev /dev --proc /proc --setenv HOME /tmp --setenv PYTHONDONTWRITEBYTECODE 1 --chdir "$REPO" "$PY" -B -m pytest -q -p no:cacheprovider -o asyncio_default_fixture_loop_scope=function --junitxml="$ROOT/artifacts/red.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 >"$ROOT/artifacts/red.txt" 2>&1; rc=$?;set -e
[[ $rc == 1 ]]&&grep -q '2 failed' "$ROOT/artifacts/red.txt"
run(){ local n=$1;shift;bwrap --die-with-parent --unshare-net --ro-bind / / --bind "$ROOT" "$ROOT" --tmpfs /tmp --dev /dev --proc /proc --setenv HOME /tmp --setenv PYTHONDONTWRITEBYTECODE 1 --chdir "$REPO" "$PY" -B -m pytest -q -p no:cacheprovider -o asyncio_default_fixture_loop_scope=function --junitxml="$ROOT/artifacts/$n.junit.xml" "$@" >"$ROOT/artifacts/$n.txt" 2>&1;}
run green 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
run aliases tests/gateway/test_nutrition_coaching.py::test_manual_customer_aliases_render_one_reusable_card_with_stable_callback
run related tests/gateway/test_nutrition_coaching.py tests/gateway/test_telegram_polling_receipts.py
"$REPO/.venv/bin/ruff" check "$REPO/gateway/platforms/telegram.py" "$REPO/tests/gateway/test_nutrition_coaching.py" "$REPO/tests/gateway/test_telegram_polling_receipts.py">"$ROOT/artifacts/ruff.txt"
for i in 1 2;do o=/tmp/task26-dmfix-build-$i;rm -rf "$o";mkdir "$o";bwrap --die-with-parent --unshare-net --ro-bind / / --bind /home/cube/.cache/uv /home/cube/.cache/uv --tmpfs /tmp --dev /dev --proc /proc --bind "$o" "$o" --setenv HOME /tmp --setenv TMPDIR /tmp --setenv UV_CACHE_DIR /home/cube/.cache/uv --chdir "$REPO" "$REPO/scripts/reproducible-wheel-build" "$REPO" "$o">"$ROOT/artifacts/wheel-build-$i.txt" 2>&1;done
W1=$(find /tmp/task26-dmfix-build-1 -name '*.whl');W2=$(find /tmp/task26-dmfix-build-2 -name '*.whl');cmp "$W1" "$W2";install -m400 "$W1" "$ROOT/artifacts/hermes_agent-0.17.0-py3-none-any.whl"
"$PY" - "$PW" "$ROOT/artifacts/hermes_agent-0.17.0-py3-none-any.whl" >"$ROOT/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/telegram.py'] else 'FAIL';print(json.dumps(o,sort_keys=True,separators=(',',':')));raise SystemExit(o['status']!='PASS')
PY
cat >"$ROOT/bindings/launch-alias-incident-correction.json" <<'JSON'
{"correction":"Production launch aliases are exactly 체크인 시작 and 오늘 체크인. The English token checkin was diagnostic reproduction input only and is never a production launch alias.","historical_runbooks_mutated":false,"production_aliases":["체크인 시작","오늘 체크인"],"prohibited_aliases":["checkin"],"schema":"task26-live-dm-checkin-alias-correction-v1","test_node":"tests.gateway.test_nutrition_coaching::test_manual_customer_aliases_render_one_reusable_card_with_stable_callback"}
JSON
"$PY" - "$ROOT" "$PRED" "$PW" "$FW" <<'PY'
import hashlib,json,sys
from pathlib import Path
r,p,pw,fw=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()
d=[]
for rel in ['gateway/platforms/telegram.py','tests/gateway/test_nutrition_coaching.py','tests/gateway/test_telegram_polling_receipts.py']:d.append({'path':rel,'before_sha256':h(r/'snapshot/before'/rel),'after_sha256':h(r/'snapshot/after'/rel),'change':'modified'})
(r/'bindings/source-delta.json').write_text(json.dumps({'predecessor_full_digest':'6aaef77de87489c3be493978645781b24b55ab07513ff1f880164741d0bd3c73','entries':d,'only_declared_paths':True},sort_keys=True,separators=(',',':'))+'\n')
q={'red':{'failed':2,'junit_sha256':h(r/'artifacts/red.junit.xml')},'green':{'passed':2,'junit_sha256':h(r/'artifacts/green.junit.xml')},'aliases':{'passed':1,'junit_sha256':h(r/'artifacts/aliases.junit.xml')},'related':{'passed':183,'junit_sha256':h(r/'artifacts/related.junit.xml')},'ruff':'PASS','wheel_reproducible':True};(r/'artifacts/quality-summary.json').write_text(json.dumps(q,sort_keys=True,separators=(',',':'))+'\n')
core={'schema':'task26-live-dm-checkin-fix-core-v1','predecessor_full_digest':'6aaef77de87489c3be493978645781b24b55ab07513ff1f880164741d0bd3c73','predecessor_manifest_sha256':h(p/'candidate-manifest.json'),'predecessor_inventory_sha256':h(p/'hash-inventory.json'),'predecessor_hermes_wheel_sha256':h(pw),'source_delta':d,'hermes_wheel_sha256':h(r/'artifacts/hermes_agent-0.17.0-py3-none-any.whl'),'profile_wheel_sha256':h(fw),'profile_wheel_unchanged':True,'quality':q,'incident_correction_sha256':h(r/'bindings/launch-alias-incident-correction.json')};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'};entries=[]
for x in sorted(r.rglob('*')):
 rel=x.relative_to(r).as_posix()
 if x.is_file() and rel not in exclude:entries.append({'path':rel,'bytes':x.stat().st_size,'sha256':h(x)})
id=hashlib.sha256(canon(entries)).hexdigest();(r/'hash-inventory.json').write_text(json.dumps({'schema':'task26-live-dm-checkin-inventory-v1','entries':entries,'inventory_digest':id},sort_keys=True,separators=(',',':'))+'\n');full=hashlib.sha256(canon({'core_digest':cd,'inventory_digest':id})).hexdigest();m={'schema':'task26-live-dm-checkin-successor-v1','state':'PASS_SEALED_UNDEPLOYED','full_candidate_digest':full,'core_candidate_digest':cd,'inventory_digest':id,'inventory_count':len(entries),'predecessor_full_digest':core['predecessor_full_digest'],'hermes_wheel_sha256':core['hermes_wheel_sha256'],'profile_wheel_sha256':core['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-live-dm-checkin-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),'profile_wheel':str(fw)},sort_keys=True,separators=(',',':'))+'\n');print(json.dumps({'full':full,'core':cd,'inventory':id,'wheel':core['hermes_wheel_sha256']}))
PY
