[object Object]

← back to Ticket System

Rehearse backup reconstruction while preserving cached index trees

8c953a2e4a4d31f9810069ebaac733b0bf884aa1 · 2026-09-08 06:58:49 -0700 · Steve Abrams

Files touched

Diff

commit 8c953a2e4a4d31f9810069ebaac733b0bf884aa1
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Sep 8 06:58:49 2026 -0700

    Rehearse backup reconstruction while preserving cached index trees
---
 .../ops-reconstruct-cachetree-v2.py                | 117 +++++++++++
 .../ops-reconstruct-full-restore.json              |  15 ++
 .../ops-reconstruct-result.json                    | 122 +++++++++++
 .../ops-reconstruct-runner-v1.py                   | 233 +++++++++++++++++++++
 .../ops-reconstruct-v2-result.json                 | 198 +++++++++++++++++
 .../parent-cache-tree-defect-repro.json            |  17 ++
 ...cycle-20260908T1322Z-m4gNE5-reconstruction.json |  25 +++
 .../cycle-20260908T1322Z-m4gNE5-recovery-memo.md   | 115 ++++++++++
 8 files changed, 842 insertions(+)

diff --git a/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-cachetree-v2.py b/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-cachetree-v2.py
new file mode 100644
index 00000000..f2b70a9e
--- /dev/null
+++ b/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-cachetree-v2.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python3
+"""Preserve failed v1, add index TREE closure, rebuild under same data deadline."""
+import datetime,hashlib,json,os,shutil,stat,struct,subprocess,time
+from pathlib import Path
+B=Path('/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5')
+R=Path('/private/tmp/ops-TK10928-reconstruct.xcLYt5')
+S=Path('/private/tmp/ops-TK10928-rehearsal.ndMwgA/original.git')
+RESTORE=R/'restored.git';FAILED=R/'trial.git';TRIAL=R/'trial-cachetree-complete.git'
+STORE=R/'trial-superseded-objects-retained'
+start=json.loads((B/'ops-reconstruct-data-start.json').read_text())['started_epoch'];deadline=start+580
+ENV={k:v for k,v in os.environ.items() if not k.startswith('GIT_')}
+ENV.update(GIT_OPTIONAL_LOCKS='0',GIT_CONFIG_NOSYSTEM='1',GIT_CONFIG_GLOBAL='/dev/null',GIT_NO_REPLACE_OBJECTS='1')
+FULL=dict(ENV,GIT_OBJECT_DIRECTORY=str(STORE));checks=[];commands=[];result={}
+def dump(n,x):
+ p=B/('ops-reconstruct-'+n+'.json');p.write_text(json.dumps(x,indent=2)+'\n');return str(p)
+def left():
+ n=deadline-time.time()
+ if n<=0:raise TimeoutError('same original data phase580second internal cap')
+ return n
+def check(n,c,d=None):
+ checks.append({'check':n,'verdict':'PASS' if c else 'FAIL','detail':d});dump('v2-checks-progress',checks)
+ if not c:raise ValueError(n+': '+repr(d))
+def run(a,inp=None,env=None,out=None):
+ e={'argv':[str(x) for x in a],'input_sha256':hashlib.sha256(inp).hexdigest() if inp else None,'stdout_path':str(out) if out else None,'object_directory':(env or ENV).get('GIT_OBJECT_DIRECTORY'),'started_epoch':time.time()};commands.append(e);dump('v2-commands',commands)
+ if out:
+  with Path(out).open('xb') as f:p=subprocess.run(a,input=inp,stdout=f,stderr=subprocess.PIPE,env=env or ENV,timeout=left())
+  data=b''
+ else:p=subprocess.run(a,input=inp,capture_output=True,env=env or ENV,timeout=left());data=p.stdout
+ e.update(exit=p.returncode,stderr=p.stderr.decode(errors='replace')[:4000],finished_epoch=time.time());dump('v2-commands',commands)
+ if p.returncode:raise ValueError(repr(e))
+ return data
+def git(repo,*a,**kw):return run(['git','--git-dir='+str(repo),'-c','core.commitGraph=false','-c','gc.auto=0',*a],**kw)
+def sha(p):
+ h=hashlib.sha256()
+ with Path(p).open('rb') as f:
+  for d in iter(lambda:f.read(1048576),b''):left();h.update(d)
+ return h.hexdigest()
+def du(p):return int(run(['du','-sk',str(p)]).split()[0])
+def inv(raw):return {l.split()[0]:l.split() for l in raw.decode().splitlines()}
+def parse_index(data):
+ assert data[:4]==b'DIRC' and hashlib.sha1(data[:-20]).digest()==data[-20:]
+ version,count=struct.unpack('>II',data[4:12]);assert version==2
+ at=12
+ for i in range(count):
+  base=at;flags=struct.unpack('>H',data[at+60:at+62])[0];assert not flags&0x4000
+  nul=data.index(0,at+62);at=base+((nul+1-base+7)//8)*8
+ exts=[];trees=[]
+ def cache(payload,pos,parent):
+  nul=payload.index(0,pos);name=payload[pos:nul].decode();nl=payload.index(10,nul+1)
+  entries,children=map(int,payload[nul+1:nl].split());pos=nl+1;path=parent+name
+  if entries>=0:
+   oid=payload[pos:pos+20].hex();assert len(oid)==40;pos+=20;trees.append({'path':path,'entries':entries,'children':children,'oid':oid})
+  for _ in range(children):pos=cache(payload,pos,path+'/')
+  return pos
+ while at<len(data)-20:
+  sig=data[at:at+4].decode();length=struct.unpack('>I',data[at+4:at+8])[0];payload=data[at+8:at+8+length];at+=8+length
+  exts.append({'signature':sig,'bytes':length})
+  if sig=='TREE':assert cache(payload,0,'')==len(payload)
+  else:raise ValueError('Unreviewed index extension '+sig)
+ assert at==len(data)-20
+ return {'version':version,'entry_count':count,'extensions':exts,'cache_trees':trees,'index_internal_sha1':data[-20:].hex()}
+try:
+ check('canonical zero-cost guard unchanged',Path('/Users/macstudio3/Projects/ticket-system/config/dtd-cost-mode').read_bytes()==b'ZERO_COST_REQUIRED\n')
+ first=json.loads((B/'ops-reconstruct-result.json').read_text());check('v1 real negative connectivity retained',first['status']=='blocked' and 'invalid sha1 pointer in cache-tree' in first['error'])
+ idx=parse_index((RESTORE/'index').read_bytes());dump('index-cachetree-proof',idx)
+ roots={x['oid'] for x in idx['cache_trees']}
+ allold=inv((Path('/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1221Z.HzXvuQ')/'ops-original-object-inventory.txt').read_bytes())
+ check('all cache-tree pointers are known verified source trees',all(o in allold and allold[o][1]=='tree' for o in roots),{'roots':len(roots)})
+ cacheclosure=set(git(RESTORE,'rev-list','--objects','--no-object-names','--stdin',inp=('\n'.join(sorted(roots))+'\n').encode()).decode().splitlines())
+ initial=set((R/'candidate-retained-object-ids.txt').read_text().splitlines());retained=initial|cacheclosure
+ oldprotected=set((R/'protected-closure.txt').read_text().splitlines());protected=oldprotected|cacheclosure
+ kept=R/'candidate-v2-retained-object-ids.txt';kept.write_text('\n'.join(sorted(retained))+'\n')
+ (R/'protected-v2-object-ids.txt').write_text('\n'.join(sorted(protected))+'\n')
+ effect=json.loads((B/'ops-reconstruct-effect.json').read_text());candidates=set(x['git_oid'] for x in json.loads((B/'ops-five-render-proof.json').read_text())['restores'])
+ dump('cachetree-closure',{'cache_roots':len(roots),'cache_closure_objects':len(cacheclosure),'protected_before':len(oldprotected),'protected_after':len(protected),'newly_added_protected_objects':sorted(retained-initial),'candidate_blobs_protected':sorted(protected&candidates)})
+ TRIAL.mkdir()
+ for p in FAILED.rglob('*'):
+  rel=p.relative_to(FAILED)
+  if rel.parts[0]=='objects':continue
+  d=TRIAL/rel
+  if p.is_dir():d.mkdir(parents=True,exist_ok=True)
+  elif p.is_file():
+   d.parent.mkdir(parents=True,exist_ok=True);shutil.copyfile(p,d);os.chmod(d,stat.S_IMODE(p.stat().st_mode));os.utime(d,ns=(p.stat().st_atime_ns,p.stat().st_mtime_ns))
+ (TRIAL/'objects/pack').mkdir(parents=True);(TRIAL/'objects/info').mkdir()
+ packid=git(FAILED,'pack-objects','--window=0','--threads=2',str(TRIAL/'objects/pack/pack'),inp=kept.read_bytes(),env=FULL).decode().strip()
+ pack=TRIAL/'objects/pack'/('pack-'+packid+'.pack');packidx=pack.with_suffix('.idx')
+ git(TRIAL,'verify-pack','-v',str(packidx),out=R/'candidate-v2-verify-pack.txt')
+ git(TRIAL,'fsck','--connectivity-only','--no-dangling',out=R/'candidate-v2-connectivity.txt')
+ check('Git connectivity passes with exact preserved index cache-tree',True)
+ raw=git(TRIAL,'cat-file','--batch-all-objects','--batch-check=%(objectname) %(objecttype) %(objectsize) %(objectsize:disk)');(R/'candidate-v2-object-inventory.txt').write_bytes(raw);actual=inv(raw)
+ check('actual candidate pack inventory equals entire required closure',set(actual)==retained,{'actual':len(actual),'expected':len(retained)})
+ check('all original type and size identities preserved',all(actual[o][:3]==allold[o][:3] for o in set(allold)&retained))
+ check('all protected roots cache-trees and descendants present',protected<=retained and roots<=retained)
+ check('candidate is self-contained',not (TRIAL/'objects/info/alternates').exists())
+ snap=effect['new_snapshot'];old=effect['original_snapshot'];master=git(RESTORE,'rev-parse','refs/heads/master').decode().strip()
+ check('full leaf tree matches exact five-path effect proven in v1',git(TRIAL,'ls-tree','-rz','--full-tree',snap)==git(FAILED,'ls-tree','-rz','--full-tree',snap))
+ origrefs=git(RESTORE,'for-each-ref','--format=%(objectname) %(refname)').decode().splitlines();newrefs=git(TRIAL,'for-each-ref','--format=%(objectname) %(refname)').decode().splitlines()
+ check('every non-snapshot effective ref unchanged',[r for r in origrefs if not r.endswith(' refs/auto-snapshot/latest')]==[r for r in newrefs if not r.endswith(' refs/auto-snapshot/latest')])
+ check('master full tree unchanged',git(TRIAL,'ls-tree','-rz','--full-tree',master)==git(RESTORE,'ls-tree','-rz','--full-tree',master))
+ original_manifest=json.loads((Path('/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1221Z.HzXvuQ')/'ops-source-git-manifest.json').read_text())
+ metadata=[p for p in original_manifest if not p.startswith('objects/') and p not in ['refs/auto-snapshot/latest','logs/refs/auto-snapshot/latest','packed-refs']]
+ check('all protected metadata,index,reflogs,pseudorefs bytes and modes unchanged',all(sha(TRIAL/p)==original_manifest[p]['sha256'] and stat.S_IMODE((TRIAL/p).stat().st_mode)==original_manifest[p]['mode'] for p in metadata),metadata)
+ non_snapshot=lambda t:[x for x in t.splitlines(keepends=True) if not x.rstrip().endswith(' refs/auto-snapshot/latest')]
+ check('non-snapshot packed-ref lines byte-identical',non_snapshot((TRIAL/'packed-refs').read_text())==non_snapshot((RESTORE/'packed-refs').read_text()))
+ kib=du(TRIAL);mb=(kib+512)//1024
+ js="import {classifyRepo,KNOWN_LEGIT} from '/Users/macstudio3/.claude/skills/dw-backup-canary/lib.mjs';let mb="+str(mb)+";console.log(JSON.stringify({mb,known:KNOWN_LEGIT['(projects-root)'],classification:classifyRepo({repo:'(projects-root)',mb,remote:null,upstream:null,ahead:0})}))"
+ classified=json.loads(run(['node','--input-type=module','-e',js]));check('actual installed canary floor remains3000',classified['known']['floor_mb']==3000)
+ archived=sorted(set(allold)-retained);(R/'archived-v2-original-object-inventory.txt').write_text('\n'.join(' '.join(allold[o]) for o in archived)+'\n')
+ generated=json.loads((B/'ops-reconstruct-new-object-hashes.json').read_text())
+ check('all four generated object Git identities persist',all(o['oid'] in actual and actual[o['oid']][1:3]==[o['type'],str(o['bytes'])] for o in generated))
+ immutable=[S,*S.rglob('*')];check('all historical source immutable flags still set',all(p.stat().st_flags & stat.UF_IMMUTABLE for p in immutable))
+ total=du(R);original_kib=du(S)
+ result={'status':'complete' if mb<2850 and classified['classification']['verdict']=='PASS' else 'blocked','historical_feasibility':mb<2850,'operational_outcomes':0,'original':str(S),'restored':str(RESTORE),'candidate':str(TRIAL),'failed_candidate_retained':str(FAILED),'scratch':str(R),'original_allocated_kib':original_kib,'candidate_allocated_kib':kib,'candidate_canary_mb':mb,'candidate_classification':classified,'below2850':mb<2850,'protected_objects':len(protected),'index_cache_tree_roots':len(roots),'candidate_blobs_protected':sorted(protected&candidates),'objects_original':len(allold),'objects_candidate':len(actual),'objects_archived_outside_candidate':len(archived),'archived_by_type':{t:sum(allold[o][1]==t for o in archived) for t in ['commit','tree','blob','tag']},'archive_inventory':str(R/'archived-v2-original-object-inventory.txt'),'generated_objects':generated,'pack_id':packid,'pack_sha256':sha(pack),'pack_index_sha256':sha(packidx),'candidate_snapshot':snap,'original_snapshot':old,'candidate_tree':effect['new_tree'],'master':master,'index_sha256':sha(TRIAL/'index'),'scratch_total_allocated_kib':total,'scratch_plus_immutable_original_allocated_kib':total+original_kib,'current_free_bytes':shutil.disk_usage(R).free,'disk_reclamation_claim':False,'full_fsck_semantics':False,'external_gitlinks_recovered':False,'source_current_identity_claim':False,'source_mutations':False,'checks':checks}
+except Exception as ex:result={'status':'blocked','error':str(ex),'error_class':type(ex).__name__,'checks':checks,'scratch':str(R),'source_mutations':False}
+finally:
+ result['elapsed_combined_data_seconds']=time.time()-start;result['finished_utc']=datetime.datetime.now(datetime.timezone.utc).isoformat();result['commands']=dump('v2-commands',commands);result['all_partial_state_retained']=True
+ dump('v2-result',result);print(json.dumps({k:v for k,v in result.items() if k!='checks'}),flush=True)
diff --git a/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-full-restore.json b/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-full-restore.json
new file mode 100644
index 00000000..16d1bf19
--- /dev/null
+++ b/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-full-restore.json
@@ -0,0 +1,15 @@
+{
+  "verdict": "PASS",
+  "source": "/private/tmp/ops-TK10928-rehearsal.ndMwgA/original.git",
+  "restored": "/private/tmp/ops-TK10928-reconstruct.xcLYt5/restored.git",
+  "files": 4059,
+  "manifest": "/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1221Z.HzXvuQ/ops-source-git-manifest.json",
+  "refs": [
+    "ad275a5b3bbabd66d23893af977e96553f969eaa refs/auto-snapshot/latest",
+    "c539b5a08a4f84a551d8825a0fc73fa588bcbce8 refs/heads/master"
+  ],
+  "index_sha256": "70711329911428bc7dc20cfbf7e118b05762f38eee3fc7d51b18d35c12fe76ff",
+  "shared_source_inodes": 0,
+  "immutable_source_flags_unchanged": true,
+  "full_fsck_semantics": false
+}
diff --git a/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-result.json b/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-result.json
new file mode 100644
index 00000000..35c862dd
--- /dev/null
+++ b/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-result.json
@@ -0,0 +1,122 @@
+{
+  "status": "blocked",
+  "error": "command failed: {'argv': ['git', '--git-dir=/private/tmp/ops-TK10928-reconstruct.xcLYt5/trial.git', '-c', 'core.commitGraph=false', '-c', 'gc.auto=0', 'fsck', '--connectivity-only', '--no-dangling'], 'input_sha256': None, 'stdout_path': '/private/tmp/ops-TK10928-reconstruct.xcLYt5/candidate-connectivity.txt', 'started_epoch': 1788875285.428448, 'exit': 8, 'stderr': 'error: e9a40128f55647db8713e021ee1c20471e32b466: invalid sha1 pointer in cache-tree of /private/tmp/ops-TK10928-reconstruct.xcLYt5/trial.git/index\\n', 'finished_epoch': 1788875285.528594}",
+  "error_class": "ValueError",
+  "scratch": "/private/tmp/ops-TK10928-reconstruct.xcLYt5",
+  "retained": "All completed/partial copies and stores remain retained",
+  "source_mutations": false,
+  "checks": [
+    {
+      "check": "canonical zero-cost guard",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "reuse previously accepted full object integrity pins",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "exact five candidate set",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "headroom for two copies plus rebuilt store and5GB",
+      "verdict": "PASS",
+      "detail": {
+        "free_bytes": 75382607872,
+        "source_kib": 3106332
+      }
+    },
+    {
+      "check": "five original snapshot Git identities",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "independent full copy exact restored.git",
+      "verdict": "PASS",
+      "detail": {
+        "files": 4059,
+        "bytes": 3167634917,
+        "receipt": "/private/tmp/ops-TK10928-reconstruct.xcLYt5/restored-copy-receipts.jsonl"
+      }
+    },
+    {
+      "check": "restored refs exact",
+      "verdict": "PASS",
+      "detail": [
+        "ad275a5b3bbabd66d23893af977e96553f969eaa refs/auto-snapshot/latest",
+        "c539b5a08a4f84a551d8825a0fc73fa588bcbce8 refs/heads/master"
+      ]
+    },
+    {
+      "check": "restored metadata identity including index refs reflogs pseudorefs",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "independent full copy exact trial.git",
+      "verdict": "PASS",
+      "detail": {
+        "files": 4059,
+        "bytes": 3167634917,
+        "receipt": "/private/tmp/ops-TK10928-reconstruct.xcLYt5/trial-copy-receipts.jsonl"
+      }
+    },
+    {
+      "check": "all protected roots present in pinned source inventory",
+      "verdict": "PASS",
+      "detail": []
+    },
+    {
+      "check": "snapshot exact five-path-only leaf effect",
+      "verdict": "PASS",
+      "detail": {
+        "original_entries": 3727,
+        "candidate_entries": 3722,
+        "removed": [
+          "rentv-slideshow/renders/rentv-slideshow_2026-07-29_07-02-25.mp4",
+          "rentv-slideshow/renders/rentv-slideshow_2026-07-29_08-29-23.mp4",
+          "rentv-slideshow/renders/rentv-slideshow_2026-07-29_08-22-08.mp4",
+          "rentv-slideshow/renders/rentv-slideshow_2026-07-29_08-16-11.mp4",
+          "rentv-slideshow/renders/rentv-slideshow_2026-07-29_07-49-53.mp4"
+        ]
+      }
+    },
+    {
+      "check": "master tree/ref and index preserved before packing",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "entire protected closure retained",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "new object Git hash 173a76acd04090f7987d533350900643aa24b2cd",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "new object Git hash 1b6d8e01c8e6a27678d9717fb4824dc87b683525",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "new object Git hash 5672ed8be8d24de1a2a4fd2e744dd16e640b11e8",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "new object Git hash e5906c0c9d428bce148fe1d1ea00fde5f1113be4",
+      "verdict": "PASS",
+      "detail": null
+    }
+  ],
+  "elapsed_data_seconds": 123.20598562504165,
+  "finished_utc": "2026-09-08T13:48:05.529922+00:00",
+  "commands": "/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-commands.json"
+}
diff --git a/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-runner-v1.py b/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-runner-v1.py
new file mode 100644
index 00000000..6c5bf44e
--- /dev/null
+++ b/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-runner-v1.py
@@ -0,0 +1,233 @@
+#!/usr/bin/env python3
+"""Retained historical Git restoration/reconstruction; no live source writes."""
+import hashlib,json,os,re,shutil,stat,subprocess,time,datetime
+from pathlib import Path
+
+B=Path('/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5')
+OLD=Path('/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1221Z.HzXvuQ')
+S=Path('/private/tmp/ops-TK10928-rehearsal.ndMwgA/original.git')
+R=Path('/private/tmp/ops-TK10928-reconstruct.xcLYt5')
+RESTORE=R/'restored.git';TRIAL=R/'trial.git'
+GUARD=Path('/Users/macstudio3/Projects/ticket-system/config/dtd-cost-mode')
+ENV={k:v for k,v in os.environ.items() if not k.startswith('GIT_')}
+ENV.update(GIT_OPTIONAL_LOCKS='0',GIT_CONFIG_NOSYSTEM='1',GIT_CONFIG_GLOBAL='/dev/null',GIT_NO_REPLACE_OBJECTS='1')
+checks=[];commands=[];result={};started=time.monotonic();deadline=started+580
+HEX=re.compile(r'^[0-9a-f]{40}$');ZERO='0'*40
+
+def dump(name,x):
+    p=B/('ops-reconstruct-'+name+'.json')
+    p.write_text(json.dumps(x,indent=2)+'\n')
+    return str(p)
+
+def left():
+    n=deadline-time.monotonic()
+    if n<=0:raise TimeoutError('580second internal deadline;600second total maximum')
+    return n
+
+def check(name,yes,detail=None):
+    checks.append({'check':name,'verdict':'PASS' if yes else 'FAIL','detail':detail})
+    dump('checks-progress',checks)
+    if not yes:raise ValueError(name+': '+repr(detail))
+
+def run(argv,inp=None,env=None,out=None,ok=True):
+    entry={'argv':[str(x) for x in argv],'input_sha256':hashlib.sha256(inp).hexdigest() if inp is not None else None,'stdout_path':str(out) if out else None,'started_epoch':time.time()}
+    commands.append(entry);dump('commands',commands)
+    if out:
+        with Path(out).open('xb') as f:
+            p=subprocess.run(argv,input=inp,stdout=f,stderr=subprocess.PIPE,env=env or ENV,timeout=left())
+        data=b''
+    else:
+        p=subprocess.run(argv,input=inp,capture_output=True,env=env or ENV,timeout=left());data=p.stdout
+    entry.update(exit=p.returncode,stderr=p.stderr.decode(errors='replace')[:4000],finished_epoch=time.time());dump('commands',commands)
+    if ok and p.returncode:raise ValueError('command failed: '+repr(entry))
+    return data
+
+def git(repo,*args,**kwargs):
+    return run(['git','--git-dir='+str(repo),'-c','core.commitGraph=false','-c','gc.auto=0',*args],**kwargs)
+
+def sha(path):
+    h=hashlib.sha256()
+    with Path(path).open('rb') as f:
+        for data in iter(lambda:f.read(1024*1024),b''):
+            left();h.update(data)
+    return h.hexdigest()
+
+def du(path):return int(run(['du','-sk',str(path)]).split()[0])
+
+def refs(repo):return git(repo,'for-each-ref','--format=%(objectname) %(refname)').decode().splitlines()
+
+def tree(repo,ref):
+    entries={}
+    for row in git(repo,'ls-tree','-rz','--full-tree',ref).split(b'\0'):
+        if row:
+            meta,path=row.split(b'\t',1);entries[path.decode('utf-8','surrogateescape')]=meta.decode()
+    return entries
+
+def manifest_copy(target,expected):
+    target.mkdir()
+    copied={};receipt=R/(target.stem+'-copy-receipts.jsonl')
+    with receipt.open('x') as stream:
+        for rel,old in sorted(expected.items()):
+            left();source=S/rel;dest=target/rel
+            ss=source.stat();check_immutable=ss.st_flags & stat.UF_IMMUTABLE
+            if not check_immutable:raise ValueError('source immutable flag missing: '+rel)
+            dest.parent.mkdir(parents=True,exist_ok=True)
+            shutil.copyfile(source,dest)
+            os.chmod(dest,old['mode']);os.utime(dest,ns=(ss.st_atime_ns,old['mtime_ns']))
+            ds=dest.stat();actual={'sha256':sha(dest),'bytes':ds.st_size,'mode':stat.S_IMODE(ds.st_mode),'mtime_ns':ds.st_mtime_ns}
+            if actual!=old:raise ValueError('copy identity mismatch: '+rel)
+            if ds.st_ino==ss.st_ino and ds.st_dev==ss.st_dev:raise ValueError('shared source inode: '+rel)
+            if ds.st_nlink!=1:raise ValueError('unexpected hardlink: '+rel)
+            if ds.st_flags & stat.UF_IMMUTABLE:raise ValueError('destination unexpectedly immutable')
+            copied[rel]=actual
+            stream.write(json.dumps({'path':rel,**actual})+'\n');stream.flush()
+        os.fsync(stream.fileno())
+    # Preserve every source directory, including empty directories, without flags.
+    for p in S.rglob('*'):
+        if p.is_dir():
+            d=target/p.relative_to(S);d.mkdir(parents=True,exist_ok=True);os.chmod(d,stat.S_IMODE(p.stat().st_mode))
+    actual_paths={str(p.relative_to(target)) for p in target.rglob('*') if p.is_file()}
+    check('independent full copy exact '+target.name,actual_paths==set(expected) and copied==expected,{'files':len(copied),'bytes':sum(x['bytes'] for x in copied.values()),'receipt':str(receipt)})
+    return copied
+
+try:
+    check('canonical zero-cost guard',GUARD.read_bytes()==b'ZERO_COST_REQUIRED\n')
+    oldmanifest=json.loads((OLD/'ops-source-git-manifest.json').read_text())
+    source_rows={r.split()[0]:r for r in (OLD/'ops-original-object-inventory.txt').read_text().splitlines()}
+    pins=json.loads((B/'ops-object-pins.json').read_text())
+    prior=json.loads((B/'ops-object-result.json').read_text())
+    check('reuse previously accepted full object integrity pins',prior['verdict']=='PASS' and prior['remaining']==0 and sha(OLD/'ops-original-object-inventory.txt')==pins['inventory_sha256'] and sha(OLD/'ops-source-git-manifest.json')==pins['manifest_sha256'])
+    render=json.loads((B/'ops-five-render-proof.json').read_text())
+    candidates={x['path']:x['git_oid'] for x in render['restores']}
+    check('exact five candidate set',len(candidates)==5 and render['verdict']=='PASS')
+    size=du(S);free=shutil.disk_usage(R).free
+    check('headroom for two copies plus rebuilt store and5GB',free>size*1024*4+5*1024**3,{'free_bytes':free,'source_kib':size})
+    dump('data-start',{'started_utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),'started_epoch':time.time(),'upper_bound_seconds':600,'internal_deadline_seconds':580,'max_workers':2,'scratch':str(R),'source':str(S)})
+    source_refs=refs(S);snap=git(S,'rev-parse','refs/auto-snapshot/latest').decode().strip();master=git(S,'rev-parse','refs/heads/master').decode().strip()
+    snapshot=tree(S,snap);master_tree=tree(S,master)
+    check('five original snapshot Git identities',all(snapshot.get(p,'').endswith(o) for p,o in candidates.items()))
+    restored_manifest=manifest_copy(RESTORE,oldmanifest)
+    check('restored refs exact',refs(RESTORE)==source_refs,source_refs)
+    check('restored metadata identity including index refs reflogs pseudorefs',all(restored_manifest[p]==x for p,x in oldmanifest.items() if not p.startswith('objects/')))
+    dump('full-restore',{'verdict':'PASS','source':str(S),'restored':str(RESTORE),'files':len(restored_manifest),'manifest':str(OLD/'ops-source-git-manifest.json'),'refs':source_refs,'index_sha256':restored_manifest['index']['sha256'],'shared_source_inodes':0,'immutable_source_flags_unchanged':True,'full_fsck_semantics':False})
+    manifest_copy(TRIAL,oldmanifest)
+    print('Two full independent copies verified; constructing exact five-path snapshot',flush=True)
+    indexraw=git(RESTORE,'ls-files','--stage','-z');(R/'protected-index-entries.bin').write_bytes(indexraw)
+    protected=set();external_index=[]
+    protected_evidence={}
+    for line in source_refs:
+        oid,name=line.split()
+        if name!='refs/auto-snapshot/latest':protected.add(oid);protected_evidence.setdefault(oid,[]).append(name)
+    protected.add(git(RESTORE,'rev-parse','HEAD').decode().strip())
+    for p in (RESTORE/'logs').rglob('*'):
+        if p.is_file() and str(p.relative_to(RESTORE/'logs'))!='refs/auto-snapshot/latest':
+            for row in p.read_text().splitlines():
+                for oid in row.split()[:2]:
+                    if oid!=ZERO:protected.add(oid);protected_evidence.setdefault(oid,[]).append(str(p.relative_to(RESTORE)))
+    for p in RESTORE.iterdir():
+        if p.is_file() and re.fullmatch('[A-Z_]+',p.name) and p.name!='COMMIT_EDITMSG':
+            for row in p.read_text(errors='strict').splitlines():
+                tokens=row.split()
+                if tokens and HEX.fullmatch(tokens[0]) and tokens[0]!=ZERO:
+                    protected.add(tokens[0]);protected_evidence.setdefault(tokens[0],[]).append(p.name)
+    packed=RESTORE/'packed-refs'
+    if packed.exists():
+        for row in packed.read_text().splitlines():
+            if row and row[0] not in '#^':
+                oid,name=row.split()
+                if name!='refs/auto-snapshot/latest':protected.add(oid);protected_evidence.setdefault(oid,[]).append('packed:'+name)
+    for row in indexraw.split(b'\0'):
+        if row:
+            meta,path=row.split(b'\t',1);mode,oid,stage=meta.decode().split()
+            if mode=='160000':external_index.append({'path':path.decode(),'oid':oid})
+            elif oid!=ZERO:protected.add(oid);protected_evidence.setdefault(oid,[]).append('index:'+path.decode())
+    check('all protected roots present in pinned source inventory',protected<=set(source_rows),sorted(protected-set(source_rows)))
+    (R/'protected-roots.txt').write_text('\n'.join(sorted(protected))+'\n')
+    closure_raw=git(RESTORE,'rev-list','--objects','--no-object-names','--stdin',inp=('\n'.join(sorted(protected))+'\n').encode())
+    protected_closure=set(closure_raw.decode().splitlines());(R/'protected-closure.txt').write_bytes(closure_raw)
+    dump('protected',{'roots':len(protected),'closure_objects':len(protected_closure),'sources':protected_evidence,'external_index_gitlinks':external_index,'candidate_blobs_protected':sorted(set(candidates.values())&protected_closure)})
+    newidx=R/'snapshot.index';ienv=dict(ENV,GIT_INDEX_FILE=str(newidx))
+    git(TRIAL,'read-tree','--empty',env=ienv)
+    kept=[]
+    for path,meta in snapshot.items():
+        if path not in candidates:
+            mode,kind,oid=meta.split();kept.append((mode+' '+oid+'\t'+path).encode('utf-8','surrogateescape')+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 exclusion trial',env=cenv).decode().strip()
+    git(TRIAL,'update-ref','refs/auto-snapshot/latest',newsnap,snap)
+    # The capture has a hidden older packed value for the same snapshot ref.
+    # Rebase only that snapshot entry too; preserve every non-snapshot line.
+    packed_preimage=(TRIAL/'packed-refs').read_text() if (TRIAL/'packed-refs').exists() else ''
+    packed_lines=[];packed_snapshot_changes=[]
+    for line in packed_preimage.splitlines(keepends=True):
+        parts=line.strip().split()
+        if len(parts)==2 and parts[1]=='refs/auto-snapshot/latest':
+            packed_snapshot_changes.append({'before':parts[0],'after':newsnap,'ref':parts[1]})
+            packed_lines.append(newsnap+' '+parts[1]+'\n')
+        else:packed_lines.append(line)
+    if packed_snapshot_changes:
+        (R/'candidate-packed-refs.preimage').write_text(packed_preimage)
+        (TRIAL/'packed-refs').write_text(''.join(packed_lines))
+    dump('packed-snapshot-effect',packed_snapshot_changes)
+    nexttree=tree(TRIAL,newsnap)
+    check('snapshot exact five-path-only leaf effect',nexttree=={p:x for p,x in snapshot.items() if p not in candidates},{'original_entries':len(snapshot),'candidate_entries':len(nexttree),'removed':list(candidates)})
+    check('master tree/ref and index preserved before packing',tree(TRIAL,master)==master_tree and git(TRIAL,'rev-parse','refs/heads/master').decode().strip()==master and sha(TRIAL/'index')==oldmanifest['index']['sha256'])
+    retained=set(git(TRIAL,'rev-list','--objects','--no-object-names','--stdin',inp=('\n'.join(sorted(protected|{newsnap}))+'\n').encode()).decode().splitlines())
+    check('entire protected closure retained',protected_closure<=retained)
+    retained_file=R/'candidate-retained-object-ids.txt';retained_file.write_text('\n'.join(sorted(retained))+'\n')
+    prepack_raw=git(TRIAL,'cat-file','--batch-all-objects','--batch-check=%(objectname) %(objecttype) %(objectsize) %(objectsize:disk)')
+    (R/'trial-prepack-object-inventory.txt').write_bytes(prepack_raw)
+    generated=set(x.split()[0] for x in prepack_raw.decode().splitlines())-set(source_rows)
+    newchecks=[]
+    for oid in sorted(generated):
+        kind=git(TRIAL,'cat-file','-t',oid).decode().strip();payload=git(TRIAL,'cat-file',kind,oid)
+        digest=hashlib.sha1((kind+' '+str(len(payload))+'\0').encode()+payload).hexdigest()
+        check('new object Git hash '+oid,digest==oid)
+        newchecks.append({'oid':oid,'type':kind,'bytes':len(payload),'hash':digest})
+    dump('new-object-hashes',newchecks)
+    archived=sorted(set(source_rows)-retained)
+    (R/'archived-original-object-inventory.txt').write_text('\n'.join(source_rows[o] for o in archived)+'\n')
+    oldcommits=set(git(RESTORE,'rev-list',snap).decode().splitlines())
+    archived_commits=sorted(oldcommits-retained)
+    dump('effect',{'original_snapshot':snap,'new_snapshot':newsnap,'new_tree':newtree,'new_commit_has_no_parent':True,'old_snapshot_commit_chain_count':len(oldcommits),'old_snapshot_commits_outside_candidate':archived_commits,'original_objects':len(source_rows),'retained_objects':len(retained),'generated_objects':sorted(generated),'archived_original_objects_count':len(archived),'archived_original_objects_path':str(R/'archived-original-object-inventory.txt'),'archived_by_type':{t:sum(source_rows[o].split()[1]==t for o in archived) for t in ['commit','tree','blob','tag']},'candidate_blobs_still_required':sorted(set(candidates.values())&retained),'snapshot_gitlink_entries':sum(x.startswith('160000 ') for x in snapshot.values()),'snapshot_exact_removed_paths':list(candidates),'protected_index_sha256':oldmanifest['index']['sha256'],'scope_warning':'Much broader historical-object archival than five blobs; all originals stay in complete undo stores.'})
+    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=retained_file.read_bytes()).decode().strip()
+    # Preserve superseded stores as siblings; no object deletion or cleanup.
+    os.rename(TRIAL/'objects',R/'trial-superseded-objects-retained')
+    os.rename(stage,TRIAL/'objects')
+    log=TRIAL/'logs/refs/auto-snapshot/latest'
+    if log.exists():os.rename(log,R/'trial-superseded-snapshot-reflog-retained')
+    pack=TRIAL/'objects/pack'/('pack-'+packid+'.pack');idx=pack.with_suffix('.idx')
+    print('Candidate store built; verifying exact pack inventory and Git connectivity',flush=True)
+    git(TRIAL,'verify-pack','-v',str(idx),out=R/'candidate-verify-pack.txt')
+    git(TRIAL,'fsck','--connectivity-only','--no-dangling',out=R/'candidate-connectivity.txt')
+    raw=git(TRIAL,'cat-file','--batch-all-objects','--batch-check=%(objectname) %(objecttype) %(objectsize) %(objectsize:disk)')
+    (R/'candidate-object-inventory.txt').write_bytes(raw)
+    actual={x.split()[0]:x.split() for x in raw.decode().splitlines()}
+    check('actual pack inventory exact retained object set',set(actual)==retained,{'expected':len(retained),'actual':len(actual)})
+    check('all original object type and size identities preserved',all(actual[o][:3]==source_rows[o].split()[:3] for o in retained&set(source_rows)))
+    check('self-contained candidate no alternate store',not (TRIAL/'objects/info/alternates').exists())
+    check('candidate tree exact after self-contained reconstruction',tree(TRIAL,newsnap)==nexttree)
+    check('protected refs/index/master tree unchanged after reconstruction',[(o,n) for o,n in (x.split() for x in refs(TRIAL)) if n!='refs/auto-snapshot/latest']==[(o,n) for o,n in (x.split() for x in source_refs) if n!='refs/auto-snapshot/latest'] and sha(TRIAL/'index')==oldmanifest['index']['sha256'] and tree(TRIAL,master)==master_tree)
+    protected_metadata=[p for p in oldmanifest if not p.startswith('objects/') and p not in ['refs/auto-snapshot/latest','logs/refs/auto-snapshot/latest','packed-refs']]
+    check('all protected metadata file bytes and modes unchanged',all((TRIAL/p).is_file() and sha(TRIAL/p)==oldmanifest[p]['sha256'] and stat.S_IMODE((TRIAL/p).stat().st_mode)==oldmanifest[p]['mode'] for p in protected_metadata))
+    non_snapshot=lambda text:[x for x in text.splitlines(keepends=True) if not x.rstrip().endswith(' refs/auto-snapshot/latest')]
+    check('non-snapshot packed-ref lines byte-identical',non_snapshot((TRIAL/'packed-refs').read_text())==non_snapshot(packed_preimage))
+    kib=du(TRIAL);mb=(kib+512)//1024
+    script="import {classifyRepo,KNOWN_LEGIT} from '/Users/macstudio3/.claude/skills/dw-backup-canary/lib.mjs';let mb="+str(mb)+";console.log(JSON.stringify({mb,known:KNOWN_LEGIT['(projects-root)'],classification:classifyRepo({repo:'(projects-root)',mb,remote:null,upstream:null,ahead:0})}))"
+    classified=json.loads(run(['node','--input-type=module','-e',script]))
+    check('actual canary root floor unchanged3000',classified['known']['floor_mb']==3000)
+    source_entries=[S,*S.rglob('*')]
+    check('all original immutable flags remain set',all(p.stat().st_flags & stat.UF_IMMUTABLE for p in source_entries))
+    total=du(R);afterfree=shutil.disk_usage(R).free
+    result={'status':'complete' if mb<2850 and classified['classification']['verdict']=='PASS' else 'blocked','scope':'historical copy-only feasibility;zero operational outcomes','original':str(S),'restored':str(RESTORE),'trial':str(TRIAL),'scratch':str(R),'original_allocated_kib':size,'candidate_allocated_kib':kib,'candidate_canary_mb':mb,'candidate_classification':classified,'target_below2850':mb<2850,'scratch_total_allocated_kib':total,'scratch_plus_original_allocated_kib':total+size,'free_bytes_before':free,'free_bytes_after':afterfree,'observed_filesystem_free_delta_bytes':afterfree-free,'physical_disk_reclaimed_claim':False,'pack_id':packid,'pack_sha256':sha(pack),'pack_index_sha256':sha(idx),'restored_files':len(oldmanifest),'original_objects':len(source_rows),'candidate_objects':len(actual),'archived_original_objects':len(archived),'protected_objects':len(protected_closure),'candidate_blobs_protected':sorted(set(candidates.values())&protected_closure),'generated_object_ids':sorted(generated),'original_snapshot':snap,'candidate_snapshot':newsnap,'candidate_tree':newtree,'master':master,'index_sha256':oldmanifest['index']['sha256'],'snapshot_entries_before':len(snapshot),'snapshot_entries_after':len(nexttree),'full_fsck_semantics':False,'external_gitlinks_validated':False,'source_current_identity_claim':False,'source_mutations':False,'no_cleanup':True,'checks':checks}
+except Exception as ex:
+    result={'status':'blocked','error':str(ex),'error_class':type(ex).__name__,'scratch':str(R),'retained':'All completed/partial copies and stores remain retained','source_mutations':False,'checks':checks}
+finally:
+    result['elapsed_data_seconds']=time.monotonic()-started
+    result['finished_utc']=datetime.datetime.now(datetime.timezone.utc).isoformat()
+    result['commands']=dump('commands',commands)
+    dump('result',result)
+    print(json.dumps({k:v for k,v in result.items() if k!='checks'}),flush=True)
diff --git a/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-v2-result.json b/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-v2-result.json
new file mode 100644
index 00000000..ae468676
--- /dev/null
+++ b/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-v2-result.json
@@ -0,0 +1,198 @@
+{
+  "status": "complete",
+  "historical_feasibility": true,
+  "operational_outcomes": 0,
+  "original": "/private/tmp/ops-TK10928-rehearsal.ndMwgA/original.git",
+  "restored": "/private/tmp/ops-TK10928-reconstruct.xcLYt5/restored.git",
+  "candidate": "/private/tmp/ops-TK10928-reconstruct.xcLYt5/trial-cachetree-complete.git",
+  "failed_candidate_retained": "/private/tmp/ops-TK10928-reconstruct.xcLYt5/trial.git",
+  "scratch": "/private/tmp/ops-TK10928-reconstruct.xcLYt5",
+  "original_allocated_kib": 3106332,
+  "candidate_allocated_kib": 1492032,
+  "candidate_canary_mb": 1457,
+  "candidate_classification": {
+    "mb": 1457,
+    "known": {
+      "floor_mb": 3000,
+      "reason": "intentional auto-data-snapshot monorepo at ~/Projects/.git (commits logs/data on a schedule); ~2.3GB and growing \u2014 crossing floor = time to trim history / gitignore logs"
+    },
+    "classification": {
+      "verdict": "PASS",
+      "issues": [],
+      "legit_note": "known-legit 1457MB (<= 3000MB floor): intentional auto-data-snapshot monorepo at ~/Projects/.git (commits logs/data on a schedule); ~2.3GB and growing \u2014 crossing floor = time to trim history / gitignore logs"
+    }
+  },
+  "below2850": true,
+  "protected_objects": 641,
+  "index_cache_tree_roots": 101,
+  "candidate_blobs_protected": [],
+  "objects_original": 14061,
+  "objects_candidate": 2409,
+  "objects_archived_outside_candidate": 11656,
+  "archived_by_type": {
+    "commit": 2483,
+    "tree": 6821,
+    "blob": 2352,
+    "tag": 0
+  },
+  "archive_inventory": "/private/tmp/ops-TK10928-reconstruct.xcLYt5/archived-v2-original-object-inventory.txt",
+  "generated_objects": [
+    {
+      "oid": "173a76acd04090f7987d533350900643aa24b2cd",
+      "type": "tree",
+      "bytes": 957,
+      "hash": "173a76acd04090f7987d533350900643aa24b2cd"
+    },
+    {
+      "oid": "1b6d8e01c8e6a27678d9717fb4824dc87b683525",
+      "type": "tree",
+      "bytes": 36393,
+      "hash": "1b6d8e01c8e6a27678d9717fb4824dc87b683525"
+    },
+    {
+      "oid": "5672ed8be8d24de1a2a4fd2e744dd16e640b11e8",
+      "type": "tree",
+      "bytes": 1675,
+      "hash": "5672ed8be8d24de1a2a4fd2e744dd16e640b11e8"
+    },
+    {
+      "oid": "e5906c0c9d428bce148fe1d1ea00fde5f1113be4",
+      "type": "commit",
+      "bytes": 256,
+      "hash": "e5906c0c9d428bce148fe1d1ea00fde5f1113be4"
+    }
+  ],
+  "pack_id": "2289315739e5095870047d179349e7a12333196d",
+  "pack_sha256": "36b30a14d6cbef286ce967f5fc29130f152de4d60f52cc5d96c930963533d4b9",
+  "pack_index_sha256": "15c7b0321e66c0d465671f4aab7cdf8bdf63db7abf62602aa02673915903a126",
+  "candidate_snapshot": "e5906c0c9d428bce148fe1d1ea00fde5f1113be4",
+  "original_snapshot": "ad275a5b3bbabd66d23893af977e96553f969eaa",
+  "candidate_tree": "1b6d8e01c8e6a27678d9717fb4824dc87b683525",
+  "master": "c539b5a08a4f84a551d8825a0fc73fa588bcbce8",
+  "index_sha256": "70711329911428bc7dc20cfbf7e118b05762f38eee3fc7d51b18d35c12fe76ff",
+  "scratch_total_allocated_kib": 9197040,
+  "scratch_plus_immutable_original_allocated_kib": 12303372,
+  "current_free_bytes": 66086895616,
+  "disk_reclamation_claim": false,
+  "full_fsck_semantics": false,
+  "external_gitlinks_recovered": false,
+  "source_current_identity_claim": false,
+  "source_mutations": false,
+  "checks": [
+    {
+      "check": "canonical zero-cost guard unchanged",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "v1 real negative connectivity retained",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "all cache-tree pointers are known verified source trees",
+      "verdict": "PASS",
+      "detail": {
+        "roots": 101
+      }
+    },
+    {
+      "check": "Git connectivity passes with exact preserved index cache-tree",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "actual candidate pack inventory equals entire required closure",
+      "verdict": "PASS",
+      "detail": {
+        "actual": 2409,
+        "expected": 2409
+      }
+    },
+    {
+      "check": "all original type and size identities preserved",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "all protected roots cache-trees and descendants present",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "candidate is self-contained",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "full leaf tree matches exact five-path effect proven in v1",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "every non-snapshot effective ref unchanged",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "master full tree unchanged",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "all protected metadata,index,reflogs,pseudorefs bytes and modes unchanged",
+      "verdict": "PASS",
+      "detail": [
+        "COMMIT_EDITMSG",
+        "HEAD",
+        "ORIG_HEAD",
+        "config",
+        "description",
+        "hooks/applypatch-msg.sample",
+        "hooks/commit-msg.sample",
+        "hooks/fsmonitor-watchman.sample",
+        "hooks/post-update.sample",
+        "hooks/pre-applypatch.sample",
+        "hooks/pre-commit.sample",
+        "hooks/pre-merge-commit.sample",
+        "hooks/pre-push.sample",
+        "hooks/pre-rebase.sample",
+        "hooks/pre-receive.sample",
+        "hooks/prepare-commit-msg.sample",
+        "hooks/push-to-checkout.sample",
+        "hooks/sendemail-validate.sample",
+        "hooks/update.sample",
+        "index",
+        "info/exclude",
+        "info/refs",
+        "logs/HEAD",
+        "logs/refs/heads/master",
+        "refs/heads/master"
+      ]
+    },
+    {
+      "check": "non-snapshot packed-ref lines byte-identical",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "actual installed canary floor remains3000",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "all four generated object Git identities persist",
+      "verdict": "PASS",
+      "detail": null
+    },
+    {
+      "check": "all historical source immutable flags still set",
+      "verdict": "PASS",
+      "detail": null
+    }
+  ],
+  "elapsed_combined_data_seconds": 420.1357727050781,
+  "finished_utc": "2026-09-08T13:53:02.629168+00:00",
+  "commands": "/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-v2-commands.json",
+  "all_partial_state_retained": true
+}
diff --git a/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/parent-cache-tree-defect-repro.json b/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/parent-cache-tree-defect-repro.json
new file mode 100644
index 00000000..baae222d
--- /dev/null
+++ b/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/parent-cache-tree-defect-repro.json
@@ -0,0 +1,17 @@
+{
+  "argv": [
+    "git",
+    "--git-dir=/private/tmp/ops-TK10928-reconstruct.xcLYt5/trial.git",
+    "-c",
+    "core.commitGraph=false",
+    "fsck",
+    "--connectivity-only",
+    "--no-dangling"
+  ],
+  "exit_code": 8,
+  "stdout": "",
+  "stderr": "error: e9a40128f55647db8713e021ee1c20471e32b466: invalid sha1 pointer in cache-tree of /private/tmp/ops-TK10928-reconstruct.xcLYt5/trial.git/index\n",
+  "reproduced": true,
+  "candidate_index_sha256": "70711329911428bc7dc20cfbf7e118b05762f38eee3fc7d51b18d35c12fe76ff",
+  "original_index_sha256": "70711329911428bc7dc20cfbf7e118b05762f38eee3fc7d51b18d35c12fe76ff"
+}
diff --git a/verification/cycle-20260908T1322Z-m4gNE5-reconstruction.json b/verification/cycle-20260908T1322Z-m4gNE5-reconstruction.json
new file mode 100644
index 00000000..26d2f412
--- /dev/null
+++ b/verification/cycle-20260908T1322Z-m4gNE5-reconstruction.json
@@ -0,0 +1,25 @@
+{
+  "ticket": "TK-10928",
+  "cycle": "cycle-20260908T1322Z.m4gNE5",
+  "status": "PASS historicalfeasibility;liveHOLD",
+  "candidate_mb": 1457,
+  "archived_objects": 11656,
+  "all_undo_retained": true,
+  "pack_sha256": "36b30a14d6cbef286ce967f5fc29130f152de4d60f52cc5d96c930963533d4b9",
+  "artifact_hashes": {
+    "ops-reconstruct-runner-v1.py": "592812c116b9a434ae466ad51c5e822d43225eedc28ce68115bac77d7305dfd3",
+    "ops-reconstruct-cachetree-v2.py": "c0bd9e19b7b7ea0af237f235dd603e1099b83721d0d36e5237574f09c4bfd092",
+    "ops-reconstruct-result.json": "5baae1a9b60ffb4e70bf9e43ece4e10df5d4e4d78d50aa67564c0cf02afb955c",
+    "ops-reconstruct-v2-result.json": "bb7392a17a7863eded7d1c71ff3c26103a12a5af17ce37e602d661e803b0538f",
+    "ops-reconstruct-full-restore.json": "4f0911a3f67f9eabc30813b682a1c664b7a1e1281e86a40b5fd09d3a0d2a6073",
+    "ops-reconstruct-handoff.json": "05c90b52ffa54a1f0b9938be05cd046e9c6085f529e12a0399c3e16d29bc86ae",
+    "ops-reconstruct-e2e-proof.json": "c92adc5f8df66d09fe367074bf4fa5147ff2d315b2ce5d40a95049c8347c2ee3",
+    "parent-cache-tree-defect-repro.json": "0d9af0ce4bbec572bdbfb893313aced06909ffc29b4aa255719f8519e43daedb",
+    "parent-reconstruction-proof.json": "a122bd7955e725ccfcbd29b8e4bc3db796e994306ea3330a5c6f908dde6f5de6",
+    "cody-reconstruct-review.md": "f7d76f9309374e708528b443fc2fe70497a2d2cfa81cd66abcbb6b9488564a4c",
+    "cody-reconstruct-handoff.json": "9139ba2ddfaa4e542a3013df662849499b215bd2ea760ded51391bf8f95b5983"
+  },
+  "evidence_directory": "/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5",
+  "cody": "SHIP IT5/5 after cacheTREE14treecorrection",
+  "parent": "Independently verified4059fullrestore+nativeGitconn+exact5paths+protectedmetadata+packinventory+size"
+}
diff --git a/verification/cycle-20260908T1322Z-m4gNE5-recovery-memo.md b/verification/cycle-20260908T1322Z-m4gNE5-recovery-memo.md
new file mode 100644
index 00000000..d0f545c2
--- /dev/null
+++ b/verification/cycle-20260908T1322Z-m4gNE5-recovery-memo.md
@@ -0,0 +1,115 @@
+# TK-10928 — projects-root snapshot growth: approval draft
+
+Status: BLOCKED pending explicit Steve approval; prepared proposal only, zero operational fixes.
+Recommendation: HOLD live application pending a fresh current-state maintenance preflight and explicit Steve approval. The 13:53Z historical-copy rehearsal now passes at 1457MB; its complete preservation, scope, and remaining gates are recorded in the final addendum below. Earlier timeout entries are retained as history, not the current preparation result.
+
+## Verified failure and scope
+
+On 2026-09-08 at 11:28Z, /Users/macstudio3/Projects/.git measured 3,101,072 KiB, rounded by the actual canary to 3028 MB, above its existing 3000 MB ceiling. Pure classifyRepo confirms FAIL, with 3000 MB WARN and 3001 MB FAIL. No remote is configured. The 11:00:26Z artifact has 697 repos, FAIL 1/WARN 10, and only projects-root failing; pgdump logs report all 38 databases fresh. launchctl shows runs=24 and last exit=1, consistent with run.sh intentionally failing on this artifact; a new correlated scheduler run is still required to prove recovery.
+
+The Sept-4 Designer-Wallcoverings/dead-agentabrams approvals and repairs do not authorize this new projects-root action. Active pending top-level files contain no duplicate root proposal; the only TK-10928 search hit was historical _decisions.jsonl.
+
+## Exact recovery identities
+
+- Root: /Users/macstudio3/Projects/.git
+- refs/heads/master: c539b5a08a4f84a551d8825a0fc73fa588bcbce8
+- refs/auto-snapshot/latest: 023b0072c04562ab9f19bcf5c9fb3c6c5d242316 (2026-09-08 04:01:30 PDT; parentless).
+- Current master tree: 54 files, 405,240 logical bytes; recovery snapshot: 3031 files, 3,161,747,626 logical bytes. Logical bytes are not disk savings.
+- Recurrence source: /Users/macstudio3/scripts/auto-commit-fleet.sh SHA256 6fc0e884b527d91c1f656c772860b589646629eb9093e6e060a0cb8cf77b0a78, invoked every 1800 seconds by com.steve.auto-commit-fleet. Job2 lines58–73 stages the full tree to an orphan recovery ref; Job1 BLOAT_GLOBS exclusions do not apply there. Unreferenced snapshots do not automatically free their stored objects.
+
+## Bounded candidate set
+
+Propose excluding only these five generated renders from the ROOT recovery snapshot after their independent archived recovery has been proven. Do not apply a fleet-wide binary exclusion, modify nested repositories, or rewrite master. Preserve all original worktree files.
+
+| Relative path | Blob SHA | Stored object bytes |
+|---|---|---:|
+| rentv-slideshow/renders/rentv-slideshow_2026-07-29_07-02-25.mp4 | efbba50e8faff2c98d5cd58654619ffcc85b7e92 | 53759218 |
+| rentv-slideshow/renders/rentv-slideshow_2026-07-29_08-29-23.mp4 | b43f1788d2681871ff6ac918be7b5857a2e5cdd5 | 52302303 |
+| rentv-slideshow/renders/rentv-slideshow_2026-07-29_08-22-08.mp4 | 4744083a7a07447f55cde597cdbb14b530e82371 | 52235507 |
+| rentv-slideshow/renders/rentv-slideshow_2026-07-29_08-16-11.mp4 | f868b83b92bc085314fd49cd1f791c7ea3b114bf | 28569567 |
+| rentv-slideshow/renders/rentv-slideshow_2026-07-29_07-49-53.mp4 | dbf6ed038c5be1d119c9f8956cbe83dd72bb1e42 | 26285652 |
+
+Total stored object representations: 213,152,247 bytes (203.28 MiB); this is a candidate estimate, not a promise of filesystem reclamation. The 723,501,447-byte dw-dup-sweep/data/active-products.jsonl blob ada9d21265593b91fb6331d40f4aae8acd1053dc occupies only 46,666,955 stored bytes; it remains preserved and is not in the proposed exclusion set. Settlement audit records remain preserved.
+
+## Requested authorization and sequence
+
+1. Re-read canonical ownership, refs, source hashes, writer state and free space. Stop if identities drift until the exact revised inventory is reviewed. Coordinate a maintenance window with the fleet owner; check pm2 list before any process action, and obtain awareness before pausing the shared writer. Do not silently alter schedules.
+2. Retain a byte-complete copy of the entire root .git including loose/unreachable objects, refs, index and reflogs on a verified mounted backup volume outside ~/Projects, using a freshly allocated mktemp directory; a mirror alone is insufficient for unreachable undo. Separately preserve the five source files and the snapshot script. Record original file hashes, status, permissions and all refs; leave all copies retained without automatic expiry. Reject if backup space or write integrity cannot be verified.
+3. Rehearse exclusively in a fresh isolated copy with no hardlinks/shared Git object store. Confirm both recorded refs and each candidate blob restore exactly. In the rehearsal only, evaluate a root-only Job2 path exclusion for the five exact files, keep master and all other snapshot paths intact, and measure storage after bounded unreachable-object maintenance. Do not infer safe removal from Git garbage warnings or logical sizes. Retain old full recovery state outside the measured repository.
+4. A live change is conditional on the rehearsal: candidate render hashes restored correctly, every non-candidate snapshot path retained, master ref unchanged, original worktree/index unchanged, and measured .git below 2850 MB with the existing policy. The approved scope is the five exact root snapshot exclusions and only unreachable-object maintenance needed to attain that result, with a documented restore path; history rewriting, broader exclusions, threshold raises and old mirror removal are outside scope. If the exact bounded set does not work, return REVISE rather than expanding it.
+5. After authorized application, exercise two 30-minute snapshot intervals to show excluded binaries are not recaptured and preserved source/data are captured. Check actual du and pure classifyRepo each time. Allow the next normal canary window to produce fresh repo and pgdump artifacts, run log and an incremented launchctl run counter with exit0; no manual alerting run is authorized by this draft. Correlate captured start/end timestamps and artifact mtime. Expected repo fail count0, pgdump PASS, root below2850MB. WARN elsewhere may remain.
+
+## Rollback and acceptance
+
+Rollback requires the retained full root Git copy, five original render files and original snapshot script, with recorded SHA256 manifests and a successful rehearsal restoration; preserve all other owners’ concurrent work and coordinate exclusive writer access before replacing state. Keep undo indefinitely unless Steve separately approves retirement. No destructive command sequence is included in this draft.
+
+No run.sh, scan.mjs, pgdump-freshness.sh, history edit, repack/GC, threshold write, schedule mutation, outbound message or push was executed for this diagnosis. It is not a fix. Source ticket remains blocked on explicit approval and successful real scheduler proof.
+
+Evidence: /Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1121Z.O0jFR4
+A2A task: cycle-20260908T1121Z.O0jFR4/backup-root; source DM M-02264 correlation dm-mtsl2sdz-55763-4mm05s; ACK action-mtsl50m2-6871-f1lvsn.
+
+
+## 2026-09-08 12:35Z isolated capture and rehearsal result — BLOCKED
+
+This measured update supersedes the older capture identities above; it does not authorize live maintenance. The complete recovery rehearsal remains incomplete because its critical full-object integrity scan timed out after 240 seconds. No restore, snapshot rewrite, packing, physical reclamation, or threshold test was executed after that failed precondition; the five-path plan has NOT been shown to bring the repository below 2850 MB.
+
+The exact stable capture ran 12:29:32–12:29:58Z. Source and complete original-copy manifests match across all 4059 Git files (3,167,634,917 logical file bytes; source 3,106,332 KiB, rounded 3034 MB). This includes the loose and packed object store, unreachable storage, all refs, index, pseudorefs and reflogs. No alternate object store or hardlinks were used; initial scratch availability was 78,984,818,688 bytes. All original-copy entries now carry the immutable UF_IMMUTABLE flag, with no expiry or cleanup.
+
+- Retained complete original: `/private/tmp/ops-TK10928-rehearsal.ndMwgA/original.git`.
+- Exact Git manifest: `ops-source-git-manifest.json`, SHA256 `02f539816fccb989c307bece019b48c3dadf8bbead1c4f977395af3053c78746`.
+- Capture snapshot: `ad275a5b3bbabd66d23893af977e96553f969eaa`; master remains `c539b5a08a4f84a551d8825a0fc73fa588bcbce8`.
+- Index SHA256: `70711329911428bc7dc20cfbf7e118b05762f38eee3fc7d51b18d35c12fe76ff`.
+- Source script SHA256 remains `6fc0e884b527d91c1f656c772860b589646629eb9093e6e060a0cb8cf77b0a78`; archived at the scratch root as `auto-commit-fleet.original.sh`.
+- Final source refs/index/script and writer-lock state match capture; no source Git operation mutated these surfaces.
+
+Exact failed command, run only against the copy:
+
+```sh
+git --git-dir=/private/tmp/ops-TK10928-rehearsal.ndMwgA/original.git fsck --full --no-reflogs --unreachable
+```
+
+The executor's 240-second deadline expired; stdout remained empty and there is no completed integrity verdict. The completed object inventory contains 14,061 objects, expanding to 60,348,642,328 logical bytes from 3,166,847,109 stored object bytes, explaining the measured CPU-bound verification cost. The timeout is not evidence of corruption. The retained original copy has independently passed the parent’s byte/size/inode comparison, which does not substitute for the missing object-integrity and restore proof.
+
+`ops-root-only-snapshot-exclusions.patch` is the exact, unapplied proposed Job2 change. Its argument array always starts with `.`; only the root appends the five literal path exclusions. The first draft’s empty-array expansion failed under installed `/bin/bash` with `set -uo pipefail` (rc127); that draft and incomplete fixture are retained as defect evidence. The corrected actual snippet passes those exact shell settings for root (three preserved fixture files) and nested repository (all eight fixture files), including a noncandidate render and unchanged real index. This validates the staging boundary only, not the unperformed storage/recovery stages.
+
+### Exact operation/effect inventory and continuation boundary
+
+1. Executed: read-only source identity and free-space checks, complete fresh Git copy via independent file copies, whole-file SHA256 capture comparison, full object metadata enumeration, and the bounded failed integrity scan. Retained immutable original, memo preimage, script archive, runtime-matched staging fixtures and command log.
+2. NOT EXECUTED: separately archive/restore the five source renders, recreate the entire Git directory in a fresh restore destination, and compare both refs, index, all files and unreachable-object integrity. These remain critical-path SKIP after the timeout.
+3. NOT EXECUTED: construct a new parentless snapshot from the captured tree with exactly five paths absent, preserving every other path/mode/OID and master. The reviewed runner uses a throwaway index; the real index remains byte-identical.
+4. NOT EXECUTED: compute complete keep-closure from all surviving refs, original index objects, pseudorefs and non-snapshot reflogs; create an independent pack with `pack-objects --window=0 --threads=2`; move only the isolated trial’s prior objects and recovery-snapshot reflog to retained siblings outside its measured Git directory. No deletion command is required. Every object omitted from the measured trial must appear in an exact retained-outside inventory and remain restorable from the immutable original.
+5. NOT EXECUTED: full trial integrity, original restore comparison, all five blob hash comparisons, exact noncandidate tree comparison, physical `du` and existing pure classifier measurement. Therefore no storage savings or below-2850-MB result is claimed.
+
+Next preparation step: in a separately bounded verification window sized for the measured 60.35-GB expansion, complete integrity validation of this retained capture, then perform the still-skipped restoration and exact bounded storage stages; allocate every destination freshly and retain all copies. A future live application needs a new stable current capture and explicit Steve approval of the completed package, followed by the already-required actual scheduled recovery proof. This window did not change any source service, threshold, schedule, root ref/history/index, production record or remote.
+
+Evidence directory: `/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1221Z.HzXvuQ`.
+Task: `cycle-20260908T1221Z.HzXvuQ/ops-rehearsal`; DM `M-02273`, correlation `dm-mtsn6anh-42285-84e12c`; parent `/root` finalizer. Local evidence commit and independent acceptance belong to the parent.
+
+
+## 2026-09-08 13:53Z completed historical feasibility — live action still HOLD
+
+This supersedes the unfinished preparation result above. It grants no authority to apply a historical candidate to the current source.
+
+- All14,061 captured Git objects (60,348,642,328logical bytes) passed streamed type/size/Git-hash checks and246,888 internal-link assertions. Exact4059file capture hashes and immutable flags passed independently. Full `git fsck` semantics and external Gitlink repositories are not claimed.
+- Allfive exact renders were restored and independently matched current local bytes, SHA256 and Git identity:228,581,318bytes total. The separate complete `restored.git` matches all4059captured file bytes/modes, refs, index and recovery metadata. Native restored-Git connectivity passes.
+- Initial reconstruction exposed a genuine preserved-index cache-tree omission (`e9a40128f55647db8713e021ee1c20471e32b466`). Parent and Cody independently reproduced Git exit8. The correction retains101cache-tree roots and their closure, adding14trees; the index bytes were never rewritten. The failed candidate and diagnostics remain retained.
+- Corrected self-contained candidate: `/private/tmp/ops-TK10928-reconstruct.xcLYt5/trial-cachetree-complete.git`. Pack verification and native Git connectivity pass; actualdu1,492,032KiB rounds to1457MB under the unchanged3000MBcanaryfloor, below the2850MBtarget. Combined reconstruction data work420.14seconds, including the failure and correction, stayed below600seconds.
+- Candidate snapshot `e5906c0c9d428bce148fe1d1ea00fde5f1113be4` contains3722leafentries versus3727before. Exactly the five named renders are absent; allother leaf modes/OIDs are unchanged. Master, every non-snapshot effective ref, protected packed-ref lines, index, reflogs and pseudorefs remain preserved. The candidate contains2409objects including4new objects and641protected objects.
+
+### Exact archival effect needing explicit review
+
+This is **not merely removal of five stored blobs**. The measured candidate excludes11,656original objects from its active store:2483commits,6821trees and2352blobs. They all remain in the complete original/restore stores. The latest parentless snapshot is replaced, the loose and hidden packed snapshot ref are updated, and the old snapshot reflog is retained outside the candidate. Non-snapshot history is preserved. Exact archive inventory: `/private/tmp/ops-TK10928-reconstruct.xcLYt5/archived-v2-original-object-inventory.txt`.
+
+Undo original: `/private/tmp/ops-TK10928-rehearsal.ndMwgA/original.git`. Full restored copy: `/private/tmp/ops-TK10928-reconstruct.xcLYt5/restored.git`. Original failed trial and superseded object stores also remain. This scratch allocates9,197,040KiB; scratch plus immutable original totals12,303,372KiB bydu. **No disk-space reclamation is claimed.** Retiring any retained copy requires separate approval.
+
+### Approval boundary and next action
+
+HOLD / conservative default: preserve every live source file, ref, index, script, schedule and threshold. No new ambiguity requires an unattended question. This is a tested historical proposal for review, not a ready-to-swap current Git directory.
+
+The live root has advanced beyond captured snapshot `ad275a5b3bbabd66d23893af977e96553f969eaa` and measured3049MB at13:54Z. Before any source mutation, obtain an explicitly approved maintenance window with its owner, make a fresh stable current capture, re-enumerate the entire current protected and archived object inventories, rerun the lossless candidate/undo checks against that capture, and have Steve approve the exact resulting archive and root-only script change. Do not substitute this historical1457MBcandidate for newer source state, broaden the five exclusions, drop protected cache-tree objects, raise thresholds, delete undo, or treat previous September4approval as applicable.
+
+After any separately approved live execution, the original required two scheduled snapshot intervals and scheduler-correlated canary recovery remain mandatory. No schedule/pause/restart, source maintenance, script install, alert send, or external write happened during this cycle.
+
+Evidence: `/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1322Z.m4gNE5/ops-reconstruct-handoff.json`, `ops-reconstruct-e2e-proof.json`, `parent-reconstruction-proof.json`, and the final Cody reconstruction review. Initial bounded verifier and core evidence are locally committed as `1809c161af09756b1db81875f42c5888812a1cd0`. Parent is the final acceptance owner.
+
+Additional reproduced scope limit: the captured `info/refs` cache was already stale. It is preserved byte-identically in the candidate and advertises snapshot `97bcd6217a80edeaa87a9c83097060610ae34b78`, absent from the candidate active store. Local effective refs and Git connectivity pass. No dumb-HTTP/export readiness is claimed; a future explicitly scoped maintenance/export action must address or refuse this stale advertisement. Parent evidence: `parent-stale-advertisement-repro.json`.

← 1809c161 Verify retained backup objects and restore five scoped rende  ·  back to Ticket System  ·  Record verified backup rehearsal and ordered overnight cycle e79c66ac →