Two changes, both driven by measurement against 128 production gifsmith runs and a local replica of the agent loop (docs/audits/2026-07-24-gifsmith-quality.md in mort). Staging rules. The renders that "worked" still read badly, always the same four ways: the subject drawn 5-10% of the frame height under an empty sky, long runs of pixel-identical frames, captions narrating action that was never drawn, and flat untextured shapes. The recipe never asked for anything else. SKILL.md now states four checkable rules — fill the frame, every frame moves, show it don't caption it, give it depth — plus easing/anticipation/squash-and-stretch, and the self-critique makes a "no" on depiction, subject size or caption legibility a mandatory re-render instead of a suggestion. Render harness. 26% of production code_exec calls end in a traceback, and every crash class lives in plumbing the agent re-derives each run: scratch dir, frame loop, numbering, canvas size, contact sheet, encode. scripts/gifsmith.py owns all of it and the agent writes only @anim.scene drawing functions. A scene that raises is now skipped while the others still render and ship, the canvas is size-locked so ffmpeg cannot fail on jittering frames, and the drawing vocabulary is re-exported so a missing import cannot NameError. The harness also unblocks the multi-minute pieces this skill advertises but could never produce: /tmp is a 16 MiB tmpfs (~13s of frames) and /workspace 64 MiB (~51s), which is why "No space left on device" is 15% of all production crashes. Anything past the GIF length threshold is an MP4, and an MP4 encodes in one streaming pass, so those frames now go straight into ffmpeg's stdin and never touch disk — verified at 1350 frames / 90 s with 0 MiB on disk. encode.py is removed; the harness supersedes it.
executus-skills
Skill packs for executus
harnesses (mort, gadfly, …), in the SKILL.md "agent-skills" format. Each pack is
its own subfolder with a SKILL.md manifest plus any bundled helper files.
executus-skills/
gif/
SKILL.md # instructions the agent loads on demand
scripts/encode.py # bundled helper the agent runs in its sandbox
Packs
gif— make a funny animated GIF or MP4 for Discord from a description. Plans scenes, keeps recurring people/props consistent via a reusable cast, renders frames in Python, and lets a bundledencode.pypick the format: a GIF for short loops, or an H.264 MP4 (that Discord plays inline) for long bits or when a GIF comes out too big. Adapted from mort's builtinanimateskill. Handles multi-minute pieces.
Using a pack from mort
Each subfolder is a separate subscription (pinned by content digest):
.set skillpacks.enabled true # then restart mort (boot-read)
.set skillpacks.allowed_sources git:https://gitea.stevedudenhoeffer.com/steve/
.skillpack subscribe git https://gitea.stevedudenhoeffer.com/steve/executus-skills subpath=gif ref=main
Then attach the pack to an agent that already has the tools the pack uses (the
pack supplies instructions, not tools). For gif, the agent needs
code_exec, image_describe, send_attachments, file_get_metadata, and
think in its toolset, a generous max_runtime with critic_enabled: true
(long renders run for minutes), and a sandbox image with ffmpeg (plus
gifski/gifsicle for nicer GIFs). Add it in the agent's YAML:
low_level_tools: [code_exec, image_describe, send_attachments, file_get_metadata, think]
skill_packs: [gif]
When the pack is updated upstream, .skillpack check reports it and
.skillpack apply gif re-pins — nothing an agent runs changes until you apply.