Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save alexlazarian/83aab68593a55787b63d049116a32719 to your computer and use it in GitHub Desktop.

Select an option

Save alexlazarian/83aab68593a55787b63d049116a32719 to your computer and use it in GitHub Desktop.
JUS-1204 Select Editor Migration — Verification Checklist (2026-05-01)

JUS-1204 Verification Checklist — Tiptap Migration

Date: 2026-05-01 Branch: alexlazarian/JUS-1204-followup-select Scope: Confirm the TinyMCE → Tiptap migration introduces no user-visible regressions across the Select domain (15 editor surfaces, the shared message-form gem, the bidder text-answer flow, three Rails apps, and three email templates).

The plan is split into two phases. Before merge is everything a person on a dev laptop with the local stack running can confirm (build, click-through, dev mail-catcher). After deploy is everything that needs the staging environment, real mail clients, real production-shape data, or a longer soak window.

Tick a checkbox only when you have personally observed the expected behaviour or output. If a check is non-applicable (e.g. you have no permissions on a particular surface), strike it through with a note rather than ticking it.

Known and accepted regressions

The two items below are intentional outcomes of the migration and not bugs. Don't open tickets against them while running this checklist — they are tracked as a post-migration follow-up.

  • Tables lose visual styling outside the React app. Tables you create in the new editor still preserve their structure and content. But on server-rendered pages (the bidder workflow Slim views) and inside emails, they may render without borders, padding, column widths, or background fills — because the styling is class-based and those rendering contexts don't load the matching CSS. As long as a table is structurally intact (rows, columns, cell text), this is acceptable for now.
  • Underline is now a styled span, not a <u> tag. Visually identical in every browser and mail client. If you View Source on saved content, expect to see <span style="text-decoration: underline">…</span> instead of <u>…</u>. This was deliberate — the old <u> tag was being stripped by backend sanitizers, so underline was silently failing on save in some surfaces before the migration.

Phase 1 — Before merge

Run on a local dev environment with the full Select stack (Bid, Launchpad, Messaging, Postgres, MySQL, Redis, the dev mail-catcher) up. CI must also be green before this phase is complete.

Build pipelines

  • Fresh checkout of the branch, run the workspace install — completes without warnings about missing peer dependencies for any of the three Select apps or the messaging gem.
  • Bid dev server starts without errors and serves the home page. Open the dev URL, watch the terminal, then load any page that shows the negotiation setup tabs.
  • Launchpad dev server starts without errors. Same — watch the boot log, then open the platform admin landing page.
  • Messaging dev server starts without errors. Same — load the messaging inbox.
  • Bid production-style asset build completes. Run the build command for the Bid app and confirm no error referencing TinyMCE, vite-plugin missing modules, or empty public asset paths.
  • Launchpad production-style asset build completes.
  • Messaging production-style asset build completes.
  • The shared design-system package's component tests all pass. The rich-text editor test suite specifically should report 21 passing tests including the underline-as-span variants.
  • CI pipeline on the PR is fully green. Check the PR's checks tab — biome, typecheck, vite builds, RSpec, Storybook build, and the Docker image jobs for all three apps must succeed.

Each editor surface mounts and accepts input

For every surface below, open the page, focus the editor, type a few words, and confirm the browser console shows no red errors.

  • FAQ topic content (Launchpad — FAQ manage page → click into a topic content card)
  • AI Prompt system instructions (Launchpad — AI prompts page → open or create a prompt)
  • Organization description (Launchpad — open an organization → General Info tab)
  • Attorney overview (Launchpad — organization → attorneys → new or edit attorney)
  • Attorney pro bono (same form, different field)
  • Notable accomplishments — at least one item (same form, expandable list)
  • Notable cases — at least one item (same form)
  • Question title (Bid — Information Request setup → open a question)
  • Question body (same surface, larger field)
  • Question body in the wizard variant (Bid — setup wizard → questions step)
  • Scorecard item description (Bid — Evaluation Guide setup → click into a scorecard item)
  • Negotiation Summary body (Bid — General Settings → Summary tab)
  • Invitation HTML (Bid — Participation tab → Invitation editor)
  • Award email body (Bid — Award setup → Award email tab)
  • Unsuccessful email body (Bid — Award setup → Unsuccessful email tab)
  • Bidder text-answer (Bid — log in as a bidder, open a text-type question, click into the answer card)
  • New-message body in the messaging app (Messaging — compose a new message)
  • Reply body in the messaging app (Messaging — open an existing thread, focus the reply composer)
  • New-message body via the messaging gem inside Bid (Bid — open the side messaging panel and start a new message)

Formatting controls round-trip cleanly

For each of the three surface "weight classes" pick one representative surface and exercise every formatting control the user used to have. The expectation: type → format → save → fully reload the page → the formatting is still visually present, and clicking back into the editor shows it as still active in the toolbar.

