use super::super::session_facts::{SESSION_FACTS_HEADING, SessionFacts, session_facts_text};
use super::super::turn_context::LAST_SQL_BLOCK_LABEL;
use super::*;
use crate::connection::{ConnectionEntry, ConnectionRegistry};
use async_trait::async_trait;
use saya_agent::{build_messages, turn_bytes};
use saya_types::{ConnectionError, QueryRequest, QueryResult, SchemaTree, SqlDialect};
use std::path::PathBuf;
struct DummyConnector {
dialect: SqlDialect,
}
#[async_trait]
impl saya_connectors::DatabaseConnector for DummyConnector {
fn dialect(&self) -> SqlDialect {
self.dialect
}
async fn connect(&self) -> Result<(), ConnectionError> {
Ok(())
}
async fn schema(&self) -> Result<SchemaTree, ConnectionError> {
Ok(SchemaTree::default())
}
async fn execute(&self, req: QueryRequest) -> Result<QueryResult, ConnectionError> {
Ok(QueryResult::empty(req.sql))
}
}
fn single_registry(name: &str) -> ConnectionRegistry {
let mut reg = ConnectionRegistry::new(name);
reg.insert(
name,
ConnectionEntry {
connector: Box::new(DummyConnector {
dialect: SqlDialect::Postgres,
}),
dialect: SqlDialect::Postgres,
profile_id: None,
},
);
reg
}
fn multi_registry() -> ConnectionRegistry {
let mut reg = ConnectionRegistry::new("db1");
reg.insert(
"db1",
ConnectionEntry {
connector: Box::new(DummyConnector {
dialect: SqlDialect::Postgres,
}),
dialect: SqlDialect::Postgres,
profile_id: None,
},
);
reg.insert(
"db2",
ConnectionEntry {
connector: Box::new(DummyConnector {
dialect: SqlDialect::Mysql,
}),
dialect: SqlDialect::Mysql,
profile_id: None,
},
);
reg
}
#[test]
fn memory_section_appears_under_assisted_and_absent_under_off() {
assert!(memory_section(MemoryMode::Assisted).is_some());
assert!(memory_section(MemoryMode::Off).is_none());
let text = memory_section(MemoryMode::Assisted).unwrap();
assert!(text.contains("durable knowledge"));
assert!(text.contains("Confirmed facts relevant to the question are already supplied"));
assert!(text.contains("contract_search"));
assert!(text.contains("contract_read"));
assert!(text.contains("restate it explicitly and precisely in the answer"));
assert!(!text.contains("catalog.schema.object"));
}
#[test]
fn assemble_system_prompt_single_connection_off() {
let reg = single_registry("main");
let prompt =
assemble_system_prompt_with_session(®, MemoryMode::Off, true, &facts(®, &None))
.expect("a prompt");
assert!(!prompt.contains("durable knowledge"));
assert!(prompt.contains("catalog.schema.object"));
}
#[test]
fn assemble_system_prompt_single_connection_assisted() {
let reg = single_registry("main");
let prompt =
assemble_system_prompt_with_session(®, MemoryMode::Assisted, true, &facts(®, &None))
.expect("a prompt");
assert!(prompt.starts_with(MEMORY_SYSTEM_PROMPT));
assert!(prompt.contains("catalog.schema.object"));
}
#[test]
fn assemble_system_prompt_with_last_sql_and_memory_off() {
let reg = single_registry("main");
let prompt =
assemble_system_prompt_with_session(®, MemoryMode::Off, true, &facts(®, &None))
.expect("a prompt");
assert!(!prompt.contains("durable knowledge"));
assert!(
!prompt.contains("most recent SQL you ran was"),
"the hint must not appear in the system prompt: {prompt}"
);
}
#[test]
fn assemble_system_prompt_with_last_sql_and_assisted() {
let reg = single_registry("main");
let prompt =
assemble_system_prompt_with_session(®, MemoryMode::Assisted, true, &facts(®, &None))
.expect("a prompt");
assert!(prompt.contains(MEMORY_SYSTEM_PROMPT));
assert!(
!prompt.contains("most recent SQL you ran was"),
"the hint must not appear in the system prompt: {prompt}"
);
}
#[test]
fn assemble_system_prompt_multi_connection_assisted_and_sql() {
let reg = multi_registry();
let prompt =
assemble_system_prompt_with_session(®, MemoryMode::Assisted, true, &facts(®, &None))
.expect("a prompt");
let conn_idx = prompt.find("Available database connections").unwrap();
let mem_idx = prompt.find("SAYA maintains durable knowledge").unwrap();
let guidance_idx = prompt.find("Multi-step work is expected").unwrap();
assert!(conn_idx < mem_idx);
assert!(mem_idx < guidance_idx);
assert!(
!prompt.contains("most recent SQL you ran was"),
"the hint must not appear in the system prompt: {prompt}"
);
assert!(
prompt.contains("The SQL dialect is the connected engine's, whatever the DDL says"),
"the dialect statement must appear on every request: {prompt}"
);
assert!(
prompt.contains("- postgresql: `catalog.schema.object`"),
"the multi-connection list must name each engine: {prompt}"
);
}
#[test]
fn assemble_system_prompt_stays_within_budget() {
let reg = multi_registry();
let prompt =
assemble_system_prompt_with_session(®, MemoryMode::Assisted, true, &facts(®, &None));
assert!(prompt.is_some());
let system_text = prompt.unwrap();
assert!(system_text.len() < 4000);
let bytes = turn_bytes(Some(&system_text), &[], "How many orders were placed?");
assert!(bytes < 32 * 1024);
}
#[test]
fn memory_section_is_absent_when_memory_is_configured_on_but_unreachable() {
let reg = single_registry("main");
let prompt =
assemble_system_prompt_with_session(®, MemoryMode::Assisted, false, &facts(®, &None))
.expect("a prompt");
assert!(
!prompt.contains("durable knowledge"),
"memory is unreachable, so the briefing must not claim otherwise: {prompt}"
);
assert!(memory_reachable(true, true));
assert!(
!memory_reachable(false, true),
"no state store, no briefing"
);
assert!(
!memory_reachable(true, false),
"privacy gate shut, no briefing"
);
}
fn registry_with(dialect: SqlDialect) -> ConnectionRegistry {
let mut reg = ConnectionRegistry::new("db");
reg.insert(
"db",
ConnectionEntry {
connector: Box::new(DummyConnector { dialect }),
dialect,
profile_id: None,
},
);
reg
}
#[test]
fn the_naming_rule_matches_what_the_engine_accepts() {
let sqlite_reg = registry_with(SqlDialect::Sqlite);
let sqlite = assemble_system_prompt_with_session(
&sqlite_reg,
MemoryMode::Assisted,
true,
&facts(&sqlite_reg, &None),
)
.expect("a prompt");
assert!(
!sqlite.contains("catalog.schema.object"),
"SQLite cannot parse a three-part name, so the prompt must not ask for one: {sqlite}"
);
let postgres_reg = registry_with(SqlDialect::Postgres);
let postgres = assemble_system_prompt_with_session(
&postgres_reg,
MemoryMode::Assisted,
true,
&facts(&postgres_reg, &None),
)
.expect("a prompt");
assert!(
postgres.contains("catalog.schema.object"),
"PostgreSQL does accept the three-part name and should still be asked for it: {postgres}"
);
}
#[test]
fn every_engine_is_still_told_to_qualify_names() {
for dialect in [
SqlDialect::Postgres,
SqlDialect::Mysql,
SqlDialect::Sqlite,
SqlDialect::DuckDb,
SqlDialect::Snowflake,
] {
let prompt = assemble_system_prompt_with_session(
®istry_with(dialect),
MemoryMode::Assisted,
true,
&facts(®istry_with(dialect), &None),
)
.expect("a prompt");
assert!(
prompt.contains(dialect.qualified_name_form()),
"{} must be told its own name form: {prompt}",
dialect.as_str()
);
}
}
#[test]
fn the_naming_rule_is_present_with_memory_off() {
let prompt = assemble_system_prompt_with_session(
®istry_with(SqlDialect::Sqlite),
MemoryMode::Off,
false,
&facts(®istry_with(SqlDialect::Sqlite), &None),
)
.expect("a prompt");
assert!(
prompt.contains(SqlDialect::Sqlite.qualified_name_form()),
"the SQL naming rule must survive memory being off: {prompt}"
);
assert!(
!prompt.contains("durable knowledge"),
"memory is off, so the memory briefing must stay absent: {prompt}"
);
}
#[test]
fn single_connection_prompt_names_engine_and_guides_giving_up() {
let prompt = assemble_system_prompt_with_session(
®istry_with(SqlDialect::Postgres),
MemoryMode::Off,
false,
&facts(®istry_with(SqlDialect::Postgres), &None),
)
.expect("a prompt");
assert!(
prompt.contains("postgresql"),
"the engine must be named for a single connection: {prompt}"
);
assert!(
prompt.contains("Multi-step work is expected"),
"the prompt must say multi-step work is expected: {prompt}"
);
assert!(
prompt.contains("Do not repeat an attempt that already failed"),
"the prompt must tell the model not to repeat a failed attempt: {prompt}"
);
assert!(
prompt.contains("Giving up with a reason"),
"the prompt must sanction giving up with a reason: {prompt}"
);
assert!(
prompt.contains("stop and say so"),
"the prompt must tell the model to stop and explain when it cannot answer: {prompt}"
);
}
#[test]
fn multi_connection_prompt_also_guides_giving_up() {
let prompt = assemble_system_prompt_with_session(
&multi_registry(),
MemoryMode::Off,
false,
&facts(&multi_registry(), &None),
)
.expect("a prompt");
assert!(
prompt.contains("Giving up with a reason"),
"multi-connection prompt must also coach giving up: {prompt}"
);
}
#[test]
fn assembled_prompt_contains_the_answer_contract() {
let prompt = assemble_system_prompt_with_session(
&single_registry("main"),
MemoryMode::Off,
false,
&facts(&single_registry("main"), &None),
)
.expect("a prompt");
assert!(
prompt.contains(ANSWER_CONTRACT),
"the answer contract must be part of every prompt: {prompt}"
);
}
#[test]
fn answer_contract_present_regardless_of_connections_and_memory() {
let cases: [(ConnectionRegistry, MemoryMode, bool); 6] = [
(single_registry("main"), MemoryMode::Off, false),
(single_registry("main"), MemoryMode::Assisted, true),
(single_registry("main"), MemoryMode::Assisted, false),
(multi_registry(), MemoryMode::Off, false),
(multi_registry(), MemoryMode::Assisted, true),
(multi_registry(), MemoryMode::Off, true),
];
for (reg, mode, reachable) in cases {
let prompt =
assemble_system_prompt_with_session(®, mode, reachable, &facts(®, &None))
.expect("a prompt for this case");
assert!(
prompt.contains(ANSWER_CONTRACT),
"answer contract missing for memory {}, reachable {reachable}: {prompt}",
mode.as_str(),
);
}
}
const ANSWER_CONTRACT_MAX_BYTES: usize = 1200;
#[test]
fn answer_contract_directs_completing_a_computation_not_returning_its_operands() {
let prompt = assemble_system_prompt_with_session(
&single_registry("main"),
MemoryMode::Off,
false,
&facts(&single_registry("main"), &None),
)
.expect("a prompt");
assert!(
prompt.contains("a ratio, a percentage, or a difference"),
"the directive must name the class of computations it covers: {prompt}"
);
assert!(
prompt.contains("compute that value and answer with it"),
"the contract must tell the model to finish the computation the question names: {prompt}"
);
assert!(
prompt.contains("returning the operands alone stops one step short"),
"the directive must name the failure it fixes — stopping one step short: {prompt}"
);
}
#[test]
fn answer_contract_directs_a_superlative_to_name_one_row() {
let prompt = assemble_system_prompt_with_session(
&single_registry("main"),
MemoryMode::Off,
false,
&facts(&single_registry("main"), &None),
)
.expect("a prompt");
assert!(
prompt.contains("\"the fastest\", \"the highest\", \"the top one\""),
"the directive must carry the examples the earlier contract was measured on: {prompt}"
);
assert!(
prompt.contains("\"the fewest\""),
"the directive must cover a superlative in the other direction: {prompt}"
);
assert!(
prompt.contains("asks which one: answer with that row"),
"the contract must tell the model a superlative has a one-row answer: {prompt}"
);
assert!(
prompt.contains("Every row tied with it is part of the answer"),
"the one-row directive must not license dropping tied rows: {prompt}"
);
}
#[test]
fn answer_contract_section_stays_under_documented_ceiling() {
assert!(
ANSWER_CONTRACT.len() <= ANSWER_CONTRACT_MAX_BYTES,
"answer contract is {} bytes; the stated ceiling is {}",
ANSWER_CONTRACT.len(),
ANSWER_CONTRACT_MAX_BYTES,
);
}
#[test]
fn system_prompt_does_not_contain_the_previous_sql() {
let reg = single_registry("main");
let prompt =
assemble_system_prompt_with_session(®, MemoryMode::Off, true, &facts(®, &None))
.expect("a prompt");
assert!(
!prompt.contains("most recent SQL you ran was"),
"the SQL hint prose must not appear in the system prompt: {prompt}"
);
let hint = last_sql_hint_block("SELECT 1 FROM tbl").expect("a hint block");
assert!(
!prompt.contains(&hint.body),
"the system prompt must not contain the hint block body: {prompt}"
);
}
#[test]
fn system_message_is_byte_identical_across_turns_differing_only_in_last_sql() {
let reg = single_registry("main");
let system_prompt =
assemble_system_prompt_with_session(®, MemoryMode::Off, true, &facts(®, &None));
let question = "refine the last query";
let blocks_a: Vec<saya_agent::ContextBlock> = Vec::new();
let blocks_b = vec![last_sql_hint_block("SELECT 1 FROM tbl").expect("a hint block")];
let msgs_a = build_messages(
system_prompt.as_deref(),
&blocks_a,
question,
&[],
32 * 1024,
)
.unwrap();
let msgs_b = build_messages(
system_prompt.as_deref(),
&blocks_b,
question,
&[],
32 * 1024,
)
.unwrap();
assert_eq!(
msgs_a[0], msgs_b[0],
"the system message must not vary with the previous turn's SQL — that is what makes the prefix cache work"
);
assert_ne!(msgs_a[1], msgs_b[1]);
}
#[test]
fn dialect_statement_names_the_connected_engine() {
let prompt = assemble_system_prompt_with_session(
®istry_with(SqlDialect::Sqlite),
MemoryMode::Off,
false,
&facts(®istry_with(SqlDialect::Sqlite), &None),
)
.expect("a prompt");
assert!(
prompt.contains("The SQL dialect is sqlite's, whatever the DDL says"),
"the dialect statement must name the connected engine: {prompt}"
);
assert!(
prompt.contains("declared column types in this database may come from another engine"),
"the prompt must warn that declared types may come from another engine: {prompt}"
);
}
#[test]
fn last_sql_hint_reaches_user_turn_not_system_message() {
let reg = single_registry("main");
let system_prompt =
assemble_system_prompt_with_session(®, MemoryMode::Off, true, &facts(®, &None));
let hint = last_sql_hint_block("SELECT 1 FROM tbl").expect("a hint block");
let messages = build_messages(
system_prompt.as_deref(),
&[hint],
"refine it",
&[],
32 * 1024,
)
.unwrap();
assert_eq!(messages[0].role, "system");
assert!(
!messages[0].content.contains("SELECT 1 FROM tbl"),
"the hint must not leak into the system message: {messages:?}"
);
assert!(
!messages[0].content.contains("most recent SQL you ran was"),
"the hint prose must not leak into the system message: {messages:?}"
);
assert_eq!(messages[1].role, "user");
assert!(messages[1].content.contains("SELECT 1 FROM tbl"));
assert!(messages[1].content.contains("most recent SQL you ran was"));
assert!(
messages[1].content.ends_with("refine it"),
"the user's question must still trail the block: {messages:?}"
);
}
#[test]
fn last_sql_hint_block_is_none_for_empty_sql() {
assert!(last_sql_hint_block("").is_none());
assert!(last_sql_hint_block(" \n\t ").is_none());
let hint = last_sql_hint_block("SELECT 1").expect("non-empty SQL yields a block");
assert_eq!(hint.label, LAST_SQL_BLOCK_LABEL);
assert!(hint.body.contains("SELECT 1"));
assert!(hint.body.contains("most recent SQL you ran was"));
assert!(!hint.truncated);
}
use saya_agent::AgentMode;
#[test]
fn plan_paragraph_bytes_are_pinned() {
assert_eq!(
PLAN_SYSTEM_PROMPT,
"You are in Plan mode: investigate and answer with a plan. \
Write-shaped tools are absent from your tool list and would refuse if called; \
do not promise edits as if you had made them — describe the change you would make instead. \
Plan composes with the approval policy and never widens it."
);
}
#[test]
fn build_prompt_is_byte_identical_to_the_unmoded_prompt() {
for (reg, mode, reachable) in [
(single_registry("main"), MemoryMode::Off, false),
(single_registry("main"), MemoryMode::Assisted, true),
(single_registry("main"), MemoryMode::Assisted, false),
(multi_registry(), MemoryMode::Off, false),
(multi_registry(), MemoryMode::Assisted, true),
(multi_registry(), MemoryMode::Off, true),
] {
assert_eq!(
assemble_system_prompt_for_mode(
®,
mode,
reachable,
&facts(®, &None),
AgentMode::Build
),
assemble_system_prompt_with_session(®, mode, reachable, &facts(®, &None)),
"Build must be byte-identical to the unmoded prompt",
);
}
}
#[test]
fn plan_prompt_appends_the_paragraph_exactly_once_and_nothing_else() {
for (reg, mode, reachable) in [
(single_registry("main"), MemoryMode::Off, false),
(single_registry("main"), MemoryMode::Assisted, true),
(multi_registry(), MemoryMode::Off, true),
] {
let build = assemble_system_prompt_for_mode(
®,
mode,
reachable,
&facts(®, &None),
AgentMode::Build,
)
.expect("a prompt");
let plan = assemble_system_prompt_for_mode(
®,
mode,
reachable,
&facts(®, &None),
AgentMode::Plan,
)
.expect("a prompt");
assert_eq!(
plan.matches(PLAN_SYSTEM_PROMPT).count(),
1,
"the paragraph must appear exactly once: {plan}"
);
assert_eq!(
plan,
format!("{build}\n\n{PLAN_SYSTEM_PROMPT}"),
"appending the paragraph must be the only difference",
);
}
}
#[test]
fn answer_contract_bullets_are_byte_identical_to_release_0_4_1() {
let bullets = [
"- Return only the columns the question asks for; drop intermediate working columns.",
"- Do not round unless asked.",
"- Write dates as ISO YYYY-MM-DD.",
"- If the question asks for a ratio, a percentage, or a difference, compute that value and answer with it — returning the operands alone stops one step short.",
"- A superlative — \"the fastest\", \"the highest\", \"the top one\", \"the fewest\" — asks which one: answer with that row and the value that makes it so, not the ranking it came from. Every row tied with it is part of the answer.",
"- Answer every quantity the question names; if it asks for two things, answer both.",
"- Read measure words literally: \"volume\" is units, \"revenue\" is money.",
"- A qualifier on a metric is not a qualifier on the population — filter the metric, not the rows.",
"- Keep every row tied at a cut-off; never drop a tie to fit a limit.",
"- When a period is named, enumerate that whole period, not only the rows that happen to appear in the data.",
];
for bullet in bullets {
assert!(
ANSWER_CONTRACT.contains(bullet),
"contract bullet changed or missing: {bullet}\n{ANSWER_CONTRACT}"
);
}
assert_eq!(
ANSWER_CONTRACT
.lines()
.filter(|line| line.starts_with("- "))
.count(),
10,
"no bullet may be added or removed without tripping this pin: {ANSWER_CONTRACT}"
);
}
#[test]
fn answer_contract_scopes_itself_to_query_results() {
assert!(
ANSWER_CONTRACT.contains("govern answers that report query results"),
"the contract must scope itself to query-result answers: {ANSWER_CONTRACT}"
);
assert!(
ANSWER_CONTRACT.contains("leave other answers untouched"),
"the contract must leave non-query answers alone: {ANSWER_CONTRACT}"
);
}
#[test]
fn working_guidance_does_not_scope_answers_to_the_database() {
let prompt = assemble_system_prompt_with_session(
&single_registry("main"),
MemoryMode::Off,
false,
&facts(&single_registry("main"), &None),
)
.expect("a prompt");
assert!(
!prompt.contains("from this database"),
"the stopping rule must cover the whole session, not just a database: {prompt}"
);
}
#[test]
fn database_session_prompt_keeps_schema_discovery_advice() {
let prompt = assemble_system_prompt_with_session(
&single_registry("main"),
MemoryMode::Off,
false,
&facts(&single_registry("main"), &None),
)
.expect("a prompt");
assert!(
prompt.contains("discover the schema before you query it"),
"a database session must keep the schema-discovery advice: {prompt}"
);
assert!(
prompt.contains("a missing table or column"),
"a database session must keep the missing-table specifics: {prompt}"
);
}
#[test]
fn plan_paragraph_says_the_mechanism_without_overclaiming_reads() {
let reg = single_registry("main");
let plan = assemble_system_prompt_for_mode(
®,
MemoryMode::Off,
false,
&facts(®, &None),
AgentMode::Plan,
)
.expect("a prompt");
assert!(
plan.contains("You are in Plan mode: investigate and answer with a plan"),
"must say the session is in Plan mode: {plan}"
);
assert!(
plan.contains("absent from your tool list and would refuse if called"),
"must state the honest mechanism — absent, and refusing if called: {plan}"
);
assert!(
plan.contains("describe the change you would make instead"),
"must tell it to describe the change, not promise made edits: {plan}"
);
assert!(
plan.contains("Plan composes with the approval policy and never widens it"),
"must state Plan composes with the policy: {plan}"
);
assert!(
!plan.contains("unrestricted"),
"must not claim reads are unrestricted: {plan}"
);
}
fn facts<'a>(reg: &'a ConnectionRegistry, root: &'a Option<PathBuf>) -> SessionFacts<'a> {
SessionFacts {
registry: reg,
workspace_root: root.as_deref(),
}
}
#[test]
fn database_only_session_names_connections_and_no_workspace() {
let reg = single_registry("main");
let text = session_facts_text(&facts(®, &None)).expect("a database session has facts");
assert!(
text.contains("main"),
"the facts must name the connection in scope: {text}"
);
assert!(
text.contains("No workspace is bound"),
"the facts must say no workspace is bound: {text}"
);
}
#[test]
fn workspace_only_session_names_root_and_claims_no_database() {
let empty = ConnectionRegistry::new("main");
let root = Some(PathBuf::from("/repo"));
let text = session_facts_text(&facts(&empty, &root)).expect("a workspace session has facts");
assert!(
text.contains("/repo"),
"the facts must name the bound root: {text}"
);
assert!(
text.contains("No database is connected"),
"the facts must not imply a database is in scope: {text}"
);
}
#[test]
fn session_with_both_names_both() {
let reg = multi_registry();
let root = Some(PathBuf::from("/repo"));
let text = session_facts_text(&facts(®, &root)).expect("a session with both has facts");
assert!(text.contains("db1"), "both connections named: {text}");
assert!(text.contains("db2"), "both connections named: {text}");
assert!(text.contains("/repo"), "the root named: {text}");
}
#[test]
fn session_with_neither_produces_no_facts_section() {
let empty = ConnectionRegistry::new("main");
assert!(session_facts_text(&facts(&empty, &None)).is_none());
let prompt = assemble_system_prompt(&empty, MemoryMode::Off, false);
assert!(
!prompt
.as_deref()
.unwrap_or("")
.contains(SESSION_FACTS_HEADING),
"no empty facts heading may appear: {prompt:?}"
);
}
#[test]
fn session_facts_are_byte_stable_for_the_same_session_inputs() {
let reg = multi_registry();
let root = Some(PathBuf::from("/repo"));
let first =
assemble_system_prompt_with_session(®, MemoryMode::Off, false, &facts(®, &root));
let second =
assemble_system_prompt_with_session(®, MemoryMode::Off, false, &facts(®, &root));
assert_eq!(
first, second,
"same session inputs must produce byte-identical prompts"
);
}
#[test]
fn session_facts_name_no_tool() {
for tool in [
"workspace_write",
"workspace_edit",
"workspace_read",
"run_command",
"run_program",
"schema_discovery",
] {
let single = single_registry("main");
let multi = multi_registry();
let empty = ConnectionRegistry::new("main");
let root = Some(PathBuf::from("/repo"));
for (reg, root) in [(&single, &None), (&multi, &root), (&empty, &root)] {
let text = session_facts_text(&facts(reg, root)).expect("session facts");
assert!(
!text.contains(tool),
"the facts section must not name the {tool} tool: {text}"
);
}
}
}
#[test]
fn session_aware_prompt_adds_facts() {
let reg = single_registry("main");
let root = Some(PathBuf::from("/repo"));
let with_session =
assemble_system_prompt_with_session(®, MemoryMode::Off, false, &facts(®, &root))
.expect("a prompt");
assert!(
with_session.contains(SESSION_FACTS_HEADING),
"the session-aware prompt carries the facts: {with_session}"
);
assert!(
with_session.contains("main") && with_session.contains("/repo"),
"the facts name the connection and the root: {with_session}"
);
}