use rusqlite::Connection;
use crate::context::host::HostKind;
use crate::context::load_session_start_candidates;
use crate::context::policy::{ContextLimits, ContextPolicy};
use crate::context::render::render_context_output_from_inputs;
use crate::context::render_inputs::load_context_render_inputs;
use crate::context::types::ContextRequest as RenderRequest;
use crate::context_bundle::{
compile_session_start_bundle, AgentRole, ChannelKind, ContextRequest, DegradedMode, ProjectRef,
RiskClass, TrustClass, CONTEXT_BUNDLE_SCHEMA_VERSION,
};
use super::{insert_memory, insert_memory_with_branch, setup_context_schema};
const PROJECT: &str = "demo/project";
const EPOCH: i64 = 1_710_000_000;
fn conn_with_schema() -> Connection {
let conn = Connection::open_in_memory().expect("in-memory connection");
setup_context_schema(&conn);
conn
}
fn request() -> ContextRequest {
ContextRequest {
schema_version: CONTEXT_BUNDLE_SCHEMA_VERSION,
task: "resume the migration work".to_string(),
project: ProjectRef {
key: PROJECT.to_string(),
},
branch: None,
worktree: None,
role: AgentRole::Coder,
as_of_epoch: EPOCH,
token_budget: 3_000,
risk: RiskClass::Low,
include_superseded: false,
}
}
#[test]
fn candidates_route_memories_to_their_sections() {
let conn = conn_with_schema();
insert_memory(
&conn,
1,
PROJECT,
None,
"decision",
"Chose sqlite",
"Body",
EPOCH,
);
insert_memory(
&conn,
2,
PROJECT,
None,
"preference",
"Always run fmt",
"Body",
EPOCH,
);
insert_memory(
&conn,
3,
PROJECT,
None,
"session_activity",
"Ran the suite",
"Body",
EPOCH,
);
let candidates = load_session_start_candidates(&conn, PROJECT, "/tmp/remem-bundle-test", None)
.expect("load");
let channel_of = |key: &str| {
candidates
.iter()
.find(|item| item.stable_key == key)
.unwrap_or_else(|| panic!("missing candidate {key}"))
.channel
};
assert_eq!(channel_of("memory:1"), ChannelKind::Core);
assert_eq!(channel_of("memory:2"), ChannelKind::Preferences);
assert_eq!(channel_of("memory:3"), ChannelKind::MemoryIndex);
}
#[test]
fn candidates_carry_canonical_attribution_and_project_scope() {
let conn = conn_with_schema();
insert_memory(
&conn,
1,
PROJECT,
None,
"decision",
"Chose sqlite",
"Body",
EPOCH,
);
let candidates = load_session_start_candidates(&conn, PROJECT, "/tmp/remem-bundle-test", None)
.expect("load");
let item = candidates
.iter()
.find(|item| item.stable_key == "memory:1")
.expect("candidate");
assert_eq!(item.canonical_ref.as_deref(), Some("memory:1"));
assert_eq!(item.projection_ref, None);
assert_eq!(item.project.as_deref(), Some(PROJECT));
assert_eq!(item.trust, TrustClass::Standard);
}
#[test]
fn canonical_load_failure_is_an_error_not_a_shorter_list() {
let conn = conn_with_schema();
conn.execute("DROP TABLE memories", []).expect("drop");
let error = load_session_start_candidates(&conn, PROJECT, "/tmp/remem-bundle-test", None)
.expect_err("a failed canonical load must not return candidates");
assert!(
error.to_string().contains("canonical context load failed"),
"unexpected error: {error}"
);
}
#[test]
fn canonical_load_failure_produces_a_blocked_bundle() {
let conn = conn_with_schema();
conn.execute("DROP TABLE memories", []).expect("drop");
let bundle =
compile_session_start_bundle(&conn, &request(), "/tmp/remem-bundle-test", None, true)
.expect("compile");
assert_eq!(bundle.degraded_mode, DegradedMode::Blocked);
assert_eq!(bundle.audit.degraded_mode, DegradedMode::Blocked);
assert_eq!(
bundle.audit.truncation_reason.as_deref(),
Some("canonical_load_failed")
);
assert!(bundle.current_truth.is_empty());
assert!(bundle.memory_index.is_empty());
assert_eq!(bundle.audit.selected_count, 0);
}
#[test]
fn healthy_load_compiles_a_full_bundle_from_the_database() {
let conn = conn_with_schema();
insert_memory(
&conn,
1,
PROJECT,
None,
"decision",
"Chose sqlite",
"Body",
EPOCH,
);
let bundle =
compile_session_start_bundle(&conn, &request(), "/tmp/remem-bundle-test", None, true)
.expect("compile");
assert_eq!(bundle.degraded_mode, DegradedMode::Full);
assert!(bundle.audit.truncation_reason.is_none());
assert_eq!(
bundle
.current_truth
.iter()
.map(|item| item.stable_key.as_str())
.collect::<Vec<_>>(),
vec!["memory:1"]
);
assert!(!bundle.audit.plan_hash.is_empty());
}
#[test]
fn missing_enrichment_degrades_to_canonical_only() {
let conn = conn_with_schema();
insert_memory(
&conn,
1,
PROJECT,
None,
"decision",
"Chose sqlite",
"Body",
EPOCH,
);
let bundle =
compile_session_start_bundle(&conn, &request(), "/tmp/remem-bundle-test", None, false)
.expect("compile");
assert_eq!(bundle.degraded_mode, DegradedMode::CanonicalOnly);
assert_eq!(bundle.current_truth.len(), 1);
}
#[test]
fn production_bundle_renderer_is_byte_compatible_and_keeps_index_fallback() {
let conn = conn_with_schema();
insert_memory(
&conn,
1,
PROJECT,
None,
"decision",
"Primary decision",
"Primary body",
EPOCH + 1,
);
insert_memory(
&conn,
2,
PROJECT,
None,
"decision",
"Secondary decision",
"Secondary body",
EPOCH,
);
insert_memory(
&conn,
3,
PROJECT,
None,
"preference",
"Always run fmt",
"Always run fmt before checks",
EPOCH,
);
let request = RenderRequest {
cwd: "/tmp/remem-bundle-render-test".to_string(),
project: PROJECT.to_string(),
session_id: Some("bundle-render-test".to_string()),
hook_source: Some("startup".to_string()),
current_branch: None,
host: HostKind::CodexCli,
use_colors: false,
};
let policy = ContextPolicy::from_limits(ContextLimits {
core_item_limit: 1,
..ContextLimits::default()
});
let inputs = load_context_render_inputs(&conn, &request, false, &policy);
let legacy = render_context_output_from_inputs(
&conn,
&request,
None,
false,
policy.clone(),
inputs.clone(),
None,
false,
)
.expect("legacy render");
let bundled =
render_context_output_from_inputs(&conn, &request, None, false, policy, inputs, None, true)
.expect("bundle render");
assert_eq!(bundled.output.as_bytes(), legacy.output.as_bytes());
let bundle = bundled.context_bundle.expect("sealed context bundle");
assert_eq!(bundle.current_truth.len(), 1);
assert_eq!(bundle.current_truth[0].stable_key, "memory:1");
let fallback = bundle
.audit
.entries
.iter()
.find(|entry| entry.stable_key == "memory:2")
.expect("unselected core memory remains an index candidate");
assert_eq!(fallback.channel, ChannelKind::MemoryIndex);
assert_eq!(fallback.reason, "below_sessionstart_relevance_threshold");
assert_eq!(bundle.audit.selected_count as usize, 2);
}
#[test]
fn production_bundle_reuses_preference_snapshot_and_keeps_branch_agnostic_selection() {
let conn = conn_with_schema();
insert_memory_with_branch(
&conn,
7,
PROJECT,
None,
"preference",
"Always run fmt",
"Always run fmt before checks",
EPOCH,
Some("main"),
);
let request = RenderRequest {
cwd: "/tmp/remem-bundle-preference-snapshot".to_string(),
project: PROJECT.to_string(),
session_id: Some("bundle-preference-snapshot".to_string()),
hook_source: Some("startup".to_string()),
current_branch: Some("feature/snapshot".to_string()),
host: HostKind::CodexCli,
use_colors: false,
};
let policy = ContextPolicy::from_limits(ContextLimits::default());
let inputs = load_context_render_inputs(&conn, &request, false, &policy);
conn.execute("UPDATE memories SET status = 'superseded' WHERE id = 7", [])
.expect("supersede preference after snapshot");
let legacy = render_context_output_from_inputs(
&conn,
&request,
None,
false,
policy.clone(),
inputs.clone(),
None,
false,
)
.expect("legacy render");
let bundled =
render_context_output_from_inputs(&conn, &request, None, false, policy, inputs, None, true)
.expect("bundle render");
assert_eq!(bundled.output.as_bytes(), legacy.output.as_bytes());
let bundle = bundled.context_bundle.expect("sealed context bundle");
assert_eq!(bundle.preferences.len(), 1);
assert_eq!(bundle.preferences[0].stable_key, "memory:7");
assert_eq!(bundle.preferences[0].branch, None);
}
#[test]
fn production_bundle_drops_preferences_removed_by_total_char_limit() {
let conn = conn_with_schema();
insert_memory(
&conn,
8,
PROJECT,
None,
"preference",
"Always run fmt",
"Always run fmt before checks",
EPOCH,
);
let request = RenderRequest {
cwd: "/tmp/remem-bundle-preference-truncation".to_string(),
project: PROJECT.to_string(),
session_id: Some("bundle-preference-truncation".to_string()),
hook_source: Some("startup".to_string()),
current_branch: None,
host: HostKind::CodexCli,
use_colors: false,
};
let policy = ContextPolicy::from_limits(ContextLimits {
total_char_limit: 200,
..ContextLimits::default()
});
let inputs = load_context_render_inputs(&conn, &request, false, &policy);
let bundled =
render_context_output_from_inputs(&conn, &request, None, false, policy, inputs, None, true)
.expect("bundle render");
assert!(!bundled.output.contains("Always run fmt before checks"));
let bundle = bundled.context_bundle.expect("sealed context bundle");
assert!(bundle.preferences.is_empty());
let preference_audit = bundle
.audit
.entries
.iter()
.find(|entry| entry.stable_key == "memory:8")
.expect("preference audit entry");
assert!(!preference_audit.selected);
assert_eq!(preference_audit.reason, "total_char_limit");
}