_screenshot.py now decides capture time in three tiers: an explicit
MCRF_SHOT_AT wins; else if mcrfpy.animations reports active .animate()
animations, capture at 60% of the longest one's duration (fully automatic
via the engine's animation introspection); else fall back to the static
setup-steps path. So the ~50 .animate()-driven snippets land mid-effect
with zero per-snippet tuning.
The orchestrator's OVERRIDES table gains shot_at entries for the effects
the animation list can't see -- timer-driven pulses/flashes (Timers, not
animations) and a fade-to-invisible caption whose 60% frame is nearly blank.
Steady-state stable: 272 captured, byte-identical across repeated full runs.
A rare first-fire flake in two timer-driven snippets (048, 191) traced to a
genuine headless-clock bug, filed as #383 (Timer epoch reads wall clock, not
simulation_time); not a screenshot-tooling defect.
Addresses #381.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LTh2ZW7bd3XSd9qK86Z2CE