Skip to content

feat(cli): coordinate-frame layout findings in check#2354

Merged
xuanruli merged 6 commits into
mainfrom
feat/check-coordinate-frame-rules
Jul 13, 2026
Merged

feat(cli): coordinate-frame layout findings in check#2354
xuanruli merged 6 commits into
mainfrom
feat/check-coordinate-frame-rules

Conversation

@xuanruli

@xuanruli xuanruli commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Problem

Four production compositions (HeyGen Video Agent / Zephyr, 07-07/08) shipped with 100–600px layout drift. Each was a different coordinate-frame confusion, and check graded every one ℹ info — or saw nothing at all:

composition mechanism drift check before this PR
24h clock diagram viewport pixels written as container left/top node orbit +610px; node clipped by canvas edge node itself: silence (parent doesn't clip); only the chasing connectors surfaced, as info
Submit/Review/Revise loop gsap x/y (translate) treated as absolute position nodes +260/+210, detached from their arrows silence
framed painting -350px margin fighting flex centering hero art off-canvas top ~140px, whole scene canvas_overflow ℹ info → Check passed
research network stage-relative path coords drawn into a nested SVG line web +80/+227, fully detached from nodes container_overflow ℹ (SVG clips), buried

Fix — three new layout findings + one promotion

  • escaped_container (warning): a position:absolute element rendering mostly outside its offsetParent (transform-aware containing block). position:fixed is deliberately skipped — fixed-as-canvas-coords is correct HF authoring. Fully-detached callouts within an attachment allowance are exempt; touching-but-mostly-outside drift still flags. The offset parent needs no overflow clipping — which is exactly what let container_overflow miss the escaped nodes.
  • panel_out_of_canvas (warning for hero-sized ≥10% of canvas, info for small bleeds): a painted panel breaching the canvas. Division of labor: a breaching text range belongs to canvas_overflow, media to frame_out_of_frame — painted boxes were nobody's. Ownership is geometric: a panel whose box breaches while its own text stays in-bounds still reports (review-caught FN). Fully off-canvas rects are parked entrances and stay silent; pointer-events:none marks decorative layers.
  • connector_detached (warning): a connector path (marker attrs or word-boundary conn|connector|arrow|edge|link|flow|wire naming) whose both endpoints land far from every anchorable element. Endpoints via getPointAtLength + getScreenCTM (viewBox, preserveAspectRatio, <g transform>, all command types); defs/marker/clipPath/mask subtrees skipped. Known blind spot: anchors living inside an SVG (<image>, foreignObject) are not counted as attachable.
  • canvas_overflow escalation: info → warning when held across samples, the breach exceeds 5% of the shorter canvas edge, AND the rect is partially visible — a fully off-canvas rect is a parked entrance, not drift.

All three are persistence-tiered (single-sample → info) and respect data-layout-allow-overflow.

Verification

  • cli suite: 1657+ passed (one pre-existing host-env-sensitive telemetry failure reproduces on main). New coverage: escape/badge/callout/fixed, painted-panel severity split, direct-text panel FN regression, text-also-breaching ownership counter-case, detached/anchored/marker-tip connectors, word-boundary naming, opt-outs, tier promotion + parked-entrance and short-breach floors, message/fixHint wording pinned per finding.
  • Four incident compositions still surface their drift as held ⚠; the adversarial-review FP repros (fixed HUD, tooltip callout, parked entrance, corner bleed, marker arrowheads, g-transformed and viewBox-scaled connectors) are clean at warning level.

Rollout notes

  • Only errors gate check's exit code — everything here is warning/info, so no pipeline goes red from this PR.
  • Compositions currently riding info-level canvas_overflow as a design choice will reclassify to warning only if held + ≥5% short edge + partially visible; data-layout-allow-overflow opts out. Any --json consumer grouping canvas_overflow as info by identity will see the reclassification. Happy to split the escalation into its own PR if preferred.
  • Ambient container rotation (e.g. a 2° sway) fragments the connector dedup key across samples → per-sample info instead of one held warning; the affected incident composition still fires via escaped_container.

🤖 Generated with Claude Code

xuanruli added 2 commits July 13, 2026 19:39
Four production compositions shipped with 100-600px layout drift, each a
different coordinate-frame confusion the check graded info or missed
entirely: viewport pixels written as container left/top, gsap x/y
treated as absolute position, a -350px margin fighting flex centering,
and stage-relative path coords drawn into a nested SVG.

Three new layout findings close the class:
- positioned_out_of_parent: an absolute/fixed element rendering mostly
  outside its positioning ancestor (warning) — the parent needs no
  overflow clipping, which is what let container_overflow miss it.
- box_out_of_canvas: a painted panel breaching the canvas (warning) —
  text is canvas_overflow's, media is frame_out_of_frame's, painted
  boxes were nobody's.
- connector_detached: a connector path whose endpoints land far from
  every anchorable element (warning) — measured coordinates drawn into
  an SVG with a different origin.

canvas_overflow additionally promotes from info to warning when held
across samples AND the breach exceeds 5% of the canvas.

All three are persistence-tiered and respect data-layout-allow-overflow.
Verified against the four incident compositions: every one now surfaces
its drift as held warnings (previously: info or silence).
…ives

Reworks all three findings after two-lens review (adversarial FP hunt in
real Chrome + maintainer pass):

- escaped_container (was positioned_out_of_parent): uses offsetParent
  (transform-aware, skips fixed-as-canvas), exempts fully-detached
  callouts within an attachment allowance while still flagging
  touching-but-mostly-outside drift.
- panel_out_of_canvas (was box_out_of_canvas): paint alone qualifies
  (flat solid panels were a false negative), fully off-canvas rects are
  parked entrances and stay silent, pointer-events:none marks decorative
  layers, hero-sized breaches warn while small bleeds stay info.
- connector_detached: endpoints via getPointAtLength + getScreenCTM
  (viewBox, preserveAspectRatio, group transforms, every command type),
  defs/marker/clipPath subtrees skipped, word-boundary connector naming,
  containment tier limited to opaque non-ancestor targets (a text-bearing
  wrapper contains its own diagram's endpoints).
- canvas_overflow promotion requires partial visibility — a fully
  off-canvas rect is a parked entrance, not drift.

Verified: the four incident compositions still surface their drift as
held warnings; the review's false-positive repros (fixed HUD, callout,
parked entrance, corner bleed, marker arrowheads, g-transform and
viewBox-scaled connectors) are clean at warning level. Docs and the CLI
skill reference now describe the coordinate-frame findings.
@xuanruli

Copy link
Copy Markdown
Contributor Author

v2 pushed (c16cc17) after a two-lens review pass (adversarial false-positive hunt in real Chrome + maintainer review):

Renames: positioned_out_of_parentescaped_container, box_out_of_canvaspanel_out_of_canvas (the old names collided with text_box_overflow's vocabulary).

Heuristic rework (every item below was a confirmed FP/FN repro):

  • escaped_container: offsetParent (transform-aware containing block), position:fixed skipped (fixed-as-canvas is correct HF authoring), fully-detached callouts within an attachment allowance exempted — touching-but-mostly-outside drift still flags.
  • panel_out_of_canvas: paint alone qualifies (flat solid panels were a silent FN); fully off-canvas = parked entrance, silent; pointer-events:none = decorative layer, silent; hero-sized breaches (≥10% canvas) warn, small bleeds info.
  • connector_detached: rebuilt on getPointAtLength + getScreenCTM (viewBox, preserveAspectRatio, <g transform>, all command types); defs/marker/clipPath subtrees skipped; word-boundary connector naming (no more "knowledge"/"overflow" matches); containment tier limited to opaque, non-ancestor targets.
  • canvas_overflow promotion additionally requires partial visibility — parked entrances stay info.
  • Stale applyPersistenceTier docstring rewritten; connector paths get a geometry-anchored dedup key; skills reference + cli.mdx updated.

Validation: four production incident compositions still surface their drift as held ⚠ (escaped_container ×2 on the radial clock; connector_detached 537px/615px on the loop and network scenes; canvas_overflow→warning + panel_out_of_canvas on the off-canvas painting). The review's FP repro compositions (fixed HUD, tooltip callout, parked entrance, corner bleed, marker arrowheads, g-transformed and viewBox-scaled connectors) are clean at warning level. cli suite: 1657 passed, 1 pre-existing host-env-sensitive telemetry failure reproduces on main.

Known cosmetic limit: an ambient container rotation (e.g. a 2° sway) fragments the connector dedup key across samples, so those report per-sample info instead of one held warning — the primary finding for that composition (escaped_container) still holds. Happy to coarsen the key bucket if wanted.

@xuanruli xuanruli marked this pull request as ready for review July 13, 2026 21:24

@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.

Found one blocking false-negative in the new panel contract, plus a test-contract gap.

  1. Blocker — painted panels with direct text are excluded before their geometry/paint is checked (packages/cli/src/commands/layout-audit.browser.js:918). hasOwnTextCandidate() is true for a panel whose text is a direct text node, so the detector skips the entire element. The existing canvas_overflow path measures the text range, not the painted box; therefore a common card/hero such as <div style="background:…">Title</div> can have its background cross the canvas while its title remains in-bounds and produce neither panel_out_of_canvas nor canvas_overflow. I reproduced this in the existing happy-dom harness with a 1920×1080 canvas, an 800×600 painted hero at x=1400, and an in-bounds 200×50 text rect: expected one panel_out_of_canvas, received []. The new test at layout-audit.browser.test.ts:557 only uses empty panels, so it misses this branch. Please make ownership text-vs-panel specific (skip only a finding already covered by the same geometry, rather than every own-text element) and keep this repro as a regression test.

  2. Important — positively pin the new LLM-facing diagnostic contract. The three new findings define exact message/fixHint instructions, and formatLayoutIssue() sends them directly to CLI check output (packages/cli/src/utils/layoutAudit.ts:151-152, packages/cli/src/commands/check.ts:337-339). The new browser tests assert code/severity/selector but none asserts the intended message/fix text. Please add at least one positive assertion per new diagnostic so wording cannot silently disappear or drift while the type-only tests remain green.

Focused verification: the submitted 70 layout-audit tests pass. The added direct-text repro fails exactly as described. Current head is c16cc17a; required CI has no failures, with regression shards still running at review time.

Verdict: REQUEST CHANGES
Reasoning: The new panel_out_of_canvas contract misses a normal painted-panel shape whenever the panel owns direct text, and the LLM-facing diagnostics are not positively pinned.

— Deepwork

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nice piece of work — the coordinate-frame framing (each of the four incident compositions is a different frame confusion) is exactly the right lens, and the three new findings + the canvas_overflow info→warning escalation land the four production drifts as held warnings the way the PR promises. Persistence-tier integration is symmetric (new codes go in PERSISTENCE_TIERED_CODES, single-sample transients demote to info, and the framePositionKey extension keeps distinct connector_detached paths from collapsing into one). Tests cover the positive cases, the allow-overflow opt-out, the fixed-element skip, the hero vs. bleed severity split, and the parked-entrance-not-drift canvas-overflow-escalation guard. LGTM with one concern and two nits.

Concerns

  • PR-body naming drifts from the code. The body table and "Fix" section describe the new findings as positioned_out_of_parent and box_out_of_canvas, but the emitted codes are escaped_container and panel_out_of_canvas (layoutAudit.ts:22-25, layout-audit.browser.js:875 / :931). Same concept, two labels — but downstream anything grepping the PR body (dashboards, follow-up PR authors, docs) will look for names that don't exist. Same paragraph also promises the finding fires on "an absolute/fixed element rendering mostly outside its positioning ancestor"; the code checks only position: absolute at layout-audit.browser.js:860, and the test at layout-audit.browser.test.ts:530 (respects the allow-overflow opt-out and skips fixed elements) actually asserts fixed elements are not flagged. Behavior is sensible (position: fixed has no meaningful offsetParent), so the fix is to update the body — but the mismatch is worth resolving before either name shows up in a dashboard tile or an incident postmortem. Default per [[code-vs-stated-contract]] is make the code match the stated intent, but here the stated intent is the drifted one; updating the body is cheaper and more accurate.

Nits

  • isCanvasBreachHeldLarge uses Math.min(containerRect.width, containerRect.height) * 0.05 — the short edge, not the canvas as such. PR body says "5% of the canvas" — the short-edge choice is the more conservative bar (higher threshold on a landscape composition), and it matches the connector-gap floor's short-edge convention, but the body wording is slightly imprecise vs. the code. Same wording nit in the "Notes for review" thresholds line.
  • connectorAnchorRects excludes every element inside any <svg> via element.closest("svg") at layout-audit.browser.js:1001. That's a deliberate scope choice, but it means a diagram whose anchors live inside an SVG (<image> raster, <foreignObject> HTML, or a sibling icon-svg) won't count as attachable. For the four incident compositions the anchors are DOM elements, so this doesn't matter here — but it's worth stating as a known blind spot so a future incident that puts anchors in an SVG parent doesn't silently fail to fire connector_detached.

Questions

  • The escalation shifts canvas_overflow from always-info to potentially-warning under the "held + ≥5% short edge + partially visible" gate. Any known composition on main whose currently-info canvas_overflow will newly promote to warning after this ships? The four incident cases are the intended targets, but if any non-incident production comp currently rides on info-level canvas_overflow as a design choice, it will need data-layout-allow-overflow at once. Rolling the escalation out on its own smaller PR (as offered in your Notes) would make that migration atomic — up to you.

What I didn't verify

  • Full run of the four incident compositions myself — trusting the numbers in your Verification section.
  • Whether the escalated canvas_overflow interacts as expected with any downstream --json consumer that filters on severity strictly. If any dashboard is grouping canvas_overflow as info by identity, that grouping breaks.

Review by Rames D Jusso

…lent false negative

A painted panel whose direct text stays in-bounds while its box breaches
the canvas produced neither finding: canvas_overflow measures the text
range and panel_out_of_canvas skipped every own-text element. Skip the
panel finding only when the element's own text ALSO breaches (that
geometry belongs to canvas_overflow); pin the message/fixHint wording of
all three findings with positive assertions; document the SVG-internal
anchor blind spot.
@xuanruli

Copy link
Copy Markdown
Contributor Author

@miguel-heygen both items addressed in 84135c49:

  1. Direct-text panel FN (blocker) — ownership is now geometric: the panel finding skips an own-text element only when that element's own text rect also breaches the canvas (that geometry belongs to canvas_overflow). Your exact repro (1920×1080, painted 800×600 hero at x=1400, in-bounds 200×50 text) is a regression test — one panel_out_of_canvas, zero canvas_overflow — plus the counter-case (text also breaching → canvas_overflow only, no double report).
  2. Diagnostic wording pinned — each of the three findings now has positive message/fixHint assertions ("computed in a different frame" / "offset parent's frame", "Painted panel extends outside" / "data-layout-allow-overflow", "drawn into an SVG with a different origin" / "Subtract the SVG's own rect").

@james-russo-rames-d-jusso all four points taken, PR body rewritten in place:

  • names corrected to escaped_container / panel_out_of_canvas throughout, and the fixed-element claim now states the actual behavior (position:fixed deliberately skipped — fixed-as-canvas is correct HF authoring);
  • "5% of the canvas" → "5% of the shorter canvas edge" (matches the connector floor convention);
  • the SVG-internal anchor blind spot is documented in code and in the body;
  • on the escalation question: only errors gate the exit code, so nothing goes red; comps riding info canvas_overflow by design reclassify to warning only under held + ≥5% short-edge + partially-visible, with data-layout-allow-overflow as the opt-out — noted the --json severity-grouping caveat in Rollout notes. Happy to split the escalation out if you'd rather ship it atomically.

@xuanruli xuanruli requested a review from miguel-heygen July 13, 2026 21:39
@mintlify

mintlify Bot commented Jul 13, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
hyperframes 🟢 Ready View Preview Jul 13, 2026, 9:41 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

pointer-events:none exempted the framed-painting incident's gold frame
layers — hero content that happens to disable hit-testing. Decoration is
now gradient-only paint (spotlights, textures, vignettes); url() images,
solid fills and borders are content regardless of pointer-events.
@xuanruli

Copy link
Copy Markdown
Contributor Author

One more self-caught regression while re-validating the full incident set on the latest head: the pointer-events:none decoration heuristic (added for the spotlight FP) also exempted the framed-painting incident's gold .frame-layers — hero content that happens to disable hit-testing. Replaced with a paint-kind classifier (ce…): gradient-only backgrounds are decoration; url() images, solid fills, and borders are content. All four incident compositions re-verified on this head: clock escaped_container ×2 ⚠, loop connector_detached 537px + escaped_container ⚠, painting canvas_overflow→warning + panel_out_of_canvas ×2 ⚠, network connector_detached 615px ×9-samples ⚠; FP repros still clean at warning level.

@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-review at 6dc5504c: both prior findings are addressed. packages/cli/src/commands/layout-audit.browser.js:928-933 now distinguishes an out-of-canvas panel from its own text geometry, the exact direct-text repro is pinned at layout-audit.browser.test.ts:610-631, and all three new LLM-facing diagnostics have positive message/fixHint assertions. The PR body also now matches the emitted codes and fixed-element contract.

Blocker — the follow-up paint classifier makes every gradient-only content panel invisible. packages/cli/src/commands/layout-audit.browser.js:892-907 now returns true only for url(), a non-transparent solid color, or a border. A normal hero/card whose only paint is linear-gradient(...) is therefore classified as decoration regardless of size, pointer behavior, or content intent. I reproduced this in the submitted harness with the same 1920×1080 canvas and 800×600 hero at x=1400: changing its background to linear-gradient(90deg, #101828, #344054) yields zero panel_out_of_canvas findings. That reopens the core “painted box was nobody’s” false-negative for a common panel style. The spotlight exception needs a narrower decoration signal than paint kind alone (for example, combine gradient paint with an explicit decorative convention) and a gradient-content regression test.

Important — shallow text breaches still double-report despite the new ownership contract. packages/cli/src/commands/layout-audit.browser.js:930-932 checks the text rect using the panel floor (27px on 1920×1080), while canvas_overflow uses the audit tolerance at layout-audit.browser.js:483-498. With a hero breaching 280px and its direct text breaching only 20px, I get both canvas_overflow and panel_out_of_canvas; the new counter-test only covers a 230px text breach. Use the same geometric threshold as the owning text diagnostic and pin the below-panel-floor case so one element does not produce two competing instructions.

Submitted focused tests pass 72/72. Both added adversarial repros fail exactly as above. Current required checks have no failures; eight regression shards are still running.

— Deepwork

Verdict: REQUEST CHANGES
Reasoning: The original direct-text false-negative and diagnostic pins are fixed, but the new blanket gradient exemption creates another common painted-panel false-negative; the ownership threshold also still permits duplicate findings.

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed at 6dc5504c (R2 delta from c16cc17a).

R2 addresses both Miguel's blocker and the diagnostic-pinning gap cleanly, plus tightens isPaintedPanel with a nicer decoration heuristic.

What's clean

  • Miguel's panel-with-direct-text FN fix (layout-audit.browser.js:929-932) — the skip is now geometric: if the panel owns direct text AND that text also breaches, canvas_overflow owns it; otherwise the panel finding fires. Fallback when textRectFor() returns null is safe (falls through to the panel check). Two new tests pin both branches: painted hero + in-bounds text → panel_out_of_canvas (test at line ~605), painted hero + breaching text → canvas_overflow only (test at line ~626). Exactly the ownership contract Miguel asked for.
  • isPaintedPanel refactor (browser.js:892-910) — swaps the pointer-events:none-convention filter for a positive paint-intent check: url(...) images / non-transparent solid color with alpha > 0.05 / non-zero borders count as content; gradient-only paint is decoration. spotlight (radial gradient) test asserts NOT flagged, goldframe (border-only) test asserts IS flagged. Better signal — no longer depends on authors remembering to set pointer-events:none on decoration.
  • Diagnostic wording pinned per Miguel's #2 — escape/panel/connector tests now positively assert message + fixHint fragments (escaped_container: "computed in a different frame" + "offset parent's frame"; panel_out_of_canvas: "Painted panel extends outside the composition canvas." + "data-layout-allow-overflow"; connector_detached: "drawn into an SVG with a different origin" + "Subtract the SVG's own rect"). LLM-facing contracts can't now silently drift while type-only asserts stay green.
  • SVG-anchor blind spot documented (browser.js:1016) — my R1 nit gets a code comment. Reasonable — behavior fix would be significant scope; making the constraint visible in the source is the right call for a follow-up.

One small note

panel_out_of_canvas uses PANEL_BREACH_FLOOR_PX = 24 (or ≥2.5% short edge) for the text-owns-overflow check, while canvas_overflow runs at tolerance = 2. A panel breaching by 200px whose own text breaches by only 10px would clear canvas_overflow's 2px threshold but NOT the panel's 24px floor, so both findings could fire on the same element (text→canvas_overflow info, panel→panel_out_of_canvas warning). That's arguably the right output for a real geometric double-event, but Miguel's ownership contract read as "one or the other, not both" — worth a two-line comment on the intent, or a follow-up tightening the check to match canvas_overflow's tolerance if the intent was strict-mutex.

Non-blocking either way.

What I didn't verify

  • Didn't re-run the local vitest suite against R2 — trusting the CI signal on 6dc5504c (unit tests already green; regression shards still churning at review time).
  • Didn't verify the R2 spotlight/goldframe renders through the full happy-dom harness end-to-end — pattern matches R1's existing pinned tests, which cover the same shape.

Review by Rames D Jusso

…hes canvas_overflow's tolerance

A gradient with any solid stop (alpha >= 0.6) is content — heroes and
cards painted with linear-gradient were invisible under the blanket
gradient exemption; all-translucent stops (spotlights, vignettes) stay
decoration. The text-ownership check now uses the audit tolerance that
canvas_overflow itself fires at, making the contract strict-mutex: any
text breach past that tolerance cedes the element, so a shallow 20px
text breach no longer double-reports.
@xuanruli

Copy link
Copy Markdown
Contributor Author

@miguel-heygen both R2 findings addressed in 511bcb79:

  1. Gradient-content FN (blocker) — decoration is now decided by gradient stop opacity, not gradient-ness: any stop with alpha ≥ 0.6 (rgb(), opaque hex, named colors) makes the paint content; all-translucent stops (the spotlight's rgba(...,0.15) → transparent) remain decoration. Your repro is pinned: the same 800×600 hero at x=1400 with linear-gradient(90deg, rgb(16,24,40), rgb(52,64,84)) → exactly one panel_out_of_canvas warning; the radial-gradient spotlight fixture still asserts silence.
  2. Shallow-breach double-report (important) — the ownership check now uses the same audit tolerance canvas_overflow fires at (threaded into panelOutOfCanvasIssues), making the contract strict-mutex: any text breach past that tolerance cedes the element. Pinned with a 20px text breach (past canvas_overflow's 2px, under the old 27px panel floor) → canvas_overflow only, zero panel_out_of_canvas.

Full validation matrix re-run on this head (all six compositions, no sampling): four incidents still fire as held ⚠ (clock escaped_container ×2, loop connector_detached 537px + escaped_container, painting canvas_overflow→warning + panel_out_of_canvas ×2, network connector_detached 615px ×9), FP repros clean. 47 browser-audit tests + typecheck green locally.

@james-russo-rames-d-jusso your R2 note is the same threshold mismatch miguel escalated — resolved as strict-mutex (same tolerance), with the intent stated in the code comment.

@xuanruli xuanruli requested a review from miguel-heygen July 13, 2026 22:13
@xuanruli xuanruli merged commit 7f4eaeb into main Jul 13, 2026
54 checks passed
@xuanruli xuanruli deleted the feat/check-coordinate-frame-rules branch July 13, 2026 22:35
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.

3 participants