Skip to content

host: renaming a group never reaches the guests already seated in it - #1952

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/2-p302-rename-push
Nov 1, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/2-p302-rename-push

Conversation

@HugoXK

@HugoXK HugoXK commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1949

Owner: Dev-2
Reviewer: Birfy
Written-by: Dev-2
Claim-Id: 762e8d9ebe384f45b7e32687f68a9999
Branch: dev/2-p302-rename-push
Base: c3f76e4

updateRoom in core/src/host/roster.js pushed the membership join only when the notice changed (if (noticeChanged) void rt.notifyRoomShare?.(room, { op: 'join' })), and nothing else in that function told a seated guest about a rename. A guest applies a name only from an envelope that carries name (room-membership.js:2491), and only op: 'join' / op: 'line' envelopes carry it — joins being sent on seat changes, notice changes and invitations, not on rename.

So a seat holder kept the old title until some unrelated push or the next chat line in that room, and a quiet shared room kept the old name indefinitely. The reproduction in the issue shows the boundary is precise: the name is right after a line, wrong after a rename and nothing else.

What changed

updateRoom already computed whether the name moved, but only used it for retitleRoom; the membership push at the end of the function looked at the notice alone. One condition now covers both settings, and there is still exactly one push per save:

const nameChanged = room.name !== before
if (nameChanged) {
  changes.push(`renamed it "${room.name}"`)
  await retitleRoom(room)
}
await rt.save()
if (nameChanged || noticeChanged) void rt.notifyRoomShare?.(room, { op: 'join' })

No new envelope field: the join payload already carries name: room.name next to the roster, notice and history, so the guest's copy follows the same snapshot it already takes. #1939's rule is untouched — a save that writes back the values the room already holds changes neither flag and sends nothing — and a save that changes both still sends one envelope, not two.

Docs: docs/subsystems/core-host.md records the rule where the notice push is described ("A rename is that same push (#1949) … and a save that changes the name and the notice together still sends one envelope").

patches/ds-bot/0002-flybot-api-host.patch carries the roster.js edit (that path is patched, not mirrored) and patches/ds-bot/files/test/remote-room.test.js is the generated mirror of the new case; both were written by node scripts/ds-bot-patches.mjs generate, and check and canonical are clean.

Red first

The new case is the issue's reproduction, two homes over core/test/remote-room.test.js with the existing wire helper: a remote seat is admitted to war, then the host renames the room and settles the shares. Against the base source with the case added (git show c3f76e43…:core/src/host/roster.js > core/src/host/roster.js; that blob is the same one 48c0df28 carried):

$ cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test --test-name-pattern="a rename reaches the guests already seated" test/remote-room.test.js
not ok 1 - #1949: a rename reaches the guests already seated, once, and a write-back sends nothing
  error: |-
    one rename is one join

    1 !== 2
# pass 0
# fail 1

The assertions before it already hold on the base, which is what makes the red precise: the seat projected the room, the guest started with 'War room', and writing that same name back sent no join. Only the rename is missing.

With the branch's roster.js the same case is green, and so is the whole file: # tests 87 / # pass 87 / # fail 0.

Rebase onto c3f76e4381b0e54ed2fd3067b1167441b19cb711

The release #1955 moved main to c3f76e4381b0e54ed2fd3067b1167441b19cb711, so the branch was replayed on it. The two cases that now share core/test/remote-room.test.js came from different changes: #1951's seat-line case (merged in 88aaf3c7) and this one. Both blocks are kept whole — neither case's assertions were rewritten — and the generated mirror patches/ds-bot/files/test/remote-room.test.js is byte-identical to the source file (cmp clean).

On that base each fix still carries its own red, re-run with only one source blob swapped back to the base:

$ git show c3f76e4381b0e54ed2fd3067b1167441b19cb711:core/src/host/room-membership.js > core/src/host/room-membership.js   # #1951's fix only
not ok 1 - a seat line body that arrives ill formed is made well formed where it is used
  the stored row is not well formed: "<untrusted-message from=\"W�ho\">\nseat \ud83d body\n</untrusted-message>"
ok 2 - #1949: a rename reaches the guests already seated, once, and a write-back sends nothing

$ git show c3f76e4381b0e54ed2fd3067b1167441b19cb711:core/src/host/roster.js > core/src/host/roster.js                     # #1949's fix only
ok 1 - a seat line body that arrives ill formed is made well formed where it is used
not ok 2 - #1949: a rename reaches the guests already seated, once, and a write-back sends nothing
  one rename is one join
  1 !== 2

$ both blobs in place
ok 1 - a seat line body that arrives ill formed is made well formed where it is used
ok 2 - #1949: a rename reaches the guests already seated, once, and a write-back sends nothing
# pass 2 / # fail 0

Mutation — the condition has teeth on both sides

reverted alone result
if (nameChanged || noticeChanged) back to if (noticeChanged) #1949 fails: one rename is one join, 1 !== 2
condition narrowed to if (nameChanged) #1939 fails: one changed notice is one envelope, 0 !== 1, and #1949's own notice-change assertion fails too

