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>
The hand-run lock is a bind in the abstract socket namespace named by the state
root: atomic, exclusive, gone with its owner, so the probe, the unlink and the
takeover race are deleted. Every write to the state root and every spawn calls
guard(), which asks held() at that moment; Idunn's held() reads the lease each
time and reports a loss through the handler the daemon registered, so a request
admitted before the loss stops at its next write and a respawn cannot outlive it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Red at 4fd3f60 for stale-lock-takeover-race, fence-admits-in-flight-request and
bind-port-65536-accepted; green guards for reclaim-root-unpinned and the SIGTERM
property. The daemon's stop() no longer leaves a daemon holding the run open.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
serve(runtime) listens first, writes the state root only once the runtime
holds it, answers Idunn's route challenge ahead of the cross-site refusal,
and fences on lease loss. --health is deleted; the pid file and /proc
reclaim move behind the hand-run runtime. Adds scripts/idunn-entry.mjs,
the kimodo and SOMA-X gitlinks, encode_texts for the child, and tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes the survivors StrykerJS reported over the new supervisor code (61 mutants, 41 killed, 20 survived): the pid
file is removed when its worker exits and not when it spawned nothing, the restart kill is SIGKILL (a stubborn stray
and a SIGTERM-proof encoder child), the group match is the pid file's, not any marked process. killGroup loses its
unused return value.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The header-on vector now matches Kimodo's encoder loaded from the repo ids (cosine 1.0; the bare header gave
0.9974) because the wrapped text carries LLM2Vec's instruction/query separator, which keeps the header out of
the pooling. live_identity reads the installed versions itself, so the worker passes no version it could fake,
and the tests pin installed_versions against substituted modules, the prompt form and device in the identity,
and a text that differs past any prefix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The worker's process group is killed when the worker exits for any reason, so no encoder child survives it.
A pid file in the state root names the group; the next daemon start kills a group left by a crashed
predecessor if one of its members carries this state root's embeddings directory, before serving. The
retired operator route's 404 test returns (built from parts, so the grep stays empty) and the test script
gets a per-test timeout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The child wraps every text with text_encoding.chat_header and tokenizes it itself, so the prompt form no
longer rides on the model's _name_or_path. The identity names the assembly truthfully (header form
included, as a hash of the template) and re-keys the cache. The worker builds its identity through
live_identity and installed_versions, which the tests reach; the identity test now uses live-length
revisions, so a truncated identity or key fails. An env-gated test compares a fresh child vector with
Kimodo's own encoder loaded from the repo ids.
Supersedes encoder-assembly-b (motion-pipeline:ruling:encoder-chat-header-on).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mutation triage (StrykerJS, 81 survivors on the first full run) showed worker.model was
written and never read. The new tests pin the survivors that were behaviour: the loopback
default, --host, stray flags, the models-root resolution, the SIGTERM grace, a worker that
dies while the daemon waits for its health, the POST route, and lastError after a
requested stop.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>