Worker: with loop true the answer gains loops (one {first, last, poseCost, velocityCost}
per sample) from loop_window over Kimodo's somaskel77 posed joints and compute_global_heading.
Daemon: loop must be a boolean and needs fbx (400 'Invalid fbx request.'); sample k is
exported with '--frames first+1 last+1'; an answer without a window for a sample fails
'fbx-export-failed (no loop window)'; the record keeps request.loop and, when inline,
the constraints as sent. The daemon never computes a window.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Optional '--frames A B' after the clip name sets the scene's frame range to A..B
(Blender frames, from 1); outside the take, or malformed, it exits 4 or 3 and writes
nothing. Without it the whole take, as before. The test's BVH builder is shared.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Numpy only; the resident worker is its one caller. Hips-relative, heading-normalised
poses; cost is pose RMS plus 0.1 s of velocity RMS; ties go to the longer window,
then the earlier. Tests: a periodic stride between rests, the stated cost against a
frame-by-frame oracle, heading, ties, minimum length.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Idunn's python/node runners mount /tmp noexec; test-daemon copied the stub Blender there, so every export test saw
"unavailable" and 20 tests failed in the release build (node --test prints to stdout, which Idunn's error drops).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
readInside opens O_NONBLOCK|O_NOFOLLOW and checks the open descriptor before reading, so a FIFO is refused at once and holds no filesystem thread. work/ is a slot in the recipe; a daemon that holds the state root creates it and removes stale export-XXXXXX directories from it, nothing else, before it serves.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fbx and clipName on /motion/generate; scripts/bvh-to-fbx.py converts one BVH (SOMA77 names, standard T-pose,
metres, travel on Hips) with a fixed hash seed and time so one BVH on one host body gives one set of bytes. The
daemon runs it per listed BVH (regular file inside the job's artifact directory only), in its own process group,
killed with the worker on lease loss and stopped on SIGTERM. The seed is minted when absent and every job records
motion_job.v1 (seed, file sha256, export). GET /motion/artifacts and POST /motion/rest-fbx. The duplicated
bvh/bvhStandardTpose derivations are one plan. The entry points the daemon at the release's blender.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Red on the daemon at c5cf445: fbx jobs, the refusal ladder, export containment, kill on lease loss and SIGTERM,
the minted seed, motion_job.v1, the artifact read route and rest-fbx. test_bvh_to_fbx.py runs the converter in
real Blender (VILI_BLENDER). This commit does not pass its own tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- tick: the lease is asked every WATCH_MS (250 ms); a loss SIGKILLs the worker group at once (the bound is about a second, not the 5 s service-document tick)
- mayWrite() = not stopping and held(); guard() and the worker-output sink both read it
- shutdown: stopping first, listener closed, then the worker tree is stopped, then exit 0
- canonicalPath(): args.stateRoot is realpath'd once; lock name, reclaim marker and the worker environment derive from it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Red at 658de07 for the lease-loss bound, SIGTERM and spelling tests; the leaderless-group and later-line tests pin behaviour that already holds, for mutants.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The stub marks its first healthy answer; the stdout line it prints before ready
had been taking the lease through the log-sink gate first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fake Idunn signer takes the lease the moment a named path exists under the
state root. Each gate in generate and startWorker now has a case that only that
gate stops, and a lease lost while the state root opens leaves the daemon up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The startup persist now sits behind the same gate as every other write; its
LeaseLost ended serve() as an unhandled rejection. onLost has already run, so
serve returns without reporting active.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fake Idunn signer can lose the lease once a job's meta.json exists, which
pins the guards in startWorker (no logs or embeddings directory, no worker).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>