From e0dacfb5300470e6deb57752d840342790ba02ca Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 21 Aug 2026 17:13:57 +0200 Subject: [PATCH 1/3] docs(repo): track workarounds blocked on a Flutter release Adds FLUTTER_BLOCKED.md, a registry of workarounds that exist only because an upstream Flutter fix has not shipped, each with the release that makes it removable and how to verify that release actually carries the fix. Wires it into the two places it has to be read to be useful: a rule in STYLE_GUIDE.md pairing each row with a `TODO(flutter)` tag at the code site, and a sweep step in the flutter-version-bump skill's floor raise, which previously did not mention TODOs at all. Co-Authored-By: Claude Opus 5 (1M context) --- .claude/skills/flutter-version-bump/SKILL.md | 23 ++++++++ FLUTTER_BLOCKED.md | 56 ++++++++++++++++++++ STYLE_GUIDE.md | 7 +++ 3 files changed, 86 insertions(+) create mode 100644 FLUTTER_BLOCKED.md diff --git a/.claude/skills/flutter-version-bump/SKILL.md b/.claude/skills/flutter-version-bump/SKILL.md index e645e45709..8b7ede7d7c 100644 --- a/.claude/skills/flutter-version-bump/SKILL.md +++ b/.claude/skills/flutter-version-bump/SKILL.md @@ -534,6 +534,29 @@ Then, per `STYLE_GUIDE.md`, one short bullet under `๐Ÿ”„ Changed` in each of the Finish with `melos bootstrap` and commit the resulting root `pubspec.lock`. +### Sweep the workarounds the new floor unblocks + +Still in Track B, after the constraints move. `FLUTTER_BLOCKED.md` lists every workaround that +exists only because an upstream fix had not shipped; each row carries the release that makes it +removable. Read it, and cross-check the code so a stale file cannot hide a site: + +```bash +grep -rn 'TODO(flutter)' packages/ sample_app/ # every tagged site, even if the file drifted +``` + +For each row whose "Removable at" is now `<=` the new floor, confirm the fix really shipped +before deleting anything โ€” the recorded version is a prediction, and upstream fixes slip: + +```bash +# in a Flutter checkout, using the fix commit named in the row +git tag --contains | grep -vE '\-' | sort -V | head -1 +``` + +Then remove the workaround, its `TODO(flutter)`, any test that only pinned the workaround's +mechanism, and the row. If the fix slipped, re-date the row instead โ€” do not silently drop it. + +Keep this in the Track B commit: it is only correct because the floor moved. + ## Step 7 โ€” Changelog and PR Track A changes that are user-visible (a widget swapped, a deprecation migrated) get a CHANGELOG bullet in the diff --git a/FLUTTER_BLOCKED.md b/FLUTTER_BLOCKED.md new file mode 100644 index 0000000000..aeb37f1cad --- /dev/null +++ b/FLUTTER_BLOCKED.md @@ -0,0 +1,56 @@ +# Workarounds blocked on a Flutter release + +Every workaround in this repo that exists only because an upstream Flutter fix has not +shipped yet. Each one is removable the moment our published minimum Flutter includes the +fix, so this file is the checklist for the floor raise in the +[`flutter-version-bump`](.claude/skills/flutter-version-bump/SKILL.md) workflow. + +Code sites are tagged `// TODO(flutter): โ€ฆ`, so `grep -rn 'TODO(flutter)' packages/` finds +them all even if this file falls behind. Keep both in sync: add the tag *and* a row here. + +## Open + +| Removable at | What | Where | Upstream | +| --- | --- | --- | --- | +| Flutter 3.48 (expected) | `SelectionArea` keyed on `BrowserContextMenu.enabled` | `stream_chat_flutter` ยท `src/message_widget/components/stream_message_text.dart` | [#186459](https://github.com/flutter/flutter/issues/186459) โ†’ [#186553](https://github.com/flutter/flutter/pull/186553) | + +### `SelectionArea` keyed on `BrowserContextMenu.enabled` + +**Symptom without the workaround.** On desktop web, `Assertion failed: _selectable == null` +red-screens the message text as soon as the message list rebuilds after the browser +context menu is toggled โ€” in practice, opening a channel and then tapping the attachment +button. Reported as [#2906](https://github.com/GetStream/stream-chat-flutter/issues/2906). + +**Cause.** `SelectableRegion.build` conditionally wraps its subtree in +`PlatformSelectableRegionContextMenu` based on +`kIsWeb && BrowserContextMenu.enabled && `. Flipping that setting while a +`SelectionArea` is mounted re-inflates the inner `SelectionContainer` before the old one +unregisters. The regression arrived in stable **3.41.0** via +[#176855](https://github.com/flutter/flutter/pull/176855), which changed that condition +from the compile-time constant `kIsWeb` to the runtime-mutable `_webContextMenuEnabled`. + +**Workaround.** `key: ValueKey(BrowserContextMenu.enabled)` on the `SelectionArea`, so a +flip *replaces* the region โ€” fresh state, nothing registered โ€” instead of restructuring a +live one. + +**Upstream fix.** [#186553](https://github.com/flutter/flutter/pull/186553), merged +2026-08-07, adds a `GlobalKey` to the internal `SelectableRegionSelectionStatusScope` so +the selection subtree reparents instead of being recreated. Confirm the release with +`git tag --contains 2a469b880c19cbbec6aaa6329d4a4a9d1db22a4e` in a Flutter checkout โ€” +empty as of 3.47.1, so 3.48 is expected but **not** confirmed. + +**Why it is worth removing.** Upstream's fix preserves the selection subtree and any active +selection across a flip; the key forces a full replacement, so the workaround is marginally +worse than stock Flutter once the floor moves. Not urgent โ€” it only churns when the flag +flips, which is once per channel enter/exit on desktop web. + +**How to verify the removal.** Needs a `--platform chrome` lane, tracked in FLU-713, which +carries a working browser test for exactly this transition. Do not trust the VM test +(`'StreamMessageText keys the selection area to the browser context menu state'`) to prove +the framework fix works โ€” it passes even with the key hardcoded to a constant. + +**Do not remove** the reference-counted `_BrowserContextMenu` helper in +`src/context_menu/context_menu_region.dart` along with the key. That fixes SDK-side bugs +unrelated to the framework regression and is not blocked on any Flutter release. + +Tracked in FLU-710. diff --git a/STYLE_GUIDE.md b/STYLE_GUIDE.md index e1a6ab76d4..94f2c81f1a 100644 --- a/STYLE_GUIDE.md +++ b/STYLE_GUIDE.md @@ -1095,6 +1095,13 @@ Include an issue link when the deferred work is tracked; if the constraint is self-explanatory ("wait for backend enrichment", "wait for next major"), a link isn't required. +A workaround that only exists because an upstream Flutter fix has not shipped yet uses +the `// TODO(flutter):` tag, links the `flutter/flutter` issue, and gets a row in +[`FLUTTER_BLOCKED.md`](FLUTTER_BLOCKED.md). Both matter: the row carries the context that +does not fit in a comment, and the tag means `grep -rn 'TODO(flutter)'` still finds every +site if the file falls behind. The floor raise reads that file to decide what is now +removable. + ### Bare ignore directives are fine `// ignore: rule_name` directives do not require an explanatory comment in this repo. From 72e60e2b66172ce5c1e6d77bf107d0be3e7da61e Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 21 Aug 2026 17:16:22 +0200 Subject: [PATCH 2/3] docs(repo): spell out that FLUTTER_BLOCKED.md is one accumulating registry Reading it with a single entry left it ambiguous whether the file was per-workaround. States that it is the single registry for the monorepo, and adds an "Adding an entry" section covering the four questions a section has to answer and the fact that removed entries are deleted rather than archived. Co-Authored-By: Claude Opus 5 (1M context) --- FLUTTER_BLOCKED.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/FLUTTER_BLOCKED.md b/FLUTTER_BLOCKED.md index aeb37f1cad..9c20d42e9b 100644 --- a/FLUTTER_BLOCKED.md +++ b/FLUTTER_BLOCKED.md @@ -8,6 +8,18 @@ fix, so this file is the checklist for the floor raise in the Code sites are tagged `// TODO(flutter): โ€ฆ`, so `grep -rn 'TODO(flutter)' packages/` finds them all even if this file falls behind. Keep both in sync: add the tag *and* a row here. +This is the single registry for the whole monorepo โ€” every workaround lives here, not in a +file of its own. There is one entry today; that is the current count, not the format. + +## Adding an entry + +Append a row to the table and a `###` section below it, matching the shape of the existing +one. A section is worth writing only if it answers the four questions the person deleting +the workaround will have: what breaks without it, which upstream release fixes it, how to +confirm that release really carries the fix, and what must *not* be deleted alongside it. + +When a workaround is removed, delete its row and section โ€” git history is the archive. + ## Open | Removable at | What | Where | Upstream | From 4c44398da4f7e6fea9619d84b4ca7cfa2512787b Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 21 Aug 2026 17:19:46 +0200 Subject: [PATCH 3/3] docs(repo): cut FLUTTER_BLOCKED.md to labelled fields 68 lines and 559 words for one entry, most of it restating the cause and history already in FLU-710, PR #2909, and the code comment. Now 19 lines: fixed labelled fields per entry, so a human scans the bold labels and an agent can match them, with the H2 headings acting as the index instead of a table that duplicated every field below it. Co-Authored-By: Claude Opus 5 (1M context) --- FLUTTER_BLOCKED.md | 83 ++++++++++------------------------------------ STYLE_GUIDE.md | 10 +++--- 2 files changed, 21 insertions(+), 72 deletions(-) diff --git a/FLUTTER_BLOCKED.md b/FLUTTER_BLOCKED.md index 9c20d42e9b..901c9d7304 100644 --- a/FLUTTER_BLOCKED.md +++ b/FLUTTER_BLOCKED.md @@ -1,68 +1,19 @@ # Workarounds blocked on a Flutter release -Every workaround in this repo that exists only because an upstream Flutter fix has not -shipped yet. Each one is removable the moment our published minimum Flutter includes the -fix, so this file is the checklist for the floor raise in the -[`flutter-version-bump`](.claude/skills/flutter-version-bump/SKILL.md) workflow. - -Code sites are tagged `// TODO(flutter): โ€ฆ`, so `grep -rn 'TODO(flutter)' packages/` finds -them all even if this file falls behind. Keep both in sync: add the tag *and* a row here. - -This is the single registry for the whole monorepo โ€” every workaround lives here, not in a -file of its own. There is one entry today; that is the current count, not the format. - -## Adding an entry - -Append a row to the table and a `###` section below it, matching the shape of the existing -one. A section is worth writing only if it answers the four questions the person deleting -the workaround will have: what breaks without it, which upstream release fixes it, how to -confirm that release really carries the fix, and what must *not* be deleted alongside it. - -When a workaround is removed, delete its row and section โ€” git history is the archive. - -## Open - -| Removable at | What | Where | Upstream | -| --- | --- | --- | --- | -| Flutter 3.48 (expected) | `SelectionArea` keyed on `BrowserContextMenu.enabled` | `stream_chat_flutter` ยท `src/message_widget/components/stream_message_text.dart` | [#186459](https://github.com/flutter/flutter/issues/186459) โ†’ [#186553](https://github.com/flutter/flutter/pull/186553) | - -### `SelectionArea` keyed on `BrowserContextMenu.enabled` - -**Symptom without the workaround.** On desktop web, `Assertion failed: _selectable == null` -red-screens the message text as soon as the message list rebuilds after the browser -context menu is toggled โ€” in practice, opening a channel and then tapping the attachment -button. Reported as [#2906](https://github.com/GetStream/stream-chat-flutter/issues/2906). - -**Cause.** `SelectableRegion.build` conditionally wraps its subtree in -`PlatformSelectableRegionContextMenu` based on -`kIsWeb && BrowserContextMenu.enabled && `. Flipping that setting while a -`SelectionArea` is mounted re-inflates the inner `SelectionContainer` before the old one -unregisters. The regression arrived in stable **3.41.0** via -[#176855](https://github.com/flutter/flutter/pull/176855), which changed that condition -from the compile-time constant `kIsWeb` to the runtime-mutable `_webContextMenuEnabled`. - -**Workaround.** `key: ValueKey(BrowserContextMenu.enabled)` on the `SelectionArea`, so a -flip *replaces* the region โ€” fresh state, nothing registered โ€” instead of restructuring a -live one. - -**Upstream fix.** [#186553](https://github.com/flutter/flutter/pull/186553), merged -2026-08-07, adds a `GlobalKey` to the internal `SelectableRegionSelectionStatusScope` so -the selection subtree reparents instead of being recreated. Confirm the release with -`git tag --contains 2a469b880c19cbbec6aaa6329d4a4a9d1db22a4e` in a Flutter checkout โ€” -empty as of 3.47.1, so 3.48 is expected but **not** confirmed. - -**Why it is worth removing.** Upstream's fix preserves the selection subtree and any active -selection across a flip; the key forces a full replacement, so the workaround is marginally -worse than stock Flutter once the floor moves. Not urgent โ€” it only churns when the flag -flips, which is once per channel enter/exit on desktop web. - -**How to verify the removal.** Needs a `--platform chrome` lane, tracked in FLU-713, which -carries a working browser test for exactly this transition. Do not trust the VM test -(`'StreamMessageText keys the selection area to the browser context menu state'`) to prove -the framework fix works โ€” it passes even with the key hardcoded to a constant. - -**Do not remove** the reference-counted `_BrowserContextMenu` helper in -`src/context_menu/context_menu_region.dart` along with the key. That fixes SDK-side bugs -unrelated to the framework regression and is not blocked on any Flutter release. - -Tracked in FLU-710. +Workarounds that exist only because an upstream Flutter fix has not shipped. The +[`flutter-version-bump`](.claude/skills/flutter-version-bump/SKILL.md) floor raise reads +this to decide what is now removable. + +One registry for the whole monorepo: append an entry with the same fields, never add a +file. Tag the code site `// TODO(flutter):` too, so `grep -rn 'TODO(flutter)'` finds it if +this file drifts. Delete the entry when the workaround goes โ€” git history is the archive. + +## `SelectionArea` keyed on `BrowserContextMenu.enabled` + +- **Removable at:** Flutter 3.48 โ€” predicted, not confirmed +- **Site:** `packages/stream_chat_flutter/lib/src/message_widget/components/stream_message_text.dart` +- **Upstream:** [flutter#186459](https://github.com/flutter/flutter/issues/186459), fixed by [flutter#186553](https://github.com/flutter/flutter/pull/186553) (`2a469b88`) +- **Confirm the release:** `git tag --contains 2a469b88` in a Flutter checkout โ€” empty as of 3.47.1 +- **Verify removal:** needs the browser test in FLU-713; the VM test passes even with the key hardcoded +- **Keep:** `_BrowserContextMenu` in `context_menu_region.dart` โ€” reference counting, not Flutter-blocked +- **Tracked:** FLU-710, [#2906](https://github.com/GetStream/stream-chat-flutter/issues/2906) diff --git a/STYLE_GUIDE.md b/STYLE_GUIDE.md index 94f2c81f1a..2afa4e764c 100644 --- a/STYLE_GUIDE.md +++ b/STYLE_GUIDE.md @@ -1095,12 +1095,10 @@ Include an issue link when the deferred work is tracked; if the constraint is self-explanatory ("wait for backend enrichment", "wait for next major"), a link isn't required. -A workaround that only exists because an upstream Flutter fix has not shipped yet uses -the `// TODO(flutter):` tag, links the `flutter/flutter` issue, and gets a row in -[`FLUTTER_BLOCKED.md`](FLUTTER_BLOCKED.md). Both matter: the row carries the context that -does not fit in a comment, and the tag means `grep -rn 'TODO(flutter)'` still finds every -site if the file falls behind. The floor raise reads that file to decide what is now -removable. +A workaround that only exists because an upstream Flutter fix has not shipped yet uses the +`// TODO(flutter):` tag and gets an entry in +[`FLUTTER_BLOCKED.md`](FLUTTER_BLOCKED.md), which the floor raise reads. Both matter: the +entry holds what does not fit in a comment, the tag survives the file drifting. ### Bare ignore directives are fine