application tier

application-conversation

Both halves of a storefront chat around domain-conversation-engine. Inbound: windows a thread to the turn being answered, resolves history down to referents so historical utterances never reach intent selection, projects catalogue values to typed tokens before generic DLP masking, and admits only registry-valid classifier output as candidates. Outbound: stores a turn and the buttons it offered as one transaction, and redeems a tapped button exactly once. It detects nothing and masks nothing; it owns one table, conversation_offers, because a capability's writer and its redeemer are both here.

Both halves of a storefront chat around domain-conversation-engine. Inbound: windows a thread to the turn being answered, resolves history down to referents so historical utterances never reach intent selection, projects catalogue values to typed tokens before generic DLP masking, and admits only registry-valid classifier output as candidates. Outbound: stores a turn and the buttons it offered as one transaction, and redeems a tapped button exactly once. It detects nothing and masks nothing; it owns one table, conversation_offers, because a capability's writer and its redeemer are both here.

Tierapplication
Rolepipeline
Pathcrates/application/conversation
Edition2021
Targetsapplication_conversation, pg_end_to_end, pg_offer_claim, pg_thread_store
Public items115 across 11 modules
Tests149

What it is for

Sprint 3.114: the two halves of a storefront chat that surround the decision engine — everything before a model sees a message, and everything after the engine has spoken.

Inbound (Story 2). A buyer types a question. Before any model sees it, this crate narrows the thread to the turn being answered, consumes the history down to referents, projects catalogue values to typed tokens, masks what is left, and checks whatever the classifier returns against the registry. Only then does domain_conversation_engine::respond decide what to say.

window  ->  resolve  ->  reduce  ->  guard  ->  classify  ->  admit  ->  respond

Outbound (Story 3). What the engine concluded becomes rows in the thread: the buyer's turn, the reply, and — when the bot declines — one fixed phrase for the buyer and a full account for the operator, on a conversation that is assigned rather than merely annotated. Buttons are stored as capabilities the server issued, and a tap redeems one by id.

plan_writes  ->  store  ->  deliver        tap -> claim -> guard -> render

The two halves live together because the offer's writer and its reader are the same crate: a button is made on the way out and redeemed on the way in, and splitting them would put the capability's two ends in different places.

What this crate does not do. It detects nothing and masks nothing: screening is infrastructure-dlp-detect, the decision is domain-conversation-engine, the thread's tables belong to infrastructure-communication, and the model is whichever infrastructure-ai provider the host supplies. What it owns is the order those run in, and the two boundaries that make the order matter — reduce::resolve, where history stops being text, and admit::admit, where a model's answer stops being a claim.

It owns one table. migrations/0001_conversation_offers.sql creates conversation_offers, the capabilities a reply issued, because the button's writer and its redeemer are this crate and nothing else reads them. That is the whole of the schema it owns, and it stopped being schema-free in sprint 3.117 unit 3.

The effectful half (sprint 3.117). store holds the ports — a store::ThreadStore that puts one turn in the thread atomically and a store::Delivery that can only be handed a row already stored — plus store::store_then_deliver, which is the ordering rule as a function rather than as a sentence. postgres, behind the feature of that name, is the adapter over the schema owner's own functions and the offer claim. completion, behind the feature of that name, is classify: one model request per message, with the deadline and the byte ceiling that make a transport failure a typed error rather than a silent abstention. Everything above this paragraph compiles and is tested with neither.

On `domain-transcript-shard`. The inherited plan named it as this path's decomposition step. It is not used here, and the sprint doc records why: its Shard carries opaque speaker IDs and a court-transcript statement taxonomy and no words at all, so a classifier handed one cannot separate "is this solves a problem a chat does not have — a chat turn is labelled by role, so prose, and the prose is masked. Reusing it here would have been a dependency that satisfied a table rather than a need.

Capabilities

admit (other)

The enforcement boundary between a model and the engine.

Item
fn admit(raw : & RawCandidate, registry : & IntentRegistry,) -> Result <Vec <Candidate>, AdmitError>

AdmitError

The enforcement boundary between a model and the engine.

Item
pub enum AdmitError

RawCandidate

The enforcement boundary between a model and the engine.

Item
pub struct RawCandidate
RawCandidate :: fn new(intent : impl Into <String>, score : f32) -> Self

catalogue (other)

Filling a stored phrase from a catalogue record, without the record's

Item
pub const DEFAULT_MAX_SLOT_LEN: usize

BuyerAvailability

Filling a stored phrase from a catalogue record, without the record's

Item
pub enum BuyerAvailability
BuyerAvailability :: fn of(status : CatalogItemStatus) -> Option <Self>
BuyerAvailability :: fn catalogue_key(self) -> & 'static str

