Merge 3.0.34 into 4.0 - #26
Closed
antobinary wants to merge 160 commits into
Closed
antobinary wants to merge 160 commits into
antobinary wants to merge 160 commits into
Conversation
When cameraSortingModes.showAudioOnlyOnFirstPage is enabled, audio-only tiles (camera-less users that have taken the floor) were displayed to moderators but not to attendees. Moderators are unpaginated by default (pagination.desktopPageSizes.moderator = 0) and add audio-only tiles via the non-paginated path; attendees are paginated (viewer = 5) and, when partitionPrivilegedStreams is false, ran the !partitionPrivilegedStreams branch of useVideoStreams, which paginated camera streams but never added audio-only tiles. Add audio-only tile handling to that branch, mirroring the partitioned and the non-paginated paths (reserve first-page slots, shift later pages, keep off-page local cameras rendered), so audio-only tiles surface on the first page for viewers too. Adds a Playwright regression test asserting audio-only tile parity between a moderator and an attendee. Refs: bigbluebutton#25242 Co-authored-by: Tainan Felipe <tainanfelipe214@gmail.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ion modes Addresses PR review feedback for bigbluebutton#25242: - Extract reserveAudioOnlyTiles() and use it from both the partitionPrivilegedStreams true and false branches, removing the duplicated (and previously divergent) audio-only reservation logic and the dead `|| []` after Array.prototype.slice. The two pagination modes now share a single implementation, parameterized by reservedCount (pinned + local cameras), so they can no longer drift apart. - Share the audio-only tile locator between the audio-only e2e tests via a new audioOnlyTilesLocator() helper in user/util.ts. - Correct the audioOnlyTileVisibleForAttendee comment so it no longer overstates coverage: the regression lives in the partitionPrivilegedStreams=false branch, which is config-only (no per-meeting/userdata override), so the test asserts the moderator/attendee parity invariant and exercises the regression when the server runs partitionPrivilegedStreams=false. Validated: audio-only e2e tests green under both partitionPrivilegedStreams=true (default, no regression) and =false (the fixed branch). Co-authored-by: Tainan Felipe <tainanfelipe214@gmail.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…verride Addresses review on bigbluebutton#25284. The audio-only-tile parity test passed on the default config regardless of the fix, since partitionPrivilegedStreams is config-only with no per-meeting/userdata override. Add a reusable `clientSettingsOverrides` option to page init that patches `window.meetingClientSettings` before the client reads it (addInitScript + accessor + deep-merge), and use it to force `partitionPrivilegedStreams: false` and `showAudioOnlyOnFirstPage: true` so the test deterministically reproduces the regression. Annotate the test with linkIssue(25242).
…-grid-audio-only-tiles-attendees # Conflicts: # bigbluebutton-html5/imports/ui/components/video-provider/hooks/index.ts
chore: BigBlueButton 3.0.32 iteration
The apps gallery drifted from the latest design proposal and broke at minimum sidebar width: the two fixed grid columns left tiles too narrow, so the NEW label overlapped the app icon and titles truncated, and the search input refused to shrink. Align the panel with the latest mocks and make it responsive: - restyle the NEW label as a small semibold pill anchored to the tile's top-left corner (RTL-aware) instead of rendering it inline in the card flow - use a rounded-square (8px) app icon container instead of a circle - update tile geometry to the spec (1rem border radius, 2rem 1rem padding, 1rem gaps) - let the grid adapt its column count (auto-fill, minmax 10rem) and allow the search input to shrink, fixing the min-width overflow - stretch the section separator to the full content width
…#25165) Backport of bigbluebutton#25165 (merged on v4.0.x-develop) to v3.0.x-develop, bringing the BlockNote shared-notes e2e coverage to the 3.0 line. Cherry-picked a9c0cd5 with two conflicts resolved: - core/elements.ts: added the BlockNote selectors block at the shared-notes export insertion point. - sharednotes/blocknote/util.ts (add/add): merged the new 4.0 helpers (startSharedNotesBlockNote, getBlockNoteReadOnlyLocator) with the existing 3.0 helpers already present from bigbluebutton#25225, keeping a single getBlockNoteEditorLocator. 3.0 adaptations in sharednotes.ts for element renames that only exist on 4.0: - sharedNotesSidebarButton to sharedNotes - messagesSidebarButton to chatButton - lock flow uses manageUsers + lockViewersButton (the 3.0 lock modal has no participant permissions tab) - make-presenter uses userListItem instead of usersListSidebarButton + moreOptionsUserItemButton The "Pin and unpin notes onto whiteboard" scenario is marked fixme on 3.0: a viewer whiteboard does not re-sync to the presenter presentation state after the presenter unpins shared notes (the canvas is not restored). That sync behavior only exists on 4.0.
…ton#25448) Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.24.9 to 7.29.6. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.29.6/packages/babel-core) --- updated-dependencies: - dependency-name: "@babel/core" dependency-version: 7.29.6 dependency-type: direct:development ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bump the pinned tomcat-embed override from 10.1.55 to 10.1.56.
Bump the managed Spring Boot version from 3.5.14 to 3.5.16.
bbb-apps-akka and bbb-fsesl-akka: - jackson 2.13.5 -> 2.18.9, aligned across the suite via dependencyOverrides - pin netty to 4.1.135.Final via dependencyOverrides (transitive) - logback 1.2.13 -> 1.5.38, with slf4j-api pinned to 2.0.17 - commons-lang3 3.12.0 -> 3.18.0 - postgresql 42.5.0 -> 42.7.13 (bbb-apps-akka only)
Updated versions of Netty, PostgreSQL, Spring Framework, Jackson BOM, Logback, and Spring Data BOM.
…165-to-v3.0 test: add BlockNote shared notes e2e tests (backport of bigbluebutton#25165)
Bumps [axios](https://github.com/axios/axios) from 1.16.0 to 1.18.0. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](axios/axios@v1.16.0...v1.18.0) --- updated-dependencies: - dependency-name: axios dependency-version: 1.18.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com>
A two-column grid needs room for two legible tiles. On a narrow sidebar the columns get too tight — the NEW label overlaps the app icon and titles truncate — so grid view is not usable there. Switch the gallery to the list view whenever the sidebar-content panel is narrower than the grid's minimum width, following the design team's narrow-width mock: - derive the panel width from the layout context and force the list view below the threshold, while preserving the user's grid/list preference so grid returns once the panel is widened again - keep the grid a fixed two columns, dropping the adaptive column count that still produced cramped tiles - disable the grid toggle while the fallback is active: it stays greyed and non-interactive (aria-disabled, so its tooltip still shows) with a tooltip explaining why — "Not enough space for grid view — expand the panel to use it"
…npm_and_yarn/bbb-export-annotations/axios-1.18.0 build(deps): bump axios from 1.16.0 to 1.18.0 in /bbb-export-annotations
…igbluebutton#25465) `bolder` is a relative keyword that is only valid on the font-weight *property*, not the @font-face *descriptor*. Browsers discard the invalid descriptor, so the SemiBold and SemiBoldItalic faces fell back to weight 400/normal and collided with the Regular faces. Register them at their correct absolute weight (600) instead. Fixes SonarQube css:S8775 (At-rule descriptor values should be valid). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
) Bumps [axios](https://github.com/axios/axios) from 1.16.1 to 1.18.0. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](axios/axios@v1.16.1...v1.18.0) --- updated-dependencies: - dependency-name: axios dependency-version: 1.18.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
See docs of the package used to create random numbers (https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/RandomStringUtils.html#randomNumeric(int)) Each pos. in the created numeric string can be any valid digit (0-9) The result can be: 00000, 00001,..., 99999
When navigating between slides, the presentation area flashed white until the new slide's background SVG loaded. The root cause was in the [curPageId] effect in whiteboard/component.jsx, which ran the visible page swap (cleanupStore + updateStore(bgShape) + setCurrentPage) synchronously, mounting the new slide's tldraw background image-shape before its SVG had been fetched/decoded. CSS does not paint a background-image whose resource has not loaded, so the area went blank until the asset arrived. Defer the swap behind an Image.decode() gate (bounded by a 1500ms timeout) so the previous slide stays painted until the new one is paintable. The decode reuses the HTTP cache the forward prefetch already warms (Auth.authenticateURL is idempotent, so the same URL hits the same cache entry). A broken/401/malformed asset swallows the decode rejection and degrades to applying the swap anyway - never worse than before. Rapid navigation is protected by a staleness/unmount guard: a late decode for an abandoned page cannot clobber the current one. The getEditingShape().complete() guard from issue 25332 now runs at swap moment, adjacent to the mutation it protects, preserving the crash fix. Two e2e specs added: no-blank-on-slide-change (RED before the fix, GREEN after) and broken-asset-degrades-without-hanging. Closes bigbluebutton#25397 Co-authored-by: Tainan Felipe <tainanfelipe214@gmail.com>
- Drop the !isMountedRef.current guard from applyPageSwap. isMountedRef is not a mount flag: it is set true only late, inside adjustCameraOnMount after camera calibration, and stays false if calibration rethrows on an invalid viewbox. Gating the swap on it permanently stranded a viewer whose slide changed during the calibration window (the effect never re-fires for that curPageId). The cancelled flag already covers unmount fully; also re-check tlEditorRef, which can be torn down between the decode starting and resolving. - Add a .catch to the deferred swap so a throw in the store mutation is logged instead of escaping as an unhandled rejection (synchronously it reached ErrorBoundaryWithReload; inside the async .then there is no boundary). - Clear the fallback timer on the decode-wins path (.finally) and release the detached decode Image on cleanup so rapid navigation does not pin in-flight SVGs. - Make the decode timeout configurable via meetingClientSettings.public.whiteboard.slideSwapDecodeTimeoutMs (default 1500). - Correct the comments: the no-asset branch is the missing-svgUrl case (not infinite whiteboard), and pin the assets/curPageId lock-step invariant. Known limitation: on latency above the timeout the fallback fires before the asset is paintable and the original flash still shows.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The mobile early-return branch of useVideoStreams omitted overflowUsers, making its inferred type GridItem[] | undefined and failing tsc where VideoProviderProps requires GridItem[].
The html5 .eslintrc.js declares env: { es6: true }, which does not define
the globalThis global (it lands with es2020), and no-undef is only turned
off in the .ts/.tsx override. The same call in video-list/component.tsx
lints clean for that reason, this .jsx one did not.
Kept globalThis instead of switching back to window: SonarCloud flagged
the opposite on this line (Prefer globalThis over window).
…opyright-25431 refactor: remove deprecated copyright create parameter
The webVoice create parameter has been dead weight: its value was only ever copied into UserSession.webvoiceconf (write-only, never read) and used as a voice-conference collision key that duplicated the telVoice check. When absent it silently fell back to telVoice, so it never selected a distinct voice conference in practice. Remove webVoice end to end: - ApiParams.WEB_VOICE constant and its copy-param entry - ParamsProcessorUtil read + telVoice fallback + Meeting.withWebVoice() - Meeting.webVoice field, getter and builder support - MeetingService.getNotEndedMeetingWithWebVoice and its create collision use - UserSession.webvoiceconf write-only field and getter - ApiController existingWebVoice collision branch and webvoiceconf writes - orphan api2 VoiceProp2/Meeting2 (Meeting2 was never constructed) Voice conference selection is unchanged: it continues to derive solely from voiceBridge via telVoice, and the nonUniqueVoiceBridge collision check remains intact through telVoice. Closes bigbluebutton#25460 Co-authored-by: Tainan Felipe <tainanfelipe214@gmail.com>
Cover the two behaviours the webVoice removal must preserve, against a live server: - a create that still passes a webVoice distinct from voiceBridge succeeds and ignores webVoice, so the returned voiceBridge (voiceConf) equals the requested voiceBridge (backward compatibility); - two live meetings sharing a voiceBridge are still rejected with nonUniqueVoiceBridge (the collision check runs through telVoice, which is untouched). The BBB API is XML based, so the helper requests application/xml explicitly. Co-authored-by: Tainan Felipe <tainanfelipe214@gmail.com>
Document the create parameter removal alongside the other 4.0 removed parameters in the API reference, following the existing entry style. Co-authored-by: Tainan Felipe <tainanfelipe214@gmail.com>
The webVoiceConf join parameter set a custom Asterisk voice extension for the old Flash client. It has no code path reading it anywhere in the codebase, and Asterisk was replaced by FreeSWITCH, so the parameter has been inert for a long time. Remove its entry from the join API reference and record the removal in the 4.0 changelog alongside the create-side webVoice removal. Co-authored-by: Tainan Felipe <tainanfelipe214@gmail.com>
chore: BigBlueButton 3.0.33 iteration
…ue/275 fix(data-consumption): prevent hook usage counter from going negative
…oice-param-4.0 Remove obsolete webVoice create parameter
feat(mobile): mobile layout overhaul — paginated webcams, compact bars and responsive dialogs
…bluebutton#25571) * docs: iteration doc sync for 3.0.33 (bigbluebutton#25568) * docs: iteration doc sync for 3.0.33 Covers documentation-worthy changes on v3.0.x-release since the v3.0.32 tag that were not yet reflected in the docs: - new-features.md: Panopto external video player; the new public.whiteboard.slideSwapDecodeTimeoutMs client setting; corrected the html5PluginSdkVersion line to 0.0.104; tagged maxSharedNotesInitialContentUrlPayloadSize with 3.0.33 and added numPresentationDownloadThreads to the bbb-web properties ledger. - create.tsx: "(added 3.0.33)" on sharedNotesInitialContentMarkdown and sharedNotesInitialContentMarkdownUrl. - api.md: the three shared-notes initial-content create parameters added to the "Updated in 3.0" list, tagged with the point release that shipped them. - plugins.md: useTimer hook (plugin SDK 0.0.104) and the new senderRole field on useLoadedChatMessages. - faq.md: external video line now mentions Panopto.
…speed (bigbluebutton#25546) * fix(whiteboard): prevent stale self-updates from dropping frame name characters * compare versions before preserving frame names * fix frame rename
Every module pins sbt.version=1.10.7; the setup steps still said 1.6.2.
…-to-talk chore: Enable push-to-talk feature in settings.yml
Mechanical merge of the 3.0 line (86 commits since the 3.0.33 merge base) into
4.0. 21 files conflicted; resolutions:
Version/build markers — kept 4.0
bigbluebutton-config/bigbluebutton-release, .gitlab-ci.yml,
bigbluebutton.properties + package.json (html5PluginSdkVersion: 4.0 is on the
0.1.x SDK line, 3.0 on 0.0.x), akka Dependencies.scala (comment-only reword).
Removed in 4.0
bbb-recording-imex/pom.xml — the module is gone from 4.0; the 3.0 side was
only a dependabot logback bump, so it stays deleted.
Kept both sides
VoiceApp.scala — 3.0's orphaned-voice-join fencing plus 4.0's userRole lookup,
which the merged handleUserJoinedVoiceConfEvtMsg call still consumes.
ApiController.groovy — 4.0's XML/URL validation gate
(processValidatedRequestXmlModules) on top of 3.0's async upload pipeline
(resolvePresentationFilename + submitPresentationTask + the
"invalid_filename" insertDocument result).
external-video component — took 3.0's autoplay-mute recovery wholesale; 4.0
had none of it.
docs/plugins.md — 4.0's Apps Gallery badge and 3.0's configurable button
styles are independent additions at the same spot.
Lockfiles regenerated rather than hand-merged
Started from 4.0's lock and reconciled against the merged manifests, then
restored the 3.0 security bumps that taking 4.0's lock wholesale would have
dropped: fast-uri (html5) and body-parser, fast-uri, js-yaml, nanoid, postcss,
shell-quote (docs). Both locks are now >= each side on every package, and
neither package.json was modified.
Locales (it_IT, ja, zh_TW) — per-key three-way merge
4.0 seeds new keys with the English source string while 3.0 carries the real
translations; 22 keys per file were English placeholders and took the 3.0
translation.
Tests/docs — kept 4.0
playwright sharednotes helpers (4.0 is a superset; the differences are 4.0
selector renames), the 3.0-only test.fixme whose own comment says 4.0 passes,
and new-features.md structural hunks (4.0's file is a curated rewrite and the
3.0 topics are covered elsewhere in the 4.0 docs tree). Ported the two 3.0.33
feature sections that 4.0 documents nowhere, with the defaults corrected —
4.0 ships the Markdown toggles enabled.
Two 3.0 changes do not compile against 4.0 and are handled in follow-up
commits: the camera-dock grid snapping references LAYOUT_TYPE.CUSTOM_LAYOUT,
and the use-timer plugin hook needs SDK APIs the 0.1.x line does not have.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3.0's "grid-aware resize snapping in camera dock" (919ff6a) snaps the camera dock to the grid in the two layouts where the cameras are laid out as a grid: const snapToCameraGrid = selectedLayout === LAYOUT_TYPE.CUSTOM_LAYOUT || selectedLayout === LAYOUT_TYPE.UNIFIED_LAYOUT; 4.0 removed CUSTOM_LAYOUT from LAYOUT_TYPE (layout/enums.js now has UNIFIED_LAYOUT plus the CAMERAS_ONLY / PRESENTATION_ONLY / PARTICIPANTS_AND_CHAT_ONLY / PLUGINS_ONLY / MEDIA_ONLY hybrids), so the merged file referenced a member that no longer exists and failed tsc with TS2339. Dropping the CUSTOM_LAYOUT arm keeps the intent: of the layouts 4.0 still has, UNIFIED_LAYOUT is the grid one. This was the only CUSTOM_LAYOUT reference left in the client. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3.0's plugin-sdk-use-timer work (bigbluebutton#25335, commits bc4e822 / 172d67d / 388960c) adds a TIMER data-consumption hook and pairs it with plugin SDK 0.0.104. It merges cleanly on file level but does not compile on 4.0, because it depends on SDK APIs that exist only on the 0.0.x line: - `TimerData` is not exported (TS2694) - `DataConsumptionHooks.TIMER` does not exist (TS2339) - `useTimer(...)` takes a boolean, not `{ enableNotifications, isIndicator }` (TS2345) 4.0 tracks the 0.1.x SDK line (html5PluginSdkVersion=0.1.24). I checked the published SDK: neither 0.1.24 nor 0.1.25, the latest on that line, declares TIMER in DataConsumptionHooks — the enum ends at CUSTOM_QUERY. So there is no 0.1.x version to bump to that would make this compile, and bumping to 0.0.104 would drag 4.0's plugin API back to the 3.0 line. Removing the client half here rather than leaving 4.0 unbuildable. The feature needs a real port once the 0.1.x SDK exposes the timer types, which is an SDK change, not a merge conflict resolution. Nothing else references the hook: the two files were added whole by the merge and the only other reference was the registration in data-consumption/manager.tsx, removed here too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…right suite Both files auto-merged without a conflict but ended up with duplicate top-level declarations, which is a hard SyntaxError for the playwright loader. The whole suite collected 0 tests in 0 files — every spec failed to import, not just the shared-notes ones. sharednotes/blocknote/util.ts 3.0's backport of bigbluebutton#25165 appended its own startSharedNotesBlockNote and getBlockNoteReadOnlyLocator to a file that already defined both. The 3.0 copies open the panel via e.sharedNotes, which does not exist in 4.0's elements.ts at all (renamed to sharedNotesSidebarButton), so they were dead on arrival as well as duplicated. Removed them; the 4.0 definitions at the top of the file already do the same job with the right selectors. whiteboard/whiteboard.spec.ts `import { linkIssue } from '../core/helpers';` landed twice, once from each side, at different points in the import block. This is the same failure mode as 5840bc2 ("restore BlockNote shared-notes helpers dropped in 3.0->4.0 merge") from the previous 3.0 merge: git resolves these files happily because the additions are textually far apart, and nothing in the merge output hints that the result does not parse. Not fixed here, because they predate this merge and are identical on v4.0.x-develop (verified by running tsc against a worktree at 5fa28d6): e.userListToggleBtn in accessibility/document-title.spec.ts, ELEMENT_WAIT_EXTRA_LONG_TIME in presentation/presentation.ts, and e.sharedNotes in sharednotes/blocknote/blocknote.ts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two things the merge pulled forward that should not cross a version line. Translated locales are not merged. bigbluebutton-html5/public/locales/* comes straight from Transifex via the integration, so carrying 3.0's translation sync (ac21de5) into 4.0 fights that integration and would be overwritten by the next pull anyway. Reverted it_IT.json, ja.json and zh_TW.json to their 4.0 state; they are now byte-identical to 5fa28d6. en.json is the exception -- it is a source file, not a Transifex artifact, and its changes DO get merged. Nothing to do here: 3.0 made no en.json change in this range, and the merge left the file untouched. new-features.md is scoped to what is new in its own major/minor. The two 3.0.33 feature sections I ported (BlockNote Markdown import/export, Panopto external video) were new in 3.0, so they do not belong in "what's new in BBB 4.0" even though both features exist here. Same for the two `... added in BBB 3.0.33` property lines that auto-merged into the bbb-web "Added" list. Removed all four; the file is back to 4.0's curated content. Note the `#### Added` list already carried `added in BBB 3.0.27` and `added in BBB 3.0.30` entries before this merge. Those predate it, so I left them alone rather than widening the diff, but they look like the same carry-over and are worth a separate cleanup. Kept deliberately: the customize.md rows for maxSharedNotesInitialContentUrlPayloadSize and numPresentationDownloadThreads. That file is a reference of settings a 4.0 admin can actually set, not a per-version changelog, and both properties are live in the merged bigbluebutton.properties. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
🚨 Automated tests failed |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merging BBB 3.0.34 into v4.0.x-develop — report
Branch
antobinary/merge-3.0.34-into-4.0→ https://github.com/antobinary/bigbluebutton/pull/new/merge-3.0.34-into-4.0Scope: 86 commits / 15 PRs since the 3.0.33 merge base, 92 files. 68 of those 92 files had also been changed on 4.0 — 74% genuine divergence.
The 21 conflicts
bigbluebutton-release4.0.0-beta.5-wip).gitlab-ci.ymlakka-*/Dependencies.scalabbb-recording-imex/pom.xmlbigbluebutton.propertieshtml5PluginSdkVersion0.1.24 (4.0 is on the 0.1.x SDK line, 3.0 on 0.0.x)VoiceApp.scalauserRolelookup, which the merged call still consumesApiController.groovypackage.json+ lockit_IT/ja/zh_TW.ts/.spec.tsdocs/package-lock.jsonLockfiles. Taking either side wholesale was wrong in both directions: 4.0's lock silently dropped seven 3.0 security bumps, while 3.0's lock regressed 287 docs packages. I started from 4.0's and
npm update'd only the affected transitive packages —fast-uri(html5) andbody-parser,fast-uri,js-yaml,nanoid,postcss,shell-quote(docs). Both locks are now >= each side on every package, and neitherpackage.jsonwas modified.Locales. The obvious rule ("changed on both sides → keep 4.0") was wrong: 22 keys per file were keys new in both, where 4.0 seeds the English source string and 3.0 carries the real translation. Using
en.jsonas the reference, those 22 took the translation.Docs.
new-features.mdis a curated 4.0 rewrite (335 lines vs 3.0's 589), and the 3.0 topics are already covered elsewhere in the 4.0 tree — so 4.0 for the structural hunks. But I ported the two 3.0.33 feature sections (BlockNote Markdown import/export, Panopto) that 4.0 documents nowhere, with the defaults corrected: 3.0 says "disabled by default", 4.0 ships both togglestrue.Re-implementations (separate commits)
CUSTOM_LAYOUT— 3.0's grid-snapping referencesLAYOUT_TYPE.CUSTOM_LAYOUT, which 4.0 deleted. Kept the intent: of the layouts 4.0 still has,UNIFIED_LAYOUTis the grid one.use-timer plugin hook — merges textually but needs
TimerDataandDataConsumptionHooks.TIMER, which exist only on the 0.0.x SDK line. I checked the published SDK: neither 0.1.24 nor 0.1.25 (latest 0.1.x) declaresTIMER. There is no version to bump to, and 0.0.104 would drag 4.0's plugin API back to 3.0. Removed the client half rather than leave 4.0 unbuildable — it needs a real SDK-side port.Build from source — everything compiles
VoiceApp.scalaApiController.groovyb7b77b67fe9b485f5967tsc --noEmitI installed sbt in the container and published the SNAPSHOT artifacts via
publishM2(gradle usesmavenLocal();publishLocalwrites to Ivy, which is why the first attempt failed). Pre-merge client backup kept at/root/html5-client.backup-merge3034.Playwright: 315 passed / 29 failed / 17 skipped — 0 regressions
I ran the project's own CI gate (
--grep @ci --grep-invert '@flaky|@need-update|...'), then built the exact pre-merge client (e1e3f4d8...) in a worktree, deployed it, and re-ran the failing areas to attribute every failure:Two caveats on the numbers: an early run showed 23/24 shared-notes failures purely because
bbb-shared-notes-serverwas inactive (started it → 24/24). And this container's.envsecret is stale — the live one isYvuMwv...; I overrode it per-run rather than edit the file.The thing that actually worries me
The conflict list was not the risk surface. All 21 conflicts were resolvable and none of them broke anything. What broke the build were two files git merged cleanly:
sharednotes/blocknote/util.ts— 3.0's backport appended its ownstartSharedNotesBlockNoteandgetBlockNoteReadOnlyLocatorto a file that already had both. The 3.0 copies calle.sharedNotes, which doesn't exist in 4.0'selements.tsat all.whiteboard.spec.ts—import { linkIssue }landed twice.Duplicate top-level declarations are a hard
SyntaxErrorfor the Playwright loader: the entire suite collected 0 tests in 0 files. Not "the shared-notes tests failed" — every spec failed to import. Nothing in git's output hinted at it, because the additions were textually far apart.This is the same failure mode as
5840bc2984("restore BlockNote shared-notes helpers dropped in 3.0->4.0 merge") from the previous 3.0 merge. It has now happened twice.Merge vs. replay — recommendation
Keep merging, but change what happens after the merge.
The decisive argument is ancestry. A merge advances the merge base, so the next 3.0→4.0 merge only considers genuinely new commits. Replaying doesn't record that: git would re-present the same 86 commits forever, and the usual escape hatch (
git cherry/ patch-id) breaks precisely on the commits you had to adapt — the adaptation changes the patch-id, so the tool stops recognising them as applied. Given 74% file-level divergence, you'd be adapting a lot, so you'd be poisoning the detector on exactly the commits that matter. That cost compounds every release; the merge's cost does not.Replay's real advantage is reviewability, and you can have that without giving up ancestry — which is what I did here: mechanical resolution in the merge commit, every judgement call in its own follow-up commit with the reasoning in the message. A reviewer can read three small commits instead of auditing a 57-file merge blob.
Replay only wins where a 3.0 PR shouldn't exist on 4.0 at all. The use-timer hook is the case in point: replay would simply skip it, whereas merging forced me to merge-then-revert. That's one PR out of fifteen — not enough to change the strategy, and the revert commit documents the decision better than a silent omission would.
So, two concrete process changes:
1. A post-merge gate, because the conflict list lies. Cheapest first:
npx playwright test --listtakes seconds and would have caught both fatal duplicates immediately. Thentsc --noEmiton the client and the playwright project (that's what caughtCUSTOM_LAYOUTand the SDK mismatch),sbt compile+gradle compileGroovy, and a lockfile diff against both parents. None of these are in CI for merge commits today, as far as I can tell.2. Fix the upstream cause of the duplicates. Both incidents come from the same pattern: a 4.0-originated test file backported to 3.0 with renamed selectors, then merged forward. Either keep backports symbol-identical to the 4.0 original, or put 3.0-specific adaptations in a 3.0-only file that has no forward-merge counterpart. Otherwise this recurs every release, and it's a silent, total test-suite outage each time.
One caveat worth flagging rather than burying:
new-features.mdis release-line-specific prose, and I made editorial calls a mechanical merge can't justify. That file deserves a docs-team pass before this merges.