"""Assemble verified catalog entries, explicit held-back flyers, report and file manifest."""
from pathlib import Path
from datetime import datetime
from zoneinfo import ZoneInfo
import json,hashlib,sys
P=Path('/opt/creature-lib-20261017');E=P/'evidence'
review=json.loads((P/'scripts/review_notes.json').read_text());audits=json.loads((E/'independent-glb-audit.json').read_text());audit={r['file']:r for r in audits};node=json.loads((E/'node-validation.json').read_text())
keys={'id','role','about','good_for','names','file','height_m','clips','tint','licence','credit','speeds','style','mood','small'}
entries=[];extras=[];rows=[];groundmin=[];rms=[]
for p in sorted(E.glob('*-entry.json')):
 d=json.loads(p.read_text());r=json.loads((E/(d['id']+'-runtime.json')).read_text());a=audit[d['file']];kind=d['id'].removeprefix('enemy-')
 assert not a['findings'] and not r['errors'] and not r['browser_errors']
 assert a['bytes']<=1500000 and 3000<=a['triangles']<=20000 and len(a['skins'])==1 and r['maxWeights']<=4
 assert set(d)==keys and d['licence']=='CC0-1.0' and d['style'] in ['toon','realistic']
 assert all(name in [c['name'] for c in r['clips']] for name in d['clips'].values())
 d['height_m']=round(r['height_m'],4);d['small']=r['height_m']<.5
 d['speeds']={k:r['speeds'][k]['speed'] for k in ['walk','run']}
 ready=all(isinstance(v,(int,float)) and v>0 for v in d['speeds'].values()) and all(k in d['clips'] for k in ['idle','walk','run','attack','die'])
 if ready:
  entries.append(d)
  for c in r['clips']:
   if c['name'] in [d['clips']['walk'],d['clips']['run']]:groundmin.append((d['id'],c['name'],c['bounds'][0][1]))
  for s in r['speeds'].values():rms.extend(f.get('forward_fit_rms_m',0) for f in s['feet'])
 else:extras.append({'candidate':d,'status':'HELD BACK','reason':'No walk clip and no ground stride; flight speed is not measurable from this in-place source motion. Null speeds are deliberate. Do not feed this wrapper to the game catalog.'})
 verdict=review['overrides'].get(kind,review['default_verdict']);source='Forge generated mesh + Quaternius wolf rig' if kind=='wolf-realistic' else 'Quaternius Animals' if 'Ultimate Animated Animals' in d['credit'] else 'Quaternius Monsters'
 speeds=' / '.join(f'{d["speeds"][k]:.3f}' if d['speeds'][k] is not None else 'not measured' for k in ['walk','run'])
 clips=', '.join(d['clips']);rows.append(f'| {d["id"]} | {source} | CC0-1.0 | {d["style"]} | {clips} | {d["height_m"]:.3f} | {speeds} | **{verdict}** |')
 p.write_text(json.dumps(d,indent=2))
