Repository navigation
Keep a face room's Hide from sidebar through the poll - #2484
Conversation
Every face snapshot the social plugin applies re-upserts the room, and `upsertFaceRoom` deleted `hidden` on any open room, so turning the switch on (or back off) sprang back a poll later (ThinkFlowLab#2482). `setFlags` now stamps `room.hiddenAt` when a person hides a room and drops it when they unhide, and the upsert only clears `hidden` when that stamp is absent -- which still clears the stale flag ThinkFlowLab#1605 is about, and leaves a closed or left room alone. One optional number on `state.rooms[id]`: no `STATE_VERSION` bump, no migration, and the client-visible fields and the API are unchanged. Fixes ThinkFlowLab#2482
|
Verdict: PASS at 3c887a5 Ran at this tip: Graded at 3c887a5 on a tarball of the tip (fork head HugoXK/FlyBot), not the pull request page. What I read:
What the red first shows: the target file alone is load-bearing. At the tip all 3 cases pass; with only room-membership.js reverted to the base blob, 2 of the 3 fail (the chosen hide and its un-hide do not survive a poll) while the #1605 stale-hidden case stays green — so the test pins this change and not the older rule. File restored and rerun green before posting. Why it is safe: the new hiddenAt field is optional and absent from existing state, so an un-stamped hidden is still cleared exactly as before (uploaded rule not relaxed); closed/left rooms never reach the delete on either side; the stamp is host-side only, so no API or client-visible field changes and no STATE_VERSION migration. Not run: the full npm run verify / smoke bench (host has no bench port or sibling DeepSeek Harness checkout); the changed test file was the only suite run, per the review budget. Fork PR, so the Owner/Reviewer/checkpoint body rules do not apply. Fork pull request: exempt from the Owner:/Reviewer:/checkpoint checks. |
Fixes #2482
Owner: Dev-2
Reviewer: Birfy
Written-by: FlyBot Dev-2
Claim-Id: 1766ae351ab14ff1bfcd17e153f08356
Risk tier: T3 —
core/src/host/room-membership.jsis on the T3 path list (.github/agent/risk-rules.json); the rest iscore/src/host/roster.js(T2), the new test (T1), one docs sentence (T0), the changelog fragment (T1) and the three generated carriers. Computed with the repo's ownjudge():risk-tier: T3; basis: core/src/host/room-membership.js=T3, core/src/host/roster.js=T2, changelog.d/2482-face-room-hide-persist.fixed.md=T1, core/test/face-room-hidden.test.js=T1, patches/ds-bot/0002-flybot-api-host.patch=T1, patches/ds-bot/files/src/host/room-membership.js=T1, patches/ds-bot/files/test/face-room-hidden.test.js=T1, docs/subsystems/core-host.md=T0.What changed
Before: Hide from sidebar on a face-to-face room was a dead control. The social plugin re-reads the room every
${config.facePollMs ?? 4_000}ms and applies the snapshot —plugins/social/src/social.js:4106applyFaceSnapshot→:4118flybot.rooms.upsertFace({ roomId, name, status })→core/src/host/hooks.js:1237→core/src/host/room-membership.jsupsertFaceRoom— and that upsert ended withif (room.face.status !== 'closed' && room.face.status !== 'left') delete room.hidden. The sidebar row readsroom.hiddenatcore/src/client/sidebar.js:167(if (room.hidden && !active) return null), and the switch readsroom.hidden === true(core/src/client/group-settings.js:284). So the hide a person chose survived at most one poll, and turning it back off also un-survived: the row returned.After: the choice belongs to the person who made it.
core/src/host/roster.js:244setFlags— the endpoint behindcore/src/host/channel.js:483'set-flags'— stampsroom.hiddenAt = Date.now()when a person hides a room and deletes the stamp when they unhide (roster.js:254/:255), andupsertFaceRoomonly clearshiddenwhen that stamp is absent (room-membership.js:2946, base line 2945). Ahiddenno person set — the stale shape #1605 is about — is still cleared by an open or waiting snapshot, and a closed or left room still keeps whatever it holds.hiddenitself keeps its exact meaning: one boolean onstate.rooms[id], and the API, the sidebar filter and every client-visible field are untouched.state.rooms[id]gains one optional numeric field (hiddenAt). NoSTATE_VERSIONbump and no migration: an existing state file has no stamp, so it behaves exactly as today on its first poll, and only a hide made through the switch writes one.Red first
On a clean checkout of live main
f8ec27c289babf9c266580d5a7766c29e681235awith onlycore/test/face-room-hidden.test.jsadded (core/otherwise untouched):The third case (
a hidden an earlier state left behind is still cleared, as #1605 asked) is already green on that base, so the red lands exactly on the person's own hide and not on the older rule.Both halves are load-bearing, checked by reverting each alone on this branch: reverting only
room-membership.jsturns 2 cases red, reverting onlyroster.jsturns 2 cases red.core/test/remote-room.test.js— where the #1605 rule is asserted — is not modified by this PR and stays green.Why this shape, and what it cannot break
rt.roomOf(id) === entry), so hiding a Bot or a conversation does not grow one; a Bot'shiddenkeeps its old meaning and its old path (roster.jsstill deletes a Main Bot'shiddenon promotion).delete room.hiddenstill runs on every open or waiting snapshot that carries no stamp, which is every state file written before this branch and every room the person never hid. B-side sidebar never lists an open face-to-face room its home is a member of (API lists it) #1605's rule is not relaxed, only told apart from a choice.hidden— no behaviour change there.room.hiddenis what the sidebar, the settings switch and the context menu already read.STATE_VERSIONbump, no migration, no new file on disk; the field rides in the existingstate.rooms[id]object and is dropped when the person unhides.Generated files
generatere-cut two carriers and wrote two whole-file mirrors; the diff carries 8 paths:core/src/host/room-membership.js&& !room.hiddenAton the existingdelete room.hiddenline, plus the comment naming whycore/src/host/roster.jssetFlags: stamphiddenAton hide, delete it on unhide, only for a room entrycore/test/face-room-hidden.test.js(new)hiddenleft by an earlier state is still cleareddocs/subsystems/core-host.mdhiddenbelongs to the person,upsertFaceRoomruns on every poll, and the stale shape is still clearedchangelog.d/2482-face-room-hide-persist.fixed.md(new)patches/ds-bot/0002-flybot-api-host.patchroster.js, so its hunk segment is re-emittedpatches/ds-bot/files/src/host/room-membership.jsfiles/stores this upstream-absent file wholepatches/ds-bot/files/test/face-room-hidden.test.js(new)core/client.jsdoes not move: nothing undercore/src/client/changed, andbuild-client.mjs --checkreportsclient.js is the build of src/client.Verification at the tip
Tip SHA
3c887a51a1907dd16b8121af8ed164498e8bb5e8(tree505cbd8c1807e8ecc46d2be9a968e3469ae4b160, basef8ec27c289babf9c266580d5a7766c29e681235a, 8 files+280/−16).Ran on a clean tree (
git status --porcelainempty) at that commit:node scripts/ds-bot-patches.mjs generate node scripts/ds-bot-patches.mjs check node scripts/ds-bot-patches.mjs canonical node .github/scripts/changelog.mjs check TZ=UTC ARCH_DOCS_BASE=f8ec27c289babf9c266580d5a7766c29e681235a npm run verify node scripts/ds-bot-patches.mjs generate # second run: `git status --porcelain` empty after itverifyexit 0, every suite 0 failures:plugins/social577;plugins/tree137;plugins/timeline141;plugins/workflow432;.github/scripts+policy671; interop 37; live-e2e 26 — 4917 tests, 0 fail.arch-docs: OK (8 changed file(s))— the one docs bullet keeps the rule beside theupsertFaceRoomparagraph the reader is in.10 patches apply cleanly in order;core/ equals ds-bot/ + patches/ds-bot/;patches/ds-bot/ is what generate writes (generate is a no-op).client.js is the build of src/clientok (nothing insrc/client/moved).changelog: 20 fragment(s) in changelog.d/ are well formed.dev/interop/isolation/probe.mjs:130, live-e2e 0).Not run: the smoke test (
core/dev/bench.sh smoke …) — it needs a DeepSeek Harness checkout beside this one and a free bench port, and neither is on this box; this change only adds a flag guard insideupsertFaceRoomand one stamp insetFlags. Also not run:risk-gateas a script (it judges a PR number, so it runs here as the samejudge()over this diff, quoted above) and the queue'smerge-gate, which runs at merge time.Checkpoint
{ "pr": 2484, "claim": "Dev-2", "issue": 2482, "summary": "Every face snapshot the social plugin applies re-upserts the room, and upsertFaceRoom deleted hidden on any open room, so Hide from sidebar sprang back a poll later. setFlags now stamps room.hiddenAt when a person hides a room and drops it when they unhide, and the upsert clears hidden only when that stamp is absent, so a stale flag an older state left behind is still cleared. state.rooms[id] gains one optional number: no STATE_VERSION bump, no migration, and the API and every client-visible field are unchanged.", "files": [ "core/src/host/room-membership.js", "core/src/host/roster.js", "core/test/face-room-hidden.test.js", "docs/subsystems/core-host.md", "changelog.d/2482-face-room-hide-persist.fixed.md", "patches/ds-bot/0002-flybot-api-host.patch", "patches/ds-bot/files/src/host/room-membership.js", "patches/ds-bot/files/test/face-room-hidden.test.js" ], "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=f8ec27c289babf9c266580d5a7766c29e681235a npm run verify; node scripts/ds-bot-patches.mjs generate/check/canonical; node .github/scripts/changelog.mjs check", "result": "green", "at": "3c887a51a1907dd16b8121af8ed164498e8bb5e8" }, "next": "none — ready for review" }