feat(producer): lower parallel-DE router floor to 700 frames + power-state telemetry - #2838
Conversation
…state telemetry
HF_DE_PARALLEL_MIN_FRAMES default 2000 -> 700, re-calibrated by a controlled
crossover sweep (fixed content-per-frame, three synthetic profiles x
{350..3000f} x {single,par2,par3} x 3 reps, resolved worker counts and capture
modes verified per run): par3 beats single at EVERY size in every profile —
+17-21% at 700f rising to +28-34% at 3000f. That includes a
24-sub-composition profile built specifically to reproduce the 'workers
re-pay init' failure the original 2000 floor guarded against (92k tweens,
~2.5s pollSubCompositionTimelines per worker): workers initialize
concurrently, so duplicated init costs CPU, not wall-clock, and the comp
still parallelizes +19% at 700f. Below ~700f the win thins toward +10%
while paying three hardware-GPU browsers, so a floor remains. par2 loses to
par3 in every cell of every profile — the router's existing 3-worker pin is
confirmed, not changed. Harness:
plans/drawelement-fast-capture/de-crossover-bench.sh (docs repo).
Also adds on_battery / low_power_mode to render_complete and render_error.
The DE fleet is macOS laptops, and bench sweeps on an M4 Pro caught the SAME
render flipping between ~9.6 and ~17.2 ms/frame power-management regimes
with no existing telemetry signal to segment by — the router soak reading
this change needs that dimension to interpret perf on the machines users
actually render on. Sampled per event (volatile), pmset-based, darwin-only,
null-safe on failure.
Router stays default-off behind HF_DE_PARALLEL_ROUTER; this tunes what it
will do when the soak clears it to flip.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…hout streaming (review) Two review findings on the floor/telemetry PR: 1. powerStateFields() is spread into the properties object at the CALL SITE, so it ran before trackEvent's own `if (!shouldTrack()) return` guard — telemetry-disabled installs paid two blocking `pmset` subprocess spawns per render for an event that was then discarded. Now short-circuits on shouldTrack() (memoized, so no cost on the tracked path). Regression test asserts pmset is not sampled when telemetry is off; fault-injection verified it fails without the guard. 2. The DE parallel router pinned workerCount to 3 and skipped calibration even when verified parallel DE STREAMING — the entire reason for the pin — could not run for that render. The common case is a composition over streamingEncodeMaxDurationSeconds (240 s default): the duration cap disables streaming before the router's force flag is consulted, so the render got a hard-coded 3 workers chosen by a benchmark for a path it was not on, instead of the calibrated count. shouldPreferParallelDrawElement now takes parallelStreamingAvailable and withholds the bet without it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
miga-heygen
left a comment
There was a problem hiding this comment.
SSOT -- new surface inventory
| Symbol | Kind | Scope | Notes |
|---|---|---|---|
PowerState |
interface | cli/telemetry/system.ts |
{ on_battery: boolean | null, low_power_mode: boolean | null } |
getPowerState() |
exported fn | cli/telemetry/system.ts |
Samples pmset on darwin, null elsewhere |
parsePmsetPowerSource() |
exported fn | cli/telemetry/system.ts |
Exported for test seam only |
powerStateFields() |
private fn | cli/telemetry/events.ts |
Converts PowerState nulls to absent-property undefined for PostHog |
parallelStreamingAvailable |
new required param | shouldPreferParallelDrawElement |
Gates the router on streaming actually being viable |
HF_DE_PARALLEL_MIN_FRAMES default |
changed | executeRenderPipeline |
2000 -> 700 |
No duplicated decisions across files. The 700 literal appears twice in renderOrchestrator.ts (one for undefined/empty env var, one for NaN/Infinity fallback) -- same pattern as the prior 2000. A named const would be tidier but it is localized to a single validation chain and not a real SSOT risk.
Comments and docstrings updated to reflect the new calibration rationale. No stale references to the old 2000 threshold remain in the touched files.
CI status
| Check | Result |
|---|---|
| Build, Lint, Format, Semantic PR title | pass |
| Producer: unit + integration tests | pass |
| CLI smoke, npx shim (all 3 OS) | pass |
| CodeQL, Fallow audit, SDK, Studio, Player, Runtime contract | pass |
| Regression shards (7/9 complete) | pass (2 still running) |
| Typecheck | FAIL |
Blocking: Typecheck failure -- missing required gpu in test
The two new trackRenderComplete calls in the power-state opt-out test are missing the required gpu: boolean property:
events.test.ts(728): error TS2345: Property 'gpu' is missing in type
'{ durationMs: number; fps: number; quality: string; docker: false; }'
events.test.ts(741): same
Both calls need gpu: false (or gpu: true) added:
// line ~728
trackRenderComplete({ durationMs: 1, fps: 30, quality: "high", docker: false, gpu: false });
// line ~741
trackRenderComplete({ durationMs: 1, fps: 30, quality: "high", docker: false, gpu: false });This is the only CI failure.
Side-effect audit: power-state sampling
The getPowerState() implementation is solid:
- Platform gate: early-returns
{ null, null }off-darwin. No guessing on Linux/Windows. - Two independent try/catch blocks:
pmset -g battfor power source,pmset -gfor low-power mode. One failing does not poison the other. - Timeout: both
execSynccalls carry{ timeout: 2000 }.pmsetis near-instant (<10ms), so 2s is a generous safety net without risking a hang. - Opt-out guard:
powerStateFields()checksshouldTrack()before callinggetPowerState(), preventing subprocess spawns for opted-out installs. There is a dedicated regression test for this ("does NOT spawn pmset when telemetry is disabled"). This is especially important because the spread happens at the call site, BEFOREtrackEvent's ownshouldTrackguard. - Null-to-undefined conversion:
power.on_battery ?? undefinedcorrectly convertsnull(undetectable) to property-absent for PostHog, which treatsnullas a real value. Clean.
No concerns here.
parallelStreamingAvailable gate
The concept is sound: the router's entire value is the streaming path, so pinning 3 workers without streaming benefit is pure cost. Verified that shouldUseStreamingEncode on main already accepts the 5th forceParallelStream parameter (default false), so the call site passing true is correct. The gate checks the duration cap, format exclusions, and streaming config -- all the reasons streaming might be off despite the router wanting it.
Test coverage: the new "withholds the bet when parallel streaming can't run" test verifies the gate in shouldPreferParallelDrawElement. The call-site wiring (computing parallelStreamingAvailable from shouldUseStreamingEncode) is integration-level and covered by the existing router integration tests.
Spec vs implementation
| PR claim | Verified? |
|---|---|
Lowers HF_DE_PARALLEL_MIN_FRAMES default from 2000 to 700 |
Yes -- both fallback values changed |
Adds on_battery/low_power_mode to render telemetry |
Yes -- spread into both render_complete and render_error events |
| pmset-based, darwin-only, sampled per event | Yes -- platform gated, not cached, per-event call |
| Router stays default-off | Yes -- HF_DE_PARALLEL_ROUTER === "true" check is unchanged |
All claims match the implementation.
Verdict
Changes Requested -- fix the Typecheck failure (add gpu to both test calls), then this is ready. The power-state telemetry is well-implemented and the router threshold change is well-motivated by the benchmark data.
-- Miga
trackRenderComplete requires `gpu: boolean`; the two new opt-out test calls omitted it, failing Typecheck in CI. The fix already existed on the stacked branch, so only this base branch was broken. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
james-russo-rames-d-jusso
left a comment
There was a problem hiding this comment.
Reviewed at cddc90ae — restacked mid-review from b99c8038 with cddc90ae3 fix(cli): add required gpu prop to power-state test calls, closing what I had been going to flag as the Typecheck blocker.
Floor drop 2000→700 is the meat; power-state telemetry (powerStateFields() at packages/cli/src/telemetry/events.ts:20-27) is a clean spread with the shouldTrack() guard correctly landed AT the call site so opted-out installs no longer pay two pmset spawns per render. Router-pin fix classified as NARROW — new parallelStreamingAvailable field on the eligibility predicate at renderOrchestrator.ts:1334-1343; pin itself (ROUTER_WORKER_COUNT = 3 at line 2537-2544) is unchanged. Battery telemetry emitted on both render_complete (events.ts:301) and render_error (events.ts:382).
Blockers
None at the current head. The Typecheck failure on the prior head (b99c8038, run 30340557122) — 2 new test call sites at events.test.ts:675, 688 in the describe("power-state sampling respects the telemetry opt-out") block missing the required gpu: boolean field on the payload — has been self-fixed by cddc90ae3 before I posted this review. The commit message even calls out "the fix already existed on the stacked branch, so only this base branch was broken" — noted.
Concerns
1. Empirical harness referenced in code but missing from the tree. renderOrchestrator.ts:2329-2340 cites the block-level rationale for the 700f floor and points to plans/drawelement-fast-capture/de-crossover-bench.sh as the source of the +17–21% at 700f rising to +28–34% at 3000f win-margin numbers and the 24-sub-composition, ~2.5s pollSubCompositionTimelines per worker profile. But find /tmp/pr-2838/plans shows only 2026-07-13-studio-flat-inspector-validation-session.md — no drawelement-fast-capture/ directory, no de-crossover-bench* anywhere. A measurement claim (crossover sweep) needs a reproducible measurement — either commit the harness + a result artifact under plans/drawelement-fast-capture/, or drop the reference and let the comment stand on its own summary.
2. 350f-crossover claim vs 700f floor: no per-profile decomposition of the 350–700f margin band. Same code block claims par3 beats single at every size ≥350f but the floor is 700 because "below ~700f the win thins toward ~+10% while still paying 3 hardware-GPU browsers." That 350-frame margin is where a bimodal profile could invert — the comment gives one aggregated range (+17–21% at 700f) but no per-profile floor. Specifically, the "24-sub-composition init-expensive" profile called out at renderOrchestrator.ts:1300-1302 — which was explicitly built to threaten the drop — has zero per-size numbers in the 350–700f band. Worth asking: what's the worst per-profile 350f margin, and does 700 cover the pessimistic case with margin, or was it chosen to make the aggregate look good?
3. Stale test rationale + no coverage on the new default. renderOrchestrator.test.ts:1912-1915 still says benchmark: real-work comps clear 1.25x at ~2,000+ frames — pre-recalibration language. The fixture at renderOrchestrator.test.ts:1839 still hard-codes minFrames: 2000, and no test asserts the new 700 default. Minimum ask: update the comment to match the current threshold + rationale. Better ask: add table-driven cases in the 350–700f band that lock the crossover-vs-floor decision in a place a reviewer can find later — shouldPreferParallelDrawElement({...eligible, totalFrames: 700}) === true and ... totalFrames: 699 === false at minimum.
Nits
- No
TODO/FIXME/NOTEmarkers in the touched files — clean. - No sibling parallel-DE floor constants that would need to move with this one (sibling
HF_DE_SINGLE_MIN_FRAMES=900atrenderOrchestrator.ts:2303intentionally crosses per docstring at 1314;MIN_FRAMES_PER_WORKER=30atparallelCoordinator.ts:131is per-worker chunking, orthogonal).
Questions
None.
What I didn't verify
- CI at the new head
cddc90ae— the fix landed after I pulled logs; trusting Typecheck will be green on rerun given the diff is a strict subset of what CI already reported as the failing sites. - Whether dropping the frame floor changes the memory-pressure profile enough to matter (larger fraction of small renders will now spin 3 hardware-GPU browsers —
HF_DE_PARALLEL_MIN_MEM_MB=24576is orthogonal per the code but the interaction wasn't checked).
|
Fixed in
Typecheck clean locally across cli/producer/engine; 39 telemetry tests pass. On the |
…ower_parallel-de_router_floor_to_700_frames_power-state_telemetry # Conflicts: # packages/cli/src/telemetry/events.ts
miguel-heygen
left a comment
There was a problem hiding this comment.
Re-reviewed the authoritative six-file diff at 011f46bc1844f0bc2975170f3552c2671edf29b9 after the restack onto main.
The strongest part is the routing guard: parallelStreamingAvailable is enforced in the predicate (packages/producer/src/services/renderOrchestrator.ts:1370-1394) and computed at the only production caller with the actual 3-worker/forced-stream configuration (:2484-2492), so the 700-frame floor cannot pin three browsers when the streaming path is unavailable. The power telemetry is similarly fail-soft and privacy-aware: non-darwin returns null without spawning (packages/cli/src/telemetry/system.ts:217-238), the opt-out guard sits before the subprocess calls (packages/cli/src/telemetry/events.ts:20-26), and both success and error events receive the fields (:325, :405).
The prior Typecheck blocker is resolved at the correct test call sites. I ran the focused suites at this exact head: CLI telemetry 50/50 and producer orchestrator 147/147 passed. Build, Typecheck, Test, producer unit/integration, CLI smoke, CodeQL, lint, and format are green; the fresh Windows and regression jobs are still pending, so merge should wait for those required checks.
I am not duplicating Rames’s existing non-blocking findings: the code comment still references a benchmark harness absent from the tree (renderOrchestrator.ts:2444), and the predicate test still carries the old 2,000-frame rationale/default rather than pinning 699/700 (renderOrchestrator.test.ts:1840,1913-1915). One additional coverage nit: the new telemetry test asserts the fields on render_complete, but not independently on render_error; the production spread is present at events.ts:405, so this is not blocking.
Verdict: APPROVE
Reasoning: The behavior change is correctly gated at the sole production seam, power sampling fails soft and respects telemetry opt-out, the previous type failure is fixed, and focused tests pass; only already-recorded reproducibility/coverage follow-ups and still-running CI remain.
— Magi
What
HF_DE_PARALLEL_MIN_FRAMESdefault 2000 → 700 — the frame floor above which the (default-off)HF_DE_PARALLEL_ROUTERroutes auto multi-worker renders to verified parallel drawElement streaming.on_battery/low_power_modeadded torender_completeandrender_errortelemetry (pmset-based, darwin-only, sampled per event, null-safe).Why 700
The 2000 default was an explicit safe-high guess ("targets the long tail the inversion's benchmark didn't cover"). A controlled crossover sweep re-calibrated it: fixed content-per-frame synthetic comps, three profiles × {350, 700, 1200, 2000, 3000}f × {single, par2, par3} × 3 reps, with resolved worker counts and capture modes verified per run (earlier sweeps were invalidated by silent worker clamps and a CDN-fetch confound — the harness now records both).
par3 vs single, within-regime, total wall-clock:
The subcomp profile was built specifically to reproduce the "workers re-pay init" failure the 2000 floor guarded against: 24 external sub-compositions, 92k tweens, ~2.5s
pollSubCompositionTimelinesper worker. It still parallelizes +19% at 700f — workers initialize concurrently, so duplicated init costs CPU, not wall-clock. Below ~700f the win thins toward +10% while paying three hardware-GPU browsers, so a floor remains. par2 lost to par3 in every cell of every profile; the router's existing 3-worker pin is confirmed unchanged.Benchmarked on an M4 Pro MacBook — deliberately, since the DE path only engages on macOS+hardware-GPU and the real fleet is laptops.
Why the power-state telemetry
Those same sweeps caught the SAME render flipping between ~9.6 and ~17.2 ms/frame with no existing telemetry able to explain it (not load, not thermal) — laptop power management is a ~1.8× perf dimension we currently can't segment by. The router soak that gates flipping
HF_DE_PARALLEL_ROUTERon needs this field to interpret wild-fleet perf: renders now report whether they ran on battery and in Low Power Mode.Safety
🤖 Generated with Claude Code