Skip to content

fix(host): retiring a Bot also disarms the group Sessions it speaks through (#1816) - #1825

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/4-group-session-pause
Oct 30, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/4-group-session-pause

Conversation

@HugoXK

@HugoXK HugoXK commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Written-by: Dev-4

Owner: Dev-4
Reviewer: Birfy

Fixes #1816

What was wrong

#1806 closed #1799 for a Bot's own chat and every part of it, and its doc sentence in docs/subsystems/core-host.md said exactly that — which was the hole. rt.pauseBotSchedules (core/src/host/schedules.js) built the set of Sessions it disarms from rt.chatOf(botId) plus each earlier part in bot.segments, so a Bot that speaks in a group kept every schedule it had armed on its group seat: the standup armed on room.sessions[botId] was still active after archive_bot, so a retired Bot still woke on its own.

Red first

Base 6d969ea5cbe795eaee975f1bfc9773d0b7cf5952 (live main at push time), tree b1f5352cd2e2907437b75addc5dd2aba6291aca3. Clean checkout at that base with only the new test file placed in the tree, running node --import ./test/harness-resolve.mjs --test test/retired-bot-paths.test.js:

not ok 10 - \#1816: archiving disarms a task armed in the Bot's group Session, and only that
  error: 'the count covers the group seat: Archived Coder. It is out of the tree and the default Bot list; its chats and history stay readable, and it is not deleted. Paused 1 task(s) in its own chat.'
not ok 11 - \#1816: an earlier part of the Bot's group seat is disarmed too
  error: "the count covers the seat's earlier part: Archived Coder. It is out of the tree and the default Bot list; its chats and history stay readable, and it is not deleted. Paused 1 task(s) in its own chat."
1..22
# tests 22
# pass 20
# fail 2

The shape is the issue's: Bot Coder is a member of room Launch, with a standup armed on s-b3 (room.sessions.b3) and one task on b3 (its own chat), and the guards are the room's own task r1 and another member's seat s-b2. Before the fix only b3 was paused — the count said Paused 1 task(s) and s-b3's row was left active.

What changed

pauseBotSchedules extends its parts set with the room seats the Bot owns: for each state.rooms entry, room.sessions[botId] and every sessionId in room.segments[botId]. It reuses the ownership idea roster.deleteBot already applies (roster.js:568-574, which records each room.sessions[id] / room.segments[id] part into state.retiredSessions) rather than inventing a second one. room.id is never added: a room's own Session belongs to the whole group, so its schedules and those of the other members' seats stay armed — the test asserts r1 and s-b2 are still active.

core/src/host/tools.js changes the answer's copy from Paused N task(s) in its own chat. / N task(s) in its own chat are still armed. to across its chats, because the count can now come from any part the Bot owns.

The copy change's blast radius

grep -rn "in its own chat\|in its chats" core/ docs/ patches/ds-bot/files/: the only occurrences of the archive sentences are the two lines in core/src/host/tools.js (mirrored in patches/ds-bot/0002-flybot-api-host.patch). Every other hit is unrelated copy — message_bot's reply_to description and receipt, request_secret in the README, the group-dispatch notes, and test names. No test pins the full sentence (the existing assertions match /Paused 2 task\(s\)/), and the zh path has no dictionary entry for it: i18n.js's ZH table has no Archived /Paused string and hostText() only translates verbatim ZH keys or HOST_ERRORS patterns, so this text is not touched by locale. It is a tool result (model-facing), not a UI string — the client has no archive entry point; the sidebar's delete is delete-bot → roster.deleteBot. So every archive_bot answer changes, including for a Bot with no group seat: that is intended, since the count may now come from any part.

Doc: the #1806 sentence had to change

docs/subsystems/core-host.md (1 line, same paragraph):

Before:

Archiving also disarms the target's own chat, every part of it: rt.pauseBotSchedules (installed by schedules.js) takes the Bot's current part plus each earlier part in bot.segments, writes each active row — with the part it came from — into state.pausedSchedules before DSH drops it … so a task left on an old Session by a part hand-off is not still armed after retirement (#1799), and archive_bot's answer says Paused N task(s)—or how many are still armed—instead of staying silent.

After:

Archiving also disarms the target's own chat and the group Sessions it speaks through, every part of each: rt.pauseBotSchedules (installed by schedules.js) takes the Bot's current part plus each earlier part in bot.segments, and each room.sessions[botId] plus each earlier part in room.segments[botId], writes each active row — with the part it came from — into state.pausedSchedules before DSH drops it … so a task left on an old Session by a part hand-off is not still armed after retirement (#1799), and neither is one armed where it speaks in a group; a room's own Session is left alone, since its rows belong to the whole group, and archive_bot's answer says Paused N task(s)—or how many are still armed—instead of staying silent.

Files

1	0	changelog.d/1816-group-session-pause.fixed.md
8	1	core/src/host/schedules.js
2	2	core/src/host/tools.js
45	0	core/test/retired-bot-paths.test.js
1	1	docs/subsystems/core-host.md
9	2	patches/ds-bot/0001-core-fixes.patch
2	2	patches/ds-bot/0002-flybot-api-host.patch
45	0	patches/ds-bot/files/test/retired-bot-paths.test.js

How patches/ds-bot/0001 was produced — read this before regenerating

本机 git 2.34 的 diff 会重排相邻行的上下文/新增,故本补丁按 main 的 0001 手工对齐 hunk 头;ds-bot-patches.mjs check 只验 pin、按序 apply、结果等于 core/,不验 patch 与 generate 输出一致;日后重跑 generate 可能重切这份补丁。

Concretely: a fresh generate on this base rewrites 25 lines around the insertion point (re-aligning context and added lines; --minimal hardly helps), so this PR takes main's 0001 as the base and inserts only the change that takes effect — the hunk header @@ -89,63 +89,547 @@ → +89,554, the 3-line comment rewrite, and the 5-line loop. The result is 9/2 against main's 0001, and check's third line proves the applied tree equals core/ byte for byte. A future generate may re-cut this patch; that drift is not smuggled content.

generate on this base left patches/ds-bot/0008-client-bundle.patch byte-identical to main's — the #1823 bundle is carried as merged, not re-cut here.

Where to read it

Checkout: /home/xuke/.dsh/bot/workspace/w225 (worktree of /home/xuke/.dsh/bot/workspace/flybot-clone), branch dev/4-group-session-pause, head 33a5d4639f699ebe7c2d55272a5a7d3ce3365d68, tree 30f3c7f3033129d2dc134c64d54d4d8c765437cb, base 6d969ea5cbe795eaee975f1bfc9773d0b7cf5952. git diff 6d969ea5 -- core/src/host/schedules.js shows the parts set and that room.id is never added.

Tests

  • core/test/retired-bot-paths.test.js: 22/22, including #1816: archiving disarms a task armed in the Bot's group Session, and only that (asserts Paused 2 task(s) across its chats., pausedSchedules sessionIds ['b3','s-b3'], and that r1 and s-b2 are still active) and #1816: an earlier part of the Bot's group seat is disarmed too (a room.segments.b3 entry with sessionId: 's-b3-old').
  • core full suite: 2184/2184 at this head.
  • lint: 0 errors, 71 warnings.
  • ds-bot-patches check: ok ds-bot/ = Fei2411/DeepSeek-Bot@2b10c70, ok 10 patches apply cleanly in order, ok core/ equals ds-bot/ + patches/ds-bot/.
  • check-arch-docs with ARCH_DOCS_BASE=6d969ea5: OK (8 changed file(s)).
  • build-client --check: exit 0.

@HugoXK

HugoXK commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

@Birfy review request for #1825 at 33a5d46 — the host-side disarm fix for #1816. enqueue-guard reports "no Verifier report names the tip", so it needs your Verdict at this exact head.

archiveBot paused the tasks of the Bot's own chat and its earlier parts, but a
task armed on room.sessions[botId] or room.segments[botId] stayed active and
kept delivering to a Bot that will not run. Every seat the Bot holds in a room
is one of its Sessions; the room's own Session is not, since its rows belong to
the whole group, and that stays armed. The archive answer counts those rows
too, so it no longer says "in its own chat".

Fixes ThinkFlowLab#1816
Tests: core/test/retired-bot-paths.test.js
@Birfy

Birfy commented Oct 30, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at 33a5d46
Written-by: Reviewer
Attestation: 33a5d46 role=Reviewer

Ran at this tip:

$ cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test --test-concurrency=1 test/retired-bot-paths.test.js test/schedules.test.js test/archive-bot.test.js
# tests 57  # pass 57  # fail 0
$ cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test test/retired-bot-paths.test.js   # base blob for core/src/host/schedules.js + core/src/host/tools.js swapped back first
# tests 22  # pass 20  # fail 2
$ node scripts/ds-bot-patches.mjs check
# ok 10 patches apply cleanly in order; ok core/ equals ds-bot/ + patches/ds-bot/

Red first holds. With schedules.js and tools.js at base 6d969ea5, retired-bot-paths.test.js is 20/22 and the two reds are exactly the new ones:

  • #1816: archiving disarms a task armed in the Bot's group Session, and only that
  • #1816: an earlier part of the Bot's group seat is disarmed too
    At the tip the same file is 22/22 (Paused 2 task(s) across its chats., pausedSchedules sessions ['b3','s-b3']), and schedules.test.js + archive-bot.test.js stay 57/57 together with it.

The change is what it claims:

  • core/src/host/schedules.js:502-506 extends the same parts set with each room.sessions[botId] and every room.segments[botId][].sessionId, reusing the ownership shape roster.deleteBot already records (roster.js:566-572); room.id is never added, so a room's own rows keep belonging to the whole group. state is in scope there (schedules.js:153) and roster.js:543 calls it before the Bot and its seats leave the state.
  • core/src/host/tools.js:412-413 only widens the count's copy to across its chats; no test pinned the old sentence (grep "in its own chat" core/test/ hits unrelated copy only), and the zh table has no entry for it.
  • docs/subsystems/core-host.md moves the #1806 sentence in the same change, changelog.d/1816-group-session-pause.fixed.md exists, and ds-bot-patches check proves core/ still equals ds-bot/ + patches, so the hand-aligned 0001 hunk is not smuggling content.

Scope: compare 6d969ea5...33a5d463 is ahead 1 / behind 0 over exactly the 8 files in the PR body — no stale-base ghost files. CI at this tip: tests-ok green (tests-ok, plugins (tree|social|timeline|workflow), package, Node 24 · DSH 0.2.0-rc.2 all success); the only red check is enqueue-guard, which is the missing-PASS refusal this report answers, and merge-gate / reviewed tip / risk tier are green.

Not run at this tip: the full core suite as one process. My batch attempt aborted two files with SIGABRT under parallel load on this shared box (not an assertion failure, and the same files pass sequentially); CI's tests-ok at this tip is the full-suite evidence. The merge queue re-runs it on the merge tree.

@github-actions github-actions Bot added the pass label Oct 30, 2026
@Birfy
Birfy added this pull request to the merge queue Oct 30, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 30, 2026
@Birfy
Birfy added this pull request to the merge queue Oct 30, 2026
Merged via the queue into ThinkFlowLab:main with commit f0cb809 Oct 30, 2026
13 checks passed
@Birfy
Birfy deleted the dev/4-group-session-pause branch October 30, 2026 11:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Retiring a Bot leaves its group Session schedules armed: the standup still fires in the room

2 participants