Dannii
(Dannii)
November 5, 2026, 6:57am
1
When clicking from one post review to another from the notifications list, it shows review notes belonging to other reviews. Refreshing the page shows the correct notes.
My Discourse version: 2026.7.4.
2 Likes
pmusaraj
(Penar Musaraj)
November 5, 2026, 9:08pm
2
Can you reproducing on the latest version of Discourse or at least one from the last month or so?
Dannii
(Dannii)
November 5, 2026, 11:12pm
3
Iām on your LTS release, so Iām not bumping to a non-LTS release purely to test this.
This does feel like a new bug. Were there any review queue changes in 2026.7.4?
Dannii
(Dannii)
November 5, 2026, 11:33pm
4
This looks like it probably fixed the bug.
main ā reviewable-stale-state-between-items
Moving from one reviewable to another ā by clicking through the review queue lis⦠t in the user menu, for example ā is a change of model on the same route, so the whole `ReviewableItem` tree is re-rendered in place rather than torn down. Any state those components hold for a single reviewable therefore survives the transition.
`ReviewableTimeline` copied `reviewable_notes` into a tracked field in its constructor, which as a result only ever ran for the first reviewable rendered. A note added to one reviewable was then shown on every reviewable visited afterwards, and a reviewable's own note was missing whenever it wasn't the first one opened. Notes are now read from, and written to, the reviewable itself.
`ReviewableItem` kept `editing` and its pending `_updates` as well, so leaving a reviewable mid-edit landed on the next one still in edit mode, and saving from there wrote the previous reviewable's changes to it.
---
### Reproducing
1. Flag a few posts so the review queue has several pending items.
2. As a moderator, open the user menu, go to the review queue tab, and click one of the items.
3. Add a note.
4. Open the user menu again and click a different item.
The note from the first item is shown on the second. Doing it in the other order ā opening a reviewable with no notes first ā hides the note on the reviewable that actually owns it. The review queue list itself has always been correct, because it renders each reviewable in an `{{#each}}` and so gives each one its own components.
For the edit half, do the same with two queued posts and click "Edit Post" before navigating away.
### `ReviewableItem` becomes a glimmer component
`didUpdateAttrs` was the only place this state could be reset, and it is classic-component-only. The state belonging to a single reviewable now lives in a `@cached` `state` getter that reads `this.args.reviewable`, so a different reviewable produces a fresh `trackedObject` and nothing carries over ā the reset is a consequence of the argument changing rather than a hook that has to remember every field. The `@computed` getters become plain getters, `this.set` gives way to assignment, and `{{mut}}` on `claimed_by` gives way to a callback.
Plain getters only track what the model declares, so `Reviewable` now declares its server payload as `@tracked`, the way `Post` already does. `reviewable_notes` is an `@autoTrackedArray` seeded to `[]`, which is what lets the timeline append to it directly instead of bridging through `get`/`set` from `@ember/object`.
### Test coverage
None of this reproduces on a full page load, so `visit_reviewable` ā how every reviewable system spec navigates ā cannot exercise it. The review page object grows a `visit_reviewable_from_user_menu` that stays within the running app.
`shows correct IP when navigating between reviewables` now uses it. Worth flagging: that spec was added alongside the `didUpdateAttrs` fix it was written for, and because it navigated with `visit_reviewable` it had been passing either way ever since. It only becomes a real guard here.
The component tests swap a tracked `@reviewable` on a mounted component, which is the mechanism at fault. Each one kills a specific mutation ā removing the argument read from `state()`, or dropping `@autoTrackedArray` ā except `keeps a pending edit when the reviewable it belongs to is refreshed`, which is deliberately the other direction: `store.find` updates a record in place after every `perform`, so it guards against a cache key that invalidates too eagerly.
`removes a deleted note from the timeline` covers a path that had no test in either suite.
### Notes
`updating` and `disabled` are deliberately left outside `state`. They belong to an in-flight `perform()` request whose `.finally()` clears them ā resetting them on navigation would clear the flag out from under a request started on the new reviewable.
The bare `this.args.reviewable;` in `state()` is what ties the cache to a single reviewable; deleting it makes both reset tests fail. `no-unused-expressions` only runs on `.ts`/`.gts` here, so unlike the equivalent in `block-outlet-root-container.gts` nothing catches it, hence the comment.
Could it please be backported to 2026.7?