Problem
applyStreamedResolve (the client router's streamed-Suspense applier) silently drops a resolve when its boundary placeholder #sN is not in the DOM (if (!boundary) return;). The drop itself is the CORRECT designed posture: every legitimately-reachable absent case is benign (a superseded nav fenced by isCurrent(), a discarded revalidation, a degrading full page load, a frame-missing no-swap). But the silence has a real cost: when a BUG wrongly makes a valid boundary absent, the only symptom is a mysteriously stuck skeleton with zero signal. That is exactly how the #1048 view-transition race stayed hidden and took a full live-repro investigation to diagnose. A one-line dev console warning would have surfaced it on the first glance.
This is an observability follow-up from the PR #1049 review discussion, NOT a behavior change. Do not add a retry or apply-anyway path: a retry would run outside the streamer's nav-token fence and could splice a superseded nav's content into a recycled boundary id (boundary ids are per-render sequential).
Design / approach
Mirror the router's existing degradation-diagnostics pattern, devWarnFallback (packages/core/src/router-client.js ~L775): dev-only (process.env.NODE_ENV === 'production' early-return), warnOnce-keyed so a burst of dropped boundaries does not spam, message naming the dropped boundary id and the likely causes, e.g. [webjs] dropped streamed Suspense resolve "s1": no #s1 boundary in the DOM. Benign if this navigation was superseded or degraded; a stuck skeleton here means the shell swap did not place the boundary.
Keep the drop itself byte-identical (return unchanged). The warning goes in the !boundary branch only.
Implementation notes (for the implementing agent)
- Where to edit:
packages/core/src/router-client.js applyStreamedResolve (~L4093, the if (!boundary) return; branch, which already carries a comment block explaining why the drop is deliberate; extend it rather than contradicting it). devWarnFallback (~L775) and warnOnce are in the same file and show the exact dev-gating + once-keying idiom; key the warn on the boundary id (stream-drop:${id}).
- Landmines:
- Do NOT warn on the
isCurrent()-fenced skips in streamBoundariesProgressively (~L4110): a superseded nav's skipped applies are routine, not diagnostic. Only the reached-the-DOM-and-found-no-boundary case warns.
_resetWarnOnce is exported for tests (packages/core/test/routing/router-client.test.js uses it); reuse it so the new warn is testable deterministically.
- The function also runs during background revalidation paths; the warn must never throw and never run in production (the
devWarnFallback gate handles both).
- Invariants:
.js + JSDoc in packages/; no behavior change to the drop; client-router change, so a browser assertion is preferred, but a pure console-warn addition is observable in the node/linkedom harness too (stub console.warn), which matches how other warn-once tests in router-client.test.js assert.
- Tests: node test asserting (a) a dropped resolve emits exactly one dev warning naming the id, (b) a second drop of the same id does not re-warn, (c)
NODE_ENV=production emits nothing, and (d) a successful resolve emits nothing. Counterfactual: the test fails when the warn line is removed.
- Docs: none (dev-only diagnostics; no public surface change). No scaffold/MCP/editor surface.
Acceptance criteria
Problem
applyStreamedResolve(the client router's streamed-Suspense applier) silently drops a resolve when its boundary placeholder#sNis not in the DOM (if (!boundary) return;). The drop itself is the CORRECT designed posture: every legitimately-reachable absent case is benign (a superseded nav fenced byisCurrent(), a discarded revalidation, a degrading full page load, a frame-missing no-swap). But the silence has a real cost: when a BUG wrongly makes a valid boundary absent, the only symptom is a mysteriously stuck skeleton with zero signal. That is exactly how the #1048 view-transition race stayed hidden and took a full live-repro investigation to diagnose. A one-line dev console warning would have surfaced it on the first glance.This is an observability follow-up from the PR #1049 review discussion, NOT a behavior change. Do not add a retry or apply-anyway path: a retry would run outside the streamer's nav-token fence and could splice a superseded nav's content into a recycled boundary id (boundary ids are per-render sequential).
Design / approach
Mirror the router's existing degradation-diagnostics pattern,
devWarnFallback(packages/core/src/router-client.js~L775): dev-only (process.env.NODE_ENV === 'production'early-return),warnOnce-keyed so a burst of dropped boundaries does not spam, message naming the dropped boundary id and the likely causes, e.g.[webjs] dropped streamed Suspense resolve "s1": no #s1 boundary in the DOM. Benign if this navigation was superseded or degraded; a stuck skeleton here means the shell swap did not place the boundary.Keep the drop itself byte-identical (return unchanged). The warning goes in the
!boundarybranch only.Implementation notes (for the implementing agent)
packages/core/src/router-client.jsapplyStreamedResolve(~L4093, theif (!boundary) return;branch, which already carries a comment block explaining why the drop is deliberate; extend it rather than contradicting it).devWarnFallback(~L775) andwarnOnceare in the same file and show the exact dev-gating + once-keying idiom; key the warn on the boundary id (stream-drop:${id}).isCurrent()-fenced skips instreamBoundariesProgressively(~L4110): a superseded nav's skipped applies are routine, not diagnostic. Only the reached-the-DOM-and-found-no-boundary case warns._resetWarnOnceis exported for tests (packages/core/test/routing/router-client.test.jsuses it); reuse it so the new warn is testable deterministically.devWarnFallbackgate handles both)..js+ JSDoc inpackages/; no behavior change to the drop; client-router change, so a browser assertion is preferred, but a pure console-warn addition is observable in the node/linkedom harness too (stubconsole.warn), which matches how other warn-once tests inrouter-client.test.jsassert.NODE_ENV=productionemits nothing, and (d) a successful resolve emits nothing. Counterfactual: the test fails when the warn line is removed.Acceptance criteria