steveandClaude Opus 5 c1b2d41cc5 fix(gif): make the contact sheet an actual contact sheet
Closes mort#1519.

`_contact_sheet` built one tile per SCENE, and a single-scene animation — the
common case — short-circuited to `tiles[0][1].first_frame.save(path)`, a bare
frame. The harness then printed "still: … (contact sheet)" next to it.

Every single-scene worker across two production runs corrected that claim
unprompted, in the vision model's own words: "this is a single frame (not a
contact sheet)", "there is exactly 1 distinct panel/tile", "a single static
illustration, not an animation contact sheet" — ten of them. One worked out why
its critic could not see the brine tear it had animated, then proved it with a
pixel count, having already spent three full re-renders and 690 seconds on it.
It was the last of nine parallel workers to finish, holding the whole fan-out
open.

The sheet is now sampled across TIME: nine tiles by default, spread over the
whole animation, each labelled with its `t`. One scene gets all nine time
points; many scenes still get one tile each, as before, so nothing regresses
for the multi-scene case that already worked.

Two details that matter more than they look:

**Never sample t=1.0.** A loop is built to return to its starting state, so its
closing frame is the one instant guaranteed to show none of the motion. That is
exactly the frame the tear had faded out of.

**Label every tile with its t.** Without it the critic cannot tell "the tear is
missing" from "the tear is missing at t=0.75", and only the second is
actionable. The SKILL.md critique step now says so directly: check the other
tiles before re-rendering.

Reconciling the discrepancy the issue raised: the body said the still is the
t≈1.0 frame (backed by a pixel count on a live render), the code reads
`first_frame`. At HEAD it is NEITHER — `if sc.first_frame is None and
k >= n // 2` captures the MIDDLE frame, and that line arrived with the harness
in 7906a1a. The t≈1.0 observation therefore came from an older deployed pack.
It does not change the fix: one time point is one time point, whichever one it
is, and the single-scene short-circuit was the real defect.

Also: a failed scene now drops its samples along with its partial frames, so
the sheet can't advertise a scene that never shipped. Thumbnails are downscaled
at capture rather than held full-size — nine 640x480 RGB copies is ~8 MiB held
for the whole render, and the sheet shrinks them anyway.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-26 12:09:28 -04:00

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 bundled encode.py pick 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 builtin animate skill. 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.

S
Description
Common and example skills for executus.
Readme
118 KiB
Languages
Python 100%