from pathlib import Path
import json,hashlib
from datetime import datetime
from zoneinfo import ZoneInfo
P=Path('/opt/creature-lib-20261017');R=P/'realistic';entries=json.loads((P/'catalog_entries_realistic.json').read_text());manifest=json.loads((R/'manifest.json').read_text());node=json.loads((R/'node-validation.json').read_text());contracts=json.loads((R/'contracts.json').read_text());audit=json.loads((R/'independent-glb-audit.json').read_text());now=datetime.now(ZoneInfo('Europe/Paris')).strftime('%d %b %Y %H:%M CET')
notes={
'stag':{'verdict':'OK','status':'DELIVERED','notes':'Recognisable red-deer shape, branched rigid antlers and coherent gait. Small eyes sit in the textured sockets. Soft face, faceted antler tips and coarse cheek/neck fur remain visible close up. Not a close-up hero animal.'},
'deer':{'verdict':'OK','status':'DELIVERED','notes':'Antlerless hind, pale rump and separate slender legs. Eyes are part of the head surface, not floating objects. Soft muzzle/eye detail, a tiny shoulder texture speck and rounded knee bends in the gallop remain. Hit reaction is exaggerated, inherited from the source rig.'},
'fox':{'verdict':'BAD','status':'HELD BACK','notes':'Head, ears and coat read well, but side gallop reveals a stretched fan between the bushy tail and hindquarters. A second anatomical weight isolation reduced but did not remove it. No final GLB or catalog entry.'},
'dog':{'verdict':'OK','status':'DELIVERED','notes':'Large dark brindled hound with floppy ears, not a husky. Clean visible walk/run silhouette and a narrow hanging tail. Face and mottled coat are soft close up; some shoulder creasing remains. Attack is a closed-mouth lunge, not an open-jaw bite.'},
'horse':{'verdict':'BAD','status':'HELD BACK','notes':'Side gallop shows severe tail-to-hind-leg stretching and spikes. Generation also copied the white image background onto forelock/mane. Two texture repair attempts did not produce an acceptable forelock; the second stained nearby face polygons. No final GLB or catalog entry.'},
'boar':{'verdict':'NOT ATTEMPTED','status':'SKIPPED','notes':'The inspected Quaternius animal pack contains Alpaca, Bull, Cow, Deer, Donkey, Fox, Horse, Horse_White, Husky, ShibaInu, Stag and Wolf, but no pig/boar rig. Did not substitute a bull rig or use the unrelated static boar source.'}}
(R/'review.json').write_text(json.dumps(notes,indent=2))
rows=[]
for e,m in zip(entries,manifest):
 k=e['id'].replace('enemy-','').replace('-realistic','');rt=json.loads((R/k/'runtime-final.json').read_text());motion=json.loads((R/k/'motion-probe-v6.json').read_text());gaitmin=min(x['min_y'] for x in motion if x['clip'] in [e['clips']['walk'],e['clips']['run']]);rows.append(f"| {e['id']} | {m['bytes']/1e6:.3f} | {m['triangles']:,} | {e['height_m']:.3f} | {e['speeds']['walk']:.3f} / {e['speeds']['run']:.3f} | {abs(min(0,gaitmin))*100:.2f} cm | **OK** |")
report=f'''\n\n# W-REALISTIC: realistic animals, {now}

**Blocked on you:** nothing for this asset handoff. No live integration was requested or performed.

**Changed:** added three usable realistic animals with all six source clip roles. Two generated animals failed visual review and are not delivered as final models.

[Delivered animals](realistic-contact-sheet.png)

## Delivered files

- `glb/enemy-stag-realistic.glb`
- `glb/enemy-deer-realistic.glb`
- `glb/enemy-dog-realistic.glb`
- `catalog_entries_realistic.json`: three entries, identical key shape to the existing catalog, style `realistic`, animal-word aliases only.
- `renders/enemy-<animal>-realistic-hero.png`, `-clips.png`, `-head.png`, `-comparison.png` for each delivered animal.
- Extra evidence: `-side.png`, `-rear.png`, `-walk-side.png`, `-run-side.png`, plus `-walk.mp4` and `-run.mp4` on a marked moving ground.
- `realistic/`: generated references, source meshes, provenance, selected candidates, tests, rejected-candidate evidence and reproduction scripts.

No prior GLB was overwritten. All 29 original hashes still match. The old catalog was not edited.

| Final model | MB | Triangles | Height m | Walk / run m/s | Worst sampled gait ground crossing | Verdict |
|---|---:|---:|---:|---|---:|---|
'''+ '\n'.join(rows)+f'''

Speeds are measured from each final GLB's foot-contact marker travel divided by its stance time at playback speed 1. They are not estimated from animal height. The corrected source stance samples are retained in `realistic/<animal>/footplant-method.json`, with final measurements in `runtime-final.json`. Scale playback by actual game speed divided by catalog speed. Uniform model scaling must scale the speed too.

## Honest visual verdicts, in requested order
'''
for k,v in notes.items():
 report+=f"\n### {k.title()}: {v['verdict']}, {v['status']}\n\n{v['notes']}\n"
 if v['status']=='DELIVERED':report+=f"\n[Head close-up](renders/enemy-{k}-realistic-head.png) | [All six clips](renders/enemy-{k}-realistic-clips.png) | [Same-camera stylised comparison](renders/enemy-{k}-realistic-comparison.png) | [Side run](renders/enemy-{k}-realistic-run-side.png)\n"
 if k=='fox':report+='\n[Rejected side run](realistic/fox/v8-run-side.png)\n'
 if k=='horse':report+='\n[Rejected side run](realistic/horse/v8-run-side.png) | [Rejected face repair](realistic/horse/v8-head.png)\n'
report+=f'''
## What actually passed

| Check | Evidence |
|---|---|
| Final-path browser Three.js loading/rendering | 3 final GLBs, all 18 clips, 21 sampled poses per clip, zero page/console/validation errors; `realistic/<animal>/runtime-final.json` |
| Independent Node GLTFLoader + AnimationMixer | {node['models']} models, {node['clips']} clips, 61 poses per clip, every skinned vertex checked for finite coordinates, zero failures; `realistic/node-validation.json` |
| Embedded texture decode | Node loaded actual packed images through the canvas image decoder, not a mock |
| Original clip roles and durations | Independent consumer compared all six names and durations with each Quaternius donor; all preserved |
| Geometry audit | Three strict audits, zero findings, zero zero-area triangles and no nonfinite attributes; `realistic/independent-glb-audit.json` |
| Skin weights | At most four per vertex; worst sum error below 0.00000005; `realistic/contracts.json` |
| Rigid stag antlers | 1,852 upper-antler vertices are fully Head-weighted; sampled pair-distance change across all clips is at most 0.00000152 m |
| Frame | Metres, Y-up, head faces +Z; idle minimum height is within 0.000001 m of the ground |
| Prior files preserved | All 29 old GLB hashes match; `realistic/original-glb-verification.txt` |
| Visual acceptance | Author opened hero, head, rear, all-six-clip strips and side walk/run strips. Fox and horse were rejected despite passing load tests |

The largest remaining sampled non-gait ground crossing among delivered models is the hound's attack paw, about 1.65 cm. These are flat-ground animation assets, not a claim of perfect every-vertex foot locking. Death poses were adjusted so the new head/antlers rest above the floor instead of being buried.

## Method and failures worth keeping

1. Generated five photographic-style side-view reference images of real animal types in neutral standing poses. These are generated reference pictures, not camera photographs or third-party wildlife photos.
2. Submitted one image-to-3D job at a time through the existing loopback service. All five primary PyTorch attempts hit GPU memory errors. The service's existing C++ fallback completed all five automatically, without a service restart or configuration change. Original requests, job IDs and errors remain in each `job-final.json`.
3. Aligned each mesh by its body axis, fitted the four paws to the matching Quaternius skeleton, transferred skin weights and kept all six named actions. The hound uses the pack's proven canine skeleton, not the donor's husky appearance.
4. Fixed a transfer bug caught during review: Blender's hidden bone-display Icosphere must never be used as donor anatomy. Only real skinned meshes and bone-parented antlers are used.
5. Removed incorrect generated metallic maps from fur. Fur is non-metallic and matte. This fixed the almost-black neck and belly on the deer.
6. Solved foot contacts and baked limited head/tail floor-clearance corrections into the existing actions. Source clip identities and lengths are preserved, but corrected transforms are not byte-identical to the donor animation.
7. Side-motion inspection exposed tail distortion missed by attractive three-quarter stills. Two unsuccessful repair attempts are retained for the rejected models; they are not promoted under a weaker label.

## Limits and rollback

- **Asset handoff only.** No Factory, Engine, live game, public app URL or service configuration was edited, tested, restarted or deployed.
- No independent second-model critic was used: this bounded worker was not allowed to start nested agents. Verdicts are from the author's direct image inspection.
- No GPU frame-rate benchmark, game combat collision, attack timing, slope handling, facial animation, open-jaw bite or close-up cinematic quality claim.
- Preview target was the disposable task folder on loopback `127.0.0.1:8771`, using software WebGL. It was not a live app.
- No final fox or horse GLB exists in `glb/`; boar was not generated. Rejected candidates under `realistic/` are diagnostics, not catalog assets.
- If a delivered animal fails in a consuming game, omit its new catalog entry and use the unchanged stylised version. No running game depends on these new files yet.
- Local disk was below the 15 GB free threshold before work began. Only task-owned review copies and obsolete scratch are cleaned; no shared caches are touched.

## Provenance

Generated reference and generated mesh: Forge, dedicated to this library under CC0-1.0. Source skeletons and animation: Quaternius, Ultimate Animated Animals, CC0-1.0, https://quaternius.com/packs/ultimateanimatedanimals.html . Exact donor source URLs and hashes remain in the previous library's `evidence/enemy-<animal>-normalization.json`. The new images had no third-party input picture. Full generated prompts and job records remain under `realistic/<animal>/`.
'''
(R/'REPORT.md').write_text(report.lstrip().replace('](realistic-contact-sheet.png)','](../realistic-contact-sheet.png)').replace('](renders/','](../renders/').replace('](realistic/','](../realistic/'));existing=(P/'REPORT.md').read_text();assert '# W-REALISTIC:' not in existing
with (P/'REPORT.md').open('a') as f:f.write(report)
summary={'checked_at_CET':now,'delivered':manifest,'reviews':notes,'node':{'models':node['models'],'clips':node['clips'],'failures':node['failures']},'contracts':contracts,'audit_exit_codes':[x['exit_code'] for x in audit],'original_glbs_unchanged':29}
(R/'summary.json').write_text(json.dumps(summary,indent=2));print('REPORT APPENDED',now)
