feat(producer): surface init telemetry from parallel workers — the band's missing motion axis

The routing surface the short-comp benchmarks validated is (motion x DOM
size x frames). After the baseline release, fleet telemetry carries DOM
size (composition_element_count) and frames on every render — but the
motion proxy, observability_init_tween_count, has 0% coverage on the
exact renders the band routes: parallel workers' console buffers (and so
the [FrameCapture:INIT] line the summary parses) only propagate to the
orchestrator on FAILURE. Single-worker screenshot renders report it;
the multi-worker clamp bucket never does. Verified against 7d of fleet
data: 35k screenshot renders carry tween counts, 0 of 9,600 band renders.

Fix rides the one channel parallel workers already return on success —
the per-worker CapturePerfSummary. Sessions record initTelemetry on
every init path; the perf summary now carries it; the orchestrator
max-merges across workers (same multi-session semantics the console
parser uses) and feeds it to the observability summary as a structured
fallback, console lines still refining when present.

With this, every band render carries full coordinates — (elements,
tweens, frames, path, speed) — which buys two reads: regressing wild DE
speed against element count on the existing 900+ inversions validates
the bench's 0.50ms/element slope BEFORE the routing flip, and any
post-flip misroute can be reproduced locally by feeding its telemetry
row straight into gen-crossover-comp's knobs (--movers ~ tween count,
--static ~ element count) and re-benching.

(Also drops a now-stale fallow suppression in render.ts — the test-only
reset export it guarded gained real test importers, so the issue it
suppressed no longer exists and the gate flags the leftover.)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vance Ingalls
2026-07-29 10:54:24 -07:00
co-authored by Claude Opus 5
parent e9de2fa14f
commit 23854f7c6a
7 changed files with 115 additions and 14 deletions
@@ -407,3 +407,34 @@ describe("RenderObservabilityRecorder", () => {
);
});
});
describe("init observability fallback (parallel workers)", () => {
const makeRecorder = () =>
new RenderObservabilityRecorder({ renderJobId: "render-par", pipelineStartMs: Date.now() });
it("uses the structured fallback when the console has no INIT line — the parallel success path", () => {
const summary = makeRecorder().summary({
lastBrowserConsole: ["[FrameCapture:NAV] page.goto start"],
capture: { forceScreenshot: false, captureMode: "screenshot" },
initFallback: { initDurationMs: 850, tweenCount: 1200 },
});
expect(summary.init).toEqual({ initDurationMs: 850, tweenCount: 1200 });
});
it("max-merges console INIT lines over the fallback, matching multi-session semantics", () => {
const summary = makeRecorder().summary({
lastBrowserConsole: ["[FrameCapture:INIT] complete initDurationMs=1234 tweenCount=42"],
capture: { forceScreenshot: false, captureMode: "screenshot" },
initFallback: { initDurationMs: 850, tweenCount: 1200 },
});
expect(summary.init).toEqual({ initDurationMs: 1234, tweenCount: 1200 });
});
it("stays undefined when neither source has anything", () => {
const summary = makeRecorder().summary({
lastBrowserConsole: [],
capture: { forceScreenshot: false, captureMode: "screenshot" },
});
expect(summary.init).toBeUndefined();
});
});
@@ -270,20 +270,26 @@ function readUnsignedIntAfter(line: string, prefix: string): number | undefined
return digits > 0 ? value : undefined;
}
function summarizeInitObservability(lines: string[]): RenderInitObservability | undefined {
let initDurationMs: number | undefined;
let tweenCount: number | undefined;
/** Max of two optional readings — multiple worker/session INIT records can appear; keep the worst. */
function maxReading(current: number | undefined, next: number | undefined): number | undefined {
if (next === undefined) return current;
return current === undefined ? next : Math.max(current, next);
}
function summarizeInitObservability(
lines: string[],
fallback?: RenderInitObservability,
): RenderInitObservability | undefined {
// Console parsing only sees THIS process's session buffer, so parallel
// workers' INIT lines never reach it — their init telemetry arrives
// structured via the per-worker perf summaries instead. Seed with that and
// let the console parse (same max semantics) refine it.
let initDurationMs: number | undefined = fallback?.initDurationMs;
let tweenCount: number | undefined = fallback?.tweenCount;
for (const line of lines) {
if (!line.includes("[FrameCapture:INIT]")) continue;
const duration = readUnsignedIntAfter(line, "initDurationMs=");
const tweens = readUnsignedIntAfter(line, "tweenCount=");
// Multiple worker/session INIT records can appear; keep the worst observed startup cost.
if (duration !== undefined) {
initDurationMs = initDurationMs === undefined ? duration : Math.max(initDurationMs, duration);
}
if (tweens !== undefined) {
tweenCount = tweenCount === undefined ? tweens : Math.max(tweenCount, tweens);
}
initDurationMs = maxReading(initDurationMs, readUnsignedIntAfter(line, "initDurationMs="));
tweenCount = maxReading(tweenCount, readUnsignedIntAfter(line, "tweenCount="));
}
if (initDurationMs === undefined && tweenCount === undefined) return undefined;
return { initDurationMs, tweenCount };
@@ -402,6 +408,8 @@ export class RenderObservabilityRecorder {
summary(input: {
lastBrowserConsole: string[];
capture: RenderCaptureObservability;
/** Structured init telemetry from per-worker perf summaries — the only success-path channel parallel workers have (their console buffers propagate on failure only). */
initFallback?: RenderInitObservability;
extraction?: RenderExtractionObservability;
compositionHash?: string;
}): RenderObservabilitySummary {
@@ -416,7 +424,7 @@ export class RenderObservabilityRecorder {
browserDiagnostics: summarizeBrowserDiagnostics(input.lastBrowserConsole),
capture: { ...input.capture },
extraction: input.extraction ? { ...input.extraction } : undefined,
init: summarizeInitObservability(input.lastBrowserConsole),
init: summarizeInitObservability(input.lastBrowserConsole, input.initFallback),
};
}
@@ -36,6 +36,7 @@ import {
shouldRetryViaPinnedFallback,
countElementTags,
envInt,
mergeWorkerInitObservability,
shouldPreferParallelDrawElement,
shouldPreferSingleWorkerDrawElement,
shouldStreamParallelCapture,
@@ -1761,6 +1762,23 @@ describe("shouldPreferSingleWorkerDrawElement (DE priority inversion)", () => {
});
});
describe("mergeWorkerInitObservability", () => {
it("max-merges across workers and ignores workers that reported nothing", () => {
expect(
mergeWorkerInitObservability([
{ initDurationMs: 400, initTweenCount: 900 },
{},
{ initDurationMs: 1250, initTweenCount: 880 },
]),
).toEqual({ initDurationMs: 1250, tweenCount: 900 });
});
it("returns undefined when no worker reported — summary.init must stay absent, not zeroed", () => {
expect(mergeWorkerInitObservability([])).toBeUndefined();
expect(mergeWorkerInitObservability([{}, {}])).toBeUndefined();
});
});
describe("countElementTags", () => {
it("counts closing tags", () => {
expect(countElementTags("<div><span>a</span></div>")).toBe(2);
@@ -1266,6 +1266,35 @@ export function countElementTags(html: string): number {
return matches === null ? 0 : matches.length;
}
/**
* Max-merge init telemetry across per-worker capture perf summaries the
* success-path channel for PARALLEL renders, whose worker console buffers
* (and so the `[FrameCapture:INIT]` line) only propagate on failure. Max
* matches summarizeInitObservability's own multi-session semantics: keep the
* worst observed startup cost, and tween count is per-composition so any
* worker's reading is the reading.
*/
export function mergeWorkerInitObservability(
perfs: ReadonlyArray<{ initDurationMs?: number; initTweenCount?: number }>,
): { initDurationMs?: number; tweenCount?: number } | undefined {
let initDurationMs: number | undefined;
let tweenCount: number | undefined;
for (const perf of perfs) {
if (perf.initDurationMs !== undefined) {
initDurationMs =
initDurationMs === undefined
? perf.initDurationMs
: Math.max(initDurationMs, perf.initDurationMs);
}
if (perf.initTweenCount !== undefined) {
tweenCount =
tweenCount === undefined ? perf.initTweenCount : Math.max(tweenCount, perf.initTweenCount);
}
}
if (initDurationMs === undefined && tweenCount === undefined) return undefined;
return { initDurationMs, tweenCount };
}
/**
* DE priority inversion predicate: should an AUTO-resolved multi-worker render
* drop to single-worker verified drawElement streaming?
@@ -3600,6 +3629,7 @@ async function executeRenderPipeline(input: {
const observabilitySummary = observability.summary({
lastBrowserConsole,
capture: captureObservability,
initFallback: mergeWorkerInitObservability(dedupPerfs),
extraction: extractionObservability,
compositionHash,
});
@@ -3761,6 +3791,7 @@ async function executeRenderPipeline(input: {
const observabilitySummary = observability.summary({
lastBrowserConsole,
capture: captureObservability,
initFallback: mergeWorkerInitObservability(dedupPerfs),
extraction: extractionObservability,
compositionHash,
});