Assembles a reply by filling a stored phrase from a subject's own fields, and decides which registered intent a message expresses -- refusing rather than guessing when a slot is missing, when two intents are too close to separate, or when a consumer's guard says the intent is not permitted in the conversation's current state. Pure logic, zero I/O, zero AI calls: it decides what to say and when to abstain, it never fetches, renders to a channel, or asks a model.
| Tier | domain |
| Role | capability |
| Path | crates/domain/conversation-engine |
| Edition | 2021 |
| Targets | domain_conversation_engine |
| Public items | 45 across 2 modules |
| Tests | 76 |
What it is for
Assemble a reply; never compose one.
A reply is a stored phrase with named slots, filled from whatever the consumer knows about the subject under discussion. There is no generation step here and no model call: this crate decides what to say from data it is handed, and — more often than is comfortable — decides to say nothing and hand the conversation to a human instead.
It refuses in three distinct places, and the distinction matters because each catches a failure the others cannot see:
- A slot has no value.
Template::renderwill not emit a phrase with a
hole in it, a blank, or a plausible default. A question about a measurement nobody recorded is unanswerable regardless of how well it was understood.
- The intent is not clear enough.
IntentRegistry::selectabstains when
the best candidate is below its floor, when the runner-up is too close to separate from it, or when the intent is one whose consequences warrant a stricter bar than the rest.
- The intent is not permitted right now. A
TransitionGuardthe
consumer implements decides whether an otherwise correct intent may be acted on in the conversation's current state. This crate defines the question and guarantees it is asked; it does not know what the states are.
The first two are about the answer. The third is about the moment — a correctly understood question about collection, asked before anything has been reserved, is not answerable just because it was understood.
Capabilities
crate root
Assemble a reply; never compose one.
| Item |
|---|
pub const CANCEL_INTENT: & str |
AskReason
Assemble a reply; never compose one.
| Item |
|---|
pub enum AskReason |
HandoffReason
Assemble a reply; never compose one.
| Item |
|---|
pub enum HandoffReason |
Reply
Assemble a reply; never compose one.
| Item |
|---|
pub enum Reply |
fn respond(registry : & IntentRegistry, candidates : & Candidate, templates : & Template, subject : & dyn SlotSource, guard : & dyn TransitionGuard,) -> Reply |
fn respond_to_action(intent : impl Into <String>, templates : & Template, subject : & dyn SlotSource, guard : & dyn TransitionGuard,) -> Reply |
CartHold
Who else wants this, and what they have offered.
| Item |
|---|
pub struct CartHold |
CartHold :: fn new(holder : impl Into <String>) -> Self |
CartHold :: fn offering(holder : impl Into <String>, offer : i64) -> Self |
Interest
Who else wants this, and what they have offered.
| Item |
|---|
pub struct Interest |
Interest :: fn new(holds : Vec <CartHold>) -> Self |
Interest :: fn holds(& self) -> & CartHold |
Interest :: fn cart_count(& self) -> usize |
Interest :: fn others(& self, viewer : & str) -> usize |
Interest :: fn highest_offer(& self) -> Option <i64> |
Interest :: fn next_highest(& self, viewer : & str) -> Option <i64> |
Interest :: fn is_leading(& self, viewer : & str) -> bool |
Interest :: fn bidding(& self, rounds_used : u8, elapsed_secs : u64) -> Bidding |
Interest :: fn as_slots <'a>(& 'a self, viewer : & 'a str) -> InterestSlots <'a> |
InterestSlots
Who else wants this, and what they have offered.
| Item |
|---|
pub struct InterestSlots<'a> |
InterestSlots<'_>
Who else wants this, and what they have offered.
| Item |
|---|
InterestSlots<'_> :: fn slot(& self, name : & str) -> Option <String> |
offer (other)
Haggling, decided by arithmetic.
| Item |
|---|
fn buy_now(policy : & OfferPolicy, offered : i64, bidder : & str, state : & Bidding) -> BuyNowOutcome |
fn closure(policy : & OfferPolicy, state : & Bidding) -> Option <CloseReason> |
fn close(policy : & OfferPolicy, state : & Bidding) -> OfferOutcome |
fn evaluate(policy : & OfferPolicy, offered : i64, bidder : & str, state : & Bidding) -> OfferOutcome |
Bidding
Haggling, decided by arithmetic.
| Item |
|---|
pub struct Bidding |
BuyNowOutcome
Haggling, decided by arithmetic.
| Item |
|---|
pub enum BuyNowOutcome |
CloseReason
Haggling, decided by arithmetic.
| Item |
|---|
pub enum CloseReason |
Notify
Haggling, decided by arithmetic.
| Item |
|---|
pub struct Notify |
OfferOutcome
Haggling, decided by arithmetic.
| Item |
|---|
pub enum OfferOutcome |
OfferOutcome :: fn intent(& self) -> & 'static str |
OfferOutcome :: fn notification(& self) -> Option <& Notify> |
OfferPolicy
Haggling, decided by arithmetic.
| Item |
|---|
pub struct OfferPolicy |
OfferPolicy :: fn new(asking : i64) -> Self |
OfferPolicy :: fn with_discount_percent(mut self, percent : u8) -> Self |
OfferPolicy :: fn with_refer_below_percent(mut self, percent : u8) -> Self |
OfferPolicy :: fn with_min_increment(mut self, increment : i64) -> Self |
OfferPolicy :: fn with_rebid_rounds(mut self, rounds : u8) -> Self |
OfferPolicy :: fn with_close_after(mut self, rounds : u8, secs : u64) -> Self |
OfferPolicy :: fn with_buy_now_premium_percent(mut self, percent : u8) -> Self |
OfferPolicy :: fn buy_now_price(& self, state : & Bidding) -> i64 |
OfferPolicy :: fn floor(& self) -> i64 |
ReferReason
Haggling, decided by arithmetic.
| Item |
|---|
pub enum ReferReason |
StandingOffer
Haggling, decided by arithmetic.
| Item |
|---|
pub struct StandingOffer |
StandingOffer :: fn new(amount : i64, holder : impl Into <String>) -> Self |
How to use it
No examples/ target and no doctest in this crate's rustdoc. The tests listed under Verification are the closest executable usage.
Module structure
domain_conversation_engine
interestoffer
flowchart TD n_domain_conversation_engine["domain_conversation_engine"] n_domain_conversation_engine --> n_interest["interest"] n_domain_conversation_engine --> n_offer["offer"]
Public surface
`crate root`
| Item | What it is |
|---|---|
pub enum Reply | Everything the engine can conclude from one inbound message |
pub enum AskReason | Why the engine put a choice back to the reader |
pub enum HandoffReason | Why the engine declined to answer |
fn respond(registry : & IntentRegistry, candidates : & Candidate, templates : & Template, subject : & dyn SlotSource, guard : & dyn TransitionGuard,) -> Reply | The one entry point: decide, then either render or abstain |
pub const CANCEL_INTENT: & str | The intent a "No" button carries when the engine offers one itself |
fn respond_to_action(intent : impl Into <String>, templates : & Template, subject : & dyn SlotSource, guard : & dyn TransitionGuard,) -> Reply | The turn after a reader taps a button |
`interest`
| Item | What it is |
|---|---|
pub struct CartHold | One person holding an item, with the offer they have made if any |
CartHold :: fn new(holder : impl Into <String>) -> Self | Someone holding the item at the asking price, without an offer. |
CartHold :: fn offering(holder : impl Into <String>, offer : i64) -> Self | Someone holding the item and offering a number for it. |
pub struct Interest | Every live hold on one item. |
Interest :: fn new(holds : Vec <CartHold>) -> Self | — |
Interest :: fn holds(& self) -> & CartHold | — |
Interest :: fn cart_count(& self) -> usize | How many carts this item is sitting in. |
Interest :: fn others(& self, viewer : & str) -> usize | How many carts other than this reader's |
Interest :: fn highest_offer(& self) -> Option <i64> | The best offer anyone has made. |
Interest :: fn next_highest(& self, viewer : & str) -> Option <i64> | The best offer made by anyone other than this reader |
Interest :: fn is_leading(& self, viewer : & str) -> bool | Is this reader's offer the best one standing? False when they have made no offer at all, even if nobody else has either — leading is something you do with a number, not by being present. |
Interest :: fn bidding(& self, rounds_used : u8, elapsed_secs : u64) -> Bidding | The negotiation state the offer rules work against |
Interest :: fn as_slots <'a>(& 'a self, viewer : & 'a str) -> InterestSlots <'a> | This item's interest as slot values, from one reader's point of view. |
pub struct InterestSlots<'a> | Interest expressed as template slots |
InterestSlots<'_> :: fn slot(& self, name : & str) -> Option <String> | — |
`offer`
| Item | What it is |
|---|---|
pub struct OfferPolicy | What the seller will accept, and how long they will go on about it. |
OfferPolicy :: fn new(asking : i64) -> Self | The operator's stated rule: 20% off, refer below half, two rebids for an outbid holder, and a window that closes after three rounds or a day. |
OfferPolicy :: fn with_discount_percent(mut self, percent : u8) -> Self | — |
OfferPolicy :: fn with_refer_below_percent(mut self, percent : u8) -> Self | — |
OfferPolicy :: fn with_min_increment(mut self, increment : i64) -> Self | — |
OfferPolicy :: fn with_rebid_rounds(mut self, rounds : u8) -> Self | — |
OfferPolicy :: fn with_close_after(mut self, rounds : u8, secs : u64) -> Self | — |
OfferPolicy :: fn with_buy_now_premium_percent(mut self, percent : u8) -> Self | — |
OfferPolicy :: fn buy_now_price(& self, state : & Bidding) -> i64 | What it costs to end the window early and take the item |
OfferPolicy :: fn floor(& self) -> i64 | The lowest acceptable price, in minor units |
pub struct StandingOffer | The best offer currently held, and who holds it. |
StandingOffer :: fn new(amount : i64, holder : impl Into <String>) -> Self | — |
pub struct Bidding | Where the negotiation has got to |
pub struct Notify | A message to put in somebody else's queue |
pub enum CloseReason | Why the window is shut. |
pub enum ReferReason | Why an offer stopped being the engine's business. |
pub enum OfferOutcome | What to do about one offer. |
OfferOutcome :: fn intent(& self) -> & 'static str | The conventional intent name for this outcome, so a consumer can key a template and a guard by it like any other intent. |
OfferOutcome :: fn notification(& self) -> Option <& Notify> | The message owed to somebody who is not the sender, if any. |
pub enum BuyNowOutcome | What happens when someone tries to take the item outright. |
fn buy_now(policy : & OfferPolicy, offered : i64, bidder : & str, state : & Bidding) -> BuyNowOutcome | Take the item now rather than waiting out the window |
fn closure(policy : & OfferPolicy, state : & Bidding) -> Option <CloseReason> | Has the window shut? |
fn close(policy : & OfferPolicy, state : & Bidding) -> OfferOutcome | Who takes it, if the window shut now. |
fn evaluate(policy : & OfferPolicy, offered : i64, bidder : & str, state : & Bidding) -> OfferOutcome | Decide one offer against the state of the negotiation |
Re-exports. Exported here, defined elsewhere.
| Export | Defined in |
|---|---|
Action | action::Action |
{Bidding,Notify,OfferOutcome,OfferPolicy,StandingOffer} | offer::{Bidding,Notify,OfferOutcome,OfferPolicy,StandingOffer} |
{Candidate,IntentDecision,IntentRegistry,IntentRule,Risk} | intent::{Candidate,IntentDecision,IntentRegistry,IntentRule,Risk} |
{CartHold,Interest} | interest::{CartHold,Interest} |
{GuardVerdict,PermitAll,TransitionGuard} | guard::{GuardVerdict,PermitAll,TransitionGuard} |
{RenderError,SlotSource,Template} | template::{RenderError,SlotSource,Template} |
Boundary
Depends on no other workspace tier.
Shares tier domain with 41 other crates: domain-agreements, domain-ai-report, domain-billing, domain-catalog, domain-classify, domain-comments, domain-competitive-intel, domain-contact, … (41 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) | domain |
| Architectural role (taxonomy) | capability |
| Location | crates/domain/conversation-engine |
| Vocabulary in force (lexicon) | current |
Dependencies
Runtime. None.
Development. None.
Build. None.
Depended on by. 1 workspace crate.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR n_application_conversation["application-conversation"] -->|uses| SELF SELF["domain-conversation-engine"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.
Targets
| Kind | Name | Source |
|---|---|---|
| lib | domain_conversation_engine | `src/lib.rs` |
Error model
No public error type was detected: no public item declares a type named *Error, and no public signature returns one.
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | none detected |
| async runtime | none detected |
| database access | none detected |
| network I/O | none detected |
| unsafe code | none detected |
| environment variables | none 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.
Related capabilities
1 workspace crate depends on this one: application-conversation.
Verification
| Kind | Count |
|---|---|
| Unit tests | 76 |
| Integration tests | 0 |
| Examples | 0 |
| Doctests | 0 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
crate root | 6 | 0 | 5 |
interest | 3 | 0 | 0 |
offer | 12 | 0 | 1 |
What the tests establish, by name:
a_label_and_an_intent_are_separate—src/action.rsyes_no_offers_both_sides—src/action.rsa_guard_can_answer_differently_per_intent—src/guard.rspermit_all_permits—src/guard.rsthe_engine_asks_the_guard_exactly_once—src/guard.rsa_consequential_intent_asks_where_an_ordinary_one_answers—src/intent.rsa_consequential_intent_below_the_ordinary_floor_is_unrecognized—src/intent.rsa_per_intent_floor_overrides_its_tier—src/intent.rscandidate_order_does_not_matter—src/intent.rsdeclines_below_the_floor—src/intent.rsdeclines_when_nothing_was_offered—src/intent.rsdeclines_when_the_runner_up_is_too_close—src/intent.rsignores_intents_it_does_not_know—src/intent.rsselects_a_clear_winner—src/intent.rsselects_when_the_gap_is_wide_enough—src/intent.rsthresholds_are_configuration—src/intent.rstwo_weak_candidates_are_unrecognized_not_ambiguous—src/intent.rsa_cart_hold_with_no_offer_is_not_leading—src/interest.rsa_phrase_about_competition_does_not_fire_when_there_is_none—src/interest.rsa_reader_who_is_alone_still_sees_the_cart_count—src/interest.rsan_item_nobody_holds_reports_nothing—src/interest.rscarts_without_offers_do_not_become_bids—src/interest.rscounts_every_cart_the_item_sits_in—src/interest.rscounts_other_peoples_carts_separately_from_your_own—src/interest.rsfills_a_phrase_about_the_competition—src/interest.rshands_the_offer_rules_the_best_standing_bid—src/interest.rsknows_who_is_leading—src/interest.rsreports_the_highest_offer_anyone_has_made—src/interest.rsthe_next_highest_excludes_the_readers_own_offer—src/interest.rsa_confirmation_always_offers_a_way_out—src/lib.rs- _… 46 more_
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 35 | 45 |
Public modules with a //! block | 2 | 2 |
pie showData
title Public items with rustdoc
"Documented" : 35
"No rustdoc detected" : 10
Metrics
| Metric | Value |
|---|---|
| Rust source files | 7 |
| Source lines | 2378 |
| Code lines | 1634 |
| Public API items | 45 |
| Public modules | 2 |
| Tests | 76 |
| Examples | 0 |
| Cargo features | 0 |
| Direct runtime dependencies | 0 |
| Workspace reverse dependencies | 1 |
pie showData
title Public API by kind
"constant" : 1
"enum" : 7
"function" : 6
"method" : 24
"struct" : 7
pie showData
title Rust source composition
"Code" : 1634
"Blank or comment" : 744
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.