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
-1
View File
@@ -1109,7 +1109,6 @@ let deParallelRouterTrialFiredThisProcess = false;
* resetting outside a test process where many independent test cases share * resetting outside a test process where many independent test cases share
* one imported module instance. * one imported module instance.
*/ */
// fallow-ignore-next-line unused-export
export function __resetDeParallelRouterTrialStateForTests(): void { export function __resetDeParallelRouterTrialStateForTests(): void {
deParallelRouterTrialManagedByUs = false; deParallelRouterTrialManagedByUs = false;
deParallelRouterTrialFiredThisProcess = false; deParallelRouterTrialFiredThisProcess = false;
@@ -3786,6 +3786,8 @@ export function getCapturePerfSummary(session: CaptureSession): CapturePerfSumma
p95TotalMs: percentileOf(session.capturePerf.frameMs, 0.95), p95TotalMs: percentileOf(session.capturePerf.frameMs, 0.95),
p99TotalMs: percentileOf(session.capturePerf.frameMs, 0.99), p99TotalMs: percentileOf(session.capturePerf.frameMs, 0.99),
subTimelineWaitOutcome: session.subTimelineWaitOutcome, subTimelineWaitOutcome: session.subTimelineWaitOutcome,
initDurationMs: session.initTelemetry?.initDurationMs,
initTweenCount: session.initTelemetry?.tweenCount,
warnings: cloneCaptureWarnings(session.warnings), warnings: cloneCaptureWarnings(session.warnings),
staticDedupReused: session.staticDedupCount ?? 0, staticDedupReused: session.staticDedupCount ?? 0,
staticDedupEnabled: session.staticDedupEnabled ?? false, staticDedupEnabled: session.staticDedupEnabled ?? false,
+12
View File
@@ -241,6 +241,18 @@ export interface CapturePerfSummary {
p99TotalMs: number; p99TotalMs: number;
/** Sub-composition timeline wait outcome (absent pre-init). */ /** Sub-composition timeline wait outcome (absent pre-init). */
subTimelineWaitOutcome?: SubTimelineWaitOutcome; subTimelineWaitOutcome?: SubTimelineWaitOutcome;
/**
* Session init telemetry, mirrored from the `[FrameCapture:INIT]` console
* line so PARALLEL workers report it too: worker sessions' console buffers
* only propagate to the orchestrator on failure, which left the
* multi-worker path the short-comp band's entire population with 0%
* coverage of the motion axis (`observability_init_tween_count`) in fleet
* telemetry. Riding the perf summary reuses the one channel that already
* flows back per worker on success.
*/
initDurationMs?: number;
/** GSAP tween count at init — the motion-axis signal for capture routing analysis. */
initTweenCount?: number;
/** Correctness warnings observed before or during capture. */ /** Correctness warnings observed before or during capture. */
warnings?: CaptureWarning[]; warnings?: CaptureWarning[];
/** /**
@@ -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; return digits > 0 ? value : undefined;
} }
function summarizeInitObservability(lines: string[]): RenderInitObservability | undefined { /** Max of two optional readings — multiple worker/session INIT records can appear; keep the worst. */
let initDurationMs: number | undefined; function maxReading(current: number | undefined, next: number | undefined): number | undefined {
let tweenCount: 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) { for (const line of lines) {
if (!line.includes("[FrameCapture:INIT]")) continue; if (!line.includes("[FrameCapture:INIT]")) continue;
const duration = readUnsignedIntAfter(line, "initDurationMs="); initDurationMs = maxReading(initDurationMs, readUnsignedIntAfter(line, "initDurationMs="));
const tweens = readUnsignedIntAfter(line, "tweenCount="); tweenCount = maxReading(tweenCount, 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);
}
} }
if (initDurationMs === undefined && tweenCount === undefined) return undefined; if (initDurationMs === undefined && tweenCount === undefined) return undefined;
return { initDurationMs, tweenCount }; return { initDurationMs, tweenCount };
@@ -402,6 +408,8 @@ export class RenderObservabilityRecorder {
summary(input: { summary(input: {
lastBrowserConsole: string[]; lastBrowserConsole: string[];
capture: RenderCaptureObservability; 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; extraction?: RenderExtractionObservability;
compositionHash?: string; compositionHash?: string;
}): RenderObservabilitySummary { }): RenderObservabilitySummary {
@@ -416,7 +424,7 @@ export class RenderObservabilityRecorder {
browserDiagnostics: summarizeBrowserDiagnostics(input.lastBrowserConsole), browserDiagnostics: summarizeBrowserDiagnostics(input.lastBrowserConsole),
capture: { ...input.capture }, capture: { ...input.capture },
extraction: input.extraction ? { ...input.extraction } : undefined, extraction: input.extraction ? { ...input.extraction } : undefined,
init: summarizeInitObservability(input.lastBrowserConsole), init: summarizeInitObservability(input.lastBrowserConsole, input.initFallback),
}; };
} }
@@ -36,6 +36,7 @@ import {
shouldRetryViaPinnedFallback, shouldRetryViaPinnedFallback,
countElementTags, countElementTags,
envInt, envInt,
mergeWorkerInitObservability,
shouldPreferParallelDrawElement, shouldPreferParallelDrawElement,
shouldPreferSingleWorkerDrawElement, shouldPreferSingleWorkerDrawElement,
shouldStreamParallelCapture, 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", () => { describe("countElementTags", () => {
it("counts closing tags", () => { it("counts closing tags", () => {
expect(countElementTags("<div><span>a</span></div>")).toBe(2); 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; 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 * DE priority inversion predicate: should an AUTO-resolved multi-worker render
* drop to single-worker verified drawElement streaming? * drop to single-worker verified drawElement streaming?
@@ -3600,6 +3629,7 @@ async function executeRenderPipeline(input: {
const observabilitySummary = observability.summary({ const observabilitySummary = observability.summary({
lastBrowserConsole, lastBrowserConsole,
capture: captureObservability, capture: captureObservability,
initFallback: mergeWorkerInitObservability(dedupPerfs),
extraction: extractionObservability, extraction: extractionObservability,
compositionHash, compositionHash,
}); });
@@ -3761,6 +3791,7 @@ async function executeRenderPipeline(input: {
const observabilitySummary = observability.summary({ const observabilitySummary = observability.summary({
lastBrowserConsole, lastBrowserConsole,
capture: captureObservability, capture: captureObservability,
initFallback: mergeWorkerInitObservability(dedupPerfs),
extraction: extractionObservability, extraction: extractionObservability,
compositionHash, compositionHash,
}); });