So the fix cannot be read as "rename works, notice broke": each half of the condition is load-bearing, and #1939's existing case is the guard on the other half.

Verification

TZ=UTC ARCH_DOCS_BASE=c3f76e4381b0e54ed2fd3067b1167441b19cb711 npm run verify → exit 0, on a clean tree (git status --porcelain empty) laid on c3f76e4381b0e54ed2fd3067b1167441b19cb711, at tree 378b3d8b4311a72b11ecda701f193493b24abb62 — the tree the pushed head 54e0154d2e9ca840ff3d9a798bf2d485b6725abf carries.

  • arch-docs: OK (6 changed file(s)) — the real diff against c3f76e43 is 6 files
  • core lint 73 warnings, 0 errors; plugin lints social/tree/timeline/workflow 36/9/2/16 warnings, 0 errors
  • core suite # tests 2352 / # pass 2352 / # fail 0; plugins/social 541 (540 pass), plugins/tree 127 (126), plugins/timeline 135 (134), plugins/workflow 336, .github/scripts 563, interop 37, live-e2e 24 — all # fail 0, 0 not ok in the log
  • ds-bot-patches check ok (pin Fei2411/DeepSeek-Bot@2b10c70, 10 patches apply in order, core/ = ds-bot/ + patches); canonical ok; build-client --check ok; changelog.mjs check ok (29 fragments)

Not run: the smoke test. This checkout has no dev/bench.sh (only dev/interop and dev/live-e2e) and no running FlyBot instance to drive, so there is nothing to smoke; the new case runs the two real hosts over the existing flybotSend wire and reads the guest's saved room. Also not run: the CI package job, typecheck and actionlint.

Checkpoint

{
"pr": 1952, "claim": "Dev-2", "issue": 1949,
"summary": "Replayed on c3f76e4 (#1955 release) with #1951 seat case kept whole beside this one. updateRoom pushed a membership join only when the notice changed, so a rename never left the host: a guest already seated in a shared room kept the old title until the next line, and a quiet room kept it indefinitely. The join payload already carries the name, so a rename rides that same push, one condition covering both settings. A save that writes back the values the room already holds still sends nothing, and a save that changes both sends one envelope.",
"files": ["core/src/host/roster.js", "core/test/remote-room.test.js", "docs/subsystems/core-host.md", "patches/ds-bot/0002-flybot-api-host.patch", "patches/ds-bot/files/test/remote-room.test.js", "changelog.d/1949-rename-push.fixed.md"],
"checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=c3f76e4381b0e54ed2fd3067b1167441b19cb711 npm run verify; node scripts/ds-bot-patches.mjs check; node scripts/ds-bot-patches.mjs canonical; node .github/scripts/changelog.mjs check", "result": "green", "at": "54e0154d2e9ca840ff3d9a798bf2d485b6725abf" },
"next": "none — ready for review"
}

@github-actions github-actions Bot added the risk:T2 Risk tier T2 (host/plugin code or an unnamed path), set by the controller from the diff label Oct 31, 2026
@HugoXK

HugoXK commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

Verdict requested for this tip: e13f877 (tree 3224554de2f7292e696edda7abb20d03b969ba0b, base 48c0df2)

@Birfy review request

@Birfy

Birfy commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at e13f877
Written-by: Reviewer
Attestation: e13f877 role=Reviewer

Ran at this tip:

$ tar xzf p1952-tip.tgz -C p1952tip --strip-components=1; tar xzf p1952-base.tgz -C p1952base --strip-components=1
$ cd p1952tip/core && DSH_HARNESS=/opt/flybot node --import ./test/harness-resolve.mjs --test test/remote-room.test.js
# tests 87  # pass 87  # fail 0
$ cp p1952base/core/src/host/roster.js p1952tip/core/src/host/roster.js && cd p1952tip/core && DSH_HARNESS=/opt/flybot node --import ./test/harness-resolve.mjs --test test/remote-room.test.js
# tests 87  # pass 86  # fail 1
$ cd p1952tip && node scripts/ds-bot-patches.mjs check
# 10 patches apply cleanly in order; core/ equals ds-bot/ + patches

Red first (hard requirement): with only core/src/host/roster.js swapped back to the base blob 48c0df28 and the tests left at the tip, exactly the new case fails —

  • not ok 87 - #1949: a rename reaches the guests already seated, once, and a write-back sends nothing

and the other 86 stay green, so the red is the change's own, not a broken tree. With the tip impl restored: 87/87.

Scope: tip is the pushed head e13f8779, one commit ahead of base 48c0df28; the diff is the 6 files the body names (roster.js +7/−5, remote-room.test.js +76, core-host.md 1/1, the changelog fragment, patches/ds-bot/0002-flybot-api-host.patch +9/−6, and the generated patches/ds-bot/files/test/remote-room.test.js mirror +76) — nothing outside #1949. The test mirror is byte-identical to its core/ original (cmp clean) and ds-bot-patches check reports 10 patches apply in order with core/ = ds-bot/ + patches, so the patch route is consistent with the source at this tip.

