Skip to content

Give the conductor's steps wire forms, and link rows and events - #69

Merged
sanil-23 merged 3 commits into
tinyhumansai:mainfrom
sanil-23:wire-forms
Sep 22, 2026
Merged

sanil-23 merged 3 commits into
tinyhumansai:mainfrom
sanil-23:wire-forms

Conversation

@sanil-23

Copy link
Copy Markdown
Collaborator

Summary

The conductor hands its host steps: turns to run, notes and commits to append, events to show. A host like OpenCompany journals the commits and streams the events to the UI that draws the desk, so these types need wire forms, and two links the UI needs were missing.

  • Wire forms. Turn, Channel, Note, Commit, Refusal, Event and Step now derive serde, internally tagged and snake_case like Utterance and the driver's own payloads. A Step is tagged by step with its fields beside the tag; an Event, Refusal and Channel by kind.
  • A row names its conversation. Commit::conversation is the ask row the conversation is rooted at. It is set for rows said inside a conversation, for desk work a seat lifted out of one (a broadcast or an ask made while talking, which lands on the desk with no thread), and for the row that concludes it to its asker. A host shows an agent-to-agent exchange whole by taking the ask row and every row with its sequence as conversation, wherever they landed.
  • An event names its row. Broadcast, Unplaced, CompletedByBroadcast, Refused, Discharged and Concluded carry at, the sequence the host gave the row they are about. Handoff carries origin, the broadcast row it came from. A refused row is already on the journal, so its event is what marks it refused.

Related issue

None. First of the changes laid out for integrating the conductor into OpenCompany; follows #68.

API or behavior changes

Additive to the public surface, but breaking for exhaustive matches:

  • New public field Commit::conversation.
  • New fields at on six Event variants and origin on Event::Handoff. A host that destructures these variants without .. must add it; the conducted example is updated.
  • The private Kind a commit carries is serialized as purpose so a commit survives the wire whole; it stays opaque to a host.

No change to which steps the conductor emits or in what order.

Validation

Commands actually run, with their outcome:

  • cargo fmt --all -- --check -- clean
  • cargo clippy --all-targets --all-features -- -D warnings -- clean
  • cargo build --all-targets --all-features -- ok
  • cargo test --all-features -- all green; plain cargo test too
  • .github/scripts/assert-pure.sh and assert-openhuman-pin.sh -- clean
  • .github/scripts/check-file-coverage.sh 90 -- every changed file 95% or above
  • RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features -- ok
  • examples/openhuman: clippy, cargo test -- --skip security::preflight (49 passed; the two Docker preflight tests hang without Docker), and the offline proofs on both runners and both desks

Tests

  • conduct/test/wire.rs: the exact JSON of a turn, a note, a commit and an event; every event and refusal variant round-trips alone and inside a step; missing required fields are rejected.
  • conduct/test/links.rs: every row of a conversation carries its root, including the lifted broadcast and the conclusion, and a desk row and the ask itself carry none; Broadcast, CompletedByBroadcast, Unplaced, Discharged, Refused and Concluded name the sequence the host gave their row, and Handoff its origin.

Documentation

  • The module docs for steps and the conduct README describe the wire forms and how a host reads a conversation and an event's row back.

Checklist

  • The change is focused on one logical change
  • No new #[allow(...)], #[ignore], or relaxed lints
  • No secrets, tokens, or .env contents in the diff or the description

🤖 Generated with Claude Code

A host journals the conductor's commits and events and streams them to
whatever draws the desk, so they now have pinned serde forms: internally
tagged and snake_case, like Utterance and the driver's own payloads.

Two links a desk needs were missing. A commit now names the conversation
it belongs to -- a row said inside one, desk work lifted out of one, and
the row that concludes it to its asker -- so an agent-to-agent exchange
can be shown whole wherever its rows landed. And the events about a row
now carry that row's sequence, with a handoff carrying the broadcast it
came from, so a host attaches them to the row instead of inferring it
from order.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tinysweeper

tinysweeper Bot commented Sep 22, 2026

Copy link
Copy Markdown

Tiny Sweeper review

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

State: Changes requested
Priority: critical
Reviewed head: 5d16143dae36
Updated: 1790095465 (Unix time)

Review snapshot

Change surface Files Review signal Count
Production 5 Active findings 3
Tests 6 Noted findings 0
Documentation 1 Resolved findings 24
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

  • high · critique · Define HandOff::origin before referencing it — This line accesses `handoff.origin`, but the `HandOff` definition has not been updated to provide that field in this change. That leaves the driver with an unknown-field compilatio (crates/tinyhivemind\-driver/src/conduct/wave\.rs:402)
  • medium · critique · Preserve the child until its conclusion commit succeeds — The failed `conductor.committed(sequence)` consumes the outstanding commit, and the conductor still removes the conversation before the driver's fold can succeed. As a result, this (crates/tinyhivemind\-driver/src/conduct/test/conversations\.rs:479)
  • critical · security · Update every desk queue producer for the new tuple shape — The desk queue now requires `(String, Utterance, Option<Sequence>)`, but the existing producers in this file still construct the old two-element tuples. The driver will fail to com (crates/tinyhivemind\-driver/src/conduct/wave\.rs:52)

Resolved this pass

  • Add the declared test modules before importing them
  • Update every event constructor for the new required fields
  • Update every desk queue producer for the new tuple shape
  • Define HandOff::origin before referencing handoff.origin
  • Update every event constructor for the new required fields
  • Preserve the child until its conclusion commit succeeds
  • Preserve the child until its conclusion commit succeeds
  • Update every event constructor for the new required fields
  • Define HandOff::origin before referencing handoff.origin
  • Add the declared test modules before importing them
  • Update every event constructor for the new required fields
  • Update every desk queue producer for the new tuple shape
  • Preserve the child until its conclusion commit succeeds
  • Define HandOff::origin before referencing handoff.origin
  • Add the declared test modules before importing them
  • Update every desk queue producer for the new tuple shape
  • Update every event constructor for the new required fields
  • Preserve the child until its conclusion commit succeeds
  • Define HandOff::origin before referencing handoff.origin
  • Add the declared test modules before importing them
  • Update every event constructor for the new required fields
  • Update every desk queue producer for the new tuple shape
  • Preserve the child until its conclusion commit succeeds
  • Define HandOff::origin before referencing handoff.origin

Before merge

  • Address Define HandOff::origin before referencing it (crates/tinyhivemind\-driver/src/conduct/wave\.rs).
  • Address Update every desk queue producer for the new tuple shape (crates/tinyhivemind\-driver/src/conduct/wave\.rs).

How this fits together

flowchart LR
  n0["Commit<br/>changed"]:::changed
  n1["Event<br/>changed"]:::changed
  n2["Note<br/>changed"]:::changed
  n3["Refusal<br/>changed"]:::changed
  n4["Turn<br/>changed"]:::changed
  n5["...ersation_is_desk_work_and_a_dm_is_dropped<br/>changed<br/>1 finding"]:::flagged
  n6["...ersation_is_open_is_refused_and_explained<br/>changed<br/>1 finding"]:::flagged
  n7["...hat_runs_first_and_concludes_to_the_asker<br/>changed<br/>1 finding"]:::flagged
  n8["wave"]:::impacted
  n9["iter"]:::impacted
  n10["...reply_in_a_conversation_is_not_its_answer"]:::impacted
  n11["hive"]:::impacted
  n12["begin_wave"]:::impacted
  n13["Sequence"]:::impacted
  n0 -->|uses| n13
  n1 -->|uses| n3
  n1 -->|uses| n13
  n2 -->|uses| n13
  n3 -->|uses| n13
  n4 -->|uses| n13
  n5 -->|calls| n8
  n5 -->|calls| n11
  n6 -->|calls| n8
  n6 -->|calls| n11
  n7 -->|uses| n0
  n7 -->|calls| n8
  n7 -->|calls| n11
  n7 -->|calls| n13
  n7 -->|tests| n13
  n8 -->|calls| n9
  n8 -->|tests| n9
  n8 -->|calls| n12
  n8 -->|tests| n12
  n10 -->|uses| n0
  n10 -->|uses| n2
  n10 -->|calls| n8
  n10 -->|calls| n9
  n10 -->|tests| n9
  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: Failure
  • Scope reviewed: all assigned evidence
  • Lane summary: Reviewed 2 files; 3 findings. (1 already reported on an earlier push) _The code index is behind this pull request (indexed at `97fdbf79c0b8`), so retrieved context may be out of date._ _3 memory call(s) failed (model: cortex: v1/answer answered 502 Bad Gateway), so this review saw part of what the engine holds._
  • Evidence: crates/tinyhivemind\-driver/src/conduct/wave\.rs — Define HandOff::origin before referencing it
  • Evidence: crates/tinyhivemind\-driver/src/conduct/test/conversations\.rs — Preserve the child until its conclusion commit succeeds

security

  • Conclusion: Failure
  • Scope reviewed: all assigned evidence
  • Lane summary: Reviewed 2 files; 1 finding. _The code index is behind this pull request (indexed at `97fdbf79c0b8`), so retrieved context may be out of date._ _3 memory call(s) failed (model: cortex: v1/answer answered 502 Bad Gateway), so this review saw part of what the engine holds._
  • Evidence: crates/tinyhivemind\-driver/src/conduct/wave\.rs — Update every desk queue producer for the new tuple shape

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: Adds serde wire forms, enriches events with sequence references, tracks conversation provenance on desk rows, and fixes a state-preservation bug where a failed conclusion commit would lose the conversation. All earlier findings are resolved and no new issues are introduced. _The code index is behind this pull request (indexed at `97fdbf79c0b8`), so retrieved context may be out of date._ _3 memory call(s) failed (model: cortex: v1/answer answered 502 Bad Gateway), 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: The pull request adds wire forms, conversation provenance, and event-to-row links to the conductor's steps, and fixes the earlier issues of child preservation on conclusion failure, missing test modules, event fields, and desk tuple shape. All prior findings are resolved and no new problems are introduced. The change is safe to merge. _The code index is behind this pull request (indexed at `97fdbf79c0b8`), so retrieved context may be out of date._ _3 memory call(s) failed (model: cortex: v1/answer answered 502 Bad Gateway), so this review saw part of what the engine holds._

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.031634
  • Tokens: 331250 input · 21699 output · 31188 cached · 1282 embedding
Head State Pass summary
934d6aaa3015 changes requested 5 active finding(s), 1 resolved finding(s) (at 1790093205)
5d16143dae36 changes requested 3 active finding(s), 24 resolved finding(s) (at 1790095465)

tinysweeper 0.1.0

@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown

Warning

Review limit reached

  • Run on-demand review

This review includes 12 billable files and costs up to $3.00.

Or wait 41 minutes for your next included review.

Check out review usage here.

View limit details

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

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 36def375-08f1-428d-87ad-012a57a41df5

📥 Commits

Reviewing files that changed from the base of the PR and between a1319ca and 5d16143.

📒 Files selected for processing (12)
  • crates/tinyhivemind-driver/src/conduct/README.md
  • crates/tinyhivemind-driver/src/conduct/mod.rs
  • crates/tinyhivemind-driver/src/conduct/steps.rs
  • crates/tinyhivemind-driver/src/conduct/test/conversations.rs
  • crates/tinyhivemind-driver/src/conduct/test/desk.rs
  • crates/tinyhivemind-driver/src/conduct/test/links.rs
  • crates/tinyhivemind-driver/src/conduct/test/mod.rs
  • crates/tinyhivemind-driver/src/conduct/test/support.rs
  • crates/tinyhivemind-driver/src/conduct/test/wire.rs
  • crates/tinyhivemind-driver/src/conduct/wave.rs
  • crates/tinyhivemind-driver/src/driver/brief.rs
  • examples/openhuman/src/bin/conducted.rs

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

@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.

Requesting changes: 2 lane(s) blocking, worst finding is critical.

Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.

             $0.0890 · 1,057,957 in / 40,070 out · 72,162 cached (7%) · ladder/vectors, gpt-5.6-luna, deepseek-v4-flash · 1,276 embedded
critique:    $0.0456 · 556,637 in   / 14,083 out · 31,180 cached (6%) · gpt-5.6-luna, deepseek-v4-flash
security:    $0.0418 · 439,065 in   / 8,034 out  · 24,598 cached (6%) · gpt-5.6-luna
tests:       $0.0007 · 27,972 in    / 5,211 out  · 0 cached (0%)      · deepseek-v4-flash
description: $0.0006 · 18,586 in    / 8,896 out  · 1,024 cached (6%)  · deepseek-v4-flash

mod conversations;
mod desk;
mod door;
mod links;

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 critical critique confident

Add the declared test modules before importing them

Rust resolves these declarations as links.rs/links/mod.rs and wire.rs/wire/mod.rs, but the repository contains neither module. As a result, compiling the test module fails with missing-file errors. Add the corresponding files, or remove these declarations until the modules are present.

[RULE] missing-module ·

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Both files are in this PR: crates/tinyhivemind-driver/src/conduct/test/links.rs and test/wire.rs are added by the same commit that declares them (the diff lists them as new files). The Rust job on this head compiles and runs them: 24 conductor tests pass, 13 of them in these two modules.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Resolved — the reply explains why it is not a problem (advisory), as of 5d16143.

If this is wrong, reopen the conversation and say so; the finding will be re-raised on the next push if it still reproduces.

seat: String,
/// Who took it.
to: Vec<String>,
/// The broadcast row.

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 critical critique confident

Update every event constructor for the new required fields

Adding at as a required field to event variants changes every Event::Broadcast, Unplaced, CompletedByBroadcast, Handoff, Refused, Discharged, and Concluded construction site. The unchanged conductor code still constructs these variants using their previous field sets, so the crate will fail to compile until each caller supplies the corresponding sequence (or the field is made optional/defaulted).

[RULE] build-break ·

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Every construction site is updated in this commit: the seven sites in conduct/wave.rs (Broadcast, Unplaced, CompletedByBroadcast, Handoff, both Refused arms and Discharged, plus Concluded), and the example's matches take ... The Rust job on this head compiles the crate and the example, so no site was missed.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Resolved — the reply explains why it is not a problem (advisory), as of 5d16143.

If this is wrong, reopen the conversation and say so; the finding will be re-raised on the next push if it still reproduces.

pub(super) desk: Vec<(String, Utterance)>,
/// What desk turns said, and what thread turns said to the desk:
/// `(seat, utterance, the conversation it was lifted out of)`.
pub(super) desk: Vec<(String, Utterance, Option<Sequence>)>,

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 critical critique confident

Update every desk queue producer for the new tuple shape

Changing desk from (String, Utterance) to (String, Utterance, Option<Sequence>) makes every existing push or assignment of the old two-element tuple type-check incorrectly. The unchanged producer(s) must be updated to include the conversation provenance, using None for ordinary desk turns and the originating root for lifted conversation work; otherwise the driver crate fails to compile.

[RULE] compile-error ·

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The one producer, Conductor::record in conduct/mod.rs, is updated in this commit: it pushes None for a desk turn and Some(root) for a broadcast or ask lifted out of a conversation, and conduct/test/links.rs asserts both. The Rust job on this head compiles.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Resolved — the reply explains why it is not a problem (advisory), as of 5d16143.

If this is wrong, reopen the conversation and say so; the finding will be re-raised on the next push if it still reproduces.

@@ -443,6 +459,7 @@ impl<'a, A: BoundAgent> Conductor<'a, A> {
let Some(child) = self.children.remove(&root) else {

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

Preserve the child until its conclusion commit succeeds

The conversation is removed before apply_committed can validate and apply the conclusion. If that fold fails, the method returns an error while the desk state remains unchanged, but the child and its pending conclusion are already gone, so the conversation cannot be retried or concluded later. Borrow or clone the child first, apply the transition successfully, and remove it only after the fold succeeds.

[RULE] state-preservation-on-error ·

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in the follow-up commit on this branch: the fold is applied first, and the conversation is removed only once it accepts the conclusion. A refused conclusion now leaves the conversation open and the next wave concludes it at a row the host gives properly; a_conclusion_the_fold_refuses_leaves_the_conversation_to_conclude_later covers it by reporting a sequence the episode already holds.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Resolved — the review agent found this finding fixed in the new code, as of 5d16143.

If this is wrong, reopen the conversation and say so; the finding will be re-raised on the next push if it still reproduces.

sanil-23 and others added 2 commits September 22, 2026 22:02
The child was removed before its conclusion was applied, so a conclusion
the fold refused left the desk unchanged and the conversation gone, with
no way to conclude it later. The fold goes first now; a refused
conclusion leaves the conversation open, and the next wave concludes it
at a row the host gives properly.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@sanil-23
sanil-23 merged commit 1f2da53 into tinyhumansai:main Sep 22, 2026
5 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.

Requesting changes: 2 lane(s) blocking, worst finding is critical.

Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.

             $0.0316 · 331,250 in / 21,699 out · 31,188 cached (9%)  · ladder/vectors, gpt-5.6-luna, deepseek-v4-flash · 1,282 embedded
critique:    $0.0138 · 126,446 in / 8,457 out  · 14,280 cached (11%) · gpt-5.6-luna, deepseek-v4-flash
security:    $0.0163 · 136,969 in / 5,586 out  · 14,860 cached (11%) · gpt-5.6-luna
tests:       $0.0006 · 29,223 in  / 2,479 out  · 1,024 cached (4%)   · deepseek-v4-flash
description: $0.0004 · 19,841 in  / 1,396 out  · 1,024 cached (5%)   · deepseek-v4-flash

self.wave.event(Event::Handoff {
to: agent_id.clone(),
from: handoff.from.clone(),
origin: handoff.origin,

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 high critique confident

Define HandOff::origin before referencing it

This line accesses handoff.origin, but the HandOff definition has not been updated to provide that field in this change. That leaves the driver with an unknown-field compilation error. Add the origin field to HandOff and populate it at every construction site, or remove this access if provenance is not part of the handoff contract.

[RULE] missing-field ·

assert!(!conductor.finished(), "the conversation is still open");

// The next wave concludes it, at a row the host gives properly.
let later = wave(&mut conductor, &journal, &[]).expect("wave");

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

Preserve the child until its conclusion commit succeeds

The failed conductor.committed(sequence) consumes the outstanding commit, and the conductor still removes the conversation before the driver's fold can succeed. As a result, this follow-up wave has no child left to retry or conclude, so the new regression test fails (or the conversation is permanently lost). Apply the conclusion first and remove the child only after the transition succeeds, preserving the outstanding conversation when the fold rejects the supplied sequence.

[RULE] state-preservation-on-error ·

pub(super) desk: Vec<(String, Utterance)>,
/// What desk turns said, and what thread turns said to the desk:
/// `(seat, utterance, the conversation it was lifted out of)`.
pub(super) desk: Vec<(String, Utterance, Option<Sequence>)>,

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 critical security confident

Update every desk queue producer for the new tuple shape

The desk queue now requires (String, Utterance, Option<Sequence>), but the existing producers in this file still construct the old two-element tuples. The driver will fail to compile until every producer supplies the conversation provenance, using None for ordinary desk work and the originating root for lifted conversation work.

[RULE] compile-error ·

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