Epic 5 Phase 2.3 — instrument Assembly for measurement

Gallery, metadata, scene-gate and scene-sweep now handle kind:'model'
with a deterministic ActorSpec. Drawing uses a forked ActorGenerator
so the actor stream never shifts the identity/palette streams:
  gallery.renderScene → generateActor per context for model stages
  metadata.measureLibrary now includes model stages, SCHEMA 5,
    fingerprint covers actors/
  scene-gate.runSceneGate → alive/animates/deterministic/distinct/
    param-sweep/flash-rate all drive Engine.setLayerSpecs with
    actorSpec, and consumes: searches re-derive the actor for the
    swapped identity (so the consumes: gate is still testable).
  scene-sweep behaves the same per-cell.
  Engine.setLayerSpecs now forwards actorSpec/framing into createLayer
    so the checks don't need ArcDriver.

SCHEMA bump 4→5 forces metadata refresh on next gallery build,
which is intentional: model stages now participate. Gates remain
all green (69 scenes) and vite builds clean — no runtime change to
the render path for fragment stages.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Dejvino
2026-08-20 18:38:21 +02:00
co-authored by Claude
parent 16bb67e703
commit 93a5dc8437
5 changed files with 48 additions and 9 deletions
+8 -1
View File
@@ -27,6 +27,7 @@ import { describeIdentity } from '../look/Identity.js';
import { frameDescriptor, motionDescriptor } from './variety/descriptors.js';
import { frameLuminance, frameVariance } from '../engine/hash.js';
import { descriptorDistance, STRUCTURAL } from './variety/signature.js';
import { generateActor } from '../actors/ActorGenerator.js';
const THUMB = { width: 256, height: 144 };
@@ -89,6 +90,11 @@ export function renderScene(engine, module, contexts) {
const rng = new Rng(hashString(`${module.name}:${ctx.name}`));
const params = sampleValues(module, rng, ctx.bias, ctx.personality.temperament);
const actorRng = new Rng(rng.fork(`actor:${module.actor || 'model'}`).seed);
const summary = ctx.track.summary;
const actorSpec = module.kind === 'model' && module.actor
? generateActor({ summary, rng: actorRng, archetype: module.actor, personality: ctx.personality, identity: ctx.personality.identity })
: null;
engine.setLayerSpecs([{
module,
@@ -98,6 +104,7 @@ export function renderScene(engine, module, contexts) {
blend: 'normal',
palette: ctx.palette,
personality: ctx.personality,
actorSpec,
}]);
engine.compositor.reset();
// A few frames of warm-up so anything with state is past its first frame.
@@ -181,7 +188,7 @@ export function renderScene(engine, module, contexts) {
export async function buildGallery({ contexts, onScene, only = null } = {}) {
const list = only
? scenes.filter((m) => only.includes(m.name))
: scenes.filter((m) => m.kind === 'fragment');
: scenes.filter((m) => m.kind === 'fragment' || m.kind === 'model');
const engine = new Engine({ ...THUMB });
const rows = [];