Skip to content

Stop a disposed social layer from re-arming its poll - #630

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:fix/social-dispose-poll
Oct 21, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:fix/social-dispose-poll

Conversation

@HugoXK

@HugoXK HugoXK commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Stop a disposed social layer from re-arming its poll
When the social layer is stopped (a Bot Social server switch, or a start that finished late), dispose() clears the poll timer but leaves no marker that the instance is finished. A face poll already in flight can resolve afterwards and call armSocialPoll(); if the cadence changed meanwhile (a face room closed, so socialPollMs() differs from lastPollMs), the guard ms === lastPollMs && pollTimer does not stop it and it installs a new interval on the discarded layer. That layer then keeps polling the server it was built for — which the server-switch flow deliberately leaves reachable — and can deliver its inbox lines after the switch.
createSocial now sets a disposed flag in dispose(), armSocialPoll() returns at once when it is set, and the poll timer's disposer clears the handle too. plugins/social/test/social.test.js sets up a live face room (fast cadence), stalls the first face poll, changes the cadence, disposes, and resolves the in-flight poll: no timer may be re-armed. It fails without the change (a 15 s timer appears).
Ran at the tip: npm run verify (green, TZ=UTC). Not run: smoke (this macOS box has no setsid, so dev/bench.sh cannot start dsh web), Desktop, real-model steward.

A face poll already in flight when the layer is disposed could finish
afterwards and call armSocialPoll, which installed a new interval on the
discarded instance: after a server switch the old server kept being
polled. Set a disposed flag that armSocialPoll checks, and clear the
stopped timer's handle so nothing re-arms it.
@Birfy Birfy added agent-pr Pull request opened by an agent area:social Friends, invites, grants, Bot Social bug Something isn't working labels Oct 21, 2026
@Birfy

Birfy commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator

Verdict (written by Reviewer, posted by Chief — Reviewer's GitHub write channel is down)

Tip SHA: 9987a25f6fe9d313a201cc19411f273b3d556e8b
Verdict: PASS
Ran:      npm --prefix plugins/social test   -> 317/317
          mutation: revert the three `plugins/social/src/social.js` hunks -> 316/317, the only red being
          "a face poll in flight when the plugin is disposed does not re-arm its timer"
          tip checks: Test (6 jobs) + Risk tier + Review gate all succeeded
Not run:  full `npm run verify`; a real server switch (the dispose path is exercised by unit tests only)

Tier note: controller.yml has not run since 07:28:19Z (#629), so no risk:T* exists for this sha — unknown, not low.

@Birfy
Birfy added this pull request to the merge queue Oct 21, 2026
@Birfy

Birfy commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at 9987a25

Tip SHA: 9987a25f6fe9d313a201cc19411f273b3d556e8b
CI at tip: 8/8 success (tests-ok, package, plugins social/tree/timeline, ubuntu-latest Node 24,
           reviewed tip, risk tier)
Tier (from the diff): T3 — plugins/social/src/** is a T3 path in .github/agent/risk-rules.json

Ran at this tip (the whole social suite, not just the new test):

$ cd plugins/social && TZ=UTC DSH_HARNESS=… node --import ./test/resolve.mjs --test test/social.test.js
# tests 275 / pass 275 / fail 0

Gate ③, source change reverted (plugins/social/src/social.js from base 3995d9b9), tests kept:

not ok 8 - a face poll in flight when the plugin is disposed does not re-arm its timer
# tests 275 / pass 274 / fail 1

Exactly the one new test goes red.

Why the fix is right: dispose() cleared the timer but left nothing that told a late armSocialPoll() the instance was finished. The ms === lastPollMs && pollTimer guard only stops re-arming while the cadence is unchanged, and a face room closing mid-flight changes socialPollMs(), so the guard misses and a fresh interval lands on the discarded layer. disposed = true set before the disposers run, plus the early return, closes that path regardless of cadence, and clearing pollTimer in the disposer keeps the stale-timer state honest. A live instance is untouched: disposed is only ever set in dispose().

Not the same place as #622: that one is the timeline panel's inFlight guard on a superseded poll; this one is the social layer re-arming after disposal. Different modules, same class of bug.

Entering the merge queue now.

Merged via the queue into ThinkFlowLab:main with commit 21b12e2 Oct 21, 2026
8 checks passed
@Birfy

Birfy commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator

Post-merge record: 21b12e25 (squash, via the merge queue). Reviewed tip 9987a25f unchanged.

@Birfy Birfy removed the agent-pr Pull request opened by an agent label Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:social Friends, invites, grants, Bot Social bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants