← back to Ticket System

data/codex-yoloforever/cycle-20260908T1221Z.HzXvuQ/ops-rehearsal.py

163 lines

import hashlib,json,os,shutil,stat,subprocess,time
from pathlib import Path
S=Path('/Users/macstudio3/Projects/.git')
W=S.parent
R=Path('/private/tmp/ops-TK10928-rehearsal.ndMwgA')
E=Path('/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1221Z.HzXvuQ')
M=Path('/Users/macstudio3/.claude/yolo-queue/pending-approval/2026-09-08-TK-10928-projects-root-snapshot-recovery.md')
C=['rentv-slideshow/renders/rentv-slideshow_2026-07-29_'+x+'.mp4' for x in ['07-02-25','08-29-23','08-22-08','08-16-11','07-49-53']]
EXPECTED=['efbba50e8faff2c98d5cd58654619ffcc85b7e92','b43f1788d2681871ff6ac918be7b5857a2e5cdd5','4744083a7a07447f55cde597cdbb14b530e82371','f868b83b92bc085314fd49cd1f791c7ea3b114bf','dbf6ed038c5be1d119c9f8956cbe83dd72bb1e42']
ENV=dict(os.environ,GIT_OPTIONAL_LOCKS='0',GIT_CONFIG_NOSYSTEM='1',GIT_CONFIG_GLOBAL='/dev/null')
checks=[]; commands=[]
def dump(n,o):
 p=E/('ops-'+n+'.json');p.write_text(json.dumps(o,indent=2)+'\n');return str(p)
def check(n,b,detail=None):
 checks.append({'check':n,'verdict':'PASS' if b else 'FAIL','detail':detail});dump('checks-progress',checks)
 if not b: raise RuntimeError(n+': '+str(detail))
def run(a,inp=None,env=None,timeout=240,out=None,ok=True):
 commands.append({'argv':[str(x) for x in a],'input_sha256':hashlib.sha256(inp).hexdigest() if inp is not None else None,'stdout_path':str(out) if out else None})
 dump('commands',commands)
 if out:
  with open(out,'wb') as f:r=subprocess.run(a,input=inp,stdout=f,stderr=subprocess.PIPE,env=env or ENV,timeout=timeout)
  data=b''
 else:
  r=subprocess.run(a,input=inp,stdout=subprocess.PIPE,stderr=subprocess.PIPE,env=env or ENV,timeout=timeout);data=r.stdout
 if r.returncode and ok:raise RuntimeError(str(a)+': '+r.stderr.decode(errors='replace')[:2000])
 return data
def git(g,*a,**kw):return run(['git','--git-dir='+str(g),*a],**kw)
def sha(p):
 h=hashlib.sha256()
 with open(p,'rb') as f:
  for b in iter(lambda:f.read(8*1024*1024),b''):h.update(b)
 return h.hexdigest()
def manifest(d):
 out={}
 for p in sorted(d.rglob('*')):
  st=p.lstat();rel=str(p.relative_to(d))
  if p.is_symlink():raise RuntimeError('symlink requires review: '+str(p))
  if p.is_file():out[rel]={'sha256':sha(p),'bytes':st.st_size,'mode':stat.S_IMODE(st.st_mode),'mtime_ns':st.st_mtime_ns}
 return out
def refs(g):return git(g,'show-ref').decode().strip().splitlines()
def du(g):return int(run(['du','-sk',str(g)]).split()[0])
def tree(g,ref):
 result={}
 for row in git(g,'ls-tree','-rz','--full-tree',ref).split(b'\0'):
  if row:
   a,p=row.split(b'\t',1);result[p.decode()]=a.decode()
 return result
def copytree(a,b):
 shutil.copytree(a,b,copy_function=shutil.copy2)
 for p in b.rglob('*'):
  if p.is_file():
   q=a/p.relative_to(b)
   if p.stat().st_ino==q.stat().st_ino or p.stat().st_nlink!=1:raise RuntimeError('shared inode: '+str(p))
