* feat(web): measure a collapsed deck stage in the preview bridge
OPEND-2147 reports deck slides rendering at inconsistent scale, with content
cut off. The one confirmed sighting has resisted reproduction across seven
eliminations -- the artifact, the host injections, the layout chase, mount
timing, in-place rewrites, a hidden tab, and an older build all render the
correct scale -- which points at a race rather than a deterministic defect.
A race that cannot be reproduced has to be caught in the wild, and today it
leaves no trace: the slide is still in the DOM and still paints, so the
white-screen probe stays quiet and nothing is recorded.
Add a deck-specific probe to the preview observability bridge. When a deck's
stage sits at effectively no scale in a settled, visible, sized frame, it
reports the measurement -- resolved scale, stage rect, authored canvas size,
frame size, document state and time since the bridge started. The host files
it as its own event rather than letting it fall through to the runtime-error
path, and mirrors one flat line to console at warn level so it reaches
renderer.log and therefore od diagnostics export.
* test(web): execute the deck stage probe instead of grepping its source
Asserting the bridge source contains a string proves nothing about whether the
probe can run. The gate it sits behind starts closed and only opens when
FileViewer posts the host-state message, so a probe wired to the wrong
lifecycle would be dead code that every string-level test still passes.
Run the real bridge in a document and assert what it posts: silent until the
host reports an active preview, silent for a deck that fitted, silent for a
preview with no fixed canvas, and distinguishing a stage never fitted from one
fitted to nothing. Mutating the collapse check to never report turns two of
these red.
* fix(web): measure the deck shapes this product actually ships
The probe keyed on .stage plus a --canvas-w custom property. That is one
template shape, not the fit contract first-party decks use, so on the decks
OPEND-2147 is about it returned at the selector lookup and never sent anything
-- telemetry that is silent exactly where it was added to help.
Resolve the scaled canvas from the whole family instead: <deck-stage> keeps its
canvas in shadow DOM where document.querySelector cannot reach it, the
canonical skeleton in prompts/deck-framework.ts authors #deck-stage /
.deck-stage at a fixed size and scales it inline, and some design templates use
.stage with --canvas-w. This is the same family the export path already targets
through DECK_STAGE_SELECTOR. Size now comes from the computed width so all
three resolve through one path, and the report carries stage_kind.
--canvas-w is no longer a gate. What keeps a 100vw/100vh wrapper quiet is that
its canvas tracks the viewport, so it has no fixed canvas to fit.
Replaces the string-contains spec with a fixture matrix over the real shapes:
collapsed and healthy .deck-stage, collapsed shadow canvas, template .stage,
never-fitted, viewport wrapper, and host-gating. Deleting the .deck-stage
branch turns three of them red. The suite also now unregisters each bridge it
installs -- without that a bridge from an earlier case reports against the next
case's DOM.
* fix(web): stop a healthy sample from blinding the deck probe, and keep stage_kind
Two gaps that both cost triage value on the failure this PR exists to catch.
A healthy first sample latched the probe off for the life of the document, so a
stage that fitted and then collapsed never reported. OPEND-2147 is a race --
long generation, repeated rewrites, a backgrounded tab -- so late collapse in
the same document is the case, not an edge of it. resize and visibilitychange
already re-schedule the check; only the latch stood in the way. The white-screen
probe alongside it draws the same distinction: it returns on a healthy sample
and latches only once it has actually reported. Now this one does too.
stage_kind reached the host and died there. The bridge resolves which authored
shape collapsed -- canonical .deck-stage, <deck-stage> shadow canvas, template
.stage -- and neither the analytics event nor the warn line carried it, so an
exported log would show the scale and canvas size without saying which shape
produced them. Forwarded to both sinks.
Specs first, both red before the fix: a stage healthy at the first tick then
collapsed reports on the next trigger, and the host cases assert stage_kind on
the event and in the greppable line. Re-latching on healthy, or dropping the
field at the host, turns them back red.
* fix(web): require a declared canvas before treating .stage as a deck
Two things that would have made this instrumentation less trustworthy than no
instrumentation.
.stage is not a deck marker. Plain pages use that class too --
design-templates/social-carousel is a max-width: 1280px article shell with no
fit transform and no authored canvas -- and the probe read its computed width
as a 1280px canvas, saw transform: none, and filed a healthy render as a
collapsed deck. False positives on that path would pollute both the frequency
signal and the exported log, which is worse than being silent.
Only the shapes that are markers -- <deck-stage> shadow canvas, #deck-stage,
.deck-stage -- may take their computed width as the authored canvas. The
generic .stage fallback now has to declare one through --canvas-w / --canvas-h,
which is what the templates using that path already do.
Also restores apps/web/next-env.d.ts. A local E2E run had Next rewrite its
route-types import to a path inside .tmp/e2e/, and git add -A swept it in. That
target does not exist in a clean checkout, and the file is generator-owned.
Red spec first: a non-deck .stage at 1280px with no custom properties must stay
quiet. Restoring the computed-width fallback turns it back red.
* fix(web): make the viewport exemption two-dimensional
The exemption exists for wrappers that track the viewport, and a wrapper tracks
it in both dimensions. Comparing width alone skipped a real failure: a 1920x1080
canvas in a 1920x530 frame still has to scale, because height constrains it to
roughly 0.46. The width match returned before resolvedScale() ever ran, so an
unfitted deck in a short wide frame produced neither the analytics event nor the
exported warning -- the clipped-slide shape OPEND-2147 describes.
Now a canvas is exempt only when both dimensions match the frame and the height
is usable; anything else goes on to have its transform inspected.
Red spec first: canonical 1920x1080 .deck-stage, 1920x530 frame, transform none,
must report. Reverting to the width-only comparison turns it back red.