Light surface — pick FAQ topic content:

  • Bold, italic, underline, strikethrough each survive a save+reload.
  • Bullet list survives.
  • Numbered list survives.
  • A link with custom URL survives — clicking it opens in a new tab and the URL is correct.
  • Underline specifically: after save, View Source / inspect the saved value — confirm it's a styled span and not the <u> tag.

Heavy surface — pick Question body or Negotiation Summary body:

  • Bold, italic, underline, strikethrough survive.
  • Bullet list and numbered list survive, including nested levels.
  • Link, text alignment (left, center, right), text color, and highlight color all survive.
  • Subscript / superscript survive (where the toolbar offers them).
  • A 3-by-3 table inserts, accepts text in cells, and survives save+reload — content and structure intact even if borders look minimal.
  • Headings (H1, H2, H3) survive on surfaces that accept them. (Surfaces with narrow sanitizers will silently downgrade headings to paragraph text on save — that's not a regression, it's the same allow-list as before.)

Email-template surface — pick Invitation HTML:

  • Bold, italic, color, alignment, lists, and links survive a save+reload.
  • Pasting from an external source (e.g. a Word doc or a previous email body) does not produce visible HTML escape sequences (&lt;, &amp;, etc.) inside the editor or on save.
  • Liquid-style placeholders the user might type — e.g. {{ company_name }} or {% if x %} — round-trip byte-identical through save+reload, with no curly-brace escaping.

Pre-migration content compatibility

Pick at least one record per surface kind that was created before the migration (i.e. saved by the old TinyMCE editor) and confirm it still loads and re-edits cleanly. The goal is to catch any silent stripping or corruption when old HTML hits the new editor's parser.

  • An old FAQ topic with bold / italic / lists / links loads, displays correctly, and re-saves identically (no diff in the database except the underline tag swap if any underline existed).
  • An old organization description with formatted content loads and re-saves cleanly.
  • An old attorney overview with notable records loads and re-saves cleanly. Confirm any pre-existing <u> underline now displays correctly via the new span pathway.
  • An old AI prompt's system instructions load and re-save without escaping any Liquid placeholders.
  • An old question body that contains a table loads — table structure intact, cells editable.
  • An old negotiation summary loads — including any inline color/alignment styling from the previous editor.
  • An old invitation, award email, and unsuccessful email template each load with their content visible, and re-save cleanly.
  • An old in-app message thread renders correctly: bold, color, and any image/file placeholders preserved on the in-thread reading view.

Click-to-edit fade-back-to-card surfaces

These surfaces show a read-only card by default and enter edit mode on click; on blur with no validation error, they fade back to the read-only card and trigger autosave. Expected: smooth transitions, no flash of unstyled content, no autosave on initial mount, autosave fires once after blur with the latest content.

  • FAQ topic content: card → click → editor focused → type → click outside → card fades back showing the new content → autosave success indicator fires once.
  • Question title: same flow.
  • Question body: same flow.
  • Scorecard item description: same flow.
  • None of these surfaces fire an autosave on first page load. Open the network panel, load a page with one of these surfaces, confirm no save POST until you actually type and blur.

Reset-to-template button (email surfaces)

The email surfaces have a "Reset email" button that previously called into TinyMCE imperatively. The new behaviour drives the editor via prop sync — the click should still produce a visible content swap.

  • Invitation tab: edit the body → click "Reset email" → editor visibly updates to the template's default text without requiring a manual page reload, no flash of the previous edited content lingering.
  • Award email tab: same flow.
  • Unsuccessful email tab: same flow.
  • After a reset, clicking "Send test email" sends the reset content (not the previous edits) — verifiable in the dev mail-catcher.

Email rendering — local dev mail-catcher

For the surfaces that send mail, send a test message containing a paragraph, a list, bold + italic + colored text, a link, and an inline image (where the editor allows). Open the resulting email in the dev mail-catcher.

  • Invitation send-test arrives in the mail-catcher — body is visible and styled, link clickable.
  • Award email send-test arrives — same.
  • Unsuccessful email send-test arrives — same.
  • Messaging new-message email arrives — body visible and styled.
  • Tables in the email body render with their cell content visible — borders may be missing but text and structure are preserved (acceptable per the known regression note above).
  • No raw HTML strings (e.g. <p>, <span>) are visible as literal text in the rendered email body.

Bidder text-answer flow (legacy CoffeeScript host)

This was the riskiest single migration because the host is jQuery/CoffeeScript and the editor is bridged in via a small adapter module. Expected behaviour: clicking the answer card mounts the editor, typing updates the character counter live, blur triggers a save, save success unmounts the editor and shows the formatted answer in the read-only card.

  • Log in as a bidder on a negotiation that has at least one text-type question.
  • Click into the answer card — editor mounts, focus lands inside it, no console errors, the read-only card hides cleanly.
  • Type — the character count under the answer updates as you type.
  • Blur — autosave triggers, success toast appears, editor unmounts, the formatted answer text is visible in the read-only card.
  • Reload the page — the saved answer is still there.
  • Click again into the same answer — editor mounts again, original content loads correctly, can be edited.
  • Cancel save (if your test data triggers a validation error) — the editor unmounts and the previous saved content is restored without a partial-edit ghost.

Multi-editor performance — Notable Records list

This is the only surface that mounts many independent editor instances on one page (one per notable accomplishment / case). The pre-migration ceiling was effectively unlimited because TinyMCE used a lighter inline mode; Tiptap's per-editor cost is higher. Test at realistic scale.

  • Open or create an attorney record with at least 20 notable records distributed across accomplishments and cases.
  • Page first paint takes under 200ms after the form data finishes loading. Use the browser performance panel — measure from the network response of the form GET to first interactive paint.
  • Scrolling the form is smooth (no visible jank).
  • Typing into one editor does not cause perceptible lag in the others.
  • Saving the form persists every notable record correctly with its formatting intact.

Phase 2 — After deploy

Run after the branch is merged and deployed to a staging environment that mirrors production data. Some checks here require real mail clients, longer-running soak windows, or production-shape data volumes that aren't available locally.

Cross-app smoke

  • Bid app loads its negotiation setup tabs in staging — no editor surface throws an uncaught error in the browser console on first load.
  • Launchpad loads its admin tabs in staging — same.
  • Messaging loads the inbox and a representative thread in staging — same.
  • Browser console shows no warnings about missing CSS files, missing JS chunks, or 404s from removed legacy editor assets.
  • No 5xx errors related to editor surfaces appear in the application logs for at least 30 minutes after deploy.

Real-mail-client rendering

The dev mail-catcher is forgiving. Real mail clients are not — Outlook in particular is a strict renderer that drops or rewrites a lot of CSS. Send one test of each email-bound surface to a real inbox and inspect the result.

  • Send a test invitation to a real Gmail inbox — body is visible, links work, formatting is recognizable. Tables, if any, have their content visible (borders may be absent — acceptable).
  • Send the same to an Outlook inbox — same expectations.
  • Send a test award email to Gmail — same.
  • Send a test unsuccessful email to Gmail — same.
  • Send a messaging-app message to a recipient who reads via email — the email arrives styled and readable.
  • No raw HTML is visible in any rendering.

Server-rendered Slim pages

A few Bid pages render persisted HTML directly through Rails templates rather than through the React app. These don't load the design-system stylesheet, so any class-based styling the editor adds is lost there — but inline-style content should still display correctly.

  • Bidder workflow → invitation page renders the saved invitation HTML with text formatting (color, alignment, lists) preserved. Tables, if present, may render plain — acceptable.
  • Bidder workflow → summary page renders the saved summary HTML similarly.
  • Bidder workflow → text-answer view (read-only) renders saved answers with formatting preserved.
  • None of these pages produce visible HTML escape sequences in the body text.

Production-shape content compatibility

Staging usually has more historical data than dev. Open a sampling of records — pick 3 per surface kind from across different organizations and date ranges — and confirm they still render and re-edit cleanly.

  • FAQ topics: 3 random records, each loads and re-saves identically.
  • Organization descriptions: 3 random records.
  • Attorney overviews + notable records: 3 random attorneys.
  • AI prompts: at least one prompt from each tenant that has them.
  • Question titles + bodies: 3 random questions across at least 2 negotiations.
  • Negotiation summaries: 3 random summaries.
  • Invitation / Award / Unsuccessful templates: one of each from at least 2 negotiations.
  • In-app message threads: read at least 5 historical messages — formatting renders, no broken layout in the thread view.

Post-merge soak

  • No new Sentry issues mentioning rich-text-editor, Tiptap, ProseMirror, or any of the migrated surface paths in the 24 hours after staging deploy.
  • No customer support tickets referencing missing formatting, broken editors, or save failures in 48 hours.
  • One round of QA-by-feel: a non-engineer (PM or QA) clicks through the most-used surfaces (Question body, Org description, Messaging composer, Invitation editor) and reports no surprises.

Performance in production conditions

  • An attorney form with the largest historical Notable Records count in production data loads in under 500ms TTI on a representative laptop. If staging has a record exceeding 30+ notable items, use that one.
  • Sentry / APM dashboard shows no regression in front-end interaction latency for any of the editor pages over the 48h post-deploy window.

Rollback rehearsal

  • Confirm the rollback path is understood. Reverting any of the four migration commits restores the matching surface or the cleanup; reverting the cleanup commit alone is safe (the surfaces stay on the new editor).
  • Confirm a Sentry alert exists, or is being created, for save failures on any of these surfaces, so a regression is detected within minutes rather than days.

When every box on both phases is ticked (or explicitly struck through with a justified note), the migration is verified.

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