@lovelace_lol/polymer 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +434 -12
  2. package/dist/index.d.ts +1945 -244
  3. package/dist/index.js +1686 -1017
  4. package/dist/vision/face-worker.js +296 -0
  5. package/dist/vision/face_landmarker.task +0 -0
  6. package/dist/vision/manifest.edn +1 -0
  7. package/dist/vision/vision_bundle.js +2 -0
  8. package/dist/vision/wasm/vision_wasm_internal.js +8841 -0
  9. package/dist/vision/wasm/vision_wasm_internal.wasm +0 -0
  10. package/dist/vision/wasm/vision_wasm_module_internal.js +8837 -0
  11. package/dist/vision/wasm/vision_wasm_module_internal.wasm +0 -0
  12. package/dist/vision/wasm/vision_wasm_nosimd_internal.js +8832 -0
  13. package/dist/vision/wasm/vision_wasm_nosimd_internal.wasm +0 -0
  14. package/dist/worker/agency-worker.js +1276 -962
  15. package/dist/worker/manifest.edn +1 -1
  16. package/package.json +33 -20
  17. package/dist/test.cjs +0 -1519
  18. package/shadow-cljs.edn +0 -173
  19. package/src/polymer/animation/agency.cljs +0 -84
  20. package/src/polymer/animation/domain.cljs +0 -110
  21. package/src/polymer/animation/planner.cljs +0 -88
  22. package/src/polymer/animation/runtime.cljs +0 -137
  23. package/src/polymer/animation/scheduler.cljs +0 -178
  24. package/src/polymer/animation/service.cljs +0 -981
  25. package/src/polymer/animation/state.cljs +0 -112
  26. package/src/polymer/blink/agency.cljs +0 -131
  27. package/src/polymer/blink/planner.cljs +0 -115
  28. package/src/polymer/blink/scheduler.cljs +0 -90
  29. package/src/polymer/blink/snippet.cljs +0 -50
  30. package/src/polymer/blink/state.cljs +0 -102
  31. package/src/polymer/camera_context/agency.cljs +0 -132
  32. package/src/polymer/camera_context/domain.cljs +0 -186
  33. package/src/polymer/camera_context/planner.cljs +0 -50
  34. package/src/polymer/camera_context/scheduler.cljs +0 -113
  35. package/src/polymer/camera_context/state.cljs +0 -121
  36. package/src/polymer/character.cljs +0 -403
  37. package/src/polymer/conversation/agency.cljs +0 -83
  38. package/src/polymer/conversation/domain.cljs +0 -35
  39. package/src/polymer/conversation/effector.cljs +0 -130
  40. package/src/polymer/conversation/planner.cljs +0 -178
  41. package/src/polymer/conversation/scheduler.cljs +0 -202
  42. package/src/polymer/conversation/service.cljs +0 -329
  43. package/src/polymer/conversation/state.cljs +0 -186
  44. package/src/polymer/core.cljs +0 -172
  45. package/src/polymer/eye_head/agency.cljs +0 -75
  46. package/src/polymer/eye_head/domain.cljs +0 -133
  47. package/src/polymer/eye_head/goap.cljs +0 -106
  48. package/src/polymer/eye_head/scheduler.cljs +0 -176
  49. package/src/polymer/eye_head/service.cljs +0 -259
  50. package/src/polymer/eye_head/snippet.cljs +0 -92
  51. package/src/polymer/eye_head/state.cljs +0 -167
  52. package/src/polymer/gaze/agency.cljs +0 -85
  53. package/src/polymer/gaze/domain.cljs +0 -211
  54. package/src/polymer/gaze/goap.cljs +0 -208
  55. package/src/polymer/gaze/scheduler.cljs +0 -184
  56. package/src/polymer/gaze/state.cljs +0 -202
  57. package/src/polymer/gesture/agency.cljs +0 -185
  58. package/src/polymer/gesture/goap.cljs +0 -461
  59. package/src/polymer/gesture/scheduler.cljs +0 -122
  60. package/src/polymer/gesture/snippet.cljs +0 -198
  61. package/src/polymer/gesture/state.cljs +0 -273
  62. package/src/polymer/lipsync/agency.cljs +0 -593
  63. package/src/polymer/lipsync/articulation/jaw.cljs +0 -258
  64. package/src/polymer/lipsync/articulation/snippet.cljs +0 -605
  65. package/src/polymer/lipsync/articulation/tongue.cljs +0 -418
  66. package/src/polymer/lipsync/articulation/visemes.cljs +0 -429
  67. package/src/polymer/lipsync/goap.cljs +0 -132
  68. package/src/polymer/lipsync/pronunciation/effector.cljs +0 -51
  69. package/src/polymer/lipsync/scheduler.cljs +0 -302
  70. package/src/polymer/lipsync/state.cljs +0 -202
  71. package/src/polymer/nlp/phonemes.cljs +0 -164
  72. package/src/polymer/prosodic/agency.cljs +0 -196
  73. package/src/polymer/prosodic/goap.cljs +0 -122
  74. package/src/polymer/prosodic/scheduler.cljs +0 -99
  75. package/src/polymer/prosodic/snippet.cljs +0 -106
  76. package/src/polymer/prosodic/state.cljs +0 -139
  77. package/src/polymer/runtime/embody.cljs +0 -117
  78. package/src/polymer/runtime/embody_wasm.cljs +0 -177
  79. package/src/polymer/runtime/rust_embody_host.cljs +0 -647
  80. package/src/polymer/society.cljs +0 -253
  81. package/src/polymer/stream.cljs +0 -130
  82. package/src/polymer/transcription/agency.cljs +0 -69
  83. package/src/polymer/transcription/domain.cljs +0 -68
  84. package/src/polymer/transcription/effector.cljs +0 -611
  85. package/src/polymer/transcription/planner.cljs +0 -203
  86. package/src/polymer/transcription/scheduler.cljs +0 -225
  87. package/src/polymer/transcription/service.cljs +0 -209
  88. package/src/polymer/transcription/state.cljs +0 -239
  89. package/src/polymer/tts/agency.cljs +0 -503
  90. package/src/polymer/tts/azure.cljs +0 -290
  91. package/src/polymer/tts/planner.cljs +0 -178
  92. package/src/polymer/tts/runtime.cljs +0 -624
  93. package/src/polymer/tts/scheduler.cljs +0 -162
  94. package/src/polymer/tts/state.cljs +0 -191
  95. package/src/polymer/worker.cljs +0 -81
package/README.md CHANGED
@@ -1,8 +1,47 @@
1
1
  # Polymer
2
2
 
3
+ Character runtime hosts expose `getViseme(index)` and `getVisemeJawScale(index)`
4
+ to snapshot direct mouth-shape controls before a temporary preview. These are
5
+ read-only views of Embody's canonical state, not the current mixer pose. Unset,
6
+ cleared, or out-of-range slots return `0` and `1`. Restore a snapshot with
7
+ `setViseme(index, value, jawScale)`; restoring zero releases the direct override
8
+ and allows any underlying clip to keep playing.
9
+
3
10
  Clean ClojureScript character agency package for Character Loom.
4
11
 
5
- Production releases publish to npm as `@lovelace_lol/polymer` on every push to `main`. Hosts should depend on the npm package (`latest` or a semver range). PR staging may still use git SHA pins or LoomLarge `Depends-on` links.
12
+ Git stores Polymer source only. CI builds and tests each commit once. Verified
13
+ `main` builds publish versioned packages to npm for production; PRs and requested
14
+ preview SHAs publish compiled packages to [pkg.pr.new](https://pkg.pr.new/). Install the
15
+ [pkg-pr-new GitHub App](https://github.com/apps/pkg-pr-new) on this repository.
16
+ Production consumers pin an exact npm version. Work testing an upstream PR pins
17
+ the immutable full-SHA preview URL emitted by CI. Git-SHA installs are unsupported
18
+ because compiled JavaScript is not checked in.
19
+
20
+ Polymer consumes the public npm release `@lovelace_lol/embody@0.0.8`, built
21
+ from Embody main source commit
22
+ `a55e89edbb22b26059a63ce002a2c3e25caf0cf0`:
23
+
24
+ ```text
25
+ @lovelace_lol/embody@0.0.8
26
+ ```
27
+
28
+ This release retains the renderer lifecycle support and speech AU 103 in
29
+ the existing `JAW.pitch.aus` list in CC4's `compositeRotations`. LipSync can send
30
+ the AU through Animation and use its saved bone and morph outputs.
31
+
32
+ The CC4 body-control catalog places hip bend, twist and side bend in **Whole
33
+ body**, separate from the waist, spine and chest controls in **Torso**. These
34
+ existing animation controls rotate the hip bone, which carries both torso and
35
+ legs. They differ from import/setup rotation, which rotates the entire model
36
+ object and saves its orientation as `modelRotation`. Hosts using
37
+ `getBodyControls` inherit the catalog labels and sections unless a profile
38
+ explicitly overrides them. Action IDs and bone motion stay unchanged.
39
+
40
+ The character host restores `profile.meshes` visibility and material settings
41
+ through Embody when binding a model or applying `setProfile`. Explicit hidden
42
+ meshes, zero opacity, disabled depth flags, and render order survive a saved
43
+ profile round trip. Hosts supply the selected character's profile; they do not
44
+ need a second mesh-settings replay path.
6
45
 
7
46
  Polymer is intentionally separate from Latticework and Polyester. It is built as
8
47
  a Society of Mind agency system: small CLJS agencies collaborate through streams,
@@ -11,6 +50,52 @@ responsible for performing them.
11
50
 
12
51
  See [docs/agency-architecture.md](docs/agency-architecture.md) for the agency
13
52
  collaboration, scheduler, side-effect, stream, and runtime-target rules.
53
+ The Rust host's live-control release and mixer ownership contract is documented
54
+ in [docs/live-control-ownership.md](docs/live-control-ownership.md).
55
+
56
+ For gaze tracking, head following takes the full target turn from reference neutral,
57
+ bounded by the profile's head AU limits;
58
+ camera-facing preference requires `lockHeadToCamera: true`. With geometry-backed focus, live eye/head strength
59
+ changes move continuously toward the latest value in [0, 1], including when a
60
+ slider reverses direction. See the [eye-contact contract](docs/gaze-tracking-contract.md)
61
+ for geometry, timing, channel ownership, and authored motion behavior.
62
+
63
+ ## Camera and annotation runtime
64
+
65
+ Polymer directly re-exports Embody's Three.js camera and annotation runtime.
66
+ `DPthreeCameraController`, `DPthree3DMarkers`, `DPthreeHTMLMarkers`, and the
67
+ `DPthree` convenience wrapper are the same constructors provided by
68
+ `@lovelace_lol/embody/three`. Embody owns marker creation, surface projection,
69
+ rendering, camera transitions, visibility timing, and teardown. Polymer does not
70
+ maintain a second implementation or introduce an annotation agency.
71
+
72
+ ```js
73
+ import { DPthree } from '@lovelace_lol/polymer';
74
+
75
+ const annotations = new DPthree({ scene, camera, domElement: canvas });
76
+ annotations.setModel(model);
77
+ await annotations.loadCharacter(characterConfig);
78
+ await annotations.focus('head');
79
+ // Call annotations.update() from the host's existing render loop.
80
+ // Call annotations.dispose() when its scene is destroyed.
81
+ ```
82
+
83
+ Hosts own editor UI, persistence, scene attachment, and external input wiring.
84
+ Use `createMarkerVisibilityLifecycle` for automatic reveal/hide and
85
+ `createRuntimeAnnotationPreviewLifecycle` for transient editor previews rather
86
+ than implementing host timers. TypeScript declarations are forwarded from
87
+ Embody so runtime classes and their public signatures stay aligned. The package
88
+ contract checks constructor identity against the pinned Embody preview.
89
+
90
+ For a newly attached model, await the existing camera controller's
91
+ `prepareModelForRender(model, { signal })` before reporting render readiness.
92
+ Embody coordinates compilation and a full-scene preparation draw with its
93
+ existing loop; Polymer forwards the method without another rendering scheduler.
94
+ See [model render readiness](docs/character-host-readiness.md#model-render-readiness).
95
+
96
+ Pure [unscored questionnaire content contracts](docs/questionnaire-content.md)
97
+ cover authored choice/text validation, participant projection and editor metadata.
98
+ They do not yet provide a Questionnaire runtime agency or participant UI.
14
99
 
15
100
  ## Agency API
16
101
 
@@ -50,7 +135,7 @@ schedulers.
50
135
 
51
136
  Polymer currently includes early Blink, Animation, Gaze, Eye/Head Tracking,
52
137
  TTS, LipSync, Prosodic Expression, and Gesture agencies, plus first slices for
53
- Conversation, Camera Context, and Transcription. These are implementation
138
+ Conversation, Camera Context, User Perception, and Transcription. These are implementation
54
139
  milestones, not the limits of the architecture. Each agency should follow the
55
140
  same local GOAP/planner, scheduler, stream, transform, and effector pattern as
56
141
  the package grows.
@@ -58,6 +143,68 @@ the package grows.
58
143
  The text-only LipSync pronunciation fallback and its accuracy limits are
59
144
  documented in [`docs/lipsync-pronunciation-fallback.md`](docs/lipsync-pronunciation-fallback.md).
60
145
 
146
+ The five supported live Prosodic settings, revision receipts, safe decision
147
+ projection, and routing-disable lifecycle are documented in
148
+ [`docs/prosodic-configuration-pilot.md`](docs/prosodic-configuration-pilot.md).
149
+ This bounded pilot preserves the other eleven agencies and does not implement a
150
+ global configuration controller or captured-session replay.
151
+
152
+ Gesture, Gaze, and Camera Context also expose portable definitions and
153
+ agency-local revisioned settings under Action, Cognition, and Perception
154
+ namespaces. Their supported consumers, apply boundaries, and safe diagnostic
155
+ projections are documented in
156
+ [`docs/agency-configuration-families.md`](docs/agency-configuration-families.md).
157
+ Public agency IDs and constructors remain unchanged.
158
+
159
+ The opt-in [Gaze Lab POC](docs/gaze-attention-poc.md) retains static candidates,
160
+ uses actual routed Conversation credit or explicitly synthetic fixture influence,
161
+ and measures final-goal optical alignment through Animation. It is a bounded
162
+ feedback demonstration, not general moving-peer arbitration or learning; normal
163
+ mouse/webcam behavior remains unchanged.
164
+
165
+ User Perception's camera, worker, model assets, compact fact contract, and
166
+ privacy boundary are documented in
167
+ [`docs/user-perception.md`](docs/user-perception.md).
168
+
169
+ LipSync receives rig articulation from the resolved Embody preset/profile.
170
+ After `initEmbodyCore()`, call `resolveVisemeProfile(runtime.getProfile())` once
171
+ when binding the character and after each profile change. The synchronous result
172
+ contains `visemeSlots` in exact runtime order (missing order is zero; ties keep
173
+ source order), weighted/numeric binding targets, resolved jaw amounts and tongue
174
+ AU targets. Use its array indices for manual `setViseme(index, value)` preview and
175
+ configure LipSync with `{ visemeProfile: descriptor }`. This is a host profile
176
+ projection boundary, not work to repeat every frame. A live descriptor change
177
+ rebuilds the current clip at its current audio position. Explicitly configure this
178
+ field after every runtime `setProfile`, even when the descriptor is unchanged:
179
+ profile rebinding can clear animation actions. Polymer re-emits the active clip
180
+ and seeks to the current audio position without restarting audio.
181
+
182
+ For custom slots, Azure speech matches `providerIds.azure`; generated English
183
+ text matches `phonemes` using CMU/ARPABET symbols with optional stress digits.
184
+ External timelines can carry `provider` and `providerId`, or a `phoneme`.
185
+ A timeline containing only canonical `visemeId` needs an explicit
186
+ `providerIds.canonical` route in a custom profile. An unknown, ambiguous, or
187
+ unbound route retains neutral timing rather than selecting a guessed slot.
188
+ Azure zero reaches a declared zero route; otherwise it is neutral. Custom slots
189
+ can exceed 15: their model indices never become AU or jaw control IDs.
190
+
191
+ The descriptor overrides both `visemeJawAmounts` and `visemeTongueTargets` so
192
+ articulation uses the same runtime slot order. Without a descriptor, the existing
193
+ canonical 15-slot behavior and separately supplied tables remain supported.
194
+ Keys-only profiles and the unchanged stock CC4 routing metadata also
195
+ retain that behavior, including stock Azure refinement/diphthong rules. The
196
+ stock metadata contains overlapping aliases, so replacing its routing metadata
197
+ opts into explicit matching rather than silently changing legacy speech. Reordering
198
+ all stock slots preserves canonical sound semantics through `canonicalSlotIndices`,
199
+ which maps canonical sounds to their new runtime positions.
200
+
201
+ Resolution describes authored bindings, not successful deformation: a referenced
202
+ morph can still be missing from the loaded model. Polymer does not infer a VRM
203
+ loader, a complete system recipe, or target correctness from a slot's name.
204
+ Weighted targets are resolved by Embody, and manual and speech channels use that
205
+ same binding. Polymer owns timing, grouping, and curve planning; the profile owns
206
+ jaw apertures and tongue poses. With no profile jaw amounts, no jaw track is authored.
207
+
61
208
  Runtime-specific work belongs at the edge. The current animation path can target
62
209
  the existing web runtime, but Polymer core should remain able to support other
63
210
  runtime targets such as Babylon, Unity, Godot, robotics, or future animation
@@ -134,10 +281,45 @@ The host supplies the gesture library and may observe streams for diagnostics,
134
281
  but Gesture's own scheduler is responsible for replacement, cancellation, and
135
282
  completion cleanup before Animation owns the runtime side effect.
136
283
 
284
+ ### Animation snippet looping
285
+
286
+ Animation snippets default to repeating when no loop preference is supplied.
287
+ Authored `mixerLoopMode` (`repeat`, `pingpong`, or `once`) takes precedence over
288
+ the snippet's `loop` boolean. A `scheduleSnippet` command's `options.loopMode`
289
+ or `options.loop` overrides that authored preference for the scheduled play;
290
+ an explicit options mode takes precedence over the options boolean.
291
+ The compatibility service's `schedule` method accepts the same options.
292
+
293
+ Animation uses that effective loop policy for the runtime, schedule snapshots,
294
+ and compatibility UI state. Repeating clips stay scheduled until removed or
295
+ stopped, including when a one-shot is switched to repeat before its original
296
+ completion callback resolves. Explicit one-shots retain completion cleanup.
297
+
137
298
  ## Character host (Embody scene + model loading)
138
299
 
139
- Polymer re-exports Embody's character host so LoomLarge and other hosts can set
140
- up a character without depending on Embody directly.
300
+ Polymer composes Embody's scene/model utilities with its animation host so
301
+ LoomLarge and other hosts can set up a character without depending on Embody
302
+ directly. Owned scenes await initial renderer preparation before the host
303
+ resolves. The async default is `auto`: Embody tries native WebGPU and releases
304
+ a failed initialization attempt before preparing WebGL. Later scene-preparation
305
+ errors remain errors. Use `scene: { renderer: 'webgl' }` to
306
+ force WebGL or `'webgpu'` to require native WebGPU without fallback. The synchronous scene
307
+ factory stays WebGL-only; `createDefaultCharacterSceneAsync` exposes the
308
+ scene-only Promise API. Three and its declarations use the coordinated r184
309
+ contract.
310
+
311
+ The new `createDefaultCharacterSceneRuntime` export and `host.rendering`
312
+ forward Embody's renderer-settings controller. It owns switching and recovery;
313
+ Polymer keeps the scene, camera, model, and animation engine intact and exposes
314
+ current renderer/backend getters. This source integration awaits an Embody
315
+ preview with the new controller API; the existing published pin is retained.
316
+ See [renderer settings and replacement](docs/character-host-readiness.md#renderer-settings-and-replacement)
317
+ for subscriptions, capture leases, and ownership.
318
+
319
+ Pass `signal` to cancel host creation cooperatively, then call `host.dispose()`
320
+ for the resolved host's lifetime. Borrowed external scenes remain caller-owned.
321
+ See [readiness and cleanup semantics](docs/character-host-readiness.md), including
322
+ which GPU/model stages are outside this readiness contract.
141
323
 
142
324
  Default path — pass a container element and a character URL:
143
325
 
@@ -210,19 +392,221 @@ if (supportsRustEmbodyPreset('cc4')) {
210
392
 
211
393
  The CLJS host owns the JavaScript API and render-loop bridge. Rust owns preset
212
394
  lookup, profile merge and intake, compiled bindings, transitions, and packed
213
- frame evaluation. Unsupported presets must stay on the compatibility host until
395
+ frame evaluation. Rust selects active procedural channels. The host preserves the underlying
396
+ renderer values while applying those packed writes, so releasing live controls
397
+ reveals paused or unchanged mixer samples. Host mixers own clip playback.
398
+ Unsupported presets must stay on the compatibility host until
214
399
  they are explicitly embedded; they are never treated as CC4 aliases.
215
400
 
216
- ## Git Install Contract
401
+ Authoring controls can read procedural state with `engine.getAU(id)`,
402
+ `engine.getAUBalance(id)` (left/right balance in `-1..1`), and
403
+ `engine.getContinuum(negativeId, positiveId)`. These read Rust's current values,
404
+ including neutral values after `clear()` or `setProfile()`, so a remounted
405
+ control can resume the current pose without maintaining a second state cache.
406
+
407
+ ### Semantic humanoid skeleton
408
+
409
+ `getHumanoidSpecification()` exposes Embody's canonical VRM 1.0 catalog: all
410
+ 55 bone roles, the 15 required roles, and parent/optional-parent requirements.
411
+ Call it after `initEmbodyCore()`, like the other synchronous profile queries.
412
+ `resolveHumanoidCharacterization(profile)` resolves authored role aliases;
413
+ `validateHumanoidCharacterization(model, profile)` additionally checks the
414
+ actual model hierarchy, unique assignments, and supported transform constraints.
415
+
416
+ `setHumanoidRoleBinding(profile, role, boneName)` returns a new profile that
417
+ assigns an actual model bone; pass `null` to clear a role. Persist the returned
418
+ characterization as a complete replacement, together with changed bone aliases,
419
+ action/composite mappings, and continuum pairs, then reconfigure the runtime. Authored catalogs
420
+ remain authoritative across preset upgrades. Clearing a role makes its semantic
421
+ bone outputs dormant while preserving the rest of the profile.
422
+
423
+ The CC4 preset supplies a complete role map and controls across torso, head,
424
+ arms, hands/fingers, legs, and feet/toes. Bound UI controls use
425
+ `engine.getBodyControls()` to read descriptors cached from the configured Rust
426
+ profile/model, avoiding scene traversal and repeated profile parsing. The standalone
427
+ `getBodyControls(profile, model)` query remains available for unbound authoring.
428
+ `engine.resetBodyControls()` clears the configured catalog's action values and
429
+ balances in one Rust call and applies one frame, preserving unrelated facial actions
430
+ and morph-strength settings. Head, eye, and jaw controls
431
+ share existing facial actuators; all controls use the existing AU/continuum
432
+ host methods. Applications should show the full role inventory even when a
433
+ model omits optional bones, and allow users to repair its assignments.
434
+
435
+ Every configured Body action, including each direction of a signed control,
436
+ can drive authored left/right/center morph mappings. Morph strength scales
437
+ those outputs even when the action has no bone bindings. Persist the action's
438
+ `auToMorphs` and `auMixDefaults`, and retain its mesh category: head, eye, and
439
+ jaw controls may share facial actions with different target meshes. The package
440
+ contract tests exercise all six sections through the actual Rust-backed host,
441
+ including mesh selection, balance, zero strength, and mapping removal.
442
+ An explicitly configured mesh list restricts these Body actions to that list;
443
+ an empty list disables their morph outputs rather than selecting other meshes.
444
+ After a temporary direct morph preview, call `engine.releaseMorph(key, meshes)`
445
+ to let current AU/viseme/clip output resume. Setting a morph to zero is still an
446
+ explicit override and intentionally continues to suppress those other sources.
447
+
448
+ `setAUMorphTargets(profile, auId, side, targets)` edits one action side's
449
+ morph targets without removing the same targets from other actions. Sides are
450
+ `left`, `right`, or `center`; targets are names or numeric morph indices. The
451
+ returned profile includes the complete edited AU row, with other sides preserved
452
+ and explicit empty arrays retained across preset reloads. Hosts can persist just
453
+ that row as an override and reconfigure the runtime.
454
+
455
+ Head and eye semantic controls use the existing FACS AU IDs, mappings, balances,
456
+ and morph-strength settings. CC4 neck controls seed the corresponding head tissue
457
+ morphs and ratios while keeping separate neck bone actions and profile overrides.
458
+ Neck morphs use the face mesh route; limb muscle morphs use their authored route.
459
+ `auMixDefaults` scales morph deformation without reducing bone movement. Editors
460
+ should expose these settings even when configured morph targets are missing, so
461
+ users can repair the assignment or choose another model.
462
+
463
+ VRM defines anatomical roles and hierarchy. Motion axes/ranges and muscle morphs
464
+ remain authored rig data. Editing an unmapped import does not silently give it
465
+ the CC4 preset. Polymer provides this compatibility API as a thin Rust adapter;
466
+ it adds no second role catalog, pose evaluator, or character state cache.
467
+ Use `engine.getAU(id)` and `engine.getAUBalance(id)` together when saving a live
468
+ AU pose. The balance reader returns Rust's current control value in `[-1, 1]`
469
+ (`-1` left, `0` both sides, `1` right), or `0` for an unset or cleared AU.
470
+ Save that value in the snippet's `snippetBalanceMap` to preserve sided playback
471
+ through AnimationService. These readers report live control state, not the
472
+ sampled pose of a playing mixer clip.
473
+
474
+ ### Stable character reference poses
475
+
476
+ The first `onReady` captures the model's loaded transforms and morph weights,
477
+ before Polymer applies the profile or starts playback. `setProfile`, `bindModel`,
478
+ and subsequent `onReady` calls for that model reuse the capture. Animation
479
+ intensity is measured from that same reference. Live inherited animation starts
480
+ still sample the rendered pose when a clip is materialized.
481
+
482
+ Call `onReady` while a newly loaded model is intentionally in its reference pose.
483
+ This does not infer a skin bind pose or use animation frame zero. If the same
484
+ model survives engine recreation, retain an explicit capture with the model:
217
485
 
218
- Polymer Git-SHA consumers should import checked-in JavaScript artifacts from
219
- `dist`. They should not need Java, Shadow CLJS, or a local Polymer build during
220
- application install.
486
+ ```js
487
+ import { captureModelReferencePose } from '@lovelace_lol/polymer';
488
+ const referencePose = captureModelReferencePose(model); // before playback/edits
489
+ engine.onReady({ model, referencePose });
490
+ // Pass referencePose again when creating a replacement engine for this model.
491
+ ```
492
+
493
+ Intentional skeleton additions must append new children without renaming,
494
+ removing, or reordering existing nodes. After insertion, call
495
+ `engine.extendReferencePose()` before applying new profile mappings. This returns
496
+ a new immutable capture that includes the added nodes and preserves every old
497
+ reference transform; retain it if the model will outlive its engine. Other
498
+ hierarchy edits fail explicitly before the active profile or playback is cleared.
499
+ Ordinary profile, transform, material, and morph-target edits need no recapture.
500
+
501
+ Baked-animation pause and resume also publish the current runtime playhead, so
502
+ the paused timeline remains at the displayed pose instead of reverting to an old
503
+ time. These are discrete state updates; playback progress stays in the runtime.
504
+
505
+ ### Optional baked animation assets
506
+
507
+ Use `engine.loadAnimationClips(initialClips)` during initialization. After the
508
+ model is bound, `engine.registerAnimationClips([clip])` adds a downloaded clip
509
+ without starting it, resetting the mixer, or interrupting native, snippet, or
510
+ hair playback. Re-registering the same clip object does no serialization work.
511
+ A new source under an existing name replaces the next playback; the current
512
+ action continues until playback is explicitly requested. Registered sources
513
+ survive model rebinding, and `removeAnimationClip(name)` also removes them from
514
+ the sources retained for rebinding. Treat registered source objects as immutable.
515
+
516
+ The Animation agency's existing `loadBakedClips` command accepts lightweight
517
+ `{name, duration, channels}` catalog entries before optional tracks are loaded.
518
+ Hosts can expose that catalog immediately, fetch and register a selected asset,
519
+ then request `playBaked` through Animation. Registration is asset setup; it does
520
+ not introduce an additional scheduler or playback owner.
521
+
522
+ ## Package Distribution Contract
523
+
524
+ Only source and the hand-authored `types/index.d.ts` declarations are committed.
525
+ `pnpm build` recreates `dist`, builds the npm and worker targets once, and copies
526
+ the declarations into that staging directory. `pnpm verify` checks the
527
+ source-only rule, validates the runtime exports and packed file list, and runs
528
+ the CLJS and worker tests.
221
529
 
222
530
  The package intentionally has no `prepare`, `install`, or `postinstall` build
223
- lifecycle. Polymer CI runs the CLJS build, verifies that `dist` exposes the
224
- browser ESM API that Git-SHA consumers import, and `prepublishOnly` repeats the
225
- build/test/API check before an npm publish.
531
+ lifecycle. Consumers install an already-built npm release or a pkg.pr.new preview
532
+ for a specific commit and therefore do not need Java, Shadow CLJS, or a local
533
+ Polymer build. For work testing an upstream PR, use the full URL reported by CI:
534
+
535
+ ```text
536
+ https://pkg.pr.new/meekmachine/Polymer/@lovelace_lol/polymer@<40-character-sha>
537
+ ```
538
+
539
+ On pushes to `main`, CI prepares a stable npm version and validates the **committed**
540
+ Embody dependency before the single build. The source manifest must already pin
541
+ an exact stable Embody version, with a synchronized lockfile. CI reads public npm
542
+ metadata anonymously and checks that the selected release records a commit in
543
+ Embody's `main` history. It rejects preview URLs anywhere in the production
544
+ lockfile. Publication never replaces a preview with whatever npm calls `latest`: adopting a release is
545
+ an ordinary dependency PR, so the dependency tested by PR CI is the one shipped.
546
+ The existing preview pin remains until a fresh Embody npm release is available;
547
+ stable publication is intentionally blocked during that bootstrap.
548
+
549
+ After all checks pass, CI packs the verified output and uploads it to a separate
550
+ publish job using the main-only `npm` environment's `NPM_KEY`. That job publishes
551
+ the same tarball with public access and provenance disabled, without building
552
+ again. The tarball must also declare public access. GitHub source access remains
553
+ separate from distribution of the public compiled npm package.
554
+
555
+ Package metadata and dependency installation use the public registry without a
556
+ read token. Registry lookups also stay anonymous when a publishing credential is
557
+ present in the environment, and reject redirects to another registry endpoint.
558
+ Only the publish step receives `NPM_KEY`; PR installs and checks receive no npm
559
+ credentials. Same-repository PRs can publish previews; fork PRs can run checks
560
+ without receiving publication or coordination write credentials.
561
+
562
+ The package allowlist remains `dist` and `README.md`, alongside npm's package
563
+ metadata. The existing CI package checks require staged runtime files and reject
564
+ repository source/test inputs. Build output, source maps, minification and
565
+ obfuscation are unchanged by this release setup.
566
+
567
+ Successful stable and same-repository PR preview publication dispatch
568
+ `upstream-package-published` to LoomLarge. The write credential
569
+ `CROSS_REPO_CI_TOKEN` belongs in the main-only `release-coordination` environment;
570
+ it must read Embody for release ancestry and dispatch to LoomLarge. PR jobs only
571
+ upload notification JSON and never receive this credential. The separate
572
+ `workflow_run` consumer checks out **main**, validates the completed build job,
573
+ the JSON package identity, and the current open same-repository PR head, then
574
+ sends the event. It never executes a PR script or downloaded artifact. The build
575
+ job must succeed even when the independent production-readiness job rejects a
576
+ preview dependency. During migration the consumer accepts both the former
577
+ `CI and package preview` name and `CI and package publishing`, while requiring
578
+ the exact CI workflow path. Missing credentials or failed dispatch fail visibly.
579
+
580
+ Stable events include the exact version and source SHA; preview events include
581
+ the PR number and actual published URL, expanded to its full source SHA and
582
+ checked before dispatch. LoomLarge's coordinator owns downstream manifest/lockfile
583
+ PR updates; release-system companion PR links alone do not create package-stack
584
+ dependencies. The coordinator and trusted notification consumer must be on their
585
+ respective main branches before activation. Explicit preview requests without a
586
+ PR publish previews but do not imply a downstream stack to update.
587
+
588
+ Release versions and source commits are recorded in npm; rerunning a published
589
+ commit retains its version and exact Embody dependency. If a concurrent publisher
590
+ claims that source/version with different runtime dependencies, publication fails
591
+ instead of sending a success notification for a package the job did not verify.
592
+ Main release runs are serialized. A manual run on `main` with no alternate ref can retry publication
593
+ and notification; PRs, repository-dispatch preview requests, and alternate refs
594
+ cannot publish stable npm packages.
595
+
596
+ The first restored release reads Polymer `0.0.7`'s source from the verified
597
+ canonical `v0.0.7` tag because that legacy npm version has no `gitHead` metadata.
598
+ The tag must still point to `73feb34f8afa5a36962e86bc0f3e91f9d91205ef`, and the
599
+ new release must contain that commit. Subsequent releases use npm's recorded
600
+ `gitHead`; missing or conflicting source information stops publication.
601
+
602
+ The separate `Production npm dependency` job rejects preview pins and verifies
603
+ the committed Embody registry version before merge. A trusted main-only job checks
604
+ Embody main ancestry before publication. PR build jobs can still publish their
605
+ immutable previews independently. Bootstrap requires publishing and coordinator
606
+ credentials, an Embody main release, adoption of that actual version in Polymer's
607
+ manifest and lockfile, Polymer's main release, then adoption in LoomLarge.
608
+ Upstream feature stacks keep using immutable previews before this release sequence. Production hosts adopt
609
+ actual released versions in dependency PRs whose CI checks the selected packages.
226
610
 
227
611
  ## Worker Protocol Artifact
228
612
 
@@ -238,3 +622,41 @@ not use this artifact yet, and this PR does not decide which agencies should be
238
622
  placed in workers. Selective placement, profiling, transferables, and worker
239
623
  pool decisions remain tracked in
240
624
  [issue #62](https://github.com/meekmachine/Polymer/issues/62).
625
+
626
+ For generator modules bound to a shared character, see
627
+ [Conversation sessions and interruption](docs/conversation-interruption.md).
628
+
629
+ Presentation and Questionnaire share pure lifecycle, viewed/active context,
630
+ request correlation and opaque checkpoint helpers. See
631
+ [shared interaction contracts](docs/interaction-contracts.md) for their module
632
+ composition interface, account/mount identity and scoped cleanup rules. These
633
+ contracts do not enable a feature runtime or persist conversation/run data.
634
+
635
+ ### Semantic pose capture and generated animations
636
+
637
+ The Rust host exposes `getSemanticPoseCatalog()`, `captureSemanticPose()`,
638
+ `validateSemanticPose(pose)` and `applySemanticPose(pose)`. The catalog follows
639
+ the configured profile and actual model, including independent direction support,
640
+ VRM role names, owned bone/morph names and authored default tissue strengths.
641
+ Captures preserve both opposing directions, bilateral balance and morph strength;
642
+ they represent manual semantic state, not inverse inference of a baked pose.
643
+ Validation is atomic and lives in Embody. Known missing outputs may be saved for
644
+ partial rigs; generation should offer only supported catalog directions.
645
+
646
+ Normal `AnimationService.schedule` snippets may include `semanticAnimation`:
647
+ `{version:1,durationSeconds,tracks:[{controlId,direction,balance?,morphStrength?,
648
+ keyframes:[{time,intensity}]}]}`. Existing `curves` are non-Body facial AUs.
649
+ Embody compiles both sets together into bone/morph tracks, preserving each tissue
650
+ strength without changing live controls. Service save, pause, resume and cleanup
651
+ use the existing snippet lifecycle. `validateSemanticAnimation(animation, options)`
652
+ compiles without retaining clips. `semanticAnimationToClip(name, animation,
653
+ options)` builds an ordinary playback handle; `options.faceCurves` adds facial
654
+ curves for direct callers. IDs and direction labels come from the catalog, amounts
655
+ are normalized `0..1`, balance is character-relative `-1` left / `+1` right, and
656
+ times are seconds. Unknown controls, unsupported directions and invalid tracks fail.
657
+
658
+ Manual `setAU` / `transitionAU` selects one configured continuum direction:
659
+ nonzero input clears its opposite, negative input selects the opposite, and
660
+ zero releases only the named action. `setContinuum` controls both directions
661
+ explicitly. Body and individual AU/morph previews therefore share the same
662
+ live state; separate axes remain independent.