assert len(entries)==24 and len(extras)==5 and node['models']==29 and node['clips']==169 and node['failures']==0
(P/'catalog_entries.json').write_text(json.dumps(entries,indent=2)+'\n');(P/'held_back_flyers.json').write_text(json.dumps(extras,indent=2)+'\n')
checks={'catalog_entries':len(entries),'held_back_flyers':len(extras),'final_glbs':len(audits),'node_clips_played':node['clips'],'node_errors':node['failures'],'browser_errors':0,'independent_mesh_audit_findings':0,'max_file_bytes':max(a['bytes'] for a in audits),'triangle_range':[min(a['triangles'] for a in audits),max(a['triangles'] for a in audits)],'max_full_contact_marker_forward_fit_rms_m':max(rms),'worst_sampled_walk_run_mesh_min_y':min(groundmin,key=lambda x:x[2]),'per_model_gait_min_y':groundmin,'note':'Contact marker fit is not a promise of perfectly stationary every toe vertex; mesh minima sample 21 poses per clip.'}
(E/'delivery-checks.json').write_text(json.dumps(checks,indent=2))
now=datetime.now(ZoneInfo('Europe/Paris')).strftime('%d %b %Y %H:%M CET')
text=f'''# W-CREATURES: rigged creature library

**Blocked on you:** nothing for this asset delivery. Connecting it to games is a separate, unperformed task.

**What changed:** prepared 24 catalog-ready creatures, including a realistic wolf, plus five separately held-back flyers, without changing any live game or service.

**Status at {now}:** files and runtime-loading checks complete; this is an asset handoff, not a deployed game update.

## Delivered

[Whole library](contact_sheet.png) | [Matched wolf comparison](wolf-comparison.png) | [Wolf head close-ups](wolf-head.png)

- Canonical folder: `gex44:/opt/creature-lib-20261017/`.
- `catalog_entries.json`: 24 exact-shape entries, 23 toon and one realistic; every entry has idle, walk, run, attack, hit and die.
- `glb/`: 29 final GLBs. Main catalog entries name 24 of them. Five flyers are excluded from the main catalog rather than given invented speeds.
- `held_back_flyers.json`: explicit incomplete candidates, not a catalog to import unchanged.
- `renders/`: one three-quarter hero and five-row clip strip per final model, all made by reloading the final GLB in Three.js.
- `contact_sheet.png`: all 29 models, including the five marked held-back in this report.
- Wolf proof: `renders/wolf-comparison-side-by-side.png`, `renders/wolf-head-side-by-side.png`, `renders/enemy-wolf-realistic-walk.png`, and moving-ground walk videos for both wolves.
- Scripts: committed under `tools/creatures/` in the worker worktree. The README gives the reproduction order.

## Evidence and limits

| Check | Actual result |
|---|---|
| Independent Node GLTFLoader + AnimationMixer | 29 files, 169 clips, 61 poses per clip, zero errors; embedded images decoded too |
| Browser Three.js load and rendering | 29 files, all clips, zero errors; software WebGL, not a GPU performance test |
| Independent GLB geometry audit | 29 files, zero findings, no external image links, one skin each, no duplicate primitives |
| File budget | Largest {checks['max_file_bytes']/1e6:.3f} MB, below 1.5 MB |
| Triangle budget | {checks['triangle_range'][0]:,} to {checks['triangle_range'][1]:,} triangles |
| Skin weights | At most four per vertex, normalized |
| Frame and scale | glTF Y-up, metres, forward +Z; idle ground bounds checked; final height measured |
| Gait speeds | Measured from final foot-contact travel and stance time, not guessed from height |
| Contact correction | Authored clips retained; stance positions corrected, leg joints solved and baked |
| Contact marker line-fit error | At most {max(rms):.6f} m across measured full-contact markers |
| Remaining sampled toe/skin ground crossing | Worst {abs(min(groundmin,key=lambda x:x[2])[2])*100:.2f} cm; not a claim of mathematically perfect every-vertex foot lock |
| Visual review | Every final hero and all eight final clip sheets opened and judged; both wolf heads and side walk also inspected full size |

Use each speed at animation playback speed 1. If the game moves faster, scale animation playback by `actual_speed / catalog_speed`. Uniform model scaling must scale movement speeds too. Flat-ground contact correction does not replace slope-aware foot placement in a game.

Some Quaternius animal meshes started below 3,000 triangles. SIMPLE subdivision brought them inside the requested range without changing their silhouette. This adds triangles, not new sculpted detail. Unused clubs, carrot and hammer geometry were removed. The stag's rigid antlers were preserved and verified in motion.

## Creature table

Speeds are walk / run in metres per second. GOOD means it looked coherent in the inspected renders for its stated style, not that it is suitable for every art direction. OK means a stated quality or completeness limit remains.

| ID | Source / author | Licence | Style | Clip roles present | Height m | Speeds m/s | Visual verdict |
|---|---|---|---|---|---:|---|---|
'''+ '\n'.join(rows)+'''

The five flying rows have idle/flying, fast-flying, attack, hit and die. Their `run` mapping points to `Fast_Flying`, not a fabricated running clip. **Walk is missing and movement speed is unmeasured, so they are not among the 24 main catalog entries.**

## Realistic wolf verdict: OK, usable at game distance

- It reads as a grey wolf rather than a procedural monster or cartoon dog.
- Eyes are part of the textured head and sit in the sockets. No separate floating eye spheres were added.
- The generated mesh was aligned by its body axis, then fitted to the proven wolf skeleton using measured paw positions. The initial unfitted bind was rejected.
- Cleaned four-weight skinning, isolated tail influence from hind-leg influence, and corrected ground contacts. Final walk and gallop were inspected from the side.
- 15,912 triangles, one armature, below 1.5 MB. Six real source clips are preserved.
- **Remaining limits:** soft detail around the far eye, a small forehead blemish, and a coarse tail tip at close range. The jaw is not independently articulated. Attack is a closed-mouth head-first lunge, not an open-jaw bite. This is not a polished close-up hero wolf.
- The stylised wolf remains the cleaner choice for low-poly games. Matched-camera comparisons show the two at the same 0.95 m height.

## Per-model visual notes

'''+ '\n'.join(f'- **{k}:** {v}' for k,v in review['notes'].items())+'''

## Licences and exact sources

- Quaternius, Ultimate Animated Animals: https://quaternius.com/packs/ultimateanimatedanimals.html . The author page and pack licence declare CC0.
- Quaternius, Ultimate Monsters: https://quaternius.com/packs/ultimatemonsters.html . The author page declares CC0; the archived pack licence and download records were retained.
- **Every imported model's exact download URL, author, licence and input hash are in `evidence/sources.json`; each catalog entry also carries its exact source URL in `credit`.** Animal files came from a pinned-commit public mirror after the author's Drive files returned quota errors. They remain Quaternius assets, not original work by the mirror owner.
- The realistic mesh and reference were generated for Forge for this library, with the generated asset offered under CC0 here. The underlying skeleton and motion clips remain Quaternius CC0. Model weights/software are not bundled. `sources/generated-wolf/provenance.json`, the reference request and `evidence/wolf-image-job-final.json` identify the generation route and exact resulting mesh URL.
- No non-commercial, editorial, unclear or share-alike-only animal model was included. No Mixamo file was redistributed.

## Failures, rejected work and omissions

- Google Drive returned quota HTML instead of animal files. Recovered through a public, licence-preserving mirror with pinned URLs and hashes.
- Both text-to-3D wolf jobs failed with GPU out-of-memory errors. Stopped that route. A separate image-upload job succeeded through the service's existing lower-memory C++ fallback. No service configuration was changed or process restarted.
- The downloaded NewDLC CC0 wolf was rejected for the realistic lane: it looked like a cartoon dog. It is not a delivered catalog entry.
- The first generated-mesh bind had poor paw alignment and tail/leg cross-weights. It was replaced, not called good.
- A browser executable path and a missing Node image decoder caused initial test setup failures. Both were fixed inside the disposable work folder; final consumer checks passed.
- The initial contact-sheet summary hit stale experiment filenames after renaming assets. It was fixed to enumerate only final GLBs.
- The initial stag import dropped rigid antlers. The final import retains and animates them.
- Initial source gaits had foot sinking. Baked contact correction reduced it substantially; the remaining sampled maximum is reported above rather than hidden.
- No claim that every requested priority animal was found. Bear, boar, big cat, crocodile/lizard, snake, crab, raven/eagle, troll/ogre, ghoul and a true stone golem are **not delivered as ready entries**. Boar and crab sources were inspected but lacked the needed motion set; no clips were fabricated.
- The 19-motion Mixamo route was checked in memory. It used an operator-authorised authenticated browser. It was unnecessary for these already-rigged sources and was not used.
- Existing rat, spider, bat, ghost, slime, skeleton, robot, zombie, demon boss, shark and the liked procedural scorpion were not edited or removed.
- No Factory/Engine catalog edit, game integration, deployment, live-app test, collision tuning, attack damage-window authoring, slope handling or GPU frame-rate benchmark was performed.
- No independent model reviewer or nested agent was started. The visual verdicts are the author's direct inspection, as requested.

## If this is wrong, and how to undo it

A consumer that ignores scale or playback speeds can reintroduce sliding. A wrong clip mapping could make an enemy attack or die incorrectly. Keep the existing catalog unchanged until the asset handoff is accepted; remove the new entry or restore its previous GLB to undo an import. Nothing live needs rolling back now.

## Files that prove the checks

- `evidence/node-validation.json`: exact final hashes, every played clip, sampled skinned vertices and errors.
- `evidence/independent-glb-audit.json`: geometry, packed textures, skin count and file sizes.
- `evidence/delivery-checks.json`: final contract counts and measured limits.
- `evidence/enemy-*-runtime.json`: browser clip playback, measured heights, speeds and contact data.
- `evidence/enemy-*-footplant-method.json`: source-measured speed, exact stance frames and correction method.
- `evidence/enemy-*-normalization.json`: source, scale, removed objects and missing clips.
- `MANIFEST.json`: final delivery file hashes.

Local disk was below the operator's 15 GB reserve: about 7.7 GB free during the final checks. Task-owned temporary local review copies were removed; delivered files remain.
'''
(P/'REPORT.md').write_text(text)
manifest=[]
for f in sorted(list((P/'glb').glob('*.glb'))+list((P/'renders').glob('enemy-*-hero.png'))+list((P/'renders').glob('enemy-*-clips.png'))+[P/'catalog_entries.json',P/'held_back_flyers.json',P/'contact_sheet.png',P/'REPORT.md']):
 if 'footplant' in f.name:continue
 manifest.append({'file':str(f.relative_to(P)),'bytes':f.stat().st_size,'sha256':hashlib.sha256(f.read_bytes()).hexdigest()})
(P/'MANIFEST.json').write_text(json.dumps(manifest,indent=2))
print(json.dumps(checks,indent=2));print('REPORT READY',now)
