Skip to content

refactor(producer): make capture plans immutable#2163

Merged
jrusso1020 merged 3 commits into
mainfrom
07-10-refactor_producer_make_capture_plans_immutable
Jul 18, 2026
Merged

refactor(producer): make capture plans immutable#2163
jrusso1020 merged 3 commits into
mainfrom
07-10-refactor_producer_make_capture_plans_immutable

Conversation

@jrusso1020

@jrusso1020 jrusso1020 commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

What

Represent render routing as immutable CapturePlan variants with pure fallback transitions.

Why

The orchestrator mutated shared routing flags during fallback, making stage behavior order-dependent.

How

Add frozen SDR streaming, SDR disk, and HDR layered plans plus pure replanAfterFailure transitions consumed by stages.

Test plan

  • capture-plan transition, stage, distributed render, and orchestrator tests
  • Stack-wide lint, format, build, typecheck, and relevant integration gates

jrusso1020 commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_unify_render_requests branch from a955dbb to a7d8a90 Compare July 13, 2026 23:08
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_make_capture_plans_immutable branch from e249db4 to 7e67f17 Compare July 13, 2026 23:08
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_unify_render_requests branch from a7d8a90 to 46e7941 Compare July 14, 2026 00:03
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_make_capture_plans_immutable branch from 7e67f17 to 1bf2e0c Compare July 14, 2026 00:03
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_unify_render_requests branch from 46e7941 to eb1b74b Compare July 14, 2026 03:20
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_make_capture_plans_immutable branch from 1bf2e0c to 864beed Compare July 14, 2026 03:20
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_unify_render_requests branch from eb1b74b to 711fcef Compare July 14, 2026 17:03
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_make_capture_plans_immutable branch from 864beed to 4dd9950 Compare July 14, 2026 17:03
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_unify_render_requests branch from 711fcef to a3546c2 Compare July 14, 2026 17:11
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_make_capture_plans_immutable branch 2 times, most recently from 25bffba to 406554a Compare July 14, 2026 17:35
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_unify_render_requests branch from a3546c2 to 25a96c1 Compare July 14, 2026 17:35
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_make_capture_plans_immutable branch from 406554a to fab40bd Compare July 14, 2026 17:57
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_unify_render_requests branch from 25a96c1 to cd77f50 Compare July 14, 2026 17:57
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_make_capture_plans_immutable branch from fab40bd to a4a115e Compare July 14, 2026 18:06
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_unify_render_requests branch from cd77f50 to 1c04cc5 Compare July 14, 2026 18:06
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_make_capture_plans_immutable branch from a4a115e to 8134c99 Compare July 14, 2026 19:37
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_unify_render_requests branch from 1c04cc5 to 4b7d2bf Compare July 14, 2026 19:37
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_make_capture_plans_immutable branch 2 times, most recently from f66d284 to 7d48c5b Compare July 14, 2026 19:47
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_unify_render_requests branch 2 times, most recently from 69734b3 to e08d6df Compare July 14, 2026 19:55
@jrusso1020
jrusso1020 force-pushed the 07-10-refactor_producer_make_capture_plans_immutable branch from 7d48c5b to 2db2ea7 Compare July 14, 2026 19:55

@miguel-heygen miguel-heygen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The immutable-plan shape is strong: capturePlan.ts:74-115 freezes both the plan and nested routing target, and the stage-specific unions make HDR/streaming/disk dispatch invalid states unrepresentable at the call sites. I also verified the distributed chunk constructs the same disk-plan contract. One hostile fallback ordering is not behavior-preserving:

  • [blocker] packages/producer/src/services/renderOrchestrator.ts:2702-2717 / packages/producer/src/services/render/capturePlan.ts:135-147 — re-resolve the route after applying the OOM worker reduction. Both routing fallbacks are precomputed with isMemoryExhaustion: false, normally at the pre-routing multi-worker count, so their frozen target is sdr_disk. On a later OOM, replanAfterFailure() changes only workerCount to 1 and retains that disk kind. Exact-head falsifier:

    worker_inversion: sdr_streaming/W1 --OOM--> sdr_disk/W1
    parallel_router:  sdr_streaming/W3 --OOM--> sdr_disk/W1
    

    That differs from the pre-refactor contract still documented and implemented by resolveInversionRetryPlan() / resolveParallelRouterRetryPlan() at renderOrchestrator.ts:1251-1288,1357-1397: for OOM they first reduce to one worker and then call shouldUseStreamingEncode(..., workerCount=1), yielding the single-worker screenshot-streaming recovery route. The new test at capturePlan.test.ts:90-106 currently pins the regressed disk result.

    This matters precisely on the resource-failure path: the retry now accumulates frames for a separate disk encode instead of using the lighter one-worker streaming route selected after OOM. Store a separate memory-exhaustion target (or otherwise derive kind after the failure-specific worker count is known), and pin both inversion and parallel-router OOM transitions as sdr_streaming, worker 1, screenshot forced. Keep the ordinary non-OOM fallback at the pre-routing worker count/disk kind.