BuyerItemView

Filling a stored phrase from a catalogue record, without the record's

Item
pub struct BuyerItemView<'a>

BuyerItemView<'_>

Filling a stored phrase from a catalogue record, without the record's

Item
BuyerItemView<'_> :: fn slot(& self, name : & str) -> Option <String>
BuyerItemView<'_> :: fn subjects_in(& self, text : & str) -> Vec <String>

BuyerItemView<'a>

Filling a stored phrase from a catalogue record, without the record's

Item
BuyerItemView<'a> :: fn of(item : & 'a CatalogItem, viewer : & Viewer) -> Option <Self>
BuyerItemView<'a> :: fn with_max_slot_len(mut self, max : usize) -> Self
BuyerItemView<'a> :: fn slot_names() -> & 'static & 'static str

Viewer

Filling a stored phrase from a catalogue record, without the record's

Item
pub struct Viewer
Viewer :: fn may_see(& self, tier : Visibility) -> bool

classify (other)

The Classifier over a one-shot completion provider.

Item
pub const DEFAULT_DEADLINE: Duration
pub const DEFAULT_MAX_RESPONSE_BYTES: usize

CompletionClassifier

The Classifier over a one-shot completion provider.

Item
pub struct CompletionClassifier
CompletionClassifier :: fn fmt(& self, f : & mut std::fmt::Formatter <'_>) -> std::fmt::Result
CompletionClassifier :: fn new(provider : Arc <dyn AiProvider>, registry : & IntentRegistry) -> Self
CompletionClassifier :: fn with_deadline(mut self, deadline : Duration) -> Self
CompletionClassifier :: fn with_max_response_bytes(mut self, bytes : usize) -> Self
CompletionClassifier :: fn with_model(mut self, model : impl Into <String>) -> Self
CompletionClassifier :: fn system_prompt(& self) -> & str
CompletionClassifier :: async fn classify(& self, input : & ClassifierInput,) -> Result <Vec <RawCandidate>, ClassifierError>

guard (other)

Whether the conversation is in a state where an intent may be acted on.

Item
pub const DEFAULT_MAX_SNAPSHOT_AGE: Duration

ConversationGuard

Whether the conversation is in a state where an intent may be acted on.

Item
pub struct ConversationGuard
ConversationGuard :: fn new(snapshot : WindowSnapshot, policy : IntentPolicy, registered : & & str,) -> Result <Self, UnclassifiedIntents>
ConversationGuard :: fn with_max_age(mut self, max_age : Duration) -> Self
ConversationGuard :: fn is_stale(& self, now : Instant) -> bool
ConversationGuard :: fn permits(& self, intent : & str) -> GuardVerdict

HoldFact

Whether the conversation is in a state where an intent may be acted on.

Item
pub struct HoldFact
HoldFact :: fn consumes_capacity(& self, now : DateTime <Utc>) -> bool
HoldFact :: fn held_by(& self, party : & str, now : DateTime <Utc>) -> bool

IntentPolicy

Whether the conversation is in a state where an intent may be acted on.

Item
pub struct IntentPolicy
IntentPolicy :: fn new() -> Self
IntentPolicy :: fn with(mut self, intent : impl Into <String>, requires : Requires) -> Self
IntentPolicy :: fn requirement(& self, intent : & str) -> Option <Requires>

Requires

Whether the conversation is in a state where an intent may be acted on.

Item
pub enum Requires

UnclassifiedIntents

Whether the conversation is in a state where an intent may be acted on.

Item
pub struct UnclassifiedIntents

WindowSnapshot

Whether the conversation is in a state where an intent may be acted on.

Item
pub struct WindowSnapshot
WindowSnapshot :: fn consumed(& self) -> u32
WindowSnapshot :: fn permitted(& self) -> u32
WindowSnapshot :: fn has_room(& self) -> bool
WindowSnapshot :: fn party_holds(& self) -> bool

offer (other)

A button is a capability the server issued, and a tap redeems it.

Item
fn verdict(offer : Option <& Offer>, at : & Redemption) -> OfferVerdict
fn buyer_handoff_text() -> & 'static str

Offer

A button is a capability the server issued, and a tap redeems it.

Item
pub struct Offer

OfferId

A button is a capability the server issued, and a tap redeems it.

Item
pub struct OfferId
OfferId :: fn new(id : impl Into <String>) -> Self
OfferId :: fn as_str(& self) -> & str

OfferVerdict

A button is a capability the server issued, and a tap redeems it.

Item
pub enum OfferVerdict

Redemption

A button is a capability the server issued, and a tap redeems it.

Item
pub struct Redemption

pipeline (other)

The order the steps run in, and what happens when one of them says no.

Item
async fn handle_message(turns : & Turn, cfg : & Inbound <'_>) -> Outcome
async fn handle_tap(offer_id : & OfferId, at : & Redemption, offers : & dyn OfferStore, templates : & Template, subject : & dyn SlotSource, transition_guard : & dyn TransitionGuard,) -> Outcome

ClaimOutcome

The order the steps run in, and what happens when one of them says no.

Item
pub enum ClaimOutcome

Classifier

The order the steps run in, and what happens when one of them says no.

Item
pub trait Classifier

ClassifierError

The order the steps run in, and what happens when one of them says no.

Item
pub enum ClassifierError
ClassifierError :: fn fmt(& self, f : & mut fmt::Formatter <'_>) -> fmt::Result

Handoff

The order the steps run in, and what happens when one of them says no.

Item
pub enum Handoff

Inbound

The order the steps run in, and what happens when one of them says no.

Item
pub struct Inbound<'a>

OfferStore

The order the steps run in, and what happens when one of them says no.

Item
pub trait OfferStore

Outcome

The order the steps run in, and what happens when one of them says no.

Item
pub enum Outcome

postgres (other)

The crate::store::ThreadStore over infrastructure-communication's tables.

Item
async fn store_turn_in(conn : & mut PgConnection, conversation_id : Uuid, writes : & ThreadWrite, operator : Option <Uuid>, parties : & TurnParties,) -> Result <StoredTurn, StoreError>
async fn thread_rows(pool : & PgPool, conversation_id : Uuid,) -> Result <Vec <StoredMessage>, StoreError>
async fn store_turn_and_offers(pool : & PgPool, conversation_id : Uuid, writes : & ThreadWrite, operator : Option <Uuid>, parties : & TurnParties, terms : & OfferTerms, computed_under : Option <u64>,) -> Result <TurnCommit, StoreError>
async fn take_over(pool : & PgPool, conversation_id : Uuid, operator : Uuid,) -> Result <u64, StoreError>

NewOffer

The crate::store::ThreadStore over infrastructure-communication's tables.

Item
pub struct NewOffer
async fn insert_offer(pool : & PgPool, offer : & NewOffer) -> Result <(), StoreError>

OfferTerms

The crate::store::ThreadStore over infrastructure-communication's tables.

Item
pub struct OfferTerms
async fn insert_offers_in(conn : & mut PgConnection, conversation_id : Uuid, writes : & ThreadWrite, stored : & StoredTurn, terms : & OfferTerms,) -> Result <Vec <OfferId>, StoreError>

PostgresOfferStore

The crate::store::ThreadStore over infrastructure-communication's tables.

Item
pub struct PostgresOfferStore
PostgresOfferStore :: fn new(pool : PgPool) -> Self
PostgresOfferStore :: async fn claim(& self, id : & OfferId, at : & Redemption) -> ClaimOutcome

PostgresThreadStore

The crate::store::ThreadStore over infrastructure-communication's tables.

Item
pub struct PostgresThreadStore
PostgresThreadStore :: fn new(pool : PgPool) -> Self
PostgresThreadStore :: async fn store_turn(& self, conversation_id : Uuid, writes : & ThreadWrite, operator : Option <Uuid>, parties : & TurnParties,) -> Result <StoredTurn, StoreError>
PostgresThreadStore :: async fn mark_delivered(& self, message_id : Uuid) -> Result <(), StoreError>
PostgresThreadStore :: async fn mark_delivery_failed(& self, message_id : Uuid, note : & str) -> Result <(), StoreError>

TurnCommit

The crate::store::ThreadStore over infrastructure-communication's tables.

Item
pub enum TurnCommit

reduce (other)

History in, referents out — then mask what is left.

Item
pub const SUBJECT_TOKEN: & str
fn resolve(turns : & Turn, window : & Window, subjects : & dyn SubjectSource) -> Resolved
fn reduce(resolved : & Resolved, denylist : & String) -> Option <ClassifierInput>

ClassifierInput

History in, referents out — then mask what is left.

Item
pub struct ClassifierInput

Resolved

History in, referents out — then mask what is left.

Item
pub struct Resolved

SubjectSource

History in, referents out — then mask what is left.

Item
pub trait SubjectSource

store (other)

Where a turn stops being a decision and becomes rows.

Item
async fn store_then_deliver(conversation_id : Uuid, writes : & ThreadWrite, operator : Option <Uuid>, parties : & TurnParties, store : & dyn ThreadStore, delivery : & dyn Delivery,) -> Result <TurnOutcome, StoreError>
async fn deliver_stored(stored : StoredTurn, store : & dyn ThreadStore, delivery : & dyn Delivery,) -> TurnOutcome

Delivery

Where a turn stops being a decision and becomes rows.

Item
pub trait Delivery

DeliveryError

Where a turn stops being a decision and becomes rows.

Item
pub struct DeliveryError
DeliveryError :: fn fmt(& self, f : & mut fmt::Formatter <'_>) -> fmt::Result

StoreError

Where a turn stops being a decision and becomes rows.

Item
pub struct StoreError
StoreError :: fn fmt(& self, f : & mut fmt::Formatter <'_>) -> fmt::Result

StoredMessage

Where a turn stops being a decision and becomes rows.

Item
pub struct StoredMessage

StoredTurn

Where a turn stops being a decision and becomes rows.

Item
pub struct StoredTurn

ThreadStore

Where a turn stops being a decision and becomes rows.

Item
pub trait ThreadStore

TurnOutcome

Where a turn stops being a decision and becomes rows.

Item
pub struct TurnOutcome

TurnParties

Where a turn stops being a decision and becomes rows.

Item
pub struct TurnParties
TurnParties :: fn sender_for(& self, direction : Direction) -> Option <Uuid>

thread (other)

What one turn puts in the thread.

Item
fn may_commit(computed_under : u64, current : u64) -> bool
fn operator_note(handoff : & Handoff) -> String

Direction

What one turn puts in the thread.

Item
pub enum Direction
Direction :: fn as_str(self) -> & 'static str

ThreadWrite

What one turn puts in the thread.

Item
pub struct ThreadWrite
fn plan_writes(buyer_text : & str, outcome : & Outcome) -> Vec <ThreadWrite>

window (other)

Which turns the classifier is allowed to see.

Item
fn needs_context(text : & str) -> bool

Speaker

Which turns the classifier is allowed to see.

Item
pub enum Speaker

Turn

Which turns the classifier is allowed to see.

Item
pub struct Turn
Turn :: fn buyer(text : impl Into <String>) -> Self
Turn :: fn operator(text : impl Into <String>) -> Self

Window

Which turns the classifier is allowed to see.

Item
pub struct Window
fn window(turns : & Turn, policy : WindowPolicy) -> Option <Window>

WindowPolicy

Which turns the classifier is allowed to see.

Item
pub struct WindowPolicy
WindowPolicy :: fn default() -> Self

How to use it

From this crate's own rustdoc:


**Outbound (Story 3).** What the engine concluded becomes rows in the thread:
the buyer's turn, the reply, and — when the bot declines — one fixed phrase
for the buyer and a full account for the operator, on a conversation that is
assigned rather than merely annotated. Buttons are stored as capabilities the
server issued, and a tap redeems one by id.

Module structure

application_conversation

flowchart TD
  n_application_conversation["application_conversation"]
  n_application_conversation --> n_admit["admit"]
  n_application_conversation --> n_catalogue["catalogue"]
  n_application_conversation --> n_classify["classify"]
  n_application_conversation --> n_guard["guard"]
  n_application_conversation --> n_offer["offer"]
  n_application_conversation --> n_pipeline["pipeline"]
  n_application_conversation --> n_postgres["postgres"]
  n_application_conversation --> n_reduce["reduce"]
  n_application_conversation --> n_store["store"]
  n_application_conversation --> n_thread["thread"]
  n_application_conversation --> n_window["window"]

Public surface

`admit`

ItemWhat it is
pub struct RawCandidateWhat a classifier returned, before anything has been checked.
RawCandidate :: fn new(intent : impl Into <String>, score : f32) -> Self
pub enum AdmitErrorWhy nothing usable survived admission
fn admit(raw : & RawCandidate, registry : & IntentRegistry,) -> Result <Vec <Candidate>, AdmitError>Check a classifier's output into the engine's input type

`catalogue`

ItemWhat it is
pub struct ViewerWhat the reader is allowed to be, for access purposes
Viewer :: fn may_see(& self, tier : Visibility) -> boolMay this viewer see an item at tier?
pub enum BuyerAvailabilityWhat a buyer may be told about an item, in a buyer's vocabulary
BuyerAvailability :: fn of(status : CatalogItemStatus) -> Option <Self>The catalogue state a buyer may be told about, if any
BuyerAvailability :: fn catalogue_key(self) -> & 'static strThe catalogue key for this word
pub struct BuyerItemView<'a>An item, already established as one this viewer may be told about.
pub const DEFAULT_MAX_SLOT_LEN: usizeSlots longer than this refuse the render.
BuyerItemView<'a> :: fn of(item : & 'a CatalogItem, viewer : & Viewer) -> Option <Self>The only way to get a view
BuyerItemView<'a> :: fn with_max_slot_len(mut self, max : usize) -> SelfRefuse a render past this length rather than truncating it.
BuyerItemView<'a> :: fn slot_names() -> & 'static & 'static strEvery reader-facing slot name this view answers to
BuyerItemView<'_> :: fn slot(& self, name : & str) -> Option <String>
BuyerItemView<'_> :: fn subjects_in(& self, text : & str) -> Vec <String>AC7

`classify`

ItemWhat it is
pub const DEFAULT_DEADLINE: DurationHow long one classification may take, end to end
pub const DEFAULT_MAX_RESPONSE_BYTES: usizeThe most answer this adapter will parse before refusing it
pub struct CompletionClassifierA Classifier backed by one completion per message
CompletionClassifier :: fn fmt(& self, f : & mut std::fmt::Formatter <'_>) -> std::fmt::Result
CompletionClassifier :: fn new(provider : Arc <dyn AiProvider>, registry : & IntentRegistry) -> SelfBuild a classifier that answers in registry's vocabulary
CompletionClassifier :: fn with_deadline(mut self, deadline : Duration) -> SelfOverride the whole-turn deadline.
CompletionClassifier :: fn with_max_response_bytes(mut self, bytes : usize) -> SelfOverride the byte ceiling on an answer.
CompletionClassifier :: fn with_model(mut self, model : impl Into <String>) -> SelfName a model rather than taking the provider's default.
CompletionClassifier :: fn system_prompt(& self) -> & strThe instruction the model is given, without the message
CompletionClassifier :: async fn classify(& self, input : & ClassifierInput,) -> Result <Vec <RawCandidate>, ClassifierError>

`guard`

ItemWhat it is
pub struct HoldFactOne hold on the window under discussion, as it was read.
HoldFact :: fn consumes_capacity(& self, now : DateTime <Utc>) -> boolDoes this hold consume a unit of the window's capacity? Not the same question as `Hold::is_valid()`, and the difference is the one most likely to ship unnoticed
HoldFact :: fn held_by(& self, party : & str, now : DateTime <Utc>) -> boolIs this a live hold belonging to party?
pub struct WindowSnapshotWhat the conversation could see when it was read
WindowSnapshot :: fn consumed(& self) -> u32How many units of the window are spoken for.
WindowSnapshot :: fn permitted(& self) -> u32The most that may be consumed, given the pool's own policy.
WindowSnapshot :: fn has_room(& self) -> boolIs there room for one more, by the pool's rule rather than by whether a rival exists? "Someone else holds one" is the right question only for a capacity of one with overbooking off — one configuration, not the rule.
WindowSnapshot :: fn party_holds(& self) -> boolDoes this conversation's party already hold a live unit?
pub enum RequiresWhat an intent needs from the conversation's state.
pub struct IntentPolicyThe intents this guard knows how to judge.
IntentPolicy :: fn new() -> Self
IntentPolicy :: fn with(mut self, intent : impl Into <String>, requires : Requires) -> Self
IntentPolicy :: fn requirement(& self, intent : & str) -> Option <Requires>
pub struct UnclassifiedIntentsAn intent was registered that the guard has no policy for.
pub const DEFAULT_MAX_SNAPSHOT_AGE: DurationHow stale a snapshot may be before a consequential intent is refused on that ground alone.
pub struct ConversationGuardThe consumer's answer to the engine's question, from a snapshot.
ConversationGuard :: fn new(snapshot : WindowSnapshot, policy : IntentPolicy, registered : & & str,) -> Result <Self, UnclassifiedIntents>Build a guard, refusing to exist if any registered intent has no policy
ConversationGuard :: fn with_max_age(mut self, max_age : Duration) -> Self
ConversationGuard :: fn is_stale(& self, now : Instant) -> boolHas this snapshot aged past the tolerance? Monotonic.
ConversationGuard :: fn permits(& self, intent : & str) -> GuardVerdict

`offer`

ItemWhat it is
pub struct OfferIdAn opaque handle to one offered button
OfferId :: fn new(id : impl Into <String>) -> Self
OfferId :: fn as_str(& self) -> & str
pub struct OfferOne button, as the server stored it when it offered it.
pub struct RedemptionWhere a tap arrived.
pub enum OfferVerdictWhether the offer may be redeemed here and now.
fn verdict(offer : Option <& Offer>, at : & Redemption) -> OfferVerdictCan this offer be redeemed by this tap? Pure
fn buyer_handoff_text() -> & 'static strThe one thing a buyer is told when the bot hands off

`pipeline`

ItemWhat it is
pub trait ClassifierMaps a reduced input to scored intent names
pub enum ClassifierErrorWhy no answer was obtained, cut by repair rather than by cause
ClassifierError :: fn fmt(& self, f : & mut fmt::Formatter <'_>) -> fmt::Result
pub enum OutcomeWhat one inbound turn produced.
pub enum HandoffWhy a person is needed, and from where
pub struct Inbound<'a>Everything the inbound path needs that it does not own.
async fn handle_message(turns : & Turn, cfg : & Inbound <'_>) -> OutcomeA typed message: window, resolve, reduce, guard, classify, admit, respond.
pub trait OfferStoreClaims an offer, atomically, or says why it could not
pub enum ClaimOutcomeWhat a claim attempt did.
async fn handle_tap(offer_id : & OfferId, at : & Redemption, offers : & dyn OfferStore, templates : & Template, subject : & dyn SlotSource, transition_guard : & dyn TransitionGuard,) -> OutcomeA tapped button

`postgres`

ItemWhat it is
pub struct PostgresThreadStoreA ThreadStore over a Postgres pool.
PostgresThreadStore :: fn new(pool : PgPool) -> Self
async fn store_turn_in(conn : & mut PgConnection, conversation_id : Uuid, writes : & ThreadWrite, operator : Option <Uuid>, parties : & TurnParties,) -> Result <StoredTurn, StoreError>Store one turn on a caller-supplied connection
async fn thread_rows(pool : & PgPool, conversation_id : Uuid,) -> Result <Vec <StoredMessage>, StoreError>The conversation's rows, in the order they happened
PostgresThreadStore :: async fn store_turn(& self, conversation_id : Uuid, writes : & ThreadWrite, operator : Option <Uuid>, parties : & TurnParties,) -> Result <StoredTurn, StoreError>
PostgresThreadStore :: async fn mark_delivered(& self, message_id : Uuid) -> Result <(), StoreError>
PostgresThreadStore :: async fn mark_delivery_failed(& self, message_id : Uuid, note : & str) -> Result <(), StoreError>
pub struct NewOfferOne offer to store at a stated epoch and issue time
async fn insert_offer(pool : & PgPool, offer : & NewOffer) -> Result <(), StoreError>Mint one offer at a caller-stated epoch and issue time
pub struct OfferTermsWhat an offer needs that the reply cannot know
async fn insert_offers_in(conn : & mut PgConnection, conversation_id : Uuid, writes : & ThreadWrite, stored : & StoredTurn, terms : & OfferTerms,) -> Result <Vec <OfferId>, StoreError>Store one turn's buttons as capabilities, on the caller's connection
pub enum TurnCommitWhat became of a turn that was offered to the thread.
async fn store_turn_and_offers(pool : & PgPool, conversation_id : Uuid, writes : & ThreadWrite, operator : Option <Uuid>, parties : & TurnParties, terms : & OfferTerms, computed_under : Option <u64>,) -> Result <TurnCommit, StoreError>One turn and its buttons, in one transaction, if the conversation has not moved under it
async fn take_over(pool : & PgPool, conversation_id : Uuid, operator : Uuid,) -> Result <u64, StoreError>Take a conversation for an operator: assign it and spend every capability the bot issued under the current epoch, in one single-row update
pub struct PostgresOfferStoreAn OfferStore over a Postgres pool.
PostgresOfferStore :: fn new(pool : PgPool) -> Self
PostgresOfferStore :: async fn claim(& self, id : & OfferId, at : & Redemption) -> ClaimOutcomeLock the row, ask the pure verdict, consume only if it said so — one transaction

`reduce`

ItemWhat it is
pub trait SubjectSourceThe catalogue values this thread might be about
pub struct ResolvedWhat survived the history.
pub struct ClassifierInputWhat the classifier is given.
pub const SUBJECT_TOKEN: & strThe token a catalogue value is projected to before masking.
fn resolve(turns : & Turn, window : & Window, subjects : & dyn SubjectSource) -> ResolvedConsume the context into referents
fn reduce(resolved : & Resolved, denylist : & String) -> Option <ClassifierInput>Project subjects to a typed token, then mask what is left

`store`

ItemWhat it is
pub struct StoredMessageOne row as the store wrote it, carrying the durable id delivery needs.
pub struct StoredTurnWhat one stored turn returned.
pub struct StoreErrorThe store could not do what was asked
StoreError :: fn fmt(& self, f : & mut fmt::Formatter <'_>) -> fmt::Result
pub struct DeliveryErrorThe transport could not deliver
DeliveryError :: fn fmt(& self, f : & mut fmt::Formatter <'_>) -> fmt::Result
pub struct TurnPartiesWho the rows of one turn are from
TurnParties :: fn sender_for(& self, direction : Direction) -> Option <Uuid>The sender for a row in this direction
pub trait ThreadStorePuts one turn in the thread, atomically
pub trait DeliveryGets a stored row to the reader it was written for
pub struct TurnOutcomeOne stored turn, and what happened to it after it was stored.
async fn store_then_deliver(conversation_id : Uuid, writes : & ThreadWrite, operator : Option <Uuid>, parties : & TurnParties, store : & dyn ThreadStore, delivery : & dyn Delivery,) -> Result <TurnOutcome, StoreError>Store the turn, then deliver what the reader is meant to see
async fn deliver_stored(stored : StoredTurn, store : & dyn ThreadStore, delivery : & dyn Delivery,) -> TurnOutcomeDeliver a turn that is already stored

`thread`

ItemWhat it is
pub enum DirectionWho a row is from, in the store's own vocabulary.
Direction :: fn as_str(self) -> & 'static str
pub struct ThreadWriteOne row this turn puts in the thread.
fn plan_writes(buyer_text : & str, outcome : & Outcome) -> Vec <ThreadWrite>What the thread records for one inbound turn and its outcome
fn may_commit(computed_under : u64, current : u64) -> boolMay a reply computed under computed_under still be written? AC6, and the reason it is a check at commit rather than a check before computing: the operator can take the conversation while a reply is already in flight
fn operator_note(handoff : & Handoff) -> StringThe operator-facing description of an abstention

`window`

ItemWhat it is
pub enum Speaker
pub struct TurnOne turn of a thread.
Turn :: fn buyer(text : impl Into <String>) -> Self
Turn :: fn operator(text : impl Into <String>) -> Self
pub struct WindowPolicyHow far back the search may go before it gives up
WindowPolicy :: fn default() -> Self
pub struct WindowThe turns the classifier may see, as indices into the thread.
fn needs_context(text : & str) -> boolCan this turn be read on its own? False means it depends on something said earlier
fn window(turns : & Turn, policy : WindowPolicy) -> Option <Window>The window for a thread: the last buyer turn, plus the preceding turns it needs, bounded

Re-exports. Exported here, defined elsewhere.

ExportDefined in
CompletionClassifierclassify::CompletionClassifier
{admit,AdmitError,RawCandidate}admit::{admit,AdmitError,RawCandidate}
{buyer_handoff_text,verdict,Offer,OfferId,OfferVerdict,Redemption}offer::{buyer_handoff_text,verdict,Offer,OfferId,OfferVerdict,Redemption}
{deliver_stored,store_then_deliver,Delivery,DeliveryError,StoreError,StoredMessage,StoredTurn,ThreadStore,TurnOutcome,TurnParties,}store::{deliver_stored,store_then_deliver,Delivery,DeliveryError,StoreError,StoredMessage,StoredTurn,ThreadStore,TurnOutcome,TurnParties,}
{handle_message,handle_tap,ClaimOutcome,Classifier,ClassifierError,Handoff,Inbound,OfferStore,Outcome,}pipeline::{handle_message,handle_tap,ClaimOutcome,Classifier,ClassifierError,Handoff,Inbound,OfferStore,Outcome,}
{needs_context,window,Speaker,Turn,Window,WindowPolicy}window::{needs_context,window,Speaker,Turn,Window,WindowPolicy}
{operator_note,plan_writes,Direction,ThreadWrite}thread::{operator_note,plan_writes,Direction,ThreadWrite}
{reduce,resolve,ClassifierInput,Resolved,SubjectSource,SUBJECT_TOKEN}reduce::{reduce,resolve,ClassifierInput,Resolved,SubjectSource,SUBJECT_TOKEN}

Boundary

Reaches into domain, identity, infrastructure.

Shares tier application with 120 other crates: application-agreements, application-ai, application-analytics, application-approvals, application-assessments, application-audit-log, application-auth, application-billing, … (120 total).

_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._

Where it sits

Tier (ontology)application
Architectural role (taxonomy)pipeline
Locationcrates/application/conversation
Vocabulary in force (lexicon)current

Tier flow. Which tiers this crate's own edges cross.

flowchart LR
  n_application["application"] --> n_domain["domain"]
  n_application["application"] --> n_identity["identity"]
  n_application["application"] --> n_infrastructure["infrastructure"]

Dependencies

Runtime, in this workspace.

CrateTierOptionalOnly on
`domain-catalog`domainnoalways
`domain-conversation-engine`domainnoalways
`domain-reservations`domainnoalways
`identity-parties`identityyesalways
`infrastructure-agent`infrastructurenoalways
`infrastructure-ai`infrastructureyesalways
`infrastructure-communication`infrastructureyesalways
`infrastructure-dlp-detect`infrastructurenoalways

Runtime, from outside the workspace.

CrateRequirementFeaturesOptionalOnly on
async-trait^0.1noalways
chrono^0.4serdenoalways
serde^1deriveyesalways
serde_json^1yesalways
sqlx^0.8runtime-tokio, postgres, chrono, uuid, jsonyesalways
tokio^1fullyesalways
uuid^1v4, v7, serde, jsnoalways

Development, in this workspace.

CrateTierOptionalOnly on
`foundation-money`foundationnoalways

Development, from outside the workspace.

CrateRequirementFeaturesOptionalOnly on
dotenvy^0.15noalways
rust_decimal^1serdenoalways
tokio^1full, macros, rt, rt-multi-threadnoalways

Build. None.

Depended on by. Nothing in this workspace.

Signal flow — what reaches this crate, and what it reaches.

flowchart LR
  SELF["application-conversation"]
  SELF -->|development| n_foundation_money["foundation-money"]
  SELF -->|runtime| n_domain_catalog["domain-catalog"]
  SELF -->|runtime| n_domain_conversation_engine["domain-conversation-engine"]
  SELF -->|runtime| n_domain_reservations["domain-reservations"]
  SELF -->|runtime| n_identity_parties["identity-parties"]
  SELF -->|runtime| n_infrastructure_agent["infrastructure-agent"]
  SELF -->|runtime| n_infrastructure_ai["infrastructure-ai"]
  SELF -->|runtime| n_infrastructure_communication["infrastructure-communication"]
  SELF -->|runtime| n_infrastructure_dlp_detect["infrastructure-dlp-detect"]
  classDef self fill:#1f883d,stroke:#1f883d,color:#fff;
  class SELF self;

Feature flags

FeatureEnablesOn by default
completiondep:infrastructure-ai, dep:tokio, dep:serde, dep:serde_jsonno
defaultyes
postgresdep:sqlx, dep:serde_json, dep:infrastructure-communication, dep:identity-partiesno
flowchart LR
  n_completion["completion"] --> n_dep_infrastructure_ai["dep:infrastructure-ai"]
  n_completion["completion"] --> n_dep_tokio["dep:tokio"]
  n_completion["completion"] --> n_dep_serde["dep:serde"]
  n_completion["completion"] --> n_dep_serde_json["dep:serde_json"]
  n_default["default"]
  n_postgres["postgres"] --> n_dep_sqlx["dep:sqlx"]
  n_postgres["postgres"] --> n_dep_serde_json["dep:serde_json"]
  n_postgres["postgres"] --> n_dep_infrastructure_communication["dep:infrastructure-communication"]
  n_postgres["postgres"] --> n_dep_identity_parties["dep:identity-parties"]

Targets

KindNameSource
libapplication_conversation`src/lib.rs`
testpg_end_to_end`tests/pg_end_to_end.rs`
testpg_offer_claim`tests/pg_offer_claim.rs`
testpg_thread_store`tests/pg_thread_store.rs`

Error model

Error typeNamed by
AdmitErroradmit
ClassifierErrordeclared, no public signature returns it
DeliveryErrordeclared, no public signature returns it
StoreErrorinsert_offer, insert_offers_in, store_then_deliver, store_turn_and_offers, store_turn_in, take_over, … (7 total)

Operational characteristics

PropertyEvidence
async public surfaceyes
async runtimeyes
database accessyes
network I/Onone detected
unsafe codenone detected
environment variablesnone detected

No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.

Configuration

No environment variable is read with a literal name anywhere in this crate. A variable whose key is computed at run time cannot be listed here, and is not claimed to be absent.

No workspace crate depends on this one.

Verification

KindCount
Unit tests125
Integration tests24
Examples0
Doctests1

Evidence by module. How often each public module is named by something executable.

ModuleTestsExamplesConsumers
admit300
catalogue400
classify300
guard700
offer600
pipeline900
postgres1100
reduce600
store1000
thread500
window600

What the tests establish, by name:

Documentation coverage

MeasureDocumentedTotal
Public items with rustdoc91115
Public modules with a //! block1111
pie showData
    title Public items with rustdoc
    "Documented" : 91
    "No rustdoc detected" : 24

Metrics

MetricValue
Rust source files12
Source lines6003
Code lines4008
Public API items115
Public modules11
Tests149
Examples0
Cargo features3
Direct runtime dependencies15
Workspace reverse dependencies0
pie showData
    title Public API by kind
    "constant" : 5
    "enum" : 11
    "function" : 20
    "method" : 45
    "struct" : 29
    "trait" : 5
pie showData
    title Rust source composition
    "Code" : 4008
    "Blank or comment" : 1995

Generation

Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.

All application · Manual