Recent SupaWave releases, fixes, and improvements in one place.
May 24, 2026
Fix inbox content getting cut off on the right on phones
On phone-sized screens the inbox list was wider than the viewport, so each wave's timestamp, message count and unread badge were clipped off the right edge. The layout now fits the screen.
✓Fixed
- Constrain the mobile root-shell layout to the viewport width so the inbox digest no longer overflows and clips unread counts and badges on the right edge.
May 22, 2026
Recover from waves that get stuck on "Loading"
Opening a wave whose realtime channel never finished connecting could leave the wave panel stuck on the "Loading" spinner forever with no feedback. A watchdog now surfaces a one-tap Retry instead of hanging indefinitely.
✓Fixed
- Add a wave-open watchdog so a stalled realtime connection no longer leaves the wave panel stuck on "Loading" indefinitely.
- Show a dismissible notification with a Retry action when a wave takes too long to open.
May 19, 2026
J2CL user opt-in preference
Users can choose whether the default Wave experience opens the J2CL beta or the classic GWT client, while explicit view links remain reversible.
✦New
- Added a Wave Client preference in Account Settings with Default, J2CL beta, and Classic GWT choices; the saved preference controls only default Wave routes and keeps ?view=j2cl-root and ?view=gwt available for direct switching.
May 19, 2026
J2CL topbar control alignment
The J2CL root topbar now aligns its signed-in controls like the classic GWT topbar while preserving the existing control behavior.
✓Fixed
- Right-aligned the compact J2CL topbar control cluster so language, save state, network state, notifications, inbox, user menu, admin, and sign-out controls match the GWT layout.
May 19, 2026
Fix J2CL blip edit submit routing
J2CL edit mode now submits changes to the selected blip instead of creating a new reply blip.
✓Fixed
- The J2CL edit action captures the existing blip text and document metadata, prefills the edit composer, and routes Done through a blip-edit submit path.
- Edit-mode submissions now target the existing blip document and no longer mutate the conversation manifest as a reply.
May 19, 2026
Fix J2CL editing for empty root blips with replies
J2CL can now edit an empty-looking root blip that still contains inline reply structure.
✓Fixed
- Editing an empty-looking J2CL root blip now inserts the new text while preserving existing inline reply structure.
- J2CL still blocks edits for blips that mix visible text with unsupported inline structure until full structural edit support is available.
May 19, 2026
J2CL no longer shows a duplicate sign-out button in the compact topbar.
The compact J2CL topbar now relies on the user-menu sign-out action only, leaving the visible topbar controls uncluttered.
✓Fixed
- Removed the duplicate top-level J2CL sign-out link while keeping sign-out available in the user menu.
May 19, 2026
Exclude J2CL debug overlay from production bundles
Production J2CL builds now emit a literal false compile-time constant for the debug-overlay flag, enabling Closure dead-code elimination of all markDebugOnly tagging and associated debug strings.
✓Fixed
- Replace runtime System.getProperty() flag with a Maven-generated literal constant so the Closure compiler can reliably DCE debug-only code paths in production
- Guard detail element visibility in render() to prevent diagnostic text from reaching users in production builds where the debug overlay is disabled
May 19, 2026
J2CL wave controls menu spacing
The J2CL wave-controls toggle no longer overlaps the compact topbar user menu.
✓Fixed
- Moved the floating wave-controls toggle below the compact J2CL topbar so it stays clear of the user menu and avatar controls.
May 19, 2026
J2CL bottom reply control now matches GWT
The J2CL bottom reply affordance now appears as the GWT-style "Click here to reply" row while the per-blip toolbar reply icon remains dedicated to inline replies.
✓Fixed
- Changed the J2CL bottom-of-thread reply trigger from a compact plus button to a full-width dashed reply row.
- Added regression coverage that keeps bottom continuation, per-blip continuation, and inline reply actions distinct.
May 19, 2026
Restore initial GWT search results when OT search is enabled
The GWT search panel now performs a direct search bootstrap while opening the OT search subscription, so initial results appear even when the live search channel has not delivered a snapshot yet.
✓Fixed
- Load initial GWT search results from the HTTP search path while keeping OT search available for live updates.
- Keep repeating search polling disabled unless the OT search fallback path explicitly activates it.
May 19, 2026
Fix root reply duplicate locale imports
Remove duplicate subscribe import and duplicate locale aria-label rerender test introduced by the #1284 merge overlap.
✓Fixed
- Removed duplicate `subscribe` import in wavy-wave-root-reply-trigger.js left from the #1284 merge.
- Removed duplicate setLocale/resetLocale import and duplicate aria-label rerender test block in wavy-wave-root-reply-trigger.test.js.
May 19, 2026
Retry transient Alpine package failures in deploy sanity checks
Production deploy sanity checks now retry the temporary Alpine package install used for curl and jq, so a brief package mirror outage no longer aborts an otherwise healthy blue-green deploy.
✓Fixed
- Wrapped the sanity container's apk install of curl and jq in a bounded three-attempt retry loop for both current and legacy Contabo deploy scripts.
May 18, 2026
J2CL search rail now uses the compact GWT search toolbar actions.
The J2CL root search rail now presents the same compact icon action row as GWT for New Wave, saved-search management, folder shortcuts, and refresh, instead of the separate large New Wave/manage buttons and folder list.
✓Fixed
- Moved J2CL New Wave, Manage saved searches, Inbox, Mentions, Tasks, Public, Archive, Pinned, and Refresh into one GWT-style toolbar row.
- Kept the existing J2CL events and canonical search queries behind the toolbar icons so functionality matches the previous controls while matching the GWT layout.
May 18, 2026
J2CL search filters align with the GWT panel
Moves the J2CL search filter chips above the result list so the search panel controls match the GWT layout.
✓Fixed
- J2CL search filter chips now render with the upper search controls instead of below the wave results.
May 18, 2026
J2CL now keeps the root title blip visible when a wave opens.
The J2CL wave panel now includes the editable root blip in the initial viewport window, so the text used as the wave title is also shown as a normal blip like it is in GWT.
✓Fixed
- Initial J2CL viewport windows now order the root blip before generated numeric blips instead of hiding the title source behind later messages.
May 18, 2026
J2CL now matches GWT inline reply placement and wave-open positioning.
J2CL replies now use the same inline-blip fallback position as GWT when no caret is captured, and initial wave-open viewport ordering keeps the root title blip visible.
✓Fixed
- Inline replies created from J2CL now fall back to the end of the parent blip body instead of creating a manifest-only child thread.
- Initial J2CL viewport windows now keep the root blip ordered before generated numeric blips so wave-open scrolling has the same starting content as GWT.
May 18, 2026
J2CL blip reply continuation control is compact like GWT
The J2CL same-level reply control no longer reserves a full text badge row below every blip, allowing compact blip stacks while preserving the hover/focus reply affordance.
✓Fixed
- Changed the J2CL below-blip continuation reply affordance to a zero-footprint GWT-style icon/bar.
- Preserved the same-level reply click behavior while removing the always-reserved vertical gap between blips.
May 18, 2026
J2CL no longer paints a focus-frame line across the first blip on wave open.
The J2CL focus frame now waits for explicit keyboard navigation before painting, so the initial programmatic blip selection remains available for navigation without drawing a horizontal cyan line across the first blip.
✓Fixed
- Initial wave-open blip selection keeps logical focus markers but does not show the absolute focus-frame overlay until the user navigates.
May 18, 2026
J2CL wave loading now keeps blip layout stable while formatting and attachments settle.
J2CL selected-wave rendering now avoids full blip-stream rebuilds when attachment metadata resolves, hides client-created blips until their Lit wrapper has rendered, and keeps pending placeholders compact so initial load no longer flashes raw content.
✓Fixed
- Attachment metadata refreshes update only the affected attachment tile subtree instead of clearing and rebuilding the whole read surface.
- Client-created wave-blip hosts hide their light-DOM body until the formatted Lit card has completed its first render.
- Pending placeholders stay compact while attachment metadata is loading, avoiding a shrink-shift for attachments that resolve without a large preview.
May 18, 2026
GPT Bot Attachment Understanding
GPT bot replies can now use Wave attachments as context, including OpenAI transcription for supported audio and video files.
✦New
- The GPT bot exports attached files from the triggering blip and includes bounded text attachment content in its prompt context
- Supported audio and video attachments are transcribed through OpenAI before the bot answers
May 13, 2026
J2CL read mode now keeps inline reply threads attached to their root-blip anchors.
J2CL selected-wave document snapshots now preserve inline reply anchor metadata from reply elements, so child blips render at the same inline locations as they do in GWT instead of being flattened or omitted from the root blip context.
✓Fixed
- Sidecar document decoding records reply element ids as inline reply anchors at the decoded visible-text offset.
- Document-backed J2CL viewport entries and read blips now carry those anchors through projection while preserving fragment-derived anchors when fragments already provide them.
May 13, 2026
GWT wave deep-link route parity
Keeps GWT wave loading aligned with J2CL deep links that carry the selected wave in the URL query.
✓Fixed
- Makes the legacy GWT client honor the canonical wave= route parameter before falling back to the hash token, so J2CL-compatible wave links open the same selected wave in both views.
May 10, 2026
J2CL root shell now shows the deploy upgrade banner just like GWT.
The J2CL root shell page now polls /version every 60 seconds and shows the same upgrade banner GWT shows when the deployed build commit or build time changes, so users on the J2CL client get notified to reload after a deploy.
✦New
- Server now injects the same /version polling + upgrade-banner script into the J2CL root shell page, mirroring the GWT wave client.
May 10, 2026
J2CL shell: tooltips and i18n for all action buttons
Every interactive button in the J2CL Lit shell now carries both aria-label and title attributes, localized via a lightweight i18n primitive with English and German catalogs.
✦New
- Added t()/setLocale()/subscribe() i18n primitive for the J2CL Lit shell with English and German catalogs
- All <button> elements in j2cl/lit/src/elements/ now have aria-label and title attributes for accessibility and tooltip support
- User locale is surfaced from HumanAccountData through J2clBootstrapContract.SESSION_LOCALE to window.__bootstrap.session.locale
- Added scripts/audit-buttons.mjs prebuild check that rejects any button missing aria-label or title
May 10, 2026
J2CL toolbar Reply now preserves the text selection so caret-anchored inline replies actually land at the caret.
PR #1243 wired the caret-anchor plumbing end-to-end (wave-blip captures the offset, the controller forwards it, the factory emits the body op), but the toolbar Reply button only had an @click handler. The blip body is not contenteditable, so when the user pressed the button the browser's default mousedown moved focus to the button and collapsed the text selection BEFORE @click ran — making _currentSelectionItemOffsetWithinBody return -1 every time and routing through the legacy "no anchor" fallback (the blip rendered at the bottom of the thread instead of inline at "draw"). The toolbar Reply now calls preventDefault on mousedown to keep the selection alive, and wave-blip captures the caret offset on mousedown as defense-in-depth for browsers that ignore preventDefault on cross-shadow buttons.
✓Fixed
- wave-blip-toolbar's Reply button calls event.preventDefault() on mousedown so the user's text selection in the (non-contenteditable) parent blip body is preserved across the click — matching the well-established rich-text-editor toolbar pattern. The button still receives the click event normally.
- wave-blip captures the caret's wave-doc item offset on mousedown (via a new wave-blip-toolbar-reply-mousedown event) and uses that captured value at click time, falling back to the live selection only when the mousedown capture didn't fire. This keeps caret-anchored inline reply working even on engines that mutate the selection despite preventDefault.
May 10, 2026
J2CL parity: scroll to last unread blip on wave open
Opening a wave in the J2CL client now focuses and scrolls to the last unread blip (falling back to the last rendered blip when everything is read), matching GWT's FocusBlipSelector.selectInitialBlip behavior.
✓Fixed
- J2clSelectedWaveView tracks the wave-id transition already used to clear viewport scroll memory, and after the first render that produces content scans rendered blips right-to-left for the `unread` DOM marker, focusing that blip (or the last rendered blip if none are unread) and scrolling it into view via the existing focusBlip path.
- Skips the immediate mark-read side effect on auto-focus — GWT's Reader marks via dwell observers — so on wave open J2CL positions only and leaves read-marking to the user's first interaction.
May 10, 2026
J2CL parity round 6: restore the 8px gap between the dark-blue compact topbar and the inner blue panel toolbars.
Round 6 parity work after user testing of round 5. The legacy GWT WebClient gives the search rail and wave panel an 8px top margin so the inner blue title strips don't butt directly up against the outer dark-blue app topbar (WebClient.ui.xml `.searchPanel { margin: 8px 0 0 8px; }` / `.wavePanel { margin: 8px 0 0 0; }` against the `#f0f4f8` page bg). The J2CL shell-root grid had row-gap 0 between the header row and the nav/main rows, so the inner blue toolbar strips (wavy-search-rail .panel-title, .sidecar-selected-title) sat flush against shell-header[compact-gwt-topbar]. Round 6 adds an 8px top inset to the nav and main slot wrappers when the shell is in compact-gwt-topbar mode, restoring the visual separation users see in the GWT client.
✓Fixed
- Add `padding-top: 8px` to shell-root's nav and main slot wrappers when shell-header is in compact-gwt-topbar mode so the inner blue toolbar strips no longer butt against the outer dark-blue topbar — matches the 8px margin GWT WebClient.ui.xml puts on .searchPanel / .wavePanel.
May 10, 2026
J2CL parity round 5 follow-up: drop unconditional <wave-blip> padding, reset initial-focus marker on empty entries, correct scrollIntoView javadoc.
Round 5 (#1237) shipped before a self-review caught three issues. The unconditional `wave-blip { padding: 3px; margin: 0; position: relative }` declaration leaked into post-upgrade rendering for every blip; `initialFocusAppliedForWaveId` was not reset when the wave went empty, so re-population (loading state, retry) would skip the seek-to-latest path; and the `scrollBlipIntoView` Javadoc described a different scroll alignment than the code performed.
✓Fixed
- Trim the new wave-blip rule in shell-tokens.css down to `contain: layout style` only — drop padding / margin / position which were unconditionally applied post-upgrade and would have changed layout for every blip in the stream.
- Reset initialFocusAppliedForWaveId in J2clReadSurfaceDomRenderer's empty-entries branch so a re-populated wave triggers the seek-to-latest path again instead of dropping the user back at the first blip.
- Update scrollBlipIntoView Javadoc to describe the actual `scrollIntoView(false)` bottom-aligned behavior (matches the GWT pin-to-bottom-on-open pattern).
May 10, 2026
J2CL parity round 5: fix toolbar alignment, blip render flicker, initial scroll, and task-toggle rebuild.
Round 5 parity work after user testing of rounds 1–4. Fixes a runtime-broken :host(:has()) selector (blue toolbar still ~12 px lower in the search rail than in the wave panel), eliminates the 'blips appear as plain text → flicker → reflow' on big-wave open via pre-upgrade light-DOM styling for <wave-blip>, scrolls to the latest unread / latest blip on initial wave open (matching GWT FocusBlipSelector.selectInitialBlip), and adds a task-only diff fast-path so marking a reply done no longer rebuilds the entire blip stream.
✓Fixed
- Replace the runtime-broken :host(:has(> wavy-search-rail)) selector in <shell-nav-rail> with a slotchange-driven data-flush attribute so the rail blue title strip actually sits on the same baseline as the wave-panel title.
- Add pre-upgrade light-DOM CSS for <wave-blip> in shell-tokens.css so blip content paints inside its card geometry before Lit upgrades the custom element — eliminates the flash of unstyled blip text users saw on big-wave open.
- Scroll to the last unread blip (or the last blip overall) on the initial render of each wave, matching GWT's FocusBlipSelector.selectInitialBlip; subsequent re-renders preserve the user's current focus.
- Add a task-only diff fast-path to J2clReadSurfaceDomRenderer.renderWindow so marking a blip / reply as done updates the existing wave-blip attributes silently instead of clearing host.innerHTML and rebuilding every blip — closes the visible blip-stream flicker users observed on every mark-as-done.
May 10, 2026
J2CL @-mention popover now lists the wave's participants on every inline composer surface.
Typing @ inside a freshly-opened J2CL inline composer (toolbar-Reply, wave-root reply, per-blip Edit, or per-blip continuation) now opens the mention suggestion popover with the wave's participant list — matching the GWT client. The view used to read the participants list off the legacy <composer-inline-reply> element's expando, which was empty until the controller's first participants-bearing render(); a user who clicked Reply and immediately typed @ saw an empty popover. The view now reads from a new Listener.getCurrentParticipantAddresses() accessor that returns the controller's authoritative participantsForCurrentSelection() value, so the popover is mention-ready from the very first paint.
✓Fixed
- J2clComposeSurfaceController.Listener gains a default getCurrentParticipantAddresses() returning the empty list; the controller's anonymous Listener overrides it to return participantsForCurrentSelection().
- J2clComposeSurfaceView.mirrorComposerStateFromReplyElement now prefers the listener's participants list and falls back to the legacy <composer-inline-reply> expando only when the listener is not bound or returns nothing.
May 10, 2026
J2CL inline replies now anchor at the user's caret inside the parent blip — matching the GWT client.
Clicking the toolbar Reply icon while the caret is inside a blip's body now creates an INLINE reply anchored at the caret's exact position, mirroring the GWT wave client. PR #1242 restored the per-blip continuation indicator (sibling reply) but the toolbar Reply still produced an unanchored child thread that rendered as a flat blip below the parent body. Now wave-blip captures the caret's wave-doc item offset (DOM text offset + leading <body> + line-tag items + existing anchor items), forwards it through the compose surface controller into J2clRichContentDeltaFactory, which emits an additional parent-blip body op inserting a <reply id="…"> anchor element at that offset — exactly what GWT's WaveletBasedConversationBlip.addReplyThread(int location) does. Replies with no caret inside the blip body fall back to the legacy "no anchor" path so existing flows keep working.
✦New
- wave-blip's toolbar Reply now captures the caret's wave-doc item offset inside the parent body and forwards it on the wave-blip-reply-requested event detail (alongside parentBodyItemCount).
- J2clRichContentDeltaFactory.createReplyRequest gains a 6-arg overload that emits a parent-blip body op inserting a <reply id="threadId"> anchor element at the captured offset, paired with the existing manifest <thread> insert. Continuation (sibling) replies still suppress the anchor op since they have no thread to point at.
May 10, 2026
J2CL: cleaner new-blip edit surface
Removed a redundant inner border on the J2CL inline composer that visually read as a horizontal line across the middle of a new blip in edit mode.
✓Fixed
- Drop the static hairline border on the contenteditable composer body; the surrounding compose card already provides the focus envelope, and the drop-target outline still renders on drag-over.
May 10, 2026
Restore the per-blip continuation reply indicator and drop the bogus "Reply submitted" status in the J2CL client.
The J2CL read surface now renders a hover-revealed "Reply" indicator below each blip — clicking it opens an inline composer that submits as a same-level (sibling) continuation, matching the GWT wave client. The toolbar Reply icon continues to create an inline (nested) child thread, so both GWT-style affordances coexist. The J2CL inline composer also no longer surfaces a "Reply submitted. Waiting for the opened wave to update." status after submit (GWT showed nothing here — the wave just refreshes when the blip lands).
✦New
- Per-blip continuation indicator below the blip body in the J2CL read surface emits wave-blip-continuation-requested and routes through onContinuationSubmitted so the wave op uses replyManifestSiblingInsertPosition (sibling reply), distinct from the toolbar Reply icon which creates an inline (nested) reply.
✓Fixed
- J2CL inline composer no longer leaves the "Reply submitted. Waiting for the opened wave to update." status stamped on the closing composer after a successful reply or continuation.
May 9, 2026
J2CL search help opens from the search rail
The J2CL search rail help button now displays the Search Help modal instead of toggling an unrendered shell child.
✓Fixed
- Render the singleton search-help modal through the J2CL shell modal slot so the help overlay appears when the search rail question-mark button is clicked.
May 9, 2026
J2CL root header now matches the compact legacy SupaWave top bar.
The J2CL root shell uses the same compact blue header treatment as the GWT client and no longer shows a J2CL route eyebrow next to the SupaWave brand.
✓Fixed
- Restyles the J2CL root-shell header as a compact GWT-style top bar.
- Removes the visible J2CL route eyebrow from the SupaWave brand area.
May 9, 2026
J2CL root surface aligns with GWT for topbar, search rail, tasks, blip, and status icons.
Closes five user-reported parity gaps so the J2CL root shell mirrors the legacy SupaWave chrome end-to-end.
✓Fixed
- Aligns the compact J2CL topbar gradient and brand font with the GWT topbar.
- Drops the framed search-rail title strip so the search input sits flush to the rail top.
- Restores the legacy task metadata popup chrome on the J2CL task overlay.
- Paints the unread metabar background on J2CL root blips to match the GWT unread cue.
- Surfaces save and connection status icons inside the compact J2CL topbar.
May 9, 2026
J2CL search rail title strip aligns with the wave-panel title strip on the same row.
Round 4 parity work: drop the 12 px nav-inset padding and 8 px slotted-pill padding when the rail wraps a wavy-search-rail panel so the rail header does not float ~20 px below the wave-panel header.
✓Fixed
- Reset shell-nav-rail's nav-inset padding and slotted-pill chrome when it wraps wavy-search-rail so the rail's blue title strip lines up with the wave-panel title strip across panels.
May 9, 2026
J2CL search rail and wave panel toolbars now share the same blue chrome at matching heights.
Round 3 parity work: ship sidecar.css next to shell.css so the wave-panel chrome actually loads, and align the wave-nav toolbar with the search rail's action toolbar.
✓Fixed
- Bundles sidecar.css into war/j2cl/assets so the wave-panel blue title strip, empty-state recipe, and selected-wave chrome render correctly in production (the link tag pointed at a path that was 404ing).
- Repaints the wave-panel nav toolbar (`<wavy-wave-nav-row>`) with the same light-blue gradient as the search rail's action toolbar so both panels read as the same band of chrome side-by-side.
May 9, 2026
J2CL search rail and wave-panel headers align with the GWT chrome side-by-side.
Round 2 parity work: restore the rail's blue title strip at GWT geometry, hop the brand link to the landing page, and tighten the topbar right cluster.
✓Fixed
- Restores a flat full-width blue title strip on the J2CL search rail that mirrors the wave-panel header so both panels' headers sit at the same y / height / color.
- Sends the SupaWave brand link to the marketing landing page from the J2CL shell, matching the legacy GWT topbar.
- Tightens the J2CL topbar right cluster so status and user-menu chips hug the viewport edge.
May 9, 2026
J2CL task controls keep completion state and task details aligned with the legacy wave view.
The J2CL read surface now updates task completion state immediately when a task is toggled and tightens the task details dialog layout toward the legacy GWT popup.
✓Fixed
- Keeps the parent blip's task-completed marker in sync with per-task toggles while preserving multi-task blip behavior.
- Adjusts the J2CL task details dialog spacing and controls to better match the GWT task metadata popup.
May 9, 2026
J2CL search, blip, profile, and task controls now match the legacy wave view more closely.
The J2CL root view restores legacy-style search panel chrome, keeps root content inside the read surface, uses profile images in blip headers, expands profile popup details, and moves task actions toward per-task controls.
✓Fixed
- Adds the legacy blue search-panel title strip above the J2CL search toolbar.
- Stops rendering selected-wave snippet text above the root blip content.
- Uses profile image avatars for blip authors with initials fallback and aligns blip action glyph color with the legacy toolbar.
- Fetches and renders profile bio, online status, and profile links in the J2CL profile popup when profile data is available.
- Removes blip-level task controls from the blip toolbar and emits task identity/range details from per-task affordances.
May 8, 2026
SBT-only default build path
Keeps the normal app run, stage, and package flow on SBT-only build steps while leaving J2CL assets available through explicit SBT tasks.
✓Fixed
- Normal `sbt run`, `sbt Universal/stage`, and `sbt Universal/packageBin` no longer invoke the Maven-backed J2CL sidecar build by default.
- J2CL parity and sidecar lanes can still build, stage, and require those assets explicitly with `sbt j2clRuntimeBuild`, `WAVE_STAGE_INCLUDE_J2CL_ASSETS=1`, and `WAVE_SMOKE_REQUIRE_J2CL_ASSETS=1`.
May 8, 2026
Production deploys include J2CL shell assets again
The production deploy build now explicitly rebuilds and packages the J2CL root-shell assets before creating the runtime image, preventing the J2CL shell from loading HTML or 404 responses for CSS and JavaScript files.
✓Fixed
- Add a deploy guard that fails before image publication if the staged J2CL CSS or JavaScript assets are missing
May 8, 2026
J2CL wave chrome now matches the legacy wave title and participant panels more closely.
The J2CL selected-wave view uses a compact title bar, removes the extra Opened wave label, shows participant profile images, and aligns wave action buttons with the legacy panel controls.
✓Fixed
- Replaces the large selected-wave heading with a compact legacy-style title bar.
- Removes the J2CL-only Opened wave eyebrow from cold and server-first selected-wave markup.
- Renders selected-wave participants as profile image avatars and restyles the wave action strip to match the legacy participant panel density.
May 8, 2026
Smooth J2CL wave loading and reply parity
J2CL waves now avoid flashing raw blip text before the Lit read surface upgrades, reserve attachment preview space to reduce resizing while images load, and remove the redundant below-blip plus reply affordance so replying matches the GWT action flow.
✓Fixed
- Hides pre-upgrade J2CL blip light DOM behind the wave-blip shell so users no longer see unformatted wave text while Lit finishes upgrading the read surface.
- Stamps known attachment image dimensions and adds read-surface attachment sizing CSS so image previews reserve their final layout space while loading.
- Removes the redundant below-blip continuation plus from the J2CL blip surface so users reply through the existing GWT-style blip action affordance.
May 8, 2026
J2CL read markers no longer fail on missing user-data wavelets
The J2CL read surface now creates the current user's missing user-data wavelet before marking a blip as read, preventing repeated 500 responses from /j2cl/mark-blip-read.
✓Fixed
- Avoid false internal-server errors when J2CL marks a visible blip as read for a user whose supplement wavelet has not been created yet
May 8, 2026
J2CL blip plus follows GWT continuation behavior
Makes the visible plus below J2CL blips open a same-thread continuation composer instead of leaving an unclear inert-looking control.
✓Fixed
- The J2CL blip plus now has a tooltip, opens the inline composer for that blip, and submits through the same sibling-continuation manifest path used by the GWT reply-box plus.
May 8, 2026
J2CL inline reply controls match GWT
Removes redundant J2CL inline-thread plus controls from parent-owned blips so the visible reply-thread affordance matches the GWT-style parent chevron.
✓Fixed
- Parent-owned inline replies in the J2CL wave view now use the blip's existing reply chevron instead of an extra gutter plus control that had no useful label and often appeared inert.
May 8, 2026
GWT view-switch unload permission
The legacy GWT route now sends a permissions policy that allows its same-origin unload handler when users switch back from the J2CL view.
✓Fixed
- Allows the legacy GWT web client to register its unload handler without Chrome logging a permissions-policy violation during J2CL-to-GWT view switches.
May 8, 2026
Contabo deploys require a pinned SSH host key
The Contabo deploy and rollback workflows now share the same pinned SSH host-key verification path, so emergency rollback tooling fails early when the trusted fingerprint is missing or mismatched.
✓Fixed
- Document the required CONTABO_HOST_FINGERPRINT repository variable or secret for production deploy and rollback workflows
May 7, 2026
J2CL wave blip click and reply parity
Improves the J2CL wave reader so blip click, unread, avatar, thread-toggle, and depth-limited reply behavior match the legacy wave view more closely.
✓Fixed
- Clicking an unread blip now marks it read immediately without shifting the wave panel.
- Unread blips receive a stronger visual highlight and missing author metadata no longer renders a question-mark avatar.
- Inline thread controls now use clearer chevrons with tooltips, hide duplicate inert buttons, and depth-limited inline replies fall back to regular replies.
May 4, 2026
J2CL wave thread and chrome parity
Tightens the J2CL wave panel so read-mode scrolling, inline-thread toggles, and blip chrome behave closer to the GWT wave view.
✓Fixed
- Removes the nested selected-wave scrollbar so the wave panel has a single vertical scroll path.
- Keeps inline-thread expand and collapse clicks from bubbling into blip focus/navigation handlers.
- Hides inert blip overflow chrome, adds hover tooltips to wired blip actions, and removes read/root debug text from normal wave chrome.
May 4, 2026
J2CL wave navigation and read-state parity
Fixes remaining J2CL wave-panel parity gaps around single-scroll layout, explicit blip navigation, and read-state chrome.
✓Fixed
- Constrains the J2CL root shell to the viewport and makes the selected wave content the single vertical scroll owner.
- Marks unread blips read immediately when the user focuses them via wave navigation or direct blip focus.
- Hides text-only top read-state labels from the wave chrome while preserving unread counts in the navigation controls.
May 4, 2026
J2CL inline thread lazy-load parity
Fixes J2CL inline-thread lazy loading when the next loading sentinel is only partially visible, matching the GWT expectation that visible inline replies continue loading without manual page-bottom scrolling.
✓Fixed
- Triggers viewport fragment growth for any visibly intersecting loading placeholder while preserving the stricter visibility threshold used for marking real blips read.
- Keeps the active inline composer mounted when viewport fragment growth rebuilds the target blip, so mention typing and reply drafting survive lazy-loaded inline thread updates.
May 4, 2026
J2CL inline reply chevrons match GWT
Inline reply controls in the J2CL wave view now expand and collapse replies in place instead of navigating away or showing stray per-thread plus controls.
✓Fixed
- Changes the compact reply chevron to request inline thread expand/collapse instead of shell depth navigation.
- Hides the generated per-thread plus/minus button from layout so the visible reply affordance lives on the parent blip like the GWT view.
- Adds regression coverage for inline-thread toggle dispatch and rich reply manifest trailing-retain placement.
May 4, 2026
J2CL inline replies target the clicked blip
Inline reply submissions in the J2CL wave view now use the blip that opened the composer and J2CL wave URLs include a GWT-compatible WaveRef hash.
✓Fixed
- Passes the inline composer's target blip through the reply submit path so replies are inserted under the clicked blip instead of the default root target.
- Carries per-blip conversation-manifest insert positions in the write session so target-specific replies keep the correct thread ordering.
- Adds a GWT-compatible hash WaveRef to J2CL route URLs while preserving the existing query route and legacy hash parsing.
May 4, 2026
J2CL editor shortcuts stay scoped while composing
Wave-level keyboard shortcuts no longer open version history while editing a blip in the J2CL wave view.
✓Fixed
- Suppresses the J2CL wave navigation version-history shortcut when the key event originates from a composed-path editor target, including Lit shadow-DOM composer events.
May 3, 2026
J2CL read-state and navigation parity
Restores per-blip unread markers and toolbar navigation behavior in the J2CL wave view.
✓Fixed
- Adds per-blip unread IDs to the selected-wave read-state response so J2CL can mark unread blips inside the wave, not only show the aggregate unread count.
- Keeps newly loaded J2CL viewport fragments aligned with the current read state so the search list and visible blip markers update after read-state refreshes.
- Preserves the focused blip while using wave navigation toolbar buttons so previous, next, recent, and unread navigation commands keep moving from the current blip instead of losing context to the toolbar.
May 3, 2026
J2CL parity harness focus stability
Stabilized the J2CL/GWT parity harness around keyboard focus and mention selection so parity regressions are caught reliably.
✓Fixed
- Made the parity tests drive J2CL blip navigation and GWT mention selection from deterministic focus state.
May 3, 2026
J2CL parity GWT mention harness stability
Stabilized the J2CL/GWT mention parity harness when driving the legacy GWT editor from Playwright.
✓Fixed
- Made the GWT mention helper verify the observable mention popup instead of strict DOM activeElement ownership.
May 3, 2026
J2CL mention visual parity coverage
Stabilizes the J2CL/GWT mention-popover visual parity gate on deterministic authored wave content.
✓Fixed
- Moves the mention-popover visual parity check off the seeded Welcome wave so inline reply hydration no longer hides the composer during audit runs.
May 3, 2026
J2CL inline reply and task parity hardening
Tightens J2CL wave-view parity for inline reply focus navigation, mention composer visual coverage, and task detail affordances.
✓Fixed
- Routes repeated next/previous blip keyboard navigation through the Lit shell so J2CL follows the same visible inline-reply DOM order as GWT.
- Keeps clicked J2CL blips selected for focus-frame navigation before keyboard or toolbar actions continue from that blip.
- Hardens parity coverage so mention popovers and task details are tested on deterministic authored waves with real task state instead of relying on seeded welcome content.
May 3, 2026
J2CL blip keyboard focus parity
Keeps J2CL blip keyboard navigation aligned with the visual focus marker during repeated shortcut use.
✓Fixed
- Moves native focus to the visually focused J2CL blip so repeated next/previous blip shortcuts continue from the current blip instead of a stale earlier target.
May 2, 2026
J2CL wave panel width parity
Makes the J2CL selected-wave panel fill the available shell width like the GWT wave panel.
✓Fixed
- Stretches the selected-wave host and card to the full main column so wide screens no longer show a narrow J2CL wave panel with unused whitespace.
May 2, 2026
J2CL wave chrome parity cleanup
Removes developer-only J2CL wave chrome from the user-facing wave panel and keeps inline replies anchored in place.
✓Fixed
- Hides raw live-update, channel, snapshot, and reply-target identifiers from the normal selected-wave and composer UI.
- Stops writable waves from showing the legacy text-button formatting toolbar outside an active inline composer.
- Preserves document scroll when opening an inline reply composer so the wave does not jump to the bottom.
May 2, 2026
J2CL New Wave composer parity
Mounts the J2CL root New Wave composer in the visible wave panel instead of the hidden legacy search card.
✓Fixed
- The root-shell New Wave button and shortcut now focus a visible create composer under the selected-wave panel.
May 2, 2026
J2CL keyboard and mention parity
Fixes repeated J2CL blip keyboard navigation and keeps the mention parity harness on the root blip toolbar.
✓Fixed
- Keeps repeated j/k shortcut ownership at the shell level so focus advances across viewport-windowed blips like GWT.
- Scopes mention-reply parity clicks to the root blip toolbar so nested inline reply buttons no longer mask production @mention chooser checks.
May 2, 2026
J2CL inline reply parity gate stabilization
Stabilizes the J2CL inline reply parity coverage so nested reply toolbars no longer mask the compose behavior under test.
✓Fixed
- Scopes the J2CL inline reply parity test to the outer blip toolbar and adds coverage for slotted Bold toolbar submits.
May 2, 2026
J2CL New Wave composer layout parity
Makes the J2CL root-shell New Wave composer match the stacked GWT compose-card layout.
✓Fixed
- Stacks the New Wave title, body, and Create wave controls vertically instead of letting them paint as one inline row.
- Removes the implementation-facing default helper text from the create composer so the card stays focused on title, body, and submit controls.
May 1, 2026
J2CL wave panel chrome parity
Improves the J2CL selected-wave layout, resize affordance, compact status chrome, and wave navigation controls toward GWT parity.
✦New
- Adds a keyboard and pointer resize splitter between the J2CL search rail and selected wave panel.
- Replaces visible raw live-status prose with compact accessible status chips for connection, saved state, and selected-wave route state.
- Wires selected-wave navigation controls to focus and scroll across rendered blips, unread blips, mentions, and recent activity.
May 1, 2026
J2CL thread chrome parity
Compacts J2CL thread affordances so inline replies no longer consume extra blip-body space.
✓Fixed
- Moves reply-thread drill-in from the body-level diamond chip to the compact blip-header chevron.
- Replaces full-width inline-thread collapse rows with small accessible icon controls.
May 1, 2026
J2CL search controls and saved searches parity
Makes the J2CL search rail controls functional for saved-search management and wave sorting while preserving the existing GWT route.
✦New
- Adds a J2CL Manage saved searches dialog backed by the existing /searches endpoint, including pinned saved-search shortcuts in the search rail.
- Replaces the placeholder J2CL sort affordance with a keyboard-accessible sort menu that writes supported orderby tokens and submits the updated query.
May 1, 2026
J2CL participant parity
Improves J2CL participant avatars, profile popup content, and add-participant suggestions toward GWT parity.
✦New
- Renders selected-wave participants as avatar buttons instead of raw text.
- Populates the participant profile overlay with selected-wave participants so profile cards show real participant identifiers.
- Adds icon-style wave header actions and frequent-contact suggestions for adding participants.
May 1, 2026
J2CL inline reply placement parity
J2CL now places reply threads at inline reply anchors when anchor metadata is available.
✓Fixed
- Render inline reply anchor mount points in plain-text blips and attach matching child threads at those anchors instead of always below the whole blip.
- Keep malformed or unanchored reply threads visible through the existing sibling fallback placement.
May 1, 2026
J2CL preserves read-surface preview hydration and inline reply anchors
The J2CL root read-surface preview route now keeps its server-rendered content visible after boot, and raw inline reply anchors are preserved as read-surface metadata for follow-up inline placement work.
✓Fixed
- Keep the J2CL read-surface preview route in preview mode after boot instead of replacing the server-rendered fixture with the normal live shell.
- Preserve inline reply anchor ids and text offsets from raw Wave blip markup through read blips and the window-render path so inline-thread placement can be restored without losing anchor metadata.
May 1, 2026
J2CL editor and task parity
Improves J2CL editor and task affordances toward GWT parity.
✓Fixed
- Stops normal J2CL blips from exposing a generic Task text toggle that strikes through the whole blip.
- Keeps existing task controls icon-style while preserving task metadata and completion events for real task blips.
May 1, 2026
J2CL attachment display-size parity
Aligns J2CL attachment previews with GWT source selection and display-size behavior.
✓Fixed
- Uses GWT-compatible preview source priority: attachment URLs for medium and large inline images with thumbnail fallback, and thumbnail URLs for small tiles and attachment cards with attachment fallback.
- Caps J2CL small, medium, and large attachment previews to GWT-compatible dimensions so oversized images do not break the wave panel.
April 30, 2026
J2CL version history can preview and restore wave versions
The J2CL wave action bar now opens a functional version-history overlay backed by the existing history API, with read-only snapshot previews and restore gating.
✦New
- Load version history from the existing /history API, show a read-only snapshot preview, and submit confirmed restores only when the server reports restore permission.
April 30, 2026
J2CL task toggle body-size follow-up
J2CL task and delete annotation writes now preserve authoritative blip body sizes during viewport refreshes and reject legacy no-size write calls with explicit errors.
✓Fixed
- Preserves server-computed blip body item counts when document refreshes replace viewport fragment entries.
- Keeps unknown body sizes as unavailable instead of deriving unsafe counts from raw text or debug XML lengths.
April 30, 2026
J2CL task toggle persistence
J2CL task toggles, task metadata updates, and blip deletion now build valid blip-body annotation deltas by retaining the target blip body between annotation boundaries.
✓Fixed
- Propagates selected-wave blip document body item counts through the J2CL read model, rendered wave-blip host, Lit task events, compose controller, and rich-content delta writer.
- Prevents J2CL from submitting task or delete mutations when the body size is unavailable, avoiding server-rejected adjacent annotation boundaries.
- Re-enables the J2CL reload and cross-context task persistence parity gate.
April 30, 2026
J2CL superscript and subscript toolbar buttons
The J2CL rich-edit toolbar now exposes superscript and subscript controls and preserves their vertical-align annotations through rich reply submission.
✦New
- Adds Superscript and Subscript buttons to the J2CL rich-edit toolbar between Strikethrough and Heading.
- Serializes <sup> and <sub> rich-composer content as style/verticalAlign annotations for DocOp round trips.
April 30, 2026
J2CL profile cards can send messages and edit own profile
The J2CL profile overlay now includes parity actions for starting a 1:1 wave with another participant and opening the existing profile editor for the signed-in user's own profile.
✦New
- Added Wavy-styled Send Message and Edit Profile actions to the J2CL profile overlay, bridged Send Message through the root compose pipeline, and emitted additional participant AddParticipant operations for direct-message creates.
April 30, 2026
J2CL New Wave keyboard shortcut
Completes the J2CL New Wave shortcut by focusing the create composer body from Shift+Cmd/Ctrl+O while preserving button title-focus behavior.
✦New
- Suppresses the New Wave shortcut while editable fields own focus, matching the J2CL parity acceptance.
- Adds trigger-aware compose.opened telemetry for New Wave button and shortcut invocations.
April 30, 2026
J2CL composer font dropdowns
Adds Font and Size dropdown actions to the J2CL rich-text toolbar so selected text can carry GWT-parity font family and font-size annotations.
✦New
- Adds Font and Size dropdowns to the J2CL rich-edit toolbar.
- Serializes selected font family and font size spans as style/fontFamily and style/fontSize annotations for rich-content DocOp round trips.
April 30, 2026
J2CL composer text and highlight colors
Adds GWT-parity text color and highlight color affordances to the J2CL rich-text composer.
✦New
- Adds a keyboard-accessible J2CL color picker using the legacy GWT color palette.
- Serializes selected text color as style/color and highlight color as GWT-compatible style/backgroundColor annotations for rich-content DocOp round trips.
April 30, 2026
G-PORT-9: J2CL visual style parity
J2CL now uses the legacy GWT light visual baseline for its production wave UI while retaining explicit dark/contrast preview variants. The parity lane also adds targeted pixel-diff gates for the search rail, open wave, composer, mention popover, and task details overlay.
✦New
- Retuned the default J2CL visual tokens, sidecar shell, search rail, open-wave blips, composer, edit toolbar, wave navigation strip, task affordance, and task metadata dialog to the GWT light palette, Arial typography, flat hairlines, compact radii, and task/mention color rules.
- Added a Playwright visual parity harness that captures comparable J2CL and GWT regions, attaches left/right/diff screenshots, and enforces a 5% maximum mismatch threshold for the five #1118 visual regions.
- Hydrates visible interior J2CL viewport placeholders after reload so newly submitted inline replies do not remain stuck on Loading wave content while the leading and trailing entries are already loaded.
- Keeps a forward-tab reachable Send or Save affordance after the compact reply and edit composer body while preserving the reply-chip action.
- Preserved issue #1129 as a task-persistence blocker only: the new task overlay gate compares dialog visuals without claiming J2CL task toggle persistence.
April 30, 2026
G-PORT-5: Mention autocomplete read-surface parity
J2CL mention replies now persist and reload with the same read-surface affordance expected from the legacy GWT mention flow. The parity lane also adds a focused visual regression harness that compares the active J2CL suggestion row against the GWT mention popup without loading unrelated wave content.
✦New
- Persisted J2CL mention replies render @mentions as highlighted read-surface chips after reload, matching the GWT mention annotation surface instead of reducing them to plain text.
- The J2CL read surface derives mention chips from annotation ranges and visible-window read blip text, preserving lazy viewport loading while still hydrating only the relevant blips in view.
- The GWT mention popup and annotation renderer now expose stable parity attributes so J2CL/GWT E2E tests can compare keyboard state, mention addresses, and focused visual rows directly.
April 30, 2026
Fix DOM XSS vulnerability in UI builder inline JavaScript
Replaced innerHTML assignment with innerText in TagsViewBuilder and ParticipantsViewBuilder.
✓Fixed
- Replaced innerHTML assignment with innerText in TagsViewBuilder and ParticipantsViewBuilder to fix a potential DOM-based XSS.
April 29, 2026
G-PORT-4: Inline reply + working compose toolbar (default on)
The j2cl-inline-rich-composer feature flag now defaults to ON. Every visitor at /?view=j2cl-root sees the inline contenteditable composer at the chosen blip with a real selection-driven format toolbar — clicking Bold / Italic / Underline / Strikethrough / lists / link applies formatting to the active range, and Send submits the reply through the rich-content compose path as a new blip. The legacy <composer-inline-reply> textarea is retained as the per-participant rollback path; ops can still opt out individual users via scripts/feature-flag.sh disable j2cl-inline-rich-composer <address>.
✦New
- Inline rich-text reply composer is now the default at /?view=j2cl-root, replacing the textarea-shaped <composer-inline-reply> that previously painted a non-functional editor wall in the right column.
- Toolbar buttons (bold, italic, underline, strikethrough, bulleted list, numbered list, link) now apply formatting to the selected range AND light with the cyan signal accent when the cursor sits inside formatted content.
- Send delivers the reply through the rich-content component path with the composer-applied formatting preserved in the sent content, but the current J2CL read-side blip renderer still displays the new blip body as plain text.
April 29, 2026
G-PORT-4 follow-up: GWT inline-reply parity automation
The J2CL <-> GWT parity E2E suite now drives the legacy GWT inline reply, Bold toolbar, and Done/send path with stable inert hooks so compose parity is checked end to end.
✓Fixed
- Added stable GWT-only test hooks and Playwright coverage for replying inline, applying Bold, finishing the edit, and observing the submitted reply as a new GWT blip.
April 29, 2026
G-PORT-8 follow-up: stable J2CL pin and archive state
The J2CL top-of-wave action bar now keeps server-published pin and archive state tied to the selected wave so stale optimistic state cannot leak across wave changes.
✓Fixed
- Track the source wave id on the J2CL nav-row folder-state attributes, preserve matching server-published pin/archive state during controller hydration, and clear stale optimistic state when the selected wave changes.
April 29, 2026
G-PORT-8: Top-of-wave action bar parity
Clone the GWT top-of-wave action-icon strip 1-to-1 into J2CL. The <wavy-wave-nav-row> now renders the ten GWT ViewToolbar.java glyphs (recent, next-unread, prev, next, end, prev-@, next-@, archive, pin, version-history) as SVG icons with native title tooltips, and a new pure-JS controller wires archive/pin clicks to the existing /folder servlet POST and the version-history click to the existing <wavy-version-history> overlay. Also fixes a server-side bug where /folder rejected pin/archive on a freshly-created wave because the persisted wavelet store had not yet recorded any wavelets.
✦New
- Replaced text labels in <wavy-wave-nav-row> with the GWT ViewToolbar SVG glyphs (1.75 stroke, round caps, 18px on a 24-unit viewBox). Tooltips ride on the native title attribute; aria-pressed reflects the pinned/archived toggle state for AT.
- Added j2cl/lit/src/controllers/wave-action-bar-controller.js, a pure-JS controller that binds to each <wavy-wave-nav-row> via a MutationObserver and translates wave-nav-archive/pin/version-history-requested events into POSTs to /folder/?operation=… and into open/close on <wavy-version-history>. Optimistic flip + rollback on non-200 mirrors the GWT setDown(true) feel.
✓Fixed
- FolderServlet.verifyWaveVisibleToParticipant now falls back to probing the conv+root wavelet name when waveletProvider.getWaveletIds returns an empty set (a freshly-created wave that is alive in memory but not yet flushed to the persistent store). Mirrors the in-memory fallback already used by SimpleSearchProviderImpl.expandConversationalWavelets and unblocks pin/archive on Welcome waves seeded at sign-in time. Affected both GWT and J2CL views.
- J2CL viewport growth now preserves read-blip chrome metadata across fragment merges. Existing loaded blips keep their author/time/task metadata, and newly-loaded fragments use the selected digest author/time as a conservative display fallback until authoritative per-blip metadata arrives.
✦New
- Added wave/src/e2e/j2cl-gwt-parity/tests/wave-actions-parity.spec.ts. The J2CL half registers a fresh user, opens the welcome wave, then exercises pin → in:pinned → unpin → archive → restore on a single mounted nav-row with each click awaiting the controller's wavy-folder-action-completed event so the test does not race the search index. The GWT half asserts the same /folder POST wire shape (operation=pin|unpin and operation=move&folder=archive|inbox) by intercepting the network request, plus a smoke check that the version-history click does not crash the shell.
April 29, 2026
G-PORT-7 follow-up: mention popover keyboard parity coverage
The J2CL/GWT parity harness now keeps mention-popover ArrowDown, ArrowUp, and Enter behavior live instead of deferred, so keyboard regressions are caught before J2CL root cutover.
✓Fixed
- Enabled J2CL mention-popover keyboard parity coverage with live participant hydration, ArrowDown/ArrowUp navigation, Enter chip insertion, and explicit GWT follow-up annotation for the remaining #1121 harness gap.
April 29, 2026
Keyboard shortcuts for the J2CL view
Sign in to the J2CL view and you can now move between blips, open a new wave, and dismiss dialogs without leaving the keyboard.
✦New
- Press j or k to move blip focus down or up in the open wave
- Press Shift+Cmd+O (Mac) or Shift+Ctrl+O to open the New Wave compose surface
- Press Esc to close the topmost open dialog or popover; press Esc again to drop the focused blip selection
- Press Enter inside the search box to refresh search results
April 29, 2026
G-PORT-7 follow-up: stable J2CL blip keyboard focus
The J2CL read surface now keeps Java-owned j/k blip focus navigation synchronized with the Lit host focus marker, so repeated shortcut presses advance visibly between blips.
✓Fixed
- Mirror Java read-surface keyboard focus into the Lit `focused` host attribute and clear it alongside the existing class/data focus hooks.
April 29, 2026
G-PORT-6: Tasks + done state parity (E2E)
Adds the J2CL <-> GWT tasks-parity Playwright spec. The GWT half drives the full insert-task + done + reload + cross-context contract on ?view=gwt; the J2CL half asserts the per-blip <wavy-task-affordance> optimistic UI flips data-task-completed on click. Reload + cross-context J2CL persistence is gated behind issue #1129 (J2CL toggle delta emits adjacent annotation boundaries the wavelet validator rejects) -- once that lands, the J2CL test.fixme block goes live with no rewrites.
✦New
- Add wave/src/e2e/j2cl-gwt-parity/tests/tasks-parity.spec.ts asserting GWT task insert + done + reload + cross-context, plus the J2CL per-blip task-toggle optimistic UI.
April 29, 2026
G-PORT-5 follow-up: J2CL mention reply participant timing
The J2CL inline reply composer receives selected-wave participants before the write-session reply target finishes hydrating, allowing mention autocomplete replies to submit without test-only participant seeding.
✓Fixed
- Project selected-wave participants into the inline reply composer independently of full write-session readiness while keeping reply submit gated on the real server write session.
April 29, 2026
G-PORT-5: Mention autocomplete parity
Typing `@<query>` in the J2CL inline composer now opens a mention suggestion popover anchored at the caret. Arrow keys navigate the candidate list, Enter or Tab inserts a mention chip carrying the picked participant's address, and the chip round-trips through the doc model as a `link/manual` annotation. The popover keyboard / focus contract was rewritten so the composer body retains focus while the popover is open — fixing the regression that prevented arrow-key navigation from advancing the popover's highlight (follow-up #1125).
✦New
- Mention popover is view-only: the composer body owns ArrowUp/Down/Enter/Tab/Escape end-to-end. The popover no longer steals focus from the contenteditable, so the caret stays in place for natural typing while the suggestion list is visible.
- Mention options are non-focusable role=option divs that preventDefault on mousedown — clicking a candidate cannot transfer document.activeElement away from the composer body and trip the blur-dismiss path.
- Inline composer now mirrors the active wave's participants on mount, not only on the next full controller render — the popover is mention-ready from its first paint when the user clicks Reply.
April 29, 2026
G-PORT-4 follow-up: deterministic typing in inline-reply E2E
Stabilises the J2CL <-> GWT parity E2E by replacing page.keyboard.type with a single execCommand("insertText") call inside the contenteditable composer body. keyboard.type drops both leading and trailing characters when Lit's input listener races the keystrokes (observed 5/5 fail with 'ello world mo' instead of 'hello world ...' both locally and in CI). execCommand fires real beforeinput/input events through the browser's text-editing pipeline, so wavy-composer's mutation observer and draft-change listener see the canonical text. No production code changes.
✓Fixed
- Replaced page.keyboard.type with execCommand("insertText") in wave/src/e2e/j2cl-gwt-parity/tests/inline-reply-parity.spec.ts so the J2CL inline-reply E2E no longer drops leading/trailing characters under CI load.
April 29, 2026
G-PORT-3: Wave panel + threaded reading parity hooks
Internal-only: cross-view DOM hooks and a Playwright spec asserting the J2CL <-> GWT wave-panel reading surface renders parity chrome (avatar, author, timestamp), advances focus on ArrowDown, and folds collapse chevrons. No user-visible behavior changes.
✦New
- Stamped data-blip-id, data-blip-author, data-blip-time, and data-blip-focused on both the J2CL <wave-blip> and the GWT BlipViewBuilder/BlipMetaViewBuilder/FullStructure surfaces so the new wave-reading-parity Playwright spec can target a single selector across both views.
✓Fixed
- Scoped J2clReadSurfaceDomRenderer.enhanceBlips to outer <wave-blip> elements only — pre-fix, j/k navigation walked through descendant <wavy-blip-card> / <reaction-row> elements that share data-blip-id for plugin-context lookup. Also kept BlipMetaDomImpl's setAuthorAddress/setTime/setTimeTooltip in step with the new parity attributes so live updates do not freeze them at first-paint values.
April 29, 2026
G-PORT-2: Search panel parity (clone GWT digest cards)
Clone the GWT search rail layout into the J2CL <wavy-search-rail-card> and <wavy-search-rail> elements so the inbox digest list looks the same on ?view=j2cl-root and ?view=gwt. Adds a panel-level refresh / sort / filter action row, mirrors the GWT digest DOM shape (.inner > .avatars + .info + .title + .snippet), and tags both views with shared data-digest-* parity selectors so the new Playwright test can resolve the same DOM on either view with one selector.
✦New
- Cloned GWT digest-card layout into J2CL <wavy-search-rail-card>: avatars float left, timestamp + msg count + pin glyph at the top-right .info block, bold title and ellipsised snippet below.
- Added a panel-level action row to <wavy-search-rail> with refresh / sort / filter buttons mirroring the GWT SearchPresenter toolbar; the filter button toggles the existing filter chip strip open/closed.
✦New
- Tagged the GWT DigestDomImpl.ui.xml and the J2CL search rail card with shared data-digest-card / data-digest-{avatars,title,snippet,msg-count,time} selectors so a single Playwright selector resolves digest cards on either ?view=gwt or ?view=j2cl-root.
- Added wave/src/e2e/j2cl-gwt-parity/tests/search-panel-parity.spec.ts asserting refresh affordance + digest list parity between the two views.
April 29, 2026
G-PORT-1: J2CL ↔ GWT parity E2E foundation
Internal-only: stand up a Playwright harness that runs against ?view=j2cl-root and ?view=gwt as the acceptance gate for the G-PORT roadmap; no user-visible behavior changes.
✦New
- Added wave/src/e2e/j2cl-gwt-parity/ Playwright harness with shared J2CL and GWT page objects, fresh-user fixture, and a smoke test asserting both views bootstrap. Wired the J2CL ↔ GWT Parity E2E CI check that every later G-PORT slice will use as its acceptance gate.
April 28, 2026
J2CL density tuning
The /?view=j2cl-root shell tightens digest-card padding, widens the wave panel, and removes the top whitespace gap so proportions align with the Wavy design mockup.
✓Fixed
- Narrowed the J2CL shell rail to 248-280 px (was 260-296 px) and trimmed the wave panel inner inset so the wave panel widens by ~16-32 px on a 1440 viewport.
- Tightened the wavy-search-rail-card digest stack via density tokens (--wavy-rail-card-padding-*, --wavy-rail-card-gap, --wavy-rail-card-snippet-mb) so visible cards land in the 92-120 px range from 01-shell-inbox-with-waves.svg.
- Collapsed the perceptible top whitespace gap between the 64 px header bar and the first row of wave content.
April 28, 2026
J2CL shell chrome rebuild
The /?view=j2cl-root shell composes as a full-bleed header bar plus rail and wave panel that match the Wavy design mockup.
✦New
- Rebuilt the J2CL root shell layout to match 01-shell-inbox-with-waves.svg: full-bleed header with the SupaWave brand on the left, rail on the left with digest cards, and the wave panel filling the remaining viewport.
- Hid the empty Plugins rail-extension panel on the user route and removed the redundant J2 SupaWave J2CL Root Shell developer brand string.
April 28, 2026
J2CL open wave: per-blip chrome
Blips on the J2CL open-wave route now render with the visual chrome from the J-UI mockups: colored author avatar, name + relative timestamp header, hover/focus toolbar pill, root vs reply avatar sizing, and a thread chevron when a blip has replies.
✦New
- Per-author colored avatar with deterministic 4-entry palette
- Author + relative timestamp header above the blip body
- Pill-shaped per-blip toolbar with glyph-prefixed Reply / Edit / Delete / Link entries; focused-blip variant repaints with cyan-soft fill
- Thread chevron next to the avatar on blips with replies
April 28, 2026
J2CL functional-UI Wavy mockups
Added eight Wavy-aesthetic SVG mockups plus a design memo grounding every slice of the J2CL functional-UI roadmap (J-UI-1..8) to the existing Lit components and Wavy Firefly Signal tokens.
✦New
- Added docs/superpowers/mockups/2026-04-28-j2cl-functional-ui/ with eight SVG surfaces covering shell+inbox, threaded reading, inline composer, task toggle states, new-wave flow, mobile, dark mode, and server-first paint
- Added a design memo mapping each surface to its J-UI slice, parity-matrix rows, and existing Lit components in j2cl/lit/src/elements/ and j2cl/lit/src/design/
April 28, 2026
J-UI-3: New Wave create flow
Type a title and a message, then ship the wave from the J2CL root shell. The new digest lands at the top of Inbox without a page reload and the wave opens in the right panel.
✦New
- J2CL composer surface adds a single-line title input above the body textarea so users can type both a title and a message when creating a new wave (issue #1081, R-5.1)
- New wave deltas now carry the canonical conv/title annotation (TitleHelper parity); server-side WaveDigester digestion of J2CL-created waves is a known gap and will be addressed in a follow-up
- Rail prepends a digest stub immediately after a successful create so the new wave appears without page reload while the search index catches up; stub is dropped once the server confirms or after a 30-second safety bound
- Rail's New Wave button focuses the title input on click via the wavy-new-wave-requested event
✦New
- SidecarSessionBootstrap parses session.features from the bootstrap JSON and exposes getEnabledFeatures / isFeatureEnabled — forward-compat hook for client-side per-feature gates
- j-ui-3-new-wave registered in KnownFeatureFlags as a forward-compat toggle (default off in prod). Real rollback path remains the j2cl-root-bootstrap flag that gates the J2CL UI as a whole.
April 28, 2026
V-3: Real format toolbar with icons (J2CL composer)
The selection-driven format toolbar at /?view=j2cl-root now renders as a compact icon row matching the 03-inline-rich-text-composer.svg mockup. Each button shows an SVG glyph with an aria-label/title, grouped by purpose (text formatting / block / alignment / link / clear / insert). Toggled formatting (bold, italic, underline, strikethrough, lists) lights with the cyan signal accent in the active selection.
✦New
- Replaced the 22 stacked text-labelled buttons with a compact dark pill of icon tiles, matching the Stitch "Wavy — Firefly Signal" mockup pin.
- Added theme-invariant toolbar tokens (--wavy-toolbar-pill-bg/-fg, tile size/radius, divider, active + hover backgrounds) under :root in wavy-tokens.css so the pill stays dark in light/contrast themes.
- <toolbar-button> gained an opt-in icon property; non-V-3 consumers (per-blip toolbar, etc.) keep their text rendering.
April 28, 2026
V-2: Strip developer strings from the J2CL product UI
Removes the developer-facing strings that bled into the J2CL root shell during the J-UI-1..8 sprint. The Opened wave eyebrow, the Live updates connected/reconnected status, the channel/snapshot detail line, the Read. status word, and the Reply target: b+<id> paragraph no longer render on /?view=j2cl-root for default signed-in users — the data still surfaces for developers behind a new experimental flag.
✦New
- New j2cl-debug-overlay experimental flag (default off in prod). When off, /?view=j2cl-root no longer paints Opened wave, Live updates connected., Live updates reconnected., the channel ch<N> · snapshot v<NNN> URL line, the bare Read. status word, or the Reply target: b+<id> paragraph. When on, every developer string returns in the same DOM location. Enable per participant via scripts/feature-flag.sh enable j2cl-debug-overlay.
April 28, 2026
J-UI-8: Server-first first-paint of selected wave
Hardens the J2CL root-shell first-paint guarantees from the parity audit — locale-aware <html lang>, aria-busy on the server-first snapshot card until JS replaces it, and a flag-gated <noscript> banner so visitors with JavaScript disabled understand the static read-only state.
✦New
- On /?view=j2cl-root the <html lang> attribute now reflects the viewer's account locale (sanitised to a BCP-47 shape; falls back to en for unknown / hostile values) so the static first paint is AT-correct without waiting on JS — addresses parity matrix R-6.1 'locale text respects user preference'.
- The server-first selected-wave card now ships with aria-busy="true" on the snapshot path; J2clSelectedWaveView.clearServerFirstMarkers clears it on the first non-preserved live render, so AT clients are told the pre-upgrade content is in flux (parity R-6.3 'ARIA quiesce on upgrade').
- New j2cl-server-first-paint experimental flag (default off in prod). When enabled, /?view=j2cl-root emits a <noscript> info banner explaining that compose, reactions, and live updates require JavaScript. Enable per participant via scripts/feature-flag.sh enable j2cl-server-first-paint.
April 28, 2026
J-UI-7: Live unread cue + read-state hooks for the J2CL search rail
When the J2CL search rail's per-user unread count changes (decrement on open, increment on a peer reply), the matching <wavy-search-rail-card> now fires a host-level cyan signal-pulse and refreshes its accessible name so the live update is visible and announceable. Read state is exposed to CSS via :host([unread-count="0"]); the legacy DOM digest gains a matching data-read attribute and aria-label refresh.
✦New
- <wavy-search-rail-card> reflects unreadCount to the unread-count attribute, fires a host-level pulse on every post-initial change (the pulse box-shadow lives on the host, so the 1->0 zero-out case still has a visible cue after the badge disappears), and refreshes the article aria-label so AT consumers hear 'Sprint review. 2 unread.' or 'Sprint review. Read.'.
- Rapid back-to-back unread mutations (e.g. a server push landing while the previous pulse is still ringing) keep the ring visible for the full duration after the latest pulse instead of being truncated by an earlier timer.
- Legacy J2clDigestView toggles a data-read attribute on the focusable button root and refreshes its aria-label to match the live count.
✓Fixed
- Closes the visible-cue gap recorded as R-4.4 FAIL in the 2026-04-26 J2CL/GWT parity audit. The data path (read-state fetch, search-panel listener, model mutation) was already wired across #931, #1056, and #1079; this slice attaches the cue and the assistive-technology surface so the live decrement is actually noticeable when it does fire.
April 28, 2026
J-UI-6 — Per-blip task toggle done state persists across reload + live updates
Closes the R-5.4 parity gap where the F-3.S2 task-toggle wrote the task/done annotation correctly but the J2CL ?view=j2cl-root read surface never read it back, so reload + live updates from other clients silently dropped the strikethrough/checkmark. J2clReadBlip + J2clReadWindowEntry now carry persisted taskDone, taskAssignee, taskDueTimestamp through both the flat-render and renderWindow paths; the projector reads task/done, task/assignee, task/dueTs annotations directly off the wavelet DocOps; the read renderer writes data-task-completed / data-task-assignee / data-task-due-date on each <wave-blip> host; and the fast-path equality checks (sameReadBlip + sameWindowEntry) include task state so live updates actually repaint instead of being treated as no-ops. Visible / aria / live strings on <wavy-task-affordance> are now property-driven so the Java view (or a future lit-i18n slice) can localize without re-architecting the element.
✦New
- R-5.4 reload + live-update persistence: opening a wave with a previously-completed task blip now paints the strikethrough/checkmark immediately (no flash of unchecked state), and toggling done from a second client re-renders the first client without manual reload.
- Associated metadata overlays preserved (R-5.4): the task-metadata popover re-mounts with the persisted assignee + due-date when reopened on a reloaded blip, instead of starting from empty defaults.
- Localizable labels (R-5.4 "Labels translate"): wavy-task-affordance hoists every visible / aria-label / aria-live string to a settable lit property (labelToggleOpen, labelToggleDone, labelAriaCheck, labelAriaUncheck, labelDetails, labelAnnounceDone, labelAnnounceOpen). English defaults preserved verbatim so the F-3.S2 baseline behavior is unchanged.
✓Fixed
- J2clReadBlip + J2clReadWindowEntry: new taskDone / taskAssignee / taskDueTimestamp fields with builder-style withTaskDone(boolean) helpers and loadedWithTaskMetadata(...) factory. Existing constructors stay backward-compatible — the new fields default to false / empty / UNKNOWN_DUE_TIMESTAMP.
- J2clSelectedWaveProjector: new documentTaskDone / documentTaskAssignee / documentTaskDueTimestamp helpers read directly from wavelet DocOp annotations (per project memory feedback_search_no_conversation_model — never use the conversation model for filter/state reads). Plumbed through extractDocumentReadBlips and enrichReadBlipMetadata.
- J2clSelectedWaveProjector.enrichWindowEntriesFromReadBlips: new public helper that grafts per-blip metadata from the enriched read-blip list onto matching window entries. J2clSelectedWaveView calls it before passing entries to renderWindow so the dominant production code path emits the same data-task-completed attribute the flat-render path does.
- J2clReadSurfaceDomRenderer: applyTaskState helper writes/clears data-task-completed, data-task-assignee, and data-task-due-date based on the read-blip state. Explicit clear branch ensures done → open transitions repaint the body (otherwise the F-2 fast-path would carry the stale attribute over). Wired into both renderBlip and the renderWindow → renderBlip adapter.
- sameReadBlip + sameWindowEntry: include taskDone / taskAssignee / taskDueTimestamp in the fast-path equality so a same-wave update that only flips task/done is not treated as a no-op (previously the strikethrough never repainted on live updates from other clients).
- Tests: 8 new Java cases on J2clSelectedWaveProjectorTest, 7 new Java cases on J2clReadSurfaceDomRendererTest, 1 new lit case on wavy-task-affordance.test.js (label overrides), 2 new lit cases on wave-blip.test.js (strikethrough rendering + attribute propagation). Total Java suite: 858/858 passing (131 browser-only cases skipped on JVM); total lit suite: 524/524 passing.
April 28, 2026
J-UI-5: Inline rich-text composer + working toolbar
Clicking Reply on a blip in /?view=j2cl-root now opens an inline contenteditable composer at that blip with a selection-driven format toolbar. Bold / italic / underline / strikethrough / list / link / unlink / clear-formatting apply to the active range and the toolbar's pressed state mirrors the current selection.
✦New
- Inline reply composer is contenteditable, not a textarea, and mounts at the chosen blip position rather than detached at the bottom of the right rail.
- Selection-driven format toolbar applies bold (<strong>), italic (<em>), underline (<u>), strikethrough (<s>), unordered list (<ul>), ordered list (<ol>), link (via <wavy-link-modal>), unlink, and clear-formatting to the active range; toolbar pressed state lights up when the caret sits inside a matching wrap tag.
- Reply submit forwards a per-fragment annotated-component list, so the user's formatting round-trips through the conversation-model DocOps (uses the existing fontWeight / fontStyle / textDecoration / link/manual / list/unordered / list/ordered annotations the GWT/J2CL read codec already understands; no new schema).
- Gated behind the new j2cl-inline-rich-composer experimental flag (default off in prod). Enable for a participant via scripts/feature-flag.sh enable j2cl-inline-rich-composer.
April 28, 2026
J2CL Open-Wave Threaded Reading View
The J2CL open-wave panel now nests reply threads under their parent blip instead of flattening every blip into a single root thread. R-3.1 hard-acceptance for deep nesting + reply ordering matching the conversation model is met; the existing focus frame, collapse toggles, and aria contracts inherit the nested DOM unchanged.
✦New
- SidecarTransportCodec parses the wave's conversation manifest document into a SidecarConversationManifest as a side-output of the same scan that already extracts per-document text (no extra protocol round-trip).
- J2clSelectedWaveProjector.applyConversationManifest grafts manifest parentBlipId / threadId onto each J2clReadBlip and reorders the read-blip list into manifest depth-first pre-order so reply ordering matches the conversation model.
- J2clReadSurfaceDomRenderer builds a nested <div class="thread inline-thread" data-thread-id="..."> container under each parent blip's host whenever the input blips carry a non-empty parentBlipId; legacy waves with no conversation manifest fall through to the existing flat-list path with no behavior change.
- wavy-thread-collapse.css adds a 12px left indent + cyan hairline rail on inline-thread containers so deep nesting is visually distinguishable on both light and dark themes.
April 28, 2026
J-UI-2: Folder + filter chip switching is functional
On /?view=j2cl-root the saved-search folders (Inbox / Mentions / Tasks / Public / Archive / Pinned) and the filter chips (Unread only / With attachments / From me) now drive real query changes. The active folder + chip composition round-trips through the URL so back/forward, reload, and deep links preserve the active filter.
✦New
- Each saved-search folder click reaches the search backend with its canonical query token (in:inbox, mentions:me, tasks:me, with:@, in:archive, in:pinned) and the rail repopulates.
- Filter chips compose with the active folder; toggling a chip preserves user-typed tokens, dedupes case-insensitively, and pushes the composed query into the URL so reload and deep links work.
- Server-side: the rail's Unread only chip (is:unread) now aliases unread:true in both the in-memory and Solr-backed search providers. has:attachment and from:me parse, compose, and round-trip cleanly but their server-side execution is deferred to follow-up issues.
- Saved-search clicks announce the active folder via aria-live and move keyboard focus to the active folder button so screen-reader and keyboard users land predictably (R-4.5).
- Builds on the J-UI-1 j2cl-search-rail-cards flag — no new flag is needed.
April 28, 2026
J-UI-1: Wave list renders in the search rail
On /?view=j2cl-root the saved-search rail now renders one rich digest card per wave (avatar, title, snippet, count, timestamp). Clicking a card opens the wave and updates the URL.
✦New
- Search digests on the J2CL root view now render as <wavy-search-rail-card> elements inside <wavy-search-rail>, replacing the empty rail surface flagged in the 2026-04-26 parity audit.
- Card click routes the URL via the existing J2clSidecarRouteController so deep links and back/forward survive reload.
- Gated behind the new j2cl-search-rail-cards experimental flag (default off in prod). Enable for a participant via scripts/feature-flag.sh enable j2cl-search-rail-cards.
April 27, 2026
Sign-out routes through the public landing page; clearer social-auth misconfig log
Signing out (or any signed-out hit on /) now lands on the marketing page instead of an empty Wave shell, and admins get a one-time log when the social-auth flag is on but no provider has both a client_id and a client_secret configured.
✓Fixed
- WaveClientServlet now serves the public landing page for signed-out visitors hitting bare "/" (no view, no query string), regardless of the j2cl-root-bootstrap flag, so the marketing surface is the first thing a logged-out user sees.
- SignOutServlet's no-redirect fallback now returns 302 "/" instead of a static "Logged out." HTML page, so users who reach /auth/signout without an ?r= target also land on the public site instead of a blank page.
✦New
- AuthenticationServlet emits a one-time WARNING when the 'social-auth' feature flag is globally enabled but no provider has both core.social_auth.<provider>.client_id and core.social_auth.<provider>.client_secret set, surfacing the missing OAuth configuration that silently hides the social sign-in buttons.
April 27, 2026
F-3.S2 follow-up: stop dropping task writes on no-op session refresh; force popover re-render on submit
Plugs two bugs that landed in F-3.S2 (#1066) but were posted by the post-merge bot review after auto-merge fired: a same-wave session refresh used to silently drop the deferred task-toggle / metadata write, and the metadata popover stayed visually open after submitting unchanged fields.
✓Fixed
- J2clComposeSurfaceController#onTaskToggled and #onTaskMetadataChanged now compare write sessions by selectedWaveId rather than by reference. A no-op session refresh on the same wave (tag set update, participant churn) replaces the J2clSidecarWriteSession instance with one that has the same wave id; the previous reference-equality guard treated this as a wave switch and silently discarded the user's write. The new logical-session check accepts the refreshed session, while signed-out and null-session guards still drop writes on real wave switches.
- wavy-task-affordance#_onMetadataSubmit now calls requestUpdate() after closing the popover. _popoverOpen is internal state, not a Lit reactive property, so submitting the popover with unchanged assignee + due date used to skip the re-render that tears the inner <task-metadata-popover> down — leaving the dialog visually open until the next reactive change. The forced update mirrors how _openDetails and _onPopoverClose already behave.
- Regression coverage: J2clComposeSurfaceControllerTest gains onTaskToggledSurvivesNoOpSessionRefresh, onTaskMetadataChangedSurvivesNoOpSessionRefresh, and onTaskToggledDropsWriteOnWaveSwitch (each defers bootstrap resolution, then either replays the same wave id or switches waves); wavy-task-affordance.test.js gains a case that opens the popover, submits with the existing assignee + due date, and asserts the dialog actually leaves the renderRoot.
April 27, 2026
F-2 follow-up: wavy chrome is the only rendered surface
The J2CL root view now renders only the wavy F-2 chrome — no duplicate light search rail, no flat blip-id headers, no permanent editor-toolbar wall.
✓Fixed
- Removed the default <slot> from <wavy-search-rail> shadow DOM so the SSR'd light rail no longer projects under the rendered chrome as a duplicate "Saved searches" surface.
- Stopped the J2CL read renderer from falling back to the literal label "Blip <id>" in the posted-at slot when a blip has no real modified time — the avatar / author header now owns the metadata row and the AT label moves to aria-label.
- Gated the J2CL toolbar's edit-formatting actions (Bold / Italic / ... / attachments) on an active edit session and collapsed <composer-inline-reply> until the controller flips it available so the read surface no longer shows a permanent editor-toolbar wall.
April 27, 2026
F-3 Slice 4 — Attachments + DocOp round-trip + umbrella closeout
F-3.S4 closes the umbrella issue #1038 by shipping the must-ship attachment loop (H.19 paperclip in the floating wavy-format-toolbar, drag-drop on the composer body, paste-image already shipped, and the long-deferred F.6 Delete gateway wiring through a styled wavy confirm dialog), the daily rich-edit DocOp round-trip serialiser arms (lists / blockquotes / inline links via `wavy-composer.serializeRichComponents`), and a final per-row parity rollup chaining S1+S2+S3+S4 fixtures. Six narrowly-scoped follow-ups (FU-1..FU-6) are filed as separate issues so the umbrella closes honestly against today's matrix gate.
✦New
- Attachments (R-5.6, H.19 + drag-drop + paste-image + F.6 Delete): the floating <wavy-format-toolbar> now carries a dedicated `attachment-insert` paperclip button next to Insert task. Clicking it bubbles `wavy-format-toolbar-action {actionId: "attachment-insert"}` which the existing `J2clComposeSurfaceController.handleAttachmentToolbarAction` path routes into `view.openAttachmentPicker()` and through the upload plumbing already shipped pre-F-3 (#1017, #1022, #1023, #1024). Drag-drop adds three new body listeners (dragover / dragleave / drop) on the composer body; the drop handler dispatches a `wavy-composer-attachment-dropped` CustomEvent the view forwards to a new `Listener.onDroppedFiles` listener (default delegates to onAttachmentFilesSelected so existing test doubles compile unchanged), with a separate `compose.attachment_dropped` telemetry event recording outcome + kind. The body reflects `data-droptarget="true"` while a drag is in progress so CSS can show a soft border-tint hint.
- F.6 Delete gateway wiring (deferred from S1; S1 only had the event scaffold): <wave-blip-toolbar> now ships a dedicated Delete button emitting `wave-blip-toolbar-delete`. <wave-blip> re-emits `wave-blip-delete-requested {blipId, waveId}`. The J2CL view dispatches a body-level `wavy-confirm-requested` CustomEvent carrying a request id keyed by the blip id; a new `<wavy-confirm-dialog>` element (F-0 wavy recipe, body-mounted via `ensureWavyConfirmDialogMounted`) opens, awaits Cancel / Delete, and answers with `wavy-confirm-resolved`. On confirm, the controller's new `Listener.onDeleteBlipRequested(blipId)` fetches the bootstrap and submits a delta via a new `J2clRichContentDeltaFactory.blipDeleteRequest` that writes a `tombstone/deleted=true` annotation across the blip body — symmetric with the existing taskToggleRequest single-key writer. The implementation explicitly forbids Window.confirm per the project memory rule `feedback_no_browser_popups.md`.
- DocOp round-trip for daily rich-edit affordances (R-5.7): `wavy-composer.serializeRichComponents` walks the contenteditable body and now emits annotated components for `<ul><li>` (`list/unordered`), `<ol><li>` (`list/ordered`), `<blockquote>` (`block/quote`), and `<a href>` (`link/manual`). The existing controller path consumes these via `J2clComposerDocument.Builder.annotatedText(...)` and the rich-content delta factory writes the matching annotation start/end pair around the inner text. New unit tests on `J2clRichContentDeltaFactoryTest` (4 round-trip cases for list/unordered + list/ordered + block/quote + link/manual + 1 case for blipDeleteRequest + 1 reject-blank) and new lit cases on `wavy-composer.test.js` (4 walker arms) close the loop and would FAIL on origin/main (the walker arms are new in this slice).
- Final parity rollup: new `J2clStageThreeFinalParityTest` chains S1+S2+S3+S4 fixtures via `@Suite.SuiteClasses` and prints a row-by-row coverage report listing R-5.1 ✓ R-5.2 ✓ R-5.3 ✓ R-5.4 ✓ R-5.5 ✓ R-5.6 ✓ R-5.7 ✓ plus a footer enumerating the FU-1..FU-6 follow-ups deferred outside the umbrella. New `J2clStageThreeComposeS1ParityTest` pins source-level wire contracts for R-5.1 + R-5.2 (which previously had no per-row class to pin them outside the lit web-test suite).
- Telemetry: `compose.attachment_dropped {outcome, kind, count}` distinguishes drag-drop from explicit picker selection; `compose.blip_deleted {outcome}` covers signed-out / missing-blip / no-selected-wave / failure-build / failure-bootstrap / failure-submit / success states. Both routed through the existing J2clClientTelemetry.Sink.
- Tests: 1 new lit element + 6 fresh lit web-test-runner cases for <wavy-confirm-dialog>; 8 new cases extending wavy-composer.test.js (drag-drop end-to-end + serializeRichComponents walker arms); 1 new case on wavy-format-toolbar.test.js (H.19 paperclip); 1 new case each on wave-blip-toolbar.test.js and wave-blip.test.js (Delete button + re-emit). Total lit suite: 56 files / 499 passing (up from 55 / 483). Java tests: 6 new cases on J2clRichContentDeltaFactoryTest (list/unordered + list/ordered + block/quote + link/manual round-trip + tombstone/deleted + reject-blank) and 6 new cases on J2clComposeSurfaceControllerTest (drag-drop telemetry + empty drop + delete success + delete signed-out + delete blank-blip + delete no-selected-wave). Total J2CL Maven tests: 759 passing (up from 753). New parity fixtures: J2clStageThreeComposeS1ParityTest (5 source-pin tests) + J2clStageThreeComposeS4ParityTest (15 source-pin tests) + J2clStageThreeFinalParityTest (rollup with chained suite gated by -Dj2cl.run.chained.parity=true).
- Follow-ups deferred outside the umbrella close (filed as separate focused issues, do NOT block #1038): FU-1 H.5 Superscript + H.6 Subscript; FU-2 H.7 Font family + H.8 Font size; FU-3 H.10 Text color + H.11 Highlight color + <wavy-colorpicker-popover>; FU-4 D.4 Add participant + D.5 New wave with current participants + D.6 Public/private toggle + D.8 Lock/unlock root blip; FU-5 L.2 Send Message → 1:1 wave + L.3 Edit Profile; FU-6 B.3 Shift+Cmd+O New Wave keyboard shortcut. Each follow-up references #1038 as historical context.
April 27, 2026
F-3 Slice 3 — Reactions (add/remove, live-count chips)
F-3.S3 closes the per-blip reaction loop: the F-1-era <reaction-row> + <reaction-picker-popover> primitives mount on every <wave-blip> in the J2CL read surface, the picker exposes the GWT-parity emoji set, and toggles round-trip through the conversation model via a new J2clRichContentDeltaFactory.reactionToggleRequest that emits element-tree ops against the `react+<blipId>` data document. Chips adopt the F-0 violet token contract and pulse on count-up via `--wavy-pulse-ring`. Telemetry covers `compose.reaction_toggled`.
✦New
- Reactions (R-5.5, F.8 + F.9): every <wave-blip> in the read surface now mounts a <reaction-row> in the existing F-2 reactions slot, fed by the live per-blip J2clReactionSummary list. Clicking the + button opens a body-level <reaction-picker-popover> with the GWT-parity emoji set (👍 ❤️ 😂 🎉 😮 👀, matching ReactionPickerPopup.EMOJI_OPTIONS). Picking an emoji or clicking an existing chip dispatches `reaction-pick` / `reaction-toggle` CustomEvents that the J2CL compose-surface view routes to a new J2clComposeSurfaceController.onReactionToggled(blipId, emoji) listener. The controller computes adding-vs-removing from its cached snapshot (published per-render by J2clSelectedWaveView) and emits a delta against the `react+<blipId>` data document.
- Wire contract (the load-bearing requirement): J2clRichContentDeltaFactory.reactionToggleRequest emits element-tree ops via the existing sidecar transport codec — element_start/element_end (fields 3 + 4) for toggle-on inserting `<reactions><reaction emoji=X><user address=Y/></reaction></reactions>`, retain (field 5) covering the existing root content + new sibling reaction insert when the root already exists, and delete_element_start/delete_element_end (fields 7 + 8) for toggle-off. Last-user-removal also prunes the empty `<reaction>` wrapper so the read side does not render a count-zero chip. Item-count math is unit-tested independently via the package-private reactionsRootItemCount helper (2 per empty root + 2 + 2*users per reaction).
- Visual contract (R-5.5 step 13 + step 14): <reaction-row> chips adopt --wavy-signal-violet (active border) + --wavy-signal-violet-soft (active fill) + --wavy-radius-pill (full pill) and fire a one-shot data-live-pulse attribute when a chip's count increases via supplement live-update — the CSS pulse animation hooks into --wavy-pulse-ring. Counts decreasing do NOT pulse (avoids flashing when other users remove their reactions).
- Active-for-current-user flag: J2clInteractionBlipModel exposes a new reactionSummariesForUser(address) helper that recomputes the per-summary active flag against the signed-in user's address; the J2clSelectedWaveView feeds it from a fresh hint published by the compose controller on each successful bootstrap (J2clComposeSurfaceController.setCurrentUserAddressListener) so the chip aria-pressed state ('this is your own reaction') tracks the signed-in user without an extra gateway round-trip.
- Live-count + live-pulse: the row's reactions array property is republished from J2clSelectedWaveModel.getInteractionBlips() on every render pass (J2clSelectedWaveView.publishReactionState), and <reaction-row>'s updated() lifecycle compares prev vs next counts to fire the data-live-pulse attribute on count-up. The supplement subscription that already powers F-4's mark-read pipeline drives both signals.
- Telemetry: `compose.reaction_toggled` with `{state: "added"|"removed", outcome: "success"|"failure-build"|"failure-submit"|"failure-bootstrap"}`. Routed through the existing J2clClientTelemetry.Sink.
- Plugin slot reservation: NO new slot needed; reuses the F-2 `reactions` slot on <wave-blip>. <reaction-row> mounts as a light-DOM child carrying `slot="reactions"` so plugins targeting that slot still work.
- Tests: 4 new lit web-test-runner cases extending reaction-row.test.js (violet token contract + pulse-on-count-up + no-pulse-on-count-down). 7 new Java factory tests on J2clRichContentDeltaFactoryTest covering empty-root envelope, root-exists sibling append, multi-user remove, last-user-prune, multi-emoji offset math, blank input rejection, and the reactionsRootItemCount helper. 5 new controller tests on J2clComposeSurfaceControllerTest covering empty-snapshot add, snapshot-aware remove, signed-out gating, blank-input gating, and the address listener notification on bootstrap. New per-row parity fixture J2clStageThreeComposeS3ParityTest pins 13 wire-contract assertions for R-5.5 (lit primitives, view event listeners, GWT-parity emoji set, controller listener / delta factory exposure, telemetry event names, renderer mount, view publisher hook, root-shell wiring, bundle registration). All J2CL Maven tests pass (743 total). All lit tests pass (482 total).
April 27, 2026
F-3 Slice 2 — Mentions + tasks (@-autocomplete, mention chips, per-blip task toggle)
F-3.S2 layers the two highest-frequency content affordances on top of the S1 inline composer: H.21 mention autocomplete with a violet chip that round-trips through the model as a `link/manual` annotation, and per-blip task affordances that toggle completion state via the `task/done` annotation. Adds the H.20 Insert-task toolbar action and surfaces C.13-C.15 search filters via `data-filter-token` on `<wavy-search-help>` example chips. Telemetry covers `compose.mention_picked`, `compose.mention_abandoned`, and `compose.task_toggled`.
✦New
- Mentions (R-5.3, H.21): typing @ after start-of-line or whitespace opens the F-1-era <mention-suggestion-popover> anchored at the caret. Locale-aware substring filter via `toLocaleLowerCase(document.documentElement.lang)` so Cyrillic + Turkish scripts fold correctly (e.g. `@юр` matches 'Юра' under lang=ru, `@ilk` matches 'İlker' under lang=tr). Picking a candidate inserts a contenteditable=false violet chip (`<span class="wavy-mention-chip" data-mention-id="...">@DisplayName</span>`); Backspace deletes the chip atomically. The composer's rich serializer surfaces each chip as a separate `link/manual` annotated component on submit so the chip round-trips through the existing J2clRichContentDeltaFactory path. Email addresses (e.g. `alice@example.com`) do NOT pop the suggestion sheet — the trigger only fires after start-of-line or whitespace.
- Tasks (R-5.4): each <wave-blip> mounts <wavy-task-affordance> next to the per-blip toolbar in the existing metadata slot. Single click toggles completion (emits `wave-blip-task-toggled` with `{blipId, waveId, completed}`); a separate details overflow opens the F-1-era <task-metadata-popover> for owner + due-date editing (emits `wave-blip-task-metadata-changed`). Toggle button uses role=checkbox + aria-checked + an aria-live=polite announcement region. The compose-surface controller routes both events through new J2clRichContentDeltaFactory.taskToggleRequest (`task/done`) and taskMetadataRequest (`task/owner` + `task/due`) factory methods. Completed blips reflect data-task-completed=true on the host and apply --wavy-text-quiet + line-through styling to the body.
- H.20 Insert task: <wavy-format-toolbar> adds a new toolbar button that emits `wavy-format-toolbar-action` with `actionId="insert-task"`. The composer body wraps the active selection (or inserts at caret if collapsed) in a `<ul class="wavy-task-list"><li><input type="checkbox" disabled>...</li></ul>`. The inline checkbox is display-only; the per-blip task affordance owns the model-of-record.
- C.13-C.15 search filter discoverability (R-5.4 step 8): <wavy-search-help> example chips and the tasks:user@domain code element carry data-filter-token attributes so the parity fixture can locate the filter rows without depending on copy.
- Telemetry: `compose.mention_picked` (no fields — high-cardinality on participant id), `compose.mention_abandoned` (no fields), `compose.task_toggled` (with `state="completed"|"open"`). All routed through the existing J2clClientTelemetry.Sink.
- Java compose controller: Listener interface gains onMentionPicked, onMentionAbandoned, onTaskToggled, onTaskMetadataChanged. DeltaFactory gains createTaskToggleRequest + createTaskMetadataRequest (default impls throw UnsupportedOperationException for the plain-text fallback). The rich-content factory adapter wires both through to J2clRichContentDeltaFactory. Task-toggle uses an independent gateway fetch so it cannot clobber an in-flight reply on a different blip.
- Tests: 1 new lit element + 18 fresh lit web-test-runner cases for <wavy-task-affordance>; 14 new cases extending wavy-composer.test.js (R-5.3 mention paths including locale + chip serializer); 3 new cases on wave-blip.test.js (task affordance integration); 2 new cases on wavy-format-toolbar.test.js (H.20 button); 2 new cases on wavy-search-help.test.js (data-filter-token). Total lit suite: 468/468 passing. Java tests: 12 new cases on J2clRichContentDeltaFactoryTest + 7 new cases on J2clComposeSurfaceControllerTest. New per-row parity fixture J2clStageThreeComposeS2ParityTest pins 18 wire-contract assertions for R-5.3 + R-5.4.
April 27, 2026
F-3 Slice 1 — Inline rich-text composer foundation + selection-driven format toolbar
F-3.S1 ships the inline rich-text composer foundation (R-5.1 + R-5.2) for the J2CL StageThree compose surface. Replaces the bottom-of-rail plain-textarea reply with a `<wavy-composer>` Lit element mounted inline at the originating `<wave-blip>` whenever the user clicks Reply or Edit on F-2's per-blip toolbar. Adds a selection-driven floating `<wavy-format-toolbar>` that anchors above the active selection and applies the daily-rich-edit subset (Bold/Italic/Underline/Strikethrough/Heading/lists/indent/align/RTL/link/clear-formatting). Adds the `<wavy-link-modal>` for H.17 Insert link, `<wavy-tags-row>` for I.1-I.6 wave tag editing, and `<wavy-wave-root-reply-trigger>` for J.1 click-here-to-reply. Reserves the `compose-extension` and `toolbar-extension` plugin slots per F-0. Preserves the F-2.S6 visual fix: the composer collapses to display:none until [available] and the format toolbar hides itself with a collapsed selection.
✦New
- <wavy-composer> Lit element wraps the F-0 <wavy-compose-card> recipe with a contenteditable body. Caret survives across host attribute updates (R-5.1 step 2) — the body element is cached and only writes textContent when it does not own selection. Mode attribute carries 'reply' / 'edit' / 'create' / 'wave-root' so the chip + send button label adapt. Forwards compose-extension slot.
- <wavy-format-toolbar> Lit element wraps the F-0 <wavy-edit-toolbar> recipe with the daily-rich-edit subset of H.* actions (H.1-H.4, H.9, H.12-H.18). Anchors via position:fixed + transform:translate keyed off `selection.getRangeAt(0).getBoundingClientRect()`, recomputed on selectionchange + scroll + resize through one rAF callback. Toggle state mirrors active annotations (Bold lit when selection is inside <strong>, etc.).
- <wavy-link-modal> Lit element handles the H.17 Insert-link affordance with URL + display-text fields, URL validation (http/https/mailto), and an inline error region. Esc + backdrop click + Cancel button all dismiss.
- <wavy-tags-row> Lit element ships I.3 Add-tag button, I.4 Add-tags textbox, I.5 Cancel-entry × button on top of F-2's read-only chip display. Enter in the textbox emits wave-tag-add-requested; × on a chip emits wave-tag-remove-requested.
- <wavy-wave-root-reply-trigger> Lit element ships the J.1 'Click here to reply' affordance at the bottom of the wave panel. Click dispatches wave-root-reply-requested with the active wave id; the compose view listens and mounts a wavy-composer (no replyTargetBlipId) at the read-surface bottom.
- J2clComposeSurfaceView listens for wave-blip-reply-requested + wave-blip-edit-requested CustomEvents emitted by F-2's <wave-blip-toolbar> Reply / Edit buttons and mounts a <wavy-composer> as a child of the originating blip. The legacy <composer-inline-reply> remains the canonical state mirror so the controller, model, and existing 95 controller tests stay unchanged. wavy-composer-cancelled (× / Esc) removes the inline composer.
- F-2.S6 visual fix preserved: <wavy-composer> collapses to display:none when not [available], and <wavy-format-toolbar> hides itself with a collapsed selection. Five new static-source pins on HtmlRendererJ2clRootShellIntegrationTest assert the gating contracts cannot regress silently. F-3.S1 does NOT bring back the editor-toolbar wall or the always-visible reply textarea.
- Plugin slot reservation: compose-extension on <wavy-composer> + toolbar-extension on <wavy-format-toolbar> per the F-0 contract (docs/j2cl-plugin-slots.md). composerState + activeSelection JS getters return frozen snapshots; the inner <wavy-compose-card> receives propagated state on every selection change.
- Tests: 5 new lit elements ship 45 unit tests + a 5-test integration fixture asserting the inline-composer mount/cancel lifecycle. Total lit suite: 440/440 passing. Java tests: 28/28 HtmlRendererJ2clRootShellIntegrationTest cases (5 new F-3.S1 source-pins) + 95/95 J2clComposeSurfaceControllerTest cases.
April 26, 2026
Search rail, search-help modal, and wavy header chrome on the J2CL root shell
Slice 3 of the F-2 StageOne read surface lights up the wavy left rail (search box, six saved-search folders, refresh, result count), the search-help modal documenting every supported filter and sort token, and the wavy header end-region (brand link, locale picker, notifications bell, mail icon, and user-menu trigger) on ?view=j2cl-root.
✦New
- Wavy left rail with search query box, waveform glyph, help trigger, New Wave shortcut, Manage saved searches, refresh, and result-count slot
- Six saved-search folders (Inbox, Mentions, Tasks, Public, Archive, Pinned) with the canonical query strings; Inbox selected by default; Mentions violet unread dot and Tasks amber pending count
- Per-digest result card with multi-author avatar stack (truncated at 3 + overflow chip), pinned indicator, title, 3-line snippet clamp, msg-count + signal-cyan unread pulse, and relative timestamp tooltip
- Search-help modal advertising every filter (in:, with:, creator:, tag:, unread:, title:, content:, mentions:, tasks:) and sort (orderby:date / created / creator desc/asc) token, with combination examples and a Got-it dismiss
- Wavy header chrome on the J2CL root shell: SupaWave brand link with cyan signal-dot, locale picker for the seven supported locales, notifications bell with violet unread dot, inbox/mail icon, and user-menu trigger (avatar + visible email)
April 26, 2026
Viewport-Scoped J2CL Read Surface
The J2CL root shell now delivers a viewport-clamped first paint of the selected wave and surfaces viewport telemetry counters so dashboards can see initial-window, extension, clamp, and snapshot-fallback events.
✦New
- Server-rendered first paint of the J2CL root shell clamps to the configured viewport limit (default 5 root-thread blips) and appends a visible-region terminal placeholder for AT continuity.
- Server HTML now declares per-blip role (listitem/article), per-thread role (list/group), and a single tabindex=0 on the first root-thread blip so the static HTML is keyboard-navigable before client boot.
- J2CL client emits four new telemetry events on every selected-wave open: viewport.initial_window, viewport.extension_fetch (+ outcome), viewport.clamp_applied, viewport.fallback_to_whole_wave.
- Cold-mount selected-wave surfaces now emit a shell_swap event with reason=cold-mount so dashboards see the J2CL surface mounting content for the first time, with or without a server-first card.
April 26, 2026
F-2 Slice 6 — Demo route + integration polish + closeout
F-2 closeout slice. Part A fixes the slice 5 regression where the legacy `.sidecar-search-card` wrapper still painted as a duplicate light surface below the dark <wavy-search-rail>: the wavy-thread-collapse.css rule now uses `display: none !important` instead of `display: contents`, and the integration test + a new Lit fixture lock both the source-of-truth CSS shape and the computed display so the regression cannot ship again. Part B adds a server-rendered read-surface preview route at `/?view=j2cl-root&q=read-surface-preview` that mounts a five-blip fixture wave through the full F-2 chrome surface (depth-nav, focus frame on b2, collapsed thread under b3, reactions / mentions / tasks / attachment chips, awareness pill, pre-opened version-history + profile overlays) so design + reviewer walkthrough is a single URL. Closes the F-2 umbrella #1037; the per-row parity roll-up class chains every existing per-row parity test class and asserts the demo route renders.
✓Fixed
- Visible-rail regression fix (Part A): wavy-thread-collapse.css now hides the legacy `.sidecar-search-card[data-j2cl-legacy-search-card="hidden"]` wrapper with `display: none !important`. Slice 5 used `display: contents`, which removed the wrapper's box but kept every child visible — so the `.sidecar-digests` adoption target and `.sidecar-empty-state` paragraph painted in legacy light styling directly below the dark wavy rail. The adoption path still works because `J2clSearchPanelView.queryRequired` resolves selectors regardless of computed visibility.
- Defensive collapse on the empty compose host: `.sidecar-selected-compose[data-j2cl-compose-host="true"]:empty { display: none }` so the host never inserts a layout gap before an active edit session. The `:empty` selector lets the controller un-hide naturally by appending children.
- HtmlRendererJ2clRootShellIntegrationTest now reads the wavy-thread-collapse.css source and asserts the rule uses `display: none !important` (NOT `display: contents`); a Lit fixture in `j2cl/lit/test/legacy-rail-hidden.test.js` mounts the SSR'd structure inline and asserts `getComputedStyle(legacyCard).display === 'none'` plus `offsetParent === null` on every adoption-target child. Both regression-lock the bug at the source-of-truth and computed-style layers.
✦New
- Read-surface preview route (Part B): `/?view=j2cl-root&q=read-surface-preview` is a server-rendered fixture page that mounts the full F-2 chrome surface on a single URL — depth-nav, focus frame on b2, collapsed thread under b3 with depth-nav drill chip, reactions on b1, mention chip on b2, task chip on b3, unread badge + attachment tile on b4, awareness pill, pre-opened `<wavy-version-history open>` + `<wavy-profile-overlay open>` for `carol@example.com`, and the read-only tag row. Routed by `WaveClientServlet` when `?view=j2cl-root&q=read-surface-preview` and the viewer is signed in; signed-out viewers fall through to the regular shell + sign-in flow. No WaveletProvider lookup, so reviewers always see the same content.
- Runbook at `docs/runbooks/j2cl-read-surface-preview.md` documents the URL, what each affordance demonstrates, the browser walkthrough, the side-by-side with `?view=gwt`, and the maintenance note that the fixture content lives only in `HtmlRenderer.renderJ2clReadSurfacePreviewPage` / `appendReadSurfacePreviewWorkflow` / `appendReadSurfacePreviewBlips`.
- Final per-row parity roll-up at `J2clStageOneFinalParityTest`: a JUnit `@Suite` chain of `HtmlRendererJ2clRootShellIntegrationTest`, `J2clStageOneReadSurfaceParityTest`, `J2clStageOneFloatingOverlaysParityTest`, `J2clSearchRailParityTest`, and `J2clViewportFirstPaintParityTest`, plus a summary test that asserts every F-2 owned gate row (R-3.1, R-3.2, R-3.3, R-3.4, R-3.5, R-3.6, R-3.7, R-4.1, R-4.2, R-4.3, R-4.5, R-4.6, R-4.7, R-6.1, R-6.2, R-6.3, R-6.4, R-7.1, R-7.2, R-7.3, R-7.4) has at least one passing assertion in its owner class, and that the demo route mounts the full chrome surface. R-4.4 mark-blip-read remains deferred to F-4 (#1056).
✦New
- Closes #1037 (F-2 umbrella). PRs in the umbrella: F-2.S1 #1045, F-2.S2 #1053, F-2.S3 #1049, F-2.S4 #1052, F-2.S5 #1057, F-2.S6 (this PR).
April 26, 2026
F-2 Slice 5 — View wiring + URL state + integration cleanup
F-2 view-wiring slice. Removes the legacy 'Hosted workflow' intro card + duplicate search toolbar from the J2CL root shell now that <wavy-search-rail> is the canonical surface; replaces the 'Select a wave' placeholder with the wavy empty-state recipe (centered ghost waveform mark + headline); fixes the SSR <wavy-search-rail> waveform glyph overflow with explicit width=14 height=14; suppresses the legacy editor-toolbar wall when no compose session is active by gating view actions on the toolbar surface. Adds &depth=<blip-id> URL state (round-trips through reload + back/forward), [ / ] keyboard drill-out / drill-in, g / G first-blip / last-blip jump, and the R-3.7 G.6 awareness pill landmark. Wires pin folder state from J2clSearchDigestItem.isPinned() onto the <wavy-wave-nav-row> chrome (S2 deferral closeout). Closes the S4 focus-trap deferral on <wavy-version-history> and <wavy-profile-overlay> via inert-on-siblings + previous-focus restore. R-4.4 mark-blip-read Gateway is deferred to a follow-up so this slice can land cleanly.
✦New
- Integration cleanup: removed the visible 'Hosted workflow' eyebrow + h1.sidecar-title + sidecar-detail copy from the J2CL root shell. The structural <div class="sidecar-search-card"> wrapper stays so J2clSearchPanelView can still adopt the form / digests / empty-state DOM, but the wrapper now carries data-j2cl-legacy-search-card="hidden" and the form carries data-j2cl-legacy-search-form="true" + the hidden attribute. <wavy-search-rail> in the nav slot is the canonical query surface.
- Wavy empty-state recipe: replaced the 'Select a wave to open it here.' placeholder with a centered ghost waveform mark, a wavy-empty-state-headline ('Select a wave' / mode-specific title), and a wavy-empty-state-subhead. Class names are pinned by the new HtmlRendererJ2clRootShellIntegrationTest.
- Search-rail waveform glyph fix: <wavy-search-rail> SSR'd <svg> now carries explicit width="14" height="14" so the glyph does not overflow to the SVG default 300x150 before customElements.define attaches the shadow DOM (reproduces the giant waveform reported in issue #1055). The Lit element's render also pins the dimensions, and a defensive global rule in wavy-thread-collapse.css clamps any direct svg child of .waveform inside <wavy-search-rail>'s light DOM.
- Editor-toolbar wall suppression: J2clToolbarSurfaceController grew a setViewActionsEnabled(boolean) opt-out, defaulting to true to preserve the sidecar presentation. The J2CL root shell sets it to false because <wavy-wave-nav-row> already mounts the canonical view-action chrome (E.1-E.10). The toolbar host now hides itself when the action model is empty so the legacy Bold/Italic wall no longer renders before composition starts.
- URL state (R-3.7 G.4): J2clSidecarRouteState carries an optional depthBlipId; J2clSidecarRouteCodec round-trips &depth=<blip-id> through parse/toUrl with explicit width on the new param so it stays out of the URL when no depth focus is set. The route controller exposes onDepthChanged(blipId) + getCurrentState() so the root shell can push depth changes into history.
- Keyboard wiring (R-3.7 G.5): the read surface's blip keydown handler now dispatches wavy-depth-up on '[' (drill out one level) and wavy-depth-drill-in on ']' (drill into focused blip). Aliases 'g' = first blip and 'G' = last blip layer over the existing Home / End. j2cl.depth.drill_in telemetry counter records direction (in/out) + source (keyboard).
- Awareness pill (R-3.7 G.6): the selected-wave card now mounts an <output class="wavy-awareness-pill" data-j2cl-awareness-pill="true"> landmark. View.setAwarenessPill(int pendingCount) toggles hidden + textContent ('↑ N new replies above'). The CSS reuses --wavy-pulse-ring + signal-cyan tokens for the cyan border-pill.
- Pin/archive nav-row (S2 deferral closed): J2clSelectedWaveController publishes pin folder state via View.setNavRowFolderState(boolean pinned, boolean archived) on every selection change. Pinned reflects from J2clSearchDigestItem.isPinned(); archived reflects authoritative archive search-result sets such as in:archive and remains conservative for direct deep links without a digest.
- Focus-trap on overlays (S4 deferral closed): wavy-version-history and wavy-profile-overlay now save document.activeElement on open, set inert on every <body> direct child except the overlay host, and restore focus + remove inert on close. The host's tabindex=-1 + microtask focus call from S4 stays unchanged.
✦New
- R-4.4 mark-blip-read Gateway extension is intentionally deferred from this slice. The IntersectionObserver-equivalent debounce wiring on J2clReadSurfaceDomRenderer + the supplement-op POST /j2cl/mark-blip-read endpoint will land in a follow-up so this slice can ship the user-visible duplicate-removal cleanup without growing past one merge window.
April 26, 2026
F-2 Slice 4 — Floating controls + version-history overlay + profile overlay scaffolding
The J2CL root shell now mounts six new wavy elements covering 12 inventory affordances: J.2 scroll-to-new-messages floating pill, J.3 hide/show wave controls toggle, J.4 mobile navigation drawer toggle, J.5 back-to-inbox link, K.1-K.6 version-history overlay (full-bleed dark wash, time slider with playhead, Show changes + Text only toggles, gated Restore action with confirm dialog, Exit), and L.1+L.5 profile overlay scaffolding (opens on the wave-blip-profile-requested CustomEvent emitted from the avatar click in F-2 slice 1, with prev/next participant nav). Restore is gated by a restoreEnabled property defaulting to false until the renderer wires a real WaveletProvider.getHistory loader (follow-up tracked).
✦New
- Six new Lit elements registered in the j2cl shell bundle: <wavy-floating-scroll-to-new>, <wavy-wave-controls-toggle>, <wavy-nav-drawer-toggle>, <wavy-back-to-inbox>, <wavy-version-history>, <wavy-profile-overlay>. All consume the F-0 wavy design tokens (signal-cyan, hairline border, focus ring, motion + easing) and emit bubbles+composed CustomEvents the renderer / future slices subscribe to.
- Server-side parity: HtmlRenderer.renderJ2clRootShellPage signed-in branch mounts the six elements as siblings of <shell-root> (position:fixed escapes the shell-root CSS Grid). Each carries data-j2cl-floating-mount="true" so the parity fixture can count "all six present" with one attribute. The signed-out branch is unchanged — the affordances only matter inside an open wave.
- Profile overlay (L.1) listens on document for wave-blip-profile-requested (the F-2.S1 avatar event with bubbles+composed:true that crosses the wave-blip shadow boundary), opens itself, and dispatches wavy-profile-overlay-opened. L.5 prev/next participant nav is wired with click + ArrowLeft/ArrowRight keyboard handlers and disabled state at the ends of the participant list.
- Version-history overlay (K.1-K.6) ships full chrome but defers the destructive Restore behind a restoreEnabled property (default false). When the gate is closed, the Restore button reflects aria-disabled="true" + disabled, click is a no-op, and a "Preview only — restore not available" hint renders. The renderer flips restoreEnabled=true only after wiring a real WaveletProvider.getHistory consumer (follow-up issue #1054).
- Per-row sibling parity fixture J2clStageOneFloatingOverlaysParityTest mounts the J2CL servlet at both ?view=j2cl-root and ?view=gwt and asserts each of the six S4 mount points is present on j2cl-root and absent from gwt, plus the umbrella "all six present" count assertion that guards against accidental drops in future refactors.
April 26, 2026
StageOne J2CL Wave Chrome — Wave Nav Row + Focus Frame + Depth Nav Bar
The J2CL open-wave panel gains the per-wave navigation toolbar (Recent, Next Unread, Previous, Next, End, Prev/Next mention, Archive, Pin, Version History), a cyan focus frame that follows the keyboard-focused blip with arrow / j / k navigation, token-driven thread collapse motion, and a depth-nav bar with Up-one-level + Up-to-wave controls. Builds on the F-2 slice 1 <wave-blip> foundation; emits forward-only events for slice 5 to wire to URL state and controller actions.
✦New
- New <wavy-wave-nav-row> Lit element renders all 10 audit-row buttons E.1–E.10 with correct ARIA labels, dispatches per-action CustomEvents with selectedBlipId + sourceWaveId detail, applies cyan emphasis to Next-Unread when unread > 0 and to Pin when pinned, applies violet emphasis to mention buttons when mentions > 0, and binds H to open Version History scoped to the closest selected-wave-host ancestor.
- New <wavy-focus-frame> Lit element paints the cyan focus ring around the keyboard-focused blip via the --wavy-focus-ring token, listens for wavy-focus-changed events from J2clReadSurfaceDomRenderer, and animates with --wavy-motion-focus-duration / --wavy-easing-focus per F-0. Survives incremental window swaps without destroy/recreate.
- New <wavy-depth-nav-bar> Lit element composes the F-0 <wavy-depth-nav> recipe and adds G.2 (Up one level, with parent-author-aware ARIA label) and G.3 (Up to wave) buttons. Reserves an awareness-pill slot for slice 5 (G.6) and emits wavy-depth-up / wavy-depth-root / wavy-depth-jump-to-crumb events.
- J2clReadSurfaceDomRenderer adds j (ArrowDown) and k (ArrowUp) keyboard aliases, dispatches wavy-focus-changed CustomEvents on the read surface with surface-local bounds and the originating key, exposes setDepthFocus(currentDepthBlipId, parentDepthBlipId, parentAuthorName) for slice 5 to drive the depth-nav-bar, and re-dispatches focus-changed on thread expand so the focus frame can recompute its bounds against the freshly-visible blip geometry.
- New wavy-thread-collapse.css sibling stylesheet defines the --wavy-motion-collapse-duration / --wavy-easing-collapse transitions for inline-thread max-height + opacity, plus the .j2cl-read-surface { position: relative } positioning context the focus-frame anchors against. prefers-reduced-motion: reduce overrides transition to none.
- HtmlRenderer pre-renders <wavy-depth-nav-bar hidden> and <wavy-wave-nav-row> as landmarks inside the server-first selected-wave card, so AT users see the toolbar host before client boot and the J2CL upgrade swap is property-set (not replaceChild that would reset state).
- J2clSelectedWaveView mounts the chrome elements in cold-mount, re-binds the existing landmarks in server-first, binds unreadCount + sourceWaveId reactive props from the existing model accessors, and installs delegated telemetry listeners that emit wave_chrome.nav_row.click + wave_chrome.depth_nav.click per click.
- Telemetry events wave_chrome.focus_frame.transition (with key + direction + blipId) and wave_chrome.thread_collapse.toggle (with state) provide observability for the new chrome interactions.
- Per-row parity fixture J2clStageOneReadSurfaceParityTest extends with assertions for the chrome landmarks, the wavy-thread-collapse.css link, the keyboard-binding host attribute, and the GWT non-leak; fixes a pre-existing slice 1 false-positive that asserted server-side class="blip" markup which the GWT skeleton never emitted.
April 26, 2026
StageOne J2CL Read Surface — Threaded Blip Rendering
The J2CL open-wave panel now renders each blip as a Lit <wave-blip> element wrapping the F-0 wavy design recipe — with author display name, initials avatar chip, relative timestamp, full ISO datetime tooltip, per-blip toolbar (Reply/Edit/Link/overflow), inline-reply chip, mention rail, and live-pulse glow. Replaces the previous flat "Blip <id>" raw markup.
✦New
- Per-blip metadata flows through the read-surface model: J2clReadBlip and J2clReadWindowEntry now carry author id + display name, last-modified timestamp, parent blip id, thread id, unread flag, and a has-mention flag derived from the SidecarSelectedWaveDocument annotation ranges (mention/* keys).
- Renderer emits <wave-blip> custom elements that wrap the F-0 <wavy-blip-card> recipe and surface the F-2 author header, posted-at + ISO datetime tooltip, per-blip toolbar (hidden until :focus-within or :hover), inline-reply chip when reply-count > 0, and the violet mention rail when has-mention reflects.
- Plugin-slot contract preserved unchanged: <wave-blip> forwards the named blip-extension slot to the inner <wavy-blip-card> so plugins continue to read host.dataset.blipId / blipView per docs/j2cl-plugin-slots.md.
- Per-row parity fixture J2clStageOneReadSurfaceParityTest mounts the J2CL servlet at both ?view=j2cl-root and ?view=gwt and asserts contract attributes for R-3.1 (open-wave rendering), R-3.2 (focus-frame anchor), R-3.4 (blip navigation), R-3.7 (depth-nav id contract), R-4.4 (unread slot), plus the reciprocal that ?view=gwt does not leak F-2 client markers.
April 26, 2026
Wavy Design Foundation + Plugin Slot Contracts
The J2CL parity client gains the Wavy — Firefly Signal design packet (CSS tokens + six Lit recipe elements) and a documented contract for the four plugin slot points that future robots/data-API plugins will mount into. The user menu's API group is renamed to make the plugin surface visible.
✦New
- New Lit design packet (j2cl/lit/src/design/) ships --wavy-* CSS custom properties (color, typography, motion, spacing, shape) with dark / light / high-contrast variants and reduced-motion support.
- Six new Lit recipe elements (<wavy-blip-card>, <wavy-compose-card>, <wavy-rail-panel>, <wavy-edit-toolbar>, <wavy-depth-nav>, <wavy-pulse-stage>) consume the tokens and expose four named plugin slots: blip-extension, compose-extension, rail-extension, toolbar-extension.
- New docs/j2cl-plugin-slots.md formalises the slot context (data attributes + frozen JS properties), styling contract, accessibility contract, lifecycle, and rollback semantics for each of the four slots.
- Admin/owner-only design preview route at /?view=j2cl-root&q=design-preview mounts every recipe under all three theme variants plus a live-update pulse moment, with dashed outlines around empty plugin slots so designers see where plugins will land.
- User-menu group renamed from "Automation / APIs" to "Plugins / Integrations" in both the wave-client and standalone topbars; the existing /account/robots and /api-docs links are preserved.
April 25, 2026
Mobile wave controls are clearer
The mobile wave view no longer keeps obscure pin controls or the tags tray floating over the conversation by default.
✓Fixed
- Mobile wave pages now show a labeled Tags control instead of separate floating pin buttons, and the tags tray stays hidden until opened.
April 25, 2026
J2CL bootstrap JSON no longer exposes the volatile HTML client ID seed
The /bootstrap.json contract now omits the per-render SessionConstants.ID_SEED value that remains in legacy inline HTML bootstrap globals for rollback compatibility. This prevents J2CL/Lit clients from treating the value as an HTTP session identifier or as correlated with the HTML bootstrap page.
✓Fixed
- Removed the volatile session.id client ID seed and its misleading bootstrap-contract constant from the J2CL /bootstrap.json response while preserving the legacy inline HTML bootstrap seed during the rollout overlap window
April 25, 2026
Prepare Lit attachment picker and card presentation
The J2CL Lit shell now includes attachment picker and attachment card primitives for the attachment parity flow.
✦New
- Added Lit attachment presentation primitives for picker open and close, caption and display-size selection, upload live regions, blocked attachment alerts, and open or download controls.
April 25, 2026
J2CL read surface can show attachments
The Lit/J2CL selected-wave read surface now carries attachment render metadata from image elements and exposes attachment controls in the read UI.
✦New
- Selected-wave attachment elements in the J2CL read surface now render as attachment cards or inline images with keyboard-reachable open and download actions, plus blocked or metadata-unavailable states.
April 25, 2026
Wire J2CL composer toolbar actions to rich content and attachments
The opt-in J2CL root shell now routes daily rich-edit and attachment toolbar actions into the Lit composer surface, including structured rich reply deltas, file-picker uploads, pasted-image upload safety, upload live-region status, and focus restoration.
✦New
- Connected J2CL rich-edit toolbar commands to the composer submit path so inline formatting emits structured rich-content deltas instead of plain text only
- Connected J2CL attachment commands to the attachment composer controller for file-picker uploads, pasted-image uploads, upload progress, upload errors, display-size choices, and attachment-only replies
- Added Lit composer paste and focus hooks so pasted image uploads and attachment picker close paths return focus to the reply composer
April 25, 2026
J2CL attachment uploads keep unique IDs
The J2CL composer now keeps attachment IDs monotonic across wave changes in one browser session.
✓Fixed
- Attachment uploads from the J2CL composer no longer reuse the first generated ID after switching waves, preventing duplicate upload IDs in the same session.
April 25, 2026
J2CL selected-wave attachments hydrate metadata
Selected-wave attachments in the J2CL read surface now hydrate server metadata after the initial render so read-only waves can expose real preview, open, and download actions.
✓Fixed
- J2CL selected-wave attachment placeholders now resolve to hydrated attachment models, preserve resolved metadata across unchanged viewport fragment merges, and avoid losing later attachments when self-closing image tags appear before normal image tags.
April 25, 2026
J2CL build verification documents Vertispan DiskCache shutdown noise
Clarifies the J2CL SBT build verification path for the Vertispan DiskCacheThread shutdown warning so successful parity builds are not confused with real failures.
✓Fixed
- Documented that the Vertispan DiskCacheThread shutdown warning is benign only when the SBT command exits 0 and every requested J2CL or Lit task completes successfully
April 25, 2026
Staged worktree servers stay alive after readiness
Worktree browser-verification starts now keep the staged server alive for delayed checks until the smoke stop command runs.
✓Fixed
- The staged smoke helper now detaches the launched server correctly, records the live listener PID, and lets Jakarta server shutdown release the joined process without leaving orphan listeners.
April 25, 2026
J2CL parity interactions emit telemetry
The opt-in J2CL root shell now reports attachment and rich-edit parity interactions through the shared client stats channel.
✦New
- J2CL attachment uploads, attachment metadata failures, attachment open or download clicks, and rich-edit toolbar actions now emit structured telemetry without leaking wave IDs, attachment IDs, file names, or URLs.
April 25, 2026
IME debug logs are easier to export on Android
The IME debug overlay now offers direct download and Android share-sheet actions for the current log.
✓Fixed
- IME debug tracing now includes Download and Share controls so Android users can save the log as a text file or send it through apps such as Telegram from the system share sheet.
April 25, 2026
Show recovered Android IME text while composing
Android Chrome composition now displays the recovered text while typing, so a blip no longer visibly shows the raw shortened IME scratch such as "ew lip" before the keyboard Done action commits it as "new blip".
✓Fixed
- Typing text such as "new blip" on Android Chrome now previews the recovered composition text during active typing, not only after pressing Done.
April 25, 2026
Fix Android wave loading when the topbar is absent
The legacy GWT wave client now finishes booting when server-rendered topbar controls such as the language selector are not present.
✓Fixed
- Opening SupaWave on Android no longer gets stuck on the wave-list skeleton because the GWT client skips optional topbar wiring when those controls are absent.
April 24, 2026
Social Sign-In
Google and GitHub sign-in can now be enabled behind the social-auth feature flag.
✦New
- Added feature-flagged Google and GitHub social sign-in and sign-up
- First-time social users still choose a SupaWave username before the account is created
April 24, 2026
J2CL root shell begins publishing live-surface status
The opt-in J2CL root shell now has a root-scoped live-surface coordinator that keeps existing search and selected-wave controllers wired through one shell status publication path.
✦New
- Added a root live-surface model and controller for route and selected-wave continuity inside `/?view=j2cl-root`
- Published root live-surface status into the existing `shell-status-strip` without changing the default GWT root route or server protocol
April 24, 2026
Avoid whole-wave bootstrap for J2CL viewport opens
Viewport-hinted selected-wave opens now install the server fragment handler, keep the J2CL read surface windowed on the wire, and expose scroll-edge placeholders for fragment growth while legacy no-hint opens still receive the full snapshot bootstrap.
✦New
- Installed the configured server fragment handler for the selected-wave RPC path so live J2CL viewport opens can attach fragment windows
- Suppressed the full selected-wave snapshot payload for viewport-hinted opens that successfully attach a fragment window
- Sorted snapshot blip ids naturally for deterministic viewport windows on large waves
- Added placeholder-only edge ranges to J2CL viewport fragment windows so the read surface can grow through /fragments instead of stopping at the initial window
- Made the J2CL selected-wave content region the scroll surface that owns viewport-growth callbacks
- Preserved snapshot bootstrap compatibility for no-hint GWT and legacy opens
- Added J2CL viewport initial-window and snapshot-fallback metrics, plus a fallback warning marker, so operators can distinguish windowed opens from full-snapshot fallback
April 24, 2026
Align viewport fragment limits for J2CL window growth
J2CL selected-wave fragment growth now uses the existing /fragments endpoint with the same bounded viewport policy as initial open, so fragment windows stay small and predictable while the full GWT UI remains available.
✦New
- Added a J2CL /fragments fetch path that carries the selected wave, root wavelet, anchor, direction, limit, and version bounds for future scroll-window growth
- Aligned /fragments limit clamping with the configured viewport policy used by initial open, reducing the legacy HTTP-only maximum from 200 to the shared default maximum of 50 unless deployment config overrides it
- Added scoped J2CL viewport fragment metrics so marked J2CL growth requests can be tracked without counting legacy /fragments callers as J2CL traffic
April 24, 2026
Prepare J2CL rich-content submit deltas
The opt-in J2CL shell now has a tested structured delta builder for rich text and attachment document operations, enabling later attachment and rich-edit controller slices without changing the legacy GWT default route.
✦New
- Added a structured J2CL composer document model for text, annotation boundaries, and image attachment caption elements
- Added rich-content create and reply submit delta construction with participant-add ordering, write-session basis preservation, validation, escaping, and token-progression coverage
April 24, 2026
Prepare J2CL attachment upload and metadata clients
The opt-in J2CL shell now has tested attachment upload and metadata client primitives that mirror the GWT multipart and attachment-info contracts, preparing later Lit/J2CL composer wiring without changing the legacy GWT default route.
✦New
- Added a J2CL attachment upload client that builds GWT-compatible multipart requests for file-picker and pasted-image flows
- Added a J2CL attachment metadata client that reads numeric proto JSON, preserves missing-attachment reporting, and returns typed network, HTTP, content-type, and parse failures
April 24, 2026
Refine J2CL interaction overlay models
The opt-in J2CL selected-wave pipeline now exposes pure mention, task, reaction, and focus-target models so the next Lit overlay slices can render parity features without depending on raw transport metadata.
✦New
- Added pure J2CL models for mention ranges, mention candidates, task metadata, reaction summaries, and overlay focus targets
- Projected selected-wave interaction blips into refined mention, task, and reaction views while preserving the existing read surface data
April 24, 2026
Add J2CL Lit interaction overlay primitives
The opt-in J2CL shell now includes reusable Lit primitives for mention suggestions, task metadata, reaction rows, reaction picking, reaction authors, and overlay focus handling so later controller slices can wire parity interactions without adding legacy GWT dependencies.
✦New
- Registered Lit primitives for mention, task, reaction, and overlay-layer interaction surfaces
- Added Web Test Runner coverage for keyboard close/select flows, live-region labels, focus behavior, and semantic overlay events
April 24, 2026
Prepare J2CL interaction overlay metadata
The opt-in J2CL selected-wave path now carries mention, task, and reaction metadata through its transport and projection layers so Lit interaction overlays can reach GWT parity in the follow-up UI slices.
✦New
- Decoded selected-wave mention and task annotation ranges for the J2CL interaction overlay pipeline
- Decoded per-blip reaction data documents and preserved reaction metadata across metadata-only selected-wave updates
April 24, 2026
J2CL root shell gains practical compose and daily toolbar parity
The opt-in J2CL root shell now exposes Lit-backed compose/reply controls and daily view/edit toolbar actions on top of the current selected-wave write-session seam.
✦New
- Added Lit compose primitives that show create, reply, submit, disabled, error, and stale-basis states in the J2CL root shell
- Added J2CL compose state management that consumes the selected-wave write-session listener without introducing a parallel bootstrap or reconnect lifecycle
- Added daily view and edit toolbar primitives and controller command seams while leaving mention autocomplete, task overlays, reactions, and attachments to their follow-up issues
April 24, 2026
IME debug log no longer covers mobile editing
The IME debug overlay now starts compact on mobile, reserves space for editing, and provides explicit log controls.
✓Fixed
- IME debug tracing now starts in a compact bottom control with show, copy, and clear actions instead of covering editable mobile blips by default
- The hidden Escape and double-tap clear gestures were replaced with an explicit Clear action; Escape now minimizes the debug log
- Mobile wave and tags controls now render as compact icon buttons with accessible labels instead of visible text pills
April 24, 2026
Android IME preserves first characters on real phones
Android Chrome IME composition now recovers first characters and inter-word spaces even when the browser inserts them into the editor DOM before the IME scratch baseline is captured.
✓Fixed
- Mobile Chrome editing now uses the document model to recover IME ghost text that already exists in adjacent DOM nodes when composition starts
April 24, 2026
Recover Android IME text when Done removes temporary siblings
Android Chrome can remove the adjacent DOM text node that temporarily carries leading IME characters before the editor flushes the composition. The editor now keeps the captured ghost text across that teardown and reports the same recovered composition state that it commits.
✓Fixed
- Typing text such as "new blip" into a blip from Android Chrome now preserves the leading characters when the keyboard Done action removes the browser's temporary sibling text before commit.
April 24, 2026
Keep Android IME text visible after Done
Android Chrome can move the first composed character into a neighboring text node before the editor snapshots the IME scratch, then remove that neighboring text again when the keyboard Done button closes composition. The editor now carries the activation-time ghost text through the final commit, so the visible blip and persisted content keep the leading characters.
✓Fixed
- Typing into a blip from Android Chrome now preserves leading characters even when the keyboard Done button removes the browser's temporary adjacent DOM text before the editor flushes the composition.
April 24, 2026
Recover Android IME text from composition history
Android Chrome can replace the active IME scratch span with shortened composition text, dropping the first character before commit. The editor now reconstructs the composed word from composition event data and commits standalone Android textInput separators directly.
✓Fixed
- Typing text such as "new blip" into a blip from Android Chrome now preserves leading letters and spaces when the keyboard rewrites the composition scratch text.
April 23, 2026
Repair Grafana perf exports and expand Gatling run comparison
Perf workflow runs now summarize Gatling report JSON reliably, start Alloy with a valid scrape config, and publish richer run and request comparison data for the Grafana perf dashboard.
✓Fixed
- Repaired perf summary generation by reading Gatling report JSON artifacts instead of depending on brittle console output parsing
- Fixed the generated Alloy scrape configuration so perf workflow runs can start remote-write and ship Wave, runner, and exporter metrics
- Expanded the perf dashboard to compare runs by branch, sha, run ID, simulation, and request using richer Gatling metrics
April 23, 2026
The J2CL root shell now renders through reusable Lit chrome primitives
Introduced the first `j2cl/lit/` bundle with reusable shell primitives and switched the explicit `/?view=j2cl-root` route to progressive-enhancement custom-element markup while keeping the default `/` route on the legacy GWT shell.
✦New
- Added a new `j2cl/lit/` package that ships `shell-root`, `shell-root-signed-out`, `shell-header`, `shell-nav-rail`, `shell-main-region`, `shell-status-strip`, and `shell-skip-link` into `/j2cl/assets/`
- Replaced the hand-built J2CL root-shell HTML with progressive-enhancement custom-element markup so `/?view=j2cl-root` renders styled fallback chrome before the Lit bundle upgrades
- Kept the default `/` route on the legacy GWT bootstrap while preserving the existing `j2cl-root-bootstrap` coexistence seam and workflow mount host contract
April 23, 2026
J2CL selected-wave reading gains a semantic StageOne-style read surface
The opt-in J2CL root shell now upgrades selected-wave content into stable blip/thread DOM instead of flat text blocks, preserving server-first selected-wave HTML while the live client attaches read-surface behavior.
✦New
- Selected-wave live updates render as semantic blip nodes with stable `data-blip-id` attributes for focus and later StageOne parity work
- Server-rendered selected-wave snapshots are enhanced in place with keyboard focus traversal and in-session inline-thread collapse controls
- The existing J2CL route and sidecar transport remain unchanged while the read surface moves toward GWT StageOne parity
April 23, 2026
J2CL sidecar no longer reads the session cookie from JavaScript
The J2CL search sidecar and sandbox proof now rely on the WebSocket upgrade handshake for authentication, so the primary session cookie can remain HttpOnly.
✓Fixed
- Removed the JSESSIONID lookup via document.cookie from the J2CL sidecar's selected-wave, submit, and sandbox proof sockets so the session cookie can stay HttpOnly
- Deleted the now-unused sidecar ProtocolAuthenticate envelope helper to prevent regressions that would re-introduce the client-side cookie handshake
- Added a fast-fail guard for cross-host websocket presented addresses because the handshake-only J2CL sidecar auth path now requires the current page host to receive the HttpOnly session cookie
April 23, 2026
The J2CL root shell now serves a selected-wave snapshot before live boot
The explicit `/?view=j2cl-root&wave=...` route now renders a safe server-first selected-wave surface and preserves that DOM until the live J2CL selected-wave client takes over.
✦New
- Added a targeted server snapshot path for `/?view=j2cl-root&wave=...` that reuses the existing Wave renderers instead of showing a blank selected-wave panel first
- Updated the root-shell HTML contract so the selected-wave region exposes explicit server-first markers and private/no-store caching for per-viewer snapshot responses
- Changed the root-shell J2CL boot path to preserve server-rendered selected-wave markup until the first live selected-wave content replaces it in place
April 23, 2026
J2CL Selected-Wave Unread/Read State
The J2CL sidecar's selected-wave panel now shows real per-user unread/read state that updates live.
✓Fixed
- Selected-wave panel replaces stale digest-derived unread text with server-authoritative unread counts
- Unread count refreshes after each selected-wave update and after the tab returns to visible, so UDW-only read-state changes are reflected without a full reload
April 23, 2026
Grafana Dashboards Publish From Repo
Wave analytics and perf dashboards now publish from the repo through GitHub Actions instead of relying on manual Grafana imports.
✓Fixed
- Added a repo-owned GitHub Actions workflow to validate and publish the Wave analytics and perf dashboards to Grafana
- Hardened the Grafana dashboard upsert helper so strict CI runs fail on real API errors and accept both dashboard datasource placeholder styles
April 23, 2026
Keep compact inline avatars on the author row
Updates the compact-inline-blips follow-up so inline reply avatars stay on the same row as the author line while the content still reuses the reclaimed width on desktop and mobile.
✓Fixed
- Depth 0-2 compact inline replies now keep the avatar on the same row as the author metadata on desktop and mobile.
- Compact inline reply content still spans the reclaimed width below that row, while root blips and depth 3+ reset behavior stay unchanged.
April 22, 2026
J2CL sidecar reply path now keeps the selected-wave version and history hash aligned
The J2CL selected-wave projector treats resultingVersion and resultingVersionHistoryHash as a coupled atomic pair so a reply submit cannot use a newer version with a stale history hash.
✓Fixed
- J2clSelectedWaveProjector only advances the selected-wave write-session basis when both resultingVersion and resultingVersionHistoryHash arrive together; otherwise the previous pair is preserved, preventing invalid hashed-version pairs from reaching the sidecar reply submit path
April 22, 2026
The J2CL GWT parity matrix and slice packet template freeze the acceptance contract for every downstream parity slice
A committed GWT parity matrix now defines, per target flow/surface, the behaviors the J2CL client must match before any default-root cutover can be reconsidered. A per-slice packet template makes every downstream parity issue declare its rollout seam, verification plan, telemetry checkpoints, and traceability back to the matrix.
✦New
- Added docs/j2cl-gwt-parity-matrix.md as the parity acceptance contract covering read, live, compose, server-first, and viewport-fragment surfaces with stable row anchors that downstream slices cite
- Added docs/j2cl-parity-slice-packet-template.md with a structured packet every parity slice must fill in, plus a worked example for the StageOne read-surface slice
April 22, 2026
The J2CL parity issue map now turns the remaining migration into an executable issue chain
The new issue-map doc keeps the right existing J2CL follow-ups, adds the missing parity issues, and defines the dependency order from parity matrix through read/live/edit parity to opt-in bootstrap, cutover, and eventual GWT retirement.
✦New
- Added a dedicated J2CL parity issue-map doc that separates retained open issues from the missing parity slices still needed under #904
- Documented the execution chain for parity work, including Lit shell/read/live/edit stages, server-rendered first paint, viewport-scoped fragments, and parity-gated cutover/retirement
April 22, 2026
The J2CL parity architecture memo now defines the recommended long-term view/runtime split
The new architecture memo documents the repo-backed recommendation to keep J2CL for runtime/state/transport concerns, use Lit custom elements for long-term UI composition, and preserve server-rendered first paint plus rollback-ready coexistence.
✦New
- Added a dedicated J2CL parity architecture memo that maps the legacy staged GWT runtime responsibilities to the recommended J2CL-plus-Lit direction
- Documented why the parity path should keep server-rendered read-only first paint, viewport-scoped fragment loading, and the existing coexistence/rollback contract instead of moving to a React-owned root
April 22, 2026
The J2CL/Lit workflow doc now defines how to use Stitch, image generation, and specs together
The new workflow doc explains that parity work should stay code/spec-first, use Stitch as the main structured design accelerator, and use image-generation tools only for visual direction and moodboards.
✦New
- Added a dedicated J2CL/Lit implementation workflow doc that explains how to move from current GWT behavior inventory to approved Lit implementation slices
- Documented a concrete tool split: Stitch for screen/design-system exploration, image generation for visual direction, and code/specs as the behavioral source of truth
April 22, 2026
The first parity-safe Lit design packet freezes tokens, component families, and Stitch artifact policy for downstream J2CL/Lit slices
A committed Lit design packet now defines the semantic token vocabulary, component-family inventory (shell, read surface, live indicators, compose/toolbar inventory, overlays, server-first first paint), and the Required vs Optional vs Prohibited Stitch artifact policy per family. Downstream J2CL/Lit parity slices cite this packet alongside the parity matrix so modernization cannot silently drift into behavior changes.
✦New
- Added docs/j2cl-lit-design-packet.md as the parity-safe design contract for Lit components: semantic tokens (typography, color, spacing, shape, elevation, motion, density, iconography, focus, z-index), component families keyed to parity matrix rows, and a Stitch Required/Optional/Prohibited table with consuming slices named
- Registered the packet in docs/DOC_REGISTRY.md and docs/README.md so the doc guardrails keep it in scope
April 22, 2026
J2CL sidecar and root shell now bootstrap from /bootstrap.json instead of scraping root HTML
The J2CL client now reads session, socket, and shell metadata from an explicit server-owned /bootstrap.json endpoint. The historical scraping path that parsed window.__session and window.__websocket_address out of the root HTML page is retained for one release so rolling deploys keep working, but new builds no longer depend on it. During the overlap window, server rollback must happen with or after client rollback because newer J2CL bundles do not fall back to HTML scraping.
✦New
- Added a server-owned /bootstrap.json endpoint that publishes the session, socket, and shell metadata the J2CL client needs to boot, with Cache-Control: no-store and GET-only enforcement
- Switched the J2CL sidecar and root-shell bootstrap call sites to the new JSON contract so they no longer regex-scrape the rendered root HTML page
- Reserved a nested socket object in the JSON schema so issue #933 can add signed handshake tokens without a client-side schema migration
April 22, 2026
IME tracer feature-flag enablement and server log upload
Turns the Android IME diagnostic tracer into a real per-user feature-flagged tool instead of a URL/localStorage-only toggle, and restores the remote logging path so tracer output can be forwarded back to the server for authenticated debugging sessions.
✦New
- Registered the IME diagnostic tracer as a known feature flag so admins can enable it per user
- Updated the client tracer to honor the feature flag in addition to the legacy local URL/localStorage override
- Restored the /webclient/remote_logging servlet mapping and forwarded IME tracer lines there as authenticated text/plain uploads
- Server-side remote logging now records uploaded plain-text trace lines individually with user context instead of collapsing a whole batch into one summary line
April 21, 2026
The legacy authenticated GWT client path is retired from the default runtime and packaging flow
The maintained J2CL root shell and its search-sidecar asset are now rebuilt for `sbt run`, `Universal/stage`, and `Universal/packageBin`, while the legacy GWT client path remains only as a manual bridge task.
✓Fixed
- Rebuilt the maintained J2CL search-sidecar asset as part of `sbt run` and the staged packaging flow so clean trees no longer depend on leftover artifacts
- Kept the legacy `compileGwt` task only as a manual bridge for the retired authenticated GWT client path instead of wiring it into the default runtime
- Updated the smoke and build notes to match the maintained J2CL root shell, landing page, and sidecar route contract
April 21, 2026
Admin and owner sessions can reach the existing admin controls from the J2CL root shell
The active J2CL root shell now exposes a server-rendered Admin link for admin and owner sessions so they can reach the existing `/admin` page and Feature Flags controls without falling back to the legacy shell.
✓Fixed
- Added a role-gated Admin link to the signed-in J2CL root-shell navigation for `admin` and `owner` sessions
- Kept the existing `/admin` page as the feature-flags entry point instead of introducing a separate J2CL-native admin surface
April 21, 2026
OT search open requests normalize the live-search wavelet filter
Synthetic OT search opens now replace caller-provided wavelet prefixes with the exact computed search wavelet id, so cold search bootstraps register the live subscription before the frontend guard checks it.
✓Fixed
- Search open RPC requests now normalize their effective wavelet filter to the computed synthetic search wavelet id instead of forwarding arbitrary caller prefixes
- Cold OT-search inbox loads no longer trip the server-side 'Search subscription was not registered' bootstrap failure when the request omitted the synthetic wavelet id
April 21, 2026
Fresh local boots keep OT search off by default
Fresh local development boots now inherit the existing `search.ot_search_enabled = false` default instead of forcing OT search on, so new local stores avoid the fragile OT bootstrap path unless a developer enables it intentionally.
✓Fixed
- Fresh local stores now seed `ot-search=false` from the config overlay instead of defaulting the feature on
- Existing persisted stores keep their current OT search setting unchanged
April 21, 2026
J2CL stays the default root while the legacy GWT rollback path is restored
Operators can keep the J2CL root shell on `/` by default while still serving the legacy `/webclient/**` assets and flipping `/` back to the GWT bootstrap through the restored rollback control plane.
✓Fixed
- Restored the `j2cl-root-bootstrap` control plane so fresh installs stay on the J2CL root shell by default but operators can set `ui.j2cl_root_bootstrap_enabled=false` to roll `/` back to the legacy GWT bootstrap
- Put `/webclient/webclient.nocache.js` back into the packaged runtime and local smoke checks so rollback mode remains deployable without a code revert
April 21, 2026
Legacy GWT is the default root UI again while J2CL remains opt-in
The rollback-ready coexistence path remains in place, but fresh/default root bootstrap now points back to the legacy GWT UI until J2CL reaches parity.
✓Fixed
- Changed the default `j2cl-root-bootstrap` setting back to off so `/` boots the legacy GWT UI again by default
- Kept `/?view=j2cl-root` and the coexistence plumbing available for explicit J2CL testing and rollout work
April 21, 2026
Cold blue-green deploy slots get a longer inbox sanity warmup window
Deploy sanity checks now tolerate the slower first inbox search on a cold slot so healthy releases are not rejected during blue-green warmup.
✓Fixed
- Extended the deploy inbox-search sanity window for cold blue-green slots instead of failing after the previous 60 second assumption
- Made the deploy search deadline and per-request timeout configurable so operators can tune the warmup gate without patching the script again
April 20, 2026
The J2CL search sidecar can now create a wave and send a plain-text follow-up
The isolated /j2cl-search/index.html sidecar now exposes visible plain-text compose and reply controls, submits over the existing websocket path, and updates the opened sidecar wave without changing the legacy GWT root route.
✦New
- Added visible New wave and Reply affordances to the J2CL sidecar so plain-text writes can be submitted directly from the split-view shell
- The sidecar now builds sidecar-local submit envelopes and plain-text deltas over the existing websocket transport without cutting over the root GWT editor stack
- Opened sidecar waves now retain the minimal channel, version, and target metadata needed for the first write-path pilot while preserving the existing query-plus-wave route contract
April 20, 2026
The J2CL search sidecar now restores query and selected-wave route state
The isolated /j2cl-search/index.html sidecar now keeps the active query and selected wave in the URL so reload, copied links, and browser back or forward restore the same split-view state without changing the legacy GWT root route.
✦New
- Added durable J2CL sidecar route state for the search query and selected wave under /j2cl-search/index.html
- Reloaded or copied sidecar URLs now reopen the same selected wave and preserve back or forward navigation between query-only and query-plus-wave states
- Sidecar route restore now keeps the selected-wave panel stable even when the latest search refresh cannot currently rehydrate digest metadata
April 20, 2026
SupaWave now has an explicit J2CL root-shell route that can host the current workflow
Added a non-default `/?view=j2cl-root` root-shell page that preserves auth and route state, mounts the current J2CL search/read/write workflow, and leaves the legacy GWT root as the default shell.
✦New
- Added a signed-in and signed-out J2CL root-shell seam behind `/?view=j2cl-root` without changing the default `/` bootstrap
- The J2CL root shell now preserves its explicit selector plus `q` and `wave` state across route updates and auth round-trips
- The hosted J2CL workflow now renders shell-aware copy inside the root shell while `/j2cl-search/index.html` remains available as the sidecar verification route
April 20, 2026
Allow the J2CL root shell to bootstrap from a server feature flag
The root / route can now opt into the J2CL shell through a server-side feature flag while legacy GWT remains the default fallback and the explicit /?view=j2cl-root diagnostic route stays available.
✦New
- Added the j2cl-root-bootstrap known feature flag and seeded it from ui.j2cl_root_bootstrap_enabled
- The / route now checks the server feature flag before choosing the J2CL root shell, while /?view=j2cl-root remains a direct diagnostic route
- Fresh deployments keep legacy GWT as the default unless the bootstrap flag is explicitly enabled
April 20, 2026
Make the J2CL root shell the default `/` experience
The root `/` route now boots the J2CL shell by default, while `/?view=j2cl-root` stays available as the explicit diagnostic route and `?view=landing` remains the legacy landing escape hatch.
✦New
- Seeded the j2cl-root-bootstrap feature flag from ui.j2cl_root_bootstrap_enabled with a fresh-deploy default of true
- The root `/` route now boots the J2CL shell when the bootstrap flag is enabled, including for signed-out visitors
- The legacy landing page still remains available through `/?view=landing`, and rollback to the legacy root stays config-driven
April 19, 2026
Deep reply threads stay usable on desktop and mobile
Deep reply branches now switch into a focused full-width view before nested whitespace crushes the active blip, and mobile wave chrome can hide while you read or edit.
✓Fixed
- Deep reply branches now promote into the existing breadcrumb-backed focused-thread view when the remaining blip width gets too small
- Focused reply branches restore from a lightweight #focus hash and keep the active thread readable through resize and reload
- On mobile, wave chrome pin state is stored with the user’s supplement data while transient read/edit chrome remains local to the session
April 19, 2026
Reply depth limit no longer blocks valid sibling replies
Waves that already contain one max-depth reply branch can now accept additional sibling replies at the same allowed depth instead of failing with an internal channel error.
✓Fixed
- Replying from a depth-4 blip now succeeds even when the wave already contains another depth-5 branch, as long as the new reply does not exceed the configured limit
April 19, 2026
Add an isolated J2CL sidecar transport proof
Keeps the legacy GWT webclient transport path unchanged while adding a sidecar-only transport/search proof that reuses the live root session, /search, and /socket under isolated J2CL routes.
✦New
- Added a sidecar-only transport proof under the J2CL routes so the isolated sidecar can reuse the live root session bootstrap, call /search, and complete a /socket proof flow without cutting over the main webclient
- Kept compileGwt and the staged legacy root path green while proving the isolated sidecar page and root page can both run against the same local server
- Strengthened the PST transport-family contract test so the required impl/gson/jso/proto outputs stay present for the sidecar transport path
April 19, 2026
Harden the J2CL sidecar follow-up paths
Tightens the remaining sidecar scaffold behavior after the base scaffold landed on main by hardening wrapper execution, sidecar route serving, and generated artifact handling.
✓Fixed
- Added explicit wrapper and pom preflight checks before running J2CL sidecar Maven tasks so missing wrapper assets fail with a clear build error
- Stopped the Jakarta sidecar servlet routes from resolving repo source directories when generated sidecar assets have not been built
- Ignored generated war/j2cl* sidecar outputs so local sidecar builds do not leave large untracked artifacts in git status
April 19, 2026
The J2CL sidecar can now open a read-only selected wave beside search results
The isolated /j2cl-search/index.html route now keeps the existing search rail and opens the selected wave in a live read-only panel with sidecar-owned reconnect handling, while the legacy root path stays on GWT.
✦New
- Added a split-view J2CL sidecar shell that keeps search results on the left and renders a read-only selected-wave panel on the right
- Digest selection now opens the selected wave over the existing sidecar socket path, decodes snapshot plus fragment payloads, and renders live read-only content without route persistence
- The selected-wave sidecar flow now retries once through the existing session bootstrap after a disconnect instead of requiring a full page reset
April 19, 2026
A first J2CL search slice now runs on the isolated sidecar route
The legacy root app stays on / while /j2cl-search/index.html now renders the first real J2CL search results panel against the existing search backend.
✦New
- Added an isolated J2CL search/results slice under /j2cl-search/index.html without cutting over the legacy root runtime
- The sidecar search slice now loads the default inbox query, reruns narrow searches without a full page reload, and expands the result window with show-more behavior
- The first slice uses static sidecar CSS and the J2CL-safe search response path instead of GWT UiBinder or JSO-only search widgets
April 19, 2026
Compact inline blip layout at nesting depth
Reduces horizontal indent and avatar size at inline nesting depths 0-2, recovering ~126px of content width at the deepest inline level. Feature-flagged under compact-inline-blips.
✦New
- Progressive compaction of padding, margins, and avatar sizing at inline nesting depths 0, 1, and 2.
- Mobile-specific overrides with more aggressive compaction for small viewports.
- Explicit depth 3+ reset rules prevent compaction from leaking into slide-nav threads.
April 19, 2026
Stack compact inline reply avatars above content
Updates the compact-inline-blips layout so new inline reply composers stack the author avatar above the metabar/content instead of reserving extra horizontal gutter width.
✓Fixed
- Depth 0-2 compact inline reply composers now render the avatar above the metadata row on desktop and mobile.
- Compact inline reply width is no longer driven by a left avatar gutter, while depth 3+ slide-nav threads keep the baseline layout.
April 19, 2026
Recover Android IME characters that bypass the scratch span
Real Android Chrome and Brave freeze the IME composition insertion anchor at compositionstart time, before the editor moves the DOM selection into its scratch span. The first character of each composed word (and the space between words) therefore lands in the text node adjacent to the scratch instead of inside it, and previous compositionEnd flushes read only the scratch contents — typing 'new blip' silently committed as 'ewlip' on real phones (not reproducible in Chrome DevTools emulation, which is why the prior four guard-patch PRs #850, #877, #891, #896 could not catch it). The scratch now snapshots its adjacent DOM siblings at activate() time and reunites any ghost characters into the effective composition before it is written to the content model, then restores the siblings to their baseline so the renderer does not double-count them.
✓Fixed
- Android IME composition no longer drops the first character of each composed word (for example 'new blip' no longer becomes 'ewlip') on real Chrome and Brave on Android
- The inter-word space is preserved when the browser commits it into a pre-existing text node between composition cycles
- ImeExtractor now records the scratch-adjacent text nodes at compositionstart and reconciles any browser-inserted ghost characters into the final composition, rather than relying solely on imeContainer.getInnerText()
April 19, 2026
Diagnostic tracer for the Android IME composition pipeline
Ships a flag-gated diagnostic tracer that emits structured timelines of keyboard, composition, and DOM-mutation events alongside the IME scratch and ghost-baseline state directly in the browser console and an on-screen overlay. Intended to finally collect the real-device evidence needed to understand why typing 'new blip' on a Galaxy S25 Ultra still commits as 'ewlip' despite five prior merged fix PRs. The tracer is disabled by default and a pure no-op in production unless localStorage.ime_debug is set to 'on' or the URL carries ?ime_debug=on.
✦New
- Added ImeDebugTracer — a flag-gated JSNI-backed tracer that writes structured timelines to console and an on-screen overlay
- Instrumented EditorEventHandler, CompositionEventHandler, EditorImpl, and ImeExtractor with tracer calls at every composition and DOM-mutation decision point
- Installs capture-phase window listeners for keydown, beforeinput, input, composition*, textInput, DOMCharacterDataModified, selectionchange — so events are recorded even when the editor swallows or ignores them
- Activation: append ?ime_debug=on to the URL once, or run localStorage.setItem('ime_debug', 'on') in DevTools and reload. Double-tap the overlay to clear.
April 18, 2026
Add the isolated J2CL sidecar build scaffold
Introduces an opt-in J2CL Maven sidecar with isolated SBT entrypoints and browser-reachable sidecar assets under dedicated war/j2cl* paths while the legacy GWT web client remains the active runtime.
✦New
- Added a dedicated j2cl/ Maven sidecar project with wrapper-backed search-sidecar, debug-single-project, and production build profiles
- Added opt-in sbt j2clSandbox* and j2clSearch* tasks that invoke the sidecar wrapper without changing compileGwt, run, or stage bootstrap behavior
- Staged isolated host pages and generated assets under war/j2cl-search, war/j2cl-debug, and war/j2cl so the sidecar can be loaded independently from the main GWT app
April 17, 2026
Preserve Android IME word starts across composition restarts
Android Chromium no longer drops the first letter of each composed word when the browser restarts IME composition before the editor's delayed composition-end flush runs.
✓Fixed
- Back-to-back Android IME composition cycles now flush the previous delayed composition end before the next cycle starts
- Typing `new blip` in a new or empty blip now preserves both word-leading characters and the separating space on Android Chrome and Brave
April 16, 2026
Clean pressed background for toolbar toggle icons
Pressed horizontal toolbar buttons (pinned wave, pinned saved search, and other toggle icons) no longer render a two-tone gray/blue background.
✓Fixed
- Removed the legacy buttonDown sprite under HorizontalToolbarButtonWidget pressed state so toggled toolbar icons now show a single clean rounded-blue overlay instead of a half-gray half-blue mix
April 16, 2026
Fix perf Alloy archive verification
The performance workflow now verifies and unpacks the same Grafana Alloy archive filename so checksum validation does not fail before the perf job starts.
✓Fixed
- Aligned the perf workflow's Alloy checksum verification and unzip steps on the same architecture-specific archive filename
April 16, 2026
Harden prebuilt Docker build against Ubuntu mirror sync errors
The production deploy pipeline now retries apt-get inside Dockerfile.prebuilt with progressive backoff, so a transient Ubuntu mirror hash-mismatch no longer aborts the whole deploy.
✓Fixed
- Wrapped apt-get update/install in Dockerfile.prebuilt in a retry loop with progressive backoff and Acquire::Retries so transient mirror sync hiccups do not fail the deploy
April 16, 2026
Preserve every word when typing into a blip on Android
Android Chrome and Brave were silently dropping the first character of every composed word (typing "new blip" produced "ewlip") because the editor's mutation handler was activating the typing extractor mid-composition. The IME composition flow now owns DOM character mutations between compositionstart and compositionend, so the typing extractor no longer stomps on the IME scratch container as it is torn down.
✓Fixed
- Android IME composition no longer drops the first character of each word when typing into a new or existing blip
- DOM character mutations that arrive between compositionstart and compositionend are now handled exclusively by the composition flow, avoiding a stale typing-extractor state that survived compositionend
April 13, 2026
Avoid blocking on loading wavelets
Wavelet-dependent cache and indexing paths now skip still-loading wavelets and emit load-state diagnostics instead of blocking on incomplete state.
✓Fixed
- Prevented cached wavelet invalidation and copy paths from blocking on wavelets that are still loading
- Added wavelet load-state diagnostics so slow or timed-out loads report their state, age, and executor context in server logs
April 13, 2026
Wrap pinned saved searches onto another toolbar row
Pinned saved-search buttons in the search panel now wrap onto additional toolbar rows instead of forcing horizontal scrolling, and the search results panel follows the toolbar's rendered height.
✓Fixed
- Pinned saved searches in the search panel now remain visible by wrapping onto another toolbar row when space runs out
- The search panel keeps its wave-count bar and result list aligned below the real wrapped toolbar height instead of overlapping the extra row
April 13, 2026
OT search protocol bootstrap
OT search subscriptions now carry the raw query to the server and receive their first snapshot on the OT channel itself, removing the old dependency on an HTTP `/search` bootstrap.
✓Fixed
- Search wavelet open requests now include the raw search query so the server can compute and publish the initial live-search snapshot immediately
- OT search cold-start no longer depends on a side-effect from the legacy `/search` servlet before the sidebar can populate
April 13, 2026
Same-name index upgrade conflicts
Mongo delta-store startup now treats same-name index key-spec conflicts as upgradeable so legacy applied-version indexes can be repaired without forcing append-guard mode.
✓Fixed
- Mongo delta-store startup now upgrades legacy applied-version indexes when Mongo reports same-name key-spec conflicts, instead of refusing new delta writes until restart
April 13, 2026
Clarify the Next Unread toolbar icon
The wave toolbar now uses a directional chevron plus unread-accent dot for Next Unread instead of a bell, making the action easier to recognize at a glance.
✓Fixed
- Replaced the Next Unread bell glyph with a clearer directional chevron and unread-colored dot in the wave toolbar
April 13, 2026
Mongo startup migrations now run before readiness
Mongo-backed v4 deployments now run Mongock startup migrations before the server is considered ready, with deploy-time verification tied to the migration completion marker.
✦New
- Mongo-backed v4 startup now runs versioned Mongock schema migrations before Wave begins serving traffic
- Deploy and rollback verification now wait for the canonical Mongock completion marker before continuing
- Mongo migration tests now cover the startup baseline, degraded fallback, and deploy bootstrap checks
April 13, 2026
Disable Mongock transactions for standalone Mongo deployments
Mongo-backed startup migrations now disable Mongock transactions so standalone MongoDB deployments can apply schema changes without replica-set transaction support.
✓Fixed
- Disabled Mongock transaction usage before the startup migration runner executes against Mongo v4
- Kept the driver on primary reads while leaving read and write concern overrides unchanged
April 13, 2026
Gate Mongock startup migrations to Mongo-backed persistence
Wave startup now runs Mongock migrations only when the active core persistence path is MongoDB on the v4 driver, avoiding unnecessary migration startup work for file- or memory-backed deployments.
✓Fixed
- Mongo-backed v4 deployments now run the startup migration runner exactly once during persistence wiring
- File- and memory-backed deployments skip Mongock entirely even if Mongo connection settings are present in the config
April 13, 2026
Avoid false Mongo migration marker failures on SIGPIPE
Deploy verification now treats a matched Mongock completion marker as success even when the Docker logs pipeline exits with SIGPIPE after `grep -q` stops reading.
✓Fixed
- Deployment marker verification now captures recent container logs before searching for the Mongock completion message so `set -o pipefail` does not turn a successful match into a failure
- Added regression coverage for the `docker compose logs --since ... | grep -q` SIGPIPE path that previously misreported completed migrations as missing
April 13, 2026
Reuse Lucene indexes across startup
Wave startup now reuses an existing Lucene index when rebuild-on-startup is disabled, avoiding full corpus repair on every restart while preserving empty-index bootstrap and explicit rebuilds.
✓Fixed
- Lucene startup now skips the old startup repair pass when a reusable on-disk index already exists and `core.lucene9_rebuild_on_startup` is false
- Empty Lucene directories still trigger an initial build and explicit admin reindex operations still perform full rebuilds
April 13, 2026
Add Grafana-backed performance observability
The performance workflow now exports Wave and runner metrics to Grafana Cloud during Gatling runs and can upsert a matching Grafana dashboard for perf analysis.
✦New
- Added a local perf metrics exporter plus Alloy config generation so the performance workflow can scrape Wave, runner, and summary metrics during Gatling runs
- Added Grafana dashboard provisioning assets and workflow wiring so performance runs can publish observability data into Grafana Cloud
April 13, 2026
Add an importable Grafana analytics dashboard
Added a starter Grafana dashboard for Wave analytics counters and aligned the host docs with the metrics and JSON-log signals the runtime actually emits.
✦New
- Added a starter Grafana dashboard for the exported `wave_analytics_*` Prometheus counters plus request-rate and p95 latency charts
- Documented the dashboard import path and removed references to unsupported logfmt analytics event queries so the runbook matches the current Wave and Alloy pipeline
April 13, 2026
Move admin analytics out to Grafana
Wave no longer renders an admin analytics tab; the remaining analytics signals are exported through `/metrics` and the Grafana Alloy pipeline instead of being stored and queried inside the app.
✦New
- Removed the /admin Analytics tab and its server-side analytics API/history storage paths
- Wave now exports aggregate analytics counters through the existing Prometheus `/metrics` endpoint for Grafana dashboards and alerts
- Updated the SupaWave Alloy host configuration and runbook so Grafana Cloud scrapes Wave application metrics directly
April 13, 2026
Retry transient GHCR push failures during deploy builds
The Contabo deploy workflow now retries transient GHCR push failures and rejects invalid retry settings instead of silently skipping pushes or crashing mid-loop.
✓Fixed
- Deploy builds now push images through a retry helper that retries known transient registry and network failures before giving up.
- The retry helper now validates its retry-count and retry-delay environment variables and regression coverage now includes fail-fast, retry exhaustion, and invalid-config cases.
April 13, 2026
Restore deploy health wait window
Blue-green deploys now wait up to seven minutes for the target slot to report healthy, with the poll interval and timeout still configurable through environment variables.
✓Fixed
- Restored the default slot health wait window from the old 180-second cutoff to 420 seconds so slower Lucene-backed startups can finish before deploys abort
- Kept the slot health polling interval and timeout configurable through deploy environment variables without requiring script edits
April 13, 2026
Fix remaining Android IME root-blip text corruption case
Android/mobile IME composition no longer drops already-typed leading characters when Chrome temporarily promotes the composing word into a transient selection range.
✓Fixed
- Android/mobile IME composition no longer drops already-typed leading characters when Chrome temporarily promotes the composing word into a transient selection range.
April 12, 2026
Added Worktree Diagnostics Bundle Guidance
Adds a bundled diagnostics command and runbook so issue worktrees can capture compact startup, smoke, endpoint, and log evidence without inventing a separate observability workflow.
✦New
- Add scripts/worktree-diagnostics.sh: emits a Markdown diagnostics bundle with endpoint probes, smoke-check output, startup tail, and server-log tail for the current worktree runtime
- Add docs/runbooks/worktree-diagnostics.md: explains when to run the bundle, what it captures, and how to reference it from issue comments and PR summaries
- Update scripts/worktree-boot.sh, docs/runbooks/worktree-lane-lifecycle.md, docs/runbooks/README.md, and docs/SMOKE_TESTS.md so the diagnostics bundle is discoverable from the existing worktree verification flow
April 12, 2026
Fix top-panel toggle button active surface
The Pin and Archive top-panel buttons now keep a full-width active highlight instead of showing only a partial pressed background when toggled on.
✓Fixed
- Restored the shared down-state overlay for compact top-panel icon buttons like Pin and Archive so the active highlight spans the full button width
- Removed the compact-only down-state override that reintroduced a split active background on toggled toolbar buttons
April 12, 2026
Add a restore window for tag removal
Removing a wave tag now shows a 20-second restore toast so accidental tag deletes can be undone without reopening the tag editor.
✓Fixed
- Tag removal now shows a compact restore toast instead of a passive confirmation-only message
- Restoring a just-removed tag re-adds it from the toast action without leaving the current wave
April 12, 2026
Let users close tag restore toasts
Tag-removal restore toasts now include a manual close action that immediately finalizes the delete instead of keeping the restore window open.
✓Fixed
- Tag removal toasts now include a Close action alongside Restore
- Closing the toast immediately clears the pending restore window so the delete is finalized at once
April 12, 2026
Keep tag search fresh after wave cache commit notifications
Fixes stale `tag:` results when a cached wave lags behind a newer committed wavelet version delivered through commit notifications.
✓Fixed
- Invalidate a cached wave only when a commit notification proves the cached wavelet version is older than the committed version
- Preserve fresh local wave state so immediate metadata searches still reuse the in-memory cache when no newer committed data exists
- Add regression coverage for existing tag documents, stale shared-store caches, and pure `tag:` queries that must stay on the legacy search path
April 12, 2026
Update SupaWave repository references after GitHub rename
The app welcome wave and the repo’s operational tooling now point at the renamed SupaWave GitHub repository instead of the legacy incubator-wave slug.
✓Fixed
- Updated the in-app welcome wave repository link and label to point at the renamed `vega113/supawave` GitHub repository
- Updated PR-monitoring scripts, GitHub issue filters, and related operational docs to use the new repository slug so automation follows the renamed repo
April 12, 2026
Fix SupaWave Grafana log discovery
Grafana Alloy now tails only SupaWave's structured JSON log files and the host docs call out the correct selectors for finding those logs in Grafana.
✓Fixed
- Updated the SupaWave Alloy file tailer to target the structured `wave-json*.log` files so Loki discovery stays aligned with the JSON parser pipeline
- Documented the SupaWave Grafana selectors and troubleshooting steps needed to confirm Alloy is shipping the application logs correctly
April 12, 2026
Split-brain wavelet safety
Deploy overlap handling now shuts down replaced slots more defensively, stale wavelet persists fail fast without wedging the queue, and the corrupted-wave repair classifier keeps sibling wavelets independent.
✓Fixed
- Deploy rollback and cutover now fail closed if the replaced slot cannot be verified as stopped, instead of treating a failed `docker compose ps` check as success
- Stale wavelet persist failures now clear or reschedule queued persistence work so later callers do not hang behind an unexecuted retry task
- Corrupted-wave repair classification now buckets duplicate history by both wave id and wavelet id so one ambiguous sibling no longer blocks a safe repair on the same wave
April 12, 2026
Search panel unread reload fix
Search results now stop reviving stale unread counts for public/shared waves after a page reload when the stored user-data wavelet only has the legacy empty read-state.
✓Fixed
- Search digests now treat legacy empty public/shared user-data read-state the same as the original bootstrap case, so reopening the app no longer brings back stale unread badges
- The unread:true search path now uses the same legacy empty-UDW repair logic as digest generation, keeping unread filters aligned with the sidebar count
April 12, 2026
Cover search metadata paths for tags, mentions, and tasks
Added regression coverage to ensure tag, mention, and task searches continue to route through the expected metadata and legacy-query paths.
✓Fixed
- Added Lucene query-model and provider regression coverage so pure tag queries stay on the legacy metadata filter path
- Added end-to-end coverage for mention and task metadata search lookups so assigned and mentioned waves remain discoverable
April 12, 2026
Fix stale saved indicator during local edits
The topbar save state now treats locally uncommitted wave changes as unsaved instead of waiting only for server acknowledgements.
✓Fixed
- The topbar save state now treats locally uncommitted wave changes as unsaved instead of waiting only for server acknowledgements.
April 12, 2026
Robot RPC commit-failure responses
Robot RPC write calls now return JSON-RPC errors when the underlying delta fails to commit, instead of returning a success payload with a reply id that was never persisted.
✓Fixed
- The Jakarta robot RPC servlet now rewrites optimistic write responses into JSON-RPC errors when delta submission fails, so streaming bots do not receive phantom newBlipId values
- Added a regression test covering submitRequest failure handling for mutating robot RPC operations
April 12, 2026
OT search tag routing and fallback policy
Tag searches now stay on the OT/Lucene path when indexed search is enabled, and OT search no longer silently bootstraps or falls back to legacy HTTP polling unless an explicit fallback flag is enabled.
✓Fixed
- Tag queries now use Lucene candidate filtering in OT search mode instead of forcing the legacy tag-document path
- Added an `ot-search-fallback` feature flag, defaulting off, so production OT search surfaces failures immediately instead of silently switching to legacy HTTP bootstrap or polling
April 12, 2026
Keep mobile blips visible beneath wrapped edit toolbars
The wave conversation scroller now follows the real rendered toolbar height on mobile, so wrapped edit toolbars no longer cover the first blip while you edit.
✓Fixed
- When the mobile edit toolbar grows beyond a single row, the conversation pane now shifts down to keep the first blip fully visible
- Toolbar height changes from reflow and viewport resizing now resync the wave panel instead of leaving blip content hidden under the toolbar
April 12, 2026
Fix mobile new-blip edit loss after delayed composition teardown
New blips on mobile no longer lose typed text when the browser finishes composition without restoring a selection before the trailing DOM mutation arrives.
✓Fixed
- Allowed the trailing DOM mutation fallback to materialize browser-owned text when delayed composition end returns no editor selection
- Preserved the mobile new-blip flow where text previously appeared locally but disappeared after Done or reload
April 12, 2026
Show who reacted on desktop and mobile
Adds a Wave-native reaction-authors popup so people can inspect who reacted without relying on browser-default menus or tooltips.
✓Fixed
- Reaction chips now expose a custom authors popup for desktop secondary actions and keyboard inspection
- Mobile users can tap the reaction count to inspect who reacted without losing the quick reaction-toggle path
April 12, 2026
Remove Jakarta/Main Shadow Source Duplicates
Removed same-path Jakarta/main shadow classes and tightened the duplicate-source regression guard so formatting changes cannot bypass it.
✓Fixed
- Removed remaining duplicate Java source shadows between main and jakarta-overrides for the Jakarta migration closeout
- Hardened the duplicate-source regression test so exact exclude sets must still parse correctly even if build.sbt formatting changes
April 12, 2026
Move tag creation into the tag bar
Adding tags now happens inline in the existing tag bar so mobile no longer opens a cramped modal popup for tag entry.
✓Fixed
- The tag add button now opens a compact inline composer directly in the tags bar instead of a centered popup dialog
- Inline tag entry keeps comma-separated tag creation and keyboard shortcuts while fitting cleanly inside the mobile tags footer
April 12, 2026
Keep draft GWT dev output out of the production war tree
Updated the new dev GWT compile task to emit draft artifacts into a separate directory so production packaging continues to rely on full compileGwt output.
✓Fixed
- compileGwtDev now writes draft Safari-only GWT output into war-dev instead of war.
- Added a changelog fragment for the new dev-only GWT compile task behavior.
April 12, 2026
Silence unsupported CssResource constructs in GWT wave panel CSS
Removes unsupported `@media` and `calc()` usage from legacy GWT CssResource inputs and adds a regression test so compileGwt stops emitting parser warnings for the affected wave panel resources.
✓Fixed
- Removed unsupported CssResource syntax from the affected wave panel CSS resources.
- Added a regression test that fails if the blocked `@media` or `calc()` constructs return.
April 12, 2026
Fail Deploys Without Sanity Credentials
Deploy automation now fails closed when sanity-check credentials are missing so production rollouts cannot silently skip the post-deploy gate.
✓Fixed
- Require SANITY_ADDRESS and SANITY_PASSWORD before deploy runs continue so the deploy sanity gate cannot be bypassed by missing configuration
- Documented the deploy sanity credential requirement alongside the deploy workflow guard and regression test coverage
April 12, 2026
Suppress false deploy-failure alerts on cancelled runs
Deploy failure issue creation and notification now ignore cancelled runs without hiding real deploy or pre-deploy failures.
✓Fixed
- Deploy-failure alerts now skip cancelled runs while still firing for real deploy failures and early job failures.
- The deploy workflow no longer depends on checked-out helper code to decide whether failure alerts should run.
April 11, 2026
Close the reaction picker reliably after selection
Prevents duplicate add-reaction popups from lingering after a reaction is chosen and keeps repeated keyboard and click interactions stable.
✓Fixed
- The add-reaction picker now tracks a single active popup so repeated activation does not leave stale popups behind
- The picker moves focus into the emoji grid on open so keyboard selection continues in the active popup
April 11, 2026
Fix insecure random number generation in OperationQueue
Replaced java.util.Random with java.security.SecureRandom for the ID_GENERATOR field in OperationQueue.
✓Fixed
- Replaced java.util.Random with java.security.SecureRandom for generating temporary IDs in OperationQueue to fix a security vulnerability.
April 11, 2026
Fix MD5 vulnerability in Gravatar URL generation
Upgraded the hashing algorithm in HtmlRenderer from MD5 to SHA-256 for generating avatar URLs.
✓Fixed
- Upgraded Gravatar email hashing algorithm from MD5 to SHA-256 to resolve a security vulnerability.
April 11, 2026
Fix Android IME word-boundary corruption
Android/mobile editing no longer double-applies delayed post-composition DOM mutations that dropped leading letters and spaces while finishing a blip.
✓Fixed
- Stopped delayed Android IME composition end from falling through into the legacy DOM-mutation typing fallback in the same mutation event
- Preserved word-leading characters and inter-word spacing for the empty-blip editing path that could collapse `new blip` to `ewlip` on Android
April 10, 2026
Wave Reactions
Blips now support lightweight emoji reactions with inline counts and quick toggles.
✦New
- Added lightweight emoji reactions on wave blips with quick add, remove, and replace interactions
- Reaction counts now render inline on reacted blips without changing the message text itself
April 10, 2026
Wave Tasks: cleaner assignee labels
Task assignee labels now show the selected participant identifier without the extra owner prefix.
✓Fixed
- Task assignee pills now display just the selected participant label instead of prefixing it with "Owner"
April 10, 2026
Tag chips now filter search
Wave tags now use a mobile-friendlier chip layout where tapping the chip filters search and a separate inline affordance removes the tag.
✓Fixed
- Tapping a tag chip now applies a `tag:<name>` search filter instead of opening the remove-confirm popup
- Tag removal now uses its own explicit inline affordance, so the destructive action is no longer bound to the main chip tap target
- The old centered remove-confirm popup is no longer part of the tag-removal flow, which keeps the interaction compact and usable on mobile
April 10, 2026
Restore search freshness for newly shared waves and newly added tags
Fixes a search freshness regression where recent participant/tag mutations could be missed when a per-user search view rebuilt during the reload cooldown window.
✓Fixed
- Per-user search view rebuilds now force a full wave-map refresh after recent non-search wave mutations instead of incorrectly reusing the cooldown path
- Added regression coverage for participant visibility, content searchability, and tag searchability on the runtime memory-view search path
- Extended the Java E2E suite to verify Bob can find a newly shared wave by content term and that `tag:<newtag>` returns the wave after a fresh tag mutation
April 10, 2026
Robot Streaming Replies
Robots can now progressively stream reply text into a dedicated wave blip using the documented createChild plus document.modify pattern.
✦New
- Documented and example-backed support for progressive robot replies that create one reply blip and update it incrementally with document.modify
- Robot docs now direct clients to use the passive bundle's rpcServerUrl hint for follow-on reply writes instead of hardcoding a single RPC endpoint
April 10, 2026
Robots: Refresh Legacy Passive Capability RPC Hints
Passive robots with legacy stored capabilities now refresh before event delivery so rpcServerUrl matches the configured OAuth mode after restart.
✓Fixed
- Passive robots now refresh legacy capability snapshots that are missing rpcServerUrl before generating event bundles
- Two-legged passive robots restored from older persisted records now recover /robot/rpc instead of falling back to the Data API endpoint
April 10, 2026
Robots: Auth-Mode-Aware rpcServerUrl Advertisement
Passive robot event bundles now advertise the JSON-RPC endpoint that matches the robot's configured OAuth mode.
✓Fixed
- Passive robots using 2-legged OAuth now receive /robot/rpc in EventMessageBundle.rpcServerUrl
- Passive robots using 3-legged OAuth now receive /robot/dataapi/rpc in EventMessageBundle.rpcServerUrl
April 10, 2026
Registration UX Test Coverage
Adds servlet-level regression tests for the PRG-based post-registration UX flow.
✓Fixed
- Added test coverage for GET /auth/signin?registered=1 rendering the account-created success banner
- Added test coverage for GET /auth/register?check-email=1 rendering the check-your-inbox page
April 10, 2026
Registration Success Guidance
Registration now guides you into sign-in or email activation with clearer next steps instead of leaving you on the signup form.
✓Fixed
- Successful direct registrations now send you to sign-in with a clear account-created state
- Email-confirmation registrations now open a dedicated check-your-inbox page with the activation steps
- Trying to sign in before activation now shows an action-required state instead of looking like bad credentials
April 10, 2026
Fix Android mobile text corruption while editing
Android/mobile IME edits no longer corrupt persisted text by dropping leading characters and spaces during composition-driven editing.
✓Fixed
- Android/mobile IME edits no longer corrupt persisted text by dropping leading characters and spaces during composition-driven editing.
April 10, 2026
Mobile search panel stays hidden while editing
Android/mobile blip edit sessions now preserve horizontal focus scroll state so the search panel does not slide over the typing area.
✓Fixed
- Preserved both vertical and horizontal ancestor scroll positions when the mobile editor focuses
- Prevented the off-canvas search panel from being revealed over the active blip editor during Android/mobile edit startup
April 10, 2026
Mobile edit persistence follow-up
Android/mobile edit sessions now restore a valid caret when opening a blip editor, preventing typed text from appearing only locally and disappearing after reload.
✓Fixed
- Restored mobile editor focus initialization so edit sessions start with a valid caret instead of a DOM-only typing surface
- Fixed both existing-blip edits and brand-new reply blips losing typed text after reload because no document ops were generated
April 10, 2026
Mobile attachment picker reliably handles gallery and file selections
The mobile attachment popup now avoids stale hidden-file-input state and duplicate chooser launches so camera, gallery, and general file selections reach the upload flow more reliably.
✓Fixed
- Reset the hidden attachment input before and after each mobile picker session so gallery and file selections are not dropped when the chooser reuses prior single-file state
- Prevent the nested Select Files button from triggering the native file picker twice, preserving the working camera path while improving photos, videos, and general file selection behavior
April 10, 2026
Unify the pin icon across search and wave toolbars
Uses the same pin glyph in the wave action toolbar that already appears in the search toolbar so the two compact toolbars read consistently.
✓Fixed
- The wave toolbar pin action now uses the same thumbtack-style icon as the search toolbar while keeping the existing compact toolbar sizing and alignment
April 10, 2026
Polish reaction chip alignment
Keeps the add-reaction affordance anchored and aligns reaction emoji/counts more cleanly within the existing Wave pill styling.
✓Fixed
- Reaction rows now keep the add-reaction control anchored at the leading edge instead of shifting when reactions are toggled
- Reaction chips now render explicit emoji and count spans so the pill content aligns more cleanly on a shared visual baseline
April 10, 2026
Mobile attachment upload resilience fixes
Mobile image uploads are now more tolerant of browser-specific file-picker and clipboard behavior, reducing silent failures when selecting or pasting images into a wave.
✓Fixed
- Attachment selection now recovers from mobile browsers that return from the file chooser without delivering the hidden input's normal change event
- Image paste upload now falls back to clipboard files when browsers do not populate clipboardData.items for pasted images
April 10, 2026
Polish mention previous/next toolbar icons
The wave-toolbar mention navigation buttons now use clearer directional arrows while keeping the existing mention control behavior and toolbar styling.
✓Fixed
- Prev @ and Next @ buttons in the wave toolbar now show clearer left/right direction cues
- Mention navigation behavior is unchanged; only the toolbar icon treatment was polished
April 10, 2026
Use a clearer add-reaction icon
Replaces the generic plus reaction affordance with a reaction-specific icon that reads more clearly in the wave UI.
✓Fixed
- The add-reaction control now uses a compact reaction icon instead of a bare plus
- The reaction add button keeps the existing chip styling while reading more clearly as a reaction action
April 10, 2026
Fix split active background on wave toolbar buttons
Keeps clicked and toggled wave-toolbar buttons on one coherent active surface instead of layering two mismatched background treatments.
✓Fixed
- Wave-toolbar buttons like Pin now render a single coherent active background when toggled on
- The compact toolbar active-state polish stays scoped to the clicked/toggled wave-toolbar button treatment
April 10, 2026
Restore the search toolbar chrome and remove temporary overflow affordance
Restores the intended toolbar-strip treatment for search and wave action rows, keeps refresh in the search toolbar, and removes the visible search-toolbar overflow button.
✓Fixed
- Search and wave toolbars render with the intended contained toolbar chrome again
- The search toolbar keeps its refresh action while dropping the temporary ... overflow control
April 10, 2026
Fix Grafana Alloy timestamp parsing for Wave JSON logs
Switched the Alloy timestamp format from millisecond to RFC3339Nano so microsecond-precision timestamps emitted by Wave JSON logs are parsed correctly and logs appear in Grafana.
✓Fixed
- Changed Grafana Alloy timestamp format from `2006-01-02T15:04:05.000Z07:00` (millisecond) to `RFC3339Nano` to match the microsecond-precision timestamps emitted by logstash-logback-encoder
- Added deploy-time diagnostics: script now prints WAVE_LOG_PATH, WAVE_TIMESTAMP_FORMAT, matched log files, and writes a syslog audit line when Loki shipping is configured
- Added structured JSON log output path logging at Wave server startup for operator visibility
- Added regression tests: Python unittest for Alloy config timestamp contract and Jakarta test for startup log diagnostic
April 10, 2026
Browser verification runbook wording follow-up
Clarifies the browser-verification runbook language around smoke checks, browser passes, and evidence capture.
✓Fixed
- Clarified that smoke checks prove the server boots, health responds, and the compiled web client asset is present
- Clarified that the browser pass is only for the narrow auth, routing, or UI behavior that curl cannot prove
April 10, 2026
Standardized Browser Verification Runbooks
Adds a browser-verification runbook and change-type matrix so agent lanes know when curl/smoke is enough and when a real browser pass is required, without introducing a new browser framework.
✦New
- Add docs/runbooks/browser-verification.md: defines the default UI-affecting verification path built on the existing wave-smoke-ui.sh and worktree-boot.sh + wave-smoke.sh baseline
- Add docs/runbooks/change-type-verification-matrix.md: decision matrix covering server-only, servlet/auth, GWT client/UI, packaging, and deployment-only change types
- Update docs/runbooks/worktree-lane-lifecycle.md: step 7 cross-links to the new browser-verification and change-type matrix runbooks
- Update docs/SMOKE_TESTS.md: adds browser-verification baseline section pointing to the new runbooks
- Update docs/runbooks/README.md: indexes the two new runbooks under Local Development
April 9, 2026
Welcome wave polish
The welcome wave now has clearer navigation guidance, stronger bot coaching for gpt-ts-bot@supawave.ai, public-wave discovery via the @ icon in the left search panel, and direct internal wave links across the onboarding/public support waves.
✦New
- Added gpt-ts-bot@supawave.ai guidance with concrete usage instructions in the welcome wave
- Explained public-wave discovery via the @ icon in the left search panel
- Added direct wave:// links to all six onboarding/public support waves
April 9, 2026
Welcome wave: real onboarding wave titles in navigation
The wave-to-wave navigation block in the welcome wave now uses the actual onboarding wave titles instead of generic 'Wave 0'–'Wave 5' placeholders.
✓Fixed
- Navigation links in the welcome wave now show real titles: 'SupaWave Community — Questions, Feedback & Support', 'Welcome to SupaWave', 'Search Like a Pro', 'Collaboration Features', 'Making Waves Public', and 'Tips, Shortcuts, and Hidden Features'
April 9, 2026
Toolbar SVG Icon Polish and Hover Effects
Replaces toolbar PNG sprites with refined SVG icons and adds smooth hover and press visual feedback.
✓Fixed
- Replaced edit toolbar PNG sprites with polished SVG icons using lighter 18px strokes
- Added smooth hover color transition and press scale effect on toolbar icon buttons
- Scoped icon hover effects to enabled buttons only to avoid misleading disabled state feedback
- Converted toolbar button layout to flex-based for improved vertical centering and touch sizing
April 9, 2026
Increase toolbar icon canvas size for tighter vertical fit
Bumps the shared toolbar icon display size from 17px to 20px so search and wave toolbar icons fill more of the existing 36px action buttons without changing toolbar heights or panel offsets.
✓Fixed
- Search and wave toolbar icons render at 20px inside the existing action buttons, reducing visible vertical dead space without changing toolbar layout
April 9, 2026
Polish toolbar action canvas containment
Search and wave toolbar icons now sit inside a clearer solid action canvas instead of reading against the faded edge of the bar.
✓Fixed
- Search and wave toolbar action icons now render inside a subtle inset canvas that keeps them visually contained within the toolbar strip
April 9, 2026
Tasks button shows all assigned tasks by default
The Tasks toolbar button now opens `tasks:me` instead of `tasks:me unread:true`, showing all assigned tasks rather than only unread ones.
✓Fixed
- Tasks toolbar button click now sets the search query to `tasks:me`, displaying all tasks assigned to the current user
- Unread badge polling continues to use `tasks:me unread:true` so badge counts remain accurate
April 9, 2026
Strikethrough Styling for Completed Task Lines
Completed wave tasks now render with strikethrough text and a dimmed color, providing clear visual feedback on task status. Styling is applied on toggle and on document load, and cleaned up correctly when a task checkbox is removed.
✓Fixed
- Apply text-decoration: line-through and color: #767676 to the enclosing paragraph when a task checkbox is checked
- Remove styling when the checkbox is unchecked (guarded against multiple task checkboxes per paragraph)
- Clean up stale task-completed class via onDeactivated when a checked task checkbox is deleted
- Only affects task checkboxes (name prefixed with task:); generic form checkboxes are unaffected
April 9, 2026
Enable task-search feature flag by default
The task-search feature flag now defaults to enabled so fresh deployments show tasks search help and toolbar without a manual MongoDB record.
✓Fixed
- Changed task-search default in KnownFeatureFlags from false to true — prevents silent hide of tasks:all search help and Tasks toolbar button on new deployments or DB resets
April 9, 2026
Clarify task search semantics and help examples
Search now supports `tasks:all` for any visible task wave, and the search help panel explains the supported task-query forms more clearly.
✓Fixed
- Added `tasks:all` to find waves you can access that contain any task, alongside the existing `tasks:me` and user-targeted task queries
- Updated search help with clickable task-query examples for `tasks:all`, `tasks:me`, `tasks:user@domain`, local-domain shorthand such as `tasks:alice`, and `tasks:all unread:true`
April 9, 2026
Wave Tasks: assignee picker, due dates, and clearer ownership
Tasks in the wave editor now open a metadata popup for assignee and due date, and the task row shows explicit owner and due-date pills so ownership is easier to understand at a glance.
✦New
- Insert Task now opens an in-wave task details popup where you can assign the task to any current wave participant and set or clear a due date
- Task rows now render explicit owner and due-date pills beside the checkbox, using utility styling that stays visually distinct from @mentions
- Clicking the task metadata pills reopens the same popup so assignee and due date can be edited after the task is created
April 9, 2026
Fix vertical centering of toolbar icons
Switches toolbar button layout from float/margin centering to flexbox and adds a universal CSS rule to suppress the inline SVG baseline gap.
✓Fixed
- Replace float/margin-top centering in HorizontalToolbarButtonWidget with inline-flex + align-items:center for robust vertical alignment
- Add .visualElement svg { display: block } CSS rule to eliminate the inline baseline/descender gap for all toolbar SVG icons universally
April 9, 2026
Paste-to-Upload Images in Wave Editor
Adds clipboard image paste support to the wave editor: Ctrl+V/Cmd+V with an image on the clipboard automatically uploads and inserts the image inline at the cursor position.
✦New
- Paste an image from clipboard directly into the wave editor via Ctrl+V/Cmd+V
- Spinner toast appears during upload; image renders inline at paste cursor on success
- Error toast shown if upload fails or times out after 60 seconds
- Text paste behaviour is unaffected — falls through normally when no image is present
April 9, 2026
Multi-File Upload with Thumbnail Previews and Captions
Redesigns the attachment upload dialog with multi-file selection, per-file thumbnail previews, inline captions, real XHR progress bars, image compression, and drag-and-drop support.
✦New
- Select and upload multiple files at once via the attachment popup
- Thumbnail previews for images; file-type icon badges for non-image files
- Per-file caption input inserted into the wave alongside each attachment
- Real XHR upload progress bars with wave-shimmer animation per card
- Optional image compression with configurable display size (S/M/L) before upload
- Drag-and-drop files onto the drop zone to populate the selection
- Remove individual files from the pending batch before uploading
- Graceful error handling: failed uploads stay visible; queue continues to the next file
April 9, 2026
Mobile edit persistence fix
Android/mobile blip edits now commit pending IME text before the editor closes, preventing text that looked saved locally from disappearing after reload.
✓Fixed
- Committed pending IME composition text before mobile edit sessions detach the editor
- Added webdriver hooks for editor pending-input inspection while debugging the mobile edit-loss path
April 9, 2026
Fix attachment metadata lookups for plus-sign ids
URL-encodes attachment ids before the Wave client requests `/attachmentsInfo`, so image and file metadata still load when ids contain `+`.
✓Fixed
- Attachment thumbnails and image metadata now load correctly when the stored attachment id contains a plus sign
April 9, 2026
Polish toolbar icon scale and containment
Reduces the shared toolbar SVG display size so search and wave toolbars feel better centered inside the existing compact bars.
✓Fixed
- Search and wave toolbar icons render with slightly lighter visual density while keeping the same compact toolbar height
April 9, 2026
Fix toolbar clipping and compact spacing
Fixes the remaining 24px legacy toolbar height assumptions, tightens compact icon-button spacing, and aligns the search toolbar SVG contract with the wave/edit toolbars.
✓Fixed
- Search, wave, and edit toolbar icons now render fully without vertical clipping, including wrapped edit-toolbar rows on narrow widths
- Compact toolbar buttons use denser spacing and the search toolbar now shares the polished 18px SVG wrapper/sizing contract
April 8, 2026
Standardized Worktree Lane Boot Lifecycle
Adds a worktree boot helper script and runbook so agent lanes can reliably start, verify, and shut down isolated development environments without tribal knowledge.
✦New
- Add scripts/worktree-boot.sh: stages the app, detects port conflicts, writes a port-specific runtime config, and initializes the local-verification journal entry
- Add docs/runbooks/worktree-lane-lifecycle.md: single runbook covering worktree creation through shutdown
- Update scripts/wave-smoke.sh to honor PORT from the environment with numeric validation
- Document the journal/local-verification/ evidence contract in AGENTS.md and the agent orchestration plan
April 8, 2026
Welcome wave onboarding refresh
New accounts now receive a richer SupaWave welcome wave with guided product onboarding, inline advanced details, and a clearer path for AI-oriented collaboration.
✦New
- Replaced the minimal welcome wave with a field-guide style onboarding wave that explains what Wave is, why it is different, and how to start using it
- Added inline detail blips for history, Firefly naming, robot/API orientation, and active modernization topics so advanced context stays available without overwhelming the main path
- Welcome-wave creation now happens when an account becomes usable: immediately when email confirmation is disabled, or after successful email confirmation when it is enabled
April 8, 2026
Wave Tasks: assignable inline tasks with due dates and task search
Introduces inline tasks within wave documents — assignable to participants, searchable via `tasks:me`, and tracked with a badge in the search panel.
✦New
- Inline task creation via Insert Task toolbar button — inserts a checkbox element with task annotations (id, assignee, due date, reminders)
- Task search via `tasks:me` and `tasks:user@domain` tokens in both legacy and Lucene9 search paths
- Search panel toolbar badge showing count of open tasks assigned to the current user
- Per-wave task count badge in the search digest list
- Reminder offsets stored in document model (`task/reminders` annotation) for future in-app surfacing
- Server and public HTML renderers emit disabled checkboxes for task elements
April 8, 2026
Fix Stale Search Results and Unread Filter
Empty search queries now clear the wave list, and the unread:true filter correctly excludes read waves.
✓Fixed
- Fixed wave list not clearing when search returns zero results — header showed '0-0 of 0' but previous results remained visible
- Fixed unread:true filter returning read waves due to readState-based path iterating orphaned blip documents; now uses supplement-based counting consistent with digest generation
April 8, 2026
Render Medium And Large Attachments As Inline Images
Image attachments now use true inline rendering for medium and large display modes instead of just enlarging thumbnail cards.
✦New
- Medium and large image attachments now render from the original attachment image for a more Telegram-like inline photo experience
- Image attachments keep their attachment-backed document model while preserving thumbnail mode for compact displays
- New image uploads now default to medium display size instead of small thumbnails
April 8, 2026
Support Attachment-Backed Inline Images In The Robot API
Robots can now author attachment-backed inline images with optional display sizing through the Java/Data API surface.
✦New
- Java robots can insert attachment-backed inline images with Image(attachmentId, caption) and optional display-size values of small, medium, or large
- document.modify now accepts non-form elements for inline image insertion instead of rejecting IMAGE elements server-side
- The published API docs now describe the importAttachment plus document.modify flow for attachment-backed inline images
April 8, 2026
Fix Inline Attachment Display Regressions
Follow-up fixes to PR #738 addressing source-selection regressions, thumbnail flicker, and premature attachment URL loads.
✓Fixed
- Legacy style="full" attachments now correctly use attachment source even when content metadata is temporarily unavailable (fullMode check precedes contentImage guard)
- Thumbnail size callbacks no longer trigger redundant URL reloads when the attachment source is already active
- Attachment size callbacks only trigger image loads when the attachment is the active source, reducing premature requests before the malware guard runs
April 8, 2026
Fix Profile Bio Newlines
Multi-line bios now display correctly in profile cards and the profile edit form.
✓Fixed
- Fixed newlines entered in bio being stored as literal \n sequences due to a broken JSON escape parser
- Legacy bios with literal \n sequences are migrated to real newlines on read when safe to do so
April 8, 2026
Fix Image Attachment Ellipse Shape
Image attachments were rendered with an oval/ellipse appearance due to an overly large border-radius on the thumbnail container. Reduced container border-radius to 6px and removed separate image border-radius so the container's overflow:hidden cleanly clips corners to a proper rectangle.
✓Fixed
- Image attachment thumbnails now display as rectangles with subtle 6px rounded corners instead of oval/ellipse shapes
- Removed asymmetric border-radius (8px top only) from the image element; container overflow:hidden now handles corner clipping consistently
April 8, 2026
Contact Form Accessible to Unauthenticated Users
The /contact page is now publicly accessible — anonymous visitors can view and submit the contact form without signing in.
✓Fixed
- Removed auth redirect from ContactServlet.doGet so unauthenticated users receive the contact form instead of being sent to sign-in
- Anonymous POST submissions are now accepted; email is read from the request body and validated for format
- Added IP-based rate limiting (max 3 submissions per IP per hour) to prevent abuse
- Added honeypot field to silently discard bot submissions
- Sanitized all input fields: control characters stripped (including \r for SMTP header injection prevention) and field lengths capped
- Updated HTML form: email field is editable and required for anonymous users, pre-filled and read-only for authenticated users; file attachment zone removed
April 8, 2026
Fix Robot Blip Newline Rendering
Newlines in robot-authored blip content are now converted to <line/> elements so they render correctly in the wave editor.
✓Fixed
- Robot blip content containing literal \n characters is now split into proper <line/> elements on insert
- Fixes #725: robot-generated multi-line blips were displayed as a single collapsed line
April 8, 2026
Admin Contact Notification Badge
Admin and owner users now see an envelope icon in the top bar with a real-time badge showing unread contact message counts.
✦New
- Envelope icon appears in the top bar for admin and owner roles, between the wifi icon and user avatar
- Red badge with unread contact message count, updated every 30 seconds via polling
- Pulsating red glow animation when there are unread messages
- Clicking the icon navigates to /admin#contacts and auto-activates the Contact Messages tab
- Admin page supports hash-based deep-linking for contacts, users, and other tabs
April 7, 2026
Server-side sweeper clears stale editor annotations from crashed sessions
A new background StaleAnnotationSweeper service runs every 10 minutes and submits cleanup deltas to close any user/d/ or user/e/ editor-session annotations older than 30 minutes, preventing crashed-session stale annotations from blocking bots indefinitely.
✓Fixed
- New StaleAnnotationSweeper service sweeps all wavelets every 10 minutes and closes stale open editor-session annotations (user/d/ and user/e/) older than 30 minutes
- GptBotRobot isBlipBeingEdited() now treats sessions with startTimeMs older than 30 minutes as stale so the GPT bot no longer skips blips with lingering crashed-session annotations
- EventGenerator allEditingSessionsClosed() uses the same 30-minute TTL so BLIP_EDITING_DONE fires correctly even when stale sessions remain on a blip
- Cleanup deltas are attributed to the first current participant rather than the annotation-derived user ID, closing a potential identity-spoofing vector
April 7, 2026
Clickable Examples in Search Help Panel
All search examples in the help panel are now clickable and execute the search immediately.
✦New
- Concrete filter rows (in:inbox, in:archive, in:all, in:pinned, with:@, unread:true, mentions:me) are now clickable spans that execute the search when clicked
- Abstract filter rows (with:user@domain, creator:user@domain, tag:name, title:text, content:text) now show a clickable 'try:' example in the description column
- All sort-option rows (orderby:datedesc, orderby:dateasc, etc.) are now clickable
- Combination examples (in:inbox tag:important, mentions:me unread:true, and more) are shown as a clickable grid in the Combinations section
- Removed the separate 'Examples — click to search' section; all examples are now integrated into the reference tables
April 7, 2026
Review Gate Quiet Window Extended to 10 Minutes
Increased the Codex review gate quiet window from 5 minutes to 10 minutes to prevent post-merge review bot comments from being missed.
✓Fixed
- Extended REVIEW_WINDOW_MS from 5 minutes to 10 minutes in the codex review gate script to ensure review bot comments arriving shortly after merge are captured
- Updated all user-facing messages, test constants, and workflow comments to reflect the new 10-minute window
April 7, 2026
PR Monitor Path-Based PR Number Detection
PR monitor now extracts PR numbers from worktree paths when pane titles lack a PR# marker, preventing unbounded accumulation of stale panes.
✓Fixed
- Detect PR numbers from pane worktree paths (pr-NNN-lane) as fallback when title has no PR# marker — panes with generic 'Claude Code' titles are now correctly identified and cleaned up
- Re-read pane list inside the PART 2 loop each iteration to prevent duplicate lane creation when launch_interactive_agent sleeps during startup
April 7, 2026
Pinned Waves Sort to Top in Inbox
Pinned waves are promoted to the top of the inbox and displayed with a pin icon. Explicit sort orders (orderby:) are respected and suppress pin promotion.
✦New
- Pinned waves appear at the top of in:inbox results (no orderby), sorted date-desc within each group
- Pin icon shown next to pinned waves in all search result views
- Explicit orderby: modifier disables pin promotion to respect user-chosen sort order
April 7, 2026
New Messages Pill Indicator
A floating pill appears when new messages arrive below the viewport, letting you jump to unread content with one click.
✦New
- Added floating 'N new messages' pill when scrolled up in a conversation
- Clicking the pill scrolls to the new content and dismisses it
- Pill auto-dismisses when scrolling near the bottom
- Feature-flagged via 'new-blip-indicator' server flag (off by default)
April 7, 2026
Mention UX Overhaul
Redesigned mention indicators with a red dot badge, per-wave unread counts, and in-wave navigation to the first @mention blip.
✓Fixed
- Red dot badge on the @ toolbar icon now shows accurate unread mention count using deduplicated wave IDs
- Per-wave @N badges in mention-search results correctly survive polling updates without disappearing
- Prev @/Next @ toolbar buttons now navigate directly to the first mention blip within a wave
- Legacy toolbar callers without a signed-in user no longer render non-functional Prev @/Next @ buttons
- Mention focus falls back to root blip when no blip is focused on initial wave load
April 7, 2026
Unread Mentions Badge
See at a glance how many waves have unread @mentions of you, and jump to the next one with a single click.
✦New
- Added unread mentions badge on the @ toolbar icon showing count of mentioned waves with unread messages
- Added floating @N navigation button to cycle through waves with unread mentions
- New feature flag: mention-unread-badge (requires mentions-search to also be enabled)
April 7, 2026
Mention Tracker Robustness
Fixes a loop where multi-page mention scans on slow connections were perpetually cancelled before completing.
✓Fixed
- Stale-request watchdog now resets per page fetch so slow-but-progressing multi-page scans are not incorrectly cancelled
- Watchdog timer now uses the injected TimerService for consistency and testability
- Duplicate wave IDs across paginated results are now deduplicated before updating the badge count
April 7, 2026
Fix Profile Image URL Construction
Profile avatars now display correctly for users who have uploaded a custom profile photo.
✓Fixed
- Fixed 431 error caused by base64 image data being used as a URL path segment in profile avatar requests
- InitialsProfilesFetcher and GravatarProfilesFetcher now correctly use the user address as the proxy URL key instead of the raw base64 attachment ID
April 7, 2026
Feature Flag Override Precedence Fix
User-specific feature flag overrides now take precedence over global enabled state, ensuring admins can disable features for individual users even when globally enabled.
✓Fixed
- Feature flag user overrides now take precedence over global enabled state — a user-specific disabled override correctly disables the flag even when globally enabled
- Fixed thread-safety issue in getEnabledFlagNames() that could mix cache generations during concurrent refresh
April 7, 2026
Fix Editor Cursor Corruption During Typing
Fixed an intermittent bug where characters could be inserted at incorrect positions mid-word during typing in the blip editor.
✓Fixed
- Fixed cursor corruption in the OT editor caused by AnnotationPainter's async transparentSlice DOM mutations displacing the browser caret without saving/restoring the selection
- transparentSlice in ContentDocument.FullContent now wraps DOM mutations with selectionMaintainer.saveSelection/restoreSelection, consistent with all other transparent DOM operations
April 7, 2026
CI: Unit Tests in PR Build Pipeline
Unit tests now run automatically on every pull request, catching regressions before merge.
✦New
- Added sbt unit test execution to the GitHub Actions PR build pipeline
- Test/update step retries up to 3 times to avoid transient dependency resolution failures
April 6, 2026
Smarter Upgrade Banner Messages
The upgrade popup now shows context-appropriate messages instead of a generic 'new version' notice for minor deploys.
✓Fixed
- Minor deploys (no changelog fragment) now show 'A minor update has been applied.' instead of the misleading generic update message
- Partial changelog deploys now show the first release title with 'and other updates.' for better clarity
April 6, 2026
Fix Premature Unread Badge Clear on Wave Open
Unread counts in the search panel no longer disappear when opening a wave that has unread blips from bots or other participants.
✓Fixed
- Opening a wave no longer immediately zeroes the unread badge in the search panel; the badge now reflects actual read state as reported by the supplement
April 6, 2026
Fix Android mobile reconnect after server deploy
Android Chrome users no longer see the connection indicator flip from green to yellow when they start writing after a server deploy.
✓Fixed
- Android: stale WebSocket onclose from old socket no longer triggers false reconnect after successful reconnection
April 6, 2026
Fix mention-subscription test for non-participants
Fixes a failing unit test that verified mention subscriptions are triggered for non-participant users at waveletCommitted.
✓Fixed
- Fixed Mockito stub overload mismatch in SearchWaveletUpdaterTest: DocOpCursor extends DocInitializationCursor, so any() resolved to the wrong apply() overload, preventing the test from verifying mention subscription dispatch to non-participants
April 6, 2026
Fix Lucene real-time indexer: consumer thread diagnostics and field regression tests
Adds consumer thread health logging (startup, per-enqueue at FINE, heartbeat every 100 updates) to diagnose silent index-stall after startup. Adds 7 regression tests validating that tag and mention fields are stored and queryable via in-memory Lucene round-trips.
✓Fixed
- Lucene index consumer thread now logs startup and a heartbeat every 100 processed updates
- waveletCommitted() logs each enqueue at FINE level for per-wave visibility when needed
- Added regression tests: tag fields stored in index, mention fields stored, empty mentions skipped, tag TermQuery round-trip, tag mismatch, mention TermQuery round-trip, re-index updates tag correctly
April 6, 2026
GPT Bot Smart Reply Improvements
Four improvements to the gpt-bot: editing guard, root-thread replies, per-wave conversation memory, and web search.
✦New
- Bot now waits for editing to finish (user/d annotation cleared) before responding to DOCUMENT_CHANGED events
- Bot replies are appended to the root thread instead of as inline child blips
- Per-wave conversation history with token-based pruning (len/4 estimate, drops oldest turns when >80 000 tokens)
- Web search tool via OpenAI function calling backed by DuckDuckGo instant answers
✓Fixed
- Sibling follow-up heuristic restricted to non-root threads to prevent over-triggering after root-thread replies
- Conversation history reads are now properly synchronized against concurrent pruning
April 6, 2026
Fix gpt-bot @mention handling and reply-thread continuations
The gpt-bot now reliably responds to @mentions and to follow-up messages sent in its reply threads without requiring a re-typed @mention. Three root causes were addressed: missing DOCUMENT_CHANGED capability causing the server to stop dispatching events, a missing PARENT context causing the parent-blip lookup to fail in production, and an empty-blip guard to prevent premature replies when new thread blips are created.
✓Fixed
- Declare DOCUMENT_CHANGED capability in the bot XML so the server delivers @mention events after a capability refresh
- Changed capability context from SELF,SIBLINGS to SELF,SIBLINGS,PARENT so parent blips are included in event bundles
- Bot now replies to follow-up messages in its reply threads without requiring a re-typed @mention
- Guard against empty WAVELET_BLIP_CREATED events triggering premature bot replies before the user has typed
April 6, 2026
GPT Bot Editing Guard Fix
The GPT bot now correctly detects when a user has finished editing, so it replies after editing ends instead of being permanently silenced.
✓Fixed
- Fixed GPT bot editing guard: bot was checking annotation existence instead of value, causing it to never reply to @mentions
April 6, 2026
gpt-bot: reply to bot-thread continuations and default to OpenAI engine
The gpt-bot now responds to all messages in threads where it has previously replied, not just explicit @mentions. Inline-reply siblings are also covered via a thread-scan. The startup default engine is changed from echo to openai (with graceful fallback to echo when OPENAI_API_KEY is absent), and the contributor check now uses anyMatch instead of only the last contributor.
✓Fixed
- Bot now responds to follow-up replies in threads it participated in even when @mention is absent
- Added isBotThreadReply helper that uses anyMatch across all contributors (not just last) for correctness
- Inline-reply thread siblings are scanned so inline continuations inside a bot blip also trigger replies
- gptbot-start.sh default engine changed from echo to openai; falls back to echo with a warning when OPENAI_API_KEY is not set
April 6, 2026
Fix mentions:me search returning all waves
The mentions:me search query was returning all waves instead of only waves where the user was mentioned. The Lucene9 search provider was stripping the mentions: token from the legacy query, bypassing filterByMentions in SimpleSearchProviderImpl. The Lucene MENTIONED field is also unreliable for waves indexed before the mentions feature was deployed. Fix routes mentions: filters back through the legacy annotation-based filterByMentions path, which reads directly from wave data and is always authoritative.
✓Fixed
- mentions:me search now correctly returns only waves where the current user has a mention annotation, instead of returning all accessible waves
April 6, 2026
Echo Bot Separation and Smarter Echo Timing
The echo bot is now a separate robot registration (echo-bot@supawave.ai) and replies only once when you finish editing a message, not on every keystroke.
✦New
- Introduced echo-bot@supawave.ai as a dedicated echo robot on port 8088
- Added GPTBOT_SUBMITTED_ONLY flag: when true, the bot skips DOCUMENT_CHANGED events and only replies after blip editing is complete (BLIP_SUBMITTED)
- Added echobot-start.sh and echobot-stop.sh startup scripts
✓Fixed
- Fixed mktemp race in gptbot-start.sh by using a proper XXXXXXXX suffix pattern
- Added named Cloudflare tunnel support via CLOUDFLARE_TUNNEL_NAME for persistent bot URLs
April 6, 2026
Fix Echo-Bot Not Responding to Messages
Echo-bot now processes DOCUMENT_CHANGED events regardless of the submittedOnly setting, restoring replies after BLIP_SUBMITTED was phased out.
✓Fixed
- GptBotRobot now handles DOCUMENT_CHANGED events when submittedOnly=true — BLIP_SUBMITTED is phased out in modern Wave, so DOCUMENT_CHANGED is the authoritative blip-committed signal
April 6, 2026
New BLIP_EDITING_DONE robot event replaces removed BLIP_SUBMITTED
Robots can now subscribe to BLIP_EDITING_DONE — a synthetic event fired when all editing sessions on a blip are complete. BLIP_SUBMITTED has been removed entirely (it was never generated by the server). GptBotRobot updated to use BLIP_EDITING_DONE as its primary trigger.
✦New
- New BLIP_EDITING_DONE event type synthesized by EventGenerator when all user/d/ annotations have end timestamps
- GptBotRobot now subscribes to BLIP_EDITING_DONE for reliable editing-completion detection
- Removed BLIP_SUBMITTED event type, BlipSubmittedEvent class, and onBlipSubmitted handler — the event was never generated by the server
April 5, 2026
RTL language support with auto-detection and toolbar toggle
Paragraphs now use dir="auto" for browser-native RTL auto-detection (Hebrew, Arabic). Added RTL/LTR toolbar buttons for manual override. Clicking an active direction button returns the paragraph to auto mode.
✦New
- Paragraphs with no explicit direction render with dir="auto" — the browser detects RTL from the first strong bidi character (same algorithm as Gmail)
- RTL and LTR toggle buttons in the editor toolbar let users force a paragraph direction
- Clicking an active direction button returns the paragraph to auto-detect mode
- CSS uses text-align:start so RTL paragraphs naturally align right without an explicit alignment override
- Explicit LTR stored as d="l"; explicit RTL as d="r"; auto state has no d attribute
✓Fixed
- Toggling direction off now clears any alignment that was implicitly set by the direction toggle
- Indentation uses margin-inline-start for bidi-correct indent on both auto-detected and explicit RTL paragraphs
April 5, 2026
Refine robot form element updates
Robot form element updates now preserve the existing wire format, and the form-value event docs reflect the supported value-backed element types.
✓Fixed
- UPDATE_ELEMENT now rewrites form elements through their XML serializer instead of setting invalid attributes
- Checkbox updates keep the existing default value unless it is explicitly changed
- FORM_VALUE_CHANGED is documented for value-attribute-backed form elements only
April 5, 2026
Reload after prolonged reconnect to resync the client
The client now performs a hard reload after a long disconnect so it can recover cleanly from deploys or server restarts.
✓Fixed
- Automatically reloads the page after a disconnect lasting longer than 5 seconds once the connection is restored
- Preserves short disconnects as normal network hiccups without forcing a reload
April 5, 2026
Save edits before prolonged reconnect reloads
Prolonged reconnects now end the active edit session before the client reloads, so the browser resyncs from a clean state without discarding draft changes.
✓Fixed
- Reloads after prolonged disconnects now always resync the client once the connection is restored
- Any active edit session is ended before the reload so draft changes are saved first
April 5, 2026
Add @mention support in the editor and search UI
Adds inline mention autocomplete and rendering, mention-aware search, and a backend capability gate so the search UI only advertises mentions where they are supported. The follow-up fix also rejects raw mentions queries on Solr so they fail closed instead of returning incorrect results.
✦New
- Typing @ in the editor opens a participant autocomplete popup and inserts highlighted mention annotations
- Search results can be filtered with mentions:me where the backend supports mention search
- The search help panel and Mentions toolbar button are hidden on Solr deployments that do not support mention search
- Raw mentions queries on Solr are rejected instead of producing incorrect results
April 5, 2026
Use DOCUMENT_CHANGED with editor-activity guard for passive robot replies
The gpt-bot example robot now uses DOCUMENT_CHANGED (since BLIP_SUBMITTED is not implemented server-side) with an editor-annotation guard to avoid per-keystroke replies. Also adds OpenAI Chat Completions engine and diagnostic logging to RobotsGateway.
✦New
- Added OpenAI Chat Completions engine for gpt-bot (set GPTBOT_CODEX_ENGINE=openai)
- Added diagnostic INFO logging to RobotsGateway passive dispatch path
✓Fixed
- Passive DOCUMENT_CHANGED replies now wait for editor annotations (user/d/, user/e/) to clear before responding
- Fixed null root wavelet crash in search panel snippet rendering (WaveBasedDigest)
April 5, 2026
Preserve admin-set feature flags across restarts
Startup seeding now leaves an existing ot-search flag untouched, so admin-set enabled or disabled state survives restarts. Fresh deploys still initialize ot-search from the deployment default.
✓Fixed
- Startup seeding only creates ot-search when the flag is missing from the database
- Fresh deployments continue to initialize ot-search from the configured default
April 5, 2026
Clean up blip panel: remove ID display, hide edit hint on mobile
Removes the debugging blip-ID label from blip headers and hides the keyboard-shortcut hint on mobile viewports where it is irrelevant.
✓Fixed
- Removed the monospace blip-ID span and click-to-copy handler from blip headers
- Edit-mode hint (Shift+Enter / Esc) is now hidden on viewports narrower than 768px
April 5, 2026
Add Lucene query stats to Operations
The admin Operations page now shows Lucene query counts and rolling average query time in a dedicated sub-tab, alongside the existing overview and OT Search metrics. The ops status API now emits locale-stable Lucene timing fields so the page keeps loading across JVM locales.
✦New
- Added a Lucene sub-tab to the Operations page for query stats and related search-index metrics
- Exposed Lucene query count and average query time from the ops status API
- Kept the admin UI's timing displays stable across JVM locales
April 4, 2026
Fix gpt-bot robot URL handling and add echo engine
Keeps custom robot base paths intact when passive robots fetch capabilities or post callbacks, and adds an echo completion engine for local gpt-bot runs.
✦New
- Added an echo completion engine for gpt-bot local testing without a Codex CLI process
✓Fixed
- Passive capabilities fetches now preserve custom robot base paths instead of stripping them to the origin
- Passive callback posts now only treat URLs as normalized when their path actually ends with /_wave/robot/jsonrpc
April 4, 2026
Add admin analytics tab
The admin dashboard now surfaces wave, blip, user activity, and public-wave engagement metrics in a new analytics tab with live view tracking.
✦New
- Added an Analytics tab to /admin with wave totals, blip totals, login/activity windows, private/public partitions, top public waves, and top active users
- Live public-wave page/API views are now tracked since process start so the admin analytics view can rank public waves by actual observed traffic
April 3, 2026
Stop blocking startup on eager search warmup
Search startup no longer pre-builds the owner wave view by default, so deploys and reloads reach the app shell faster while real search requests keep the existing lazy rebuild path.
✓Fixed
- Moved startup search warmup behind a new search.startup_wave_view_warmup config flag that defaults to false
- Added focused Jakarta coverage for disabled startup warmup, owner normalization, fallback wave-map warmup, and non-fatal warmup failures
April 3, 2026
Extract durable architecture references
Moves long-lived architecture guidance into dedicated docs and surfaces those references from the README documentation map.
✦New
- Added links in the README.md Documentation map section for the Jakarta dual-source rules, runtime entrypoints and wiring, and dev persistence topology references
April 2, 2026
Reduce OT search write amplification on hot public waves
OT search now keeps low-latency updates for low-fanout searches while batching hot public-wave updates back to a polling-equivalent cadence with new ops visibility.
✓Fixed
- Batch hot public OT search edits per wave
- Keep low-fanout OT search behavior unchanged
- Expose batching counters in admin ops status
- Speed up affected-subscription lookup with a direct user-to-subscriptions index
- Add focused coverage for hot-wave and boundary scenarios
April 2, 2026
Enable OT search locally
Seeds the ot-search feature flag from server config so local runtime state stays aligned with the OT-search gate.
✓Fixed
- OT search now follows search.ot_search_enabled in the persistent feature-flag store
- Fresh local stores no longer advertise ot-search as enabled unless the server config turns it on
April 2, 2026
Add the gpt-bot example robot
Adds a copyable Java gpt-bot example with Wave callback endpoints, mention-driven replies, and safer default runtime settings.
✦New
- Added a standalone gpt-bot example server with capabilities.xml, profile, and callback handling
- Local Codex invocation now powers mention-driven replies in the example robot
- README.md and docs/gpt-bot.md document local run, Cloudflare tunnel, and robot registration steps
✓Fixed
- Codex runs stay sandboxed by default unless GPTBOT_CODEX_UNSAFE_BYPASS=true is set
- Mention handling now deduplicates overlapping blip events and fetches SupaWave context only for actionable mentions
- HTTP worker threads now use caller-runs backpressure, callback tokens are redacted from status output, callback auth no longer depends on public URL exposure, and invalid env values fall back to defaults with warnings
April 1, 2026
JWT Robot API Migration with Scope Enforcement
Robot and Data APIs now enforce per-operation JWT scopes, add a /robot/token endpoint alias, and provide migration support for legacy pre-scoped long-lived tokens.
✦New
- Added /robot/token endpoint as an alias for /robot/dataapi/token for improved API discoverability
- Implemented per-operation JWT scope enforcement for Data and Robot APIs
- Notify operations (robot.notify, robot.notifyCapabilitiesHash) now require only data:read scope instead of wave:robot:active
✓Fixed
- Fixed shared mutable state race condition in BaseApiServlet operations list handling
- Added migration fallback for legacy unscoped tokens that defaults scopes by token type
- Scope validation errors now return HTTP 403 instead of 401 to properly distinguish insufficient scope from invalid token
- Removed duplicate JWT validation in DataApiServlet and ActiveApiServlet
March 30, 2026
Security: Avatar XSS Fix
Escape avatar URL values in HTML attribute context to prevent stored XSS via attacker-controlled profile image URLs.
✓Fixed
- Escape avatar src attribute values in participant, blip author, and digest views to prevent stored XSS
March 30, 2026
Harden rendered URL sanitizer
Replaces the URI-based link/image URL sanitizer with a permissive scheme-extraction check that handles URLs with spaces, adds wave:// support, and rejects protocol-relative URLs.
✓Fixed
- URLs with unencoded spaces (e.g. search query strings) are no longer incorrectly dropped by the /render endpoint
- Internal wave:// and waveid:// links remain navigable in rendered HTML output
- Protocol-relative URLs (//host/path) are now rejected to prevent open-redirect attacks
March 30, 2026
Restore runtime static assets under root war
Ensures favicons, authentication CSS, logo, and images are served correctly after relocating runtime static assets.
✓Fixed
- Move auth CSS, favicons, logo, and image assets into war/static to match the runtime asset root.
March 30, 2026
Fix default logoUrl path after static asset restructure
Updates the default logoUrl client flag from static/images/logo.png to static/logo.png to prevent a 404 after the logo was moved out of the images/ subdirectory.
✓Fixed
- Corrected default logoUrl from static/images/logo.png to static/logo.png to match the new asset location and prevent runtime 404s.
March 30, 2026
Jakarta Session and WebSocket Auth Hardening
Jakarta session-token lookup now always resolves auth tokens, and invalid WebSocket auth closes with a policy-violation reason.
✓Fixed
- Enabled Jakarta session-token lookup for token-based auth without a feature flag
- Closed invalid-auth WebSocket messages with a policy-violation reason and aligned the server log message
March 29, 2026
Robot Control Room Upgrades
Robot owners can now manage richer robot metadata, pause unsafe automations, and keep secrets masked after the initial reveal.
✦New
- Added robot descriptions, created/updated timestamps, pause state, and owner-only dashboard actions for updating callback URLs, descriptions, and deleting robots
- Kept newly created or rotated secrets available for one-time copy while masking secret previews in the dashboard and starter prompt
- Added a dashboard Test Bot action that refreshes robot capabilities after a callback URL is configured
✓Fixed
- Blocked paused robots from receiving client-credentials API tokens or passive runtime updates
March 28, 2026
Tagged Wave Scroll Fix
The bottom of a wave now stays reachable above the tags bar.
✓Fixed
- Aligned the wave scroll offset with the full rendered tags-bar height so bottom content is no longer clipped behind it
March 28, 2026
Unread Search Read-State Fix
Unread-only searches now evaluate persisted read state so read waves stop leaking into unread results.
✓Fixed
- Unread-only searches now use wavelet read-state data instead of relying on the conversation-model supplement path
- Read waves no longer remain in `unread:true` results when every matching wave has already been opened
March 28, 2026
Shared Wave Pinning
Pinning and unpinning now works for waves you can access even when you are not the wave owner.
✓Fixed
- Fixed shared-wave pin and unpin requests so they update your per-user state without requiring wave ownership
March 28, 2026
Search Refresh Stability
Search refreshes no longer preserve stale sidebar rows when the latest result set is empty.
✓Fixed
- Fixed simple search refresh handling so empty result updates clear stale wave rows instead of leaving old unread/message counts behind
March 28, 2026
Search Panel Unread Accuracy
Opened waves now keep the correct unread state in the polling search panel.
✓Fixed
- Stopped opened search results from dropping back to a stale local read state before the polling digest catches up
- Kept higher live unread and message counts when the open wave has newer local state than the last search snapshot
March 28, 2026
Search Bootstrap Loading UX
The search panel now shows loading skeletons only for blank OT bootstrap loads and keeps direct-search results visible while OT overlay state times out cleanly.
✓Fixed
- The search panel now renders loading skeleton rows only when an OT-enabled bootstrap starts from an empty result set
- Refreshing search results keeps the current result rows visible instead of blanking the panel while the next direct-search request is in flight
- If OT overlay data never arrives, the client now clears that waiting state without blanking direct-search results or retrying OT reconnect forever
March 28, 2026
Save Status Reliability
Save warnings now clear more reliably after reconnects and wavelet resets.
✓Fixed
- Fixed a case where the saving indicator could stay yellow and keep showing the slow-save warning after a disconnected wavelet was replaced.
March 28, 2026
Save Recovery
Wave tabs recover cleanly after websocket reconnects instead of getting stuck in a long-saving state.
✓Fixed
- Reopened wave streams now accept the new websocket channel id instead of filtering against a stale one
- Active wave views now fail fast on websocket disconnect so the client reconnect flow can resubscribe and clear stuck saving indicators
March 28, 2026
Robot Registration Polish
The robot registration success page now keeps long credentials readable without breaking the layout.
✓Fixed
- Redesigned the Robot Registered page with a more polished SupaWave presentation
- Long consumer token secrets now wrap cleanly on desktop and narrow screens instead of overflowing the card
March 28, 2026
Robot Registration Flow
Robot registration now lets you create credentials first, add the callback URL later, and keeps `-bot` reserved for robots.
✦New
- Robot registration now lets you mint credentials first and add or update the callback URL later without rotating the secret
- Robot usernames must end with `-bot`, and regular user registrations now reject that reserved suffix
✓Fixed
- Pending robots can no longer use the `client_credentials` token flow until a callback URL is configured
March 28, 2026
Robot Dashboard Management
Added a new authenticated robot management dashboard to manage API tokens and callback URLs.
✦New
- Robot management dashboard where users can see robots they own, update callback URLs, and rotate compromised secrets
- Generated user and robot Data API JWTs from the new dashboard
- Consolidated user top bar menu with grouped sections for easier access to Account, Automation / APIs, Product / Support, and Legal surfaces
March 28, 2026
Robot Control Room Hardening
Robot owners can now rotate secrets safely from the control room, and legacy robot claims keep working after the onboarding redesign.
✦New
- Added secret rotation to the authenticated /account/robots control room without forcing a callback URL change
✓Fixed
- Robot registration now keeps its sign-in return path and requires an XSRF token before creating or claiming a robot
- Legacy ownerless robots can be claimed during re-registration even when their callback URL is unchanged
March 28, 2026
Reply Editor Stability
Deleting all text in a new reply no longer crashes the editor.
✓Fixed
- Flushed pending editor typing state before detaching a blip editor during reply transitions
March 28, 2026
Realtime Search Stability
Experimental OT-backed search now degrades more safely when the live snapshot cannot satisfy the requested result window, and the server only tracks live search state for active subscribers.
✓Fixed
- Expanded OT search result lists now fall back to polling instead of getting stuck behind a 50-result live snapshot
- Search wavelet bootstrap registration now skips inactive listeners and re-evaluates participant subscriptions conservatively to avoid stale live results
March 28, 2026
Public Wave Search Badges
Public wave search results no longer show stale fully unread badges after you open them.
✓Fixed
- Corrected the search panel so `with:@` public wave results keep read badges in sync when a public/shared wave opens without a user-data wavelet snapshot
March 28, 2026
Profile Last Seen
Profile cards now show last seen when a user allows it, and hide it when they turn it off.
✓Fixed
- Successful sign-in and ongoing authenticated activity now update the profile last-seen timestamp
- Turning off Show last seen removes the field from the profile card response
March 28, 2026
Rollout Flags Show Up Before First Enablement
Admin feature flags now list prepared rollout flags like lucene9 before they have been saved once.
✦New
- The admin feature flags screen now shows code-known rollout flags even when no stored flag record exists yet
- Stored rollout flag settings still override the default placeholder entry once a flag has been saved
March 28, 2026
Wavier Link References
Wave links now use a distinct SupaWave accent treatment before hover.
✓Fixed
- Changed blip links to a wavy ocean underline with a tinted accent shade so they stand apart from ordinary blue text
- Kept visited links clearly clickable with a separate lavender tint instead of blending back into body copy
March 28, 2026
Clearer Link Styling
Links in wave content now look clickable before hover.
✓Fixed
- Restored visible underlines for links inside wave content while keeping the existing Supawave link colors
March 28, 2026
Landing Page Accuracy
The landing page now reflects the product capabilities that are available today.
✓Fixed
- Removed the landing page federation claim until federation is actually supported
- Added landing page messaging for Robot registration and Data API token support
March 28, 2026
Inline Reply Refresh Stability
Inline replies now preserve their anchor more reliably when created from the current selection.
✓Fixed
- Stabilized inline reply anchor placement by flushing the active editor before reading the current selection
March 28, 2026
Direct Messages Stay Explicit
Two-person waves no longer turn into direct messages unless you start them from Send Message.
✓Fixed
- Ordinary waves with two participants now stay ordinary waves
- Only profile-card Send Message conversations are treated as locked direct messages
March 28, 2026
Deleted Blip Focus Recovery
Deleting a reply now preserves unread navigation by moving focus to the next surviving blip or its containing parent.
✓Fixed
- Fixed a crashy unread-navigation case when deleting a blip that still contained the currently focused descendant.
- When a deleted reply has no remaining siblings, focus now falls back to the containing parent blip instead of jumping unpredictably.
March 28, 2026
Archive Search Fix
Archived waves now leave the inbox immediately and show up in archived results.
✓Fixed
- Fixed inbox and archived search filters so per-user archive state is honored even for waves without a full manifest structure.
March 28, 2026
API Docs Discovery
API consumers and LLM agents can now discover the SupaWave docs from dedicated llms entrypoints and direct links across the product.
✦New
- Published `/llms.txt` and `/llms-full.txt` as LLM-friendly API docs entrypoints
- Added direct `/api-docs` links in the landing-page navigation bar, hero section, footer, and signed-in app menu
March 28, 2026
AI Robot Onboarding
Robot creation now starts from a SupaWave control room with an AI starter prompt, a one-hour Data API JWT, and clearer API docs for humans and LLMs.
✦New
- New authenticated /account/robots control room for creating pending robots and activating callback URLs later
- Ready Google AI Studio / Gemini starter prompt with standardized SUPAWAVE_* environment variable names
- Automatic one-hour Data API JWT generation inside the control room prompt
✦New
- Added a Robot & Data API entry point to the signed-in app menu
- Expanded /api-docs and /api/llm.txt with Build with AI guidance, minimal common operations, and clearer security/error notes
March 27, 2026
Unread-Only Search Filter
You can now narrow SupaWave search results to waves with unread blips only.
✦New
- Added the unread:true search filter for inbox, archive, pinned, and combined searches
✓Fixed
- Updated the search help popup with unread-only examples
March 27, 2026
Title and Content Search Filters
Search can now target wave titles or any blip content explicitly.
✦New
- Added title: search for root blip titles
- Added content: search across conversational blips
- Updated search help with clickable examples for the new filters
March 27, 2026
Smarter Empty Search Reset
Clearing the search box now returns you to the inbox instead of leaving the app in an empty-query state.
✓Fixed
- Empty or whitespace-only searches now normalize back to in:inbox
March 27, 2026
Search Bootstrap Recovery
The left search panel now restores itself reliably even when OT search is enabled.
✓Fixed
- Search now performs an immediate direct bootstrap before depending on OT updates
- Archive and inbox actions refresh search results correctly again
March 27, 2026
Restore Your Last Wave
SupaWave can reopen your last active wave after login or refresh and focus the next unread blip.
✦New
- When no URL fragment is present, SupaWave restores the last opened wave from local storage
- Wave opens now focus the last unread blip before falling back to the last blip
March 27, 2026
Release-Aware Upgrade Notes
Upgrade banners now map to the deployed release notes instead of always showing the newest changelog entry.
✓Fixed
- The upgrade banner now links to the exact deployed release notes when the release mapping is known
- Deploy metadata now keeps changelog release mapping aligned across upgrades and rollbacks
March 27, 2026
Realtime OT Search
SupaWave can subscribe to realtime search wavelets when the ot-search feature flag is enabled.
✦New
- Added OT-powered realtime search subscriptions behind the ot-search flag
March 27, 2026
OT Search Fallback
Experimental OT search now falls back automatically when realtime search data does not arrive in time.
✓Fixed
- The search panel no longer stays blank when OT search is enabled but no search-wavelet data arrives
March 27, 2026
Lucene 9 Search Rollout
A new Lucene 9 BM25 search backend is available behind the lucene9 feature flag.
✦New
- Added Lucene 9 full-text search with richer parsing and BM25 ranking behind the lucene9 flag
March 27, 2026
Live Unread Digests
Unread counts stay accurate during polling and OT-driven search refreshes.
✓Fixed
- Search refreshes now preserve live unread digest proxies instead of replacing them with stale snapshots
March 27, 2026
More Accurate Latest-Activity Sorting
Date-based sorting now reflects the most recent conversational activity across the whole wave.
✓Fixed
- Wave search ordering now uses the latest conversational wavelet activity instead of only the root wavelet
March 27, 2026
Inline Reply Stability
Inline replies no longer crash when SupaWave encounters a stale inline anchor reference.
✓Fixed
- Missing inline anchor references now fall back safely instead of throwing an Item not found client error
March 27, 2026
Implicit Content Search
Bare words in the search box now behave like content filters automatically.
✦New
- Plain search terms such as hello or meeting notes now search wave content without requiring content:
March 27, 2026
Changelog System
You can now review recent SupaWave releases from the app and see what changed after a deploy.
✦New
- Added the /changelog page with release history
- Added What's New links in the app menu and landing page
March 27, 2026
Built-In API Docs
SupaWave now ships human and LLM-friendly API documentation directly inside the app.
✦New
- Added /api-docs, /api/openapi.json, and /api/llm.txt for the Data and Robot APIs