farmer/ — 3,920 lines across 24 modules · tests/ — 13 files, 1,108 lines
One generation in. Registered frames out.
assetfarmer turns a single keyed-background image into an animation strip whose frames are pixel-registered, whose background is genuinely gone, and whose failures are loud. An agent writes the choreography and judges the result; deterministic Python does every operation where a model would drift. This page is the line itself — follow it left to right, then drop into any stage.
- 11CLI subcommands in
farmer/cli.py - 4image providers; 1 exercised live end-to-end
- 2runtime dependencies: pillow, numpy
- 13/13test files pass with no AI calls (~8 s)
- 6assets committed to the in-repo registries
- 0pixels different outside a state's change region
The trunk: run_strip()
Nine stages, in order, inside one function —
farmer/pipeline.py:54. Every stage writes its output into the run directory
before the next one starts, so a failure is always inspectable. Follow the arrows; each node
opens further down this page.
- 01Choreographchoreograph.pyAction word → one pose line per cell.
- 02Anchor sheetgrids.pyMaster frame tiled into every cell on magenta.
- 03Promptprompts.pyPer-cell lines plus the strict-rules block.
- 04Generateproviders/One image. Verified PNG or a raised error.
- 05Despilldespill.pyFour passes from flat magenta to clean RGBA.
- 06Sliceslice.pyConnected components, no silent fallback.
- 07Alignalign.pyTorso-weighted registration, canvas lock.
- 08Validateqa.pyThree checks, one JSON, one contact sheet.
- 09Packpack.pyAtlas, manifest, per-frame PNGs, review page.
run_strip() in farmer/pipeline.py:54–138, and each dashed tick is a
file written to the run directory as that stage completes.“The calling agent is the creative brain: for anything not in the template library it writes its own per-cell script and passes it through. No LLM is called from inside the CLI.”
farmer/choreograph.py:4–6Walking the trunk
-
01
Choreograph
farmer/choreograph.pyAn action word becomes one explicit pose line per cell. Eight template functions cover
idle breathing,gesture presenting,stamp letter,waveand the four object presets;_resample()spreads a six-phase list across whatever frame count you asked for. Anything else is the agent's job — pass a JSON list of exactly N strings via--script.Locomotion is rejected here on purpose.
LOCOMOTION_WORDS(choreograph.py:16) holds thirteen words; if the action intersects that set and no explicit script was given,run_strip()raises before a single image is generated. -
02
Anchor sheet
farmer/grids.pyThe accepted master frame is tiled into every cell of the grid at one fixed scale and one fixed foot line, on flat
#FF00FF. The generator is then only asked to change the pose. Grid choice is table-driven and never a single row —_GRIDSmaps 4 → 2×2, 6 → 2×3, 9 → 3×3, up to 16 → 4×4. -
03
Prompt
farmer/prompts.pyFour builders, one shape: subject, style, reference notes, the numbered per-cell list, then a strict-rules block that spells out the magenta contract — no bloom on the background, no cell borders, nothing crossing a cell edge, subject inside the central 70% safe area. Unused cells are padded with
EXACT repeat of frame 1's pose (loop-return filler frame)
so no cell is ever left empty. -
04
Generate
farmer/providers/One image call per action. Providers declare capabilities, are constructed fail-fast — a missing dispatch script or unset API key raises
ProviderUnavailablefrom the constructor — and every one of them re-opens and PIL-verifies the file it produced rather than trusting a printed path. -
05
Despill
farmer/despill.pyFour passes: a hard colour-distance cut, a Chebyshev depth map out of the keyed region, a soft-alpha unmix that solves
observed = (1-k)·subject + k·keyso antialiased coverage survives as partial alpha, then a trapped-spill correction that fixes colour without touching alpha. An adaptive retry handles emissive halos, and refuses to run when the subject is genuinely magenta. -
06
Slice
farmer/slice.pyFrames are recovered as connected components, not by cutting on grid lines. The declared count must come back or
SliceErroris raised with every component's area and bbox in the message. Grid cutting exists as--slice-mode grid, an explicit opt-in, never an automatic fallback. -
07
Align
farmer/align.pyEach frame is shifted to maximise alpha overlap with frame 0 — but only the upper 65% votes, so a raised arm cannot drag the body. The union bounding box becomes a canvas lock shared by every action of that character; if a later action needs a bigger box, earlier actions are re-cropped from their stored working canvases.
-
08
Validate
farmer/qa.pyThree booleans decide
pass: frame count recovered, maximum anchor jitter ≤ 3 px, total residual key pixels ≤ 400. Torso motion is recorded but not gated — it contains the choreography you asked for. The contact sheet ships an onion-skin panel next to the frames so drift is visible at a glance. -
09
Pack
farmer/pack.pyPer-frame PNGs, a near-square atlas, a manifest with absolute rects and fps, a GIF preview and a dependency-free
review.html. Packing refuses non-uniform frame sizes outright —pack requires canvas-locked frames
.
Where the line forks
Three commands leave the trunk. Two of them reuse its organs; one refuses to use it at all.
-
is_locomotion(action) → brief.py
Walk, run, jog, sprint, stride, crawl, climb, swim, fly. Grid sheets were found to fail for these, so
farmer briefwrites a start frame plus an image-to-video prompt and stops. No API call, no pretence. -
farmer state → states.py
A registered object gets a semantic state generated in all four cells at once, then the anchor's pixels are pasted back everywhere outside the change region and the result is verified bit-for-bit.
-
farmer environment / scenevar → layers.py, scenes.py
Scene plates never touch chroma keying. Planes are cut from bboxes, holes are inpainted by iterative neighbour-mean fill, and a night variant is decomposed into reusable multiply and screen maps.
The command surface
Eleven subcommands, one argparse parser, built in
farmer/cli.py:175–299. The module docstring at the top of that file is what
--help prints:
$ python3 -m farmer --help usage: farmer [-h] {character,object,environment,adopt,state,transition,scenevar,apply-curation,selfcheck,cutout,brief} ... assetfarmer CLI. Subcommands: character grid-sheet animation pipeline for a character action object same pipeline with 2-4 frame loop presets (flicker/pulse/sway/steam) environment cut a plate into parallax planes + inpainted base cutout single frame: (generate ->) despill -> RGBA PNG brief emit a Dreamina/Seedance i2v handoff for locomotion adopt migrate pipeline output into the characters/objects/scenes registry state generate a semantic state for a registered object (composite-back) transition generate an endpoint-locked transition strip between two states scenevar generate a scene variant plate + extract its lighting overlay apply-curation drop review-rejected frames from a run and repack selfcheck run the deterministic test suite (tests/run_tests.py) options: -h, --help show this help message and exit
Pipeline commands return 0 when the report's pass is true and
2 when it is false (cli.py:46, 66, 112,
125, 135). selfcheck returns the number of failing test
files. A shell loop can gate on that without parsing anything.
What is built, and what is only declared
Accuracy about maturity matters more than a feature list. Everything below was read out of the tree, not out of the README.
| Area | State | Evidence |
|---|---|---|
| Despill, slice, align, pack, QA | Implemented, unit-tested | tests/test_despill.py, test_slice.py, test_align.py, test_manifest.py |
| Composite-back guarantee | Implemented, asserted end-to-end | tests/test_states.py drives run_state() with a mock provider that injects +1 drift on every subject pixel, then asserts bit-identity outside the region |
| Scene lighting overlays | Implemented | Committed scenes/lab night variant records mean_abs_diff 2.076, 7.81% of pixels off by 8 or more |
Providers openai, fal, gemini |
Implemented, not battle-tested | Selection and a mock CLI are covered by tests/test_providers.py; the README states only codex has run live end-to-end |
| Transition strips | Mechanism tested, not present in the shipped registry | objects/lamp/identity.json has "transitions": {} and runs/transition-idle_to_on/ holds only anchor-sheet.png and prompt.txt — the run never completed, though test_states.py exercises the whole path |
sockets, lightRig, slots, styleContract.prompt, palette |
Declared, never populated | Written as empty containers in registry.py:100 and 189–194; no other code path assigns to them |
styles/, prompts/, reference/ directories |
In DESIGN.md, absent from the tree | Identity files point at "style": "styles/clay-building", a path that does not exist in the repository; prompts live in farmer/prompts.py |
| Environment plane cutting | Deliberately below SAM quality | refine_silhouette() is kmeans plus an edge-blocked flood carve in pure numpy, and falls back to the rectangle when the object survives at under 25% of the bbox |
A single-author v0.1.0 with an unusually disciplined core and a thin outer shell. The pixel operations are the mature part — they have tests, guard clauses, and named failure modes. The registry schema is ahead of the code that fills it. Nothing here has a user base to speak of; the honest claim is this works on the author's assets, and the repository shows the artifacts to back that up.