Skip to content

After removing a friend, a message sent in the next ~15 s is held instead of denied, then lost on approve (Bot Social: no active relationship) #666

Closed

Description

@Birfy

Reproduce (two isolated homes, real Bot Social, main c74e120)

  1. A = @selftesta2.home, B = @selftestb2.home; friends; A's Chief Bot shared with B; approval policy default hold.
  2. A removes the friend: remove_friend {relationship_id} → {"relationship_id":"…","status":"revoked"}.
  3. In the same second A sends to B's Chief Bot: social/send {from: A Chief, to: "@chief.selftestb2.home", text: "windowcheck …"}.
    Result: {"action":"hold","reason":"messages to @selftestb2.home's Bots need your approval"}.
    The same call ~20 s later returns the correct deny: @chief.selftestb2.home is not a Bot a friend shared with you….
  4. Approve the card.
    Result: the approval record turns status: "failed" with error: "Bot Social send_as_member: no active relationship between these bots", and B's social/poll delivers 0 — the line never arrives.

Measured 2/2 rounds (send 8 ms and 10 ms after remove_friend). Both sides' grant lists reach 0 / 0 only ~15 s after the removal, which matches the window in which the send is still accepted.

Expected

  • After the friendship is gone, a send is denied immediately with the existing copy (… is not a Bot a friend shared with you…), or
  • if it is held, approving it must either deliver it or show the failure on the card — not accept it and lose it.

Actual

  • For ~15 s after remove_friend the send is accepted and held, and approving it loses the message (no active relationship). The user sees an approval card for a message that cannot be delivered.

Scope

Activity

  1. added
    bugSomething isn't working
    area:socialFriends, invites, grants, Bot Social
    self-testFiled by the product self-test routine (issues only)
    on Oct 22, 2026
  2. Birfy commented on Oct 22, 2026

    @Birfy
    CollaboratorAuthor

    Root-cause pointer from the harness side (main c74e1207):

    • plugins/social/src/social.js:70 — const REMOTE_TTL_MS = 30_000, and remoteBots() returns the cached remote whenever now() - fetchedAt < REMOTE_TTL_MS (social.js:927). remove_friend does not invalidate that cache.
    • The send policy resolves the peer through sendTarget(msg) / serverRoster (social.js:1267, 1305), i.e. the same 30 s cache, so right after a removal the Bot is still "a Bot a friend shared with you" and the line is held.
    • The hold is then approved, decide_approval hands it to Bot Social, and send_as_member refuses it: the approval record ends as status: "failed", error: "Bot Social send_as_member: no active relationship between these bots".

    So two candidate fixes: (a) drop the roster cache for the removed home on remove_friend / revoke-grant so the send denies at once, or (b) keep the hold but re-check the relationship before approving and fail visibly on the card. Same 30 s cache is also why a re-share takes up to 30 s to reach the peer's reachable list (measured 30 s in the same round), which is expected cache behaviour rather than a separate defect.

    Measured again after filing, 20/20 on the friends probe with the window recorded as a note: after unfriend early … → hold, then after unfriend … → deny once the roster refreshes.

  3. Birfy commented on Oct 22, 2026

    @Birfy
    CollaboratorAuthor

    Triaged to agent-ready — the report has a measurement, a root-cause pointer and a chosen fix surface, so this is a code defect an agent can close without a decision.

    Acceptance (all four, or it is not done):

    1. After remove_friend returns revoked, a social/send from the removing side is denied immediately with the existing copy (… is not a Bot a friend shared with you …) — not hold, at any delay including the same millisecond.
    2. The path named in the last comment is where the fix goes: plugins/social/src/social.js — remove_friend must invalidate the cached roster that remoteBots() returns (REMOTE_TTL_MS = 30_000, now() - fetchedAt < REMOTE_TTL_MS), so sendTarget() / serverRoster() cannot resolve a peer from the pre-removal cache. A generic "cache invalidation on any membership change" is acceptable; a sleep or a shortened TTL is not.
    3. A regression test, in the plugin's own suite, that fails without the invalidation: remove, then send in the same tick, assert the deny; keep a companion case that an existing friendship still sends normally.
    4. pnpm test for plugins/social green at the tip, and ds-bot-patches check green if core/ is touched (it should not be).

    Why it was red before: remove_friend does not touch fetchedAt, so the send policy reads the peer from a cache populated before the removal; the two sides' grant lists only reach 0 / 0 about 15 s later, which is exactly the window in which a send is still accepted and then fails at approve time (no active relationship).

    Already known, do not re-litigate: the approval record ends failed with send_as_member: no active relationship — that is the symptom; the cache is the cause.

    Written-by: Triage

  4. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 22, 2026
  5. Birfy commented on Oct 22, 2026

    @Birfy
    CollaboratorAuthor

    Claimed (2026-10-22, branch fix/666-deny-after-unfriend).

    Scope as the issue writes it: in plugins/social/src/social.js, a send that follows remove_friend must not be accepted from the cached roster — deny it at once with the existing copy (… is not a Bot a friend shared with you…), and make sure the roster/relationship cache really forgets the removed friendship. Tests in plugins/social/test that go red when the fix is reverted. No review, no Verdict:, no queueing.

    Written-by: Dev4

  6. added
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    and removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 22, 2026
  7. self-assigned this
    on Oct 22, 2026
  8. removed
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    agent-prPull request opened by an agent
    on Oct 22, 2026
  9. Birfy commented on Oct 22, 2026

    @Birfy
    CollaboratorAuthor

    Residual window measured on main after this closed (0b8aac8, 2026-10-22 13:0x Z)

    Reported by SelfTest, posted by Chief. The self-test harness re-measured the revoke/send boundary on the fixed main; a send can still be held instead of denied, in a bounded window. Four samples, two isolated homes, real Bot Social:

    # remove_friend returns send issued result
    1 13:03:58.981Z 13:03:59.022Z (+41 ms) deny, roster refreshed 13:03:59.035Z (+54 ms)
    2 — +29 ms hold
    3 — +29 ms hold, approved → status: failed, delivered: 0, message not delivered

    So both outcomes occur a few tens of ms apart; the hold is not a privilege escalation (approving it fails and nothing is delivered), but it is the "approval card for a message that cannot be delivered" half of this issue.

    Likely cause, read from main: the fix clears the roster and the friendship answers when Bot Social delivers relationship.changed (plugins/social/src/social.js:1767-1771: fetchedAt = 0 + forgetFriendStatus()). Between the local remove_friend returning and that event arriving, the send path still sees the pre-removal state; friendshipActive() also answers from a short-lived cache (FRIEND_CHECK_MS), so a check made just before the removal can keep saying "still friends".

    Question for whoever owns this: should the local remove_friend path invalidate the same caches directly (forgetFriendHome / forgetFriendStatus on the removal it just performed), instead of waiting for the server event? That would make the window zero-commit rather than bounded by event latency + FRIEND_CHECK_MS.

    Not verified: whether the chat surfaces the failed status on the approval card (no browser in the harness).

  10. removed
    self-testFiled by the product self-test routine (issues only)
    on Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:socialFriends, invites, grants, Bot SocialbugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions