Skip to content

Refuse a second ask to a seat already being waited on - #76

Merged
sanil-23 merged 1 commit into
tinyhumansai:mainfrom
sanil-23:feat/no-second-ask-while-waiting
Sep 23, 2026
Merged

sanil-23 merged 1 commit into
tinyhumansai:mainfrom
sanil-23:feat/no-second-ask-while-waiting

Conversation

@sanil-23

Copy link
Copy Markdown
Collaborator

Summary

A seat waiting on an answer is still turned on the desk. That turn is deliberate — it is what keeps the episode live while the seat it asked is parked — but look at what the turn is given. This is a real brief, in full, from a hosted desk:

## New desk messages
@strategist: The operator says you hold the sequencing constraints for a Beta Corp
rollout. I need to know what they are — dependencies, prerequisites, timeline
restrictions, resource blockers ...

## Conversations still in progress
### With @implementation_planner (thread 97) -- in progress
@strategist: The operator says you hold the sequencing constraints for a Beta Corp
rollout. I need to know what they are — ...

Your assignment was made at sequence 88. Record your part with `complete_episode`:
its message is your finding. Hand what is another seat's on with `broadcast`. A reply
that calls no tool records nothing. You cannot complete until your conversation with
@implementation_planner concludes.

Its own unanswered question, twice. complete_episode refused until the conversation concludes. broadcast with nothing to carry. And the contract closing with "a reply that calls no tool records nothing."

Every door is shut except ask, and the model is told that silence counts for nothing. So it asks again — verbatim. Sequence 104 is character-for-character sequence 97.

Each repeat opens a second conversation with the same seat, which the conductor must then track and conclude separately. In that episode one question became three conversations under one root, each with its own concluded our conversation (thread 97/104/113) row.

The record already refuses an ask inside a conversation — one line above this change, for the same reason at the other end of the same wait. This extends it to the asker's side.

Related issue

None — found while running the hosted runner under OpenCompany and reading back what each seat had actually been prompted with.

API or behavior changes

Additive, non-breaking:

  • new EpisodeTools::awaiting(seat, seats) — a per-turn snapshot, exactly like window. The ledger that knows who a seat awaits is the driver's and the record cannot read it, so the host hands it over; open_turn passes what EpisodeBrief::awaiting already computed.
  • new refusal: ask naming a seat this one is already waiting on. Asking a different seat is untouched, and the pair is reachable again once the answer lands.

Dispatch is deliberately unchanged — adding a field to it would have broken every construction site in the repo and every consumer, for state that is per-turn rather than per-dispatch.

Validation

  • cargo fmt --all -- --check — exit 0
  • cargo clippy --all-targets --all-features -- -D warnings — exit 101, pre-existing on main: missing documentation for a method at crates/tinyhivemind-openhuman/src/hosted/mod.rs:103 (tool_prefix). Verified by running the same command on unmodified 8cc4dc9c, which fails identically. cargo clippy -p tinyhivemind-tools --all-targets -- -D warnings is exit 0.
  • cargo build --all-targets --all-features — exit 0
  • cargo test --all-features — exit 0, 36 targets ok, 0 failures

The driver suite passing is the part worth noting: the waiting seat's desk turn is still proposed, still nudged, still the liveness mechanism. This refuses one duplicate call, not the turn.

Tests

a_second_ask_to_a_seat_already_being_waited_on_is_refused in crates/tinyhivemind-tools/src/tools/tests, covering four states in sequence: the first ask is taken; a second to the same seat is refused and records nothing, so no second conversation opens; a different seat is still reachable; and once the host clears the list the pair is reachable again.

Documentation

EpisodeTools::awaiting documents why the state arrives from the host rather than being read. The refusal site carries the observed failure — one question, three conversations — so the next reader knows what the branch is defending against rather than only what it does.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 27 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

This review ran on the open-source allowance, not this organization's plan, because the pull request author doesn't have an assigned seat. Waiting won't change this — ask an organization admin to assign them a seat, or add seats in Billing if every seat is already assigned, then retry.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: b72ee636-d1a6-4113-af4f-70c66d361f7a

📥 Commits

Reviewing files that changed from the base of the PR and between b628543 and ddaf106.

📒 Files selected for processing (3)
  • crates/tinyhivemind-openhuman/src/episode/mod.rs
  • crates/tinyhivemind-tools/src/tools/mod.rs
  • crates/tinyhivemind-tools/src/tools/test.rs

Comment @coderabbitai help to get the list of available commands.

@tinysweeper

tinysweeper Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Tiny Sweeper reviewed this change across 6 lane(s) and found 1 active actionable finding(s). Detailed lane evidence and any incomplete work are listed below.

State: Ready for maintainer review
Priority: medium
Reviewed head: ddaf106e68c8
Updated: 1790202967 (Unix time)

Review snapshot

Change surface Files Review signal Count
Production 2 Active findings 2
Tests 1 Noted findings 0
Documentation 0 Resolved findings 0
Configuration 0 Pending checks/questions 0

Completeness: Complete
Test assessment: No supported feature-to-test mapping was available; this does not mean tests are absent or passed.

What changed

The review could not produce a supported behavioral summary; inspect the cited changed surface and lane details below.

Features

None identified with supported citations.

Tests

No supported feature-to-test mapping was produced. Test execution is not inferred.

Findings

  • medium · security · Reject duplicate asks made in the same turn — `awaiting` is only refreshed from the driver's snapshot before the turn begins. After the first `ask` is accepted, this snapshot is not updated, so a second `ask` to the same recip (crates/tinyhivemind\-tools/src/tools/mod\.rs:292)
  • medium · description · Reject duplicate asks made in the same turn — The `awaiting` map is only refreshed by the host before each turn. If a seat calls `ask` twice in the same turn to the same recipient, the first ask does not update the map, so the (\(pull request description\))

Before merge

None.

How this fits together

flowchart LR
  n0["EpisodeTools<br/>changed<br/>1 finding"]:::flagged
  n1["...ad_returns_the_window_and_records_nothing<br/>changed"]:::changed
  n2["register"]:::impacted
  n3["dispatch"]:::impacted
  n4["Dispatch"]:::impacted
  n5["stand_up"]:::impacted
  n6["Journal"]:::impacted
  n7["drive"]:::impacted
  n0 -->|uses| n4
  n1 -->|calls| n2
  n1 -->|tests| n2
  n1 -->|calls| n3
  n1 -->|tests| n3
  n2 -->|uses| n4
  n3 -->|uses| n4
  n5 -->|uses| n0
  n7 -->|uses| n6
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading
Agent review details

critique

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change adds a host-provided waiting-seat snapshot and refuses asks against that snapshot, but it does not prevent duplicate asks within the same turn. The prior medium-severity concern therefore still stands, so this is not safe to merge. (1 already reported on an earlier push) _The code index is behind this pull request (indexed at `cd23c7fed5b0`), so retrieved context may be out of date._ _5 memory call(s) failed (model: cortex: v1/recall: timed out after 10s), so this review saw part of what the engine holds._

security

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change correctly rejects duplicate asks carried over from earlier turns, but it does not reject duplicates made within the same turn. This leaves the previously reported medium-severity issue unresolved, so it is not safe to merge yet. _The code index is behind this pull request (indexed at `cd23c7fed5b0`), so retrieved context may be out of date._ _5 memory call(s) failed (model: cortex: v1/recall: timed out after 10s), so this review saw part of what the engine holds._
  • Evidence: crates/tinyhivemind\-tools/src/tools/mod\.rs — Reject duplicate asks made in the same turn

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change introduces an `awaiting` snapshot to prevent duplicate `ask` calls to the same seat, but does not update that snapshot after an accepted ask, so a second ask to the same seat made within the same turn after the first is accepted still goes through. This leaves the earlier finding unfixed. (1 finding discarded for not matching a changed line) (1 earlier finding(s) still open) _The code index is behind this pull request (indexed at `cd23c7fed5b0`), so retrieved context may be out of date._ _5 memory call(s) failed (model: cortex: v1/recall: timed out after 10s), so this review saw part of what the engine holds._

commits

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: Nothing sensitive found in what this pull request commits.

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: This pull request adds a snapshot-based guard against a seat asking the same recipient twice across turns, using a new `awaiting` method and check. The implementation is sound and targeted, but a pre-existing race condition where duplicate asks within the same turn are not prevented remains. The change is safe to merge as it does not introduce regressions. _The code index is behind this pull request (indexed at `cd23c7fed5b0`), so retrieved context may be out of date._ _5 memory call(s) failed (model: cortex: v1/recall: timed out after 10s), so this review saw part of what the engine holds._
  • Evidence: \(pull request description\) — Reject duplicate asks made in the same turn

e2e

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: No end-to-end harness in this repository: no e2e test files and no e2e workflow.
Evidence and run details
  • Models: ladder/vectors, gpt-5.6-luna, deepseek-v4-flash
  • Spend: $0.007366
  • Tokens: 161916 input · 17836 output · 21953 cached · 446 embedding
Head State Pass summary
471897e6b334 ready for maintainer review 1 active finding(s), 0 resolved finding(s) (at 1790202257)
ddaf106e68c8 ready for maintainer review 2 active finding(s), 0 resolved finding(s) (at 1790202967)

tinysweeper 0.1.0

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinysweeper found nothing blocking. Approving.

             $0.0062 · 133,241 in / 9,853 out · 7,606 cached (6%)  · ladder/vectors, gpt-5.6-luna, deepseek-v4-flash · 446 embedded
critique:    $0.0028 · 56,246 in  / 2,255 out · 2,133 cached (4%)  · gpt-5.6-luna, deepseek-v4-flash
security:    $0.0027 · 46,664 in  / 1,821 out · 1,889 cached (4%)  · gpt-5.6-luna
tests:       $0.0003 · 17,752 in  / 1,032 out · 2,048 cached (12%) · deepseek-v4-flash
description: $0.0002 · 8,762 in   / 2,553 out · 1,536 cached (18%) · deepseek-v4-flash

//
// Refusing is the same rule the thread turn already applies
// one line up, at the other end of the same wait.
if self.awaits(seat, to) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium critique confident

Reject duplicate asks made in the same turn

awaits only checks the snapshot supplied before the turn began. If a seat calls ask for the same recipient twice during one turn, the first call is recorded but does not update awaiting, so the second call passes this check and opens a second conversation. Enforce the outstanding pair against calls already accepted in the current turn as well as the snapshot.

[RULE] duplicate-request-race ·

A seat waiting on an answer is still turned on the desk. That turn is
deliberate -- it is what keeps the episode live while the seat it asked
is parked -- but look at what it is given. Its brief is its own
unanswered question, echoed back under "New desk messages" and again
under "Conversations still in progress"; `complete_episode` is refused
until the conversation concludes; `broadcast` has nothing to carry; and
the standing contract closes with "a reply that calls no tool records
nothing".

Every door is shut except `ask`, and the model is told that silence
counts for nothing. So it asks again -- and each repeat opens a *second*
conversation with the same seat, which the conductor must then track and
conclude separately. Observed on a hosted desk: one question re-issued
verbatim, one root, three conversations, three conclusions.

The record already refuses an `ask` inside a conversation, one line
above this, for the same reason at the other end of the same wait. This
extends it to the asker's side: a seat may not ask a seat it is already
waiting on. Asking someone *else* is untouched, and the pair is
reachable again once the answer lands.

The ledger that knows who a seat awaits is the driver's, and the record
cannot read it, so `EpisodeTools::awaiting` takes it per turn exactly as
`window` takes the rows `read` may return. `open_turn` hands over what
the brief already computed.
@sanil-23
sanil-23 force-pushed the feat/no-second-ask-while-waiting branch from 471897e to ddaf106 Compare September 23, 2026 22:32
@sanil-23
sanil-23 merged commit f3c3af9 into tinyhumansai:main Sep 23, 2026
6 checks passed

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinysweeper found nothing blocking. Approving.

             $0.0074 · 161,916 in / 17,836 out · 21,953 cached (14%) · ladder/vectors, gpt-5.6-luna, deepseek-v4-flash · 446 embedded
critique:    $0.0041 · 88,580 in  / 2,979 out  · 14,591 cached (16%) · gpt-5.6-luna, deepseek-v4-flash
security:    $0.0023 · 44,789 in  / 1,251 out  · 3,778 cached (8%)   · gpt-5.6-luna
tests:       $0.0004 · 16,723 in  / 4,283 out  · 2,048 cached (12%)  · deepseek-v4-flash
description: $0.0004 · 7,717 in   / 6,558 out  · 1,536 cached (20%)  · deepseek-v4-flash

//
// Refusing is the same rule the thread turn already applies
// one line up, at the other end of the same wait.
if self.awaits(seat, to) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium security confident

Reject duplicate asks made in the same turn

awaiting is only refreshed from the driver's snapshot before the turn begins. After the first ask is accepted, this snapshot is not updated, so a second ask to the same recipient during the same turn still passes self.awaits and opens another conversation. Check accepted asks already recorded in the current turn as well as the snapshot, or update the outstanding set atomically when accepting an ask.

[RULE] duplicate-request-race ·

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant