-
Notifications
You must be signed in to change notification settings - Fork 2
Transcript projection
A shared desk has one transcript and several readers. Projection is the fold that turns that transcript into what one participant sees.
The host this library was extracted from projected a session into a (role, content) pair, which discards the author of every reply. On a shared desk the
consequence is that agent B reads agent A's replies as B's own prior turns. A
system notice, a workflow report, and a real teammate all arrive looking
identical, and an agent that cannot tell its own words from a colleague's
cannot coordinate with that colleague.
SessionMessage carries attribution instead:
pub enum SessionAuthor {
Operator,
Person { id: String, label: String },
Agent { id: String, label: String },
System { kind: String, label: String },
}A label is captured with the row rather than looked up later, so a rename does not rewrite history.
The host owns the log and lends it through one port, implemented in
src/session/:
pub trait SessionLog: Send + Sync {
fn read_before(&self, before: Option<Sequence>, limit: usize) -> SessionFuture<'_>;
}Implementations return rows newest first and treat before as an exclusive
bound. The trait is object safe and picks no executor, so the host keeps its
choice of runtime.
project_session walks backwards through pages until it has a window of
qualifying messages, then hands them back in chronological order. Three
constants bound it:
| constant | value | meaning |
|---|---|---|
SESSION_WINDOW |
30 | qualifying messages requested for a turn |
PAGE_SIZE |
512 | maximum raw rows in one host read |
SCAN_LIMIT |
2048 | maximum raw rows inspected in one projection |
Hitting SCAN_LIMIT is a successful partial projection, not an error. A busy
channel with one relevant thread in it should still produce a turn.
A desk is not flat, and the walk knows it. A thread-scoped query returns the root and its direct replies. A channel-level query returns every root and each root's first reply, so an agent reading a desk sees answers rather than a run of unanswered questions, and still never reads a second thread's interior. See Threads.
Page validation is strict. A host that violates ordering, uniqueness, size, or cursor contracts gets a typed error rather than a plausible-looking history.
The second defect: the transcript was re-read only when an agent rebound to a different chat. An agent taking two consecutive turns in one thread therefore missed a peer's reply that landed between them, which on a shared desk is most of the point.
sharing
replaces that gate with a
watermark
the caller owns.
let mut state = initialized_state(conversation, watermark);
match prepare_delta(&log, &state, &query).await? {
SharingPlan::Delta(delta) => { /* apply, then commit the state */ }
SharingPlan::Reinitialize { reason } => { /* full initialization */ }
}A delta carries only what the agent has not seen. Reinitialization is requested for three named reasons, and naming them is the point: a host that has to fall back to a full re-seed should know which of these happened.
| reason | when |
|---|---|
ConversationChanged |
the requested, bound, or stored conversation differs |
GapTooLarge |
the watermark was not crossed inside the bounded scan |
WatermarkUnavailable |
the host log ended before reaching the watermark |
State is returned, never applied. The caller commits it after the turn is
durably appended, so a crash between the two leaves the
watermark
behind rather than ahead. note_present records a later row the host already
accepted without moving the watermark, bounded by PRESENT_SET_LIMIT at 64.
tinyhivemind is GPL-3.0-only. Built by @senamakel.
Start here
The algebra
- Shared medium
- Desks and rosters
- Mentions
- Cross-desk referral
- Transcript projection
- Threads
- Recall
- Responder ladder
Hive mechanics
Working on it
Reference