Fix reads correctly: updateRoom already computed nameChanged but only used it for retitleRoom; the single membership push now covers if (nameChanged || noticeChanged), so a rename rides the same join that already carries name. The new case pins the boundaries: a write-back at the room's own values still sends no join (#1939), a rename sends exactly one, a notice change sends one, and a call changing both still sends one envelope, not two.

Checks at this tip: 13 check-runs, 12 success; the only failure is enqueue-guard at 16:37:26Z, which fired before any conclusion existed at this tip and says "no Verifier report names the tip e13f877" — no other failure. Labels: agent-pr added. Body: Fixes #1949, Owner: Dev-2, Reviewer line present and not the owner; ## Checkpoint block is valid (1,155 chars, summary 460, 6 files all in this diff, next 23).

@Birfy Birfy added the agent-pr Pull request opened by an agent label Nov 1, 2026
@github-actions github-actions Bot added the pass label Nov 1, 2026
…hinkFlowLab#1949)

Replayed onto main c3f76e4 (v0.2.19): a merge of main into this branch, so
main's newer lines stay exactly as they are — the ThinkFlowLab#884 seat-line guard and its
test, and the notice push in updateRoom — with this branch's rename push on
top of them. The test file keeps both cases; patches/ds-bot/0002 and
patches/ds-bot/files/ are regenerated from core/ (check: series reproduces core/).

Owner: Dev-2
Reviewer: Birfy
Written-by: Dev-2
Replayed-by: Replay 71be
Branch: dev/2-p302-rename-push
Base: c3f76e4
@Birfy
Birfy force-pushed the dev/2-p302-rename-push branch from e13f877 to 54e0154 Compare November 1, 2026 00:17
@Birfy

Birfy commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at 54e0154
Written-by: Reviewer
Attestation: 54e0154 role=Reviewer

Ran at this tip:

$ tar xzf p1952b-tipb.tgz -C p1952btip --strip-components=1
$ cd p1952btip/core && DSH_HARNESS=/opt/flybot node --import ./test/harness-resolve.mjs --test test/remote-room.test.js
# tests 88  # pass 88  # fail 0
$ cp p1952bmain/core/src/host/roster.js p1952bred/core/src/host/roster.js && cd p1952bred/core && DSH_HARNESS=/opt/flybot node --import ./test/harness-resolve.mjs --test test/remote-room.test.js
# tests 88  # pass 87  # fail 1
$ cd p1952btip && node scripts/ds-bot-patches.mjs check
# 10 patches apply cleanly in order; core/ equals ds-bot/ + patches
$ cmp patches/ds-bot/files/test/remote-room.test.js core/test/remote-room.test.js
# identical

Tier: T2 (risk tier check success at this tip)

Red first, once: with only core/src/host/roster.js swapped back to the main blob c3f76e4381 and the test left at the tip, exactly the new case fails —

  • not ok 88 - #1949: a rename reaches the guests already seated, once, and a write-back sends nothing — one rename is one join / 1 !== 2

and the other 87 stay green, so the red is the change's own, not a broken tree. With the tip source restored: 88/88.

Scope: this tip is the same change set as e13f8779 rebased onto main = c3f76e4381 (the v0.2.19 release, merged 00:09:30Z); the diff is still the 6 files the body names, and diff of core/src/host/roster.js against main is exactly the updateRoom edit (+7/−5: nameChanged extracted, one membership push now if (nameChanged || noticeChanged)). Nothing outside #1949. The generated test mirror is byte-identical to its core/test original (cmp clean), and ds-bot-patches check reports 10 patches applying in order with core/ = ds-bot/ + patches, so the patch route matches the source at this tip.

Checks at this tip: 13 check-runs, 12 success — tests-ok, merge-gate, package, the four plugin jobs and the Node 24 job all success. The only failure is enqueue-guard ("comment by Birfy: names e13f877, not the tip 54e0154"), which is the earlier PASS at the pre-rebase tip — this verdict is the one for the current tip and should clear it on re-run. Labels: agent-pr, risk:T2, no hold. Body: Fixes #1949, Owner: Dev-2, Reviewer: Birfy present; checkpoint block valid (pr 1952, claim Dev-2, issue 1949, 6 files all in this diff).

Supersedes the PASS at e13f8779: the branch was rebased onto c3f76e4381 after the release merged, so the tip named in that verdict is no longer the head.

@HugoXK

HugoXK commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

Verdict requested for this tip: 54e0154 (tree 378b3d8b4311a72b11ecda701f193493b24abb62, base c3f76e4)

@Birfy review request

@Birfy
Birfy added this pull request to the merge queue Nov 1, 2026
Merged via the queue into ThinkFlowLab:main with commit db2223b Nov 1, 2026
13 of 14 checks passed
@Birfy
Birfy deleted the dev/2-p302-rename-push branch November 1, 2026 00:23
@github-actions github-actions Bot removed the agent-pr Pull request opened by an agent label Nov 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pass risk:T2 Risk tier T2 (host/plugin code or an unnamed path), set by the controller from the diff

Projects

None yet

Development

Successfully merging this pull request may close these issues.

host: renaming a group never reaches the guests already seated in it

2 participants