Audience: AI coding agents (and teams running them). This page is part of the deterministic agent workflow for building and verifying Velt UI customizations. Customizing by hand? Start with the Approaches and the feature overviews. This is the workflow the UI Customization Plugin runs for you.
Use the same standard whether verification is automated or manual: cover every mapped element, every required state, and every behavior contract before marking the surface done.
What you need first (preconditions)
You can’t verify what isn’t rendering. Before anything else:- Velt is initialized. The app boots, the user is identified, documents are set, and the default version of this surface renders. Gate logic on
useVeltInitState()if needed. (SeeDebugging.) - The surface has data. Create a comment / open a thread / trigger a notification through the app’s own UI so the surface has something to show. If you genuinely can’t seed data, verify the empty / loading state only and record that limitation in the report.
- You have the design reference for this surface (the Figma frame / screenshot) and know which states it specifies.
The flow
Step 1: Drive every state the design specifies
For each visual goal, render the surface in each state the design covers and capture it. The states that matter per surface are in the matrix below. Common ones:- default, hover, selected/focused
- empty, loading / skeleton, filtered-to-zero
- unread, resolved (and
OPEN/IN_PROGRESS/RESOLVEDif the design themes by status) - long content (truncation), private (if shown)
["default","resolved"] is only checkable once you’ve driven both.
Step 2: Whole-surface measured check + visual gate (match the design)
For each state, do all four: this is the gate:- Consume or assemble the complete checklist (R26: do not hand-pick samples). Build the checklist from every distinct styled appearance in the design (deduped by its declarations: the teal mention, the placeholder, each filter row), every
mustSupplyslot, every mount-map part, and every required state. Resolve each element’s live selector by inspection: use the manifestcssClassesfor measured leaves and inspect to the leaf for the rest. Add relations + gaps from the manifestlayout. Produce a disposition for every checklist element plus the per-state visual artifact. A report that samples fewer elements than the checklist is INCOMPLETE, not PASS, so the loop cannot end on it. - Gross-mismatch pre-check first: compare total content height / element count / surface extents vs the designSpec. Grossly off ⇒ FAIL immediately (don’t let per-element props “pass” on a broken surface).
- Measure live browser output: read each live node’s
getComputedStyleplus surface-relativegetBoundingClientRect(never the 0-size*-wireframetemplate) and produce a delta table with a verdict covering style (Delta E below 2, ±1px, keywords exact) and layout (box ±2-3px, gaps, relations, missing or extra elements). - Visual side-by-side: advisory, not a gate: capture the full-surface screenshot beside the Figma frame to spot regions worth investigating. It does not FAIL on its own, the live app’s real data differs from the mock’s dummy data, so raw pixel regions and content differences are expected, not defects. For any region that reflects a genuine style issue, find the property/relation you didn’t measure, add it to the delta spec, and let that gate it. A clean delta table plus an obviously-wrong screenshot means you’re missing a style/layout assertion, add it; never fail on the pixels alone.
width===0 ⇒ BLOCKED/FAIL. Every mustSupply slot must be present and carry the design’s content: an icon slot must contain the design’s exported SVG (compare identity), not a Velt default or hand-drawn glyph (R17 FAIL). A popup must be styled on its content slot, never its container/trigger (R23). Horizontal padding must not compound across nested wrappers (R22). No feature/prop whose UI the design doesn’t show (R24). Colours still must trace to a --velt-* token / documented class (an accidentally-matching hard-coded colour breaks in dark mode: fail it even if ΔE passes).
The delta table’s failing rows ARE the feedback (advisory pixel regions only matter once named as a delta row). Mark the goal met only when, for every state, the gross check is clean and the style + layout tables are empty, the spec having actually covered every visible slot + the inter-card gap (a thin spec is INCOMPLETE, not a pass), and content differences excluded; otherwise not met, listing each diff.
Step 3: Behavior check (Velt still works), including interaction stability
Customizing presentation must never break Velt’s behavior: you never disabled it, so it must still work (R0, R7). Two halves, both required: 3a: the action functions, end-to-end. Perform the surface’s real actions (in the matrix) and confirm each still functions through to its outcome: place a pin, open a dialog, type a reply and click Send and confirm it posts, change status, filter, sidebar sync; open a notifications panel, switch tab, mark read, click through. Perform the action by a real on-screen click at the visible element’s box (a JS.click() often won’t fire Velt’s handler). If an action is dead, something was hidden with CSS instead of a prop (R7), interactivity was put in wireframe markup (R4), or a slot was dropped: fix it; it is not a “design gap.” One passing path is not proof, drive the exact reported interaction, not a convenient neighbour.
3b: the target doesn’t MOVE mid-interaction (R27). A static per-state capture proves the surface looks right while it sits still; it does not prove it holds still during a click. The failure: a visibility/layout rule keyed on a transient state (:focus/:hover/:active, or a Velt twin like velt-composer-input-focused) flips at the instant of the click, the element loses focus, a hidden piece re-appears, the control shifts out from under the cursor, the click misses. On every interactive affordance the surface renders: record its box, drop the transient state the click would drop (blur the focused element), reflow, re-measure. Any shift > 1px ⇒ FAIL, re-anchor the rule on a stable, persistent state (e.g. velt-composer-open). Passing the static capture does not exempt this gate.
Step 3c: Family real-path smoke suite (R30), after the family’s blocks pass
Fixture-green is not done: a run can pass every seeded-fixture block while real interaction paths are broken. Once a family’s blocks are clean, run its real-path suite:- a short message AND a max-length message (never only the canonical fixture text);
- the surface in every dialog context it appears in (sidebar card / popover open-dialog / hover preview, shared classes leak across contexts);
- every affordance clicked once (reply, resolve, edit, options), asserting the outcome and no dead band;
- one viewport resize; zero console errors throughout.
Step 4: Rules-compliance scan (static, on the produced code)
Walk the Quick gate inRules against what you wrote. The checks that catch the most:
- R0: no hacks: no
setTimeout/MutationObserveron Velt internals, no scraped internal markup, no timing/DOM shims. - R1 / R2: exactly one
<VeltWireframe>; the live feature component is mounted. - R4 / R5: no
onClick/useState/ hooks inside wireframe markup; UI-library components wrap around primitives, not inside wireframes. - R6 / R7 / R8 / R9: selector CSS only with shadow off or
injectCustomCss; nodisplay:noneto remove features; one stylesheet; dark values scoped to:root[data-velt-theme="dark"]. - R10: every identifier (slot, prop, variable, class, hook, API) verified against
reference/. If it isn’t there, it doesn’t exist. - R11 / R16: files under
components/velt/ui-customization/; only this one surface touched this step. - Verified gotchas:
ThreadCardnested inBody → Threads; container slots declare their full child tree; correct shadow root-vs-nested handling; pin index/number filled viavelt-data;VeltCommentDialog, never the deprecatedVeltCommentThread. (All inWireframe components.)
Step 5: Verdict
Per-surface state & behavior matrix
The concrete “what to drive” for each v1 surface. Drive the states for the visual check (step 1-2); perform the behaviors for step 3. Verify only the states the design actually specifies, but never skip the behavior column.Comments
Notifications
After an SDK upgrade
Re-run the visual + rules checks. Variable-based theming (--velt-*) is upgrade-safe; class/selector overrides and wireframe slot names are the most drift-prone pieces, so re-verify them first (Cross-cutting concerns).
