A crowd that does not agree with whoever spoke last
Three fixes to how a round behaves, found by reading one real round on the live instance rather than by testing it. A member asked "what would you have done differently" answered the person's original question again instead of critiquing what was already there. Fine on a question with one answer; on a request to *make* something it is an invitation. `crowd.turn` now says to respond to what is above and not to re-answer. The model that opened the round, told to write the final answer and take what the others got right, abandoned its own good answer and adopted the newcomer's position with no argument anywhere for why. Both closing fragments now say that an answer is not the worse one for having been written first, and that agreement with no argument behind it is not a reason to change. That second one is not cosmetic: all three answers from the observed round were compiled. The original and the critic's alternative both build; the merged answer that was actually delivered does not. A crowd's failure mode is not looping -- the caps handle that -- it is converging on the last thing said. Third, the reply that opens a round now carries a chip like every other one. It is the single contribution the crowd does not start, so there was nothing to stamp it with until the round began, and a two-model round rendered as an unmarked reply followed by one saying "2 of 2". The stamp is display state and never scheduling state: `crowd.scheduling_state` hides it from everything that decides what happens next, because fed to the scheduler it would inherit the round's clock -- regenerating the opening an hour later would end the round with "out of time" before anybody spoke -- and would hand that reply a member's tools and a member's instruction. And the chip was never translated. It is now, with the count as placeholders rather than three t() calls around one sentence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -134,6 +134,41 @@ def state_of(message: Message | None) -> Turn | None:
|
||||
return None
|
||||
|
||||
|
||||
def is_opening(state: Turn | None) -> bool:
|
||||
"""Whether this state is the main model's opening reply.
|
||||
|
||||
`phase=out, index=0` is **display state and never scheduling state**. The
|
||||
opening reply is not started by the crowd -- the composer starts it, exactly
|
||||
as it starts every other reply, and a round only begins when it *finishes*.
|
||||
Stamping it afterwards is what lets the transcript say `1 of 3` on the bubble
|
||||
that opened the round; before that it was the one contribution with no chip,
|
||||
so a two-model round read as an ordinary reply followed by a crowd.
|
||||
|
||||
Everything that asks "is a round already in progress?" has to skip it, or the
|
||||
stamp changes behaviour it was never meant to touch -- see `scheduling_state`.
|
||||
"""
|
||||
return state is not None and state.phase == PHASE_OUT and state.index == 0
|
||||
|
||||
|
||||
def scheduling_state(message: Message | None) -> Turn | None:
|
||||
"""The round state the scheduler should act on: `state_of`, minus the opening.
|
||||
|
||||
Two things would break if the opening stamp were fed to `next_turn` as real
|
||||
state, and both are silent:
|
||||
|
||||
* **`started_at` would be inherited on a regenerate.** Regenerating the
|
||||
opening reply an hour later would hand `next_turn` an hour-old clock and the
|
||||
round would stop with "out of time" before anybody spoke.
|
||||
* **The once-per-turn gates key off "no state at all"** -- compaction, the
|
||||
title, the unread push. A stamped opening reads as a later speaker, and each
|
||||
of them would be skipped for the turn that is supposed to have them.
|
||||
|
||||
So the stamp is written where the transcript reads it and nowhere else.
|
||||
"""
|
||||
state = state_of(message)
|
||||
return None if is_opening(state) else state
|
||||
|
||||
|
||||
def now_stamp() -> str:
|
||||
return datetime.now(UTC).isoformat()
|
||||
|
||||
@@ -374,9 +409,11 @@ __all__ = [
|
||||
"Turn",
|
||||
"elapsed",
|
||||
"is_newest",
|
||||
"is_opening",
|
||||
"member_speakers",
|
||||
"next_turn",
|
||||
"now_stamp",
|
||||
"scheduling_state",
|
||||
"state_of",
|
||||
"tool_defs",
|
||||
"unreachable_members",
|
||||
|
||||
Reference in New Issue
Block a user