Repository navigation
Conversation
PR 105856 — feat(hosted-rooms): designated-owner routing as the no-match responder fallbackWell-structured routing change: no-mention messages go to the configured owner (else everyone, as before), and an owner whose round-0 turn terminally deferred/failed keeps the leading slot while the rest of the roster covers — with an explicit unavailable-owner notice in the covering prompt. The Strengths (verified in diff)
Findings (all Non-blocking)
No blocking issues. |
…pt budget _build_prompt admitted transcript lines up to the full remaining budget and then appended the '[Earlier content omitted ...]' marker without accounting for it, so a valid transcript within ~46 bytes of the driver limit raised 'Discussion prompt exceeds the driver limit' instead of planning the turn. The marker's bytes are now reserved while older lines remain unplaced; the truncated-first-line path shares the same reserve. Covered by a boundary sweep through plan_next_task that fails on the previous code. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…r fallback Round 0 of a hosted-room Discussion now resolves responders in three tiers: explicit @mentions or @everyone, else the room's configured owner, else the whole roster (unchanged behavior for rooms without an owner). The owner is an optional boolean 'owner' flag on one roster member, validated in the policy module (boolean, at most one) and persisted by the service's create_room. An owner whose round-0 turn ended deferred or failed keeps its slot and the rest of the roster follows in the same round with a visible prompt notice, so an unmentioned human message never has zero responders and the fallback is derived purely from the durable log: no probing, no retry loop, bounded by the existing round and message caps. A pass by the owner is an answer and settles the round as silence, as today. Mentioned owners never trigger the fallback, a second deferral keeps the fallback going, and a later owner answer ends it without a second owner turn. plan_next_task stays pure and deterministic and reconstruct_task_plan is unchanged. Ownership is frozen with the roster at creation, so _member_digest deliberately remains unchanged; future roster mutation must preserve or version the routing configuration used by admitted tasks. tests/gateway/hosted_room_responder_precedence.json is a language-neutral precedence contract the Desktop client's group-rounds.ts can adopt once its relevance tier lands, without touching that file here. Depends on the preceding prompt-budget fix: the fallback notice moves the omission-marker boundary onto previously serviceable transcripts. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ariants Answers the two open review findings on the designated-owner routing change without altering behaviour: - Prompt budget: every delta line but the oldest is placed only if the omission marker still fits after it, so the marker always has its bytes whenever a later line is dropped; the oldest may take the full remainder because nothing older can be omitted after it, and when it is the line dropped it is still earlier than everything shown. State that at the loop and pin it with two maximal user messages: the newest survives whole, the marker leads, the prompt fits. - `terminals` latest-wins: `validated` is in strict `seq` order (`_validate_event` rejects anything else, already covered by the fail-closed log test), so the last write per (round, member) is the highest-seq terminal. Say so where the dict is built. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
4da8983 to
718dafb
Compare
|
Rebased onto current |
|
@teknium1 Flagging this against your #98610 decision, since the two overlap and I'd rather not waste your review time. You declined #98610 because routing a round to a subset changes who sees what. This PR's tier 2 does route an unmentioned round to one member, so the same objection may apply — with one difference I want to check before you spend time on it: this is the hosted driver, where every member's session still receives the full room log regardless of who is asked to speak, so visibility and order are unchanged; what narrows is only who is prompted to produce a turn. If the standing ruling covers that too — everyone gets a turn, not just everyone sees the turn — say so and I'll close this. If the distinction matters, I'll add a test pinning that non-responding members still observe the complete round. Either answer also settles Q2 on #95163, which is the last of its three questions still open. |
Implements the no-match fallback tier of group-room responder selection, as agreed with
@andredezzy on #98610 (that PR owns the client-side relevance filter; this one takes the
fallback and stays out of
group-rounds.ts), under the five-point contract accepted on #95163.What changes
Round 0 of a hosted-room Discussion (
plan_next_taskingateway/hosted_room_discussion.py)now resolves responders in three tiers:
@handlementions, or@everyone/@all. Always wins.The owner is an optional boolean
ownerfield on exactly one roster member, set throughgroups.create. It is room configuration, not a registry of room names, and carries nodeployment-specific defaults. Validation: must be a boolean, at most one per roster.
HostedRoomService.create_roompersists the flag.Unavailable owner. If the owner's round-0 turn ended
turn.deferredorturn.failed, theresponder list becomes owner first (already terminal, so skipped) followed by the rest of the
roster in order. Each fallback member's prompt carries one line:
[Owner @<handle> was unavailable for this message; it is routed to you.]The fallback is derived purely from the durable log: no connectivity probing, no retry loop,
bounded by the existing 3-round / 10-message caps. A pass by the owner is an answer and settles
the round as silence, as any full-pass round does today. A mentioned owner never triggers the
fallback (tier 1 wins outright). A second deferral keeps the fallback going; a later owner
answer ends it without a second owner turn. Rounds 1 and 2 (bot-citation driven) are unchanged.
Contract check
@everyonealways win_round_zero_responders, tier 1; fixture cases incl.@research @ALLtest_unmentioned_human_message_never_has_zero_respondersownerflag on the roster row; no names or profiles in codegroups.*client sees the same decisionDeterminism and the member digest
plan_next_taskstays pure.reconstruct_task_planis unchanged: it rebuilds a task from itsturn-id coordinates and compares, which is independent of why a member was selected, and a
fallback task at
member_index >= 1round-trips through admit, restart, and reconstruction(
test_owner_fallback_task_is_deterministic_and_reconstructs_after_restart).Ownership is frozen with the roster at creation, so
_member_digestdeliberately remainsunchanged. Future roster mutation must preserve or version the routing configuration used by
admitted tasks; hashing a member's own flag would not cover another member's changed routing.
No
room.members_changedemitter exists today, so an owner change is a new room.Shared contract for the client
tests/gateway/hosted_room_responder_precedence.jsonis a language-neutral table ofprecedence cases driving one parametrized test here. It is the hand-off for
group-rounds.ts:once #98610's relevance tier lands and the client has a real "no match" state, it can adopt the
same cases without this PR touching that file. That is what makes "one policy, both clients"
credible without a turf conflict.
Prerequisite commit
The first commit fixes a pre-existing budget bug in
_build_prompt: transcript lines wereadmitted up to the full remaining budget and the
[Earlier content omitted ...]marker was thenappended unaccounted, so a valid transcript within ~46 bytes of the driver limit raised instead
of planning the turn. Reproducible on
mainwith no owner involved; the fallback notice movesthe boundary onto previously serviceable inputs, so routing must not land without it. Happy to
split it into its own PR if preferred; merge order is fix first either way.
Out of scope
apps/desktop/src/plugins/hermes-bots/group-rounds.tsand the relevance filter (perf(desktop): route a group round to the members it concerns #98610).groups.*API (feat(groups): run Group Chats on the gateway from Desktop #97846, @dokterdok) and host-offline recovery(Let Bots collaborate across gateways #97681). This PR changes only server-side selection; Desktop reaches it once feat(groups): run Group Chats on the gateway from Desktop #97846 merges.
gateway/hosted_room_execution_policy.pyis a capability contract, not the selection seam.room.members_changed.Tests
Red-first for every tier and contract point, in the existing
test_hosted_room_discussion.pyidiom, plus
test_create_room_persists_the_designated_owner_flagin the service suite.🤖 Generated with Claude Code