Invariant: for every routing cohort, a fallback transition must preserve the pre-refactor route selected from the actual failure category and resulting worker count; an OOM producing a different capture kind falsifies it. Contract ledger: producers are inversion/router state plus verification/capture/OOM failures; consumers are the streaming retry and disk finalizer; siblings are default, worker-inversion, and parallel-router routes; encoding/owner is the frozen CaptureRouting target plus replanAfterFailure; hostile ordering is OOM after a pinned streaming attempt; relevant history is the old failure-time resolve*RetryPlan calculation. Falsifier: the exact source transition above reproduces disk/W1 for both routed cohorts. Terminal effect: the OOM recovery takes a heavier disk-frame path and can fail or exhaust storage where the prior streaming retry would not.

Audited: complete 8-file base...head diff, every CapturePlan producer/consumer, both retry-policy helpers, stage dispatches, distributed chunk adapter, all tests, prior comments/reviews, exact head/base, and checks. Trusting: the green exact-head CI matrix rather than reinstalling the workspace in this clean review tree. Not exercised: a live Chrome OOM; the pure production transition that selects its terminal route was exercised directly.

— Magi

Verdict: REQUEST CHANGES
Reasoning: Immutability is correctly enforced, but freezing the normal fallback before the failure is known changes both routed OOM recoveries from single-worker streaming to single-worker disk. That resource-path regression needs a failure-specific immutable target before merge.

@jrusso1020

Copy link
Copy Markdown
Collaborator Author

Fixed Magi's blocker on current head a7d6f7f7dc4ed03f9d3b71f83551be01fbc3ac1c (restacked onto main 4a2c9eeed).

CaptureRouting now freezes two distinct immutable targets for routed cohorts:

  • The ordinary fallback retains the pre-routing worker count and its disk route.
  • The memory-exhaustion fallback is resolved with the actual one-worker OOM retry count, so it retains the prior sdr_streaming screenshot recovery route.

replanAfterFailure selects the failure-specific target rather than changing only the worker count. Added direct regressions for both worker-inversion and parallel-router OOM transitions (sdr_streaming, worker 1, screenshot forced), while the existing non-OOM inversion regression still pins the pre-routing disk fallback.

Validated: Producer CapturePlan + renderOrchestrator suites 150/150, CLI cloud-render focused coverage, Producer typecheck, and commit hooks (lint/format/tracked-artifact/Fallow).

@miguel-heygen miguel-heygen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed exact head a7d6f7f7d. The prior OOM-routing blocker is resolved.

The invariant is now explicit: each routed plan carries distinct immutable targets for ordinary fallback and memory-exhaustion fallback (capturePlan.ts:18-25,85-91), and replanAfterFailure selects the target from the actual failure class (capturePlan.ts:140-159). The orchestrator is the single producer and computes both targets through the existing inversion/router retry policies (renderOrchestrator.ts:2702-2769). Ordinary fallback therefore retains the pre-routing disk route and worker count; OOM re-resolves to screenshot streaming at one worker.

I verified the contract three ways:

  • Read the complete eight-file diff and the three-file blocker-fix diff, then range-diffed the restack: the original two commits are content-identical and the new commit is scoped to the fix.
  • Enumerated all CaptureRouting producers/consumers and transition owners repo-wide; every production construction supplies both targets, both nested targets are frozen, and replanAfterFailure is the sole selector.
  • Ran a direct production-code falsifier for both worker_inversion and parallel_router: ordinary failure produced sdr_disk at W5, while OOM produced sdr_streaming at W1 with screenshot capture forced. The regressions pin the ordinary path plus both OOM paths (capturePlan.test.ts:72-140).

I also evaluated the existing HDR worker-count note. Using the routed plan's worker count is a conservative single-source-of-truth choice affecting parallelism, not output correctness, and does not block this refactor.

Audited: current PR lifecycle/history, all prior reviews/comments/inline comments, exact head/base/merge base, complete diff and file surface, sibling routing variants, tests, and exact-head checks. Trusting: the fully green exact-head CI matrix rather than reinstalling and rerunning the entire workspace locally. Not exercised: a live Chrome OOM; the same production transition was exercised directly with both failure classes, which is the relevant deterministic boundary.

— Magi

Verdict: APPROVE
Reasoning: The fix restores failure-specific routing without mutation: normal fallback preserves the disk target, OOM reliably selects the frozen one-worker screenshot-streaming target, all routing producers and sibling variants are covered, and the exact-head CI matrix is green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants