"""Narrow descendant transaction using installed ct guards; no channel Start."""
import json
from pathlib import Path
import remote

ROOT=Path(__file__).resolve().parent
before=json.loads((ROOT/'before-switch.json').read_text())
manifest=json.loads((ROOT/'candidate-manifest.json').read_text())
code='''
import copy,hashlib,json,os,shutil,subprocess,sys,time
from pathlib import Path
sys.path.insert(0,'/usr/local/lib/contextra-maintenance/portable-20261006T192646')
import compose_backend as cb, server_context as server, field_profiles as fp
from apply_source import normalize
from data_safety import lock
server.verify_tools();server.guard_disks()
root=Path('/storage/contextra/deploy/event-time-fallback-20261013-r01')
image=json.loads((root/'image.json').read_text())['Id']
assert json.loads(subprocess.check_output(['docker','image','inspect',MANIFEST['tag']]))[0]['Id']==image
txname='event-time-fallback-20261013-r01'
with lock(fp.STATE/'data-maintenance.lock'):
    a,site,cp,approved,live,source=cb.inspect()
    assert a==BEFORE['active'] and approved==live
    assert cb.canonical(source)==BEFORE['source_stamp']
    for name in ('apply-pending.json','network-pending.json','pending.json'):
        assert not (fp.STATE/name).exists(),name
    assert shutil.disk_usage('/').free>15*1024**3
    current=cb.containers()
    for r in current:
        role=r['Config']['Labels']['com.docker.compose.service']
        assert r['Id']==BEFORE['services'][role]['id']
        assert r['Image']==BEFORE['services'][role]['image']
    app=next(r for r in current if r['Config']['Labels']['com.docker.compose.service']==server.service('analytics'))
    check="import hashlib,os,json;from pathlib import Path;print(json.dumps({k:hashlib.sha256(Path(os.environ.get(k,d)).read_bytes()).hexdigest() if Path(os.environ.get(k,d)).exists() else None for k,d in [('STREAMS_CONFIG_PATH','/etc/contextra/streams.yaml'),('STREAMS_RUNTIME_PATH','/var/lib/contextra/runtime/streams_runtime.yaml')]}))"
    assert json.loads(subprocess.check_output(['docker','exec',app['Id'],'python3','-c',check],timeout=10))==BEFORE['app']['config_sha256']
    fp.atomic(root/'active-before.json',a)
    fp.atomic(root/'deployment.json',{'phase':'STOPPING','parent':a['transaction'],'candidate':image})
    cb._runtime('stop')
    cb.stopped()
    assert cb.active()==a
    _,_,_,again,again_live,again_source=cb.inspect()
    assert again==approved and again_live==live and cb.canonical(again_source)==BEFORE['source_stamp']
    states={r['Config']['Labels']['com.docker.compose.service']:r['State'] for r in cb.containers()}
    assert all(not s['Running'] and not s['OOMKilled'] and s['ExitCode']!=137 for s in states.values()),'Unsafe Stop'
    fp.atomic(root/'stop-states.json',states)
    doc=copy.deepcopy(approved);doc['services']['analytics']['image']=image
    inverse=copy.deepcopy(doc);inverse['services']['analytics']['image']=MANIFEST['base_image']
    assert inverse==approved
    cb.guard_images(doc)
    tx=cb.state_path()/'transactions'/txname;tx.mkdir(mode=0o700)
    fp.atomic(tx/'compose.json',doc);fp.atomic(tx/'approved.json',doc)
    fp.atomic(tx/'compose.physical.json',server.physical(doc))
    rendered=json.loads(cb.execute(cb.compose_args(tx/'compose.physical.json')+['config','--format','json']))
    assert normalize(server.logical(rendered))==normalize(doc)
    updated=dict(a,transaction=txname,compose=str(tx/'compose.json'),physical=str(tx/'compose.physical.json'),
                 compose_semantic_sha256=cb.canonical(doc))
    fp.atomic(tx/'hotfix-receipt.json',{'parent':a,'image':image,'manifest':MANIFEST,
        'scope':'5 Python files only; existing models/native/SDK/config/auth/storage unchanged',
        'channel_start':'NOT_PERFORMED','previous_blocked_events':'NOT_REQUEUED'})
    assert cb.active()==a
    fp.atomic(cb.state_path()/'active.json',updated)
    fp.atomic(root/'deployment.json',{'phase':'APPLIED_STOPPED','parent':a['transaction'],
        'active':updated,'candidate':image})
    cb.inspect()
    cb._runtime('start')
    fp.atomic(root/'deployment.json',{'phase':'SERVICES_STARTED_ACCEPTANCE_PENDING',
        'parent':a['transaction'],'active':updated,'candidate':image})
    print(json.dumps({'phase':'SERVICES_STARTED_ACCEPTANCE_PENDING','transaction':txname,'image':image,
                     'automatic_camera_start':False}))
'''
out=remote.run('BEFORE='+repr(before)+'\nMANIFEST='+repr(manifest)+'\n'+code,root=True,timeout=840)
remote.save('deployment-result.txt',out)
print(out.decode())
