You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Verified at origin/main (5777b03): every claim below was re-read in source by triage — the gap is live at main, not superseded.
A moderator promotes a çaylak from the divan; the server commits correctly and atomically. Pasaport.promoteToYazar (apps/web/worker/features/pasaport/Pasaport.ts) runs one D1 batch whose first statement is the guarded tier flip (the tier = 'çaylak' guard makes a repeat call return promoted: false) followed by sandbox sweeps over the definition/post/comment tables. The reviewer's screen never learns:
ReviewerActions.onPromote (apps/web/src/components/divan/CaylakDetail.tsx) awaits fate.mutations.user.promote(...), maps the outcome to a status string, and touches no read state — neither divan.roster nor the open divan.backlog.
Both lists are plain one-shot reads: DivanRoster.tsx issues useRequest for the gated divan.roster root, CaylakDetail.tsx for divan.backlog. The divan has no /fate/live topic by design — apps/web/worker/features/divan/queries.ts says so itself: "No flag, no /fate/live topic: a plain one-shot query."
The shared post-promote publish publishPromotion (apps/web/worker/features/pasaport/promote-live.ts) invalidates pano feed/thread/post/comment topics, sozluk definition topics, and the user entity only — nothing repairs either divan connection.
So the promoted person stays visible in the roster, their items stay pending, and a second press reports "kullanıcı zaten yazar." against stale state — reproduced on production today. Server truth is consistent (a fresh session shows the account gone from the roster), so this is purely client-side staleness. Triage note: the profile-page path has the same shape — PromotionActions.onPromote (apps/web/src/components/profile/PromotionActions.tsx) also sets only a message, and UserProfilePage.tsx reads the profile through cached useRequest rather than a live view — so its displayed çaylak/yazar status can go stale the same way.
Triage note: verified against react-fate 1.3.1 typings — useRequest returns a plain root map and its options carry only a fetch mode; there is no refetch handle. The original filer's guess stands: refreshing two private roots after a mutation may need a small fate-side expire/refetch primitive, or the handler can drive a fresh request itself. The mechanism is builder latitude; the observable outcome below is the contract.
Why this is standalone work
Searched the board for an owner of the post-promote client-refresh surface ("divan live subscription refetch stale cache after mutation", "promote yazar UI refresh moderator review") — no open issue or epic matches. Nearest neighbours do not own it: #1856 (moderation live pressure / divan surge badge) is an optional live-topic enhancement, not this staleness bug; #1206 (promotion feature) and #1841 (profile-button visibility) are closed. Minted standalone.
What done looks like
After a successful promote from the divan reviewer actions, the promoted çaylak disappears from the rendered roster and their pending items disappear from the rendered backlog without a page reload.
A second press after a visible success no longer reports "kullanıcı zaten yazar." for an entry stale state kept on screen.
The profile-page promote handler receives the same post-success refresh treatment.
Denied and errored attempts leave the current rendering untouched.
New coverage pins the refresh behavior for both call sites; existing suites stay green.
Acceptance criteria
A successful user.promote issued from ReviewerActions.onPromote refreshes both divan.roster and the open author's divan.backlog, so the promoted account and its pending items leave the screen without a full page reload.
With the fix, pressing promote a second time cannot report "kullanıcı zaten yazar." while a stale roster/backlog entry for that user is still rendered.
The profile-page promote path (apps/web/src/components/profile/PromotionActions.tsx) applies an equivalent post-success refresh mirroring the divan handler.
Promote attempts ending in UNAUTHORIZED, FORBIDDEN, or a transport error change neither list's rendered contents.
Unit tests cover the post-success refresh behavior for both call sites, and the existing divan/profile suites pass unchanged.
Original report (verbatim)
Summary
Promoting a çaylak from the divan commits correctly (tier flip + sandbox sweep in one atomic batch), but the reviewer's screen keeps showing the promoted person in divan.roster and their items in divan.backlog until the fate cache is rebuilt. The second press of "yazar yap" then reports "kullanıcı zaten yazar." against a list that should no longer contain them. Reproduced on production today.
What I was doing
Reviewing a çaylak on phoenix.kamp.us/divan as a moderator: pressed the promote button, got the success toast, then re-checked the roster after a page refresh.
What I observed
The mutation response is correct: user.promote returns {promoted: true} and the D1 batch (buildPromotionStatements) flips user.tier AND clears sandboxed_at on all three content tables atomically.
After the toast, the roster row and backlog item remain visible. Nothing in the client refetches either list: onPromote in CaylakDetail.tsx calls fate.mutations.user.promote(...) and only sets a status message.
These lists are not live-subscribed (plain useRequest + useListView, no /fate/live topic for divan roots), and publishPromotion publishes invalidations for pano/sozluk topics plus the user entity — nothing repairs the divan connections.
Verified server truth separately: a fresh logged-in session loading /divan shows the promoted account gone from the roster, so the stored state is consistent — this is purely client-side staleness.
Why it matters
A moderator who promotes one person sees a stale queue and reasonably presses promote again; the confusing "zaten yazar" message erodes trust in the review loop. Might also mask real regressions: a mod cannot tell "stale UI" from "promotion failed".
Pointers
apps/web/src/components/divan/CaylakDetail.tsx — ReviewerActions.onPromote (no refetch after success)
apps/web/src/components/divan/DivanRoster.tsx — roster read via useRequest({"divan.roster": ...})
apps/web/worker/features/divan/lists.ts — gated list resolvers, no live topic
apps/web/worker/features/pasaport/promote-live.ts — post-promote publish covers pano/sozluk/user only
After a successful user.promote, refetch/expire the two private roots (divan.roster, and the open divan.backlog) in the same handler; mirror it in the profile-page promote path. A guess: fate's request cache may need an explicit expire/refetch primitive since useRequest exposes no refetch handle.
Filed by an agent · session 01a02c89-7810-7c22-8280-3c8a1feb8191 · branch umut/m50-diataxis-active · 2026-08-23T06:25:17Z
Verified at
origin/main(5777b03): every claim below was re-read in source by triage — the gap is live at main, not superseded.A moderator promotes a çaylak from the divan; the server commits correctly and atomically.
Pasaport.promoteToYazar(apps/web/worker/features/pasaport/Pasaport.ts) runs one D1 batch whose first statement is the guarded tier flip (thetier = 'çaylak'guard makes a repeat call returnpromoted: false) followed by sandbox sweeps over the definition/post/comment tables. The reviewer's screen never learns:ReviewerActions.onPromote(apps/web/src/components/divan/CaylakDetail.tsx) awaitsfate.mutations.user.promote(...), maps the outcome to a status string, and touches no read state — neitherdivan.rosternor the opendivan.backlog.DivanRoster.tsxissuesuseRequestfor the gateddivan.rosterroot,CaylakDetail.tsxfordivan.backlog. The divan has no/fate/livetopic by design —apps/web/worker/features/divan/queries.tssays so itself: "No flag, no/fate/livetopic: a plain one-shot query."publishPromotion(apps/web/worker/features/pasaport/promote-live.ts) invalidates pano feed/thread/post/comment topics, sozluk definition topics, and the user entity only — nothing repairs either divan connection.So the promoted person stays visible in the roster, their items stay pending, and a second press reports "kullanıcı zaten yazar." against stale state — reproduced on production today. Server truth is consistent (a fresh session shows the account gone from the roster), so this is purely client-side staleness. Triage note: the profile-page path has the same shape —
PromotionActions.onPromote(apps/web/src/components/profile/PromotionActions.tsx) also sets only a message, andUserProfilePage.tsxreads the profile through cacheduseRequestrather than a live view — so its displayed çaylak/yazar status can go stale the same way.Triage note: verified against react-fate 1.3.1 typings —
useRequestreturns a plain root map and its options carry only a fetchmode; there is no refetch handle. The original filer's guess stands: refreshing two private roots after a mutation may need a small fate-side expire/refetch primitive, or the handler can drive a fresh request itself. The mechanism is builder latitude; the observable outcome below is the contract.Why this is standalone work
Searched the board for an owner of the post-promote client-refresh surface ("divan live subscription refetch stale cache after mutation", "promote yazar UI refresh moderator review") — no open issue or epic matches. Nearest neighbours do not own it: #1856 (moderation live pressure / divan surge badge) is an optional live-topic enhancement, not this staleness bug; #1206 (promotion feature) and #1841 (profile-button visibility) are closed. Minted standalone.
What done looks like
Acceptance criteria
user.promoteissued fromReviewerActions.onPromoterefreshes bothdivan.rosterand the open author'sdivan.backlog, so the promoted account and its pending items leave the screen without a full page reload.apps/web/src/components/profile/PromotionActions.tsx) applies an equivalent post-success refresh mirroring the divan handler.UNAUTHORIZED,FORBIDDEN, or a transport error change neither list's rendered contents.Original report (verbatim)
Summary
Promoting a çaylak from the divan commits correctly (tier flip + sandbox sweep in one atomic batch), but the reviewer's screen keeps showing the promoted person in
divan.rosterand their items indivan.backloguntil the fate cache is rebuilt. The second press of "yazar yap" then reports "kullanıcı zaten yazar." against a list that should no longer contain them. Reproduced on production today.What I was doing
Reviewing a çaylak on phoenix.kamp.us/divan as a moderator: pressed the promote button, got the success toast, then re-checked the roster after a page refresh.
What I observed
user.promotereturns{promoted: true}and the D1 batch (buildPromotionStatements) flipsuser.tierAND clearssandboxed_aton all three content tables atomically.onPromoteinCaylakDetail.tsxcallsfate.mutations.user.promote(...)and only sets a status message.useRequest+useListView, no/fate/livetopic for divan roots), andpublishPromotionpublishes invalidations for pano/sozluk topics plus the user entity — nothing repairs the divan connections.Why it matters
A moderator who promotes one person sees a stale queue and reasonably presses promote again; the confusing "zaten yazar" message erodes trust in the review loop. Might also mask real regressions: a mod cannot tell "stale UI" from "promotion failed".
Pointers
ReviewerActions.onPromote(no refetch after success)useRequest({"divan.roster": ...})Suggested next step (non-binding)
After a successful
user.promote, refetch/expire the two private roots (divan.roster, and the opendivan.backlog) in the same handler; mirror it in the profile-page promote path. A guess: fate's request cache may need an explicit expire/refetch primitive sinceuseRequestexposes no refetch handle.Filed by an agent · session
01a02c89-7810-7c22-8280-3c8a1feb8191· branchumut/m50-diataxis-active· 2026-08-23T06:25:17Z