def source_meta():
 return {'refs':refs(S),'index_sha256':sha(S/'index'),'script_sha256':sha(Path('/Users/macstudio3/scripts/auto-commit-fleet.sh')),'locks':[str(p.relative_to(S)) for p in S.rglob('*.lock')]}
started=time.time()
try:
 before=source_meta();check('no source Git writer lock at capture',not before['locks'],before['locks'])
 check('no source alternates or common Git object store',not (S/'objects/info/alternates').exists() and not (S/'commondir').exists())
 free=shutil.disk_usage(R).free;size=du(S)*1024;check('scratch capacity >= 6 full source copies plus 5 GB',free>size*6+5*1024**3,{'free_bytes':free,'source_allocated_bytes':size})
 shutil.copy2(M,R/'memo.preimage.md');shutil.copy2(M,E/'ops-memo-preimage.md')
 original=R/'original.git';trial=R/'trial.git';restored=R/'restored.git'
 print('Capturing complete original Git directory',flush=True)
 pre=manifest(S);dump('source-git-manifest',pre)
 copytree(S,original)
 saved=manifest(original);post=manifest(S);after=source_meta()
 check('stable byte-complete capture including index refs logs and unreachable storage',pre==saved==post and before==after)
 dump('capture',{'started_utc':time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime(started)),'finished_utc':time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime()),'source':str(S),'original':str(original),'before':before,'after':after,'file_count':len(saved),'logical_bytes':sum(v['bytes'] for v in saved.values()),'allocated_kib':size//1024,'scratch_free_bytes':free,'source_git_manifest_sha256':sha(E/'ops-source-git-manifest.json')})
 snap=git(original,'rev-parse','refs/auto-snapshot/latest').decode().strip();master=git(original,'rev-parse','refs/heads/master').decode().strip()
 orig_tree=tree(original,snap);master_tree=tree(original,master)
 git(original,'cat-file','--batch-all-objects','--batch-check=%(objectname) %(objecttype) %(objectsize) %(objectsize:disk)',out=R/'original-object-inventory.txt')
 git(original,'fsck','--full','--no-reflogs','--unreachable',out=R/'original-fsck.txt')
 idx=git(original,'ls-files','--stage','-z');(R/'original-index-entries.bin').write_bytes(idx)
 (R/'original-snapshot-tree.json').write_text(json.dumps(orig_tree,indent=2)+'\n')
 (R/'original-master-tree.json').write_text(json.dumps(master_tree,indent=2)+'\n')
 files=[]
 for rel,oid in zip(C,EXPECTED):
  check('candidate snapshot identity '+rel,orig_tree.get(rel,'').endswith(oid))
  p=W/rel;archive=R/'original-files'/rel;archive.parent.mkdir(parents=True,exist_ok=True)
  before_hash=sha(p);shutil.copy2(p,archive)
  blob=R/'restored-five'/rel;blob.parent.mkdir(parents=True,exist_ok=True)
  git(original,'cat-file','blob',oid,out=blob)
  check('candidate worktree archive and Git restore exact '+rel,before_hash==sha(p)==sha(archive)==sha(blob))
  files.append({'path':rel,'blob':oid,'sha256':before_hash,'bytes':p.stat().st_size,'mode':stat.S_IMODE(p.stat().st_mode),'archive':str(archive),'restored_blob':str(blob)})
 shutil.copy2('/Users/macstudio3/scripts/auto-commit-fleet.sh',R/'auto-commit-fleet.original.sh')
 dump('candidate-preservation',files)
 copytree(original,trial)
 copytree(original,restored)
 check('restored entire original Git state identical',manifest(restored)==saved)
 check('both original refs restore exactly',refs(restored)==refs(original),refs(restored))
 git(restored,'fsck','--full','--no-reflogs','--unreachable',out=R/'restored-fsck.txt')
 check('unreachable object recovery exact',sha(R/'original-fsck.txt')==sha(R/'restored-fsck.txt'))
 # Keep the complete original copy immutable, including otherwise unreachable undo.
 for p in sorted(original.rglob('*'),key=lambda p:len(p.parts),reverse=True):os.chflags(p,stat.UF_IMMUTABLE)
 os.chflags(original,stat.UF_IMMUTABLE)
 check('original backup immutable flag on every entry',all(p.stat().st_flags & stat.UF_IMMUTABLE for p in [original,*original.rglob('*')]))
 print('Original capture and full restore verified; constructing five-path-only snapshot',flush=True)
 # Build a separate temporary index additively from all non-candidate entries.
 newidx=R/'snapshot.index';ienv=dict(ENV,GIT_INDEX_FILE=str(newidx))
 git(trial,'read-tree','--empty',env=ienv)
 kept=[]
 for path,meta in orig_tree.items():
  if path not in C:
   mode,typ,oid=meta.split();kept.append((mode+' '+oid+'\t'+path).encode()+b'\0')
 git(trial,'update-index','-z','--index-info',inp=b''.join(kept),env=ienv)
 newtree=git(trial,'write-tree',env=ienv).decode().strip()
 cenv=dict(ENV,GIT_AUTHOR_NAME='isolated-rehearsal',GIT_AUTHOR_EMAIL='steve@designerwallcoverings.com',GIT_COMMITTER_NAME='isolated-rehearsal',GIT_COMMITTER_EMAIL='steve@designerwallcoverings.com')
 newsnap=git(trial,'commit-tree',newtree,'-m','TK-10928 isolated exact five render exclusions',env=cenv).decode().strip()
 git(trial,'update-ref','refs/auto-snapshot/latest',newsnap,snap)
 newentries=tree(trial,newsnap)
 check('exactly five snapshot paths excluded; every other mode/object unchanged',newentries=={k:v for k,v in orig_tree.items() if k not in C},{'before':len(orig_tree),'after':len(newentries),'excluded':C})
 check('master ref and full tree unchanged',git(trial,'rev-parse','refs/heads/master').decode().strip()==master and tree(trial,master)==master_tree)
 check('trial real index byte-identical',sha(trial/'index')==before['index_sha256'])
 # Full retained effect inventory: all non-snapshot ref reachability, original
 # index objects, original pseudoref closure, and original non-snapshot reflogs.
 object_rows=git(trial,'rev-list','--objects','--all').decode().splitlines()
 retained={x.split(' ',1)[0] for x in object_rows}
 for row in idx.split(b'\0'):
  if row:
   mode,oid,stagepath=row.split(b' ',2)
   if mode!=b'160000':retained.add(oid.decode())
 for name in ['ORIG_HEAD','FETCH_HEAD','MERGE_HEAD','REBASE_HEAD','AUTO_MERGE']:
  p=trial/name
  if p.exists():
   for oid in p.read_text().splitlines():
    for row in git(trial,'rev-list','--objects',oid.split()[0]).decode().splitlines():retained.add(row.split(' ',1)[0])
 # Preserve every master reflog closure; archive only recovery-snapshot reflog.
 for p in (trial/'logs').rglob('*'):
  if p.is_file() and str(p.relative_to(trial/'logs'))!='refs/auto-snapshot/latest':
   for row in p.read_text().splitlines():
    for oid in row.split()[:2]:
     if oid!='0'*40:
      for obj in git(trial,'rev-list','--objects',oid).decode().splitlines():retained.add(obj.split(' ',1)[0])
 retained_blob_hits=[oid for oid in EXPECTED if oid in retained]
 dump('retained-closure',{'objects':len(retained),'candidate_oids_still_reachable':retained_blob_hits})
 (R/'retained-object-ids.txt').write_text('\n'.join(sorted(retained))+'\n')
 # No deletion: create a new self-contained pack; move the trial's old object
 # directory and old snapshot reflog to retained siblings outside measured .git.
 stage=R/'new-objects';(stage/'pack').mkdir(parents=True);(stage/'info').mkdir()
 packid=git(trial,'pack-objects','--window=0','--threads=2',str(stage/'pack/pack'),inp=('\n'.join(sorted(retained))+'\n').encode(),timeout=240).decode().strip()
 os.rename(trial/'objects',R/'trial-original-objects-retained')
 os.rename(stage,trial/'objects')
 log=trial/'logs/refs/auto-snapshot/latest'
 if log.exists():os.rename(log,R/'trial-original-snapshot-reflog-retained')
 git(trial,'fsck','--full',out=R/'trial-fsck.txt')
 git(trial,'cat-file','--batch-all-objects','--batch-check=%(objectname) %(objecttype) %(objectsize) %(objectsize:disk)',out=R/'trial-object-inventory.txt')
 oldobjs={r.split()[0]:r for r in (R/'original-object-inventory.txt').read_text().splitlines()}
 newobjs={r.split()[0]:r for r in (R/'trial-object-inventory.txt').read_text().splitlines()}
 excluded=sorted(set(oldobjs)-set(newobjs));(R/'objects-outside-measured-trial.txt').write_text('\n'.join(oldobjs[x] for x in excluded)+'\n')
 finalkib=du(trial);mb=(finalkib+512)//1024
 cls=run(['node','--input-type=module','-e',"import {classifyRepo} from '/Users/macstudio3/.claude/skills/dw-backup-canary/lib.mjs'; console.log(JSON.stringify(classifyRepo({repo:'(projects-root)',mb:"+str(mb)+",remote:null,upstream:null,ahead:0})))"]).decode().strip()
 check('final self-contained trial contains no alternate object store',not (trial/'objects/info/alternates').exists())
 check('master and real index still exact after object reconstruction',git(trial,'rev-parse','refs/heads/master').decode().strip()==master and sha(trial/'index')==before['index_sha256'])
 check('restored backup includes every object moved outside measured trial',set(excluded)<=set(oldobjs))
 final=source_meta();source_unchanged=final==before
 check('source refs index and script unchanged through rehearsal',source_unchanged,final)
 for row in files:check('source candidate bytes unchanged at end '+row['path'],sha(W/row['path'])==row['sha256'])
 result={'status':'complete' if mb<2850 else 'blocked','scope':'isolated-copy preparation only','source':str(S),'scratch':str(R),'original_git':str(original),'restored_git':str(restored),'trial_git':str(trial),'master':master,'original_snapshot':snap,'rehearsed_snapshot':newsnap,'rehearsed_tree':newtree,'original_allocated_kib':size//1024,'trial_allocated_kib':finalkib,'trial_canary_mb':mb,'trial_classification':json.loads(cls),'below_2850':mb<2850,'candidate_still_reachable':retained_blob_hits,'objects_original':len(oldobjs),'objects_trial':len(newobjs),'objects_retained_outside_trial':len(excluded),'snapshot_paths_original':len(orig_tree),'snapshot_paths_trial':len(newentries),'original_index_sha256':before['index_sha256'],'original_script_sha256':before['script_sha256'],'pack_id':packid,'elapsed_seconds':round(time.time()-started,2),'free_bytes_after':shutil.disk_usage(R).free,'checks':checks,'live_execution':'SKIP: explicit Steve approval absent; no maintenance/scheduler/source change authorized','restore_method':'Fresh complete file copy from immutable original.git into retained restored.git; exact manifest/refs/index and fsck-unreachable comparison passed. Five blobs restored separately and SHA256 match original worktree archives.'}
 dump('rehearsal-result',result);print(json.dumps({k:v for k,v in result.items() if k!='checks'},indent=2),flush=True)
except Exception as e:
 dump('rehearsal-error',{'status':'blocked','error':str(e),'checks':checks,'scratch':str(R),'elapsed_seconds':round(time.time()-started,2),'retained':'all copies; no cleanup','source_mutations':False})
 print('BLOCKED '+str(e),flush=True);raise